"""
1. How the Gas Sensor Works
πΈ The MQ-2 sensor detect gases like:
- Methane (CHβ)
- Propane (CβHβ)
- LPG
- Smoke
- COβ, NHβ, Alcohol etc.
π§ Working Principle
- Inside the sensor is a heater coil and a semiconductor material (SnOβ) whose resistance changes in the presence of gases.
- When gas concentration increases, the resistance drops, which changes the voltage output on the AO pin (Analog Output).
π§ Circuit Configuration
The gas sensor has:
- VCC: Power (Often 3.3V can be used for some sensors with reduced range)
- GND: Ground
- AO: Analog Output β Connects to ADC pin on Raspberry Pi Pico
β The AO pin is connected in a voltage divider configuration inside the sensor.
β As gas concentration changes, so does the voltage on AO.
- DOUT: Normally Not connected in many applications
β Digital output (HIGH/LOW if threshold crossed based on an Internal Trimpot/Potentiometer)
π Output Behavior
- Clean air β Higher resistance β Lower voltage
- Gas presence β Lower resistance β Higher voltage
- You read this analog voltage with ADC.read_u16() and convert it to a float voltage using:
β V = (ADC_value / 65535 ) x 3.3
===================================================================================================================
π₯οΈ 2. How the I2C LCD Works
πΈ What Is an I2C LCD?
It's a regular 16x2 character LCD (based on the HD44780 chip) with an I2C adapter board (usually a PCF8574 I/O expander) soldered at the back.
π§ How I2C LCD Displays Data
- The I2C LCD driver chip (PCF8574) receives commands over I2C.
- It converts I2C messages to parallel commands for the actual LCD controller (HD44780).
- You send strings like lcd.putstr("Hello"), and the display lights up each character in the grid of 16 columns x 2 rows.
π§ Wiring with I2C
- Instead of using 6-8 GPIO pins (as with parallel LCDs), this LCD uses just 2 lines:
β SDA: Serial Data (e.g., GP0)
β SCL: Serial Clock (e.g., GP1)
- These are IΒ²C lines that let the Pi-Pico talk to the display using only 2 pins, leaving others free for sensors.
π§ͺ Communication Flow
MicroPython Code
β
βΌ
Pi Pico I2C (GP0, GP1)
β
βΌ
PCF8574 (I2C Expander on LCD)\
β \______ Requires i2c_lcd.py and lcd_api.py files as libraries
βΌ /
HD44780 LCD Controller ______/
β
βΌ
Characters appear on screen!
"""
from machine import ADC, Pin, I2C # Import hardware-related classes: ADC for analog input, Pin for GPIO, I2C for I2C communication
from i2c_lcd import I2cLcd # Import the custom I2C LCD class from your i2c_lcd.py file
import time # Import time module for using delays
# --- LCD Configuration ---
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000) # Initialize I2C interface (I2C0) on GP0 (SDA) and GP1 (SCL) with 400kHz frequency
lcd = I2cLcd(i2c, 0x27, 2, 16) # Initialize the 16x2 I2C LCD display at address 0x27 (2 rows, 16 columns)
# --- Gas Sensor Configuration ---
gas_sensor = ADC(26) # Set up ADC on GP26 (ADC0) to read analog voltage from gas sensor
VREF = 3.3 # Reference voltage (3.3V on Pi Pico)
ADC_RES = 65535 # Maximum ADC reading for 16-bit resolution (0 to 65535)
DOLLAR_RATE = 2.50 # Cost rate per volt: $2.50 per volt of gas reading
# --- Display Startup Message ---
lcd.clear() # Clear the LCD screen
lcd.putstr("Gas Sensor") # Print "Gas Sensor" on the top row
lcd.move_to(0, 1) # Move cursor to beginning of second row
lcd.putstr("Meter Reading") # Print "Meter Reading" on the second row
time.sleep(2) # Wait for 2 seconds to show startup message
while True:
# Read analog value and convert to voltage
raw_value = gas_sensor.read_u16() # Read raw 16-bit ADC value from gas sensor (0β65535)
print(raw_value) # Print raw value to serial terminal for debugging
voltage = (raw_value / ADC_RES) * VREF # Convert raw ADC value to actual voltage (0.0V to 3.3V)
bill = voltage * DOLLAR_RATE # Calculate billing amount based on voltage
# Display on LCD
lcd.clear() # Clear LCD screen
lcd.move_to(0, 0) # Move to beginning of first row
lcd.putstr("Raw: {}".format(raw_value)) # Show raw ADC value
lcd.move_to(0, 1) # Move to beginning of second row
lcd.putstr("Voltage: {:.1f} V".format(voltage)) # Show measured voltage (rounded to 1 decimal)
time.sleep(2) # Wait 1 second before switching screen
lcd.clear() # Clear LCD again for new screen
lcd.move_to(0, 0) # Move to top row
lcd.putstr("Bill Charge:") # Display label for billing
lcd.move_to(0, 1) # Move to second row
lcd.putstr("$ {:.4f}".format(bill)) # Display calculated billing amount (rounded to 4 decimal places)
time.sleep(2) # Wait 1 second before repeating the loop