diff --git a/examples/dht22_example/dht22_example.ino b/examples/dht22_example/dht22_example.ino new file mode 100644 index 0000000..b307c17 --- /dev/null +++ b/examples/dht22_example/dht22_example.ino @@ -0,0 +1,97 @@ +/** + * DHT22 Example - Basic Temperature and Humidity Reading + * + * Demonstrates basic usage of DHT22Driver with error handling. + */ +#include +#include + +// Initialize DHT22 on GPIO 4 +// Default: 3 retries with 2-second delay +DHT22Driver sensor(4); + +void setup() { + Serial.begin(115200); + delay(100); + + Serial.println("========================================"); + Serial.println("🌡️ DHT22 Example - MicroClaw"); + Serial.println("========================================"); + + // Initialize sensor + sensor.begin(); + + Serial.println("✅ Sensor initialized"); + Serial.println("⏳ Waiting 2 seconds for sensor warmup..."); + delay(2000); +} + +void loop() { + Serial.println("\n--- Reading DHT22 ---"); + + // Method 1: Read temperature and humidity separately + float temp = sensor.readTemperature(); + float humidity = sensor.readHumidity(); + + if (sensor.isOK()) { + Serial.printf("🌡️ Temperature: %.1f°C (%.1f°F)\n", temp, temp * 9.0/5.0 + 32.0); + Serial.printf("💧 Humidity: %.1f%%\n", humidity); + + // Calculate heat index (feels-like temperature) + float heatIndex = computeHeatIndex(temp, humidity); + Serial.printf("🔥 Heat Index: %.1f°C\n", heatIndex); + + // Comfort level + String comfort = getComfortLevel(temp, humidity); + Serial.printf("😊 Comfort: %s\n", comfort.c_str()); + } else { + Serial.println("❌ Error: " + sensor.getError()); + } + + // Method 2: Read as JSON (for MQTT/HTTP reporting) + Serial.println("\n--- JSON Output ---"); + String json = sensor.readJSON(); + Serial.println(json); + + // Wait 5 seconds before next reading + // DHT22 sensor needs 2+ seconds between reads + delay(5000); +} + +/** + * Compute heat index (feels-like temperature) + * Simplified Steadman formula + */ +float computeHeatIndex(float temp, float humidity) { + float T = temp; + float RH = humidity; + + float HI = -8.78469475556 + + 1.61139411 * T + + 2.33854883889 * RH + + -0.14611605 * T * RH + + -0.012308094 * T * T + + -0.0164248277778 * RH * RH + + 0.002211732 * T * T * RH + + 0.00072546 * T * RH * RH + + -0.000003582 * T * T * RH * RH; + + return HI; +} + +/** + * Get comfort level based on temperature and humidity + */ +String getComfortLevel(float temp, float humidity) { + if (temp < 15.0) { + return "Cold"; + } else if (temp > 30.0) { + return "Hot"; + } else if (humidity < 30.0) { + return "Dry"; + } else if (humidity > 70.0) { + return "Humid"; + } else { + return "Comfortable"; + } +} diff --git a/lib/DHT22Driver/DHT22Driver.cpp b/lib/DHT22Driver/DHT22Driver.cpp new file mode 100644 index 0000000..e1d3f08 --- /dev/null +++ b/lib/DHT22Driver/DHT22Driver.cpp @@ -0,0 +1,125 @@ +#include "DHT22Driver.h" + +DHT22Driver::DHT22Driver(uint8_t pin, uint8_t retries, uint16_t retryDelay) + : dht(pin, DHT22), pin(pin), maxRetries(retries), retryDelay(retryDelay), lastReadOK(false), lastError("") { +} + +void DHT22Driver::begin() { + dht.begin(); + lastReadOK = false; + lastError = ""; +} + +float DHT22Driver::readTemperature() { + float temp, humidity; + if (readWithRetry(temp, humidity)) { + return temp; + } + return NAN; +} + +float DHT22Driver::readTemperatureF() { + float temp = readTemperature(); + if (!isnan(temp)) { + return temp * 9.0 / 5.0 + 32.0; + } + return NAN; +} + +float DHT22Driver::readHumidity() { + float temp, humidity; + if (readWithRetry(temp, humidity)) { + return humidity; + } + return NAN; +} + +bool DHT22Driver::read(float &temp, float &humidity) { + return readWithRetry(temp, humidity); +} + +String DHT22Driver::readJSON() { + float temp, humidity; + + if (readWithRetry(temp, humidity)) { + String json = "{"; + json += "\"temperature\":" + String(temp, 1) + ","; + json += "\"humidity\":" + String(humidity, 1) + ","; + json += "\"status\":\"ok\""; + json += "}"; + return json; + } else { + String json = "{"; + json += "\"error\":\"" + lastError + "\","; + json += "\"status\":\"error\""; + json += "}"; + return json; + } +} + +bool DHT22Driver::isOK() { + return lastReadOK; +} + +String DHT22Driver::getError() { + return lastError; +} + +bool DHT22Driver::readWithRetry(float &temp, float &humidity) { + for (uint8_t attempt = 0; attempt < maxRetries; attempt++) { + // Read from sensor + humidity = dht.readHumidity(); + temp = dht.readTemperature(); + + // Check if read succeeded + if (!isnan(humidity) && !isnan(temp)) { + // Validate reasonable ranges + if (temp < -40.0 || temp > 80.0) { + lastError = "Temperature out of range: " + String(temp, 1) + "C"; + lastReadOK = false; + + if (attempt < maxRetries - 1) { + delay(retryDelay); + continue; + } + return false; + } + + if (humidity < 0.0 || humidity > 100.0) { + lastError = "Humidity out of range: " + String(humidity, 1) + "%"; + lastReadOK = false; + + if (attempt < maxRetries - 1) { + delay(retryDelay); + continue; + } + return false; + } + + // Success! + lastReadOK = true; + lastError = ""; + return true; + } + + // Read failed, set error message + if (isnan(humidity) && isnan(temp)) { + lastError = "Sensor read failed (both values NaN)"; + } else if (isnan(humidity)) { + lastError = "Humidity read failed (NaN)"; + } else { + lastError = "Temperature read failed (NaN)"; + } + + lastReadOK = false; + + // Retry if not last attempt + if (attempt < maxRetries - 1) { + delay(retryDelay); + } + } + + // All retries exhausted + lastError += " after " + String(maxRetries) + " attempts"; + return false; +} diff --git a/lib/DHT22Driver/DHT22Driver.h b/lib/DHT22Driver/DHT22Driver.h new file mode 100644 index 0000000..86ea52a --- /dev/null +++ b/lib/DHT22Driver/DHT22Driver.h @@ -0,0 +1,101 @@ +#ifndef DHT22DRIVER_H +#define DHT22DRIVER_H + +#include +#include + +/** + * DHT22Driver - Temperature and Humidity Sensor Driver + * + * Wrapper around Adafruit DHT library with error handling, + * JSON output, and configurable retry logic. + * + * Usage: + * DHT22Driver sensor(4); // GPIO 4 + * sensor.begin(); + * + * float temp = sensor.readTemperature(); + * float humidity = sensor.readHumidity(); + * + * String json = sensor.readJSON(); + */ +class DHT22Driver { +public: + /** + * Constructor + * @param pin GPIO pin connected to DHT22 DATA pin + * @param retries Number of read retries on failure (default: 3) + * @param retryDelay Delay between retries in ms (default: 2000) + */ + DHT22Driver(uint8_t pin, uint8_t retries = 3, uint16_t retryDelay = 2000); + + /** + * Initialize the sensor + * Call this in setup() + */ + void begin(); + + /** + * Read temperature in Celsius + * @return Temperature or NaN on error + */ + float readTemperature(); + + /** + * Read temperature in Fahrenheit + * @return Temperature or NaN on error + */ + float readTemperatureF(); + + /** + * Read relative humidity (%) + * @return Humidity or NaN on error + */ + float readHumidity(); + + /** + * Read temperature and humidity together (more efficient) + * @param temp Output parameter for temperature (C) + * @param humidity Output parameter for humidity (%) + * @return true on success, false on error + */ + bool read(float &temp, float &humidity); + + /** + * Read temperature and humidity and return as JSON + * Format: {"temperature":25.0,"humidity":60.0,"status":"ok"} + * or: {"error":"Sensor read failed","status":"error"} + * @return JSON string + */ + String readJSON(); + + /** + * Check if last read was successful + * @return true if last read succeeded + */ + bool isOK(); + + /** + * Get error message from last failed read + * @return Error message or empty string if no error + */ + String getError(); + +private: + DHT dht; + uint8_t pin; + uint8_t maxRetries; + uint16_t retryDelay; + bool lastReadOK; + String lastError; + + /** + * Internal read with retry logic + * @param temp Output parameter for temperature + * @param humidity Output parameter for humidity + * @return true on success, false after all retries exhausted + */ + bool readWithRetry(float &temp, float &humidity); +}; + +#endif // DHT22DRIVER_H diff --git a/lib/DHT22Driver/README.md b/lib/DHT22Driver/README.md new file mode 100644 index 0000000..8bd2452 --- /dev/null +++ b/lib/DHT22Driver/README.md @@ -0,0 +1,271 @@ +# DHT22Driver Library + +Production-ready driver for DHT22 temperature and humidity sensor with error handling and JSON output. + +## Features + +- **Automatic retry logic** (configurable retries and delay) +- **Range validation** (temperature: -40°C to 80°C, humidity: 0-100%) +- **JSON output** compatible with PicoClaw/NanoClaw reporting +- **Error messages** for debugging +- **Efficient batch reads** (read temperature and humidity together) + +## Installation + +Already included in MicroClaw! No extra installation needed. + +PlatformIO will automatically discover this library in `lib/DHT22Driver/`. + +## Wiring + +``` +DHT22 Sensor +┌─────────┐ +│ O │ Pin 1: VCC (3.3V) +│ DHT22 │ Pin 2: DATA (to GPIO 4 with 10kΩ pullup) +│ O │ Pin 3: NC (not connected) +│ O │ Pin 4: GND +└─────────┘ + +ESP32 Connection: +3.3V ────────────● VCC + │ +GPIO 4 ──┬────────● DATA + │ + [10kΩ] (pullup to 3.3V) + │ +GND ─────┴────────● GND +``` + +**Important**: 10kΩ pullup resistor required on DATA pin. + +## Usage + +### Basic Example + +```cpp +#include + +// Initialize driver on GPIO 4 +DHT22Driver sensor(4); + +void setup() { + Serial.begin(115200); + sensor.begin(); +} + +void loop() { + // Read temperature and humidity + float temp = sensor.readTemperature(); + float humidity = sensor.readHumidity(); + + if (sensor.isOK()) { + Serial.printf("Temp: %.1f°C, Humidity: %.1f%%\n", temp, humidity); + } else { + Serial.println("Error: " + sensor.getError()); + } + + delay(5000); +} +``` + +### JSON Output (for MQTT/HTTP reporting) + +```cpp +#include + +DHT22Driver sensor(4); + +void setup() { + Serial.begin(115200); + sensor.begin(); +} + +void loop() { + String json = sensor.readJSON(); + Serial.println(json); + // Output: {"temperature":25.0,"humidity":60.0,"status":"ok"} + + delay(5000); +} +``` + +### Efficient Batch Read + +```cpp +#include + +DHT22Driver sensor(4); + +void setup() { + Serial.begin(115200); + sensor.begin(); +} + +void loop() { + float temp, humidity; + + if (sensor.read(temp, humidity)) { + Serial.printf("Temp: %.1f°C, Humidity: %.1f%%\n", temp, humidity); + } else { + Serial.println("Error: " + sensor.getError()); + } + + delay(5000); +} +``` + +### Custom Retry Configuration + +```cpp +#include + +// 5 retries with 1-second delay between attempts +DHT22Driver sensor(4, 5, 1000); + +void setup() { + Serial.begin(115200); + sensor.begin(); +} + +void loop() { + String json = sensor.readJSON(); + Serial.println(json); + + delay(5000); +} +``` + +## API Reference + +### Constructor + +```cpp +DHT22Driver(uint8_t pin, uint8_t retries = 3, uint16_t retryDelay = 2000); +``` + +- `pin`: GPIO pin connected to DHT22 DATA pin +- `retries`: Number of read attempts on failure (default: 3) +- `retryDelay`: Delay between retries in milliseconds (default: 2000) + +### Methods + +#### `void begin()` + +Initialize the sensor. Call once in `setup()`. + +#### `float readTemperature()` + +Read temperature in Celsius. Returns `NaN` on error. + +#### `float readTemperatureF()` + +Read temperature in Fahrenheit. Returns `NaN` on error. + +#### `float readHumidity()` + +Read relative humidity (%). Returns `NaN` on error. + +#### `bool read(float &temp, float &humidity)` + +Read both temperature and humidity in one call (more efficient). + +Returns `true` on success, `false` on error. Output parameters are set on success. + +#### `String readJSON()` + +Read temperature and humidity and return as JSON string. + +**Success:** +```json +{"temperature":25.0,"humidity":60.0,"status":"ok"} +``` + +**Error:** +```json +{"error":"Sensor read failed after 3 attempts","status":"error"} +``` + +#### `bool isOK()` + +Check if last read was successful. + +#### `String getError()` + +Get error message from last failed read. Returns empty string if no error. + +## Error Handling + +The driver validates: +- **NaN values** from sensor +- **Temperature range**: -40°C to 80°C +- **Humidity range**: 0% to 100% + +Error messages: +- `"Sensor read failed (both values NaN)"` — Communication failure +- `"Temperature out of range: XX.XC"` — Invalid reading +- `"Humidity out of range: XX.X%"` — Invalid reading +- `"... after N attempts"` — All retries exhausted + +## Troubleshooting + +### Sensor always returns NaN + +**Causes:** +- Missing 10kΩ pullup resistor on DATA pin +- Wrong GPIO pin +- Loose connection + +**Fix:** +```cpp +// Add debug output +sensor.begin(); +delay(2000); // Wait for sensor to stabilize + +String json = sensor.readJSON(); +Serial.println(json); // Check error message +``` + +### Intermittent failures + +**Causes:** +- Insufficient delay between reads (sensor needs 2+ seconds) +- Electrical noise on DATA line + +**Fix:** +```cpp +// Increase retry delay +DHT22Driver sensor(4, 5, 3000); // 5 retries, 3s delay +``` + +### Values out of range + +**Causes:** +- Faulty sensor +- Extreme environment + +**Fix:** +- Check sensor with multimeter (DATA pin should idle HIGH) +- Replace sensor if consistently fails validation + +## Performance + +- **Read time**: ~250ms per successful read +- **Memory**: ~200 bytes +- **CPU**: Minimal (blocking I/O) + +## Dependencies + +- Adafruit DHT sensor library (installed via `platformio.ini`) + +## License + +Apache License 2.0 (same as MicroClaw) + +## Examples + +See `examples/dht22_example/` for complete working examples. + +--- + +**Built for MicroClaw Issue #2**