From f5af3419d5bdf74c6ccb77d114691bff6df53a51 Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Wed, 7 May 2025 19:28:35 +0200 Subject: [PATCH] dht22 implementation --- README.md | 116 ++++++++++++++++++++++++++++++++ docs/dht22_sensor.md | 127 +++++++++++++++++++++++++++++++++++ examples/dht22_example.py | 105 +++++++++++++++++++++++++++++ src/esp_sensors/dht22.py | 133 +++++++++++++++++++++++++++++++++++++ tests/test_dht22_sensor.py | 100 ++++++++++++++++++++++++++++ 5 files changed, 581 insertions(+) create mode 100644 README.md create mode 100644 docs/dht22_sensor.md create mode 100644 examples/dht22_example.py create mode 100644 src/esp_sensors/dht22.py create mode 100644 tests/test_dht22_sensor.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..71a1a30 --- /dev/null +++ b/README.md @@ -0,0 +1,116 @@ +# ESP Sensors + +A Python library for interfacing with various sensors on ESP32/ESP8266 microcontrollers using MicroPython. + +## Overview + +This library provides a simple and consistent interface for working with different types of sensors connected to ESP32/ESP8266 microcontrollers. It includes both real hardware implementations and simulation capabilities for testing without physical hardware. + +## Features + +- Base sensor class with common functionality +- Temperature sensor implementation +- DHT22 temperature and humidity sensor implementation +- Simulation mode for testing without hardware +- Comprehensive test suite +- Example scripts for each sensor type + +## Installation + +1. Install MicroPython on your ESP32/ESP8266 device +2. Clone this repository: + ```bash + git clone https://github.com/yourusername/esp-sensors.git + cd esp-sensors + ``` +3. Install dependencies: + ```bash + pip install -r requirements.txt + ``` + +## Usage + +### Temperature Sensor + +```python +from src.esp_sensors.temperature import TemperatureSensor + +# Initialize a temperature sensor on GPIO pin 5 +sensor = TemperatureSensor("room_temp", 5, unit="C") + +# Read temperature +temp = sensor.read() +print(f"Temperature: {temp}°C") + +# Convert to Fahrenheit +temp_f = sensor.to_fahrenheit() +print(f"Temperature: {temp_f}°F") +``` + +### DHT22 Sensor + +```python +from src.esp_sensors.dht22 import DHT22Sensor + +# Initialize a DHT22 sensor on GPIO pin 4 +sensor = DHT22Sensor("living_room", 4) + +# Read temperature +temp = sensor.read() +print(f"Temperature: {temp}°C") + +# Read humidity +humidity = sensor.read_humidity() +print(f"Humidity: {humidity}%") +``` + +## Available Sensors + +| Sensor | Description | Features | +|--------|-------------|----------| +| Temperature | Basic temperature sensor | Temperature in C/F | +| DHT22 | Digital temperature and humidity sensor | Temperature in C/F, Humidity % | + +## Examples + +Example scripts are provided in the `examples/` directory: + +- `examples/dht22_example.py`: Demonstrates how to use the DHT22 sensor + +## Documentation + +Detailed documentation for each sensor is available in the `docs/` directory: + +- [DHT22 Sensor Documentation](docs/dht22_sensor.md) + +## Development + +### Setup Development Environment + +```bash +python -m venv .venv +source .venv/bin/activate # On Windows: .venv\Scripts\activate +pip install -r requirements.txt +``` + +### Running Tests + +```bash +python -m pytest +``` + +### Code Style + +This project uses Black for code formatting: + +```bash +black . +``` + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. \ No newline at end of file diff --git a/docs/dht22_sensor.md b/docs/dht22_sensor.md new file mode 100644 index 0000000..401f9af --- /dev/null +++ b/docs/dht22_sensor.md @@ -0,0 +1,127 @@ +# DHT22 Sensor Implementation for ESP32 + +This document provides information about the DHT22 sensor implementation for ESP32 microcontrollers in this project. + +## Overview + +The DHT22 (also known as AM2302) is a digital temperature and humidity sensor that provides high-precision temperature and humidity readings. This implementation allows you to easily integrate DHT22 sensors with your ESP32 projects using MicroPython. + +## Features + +- Temperature readings in Celsius or Fahrenheit +- Humidity readings as percentage +- Automatic detection of simulation vs. hardware mode +- Error handling for sensor reading failures +- Temperature unit conversion methods +- Comprehensive metadata including sensor type and last readings + +## Hardware Requirements + +- ESP32 microcontroller +- DHT22/AM2302 temperature and humidity sensor +- MicroPython firmware installed on the ESP32 +- Appropriate wiring: + - Connect VCC to 3.3V or 5V power supply + - Connect GND to ground + - Connect DATA to a GPIO pin (default is GPIO4) + - A 10K pull-up resistor between VCC and DATA is recommended + +## Software Dependencies + +- MicroPython with `dht` and `machine` modules +- For simulation mode, only the standard Python libraries are required + +## Usage + +### Basic Usage + +```python +from src.esp_sensors.dht22 import DHT22Sensor + +# Initialize the sensor +sensor = DHT22Sensor("living_room", 4) # name, GPIO pin + +# Read temperature +temperature = sensor.read() +print(f"Temperature: {temperature}°C") + +# Read humidity +humidity = sensor.read_humidity() +print(f"Humidity: {humidity}%") + +# Get temperature in Fahrenheit +fahrenheit = sensor.to_fahrenheit() +print(f"Temperature: {fahrenheit}°F") +``` + +### Advanced Usage + +```python +from src.esp_sensors.dht22 import DHT22Sensor +import time + +# Initialize with custom parameters +sensor = DHT22Sensor( + name="outdoor", + pin=5, + interval=30, # Read every 30 seconds + unit="F" # Use Fahrenheit +) + +# Continuous reading +try: + while True: + temp = sensor.read() + humidity = sensor.read_humidity() + + # Get metadata + metadata = sensor.get_metadata() + + print(f"Sensor: {metadata['name']}") + print(f"Temperature: {temp}°F ({sensor.to_celsius()}°C)") + print(f"Humidity: {humidity}%") + + time.sleep(metadata['interval']) +except KeyboardInterrupt: + print("Monitoring stopped") +``` + +## API Reference + +### Class: DHT22Sensor + +Extends the base `Sensor` class to provide DHT22-specific functionality. + +#### Constructor + +```python +DHT22Sensor(name: str, pin: int, interval: int = 60, unit: str = "C") +``` + +- **name**: A string identifier for the sensor +- **pin**: The GPIO pin number the sensor is connected to +- **interval**: Reading interval in seconds (default: 60) +- **unit**: Temperature unit, either "C" for Celsius or "F" for Fahrenheit (default: "C") + +#### Methods + +- **read()**: Reads the current temperature and updates humidity +- **read_humidity()**: Returns the current humidity reading +- **to_fahrenheit()**: Converts the last reading to Fahrenheit if it was in Celsius +- **to_celsius()**: Converts the last reading to Celsius if it was in Fahrenheit +- **get_metadata()**: Returns a dictionary with sensor information including temperature unit and humidity + +## Example + +See the `examples/dht22_example.py` file for a complete example of how to use the DHT22 sensor with an ESP32. + +## Troubleshooting + +- **No readings or errors**: Check your wiring and ensure the DHT22 is properly connected +- **Inconsistent readings**: Make sure you have a pull-up resistor between VCC and DATA +- **ImportError**: Ensure you're running on MicroPython with the required modules +- **ValueError**: Check that you're using a valid temperature unit ("C" or "F") + +## License + +This implementation is provided under the same license as the main project. \ No newline at end of file diff --git a/examples/dht22_example.py b/examples/dht22_example.py new file mode 100644 index 0000000..3595fe0 --- /dev/null +++ b/examples/dht22_example.py @@ -0,0 +1,105 @@ +""" +Example script for using the DHT22 sensor with an ESP32. + +This script demonstrates how to initialize and read from a DHT22 sensor +connected to an ESP32 microcontroller. + +Usage: + - Upload this script to your ESP32 running MicroPython + - Connect the DHT22 sensor to the specified GPIO pin + - The script will read temperature and humidity at the specified interval +""" +import time +import sys + +# Check if running on MicroPython +if sys.implementation.name == 'micropython': + from src.esp_sensors.dht22 import DHT22Sensor + + # Configuration + SENSOR_NAME = "living_room" + SENSOR_PIN = 4 # GPIO pin where DHT22 is connected + READ_INTERVAL = 5 # seconds between readings + + def main(): + print(f"Initializing DHT22 sensor on pin {SENSOR_PIN}") + + # Initialize the sensor + sensor = DHT22Sensor(SENSOR_NAME, SENSOR_PIN, READ_INTERVAL) + + print("Starting sensor readings. Press Ctrl+C to stop.") + + try: + while True: + # Read temperature + temperature = sensor.read() + # Read humidity + humidity = sensor.read_humidity() + + # Get the current timestamp + timestamp = time.time() + + # Print readings + print(f"Time: {timestamp}") + print(f"Temperature: {temperature}°C ({sensor.to_fahrenheit()}°F)") + print(f"Humidity: {humidity}%") + print("-" * 30) + + # Wait for the next reading + time.sleep(READ_INTERVAL) + + except KeyboardInterrupt: + print("Sensor readings stopped.") + except Exception as e: + print(f"Error: {e}") + + if __name__ == "__main__": + main() +else: + print("This script is designed to run on MicroPython on an ESP32.") + print("Running in simulation mode for demonstration purposes.") + + # Import for simulation mode + from src.esp_sensors.dht22 import DHT22Sensor + + # Configuration + SENSOR_NAME = "simulation" + SENSOR_PIN = 4 + READ_INTERVAL = 2 # shorter interval for demonstration + + def main(): + print(f"Initializing DHT22 sensor simulation") + + # Initialize the sensor + sensor = DHT22Sensor(SENSOR_NAME, SENSOR_PIN, READ_INTERVAL) + + print("Starting simulated sensor readings. Press Ctrl+C to stop.") + + try: + for _ in range(5): # Just do 5 readings for the simulation + # Read temperature + temperature = sensor.read() + # Read humidity + humidity = sensor.read_humidity() + + # Get the current timestamp + timestamp = time.time() + + # Print readings + print(f"Time: {timestamp}") + print(f"Temperature: {temperature}°C ({sensor.to_fahrenheit()}°F)") + print(f"Humidity: {humidity}%") + print("-" * 30) + + # Wait for the next reading + time.sleep(READ_INTERVAL) + + print("Simulation complete.") + + except KeyboardInterrupt: + print("Sensor readings stopped.") + except Exception as e: + print(f"Error: {e}") + + if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/esp_sensors/dht22.py b/src/esp_sensors/dht22.py new file mode 100644 index 0000000..3021368 --- /dev/null +++ b/src/esp_sensors/dht22.py @@ -0,0 +1,133 @@ +""" +DHT22 temperature and humidity sensor module for ESP32. +""" +import time +try: + import dht + from machine import Pin + SIMULATION = False +except ImportError: + import random + SIMULATION = True + +from .sensor import Sensor + + +class DHT22Sensor(Sensor): + """DHT22 temperature and humidity sensor implementation.""" + + def __init__(self, name: str, pin: int, interval: int = 60, unit: str = "C"): + """ + Initialize a new DHT22 sensor. + + Args: + name: The name of the sensor + pin: The GPIO pin number the sensor is connected to + interval: Reading interval in seconds (default: 60) + unit: Temperature unit, either "C" for Celsius or "F" for Fahrenheit (default: "C") + """ + super().__init__(name, pin, interval) + if unit not in ["C", "F"]: + raise ValueError("Unit must be either 'C' or 'F'") + self.unit = unit + self._last_humidity = None + + # Initialize the sensor if not in simulation mode + if not SIMULATION: + self._sensor = dht.DHT22(Pin(pin)) + + def read(self) -> float: + """ + Read the current temperature. + + Returns: + The temperature reading as a float + """ + if SIMULATION: + # Simulate temperature reading + if self.unit == "C": + self._last_reading = round(random.uniform(15.0, 30.0), 1) + else: + self._last_reading = round(random.uniform(59.0, 86.0), 1) + + # Simulate humidity reading (between 30% and 90%) + self._last_humidity = round(random.uniform(30.0, 90.0), 1) + else: + # Actual hardware reading + try: + self._sensor.measure() + temp = self._sensor.temperature() + + # Convert to Fahrenheit if needed + if self.unit == "F": + temp = (temp * 9 / 5) + 32 + + self._last_reading = round(temp, 1) + self._last_humidity = round(self._sensor.humidity(), 1) + except Exception as e: + print(f"Error reading DHT22 sensor: {e}") + # Return last reading if available, otherwise default value + if self._last_reading is None: + self._last_reading = 0.0 + if self._last_humidity is None: + self._last_humidity = 0.0 + + return self._last_reading + + def read_humidity(self) -> float: + """ + Read the current humidity. + + Returns: + The humidity reading as a float (percentage) + """ + # If we haven't read yet, read only humidity + if self._last_humidity is None: + if SIMULATION: + # Simulate humidity reading (between 30% and 90%) + self._last_humidity = round(random.uniform(30.0, 90.0), 1) + else: + # Actual hardware reading + try: + self._sensor.measure() + self._last_humidity = round(self._sensor.humidity(), 1) + except Exception as e: + print(f"Error reading DHT22 humidity: {e}") + # Return default value if no previous reading + self._last_humidity = 0.0 + return self._last_humidity + + def get_metadata(self): + """ + Get sensor metadata including temperature unit and humidity. + + Returns: + A dictionary containing sensor metadata + """ + metadata = super().get_metadata() + metadata["unit"] = self.unit + metadata["last_humidity"] = self._last_humidity + metadata["type"] = "DHT22" + return metadata + + def to_fahrenheit(self) -> float | None: + """ + Convert the last reading to Fahrenheit if it was in Celsius. + + Returns: + The temperature in Fahrenheit + """ + if self.unit == "F" or self._last_reading is None: + return self._last_reading + return (self._last_reading * 9 / 5) + 32 + + def to_celsius(self) -> float | None: + """ + Convert the last reading to Celsius if it was in Fahrenheit. + + Returns: + The temperature in Celsius + """ + if self.unit == "C" or self._last_reading is None: + return self._last_reading + return (self._last_reading - 32) * 5 / 9 diff --git a/tests/test_dht22_sensor.py b/tests/test_dht22_sensor.py new file mode 100644 index 0000000..e2ed0ea --- /dev/null +++ b/tests/test_dht22_sensor.py @@ -0,0 +1,100 @@ +""" +Tests for the DHT22 sensor module. +""" +import pytest +from src.esp_sensors.dht22 import DHT22Sensor + + +def test_dht22_sensor_initialization(): + """Test that a DHT22 sensor can be initialized with valid parameters.""" + sensor = DHT22Sensor("test_sensor", 5, 30, "C") + assert sensor.name == "test_sensor" + assert sensor.pin == 5 + assert sensor.interval == 30 + assert sensor.unit == "C" + assert sensor._last_humidity is None + + +def test_dht22_sensor_invalid_unit(): + """Test that initializing with an invalid unit raises a ValueError.""" + with pytest.raises(ValueError): + DHT22Sensor("test_sensor", 5, 30, "K") + + +def test_dht22_sensor_read_temperature(): + """Test that reading temperature from the sensor returns a float value.""" + sensor = DHT22Sensor("test_sensor", 5) + reading = sensor.read() + assert isinstance(reading, float) + # For Celsius, the reading should be between 15.0 and 30.0 + assert 15.0 <= reading <= 30.0 + + +def test_dht22_sensor_read_humidity(): + """Test that reading humidity from the sensor returns a float value.""" + sensor = DHT22Sensor("test_sensor", 5) + humidity = sensor.read_humidity() + assert isinstance(humidity, float) + # Humidity should be between 30.0% and 90.0% + assert 30.0 <= humidity <= 90.0 + + +def test_dht22_sensor_fahrenheit(): + """Test that a sensor initialized with Fahrenheit returns appropriate values.""" + sensor = DHT22Sensor("test_sensor", 5, unit="F") + reading = sensor.read() + assert isinstance(reading, float) + # For Fahrenheit, the reading should be between 59.0 and 86.0 + assert 59.0 <= reading <= 86.0 + + +def test_dht22_temperature_conversion(): + """Test temperature conversion methods.""" + # Test Celsius to Fahrenheit + c_sensor = DHT22Sensor("celsius_sensor", 5, unit="C") + c_sensor._last_reading = 20.0 # Manually set for testing + f_value = c_sensor.to_fahrenheit() + assert f_value == 68.0 # 20°C = 68°F + + # Test Fahrenheit to Celsius + f_sensor = DHT22Sensor("fahrenheit_sensor", 5, unit="F") + f_sensor._last_reading = 68.0 # Manually set for testing + c_value = f_sensor.to_celsius() + assert c_value == 20.0 # 68°F = 20°C + + +def test_dht22_metadata(): + """Test that metadata includes the temperature unit, humidity, and type.""" + sensor = DHT22Sensor("test_sensor", 5, unit="C") + metadata = sensor.get_metadata() + assert metadata["name"] == "test_sensor" + assert metadata["pin"] == 5 + assert metadata["unit"] == "C" + assert metadata["last_reading"] is None # No reading yet + assert metadata["last_humidity"] is None # No reading yet + assert metadata["type"] == "DHT22" + + # After a reading + sensor.read() + metadata = sensor.get_metadata() + assert metadata["last_reading"] is not None + assert metadata["last_humidity"] is not None + + +def test_dht22_read_updates_both_values(): + """Test that reading temperature also updates humidity.""" + sensor = DHT22Sensor("test_sensor", 5) + assert sensor._last_humidity is None + + # Reading temperature should also update humidity + sensor.read() + assert sensor._last_humidity is not None + + # Reset humidity to test read_humidity + old_temp = sensor._last_reading + sensor._last_humidity = None + + # Reading humidity should not change temperature + humidity = sensor.read_humidity() + assert sensor._last_reading == old_temp + assert humidity is not None \ No newline at end of file