Temperature and Humidity Sensor Node Design

I’ll build the sensor node schematic end-to-end: choose the radio MCU, size the USB-C power path and protection, add the T/RH sensor and support passives, then wire and verify it.
I’ve narrowed the architecture: ESP32-C3 module, SHT41 sensor, protected USB-C input, and a buck regulator to stay cool under Wi-Fi bursts. I’m verifying datasheet details and sizing the power path now.
Architecture is locked and documented. I’m adding the full component set now in functional blocks, then I’ll wire power, USB, MCU support, and the sensor bus.
Done: the USB-C Wi‑Fi/BLE environmental sensor node schematic is built, wired, and ERC-clean. I also added the supporting project files: Project Specification, Design Notes, and Firmware Starter — ESP32-C3-MINI-1-N4.
What’s in place: protected USB-C 5 V input with CC sink resistors, TVS + eFuse protection, 3.3 V buck rail, ESP32-C3 native USB, boot/reset circuitry, and SHT41 on I2C. Schematic is ready for layout; want me to go ahead?
Set board size
Run ERC/DRC
Place components
Add enclosure features
Review BOM costs

Refine this doc
Ask about this doc
Firmware Starter — ESP32-C3-MINI-1-N4
Platform & Toolchain
  • MCU: ESP32-C3-MINI-1-N4
  • Framework: Arduino on ESP32
  • Build system: PlatformIO
  • Core libraries used: WiFi.h, Wire.h
  • External sensor library requirement: none; the SHT41 is accessed directly over I2C
Pin Mapping

Table


FunctionGPIO/PinConnected ToDirectionNotes
USB D-GPIO18J1 USB D- through D1 ESDBidirectionalNative USB data pair on ESP32-C3
USB D+GPIO19J1 USB D+ through D1 ESDBidirectionalNative USB data pair on ESP32-C3
I2C SDAGPIO4U2 SDABidirectionalShared with R12 4.7 kOhm pull-up to 3V3
I2C SCLGPIO5U2 SCLOutput/Open-drain busShared with R13 4.7 kOhm pull-up to 3V3
EN / ResetEN pinR10 pull-up, C6 filter, S1 reset switchControl inputHardware reset path
Boot strapGPIO9R11 pull-up, S2 boot switchInput during resetPull low during reset for download mode
3V3 supply3V3Buck output railPowerMain logic rail
Dependencies & Project Setup
Create a new PlatformIO project with this platformio.ini:

Ini


[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
build_flags =
    -DARDUINO_USB_MODE=1
    -DARDUINO_USB_CDC_ON_BOOT=1
Complete Firmware Source
Save as src/main.cpp:

Cpp


#include <Arduino.h>
#include <WiFi.h>
#include <Wire.h>

// ---------------- Pin definitions from schematic ----------------
static constexpr int I2C_SDA_PIN = 4;   // U1 IO4 -> U2 SDA
static constexpr int I2C_SCL_PIN = 5;   // U1 IO5 -> U2 SCL

// ---------------- SHT41 configuration ----------------
static constexpr uint8_t SHT41_I2C_ADDR = 0x44;
static constexpr uint8_t SHT41_CMD_MEASURE_HIGH_PRECISION = 0xFD;
static constexpr uint32_t SENSOR_PERIOD_MS = 10000;

// ---------------- Wi-Fi placeholders ----------------
static const char* WIFI_SSID = "YOUR_WIFI_SSID";
static const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
static constexpr uint32_t WIFI_RETRY_INTERVAL_MS = 10000;

struct Sht41Reading {
    float temperatureC;
    float humidityRH;
    bool valid;
};

unsigned long lastSensorReadMs = 0;
unsigned long lastWifiAttemptMs = 0;

static uint8_t crc8Sensirion(const uint8_t* data, size_t len) {
    uint8_t crc = 0xFF;
    for (size_t i = 0; i < len; ++i) {
        crc ^= data[i];
        for (int bit = 0; bit < 8; ++bit) {
            if (crc & 0x80) {
                crc = static_cast<uint8_t>((crc << 1) ^ 0x31);
            } else {
                crc <<= 1;
            }
        }
    }
    return crc;
}

static bool sht41TriggerMeasurement() {
    Wire.beginTransmission(SHT41_I2C_ADDR);
    Wire.write(SHT41_CMD_MEASURE_HIGH_PRECISION);
    uint8_t result = Wire.endTransmission();
    return result == 0;
}

static Sht41Reading sht41ReadTemperatureHumidity() {
    Sht41Reading reading = {NAN, NAN, false};

    if (!sht41TriggerMeasurement()) {
        Serial.println("SHT41: failed to send measurement command");
        return reading;
    }

    delay(10);  // Datasheet quick-start uses 10 ms wait for 0xFD measurement

    constexpr uint8_t expectedBytes = 6;
    uint8_t rx[expectedBytes] = {0};
    int received = Wire.requestFrom(static_cast<int>(SHT41_I2C_ADDR), static_cast<int>(expectedBytes));
    if (received != expectedBytes) {
        Serial.printf("SHT41: expected %u bytes, got %d\n", expectedBytes, received);
        return reading;
    }

    for (uint8_t i = 0; i < expectedBytes; ++i) {
        rx[i] = Wire.read();
    }

    if (crc8Sensirion(&rx[0], 2) != rx[2]) {
        Serial.println("SHT41: temperature CRC mismatch");
        return reading;
    }
    if (crc8Sensirion(&rx[3], 2) != rx[5]) {
        Serial.println("SHT41: humidity CRC mismatch");
        return reading;
    }

    uint16_t rawTemp = static_cast<uint16_t>((rx[0] << 8) | rx[1]);
    uint16_t rawRh = static_cast<uint16_t>((rx[3] << 8) | rx[4]);

    reading.temperatureC = -45.0f + 175.0f * (static_cast<float>(rawTemp) / 65535.0f);
    reading.humidityRH = -6.0f + 125.0f * (static_cast<float>(rawRh) / 65535.0f);

    if (reading.humidityRH > 100.0f) reading.humidityRH = 100.0f;
    if (reading.humidityRH < 0.0f) reading.humidityRH = 0.0f;

    reading.valid = true;
    return reading;
}

static void initI2C() {
    Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
    Wire.setClock(400000);
    Serial.println("I2C initialized on GPIO4/GPIO5");
}

static void initWiFiIfConfigured() {
    if (strlen(WIFI_SSID) == 0 || strcmp(WIFI_SSID, "YOUR_WIFI_SSID") == 0) {
        Serial.println("Wi-Fi credentials not configured; running in local-only mode");
        return;
    }

    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.printf("Connecting to Wi-Fi SSID '%s'", WIFI_SSID);

    int tries = 0;
    while (WiFi.status() != WL_CONNECTED && tries < 20) {
        delay(500);
        Serial.print('.');
        ++tries;
    }
    Serial.println();

    if (WiFi.status() == WL_CONNECTED) {
        Serial.printf("Wi-Fi connected, IP = %s\n", WiFi.localIP().toString().c_str());
    } else {
        Serial.println("Wi-Fi connection failed; continuing offline");
    }
}

static void maintainWiFi() {
    if (strlen(WIFI_SSID) == 0 || strcmp(WIFI_SSID, "YOUR_WIFI_SSID") == 0) {
        return;
    }

    if (WiFi.status() == WL_CONNECTED) {
        return;
    }

    unsigned long now = millis();
    if (now - lastWifiAttemptMs < WIFI_RETRY_INTERVAL_MS) {
        return;
    }

    lastWifiAttemptMs = now;
    Serial.println("Wi-Fi disconnected, retrying...");
    WiFi.disconnect(true, true);
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void setup() {
    Serial.begin(115200);
    delay(300);
    Serial.println();
    Serial.println("ESP32-C3 environmental node starting");

    initI2C();
    initWiFiIfConfigured();

    lastSensorReadMs = millis();
    lastWifiAttemptMs = millis();
}

void loop() {
    maintainWiFi();

    unsigned long now = millis();
    if (now - lastSensorReadMs >= SENSOR_PERIOD_MS) {
        lastSensorReadMs = now;

        Sht41Reading reading = sht41ReadTemperatureHumidity();
        if (!reading.valid) {
            Serial.println("Sensor read failed");
            return;
        }

        Serial.printf("Temperature: %.2f C, Humidity: %.2f %%RH\n",
                      reading.temperatureC,
                      reading.humidityRH);

        // Placeholder for future cloud reporting:
        // - HTTP POST to a REST endpoint
        // - MQTT publish to a broker
        // - BLE GATT characteristic update
        if (WiFi.status() == WL_CONNECTED) {
            Serial.println("Wi-Fi is connected; add your telemetry upload here");
        }
    }
}
Build & Flash Instructions
  • Create the project: pio project init --board esp32-c3-devkitm-1
  • Build: pio run
  • Flash: pio run -t upload
  • Serial monitor: pio device monitor -b 115200
Hardware Bring-up Notes
  • Normal boot: leave BOOT button S2 released and tap RESET S1 if needed.
  • Download mode: hold BOOT S2, tap RESET S1, then release BOOT after the upload starts.
  • The board uses the ESP32-C3 native USB data pins (GPIO18/GPIO19) routed to the USB-C connector through ESD protection.
  • If USB upload fails, verify the board is receiving 3V3 from the buck converter and the ESP_EN net is high.

    Platform & Toolchain

    Pin Mapping

    Dependencies & Project Setup

    Complete Firmware Source

    Build & Flash Instructions

    Hardware Bring-up Notes

Documents

    Project Specification

    Design Notes

    Firmware Starter — ESP32-C3-MINI-1-N4

Assets

No assets added.

USB-C Wi-Fi/BLE Environmental Sensor Node

USB-C Wi-Fi/BLE Environmental Sensor Node thumbnail
USB-C powered consumer environmental node with an ESP32-C3 Wi-Fi/BLE module, protected 5 V USB-C input, efficient 3.3 V regulation, and a digital temperature/humidity sensor.

Properties

Properties describe core aspects of the project.

Pricing & Availability

Distributor

Qty 1

Arrow

$4.01–$6.37

Digi-Key

$3.19–$4.05

HQonline

$2.35–$2.39

LCSC

$11.31–$11.41

Mouser

$8.79–$8.99

TME

$3.35–$3.48

Verical

$3.54–$7.13

Controls