Build Your Own IoT Weather Station: Log Temperature and Humidity to Google Sheets with a Raspberry Pi Pico

Introduction

Imagine turning a tiny Raspberry Pi Pico into your very own internet-connected weather station! In this project, you’ll harness the power of MicroPython to read temperature and humidity data from a DHT11 sensor, optionally display it on a sleek I2C LCD, and beam it straight to a Google Sheet for real-time tracking—all over WiFi. A clever Google Apps Script acts as the bridge, catching data from your Pico and neatly logging it into rows. Whether you’re monitoring your room’s climate, experimenting with IoT, or just geeking out over data, this guide walks you through every step. It’s designed for hobbyists with a bit of experience—like familiarity with MicroPython or Arduino—who want to level up their skills. Let’s dive in and bring your sensor data to life!

Part 1: Hardware Setup on the Raspberry Pi Pico

What You’ll Need

  • Raspberry Pi Pico with MicroPython installed
  • DHT11 temperature and humidity sensor
  • 16x2 I2C LCD (with PCF8574 backpack) — Optional: The LCD can be omitted if you don’t need a local display; the code will still work without it.
  • Breadboard and jumper wires
  • A WiFi network you can connect to

Steps

  1. Install MicroPython on the Pico
    • If you haven’t already, download the MicroPython UF2 file from the official MicroPython site.
    • Hold the BOOTSEL button on the Pico, plug it into your computer via USB, and release BOOTSEL. It’ll appear as a drive.
    • Drag the UF2 file onto the Pico drive. It’ll reboot and run MicroPython.
  2. Connect the Hardware
    • DHT11 Sensor:
      • VCC to 3.3V (Pin 36 on Pico)
      • GND to GND (Pin 38)
      • Data to GP15 (Pin 20)
    • I2C LCD (optional — you can skip this if you don’t want a display; the code handles its absence gracefully):
      • VCC to VSYS (Pin 39) — This provides 5V to power the LCD when the Pico is powered via USB, which is ideal for the LCD’s requirements.
      • GND to GND (Pin 38)
      • SDA to GP0 (Pin 1)
      • SCL to GP1 (Pin 2)
    • Use a breadboard to keep connections tidy. Double-check your wiring against the Pico pinout (search “Pico pinout” online for a diagram).
  3. Test Your Setup
    • You’ll load the code later, but for now, ensure all components are snug and powered. If using the LCD, its backlight should light up when connected correctly.

Part 2: Setting Up Google Sheets

Steps

  1. Create a New Google Sheet
    • Go to sheets.google.com and sign into your Google account.
    • Click “+ Blank” to create a new spreadsheet.
    • Name it something like “Pico Weather Data” by clicking the title at the top-left.
  2. Set Up Headers
    • In the first row (Row 1), add these headers in columns A to C:
    • A1: DateTime
    • B1: Temperature
    • C1: Humidity
    • This matches the data your Pico will send: a single datetime followed by temperature and humidity.
  3. Adjust Sharing Permissions
    • Click the green “Share” button at the top-right.
    • In the “Share with people and groups” section, you won’t add specific people yet (the script handles data via a URL).
    • Instead, click “Change to anyone with the link” under “General access.”
    • Set it to “Viewer” (not “Editor”—the script will write data, not public users).
    • Copy the link by clicking “Copy link,” but you won’t need it for now. Click “Done.”
    • Why? The Apps Script will use a deployment URL to write data, and this setting ensures the sheet is accessible but secure.

Part 3: Setting Up Google Apps Script

Steps

  1. Open the Apps Script Editor
    • In your Google Sheet, click “Extensions” in the top menu, then “Apps Script.”
    • A new tab opens with a blank script editor.
  2. Paste the Google Apps Script Code
    • Delete the default function myFunction() {} code.
    • Copy and paste this code:
    function doGet(e) {
      var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
      var datetime = e.parameter.datetime;
      var temp = e.parameter.temperature;
      var humidity = e.parameter.humidity;
    
      // Append data to the sheet with datetime in the first column
      sheet.appendRow([datetime, temp, humidity]);
    
      return ContentService.createTextOutput("Data received!");
    }
          
    • This script receives a single datetime value along with temperature and humidity from your Pico and logs them into the sheet.
  3. Save and Name the Script
    • Click the floppy disk icon or File > Save.
    • Name it something like “PicoDataLogger.”
  4. Deploy the Script as a Web App
    • Click “Deploy” at the top-right, then “New deployment.”
    • In the dialog:
    • Type: Leave as “Web app.”
    • Description: Optional (e.g., “Logs Pico sensor data”).
    • Execute as: Select “Me (your-email@gmail.com)” (it runs under your account).
    • Who has access: Select “Anyone” (this allows the Pico to send data anonymously via the URL).
    • Click “Deploy.”
    • You’ll see a “Review Permissions” prompt. Click “Authorize access,” then sign in if prompted.
    • If you see “This app isn’t verified,” click “Advanced” > “Go to PicoDataLogger (unsafe)” > “Allow.”
    • After authorization, a “Deployment ID” and “Web app URL” appear. Copy the Web app URL (it looks like https://script.google.com/macros/s/[long-id]/exec). You’ll use this in the secrets.py file on the Pico.
  5. Test the Deployment (Optional)
    • Paste the Web app URL into your browser with test parameters, e.g.:
    • https://script.google.com/macros/s/[long-id]/exec?datetime=03-02-2025%2014:30:00&temperature=72.5&humidity=45
    • Hit Enter. If it works, you’ll see “Data received!” in the browser, and a new row appears in your sheet.

Part 4: Loading Code onto the Raspberry Pi Pico

What You’ll Need

  • A computer with a USB port
  • Thonny IDE (download from thonny.org if not installed)

Steps

  1. Install Thonny and Connect the Pico
    • Open Thonny, plug in your Pico, and select “MicroPython (Raspberry Pi Pico)” from the bottom-right interpreter dropdown.
    • If it connects, you’ll see a prompt like >>> in the shell.
  2. Upload Library Files
    • This project uses four custom libraries: ntptime.py (for time sync), i2c_lcd.py (I2C LCD driver), lcd_api.py (LCD base API), and secrets.py (WiFi credentials and Google Sheets URL). These need to be loaded onto the Pico.
    • The code for ntptime.py, i2c_lcd.py, and lcd_api.py is provided in the “Library Code Appendix” at the end of this guide. For secrets.py, create it now:
    • In Thonny:
    • File > New, paste each library’s code from the appendix (for ntptime.py, i2c_lcd.py, and lcd_api.py), then File > Save As.
    • For secrets.py, use this template and fill in your details:
    # secrets.py
    wifi_ssid = "YOUR_WIFI_SSID"
    wifi_password = "YOUR_WIFI_PASSWORD"
    google_sheets_url = "https://script.google.com/macros/s/[long-id]/exec"
          
    • Replace the placeholders:
    • YOUR_WIFI_SSID with your WiFi network name.
    • YOUR_WIFI_PASSWORD with your WiFi password.
    • https://script.google.com/macros/s/[long-id]/exec with the Web app URL from Step 3.4.
    • Save all files to the “Raspberry Pi Pico” device with these exact names:
    • ntptime.py
    • i2c_lcd.py
    • lcd_api.py
    • secrets.py
  3. Upload the Main Code
    • File > New in Thonny, then paste the updated main MicroPython code (below). Note that the google_sheets_url is imported from secrets.py.
    • File > Save As, choose “Raspberry Pi Pico,” and name it main.py (this makes it run automatically on boot).
    • Main code:
    import network
    import ntptime
    import time
    from machine import Pin, I2C
    from dht import DHT11
    from i2c_lcd import I2cLcd
    import urequests
    
    # Define sensor read interval
    sensor_read_interval = 10  # Interval in seconds to read the sensor data
    
    # Import WiFi credentials and Google Sheets URL
    try:
        from secrets import wifi_ssid, wifi_password, google_sheets_url
    except ImportError:
        print("WiFi credentials or Google Sheets URL missing in secrets.py")
        raise
    
    # Set up I2C for the LCD
    try:
        i2c = I2C(0, scl=Pin(1), sda=Pin(0))
        lcd = I2cLcd(i2c, 0x27, 2, 16)  # Adjust the I2C address (0x27) if necessary
    
        # Define a custom character for the degree symbol
        degree_symbol = bytearray([
            0b00100,  #   *
            0b01010,  #  * *
            0b00100,  #   *
            0b00000,  #    
            0b00000,  #    
            0b00000,  #    
            0b00000,  #    
            0b00000   #    
        ])
        lcd.custom_char(0, degree_symbol)
        lcd_connected = True
    except Exception as e:
        print("LCD not connected:", e)
        lcd_connected = False
    
    # Set up the DHT11 sensor
    try:
        dht_sensor = DHT11(Pin(15))
    except Exception as e:
        print("DHT11 sensor not connected:", e)
        dht_sensor = None
    
    # Connect to WiFi with timeout
    def connect_wifi(ssid, password, timeout=10):
        wlan = network.WLAN(network.STA_IF)
        wlan.active(True)
        wlan.connect(ssid, password)
        print("Connecting to WiFi...")
        start_time = time.time()
        while not wlan.isconnected():
            if time.time() - start_time >= timeout:
                print("Failed to connect to WiFi within the timeout period.")
                return False
            time.sleep(0.5)
        print("Connected to WiFi:", wlan.ifconfig())
        return True
    
    if not connect_wifi(wifi_ssid, wifi_password):
        raise RuntimeError("WiFi connection failed")
    
    try:
        ntptime.host = "time.google.com"
        print("Synchronizing time...")
        ntptime.settime()
        print("Time synchronized!")
    except Exception as e:
        print("Failed to synchronize time:", e)
    
    # Convert UTC to Central Time (UTC-6 or UTC-5 during daylight saving) as a full datetime string
    def get_central_datetime():
        tm = time.localtime()
        utc_offset = -6  # Central Standard Time (change to -5 for DST)
        central_time = time.localtime(time.mktime(tm) + utc_offset * 3600)
        return "{:02}-{:02}-{:04} {:02}:{:02}:{:02}".format(
            central_time[1], central_time[2], central_time[0],  # Date: MM-DD-YYYY
            central_time[3], central_time[4], central_time[5]   # Time: HH:MM:SS
        )
    
    # Get single sensor reading
    def get_sensor_data():
        if dht_sensor:
            dht_sensor.measure()
            temp_f = dht_sensor.temperature() * 9 / 5 + 32  # Convert to Fahrenheit
            humidity = dht_sensor.humidity()
            return temp_f, humidity
        else:
            return None, None
    
    # Function to send data to Google Sheets with manual space encoding
    def send_to_google_sheets(temp, humidity, datetime):
        # Replace space in datetime with %20 for URL encoding
        encoded_datetime = datetime.replace(" ", "%20")
        url = "{}?datetime={}&temperature={}&humidity={}".format(
            google_sheets_url, encoded_datetime, temp, humidity
        )
        response = urequests.get(url)
        print("Data sent to Google Sheets:", response.text)
    
    # Initialize variables
    last_sensor_update = time.time()
    last_clock_update = time.time()
    temp_f = None
    humidity = None
    sensor_reads = 0
    
    # Main loop
    while True:
        try:
            current_time = time.time()
            
            # Update the sensor every sensor_read_interval seconds
            if current_time - last_sensor_update >= sensor_read_interval:
                temp_f, humidity = get_sensor_data()
                sensor_reads += 1
                last_sensor_update = current_time
                
                # Send data to Google Sheets with a single datetime
                datetime = get_central_datetime()  # e.g., "03-02-2025 14:30:00"
                send_to_google_sheets(temp_f, humidity, datetime)
            
            # Update the clock every second (using time-only for LCD)
            if current_time - last_clock_update >= 1:
                last_clock_update = current_time
                if lcd_connected:
                    tm = time.localtime()
                    clock_str = "{:02}:{:02}:{:02}".format(tm[3], tm[4], tm[5])
                    lcd.move_to(0, 1)
                    lcd.putstr("T:{} H:{}".format(temp_f if temp_f else "--", humidity if humidity else "--"))
                    lcd.move_to(8, 1)
                    lcd.putstr(clock_str)
                time.sleep(0.1)  # Small delay to prevent overwhelming the loop
        except Exception as e:
            print("Error in main loop:", e)
            time.sleep(1)  # Wait before retrying on error
          
  4. Run and Test
    • Click the green “Run” button in Thonny.
    • Watch the shell output:
    • It should connect to WiFi, sync time, and start sending data every 10 seconds.
    • If using the LCD, check it for temperature, humidity, and time updates.
    • Open your Google Sheet to see new rows appear with a single DateTime column followed by temperature and humidity.

Part 5: Troubleshooting and Final Touches

Common Issues

  • WiFi Fails: Double-check secrets.py credentials and ensure the Pico is in range.
  • LCD Blank (if connected): Verify wiring and I2C address (default is 0x27; adjust in code if different—use an I2C scanner script if needed).
  • No Data in Sheet: Ensure the Web app URL in secrets.py is correct and the script is deployed with “Anyone” access.
  • Time Sync Fails: Check internet connection; the Pico uses time.google.com.

Final Touches

  • Adjust sensor_read_interval (in seconds) in the Pico code if 10 seconds is too frequent.
  • Format your Google Sheet’s DateTime column (Format > Number > Date time) for a clean display.
  • Unplug the Pico from your computer and power it with a USB adapter to run standalone.

How It All Works Together

  • The Pico reads the DHT11 sensor every 10 seconds, displays data on the LCD (if connected), and sends it to the Google Sheets URL from secrets.py as a single datetime value with temperature and humidity.
  • The Apps Script (doGet) receives this data via HTTP GET parameters and appends it to your sheet in columns A (DateTime), B (Temperature), and C (Humidity).
  • Libraries like ntptime.py sync the clock, i2c_lcd.py and lcd_api.py drive the display (if used), and secrets.py keeps your WiFi creds and Google Sheets URL safe.

Library Code Appendix

The following library files (ntptime.py, i2c_lcd.py, and lcd_api.py) are required for this project and should be uploaded to your Pico as described in “Upload Library Files” under Part 4. Copy each code block below into Thonny and save them with the specified filenames.

ntptime.py

from time import gmtime
import socket
import struct

# The NTP host can be configured at runtime by doing: ntptime.host = 'myhost.org'
host = "pool.ntp.org"
# The NTP socket timeout can be configured at runtime by doing: ntptime.timeout = 2
timeout = 1

def time():
    NTP_QUERY = bytearray(48)
    NTP_QUERY[0] = 0x1B
    addr = socket.getaddrinfo(host, 123)[0][-1]
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.settimeout(timeout)
        s.sendto(NTP_QUERY, addr)
        msg = s.recv(48)
    finally:
        s.close()
    val = struct.unpack("!I", msg[40:44])[0]

    # 2024-01-01 00:00:00 converted to an NTP timestamp
    MIN_NTP_TIMESTAMP = 3913056000

    # Y2036 fix
    if val < MIN_NTP_TIMESTAMP:
        val += 0x100000000

    EPOCH_YEAR = gmtime(0)[0]
    if EPOCH_YEAR == 2000:
        # (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60
        NTP_DELTA = 3155673600
    elif EPOCH_YEAR == 1970:
        # (date(1970, 1, 1) - date(1900, 1, 1)).days * 24*60*60
        NTP_DELTA = 2208988800
    else:
        raise Exception("Unsupported epoch: {}".format(EPOCH_YEAR))

    return val - NTP_DELTA

def settime():
    t = time()
    import machine

    tm = gmtime(t)
    machine.RTC().datetime((tm[0], tm[1], tm[2], tm[6] + 1, tm[3], tm[4], tm[5], 0))
  

i2c_lcd.py

"""Implements a HD44780 character LCD connected via PCF8574 on I2C."""

from lcd_api import LcdApi
from time import sleep_ms

# The PCF8574 has a jumper selectable address: 0x20 - 0x27
DEFAULT_I2C_ADDR = 0x27

# Defines shifts or masks for the various LCD line attached to the PCF8574
MASK_RS = 0x01
MASK_RW = 0x02
MASK_E = 0x04
SHIFT_BACKLIGHT = 3
SHIFT_DATA = 4

class I2cLcd(LcdApi):
    """Implements a HD44780 character LCD connected via PCF8574 on I2C."""

    def __init__(self, i2c, i2c_addr, num_lines, num_columns):
        self.i2c = i2c
        self.i2c_addr = i2c_addr
        self.i2c.writeto(self.i2c_addr, bytes([0]))
        sleep_ms(20)   # Allow LCD time to power up
        # Send reset 3 times
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(5)    # Need to delay at least 4.1 msec
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(1)
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(1)
        # Put LCD into 4-bit mode
        self.hal_write_init_nibble(self.LCD_FUNCTION)
        sleep_ms(1)
        LcdApi.__init__(self, num_lines, num_columns)
        cmd = self.LCD_FUNCTION
        if num_lines > 1:
            cmd |= self.LCD_FUNCTION_2LINES
        self.hal_write_command(cmd)

    def hal_write_init_nibble(self, nibble):
        """Writes an initialization nibble to the LCD."""
        byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA
        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytes([byte]))

    def hal_backlight_on(self):
        """Allows the hal layer to turn the backlight on."""
        self.i2c.writeto(self.i2c_addr, bytes([1 << SHIFT_BACKLIGHT]))

    def hal_backlight_off(self):
        """Allows the hal layer to turn the backlight off."""
        self.i2c.writeto(self.i2c_addr, bytes([0]))

    def hal_write_command(self, cmd):
        """Writes a command to the LCD."""
        byte = ((self.backlight << SHIFT_BACKLIGHT) |
                (((cmd >> 4) & 0x0f) << SHIFT_DATA))
        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytes([byte]))
        byte = ((self.backlight << SHIFT_BACKLIGHT) |
                ((cmd & 0x0f) << SHIFT_DATA))
        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytes([byte]))
        if cmd <= 3:
            # The home and clear commands require a worst case delay of 4.1 msec
            sleep_ms(5)

    def hal_write_data(self, data):
        """Write data to the LCD."""
        byte = (MASK_RS |
                (self.backlight << SHIFT_BACKLIGHT) |
                (((data >> 4) & 0x0f) << SHIFT_DATA))
        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytes([byte]))
        byte = (MASK_RS |
                (self.backlight << SHIFT_BACKLIGHT) |
                ((data & 0x0f) << SHIFT_DATA))
        self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytes([byte]))
  

lcd_api.py

"""Provides an API for talking to HD44780 compatible character LCDs."""

import time

class LcdApi:
    """Implements the API for talking with HD44780 compatible character LCDs."""
    LCD_CLR = 0x01              # DB0: clear display
    LCD_HOME = 0x02             # DB1: return to home position
    LCD_ENTRY_MODE = 0x04       # DB2: set entry mode
    LCD_ENTRY_INC = 0x02        # DB1: increment
    LCD_ENTRY_SHIFT = 0x01      # DB0: shift
    LCD_ON_CTRL = 0x08          # DB3: turn lcd/cursor on
    LCD_ON_DISPLAY = 0x04       # DB2: turn display on
    LCD_ON_CURSOR = 0x02        # DB1: turn cursor on
    LCD_ON_BLINK = 0x01         # DB0: blinking cursor
    LCD_MOVE = 0x10             # DB4: move cursor/display
    LCD_MOVE_DISP = 0x08        # DB3: move display (0-> move cursor)
    LCD_MOVE_RIGHT = 0x04       # DB2: move right (0-> left)
    LCD_FUNCTION = 0x20         # DB5: function set
    LCD_FUNCTION_8BIT = 0x10    # DB4: set 8BIT mode (0->4BIT mode)
    LCD_FUNCTION_2LINES = 0x08  # DB3: two lines (0->one line)
    LCD_FUNCTION_10DOTS = 0x04  # DB2: 5x10 font (0->5x7 font)
    LCD_FUNCTION_RESET = 0x30   # See "Initializing by Instruction" section
    LCD_CGRAM = 0x40            # DB6: set CG RAM address
    LCD_DDRAM = 0x80            # DB7: set DD RAM address
    LCD_RS_CMD = 0
    LCD_RS_DATA = 1
    LCD_RW_WRITE = 0
    LCD_RW_READ = 1

    def __init__(self, num_lines, num_columns):
        self.num_lines = num_lines
        if self.num_lines > 4:
            self.num_lines = 4
        self.num_columns = num_columns
        if self.num_columns > 40:
            self.num_columns = 40
        self.cursor_x = 0
        self.cursor_y = 0
        self.implicit_newline = (num_lines > 1)
        self.backlight = True
        self.display_off()
        self.backlight_on()
        self.clear()
        self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
        self.hide_cursor()
        self.display_on()

    def clear(self):
        """Clears the LCD display and moves the cursor to the top left corner."""
        self.hal_write_command(self.LCD_CLR)
        self.hal_write_command(self.LCD_HOME)
        self.cursor_x = 0
        self.cursor_y = 0

    def show_cursor(self):
        """Causes the cursor to be made visible."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
                               self.LCD_ON_CURSOR)

    def hide_cursor(self):
        """Causes the cursor to be hidden."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)

    def blink_cursor_on(self):
        """Turns on the cursor, and makes it blink."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
                               self.LCD_ON_CURSOR | self.LCD_ON_BLINK)

    def blink_cursor_off(self):
        """Turns on the cursor, and makes it not blink (i.e. be solid)."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
                               self.LCD_ON_CURSOR)

    def display_on(self):
        """Turns on (i.e. unblanks) the LCD."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)

    def display_off(self):
        """Turns off (i.e. blanks) the LCD."""
        self.hal_write_command(self.LCD_ON_CTRL)

    def backlight_on(self):
        """Turns the backlight on."""
        self.backlight = True
        self.hal_backlight_on()

    def backlight_off(self):
        """Turns the backlight off."""
        self.backlight = False
        self.hal_backlight_off()

    def move_to(self, cursor_x, cursor_y):
        """Moves the cursor position to the indicated position."""
        self.cursor_x = cursor_x
        self.cursor_y = cursor_y
        addr = cursor_x & 0x3f
        if cursor_y & 1:
            addr += 0x40    # Lines 1 & 3 add 0x40
        if cursor_y & 2:
            addr += 0x14    # Lines 2 & 3 add 0x14
        self.hal_write_command(self.LCD_DDRAM | addr)

    def putchar(self, char):
        """Writes the indicated character to the LCD at the current cursor position."""
        if char != '\n':
            self.hal_write_data(ord(char))
            self.cursor_x += 1
        if self.cursor_x >= self.num_columns or char == '\n':
            self.cursor_x = 0
            self.cursor_y += 1
            if self.cursor_y >= self.num_lines:
                self.cursor_y = 0
            self.move_to(self.cursor_x, self.cursor_y)

    def putstr(self, string):
        """Write the indicated string to the LCD at the current cursor position."""
        for char in string:
            self.putchar(char)

    def custom_char(self, location, char_map):
        """Writes a custom character to the LCD character generator RAM."""
        location &= 0x7
        self.hal_write_command(self.LCD_CGRAM | (location << 3))
        for i in range(8):
            self.hal_write_data(char_map[i])

    def hal_backlight_on(self):
        """Allows the hal layer to turn the backlight on."""
        pass    # To be implemented by a subclass

    def hal_backlight_off(self):
        """Allows the hal layer to turn the backlight off."""
        pass    # To be implemented by a subclass

    def hal_write_command(self, cmd):
        """Write a command to the LCD."""
        pass    # To be implemented by a subclass

    def hal_write_data(self, data):
        """Write data to the LCD."""
        pass    # To be implemented by a subclass
  

Wrapping It Up: Your IoT Victory and Beyond

Congratulations—you’ve just built a fully functional IoT weather station with a Raspberry Pi Pico! From wiring up sensors to syncing time with NTP, connecting to WiFi, and logging data to Google Sheets, you’ve crafted a system that bridges the physical and digital worlds. With a single DateTime column, your data is now ready for easy analysis—try creating a line graph in Sheets with DateTime as the x-axis to visualize temperature and humidity trends over time! Whether you added the optional LCD for a local display or kept it simple, you’ve got a powerful tool to monitor environmental data in real time. It’s a testament to what a little code and creativity can achieve. What’s next? Maybe tweak the sensor interval, add more sensors like light or pressure, or dive deeper into graphing your data. The possibilities are endless—keep tinkering, and let your IoT adventures soar!

Comments

Popular posts from this blog

Build Your Own YouTube Subscriber Counter with a Raspberry Pi Pico W

LaunchPico v0.95: A Rocket-Themed Launcher for Your Pi Pico