-----
Microcontrollers are getting really cheap. They were already cheap, but now they seem crazy cheap. Even with onboard WiFi, Bluetooth, and a small OLED display we picked up this ESP32C3 Dev Module for about ~$2 USD; so we had to get two. One turned into an extremely useful and accurate clock while this one will be a temperature logger.
-----
We used a DS18B20 temperature sensor. The simple connection of the sensor to the ESP32C3 looks like this:
-----
So, what do you get? A graph like this. Note that we are charting two temperatures on our chart. Your chart will only show the ESP32 line:
-----Now for the software code we promised. Here is the Node Red flow to import:
[
{
"id": "e19a60a2f08ac386",
"type": "inject",
"z": "c0bb5756099d6dbc",
"name": "Every 60 secs",
"props": [],
"repeat": "60",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "",
"x": 160,
"y": 120,
"wires": [
[
"018abb4b7bfb7e86",
"41314d52d14f7623"
]
]
},
{
"id": "41314d52d14f7623",
"type": "http request",
"z": "c0bb5756099d6dbc",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "http://192.168.1.67/",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 150,
"y": 180,
"wires": [
[
"2fb863c6f3188fd2"
]
]
},
{
"id": "2fb863c6f3188fd2",
"type": "function",
"z": "c0bb5756099d6dbc",
"name": "Parse ESP32 Temp",
"func": "var payload = msg.payload;\nvar match = payload.match(/Temperature is: ([0-9.]+)/);\n\nif (match) {\n msg.payload = parseFloat(match[1]); // Fahrenheit\n msg.topic = \"ESP32\"; // Add this line\n} else {\n msg.payload = null;\n}\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 430,
"y": 180,
"wires": [
[
"ab11f8582b84df82",
"e024d71190743b50",
"e146810a6d814e42"
]
]
},
{
"id": "ab11f8582b84df82",
"type": "debug",
"z": "c0bb5756099d6dbc",
"name": "ESP32 TempF",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 880,
"y": 180,
"wires": []
},
{
"id": "e024d71190743b50",
"type": "ui_gauge",
"z": "c0bb5756099d6dbc",
"name": "",
"group": "7",
"order": 5,
"width": 5,
"height": 4,
"gtype": "donut",
"title": "ESP32 (°F)",
"label": "°F",
"format": "{{value}}",
"min": "80",
"max": "110",
"colors": [
"#00b500",
"#e6e600",
"#ff0000"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 870,
"y": 220,
"wires": []
},
{
"id": "7",
"type": "ui_group",
"name": "RasPI-3B",
"tab": "6",
"order": 1,
"disp": true,
"width": 16,
"collapse": false,
"className": ""
},
{
"id": "6",
"type": "ui_tab",
"name": "Home",
"icon": "dashboard",
"order": 1
}
]
-----
And here is the Arduino Sketch for the ESP32C3:
// ESP32-C3 Dev Module + onboard OLED
// thermometer w/ DS18B20 data pin connected to GPIO 4
//
// OLED: Fahrenheit only (1 decimal place, no units)
// Serial Monitor: Celsius + Fahrenheit
// Web page: latest calibrated Fahrenheit reading with timestamp
//
// https://www.whiskeytangohotel.com/
// SEPT 2025
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFi.h>
#include "time.h"
#include <WebServer.h>
// WiFi credentials
const char* ssid = "urssid";
const char* password = "urwifipasswrd";
// OLED setup
U8G2_SSD1306_72X40_ER_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
// Global counter
int readingCount = 0;
// DS18B20 setup
#define ONE_WIRE_BUS 4
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// Timezone
const char* ntpServer = "pool.ntp.org";
// Latest reading
float latestTempF = 0;
time_t latestTime = 0;
// Web server
WebServer server(80);
void handleRoot() {
char timeBuf[30];
struct tm *tm_info = localtime(&latestTime);
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", tm_info);
String html = "<html><head><title>ESP32-C3 Temp</title></head><body><pre>";
html += timeBuf;
html += " - Temperature is: ";
html += String(latestTempF, 1);
html += "</pre></body></html>";
server.send(200, "text/html", html);
}
void setup() {
// I2C pins for ESP32-C3 OLED dev board
Wire.begin(5, 6);
Wire.setClock(100000);
delay(200);
u8g2.begin();
Serial.begin(115200);
delay(200);
sensors.begin();
// Startup screen
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x10_tr);
u8g2.drawStr(0, 15, "Temp Monitor");
u8g2.sendBuffer();
delay(2000);
// Connect to WiFi
WiFi.begin(ssid, password);
u8g2.clearBuffer();
u8g2.drawStr(0, 15, "Connecting WiFi...");
u8g2.sendBuffer();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
// NTP
configTzTime("CST6CDT,M3.2.0/2,M11.1.0/2", ntpServer);
// Start server
server.on("/", handleRoot);
server.begin();
Serial.print("HTTP server started at: ");
Serial.println(WiFi.localIP());
}
void loop() {
sensors.requestTemperatures();
float tempC = sensors.getTempCByIndex(0);
float tempF = tempC * 9.0 / 5.0 + 32.0;
float calibrationOffsetF = 0.0;
tempF += calibrationOffsetF;
// Save latest reading
time(&latestTime);
latestTempF = tempF;
// Serial output
//readingCount++; // If reading count is desired
//Serial.print("#");
//Serial.print(readingCount);
char timeBuf[30];
struct tm *tm_info = localtime(&latestTime);
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", tm_info);
Serial.print(timeBuf);
Serial.print(" > Temperature is: ");
Serial.print(tempC);
Serial.print("°C | ");
Serial.print(tempF);
Serial.println("°F");
// OLED output
char buf[10];
snprintf(buf, sizeof(buf), "%.1f", tempF);
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_fur20_tf);
u8g2.drawStr(0, 30, buf);
u8g2.sendBuffer();
server.handleClient(); // handle web requests
delay(5000); // delay until next reading
}
-----