In this project, we will show you how to make Web Controlled Notice Board using Raspberry Pi Pico W & I2C 16X2 LCD, and a web server to create a notice board that can be controlled via the web.
This project is excellent for building a simple, low-cost display that can be used to display Messages, and information from the web server, such as weather forecasts, news headlines, or even custom messages. check out Introduction to Raspberry Pi Pico WÂ Getting Started: Tutorials, Pinout
Thank You NextPCB:
Thanks to the support and sponsorship from NextPCB, this project has been completed successfully. I highly recommend NextPCB for PCB services. Visit their website for exciting discounts, coupons, and more. Thank you, NextPCB!
- Only 0$ for 5-10pcs PCB Prototypes:Nextpcb.com/pcbprototype
- Register and get $100 from NextPCB:Â Nextpcb.com/coupon
You can use this to display any text or message that you want and control it remotely using a web browser.
A Web-Controlled Notice Board is a digital display board that can be controlled remotely using a web interface. This notice board is usually equipped with an LCD or LED screen that displays various types of information, such as custom messages, announcements, news updates, event schedules, and announcements.
Required Material
1. Raspberry Pi Pico w
2. 16×2 I2C Display
3. Jumper wires
16×2 I2C LCD Display
A 16×2 I2C LCD Display is a type of liquid crystal display (LCD) that can display information. Each of its two lines can contain up to 16 characters. It is called an I2C LCD because it uses the I2C communication protocol to communicate with other devices, such as microcontrollers like Raspberry Pi Pico or Arduino.
One of our earlier posts discussed interfacing an LCD display with a Raspberry Pi Pico. You can follow the guide to learn more.
Specifications & Features :-
- Interface: I2C
- 16 characters wide, 2 rows
- Interface Address: 0x27
- Character Color: White
- Backlight: Blue
- Supply voltage: 5V
Circuit Diagram – Web-Controlled Notice Board Using Raspberry Pi Pico W
The connection between the Raspberry Pi Pico and I2C LCD is straightforward as shown below in the schematic,
I2C LCD Connections With Pico W
I2C LCD dISPLAY | Raspberry Pi Pico w |
---|---|
GND | GND |
VCC | 5 V |
SDA | 16 |
SCL | 17 |
MicroPython Code – Web Controlled Notice Board Using Pico W
The code is divided into four parts. Namely, the parts are i2c_lcd.py, lcd_api.py, secrets.py & the fourth part is main.py. The LCD doesn’t run directly as it requires some libraries.
Note- (Don’t forget to give the .py extension because thonny IDE doesn’t add it automatically)
lcd_api.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
"""Provides an API for talking to HD44780 compatible character LCDs.""" import time class LcdApi: """Implements the API for talking with HD44780 compatible character LCDs. This class only knows what commands to send to the LCD, and not how to get them to the LCD. It is expected that a derived class will implement the hal_xxx functions. """ # The following constant names were lifted from the avrlib lcd.h # header file, however, I changed the definitions from bit numbers # to bit masks. # # HD44780 LCD controller command set 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.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 no 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. This isn't really an LCD command, but some modules have backlight controls, so this allows the hal to pass through the command. """ self.backlight = True self.hal_backlight_on() def backlight_off(self): """Turns the backlight off. This isn't really an LCD command, but some modules have backlight controls, so this allows the hal to pass through the command. """ self.backlight = False self.hal_backlight_off() def move_to(self, cursor_x, cursor_y): """Moves the cursor position to the indicated position. The cursor position is zero based (i.e. cursor_x == 0 indicates first column). """ 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, and advances the cursor by one 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 and advances the cursor position appropriately. """ for char in string: self.putchar(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 &= 0x7 self.hal_write_command(self.LCD_CGRAM | (location << 3)) time.sleep_us(40) for i in range(8): self.hal_write_data(charmap[i]) time.sleep_us(40) self.move_to(self.cursor_x, self.cursor_y) def hal_backlight_on(self): """Allows the hal layer to turn the backlight on. If desired, a derived HAL class will implement this function. """ pass def hal_backlight_off(self): """Allows the hal layer to turn the backlight off. If desired, a derived HAL class will implement this function. """ pass def hal_write_command(self, cmd): """Write a command to the LCD. It is expected that a derived HAL class will implement this function. """ raise NotImplementedError def hal_write_data(self, data): """Write data to the LCD. It is expected that a derived HAL class will implement this function. """ raise NotImplementedError |
i2c_lcd.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
from lcd_api import LcdApi from machine import I2C 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, bytearray([0])) sleep_ms(20) # Allow LCD time to powerup # 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. This particular function is only used during initialization. """ byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E])) self.i2c.writeto(self.i2c_addr, bytearray([byte])) def hal_backlight_on(self): """Allows the hal layer to turn the backlight on.""" self.i2c.writeto(self.i2c_addr, bytearray([1 << SHIFT_BACKLIGHT])) def hal_backlight_off(self): """Allows the hal layer to turn the backlight off.""" self.i2c.writeto(self.i2c_addr, bytearray([0])) def hal_write_command(self, cmd): """Writes a command to the LCD. Data is latched on the falling edge of E. """ byte = ((self.backlight << SHIFT_BACKLIGHT) | (((cmd >> 4) & 0x0f) << SHIFT_DATA)) self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E])) self.i2c.writeto(self.i2c_addr, bytearray([byte])) byte = ((self.backlight << SHIFT_BACKLIGHT) | ((cmd & 0x0f) << SHIFT_DATA)) self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E])) self.i2c.writeto(self.i2c_addr, bytearray([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, bytearray([byte | MASK_E])) self.i2c.writeto(self.i2c_addr, bytearray([byte])) byte = (MASK_RS | (self.backlight << SHIFT_BACKLIGHT) | ((data & 0x0f) << SHIFT_DATA)) self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E])) self.i2c.writeto(self.i2c_addr, bytearray([byte])) |
secrets.py
In this code, we first set up the Wi-Fi network by providing the SSID and password.
1 2 |
ssid = 'ESP Repeater' passwd = '77777777' |
Update the Wi-Fi SSID and password in the code with your network.
main.py
Copy the following code and save it as main.py in Raspberry Pi Pico W.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
import secrets import time import network import socket import rp2 from ubinascii import unhexlify import machine from machine import I2C from lcd_api import LcdApi from i2c_lcd import I2cLcd LCD_ADDR = 0x27 LCD_NUM_ROWS = 2 LCD_NUM_COLS = 16 LCD_SDA = 16 LCD_SCL = 17 i2c = I2C(0, sda=machine.Pin(LCD_SDA), scl=machine.Pin(LCD_SCL), freq=400000) lcd = I2cLcd(i2c, LCD_ADDR, LCD_NUM_ROWS, LCD_NUM_COLS) lcd.putstr('Connected to WiFi') rp2.country('IN') wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(secrets.ssid, secrets.passwd) print('Connecting to WiFi...') max_wait = 10 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 time.sleep(1) if wlan.status() != 3: raise RuntimeError('could not connect !!!') print('Connected with WiFi') status = wlan.ifconfig() ipserv = status[0] print('ip = ' + ipserv) lcd.clear() lcd.putstr(ipserv) html = """<!DOCTYPE html> <html><head> <title>Smart Notice Board</title> <meta name="viewport" content="width=device-width, initial-scale=2"> <p> <font size="7" face="sans-serif"> <marquee> Smart Notice Board </marquee> </font> </p> </head> <body><center> <form method="post"> <label for="msg">Message:</label> <input type="text" id="msg" name="Message"> <input type="submit" value="Send"> </form> </center> </body> </html> """ def txtDecode(txt): res = '' i = 0 while i < len(txt): car = txt[i] if car == '+': car = ' ' elif car == '%': code = unhexlify(txt[i+1:i+3]) if (code[0] >= 32) and (code[0] < 127): car = str(code)[2:-1] else: car = '?' i = i + 2 if (car >= ' ') and (car <= '~'): res = res + car i = i + 1 else: res = res + '?' i = i + 1 return res addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(addr) s.listen(1) print('Connection with ', addr) while True: try: cl, addr = s.accept() print('client connected from', addr) request = cl.recv(1024) req = str(request)[2:-1] if req[0:5] == 'POST ': pos = req.find('Message') if pos != -1: resp = (txtDecode(req[pos+9:]) + 16*' ')[0:16] lcd.move_to(0,1) lcd.putstr(resp) cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(html) cl.close() except OSError as e: cl.close() |
Run the library and main file.
Web-Controlled Notice Board with Raspberry Pi Pico W Demonstration
Once uploading is complete. It will display the local IP address on Thonny Shell after a successful connection is established between the client and the WIFI network.
Enter the IP address in your browser to check the webpage.
Now enter the text here and send it by clicking the send button.
Once the message is sent, the LCD will show whatever is sent.
Check some more IoT Based Projects here: DIY IoT Projects
Best Raspberry Pi Pico W Projects
- Use Blynk 2.0 With Raspberry Pi Pico W Using Micropython
- Raspberry Pi Pico W Relay Module Web Server Using Thonny IDE
- Raspberry Pi Pico W Remote Controlled Car Robot Web Server
- How to Control WS2812 LED w/ Raspberry Pi Pico W & Adafruit IO
- BME280 With Raspberry Pi Pico W Using MicroPython
- Raspberry Pi Pico W Web Server with BME280 – Mini Weather Station
2 Comments
BTW, there seems to be a small bug, the first character of the messages gets cut off.
Hi did you find the solution for this issue?