import machine
import time
# Define ADC pin for temperature sensor
adcpin = 4
sensor = machine.ADC(adcpin)
# Define onboard LED pin
led_onboard = machine.Pin(25, machine.Pin.OUT)
#Class Definition
class LCD():
def __init__(self, addr=39, blen=1):
sda = machine.Pin(0)
scl = machine.Pin(1)
self.bus = machine.I2C(0,sda=sda, scl=scl, freq=400000)
#print(self.bus.scan())
self.addr = addr
self.blen = blen
self.send_command(0x33) # Must initialize to 8-line mode at first
time.sleep(0.005)
self.send_command(0x32) # Then initialize to 4-line mode
time.sleep(0.005)
self.send_command(0x28) # 2 Lines & 5*7 dots
time.sleep(0.005)
self.send_command(0x0C) # Enable display without cursor
time.sleep(0.005)
self.send_command(0x01) # Clear Screen
self.bus.writeto(self.addr, bytearray([0x08]))
def write_word(self, data):
temp = data
if self.blen == 1:
temp |= 0x08
else:
temp &= 0xF7
self.bus.writeto(self.addr, bytearray([temp]))
def send_command(self, cmd):
# Send bit7-4 firstly
buf = cmd & 0xF0
buf |= 0x04 # RS = 0, RW = 0, EN = 1
self.write_word(buf)
time.sleep(0.002)
buf &= 0xFB # Make EN = 0
self.write_word(buf)
# Send bit3-0 secondly
buf = (cmd & 0x0F) << 4
buf |= 0x04 # RS = 0, RW = 0, EN = 1
self.write_word(buf)
time.sleep(0.002)
buf &= 0xFB # Make EN = 0
self.write_word(buf)
def send_data(self, data):
# Send bit7-4 firstly
buf = data & 0xF0
buf |= 0x05 # RS = 1, RW = 0, EN = 1
self.write_word(buf)
time.sleep(0.002)
buf &= 0xFB # Make EN = 0
self.write_word(buf)
# Send bit3-0 secondly
buf = (data & 0x0F) << 4
buf |= 0x05 # RS = 1, RW = 0, EN = 1
self.write_word(buf)
time.sleep(0.002)
buf &= 0xFB # Make EN = 0
self.write_word(buf)
def clear(self):
self.send_command(0x01) # Clear Screen
def openlight(self): # Enable the backlight
self.bus.writeto(self.addr,bytearray([0x08]))
# self.bus.close()
def write(self, x, y, str):
if x < 0:
x = 0
if x > 15:
x = 15
if y < 0:
y = 0
if y > 1:
y = 1
# Move cursor
addr = 0x80 + 0x40 * y + x
self.send_command(addr)
for chr in str:
self.send_data(ord(chr))
def message(self, text):
#print("message: %s"%text)
for char in text:
if char == '\n':
self.send_command(0xC0) # next line
else:
self.send_data(ord(char))
#################################################
def custom_char(self, location, charmap):
"""Write a character to one of the 8 CGRAM locations, available
as chr(0) through chr(7)."""
location &= 0x07 # Ensure location is between 0-7
self.send_command(0x40 | (location << 3)) # Set CGRAM address
time.sleep_us(40) # Small delay to stabilize LCD processing
for i in range(8):
self.send_data(charmap[i]) # Write each row of the custom character
time.sleep_us(40) # Brief pause for LCD to store each row
self.send_command(0x80) # Move cursor back to first line
#################################################
# Function to read temperature from the sensor
def ReadTemperature():
adc_value = sensor.read_u16() # Read raw ADC value
volt = (3.3 / 65535) * adc_value # Convert ADC value to voltage
temperature = 27 - (volt - 0.706) / 0.001721 # Convert voltage to temperature (Celsius)
return round(temperature, 1) # Round to 1 decimal place
# Loop to log temperature and runtime every 10 seconds
while True:
# Initialize LCD
lcd = LCD()
# Turn LED ON briefly before reading temperature to make sure the code is running
led_onboard.value(1)
time.sleep(0.1)
led_onboard.value(0)
# Define heart shape (5x8 pixel matrix)
heart = [
0b00000, # .....
0b01010, # .X.X.
0b11111, # XXXXX
0b11111, # XXXXX
0b01110, # .XXX.
0b00100, # ..X..
0b00000, # .....
0b00000 # .....
]
# Load custom character into CGRAM at position 0 -> "x00" where the heart shape is stored
custom_char(lcd, 0, heart) # stores the heart at CGRAM position 0 (x00)
# Read temperature from the sensor
temperature = ReadTemperature()
# Print (first line) to the LCD display (uses custom character)
lcd.write(8, 0, "I \x00 Pico\n") # Displays " I ♥ Pico"
time.sleep(2)
lcd.clear()
# Print (first line) temperature with degree symbol on LCD
lcd.message(f"Temp: {temperature} " + chr(223) + "C\n") # Displays temperature with degree symbol
# Print to the console (this is what should appear in the LCD Display)
print("\tI ♥ Pico\n")
print(f"Temp: {temperature} °C")
print("Update in → (10 seconds countdown)\n")
# Print (second line) countdown loop to the LCD display
for i in range(9, 0, -1): # Count from 10 to 1
lcd.send_command(0xC0) # Move cursor to second line without clearing the screen
lcd.message(f"Update in {chr(126)} {i}s")
time.sleep(1) # Wait one second before updating
# Question 1: What is the difference between SPI and I2C?
# SPI (Serial Peripheral Interface) is a faster communication protocol that uses multiple data lines (MISO, MOSI, SCLK, CS),
# making it great for high-speed applications like displays or SD cards.
# I2C (Inter-Integrated Circuit) is a simpler protocol that allows multiple devices to share the same two-wire bus (SDA and SCL),
# making it ideal for sensors and small modules where fewer connections are needed.
# Question 2: Which protocol should you choose when building a project?
# The choice depends on your project:
# - Use SPI for high-speed, point-to-point communication with fewer devices.
# - Use I2C for multiple devices on the same bus with simpler wiring.
# If a project involves sensors like temperature or accelerometers, I2C is usually preferred.
# Working with fast data transfer (like reading an SD card), SPI might be better.