Design software used: Fusion
Materials:
- Xiao RP2040
- NTC 10k 3750k Thermistor
- 10kΩ Resistor
- 0.1 µF Ceramic Capacitor
- 0.98" OLED
- Two 4.99kΩ pull up resistors
Files:
PCB Traces.png
PCB Edges.png
← See Week 8: Input Devices (Initial Design)
This week focused on creating a complete temperature monitoring system that combines an input sensor (NTC thermistor) with an output display (OLED screen). The system uses a Seeed XIAO RP2040 microcontroller to read temperature from a thermistor via analog input and display the readings on a 0.98-inch OLED screen via I2C communication. The entire system is integrated on a custom-designed and milled PCB.
System Architecture:
Expected Functionality: The thermistor measures ambient temperature, the microcontroller converts the resistance reading to temperature using the Beta parameter equation, and displays the result in both Celsius and Fahrenheit on the OLED screen, updating every second.
Design software used: Fusion
Materials:
Files:
PCB Traces.png
PCB Edges.png
Front of PCB
Back of PCB
Bridge between GND and 5V
Improved PCB Design - fixed width of GND traces and NTC connections
Improved Schematic
Display: 0.98" OLED Module
Resolution: 128x64 pixels (monochrome white)
Driver Chip: SSD1306 controller
Communication: I2C (2-wire serial)
Pin Configuration:
I2C Address: 0x3C (7-bit address, confirmed via I2C scanner)
Pull-up Resistors: Included on PCB for SDA and SCL lines
Thermistor Type: NTC (Negative Temperature Coefficient)
Nominal Resistance: 25K ohms at 25°C? (I don't know if I was testing correctly)
Beta Coefficient: 3750K
Circuit Configuration: Voltage divider
3.3V --- [10K Resistor] --- A0 (ADC) --- [25K Thermistor] --- GND
Operation: As temperature increases, thermistor resistance decreases, changing the voltage at A0. The 12-bit ADC reads this voltage (0-3.3V mapped to 0-4095), and software calculates temperature using the Beta parameter equation.
Positive Result: I2C scanner successfully detected device at address 0x3C ✓
The first test confirmed the display controller was responding to I2C commands:
#include <Wire.h>
void setup() {
Serial.begin(115200);
delay(3000);
Wire.begin();
Serial.println("Scanning I2C bus...");
for(byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0) {
Serial.print("Device found at 0x");
if (addr < 16) Serial.print("0");
Serial.println(addr, HEX);
}
}
}
void loop() {}
Serial Monitor Output: Device found at 0x3C
This confirmed:
Critical Problem Discovered:
display.begin() returns true (initialization succeeds)Test code that initialized successfully but produced no visual output:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(115200);
delay(2000);
Wire.begin();
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("Display failed!");
while(1);
}
Serial.println("SUCCESS at 0x3C!");
Serial.println("Screen should be WHITE");
display.fillScreen(WHITE);
display.display();
}
void loop() {}
Serial Monitor showed:
SUCCESS at 0x3C!
Screen should be WHITE
Display showed: Nothing. Completely dark.
To isolate the problem, a series of systematic tests were performed:
Hypothesis: Display might be at 0x3D instead of 0x3C
Action: Tested initialization at both 0x3C and 0x3D
Result: Only responds at 0x3C, still no output ✗
Hypothesis: 0.98" displays might be 128x32 instead of 128x64
Action: Tried both Adafruit_SSD1306(128, 64, ...) and Adafruit_SSD1306(128, 32, ...)
Result: Both initialize successfully, neither shows output ✗
Hypothesis: Display needs external charge pump mode
// Tried both initialization modes:
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Internal charge pump
display.begin(SSD1306_EXTERNALVCC, 0x3C); // External power supply
Result: Both modes initialize successfully, no visual output ✗
Hypothesis: Display contrast too low or display sleep mode
// Force maximum contrast
display.ssd1306_command(0x81); // Contrast control
display.ssd1306_command(0xFF); // Maximum (255)
// Force display ON
display.ssd1306_command(0xAF); // Display ON command
// Force all pixels ON (test pattern)
display.ssd1306_command(0xA5); // Entire display ON
Result: Commands execute without error, no visible change ✗
Hypothesis: Display might not be SSD1306, could be SH1106 or similar variant
Installed U8g2 library and tested multiple driver options:
| Driver Chip | Library/Code | Initialization | Display Output |
|---|---|---|---|
| SSD1306 | Adafruit_SSD1306 | Success ✓ | Blank ✗ |
| SH1106 | U8G2_SH1106_128X64 | Success ✓ | Blank ✗ |
| SSD1309 | U8G2_SSD1309_128X64 | Success ✓ | Blank ✗ |
| SSD1305 | U8G2_SSD1305_128X64 | Success ✓ | Blank ✗ |
Conclusion: All drivers initialize successfully but produce no visual output
Critical Test: Replaced Entire Display
To eliminate the possibility of a defective display, a completely different display module was soldered to the board.
Result: Exact same behavior - I2C responds, initialization succeeds, screen stays blank
Conclusion: Problem is NOT the displays themselves, but something systematic about the PCB or power supply
Evidence Summary:
| Component/Function | Status | Evidence |
|---|---|---|
| I2C Communication | ✓ Working | Scanner detects at 0x3C, all commands accepted |
| SSD1306 Controller | ✓ Working | Responds to commands, initialization succeeds |
| Power to Controller | ✓ Present | 3.2V measured on VCC pin (within spec) |
| Pull-up Resistors | ✓ Working | I2C communication successful |
| OLED Panel | ✗ Not lighting | No glow, no pixels, completely dark |
| Display Units Tested | 2 units | Both show identical behavior |
Most Likely Causes:
1. Voltage Insufficient for OLED Panel (Primary Suspect)
The OLED panel likely requires 5V to power the pixel matrix, even though the controller chip works at 3.3V. This split architecture is common:
Evidence: Both displays fail identically, power measures at 3.2V, controller works but panel doesn't
Problem: VCC is permanently soldered to 3.3V rail on PCB, so I will redesign the board to try 5V
2. PCB Design Flaw
Since both displays fail in the exact same way when connected to this specific PCB
3. Display Configuration Resistors
The back of the display shows "I2C ADDRESS SELECT" with resistor positions. These resistors may also control:
If these aren't configured correctly, the controller will respond to I2C but the panel won't light.
After establishing that the display controller was communicating (even though not displaying), attention turned to testing the thermistor input circuit to verify the complete system functionality.
Problem: Temperature reading showed -20.1°C (far too cold for room temperature)
Raw Data:
Raw ADC: 3702 | Voltage: 2.98V | Resistance: 94198Ω | Temp: -20.1°C
The thermistor was reading 94K ohms instead of the expected ~10K ohms at room temperature. This indicated either:
Discovery via Multimeter: Thermistor measures ~25K ohms at room temperature
Conclusion: This is a 25K nominal thermistor, NOT 10K as initially assumed
Using a multimeter to directly measure the thermistor's resistance revealed the actual specification:
After updating the code to THERMISTOR_NOMINAL = 25000, initial readings showed proper room temperature (~20°C).
Problem: Readings became erratic and unstable
Example data:
20.1°C → 96.9K ohms → -4.0°C
-2.1°C → 87.9K ohms
-2.1°C → 87.7K ohms
-1.9°C → 86.8K ohms
Pattern: Resistance jumping between 30K (correct) and 90K+ (wrong)
Diagnosis: Cold Solder Joints
The erratic readings were classic symptoms of intermittent electrical connections:
Attempted Fix: Reflow Solder Joints
Reheated the thermistor solder joints with fresh solder. Initially this worked:
Raw ADC: 3040 | Voltage: 2.45V | Resistance: 28815Ω | Temp: 21.7°C
Raw ADC: 3116 | Voltage: 2.51V | Resistance: 31828Ω | Temp: 19.4°C
Raw ADC: 3177 | Voltage: 2.56V | Resistance: 34608Ω | Temp: 17.5°C
The readings were now stable and showed a consistent cooling curve (thermistor was cooling down after being heated by the soldering iron).
After the solder reflow, temperature readings showed a steady decrease:
21.7°C → 19.4°C → 17.5°C → 15.7°C → 14.3°C → 12.9°C → 11.7°C → 10.7°C → 9.7°C
Analysis: This is actually correct behavior! The thermistor had been heated during soldering and was naturally cooling back to ambient temperature. The smooth, consistent cooling curve proved:
Final Status: Complete Component Failure
After multiple troubleshooting attempts and reflow cycles, the thermistor progressed from intermittent operation to complete failure:
Cause: Repeated exposure to soldering heat without adequate cooling time exceeded the thermistor's thermal limits, progressively destroying the internal temperature-sensitive element.
Lesson: NTC thermistors have strict thermal budgets. Each soldering attempt must be:
Violating these limits even once can cause permanent damage; multiple violations guarantee component failure.
Likely causes:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Display settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Thermistor settings
#define THERMISTOR_PIN A0
#define SERIES_RESISTOR 10000 // 10K ohm resistor in voltage divider
#define THERMISTOR_NOMINAL 25000 // 25K at 25°C (NOT 10K!)
#define TEMPERATURE_NOMINAL 25
#define BETA_COEFFICIENT 3750
void setup() {
Serial.begin(115200);
delay(2000);
// Initialize I2C and display
Wire.begin();
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("Display initialization failed!");
while(1);
}
// Configure ADC
analogReadResolution(12); // 12-bit ADC (0-4095)
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Temperature Monitor");
display.println("Ready!");
display.display();
delay(2000);
}
void loop() {
// Read thermistor
int rawValue = analogRead(THERMISTOR_PIN);
// Calculate voltage and resistance
float voltage = rawValue * (3.3 / 4095.0);
// Calculate resistance (inverted voltage divider: thermistor on bottom)
float resistance = SERIES_RESISTOR * voltage / (3.3 - voltage);
// Convert to temperature using Beta equation
float steinhart;
steinhart = resistance / THERMISTOR_NOMINAL;
steinhart = log(steinhart);
steinhart /= BETA_COEFFICIENT;
steinhart += 1.0 / (TEMPERATURE_NOMINAL + 273.15);
steinhart = 1.0 / steinhart;
steinhart -= 273.15; // Celsius
float fahrenheit = steinhart * 9.0 / 5.0 + 32.0;
// Display on OLED (if working)
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("Temperature:");
display.setTextSize(3);
display.setCursor(0, 20);
display.print(steinhart, 1);
display.println("C");
display.setTextSize(2);
display.setCursor(0, 45);
display.print(fahrenheit, 1);
display.println("F");
display.display();
// Serial output for debugging
Serial.print("ADC: ");
Serial.print(rawValue);
Serial.print(" | V: ");
Serial.print(voltage, 2);
Serial.print("V | R: ");
Serial.print(resistance, 0);
Serial.print("Ω | Temp: ");
Serial.print(steinhart, 1);
Serial.println("°C");
delay(1000);
}