├── .gitignore ├── drivers ├── __init__.py └── i2c_dev.py ├── imgs ├── demo_ip.jpg ├── demo_forex.gif ├── demo_backlight.gif ├── demo_netmonitor.gif ├── characters_table.png ├── demo_progress_bar.jpg ├── demo_home_automation.gif ├── demo_simple_strings.jpg ├── demo_tiny_dashboard.gif ├── demo_custom_characters.jpg ├── demo_extended_strings.jpg └── thumb-yt-rpiguy-lcd-tutorial.png ├── configs ├── raspi-blacklist.conf └── modules ├── .github └── ISSUE_TEMPLATE │ ├── other.md │ ├── documentation.md │ ├── compatibility.md │ ├── demo.md │ └── bug.md ├── demo_clock_and_IP.py ├── install.sh ├── demo_clock.py ├── demo_lcd_extended_strings.py ├── demo_lcd.py ├── demo_scrolling_text.py ├── demo_forex.py ├── demo_netmonit.py ├── demo_lcd_backlight.py ├── demo_lcd_custom_characters.py ├── demo_lcd_process_bar.py ├── setup.sh ├── CONTRIBUTING.md ├── demo_tiny_dashboard.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | .git 3 | .idea 4 | -------------------------------------------------------------------------------- /drivers/__init__.py: -------------------------------------------------------------------------------- 1 | from .i2c_dev import Lcd, CustomCharacters 2 | -------------------------------------------------------------------------------- /imgs/demo_ip.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_ip.jpg -------------------------------------------------------------------------------- /imgs/demo_forex.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_forex.gif -------------------------------------------------------------------------------- /imgs/demo_backlight.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_backlight.gif -------------------------------------------------------------------------------- /imgs/demo_netmonitor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_netmonitor.gif -------------------------------------------------------------------------------- /imgs/characters_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/characters_table.png -------------------------------------------------------------------------------- /imgs/demo_progress_bar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_progress_bar.jpg -------------------------------------------------------------------------------- /imgs/demo_home_automation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_home_automation.gif -------------------------------------------------------------------------------- /imgs/demo_simple_strings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_simple_strings.jpg -------------------------------------------------------------------------------- /imgs/demo_tiny_dashboard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_tiny_dashboard.gif -------------------------------------------------------------------------------- /imgs/demo_custom_characters.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_custom_characters.jpg -------------------------------------------------------------------------------- /imgs/demo_extended_strings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/demo_extended_strings.jpg -------------------------------------------------------------------------------- /imgs/thumb-yt-rpiguy-lcd-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-raspberry-pi-guy/lcd/HEAD/imgs/thumb-yt-rpiguy-lcd-tutorial.png -------------------------------------------------------------------------------- /configs/raspi-blacklist.conf: -------------------------------------------------------------------------------- 1 | # blacklist spi and i2c by default (many users don't need them) 2 | 3 | blacklist spi-bcm2708 4 | #blacklist i2c-bcm2708 5 | blacklist snd-soc-pcm512x 6 | blacklist snd-soc-wm8804 7 | -------------------------------------------------------------------------------- /configs/modules: -------------------------------------------------------------------------------- 1 | # /etc/modules: kernel modules to load at boot time. 2 | # 3 | # This file contains the names of kernel modules that should be loaded 4 | # at boot time, one per line. Lines beginning with "#" are ignored. 5 | # Parameters can be specified after the module name. 6 | 7 | snd-bcm2835 8 | i2c-dev 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: Any other issue that is directly related to this repository and is not covered 4 | by the other issue categories 5 | title: '' 6 | labels: other 7 | assignees: '' 8 | 9 | --- 10 | 11 | **Describe the issue** 12 | Add a clear and concise description of the issue here. 13 | 14 | **Related file** 15 | - File: `add the filename here` 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation 3 | about: Issues specifically related to typos or missing information in the Wiki or 4 | other documentation file 5 | title: '' 6 | labels: documentation 7 | assignees: '' 8 | 9 | --- 10 | 11 | **Describe the issue** 12 | Add a clear and concise description of the issue here. 13 | 14 | **Document** 15 | - File: `add the filename here` 16 | -------------------------------------------------------------------------------- /demo_clock_and_IP.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import drivers 4 | from time import sleep 5 | from datetime import datetime 6 | from subprocess import check_output 7 | display = drivers.Lcd() 8 | IP = check_output(["hostname", "-I"], encoding="utf8").split()[0] 9 | try: 10 | print("Writing to display") 11 | while True: 12 | display.lcd_display_string(str(datetime.now().time()), 1) 13 | display.lcd_display_string(str(IP), 2) 14 | # Uncomment the following line to loop with 1 sec delay 15 | # sleep(1) 16 | except KeyboardInterrupt: 17 | # If there is a KeyboardInterrupt (when you press ctrl+c), exit the program and cleanup 18 | print("Cleaning up!") 19 | display.lcd_clear() 20 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # POSIX script to call setup.sh with the correct interpreter 4 | # Created to maintain compatibility with the video tutorial and descriptions 5 | 6 | ########################################################################## 7 | # Automated Installer Program For I2C 16x2 LCD Screens 8 | # 9 | # Cloned and adapted from: Ryanteck LTD 10 | # 11 | # Author: Matthew Timmons-Brown (the-raspberry-pi-guy) 12 | # Contributors: https://github.com/the-raspberry-pi-guy/lcd/contributors 13 | # 14 | # Repo: https://github.com/the-raspberry-pi-guy/lcd 15 | ########################################################################## 16 | 17 | # check if user root 18 | if [ "$(id -u)" != "0" ]; then echo "Please re-run as sudo."; exit 1; fi 19 | 20 | # execute the installation script using interpreter in the shebang 21 | ./setup.sh 22 | 23 | exit 0 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/compatibility.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Compatibility 3 | about: Issues specifically related to Python 3.x usage 4 | title: '' 5 | labels: compatibility 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the issue** 11 | Add a clear and concise description of the issue here. 12 | 13 | **Executed command and associated error** 14 | ```sh 15 | add the executed command here 16 | ``` 17 | ```Python 18 | add the associated error here 19 | ``` 20 | 21 | **Host and software info** 22 | - RPi board version: `add here` 23 | - OS version: `add here` 24 | - Python version : `add here` 25 | 26 | **Checklist** 27 | - [ ] I have watched and followed the instructions in the [Youtube tutorial](https://www.youtube.com/watch?v=fR5XhHYzUK0). 28 | - [ ] I have searched [open and closed issues](https://github.com/the-raspberry-pi-guy/lcd/issues) for either an identical or similar bug before reporting this new one. 29 | - [ ] I have used Python 3.x 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/demo.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Demo 3 | about: Issues specifically related to a demo Python file 4 | title: '' 5 | labels: demo 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the issue** 11 | Add a clear and concise description of the issue here. 12 | 13 | **Demo** 14 | - File: `add the filename here` 15 | - (Optional.) Author: 16 | 17 | **Executed command and associated error** 18 | ``` 19 | add the executed command here 20 | ``` 21 | ``` 22 | add the associated error here 23 | ``` 24 | 25 | **Host and software info** 26 | - RPi board version: `add here` 27 | - OS version: `add here` 28 | - Python version : `add here` 29 | 30 | **Checklist** 31 | - [ ] I have watched and followed the instructions in the [Youtube tutorial](https://www.youtube.com/watch?v=fR5XhHYzUK0). 32 | - [ ] I have searched [open and closed issues](https://github.com/the-raspberry-pi-guy/lcd/issues) for either an identical or similar bug before reporting this new one. 33 | - [ ] I have used Python 2.7 34 | 35 | **Additional context** 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /demo_clock.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # Simple clock program. Writes the exact time. 4 | # Demo program for the I2C 16x2 Display from Ryanteck.uk 5 | # Created by Matthew Timmons-Brown for The Raspberry Pi Guy YouTube channel 6 | 7 | # Import necessary libraries for communication and display use 8 | import drivers 9 | from time import sleep 10 | from datetime import datetime 11 | 12 | # Load the driver and set it to "display" 13 | # If you use something from the driver library use the "display." prefix first 14 | display = drivers.Lcd() 15 | 16 | try: 17 | print("Writing to display") 18 | display.lcd_display_string("No time to waste", 1) # Write line of text to first line of display 19 | while True: 20 | # Write just the time to the display 21 | display.lcd_display_string(str(datetime.now().time()), 2) 22 | # Uncomment the following line to loop with 1 sec delay 23 | # sleep(1) 24 | except KeyboardInterrupt: 25 | # If there is a KeyboardInterrupt (when you press ctrl+c), exit the program and cleanup 26 | print("Cleaning up!") 27 | display.lcd_clear() 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: 'Bugs refer to replicable issues directly related to the latest version of 4 | one or more of the following files: (a) configs/*; (b) drivers/*; (c) install.sh; 5 | and (d) setup.sh' 6 | title: '' 7 | labels: bug 8 | assignees: '' 9 | 10 | --- 11 | 12 | **Describe the bug** 13 | Add a clear and concise description of the bug here. 14 | 15 | **Executed command and associated error** 16 | ``` 17 | add the executed command here 18 | ``` 19 | ``` 20 | add the associated error here 21 | ``` 22 | 23 | **Host and software info** 24 | - RPi board version: `add here` 25 | - OS version: `add here` 26 | - (If Python related.) Python version : `add here` 27 | - (If shell related.) Shell: `add name here and version` 28 | 29 | 30 | **Checklist** 31 | - [ ] I have watched and followed the instructions in the [Youtube tutorial](https://www.youtube.com/watch?v=fR5XhHYzUK0). 32 | - [ ] I have searched [open and closed issues](https://github.com/the-raspberry-pi-guy/lcd/issues) for either an identical or similar bug before reporting this new one. 33 | - [ ] (If Python related.) I have used Python 2.7 34 | 35 | **Additional context** 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /demo_lcd_extended_strings.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # Extended strings program. Writes and updates special (extended) strings 4 | # with placeholders "{}" so, that it is possible to draw any character from the 5 | # characters table using a caharcter code. 6 | # Demo program for the I2C 16x2 Display from Ryanteck.uk 7 | # Created by Matthew Timmons-Brown for The Raspberry Pi Guy YouTube channel 8 | 9 | # Import necessary libraries for communication and display use 10 | import drivers 11 | from time import sleep 12 | 13 | # Load the driver and set it to "display" 14 | # If you use something from the driver library use the "display." prefix first 15 | display = drivers.Lcd() 16 | 17 | # Main body of code 18 | try: 19 | while True: 20 | # Remember that your sentences can only be 16 characters long! 21 | print("Writing simple string") 22 | display.lcd_display_string("Simple string", 1) # Write line of text to first line of display 23 | display.lcd_display_extended_string("Ext. str:{0xEF}{0xF6}{0xA5}{0xDF}{0xA3}", 2) # Write line of text to second line of display 24 | sleep(2) # Give time for the message to be read 25 | except KeyboardInterrupt: 26 | # If there is a KeyboardInterrupt (when you press ctrl+c), exit the program and cleanup 27 | print("Cleaning up!") 28 | display.lcd_clear() 29 | -------------------------------------------------------------------------------- /demo_lcd.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # Simple string program. Writes and updates strings. 4 | # Demo program for the I2C 16x2 Display from Ryanteck.uk 5 | # Created by Matthew Timmons-Brown for The Raspberry Pi Guy YouTube channel 6 | 7 | # Import necessary libraries for communication and display use 8 | import drivers 9 | from time import sleep 10 | 11 | # Load the driver and set it to "display" 12 | # If you use something from the driver library use the "display." prefix first 13 | display = drivers.Lcd() 14 | 15 | # Main body of code 16 | try: 17 | while True: 18 | # Remember that your sentences can only be 16 characters long! 19 | print("Writing to display") 20 | display.lcd_display_string("Greetings Human!", 1) # Write line of text to first line of display 21 | display.lcd_display_string("Demo Pi Guy code", 2) # Write line of text to second line of display 22 | sleep(2) # Give time for the message to be read 23 | display.lcd_display_string("I am a display!", 1) # Refresh the first line of display with a different message 24 | sleep(2) # Give time for the message to be read 25 | display.lcd_clear() # Clear the display of any data 26 | sleep(2) # Give time for the message to be read 27 | except KeyboardInterrupt: 28 | # If there is a KeyboardInterrupt (when you press ctrl+c), exit the program and cleanup 29 | print("Cleaning up!") 30 | display.lcd_clear() 31 | -------------------------------------------------------------------------------- /demo_scrolling_text.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Example: Scrolling text on display if the string length is major than columns in display. 5 | # Created by Dídac García. 6 | 7 | # Import necessary libraries for communication and display use 8 | import drivers 9 | from time import sleep 10 | 11 | # Load the driver and set it to "display" 12 | # If you use something from the driver library use the "display." prefix first 13 | display = drivers.Lcd() 14 | 15 | # Main body of code 16 | try: 17 | print("Press CTRL + C to stop this script!") 18 | 19 | def long_string(display, text='', num_line=1, num_cols=16): 20 | """ 21 | Parameters: (driver, string to print, number of line to print, number of columns of your display) 22 | Return: This function send to display your scrolling string. 23 | """ 24 | if len(text) > num_cols: 25 | display.lcd_display_string(text[:num_cols], num_line) 26 | sleep(1) 27 | for i in range(len(text) - num_cols + 1): 28 | text_to_print = text[i:i+num_cols] 29 | display.lcd_display_string(text_to_print, num_line) 30 | sleep(0.2) 31 | sleep(1) 32 | else: 33 | display.lcd_display_string(text, num_line) 34 | 35 | 36 | # Example of short string 37 | long_string(display, "Hello World!", 1) 38 | sleep(1) 39 | 40 | # Example of long string 41 | long_string(display, "Hello again. This is a long text.", 2) 42 | display.lcd_clear() 43 | sleep(1) 44 | 45 | while True: 46 | # An example of infinite scrolling text 47 | long_string(display, "Hello friend! This is a long text!", 2) 48 | except KeyboardInterrupt: 49 | # If there is a KeyboardInterrupt (when you press ctrl+c), exit the program and cleanup 50 | print("Cleaning up!") 51 | display.lcd_clear() 52 | -------------------------------------------------------------------------------- /demo_forex.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import drivers 5 | import time 6 | import requests 7 | import datetime 8 | from bs4 import BeautifulSoup 9 | 10 | display = drivers.Lcd() 11 | sleepSecond = 1 12 | minute = 60 13 | iteration = minute/sleepSecond 14 | 15 | def GetTime(): 16 | currentTime = datetime.datetime.now() 17 | return currentTime.strftime("%d.%m %a %H:%M") 18 | 19 | def PrintTime(): 20 | display.lcd_display_string(GetTime(), 1) 21 | 22 | def PrintCurrency(currency): 23 | display.lcd_display_string(currency, 2) 24 | 25 | def PrintScreen(currency): 26 | display.lcd_clear() 27 | PrintTime() 28 | PrintCurrency(currency) 29 | 30 | def GetCurrencyList(): 31 | try: 32 | request = requests.get("https://www.investing.com/currencies/") 33 | html_content = request.content 34 | # parse content 35 | soup = BeautifulSoup(html_content, 'html.parser') 36 | table = soup.find('table', id='cr1') 37 | rows = table.find('tbody').find_all('tr') 38 | currencies_list = {} 39 | for row in rows: 40 | cells = row.find_all('td') 41 | pair = cells[1].find('a').text.strip() 42 | value = cells[2].text.strip() 43 | currencies_list[pair] = value 44 | return currencies_list 45 | except Exception as e: 46 | print(f"Failed to get currency list\n Error: {e}") 47 | return False 48 | 49 | # main logic 50 | try: 51 | while True: 52 | currencyList = GetCurrencyList() 53 | if currencyList: 54 | for i in range(int(iteration/len(currencyList))): 55 | for item in currencyList: 56 | PrintScreen(f"{item} {currencyList.get(item)}") 57 | time.sleep(sleepSecond) 58 | else: 59 | display.lcd_clear() 60 | PrintTime() 61 | time.sleep(sleepSecond) 62 | 63 | except KeyboardInterrupt: 64 | print("Cleaning up!") 65 | display.lcd_clear() -------------------------------------------------------------------------------- /demo_netmonit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import drivers 4 | from time import sleep 5 | from os import devnull 6 | from subprocess import call, check_output 7 | 8 | 9 | def cleanup(): 10 | display.lcd_clear() 11 | 12 | 13 | def end(msg=None, status=0): 14 | cleanup() 15 | display.lcd_display_string('{:^16}'.format('# Bye message: #'), 1) 16 | display.lcd_display_string('{:^16}'.format(msg), 2) 17 | exit(status) 18 | 19 | 20 | # Returns True if host responds to a ping request within the timeout interval 21 | def ping(host, timeout=3): 22 | return call(['ping', '-c', '1', '-W', str(timeout), str(host)], 23 | stdout=open(devnull, 'w'), 24 | stderr=open(devnull, 'w')) == 0 25 | 26 | 27 | # Returns True if host has given port open 28 | def nc(host, port, timeout=3): 29 | return call(['nc', '-z', '-w', str(timeout), str(host), str(port)], stderr=open(devnull, 'w')) == 0 30 | 31 | 32 | # assumes that running 16x2 LCD 33 | def lcd_print(top=None, bottom=None, delay=5): 34 | display.lcd_clear() 35 | display.lcd_display_string('{:^16}'.format(top), 1) 36 | # scroll second line if more than 16 chars 37 | if len(bottom) > 16: 38 | display.lcd_display_string(bottom[:16], 2) 39 | for i in range(len(bottom) - 15): 40 | display.lcd_display_string(bottom[i:i+16], 2) 41 | sleep(0.5) 42 | else: 43 | display.lcd_display_string('{:^16}'.format(bottom), 2) 44 | sleep(delay) 45 | 46 | 47 | def main(): 48 | try: 49 | lcd_print(top="## Welcome to ##", bottom="## NetMonitor ##", delay=20) 50 | lcd_print(top="# Who am I?? #", 51 | bottom="I am {0} at {1}".format(check_output(['hostname']).split()[0], 52 | check_output(['hostname', '-I']).split()[0]), 53 | delay=10) 54 | while True: 55 | # use ping for hosts 56 | for host, address in hosts.items(): 57 | lcd_print(top="# NetMonitor #", 58 | bottom="{} is UP".format(host) if ping(address) else "{} is DOWN".format(host)) 59 | # use netcat for services 60 | for service, address in services.items(): 61 | lcd_print(top="# NetMonitor #", 62 | bottom="{} is UP".format(service) if nc(address['ip'], address['port']) else "{} is DOWN".format(service)) 63 | except KeyboardInterrupt: 64 | end('Signal to stop', 0) 65 | except (RuntimeError, IOError): 66 | end('I2C bus error', 1) 67 | 68 | 69 | if __name__ == "__main__": 70 | # create a display object from the Lcd class 71 | display = drivers.Lcd() 72 | # customizable dict of unique, pingable hosts 73 | hosts = { 74 | 'Internet': '8.8.8.8', 75 | 'Firewall': '192.168.1.1', 76 | 'NAS': '192.168.1.2' 77 | } 78 | # customizable dict of unique, netcatable services 79 | services = { 80 | 'Cameras': {'ip': '192.168.1.2', 'port': '8000'}, 81 | 'Plex': {'ip': '192.168.1.2', 'port': '32400'} 82 | } 83 | main() 84 | -------------------------------------------------------------------------------- /demo_lcd_backlight.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # Simple string program. Writes and updates strings. 4 | # Demo program for the I2C 16x2 Display from Ryanteck.uk 5 | # Created by Matthew Timmons-Brown for The Raspberry Pi Guy YouTube channel 6 | # Backlight: Enhanced by TOMDENKT - backlight control (on/off) 7 | # Backlight: lcd_backlight(1) = ON, lcd_backlight(0) = OFF 8 | # Backlight: Usage, if lcddriver is set to "display" (like example below) 9 | # Backlight: display.lcd_backlight(0) # Turn backlight off 10 | # Backlight: display.lcd_backlight(1) # Turn backlight on 11 | 12 | # If drivers/i2c_dev.py is NOT in same folder with your scripts, 13 | # uncomment below and set path to i2c_dev, e.g. "/home/pi/lcd" 14 | # import sys 15 | # sys.path.append("/home/pi/lcd") 16 | 17 | # Import necessary libraries for communication and display use 18 | import drivers 19 | from time import sleep 20 | 21 | # Load the driver and set it to "display" 22 | # If you use something from the driver library use the "display." prefix first 23 | display = drivers.Lcd() 24 | 25 | # Main body of code 26 | try: 27 | print("Press CTRL + C to quit program") 28 | while True: 29 | # Remember that your sentences can only be 16 characters long! 30 | print("Loop: Writing to display and toggle backlight...") 31 | display.lcd_backlight(1) # Make sure backlight is on / turn on 32 | sleep(0.5) # Waiting for backlight toggle 33 | display.lcd_backlight(0) # Turn backlight off 34 | sleep(0.5) # Waiting for turning backlight on again 35 | display.lcd_backlight(1) # Turn backlight on again 36 | sleep(1) # Waiting for text 37 | display.lcd_display_string("Demo Backlight", 1) # Write line of text to first line of display 38 | display.lcd_display_string("Control ON/OFF", 2) # Write line of text to second line of display 39 | sleep(2) # Waiting for backlight toggle 40 | display.lcd_backlight(0) # Turn backlight off 41 | sleep(0.5) # Waiting for turning backlight on again 42 | display.lcd_backlight(1) # Turn backlight on again 43 | sleep(0.5) # Waiting for turning backlight off again 44 | display.lcd_backlight(0) # Turn backlight off 45 | sleep(0.5) # Waiting for turning backlight on again 46 | display.lcd_backlight(1) # Turn backlight on again 47 | sleep(2) # Give time for the message to be read 48 | display.lcd_display_string("I am a display! ", 1) # Refresh the first line of display with a different message 49 | display.lcd_display_string("Demo Backlight", 2) # Refresh the second line of display with a different message 50 | sleep(2) # Give time for the message to be read 51 | display.lcd_clear() # Clear the display of any data 52 | sleep(1) # Give time for the message to be read 53 | display.lcd_backlight(0) # Turn backlight off 54 | sleep(1.5) # Waiting until restart 55 | except KeyboardInterrupt: 56 | # If there is a KeyboardInterrupt (when you press CTRL + C), exit the program and cleanup 57 | print("Exit and cleaning up!") 58 | display.lcd_clear() 59 | # Make sure backlight is on / turn on by leaving 60 | display.lcd_backlight(1) 61 | -------------------------------------------------------------------------------- /demo_lcd_custom_characters.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # Custom characters string program. Writes strings with custom characters, that can't 4 | # be found in the characters table. It is possible to use a custom characters by definig 5 | # it's code on paseholder whyle useng and extended string. 6 | # Appearance of the custom characters can be defined by redifenition of the fields of 7 | # CustomCaracters class instance. 8 | # Codes of the custom characters, that can be used in placeholders are the following: 9 | # 1st - {0x00}, 2nd - {0x01}, 3rd - {0x02}, 4th - {0x03}, 5th - {0x04}, 10 | # 6th - {0x05}, 7th - {0x06} and 8th - {0x07} 11 | # Remember to use method load_custom_characters_data() to load the custom characters 12 | # data into the CG RAM. 13 | # Demo program for the I2C 16x2 Display from Ryanteck.uk 14 | # Created by Dmitry Safonov 15 | 16 | # Import necessary libraries for communication and display use 17 | import drivers 18 | from time import sleep 19 | 20 | # Load the driver and set it to "display" 21 | # If you use something from the driver library use the "display." prefix first 22 | display = drivers.Lcd() 23 | 24 | # Create object with custom characters data 25 | cc = drivers.CustomCharacters(display) 26 | 27 | # Redefine the default characters: 28 | # Custom caracter #1. Code {0x00}. 29 | cc.char_1_data = ["01110", 30 | "01010", 31 | "01110", 32 | "00100", 33 | "11111", 34 | "11111", 35 | "11111", 36 | "11111"] 37 | 38 | # Custom caracter #2. Code {0x01}. 39 | cc.char_2_data = ["11111", 40 | "10101", 41 | "10101", 42 | "11111", 43 | "11111", 44 | "10101", 45 | "10101", 46 | "11111"] 47 | 48 | # Custom caracter #3. Code {0x02}. 49 | cc.char_3_data = ["10001", 50 | "10001", 51 | "10001", 52 | "11111", 53 | "11111", 54 | "11111", 55 | "11111", 56 | "11111"] 57 | 58 | # Custom caracter #4. Code {0x03}. 59 | cc.char_4_data = ["11111", 60 | "11011", 61 | "10001", 62 | "10001", 63 | "10001", 64 | "10001", 65 | "11011", 66 | "11111"] 67 | 68 | # Custom caracter #5. Code {0x04}. 69 | cc.char_5_data = ["00000", 70 | "00000", 71 | "11011", 72 | "11011", 73 | "00000", 74 | "10001", 75 | "01110", 76 | "00000"] 77 | 78 | # Custom caracter #6. Code {0x05}. 79 | cc.char_6_data = ["01010", 80 | "11111", 81 | "11111", 82 | "01110", 83 | "00100", 84 | "00000", 85 | "00000", 86 | "00000"] 87 | 88 | # Custom caracter #7. Code {0x06}. 89 | cc.char_7_data = ["11111", 90 | "11011", 91 | "10001", 92 | "10101", 93 | "10101", 94 | "10101", 95 | "11011", 96 | "11111"] 97 | 98 | # Custom caracter #8. Code {0x07}. 99 | cc.char_8_data = ["11111", 100 | "10001", 101 | "11111", 102 | "00000", 103 | "00000", 104 | "11111", 105 | "10001", 106 | "11111"] 107 | # Load custom characters data to CG RAM: 108 | cc.load_custom_characters_data() 109 | 110 | # Main body of code 111 | try: 112 | while True: 113 | # Remember that your sentences can only be 16 characters long! 114 | print("Printing custom characters:") 115 | display.lcd_display_string("Custom caracters:", 1) # Write line of text to first line of display 116 | display.lcd_display_extended_string("{0x00}{0x01}{0x02}{0x03}{0x04}{0x05}{0x06}{0x07}", 2) # Write line of text to second line of display 117 | sleep(2) # Give time for the message to be read 118 | except KeyboardInterrupt: 119 | # If there is a KeyboardInterrupt (when you press ctrl+c), exit the program and cleanup 120 | print("Cleaning up!") 121 | display.lcd_clear() 122 | -------------------------------------------------------------------------------- /demo_lcd_process_bar.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # Programm showing anumated progress bar using custom characters. 4 | # Demo program for the I2C 16x2 Display 5 | # Created by Dmitry Safonov 6 | 7 | # Import necessary libraries for communication and display use 8 | import drivers 9 | from time import sleep 10 | 11 | # Load the driver and set it to "display" 12 | # If you use something from the driver library use the "display." prefix first 13 | display = drivers.Lcd() 14 | 15 | # Create object with custom characters data 16 | cc = drivers.CustomCharacters(display) 17 | 18 | # Redefine the default characters that will be used to create process bar: 19 | # Left full character. Code {0x00}. 20 | cc.char_1_data = ["01111", 21 | "11000", 22 | "10011", 23 | "10111", 24 | "10111", 25 | "10011", 26 | "11000", 27 | "01111"] 28 | 29 | # Left empty character. Code {0x01}. 30 | cc.char_2_data = ["01111", 31 | "11000", 32 | "10000", 33 | "10000", 34 | "10000", 35 | "10000", 36 | "11000", 37 | "01111"] 38 | 39 | # Central full character. Code {0x02}. 40 | cc.char_3_data = ["11111", 41 | "00000", 42 | "11011", 43 | "11011", 44 | "11011", 45 | "11011", 46 | "00000", 47 | "11111"] 48 | 49 | # Central empty character. Code {0x03}. 50 | cc.char_4_data = ["11111", 51 | "00000", 52 | "00000", 53 | "00000", 54 | "00000", 55 | "00000", 56 | "00000", 57 | "11111"] 58 | 59 | # Right full character. Code {0x04}. 60 | cc.char_5_data = ["11110", 61 | "00011", 62 | "11001", 63 | "11101", 64 | "11101", 65 | "11001", 66 | "00011", 67 | "11110"] 68 | 69 | # Right empty character. Code {0x05}. 70 | cc.char_6_data = ["11110", 71 | "00011", 72 | "00001", 73 | "00001", 74 | "00001", 75 | "00001", 76 | "00011", 77 | "11110"] 78 | 79 | # Load custom characters data to CG RAM: 80 | cc.load_custom_characters_data() 81 | 82 | MIN_CHARGE = 0 83 | MAX_CHARGE = 100 84 | 85 | # Main body of code 86 | charge = 0 87 | charge_delta = 5 88 | bar_repr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 89 | 90 | try: 91 | while True: 92 | # Remember that your sentences can only be 16 characters long! 93 | display.lcd_display_string("Battery charge:", 1) 94 | 95 | # Render charge bar: 96 | bar_string = "" 97 | for i in range(10): 98 | if i == 0: 99 | # Left character 100 | if bar_repr[i] == 0: 101 | # Left empty character 102 | bar_string = bar_string + "{0x01}" 103 | else: 104 | # Left full character 105 | bar_string = bar_string + "{0x00}" 106 | elif i == 9: 107 | # Right character 108 | if bar_repr[i] == 0: 109 | # Right empty character 110 | bar_string = bar_string + "{0x05}" 111 | else: 112 | # Right full character 113 | bar_string = bar_string + "{0x04}" 114 | else: 115 | # Central character 116 | if bar_repr[i] == 0: 117 | # Central empty character 118 | bar_string = bar_string + "{0x03}" 119 | else: 120 | # Central full character 121 | bar_string = bar_string + "{0x02}" 122 | 123 | # Print the string to display: 124 | display.lcd_display_extended_string(bar_string + " {0}% ".format(charge), 2) 125 | 126 | # Update the charge and recalculate bar_repr 127 | charge += charge_delta 128 | if (charge >= MAX_CHARGE) or (charge <= MIN_CHARGE): 129 | charge_delta = -1 * charge_delta 130 | 131 | for i in range(10): 132 | if charge >= ((i + 1) * 10): 133 | bar_repr[i] = 1 134 | else: 135 | bar_repr[i] = 0 136 | 137 | # Wait for some time 138 | sleep(1) 139 | 140 | except KeyboardInterrupt: 141 | # If there is a KeyboardInterrupt (when you press ctrl+c), exit the program and cleanup 142 | print("Cleaning up!") 143 | display.lcd_clear() 144 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ########################################################################## 4 | # Automated Installer Program For I2C 16x2 LCD Screens 5 | # 6 | # Cloned and adapted from: Ryanteck LTD 7 | # 8 | # Author: Matthew Timmons-Brown (the-raspberry-pi-guy) 9 | # Contributors: https://github.com/the-raspberry-pi-guy/lcd/contributors 10 | # 11 | # Repo: https://github.com/the-raspberry-pi-guy/lcd 12 | ########################################################################## 13 | 14 | # lcd repo directory that contains the config files 15 | LCD_CONFIG_DIR="configs" 16 | # full path to RPi's boot config file 17 | RPI_CONFIG='/boot/config.txt' 18 | 19 | start () { 20 | echo "############################################" 21 | echo "# Automated installer for the I2C 16x2 LCD #" 22 | echo "############################################" 23 | echo "" 24 | } 25 | 26 | # takes msg ($1) and status ($2) as args 27 | end () { 28 | echo "" 29 | echo "############################################" 30 | echo "# Finished the setup script" 31 | echo "# Message: $1" 32 | echo "############################################" 33 | exit "$2" 34 | } 35 | 36 | # takes message ($1) and level ($2) as args 37 | message () { 38 | echo "[LCD] [$2] $1" 39 | } 40 | 41 | apt_install () { 42 | if ! apt update; then 43 | message "Unable to update APT." "ERROR" 44 | end "Check your Internet connection and try again." 1 45 | fi 46 | 47 | message "Installing I2C-Tools." "INFO" 48 | if ! apt install i2c-tools -y; then 49 | message "Unable to install pkg 'i2c-tools'. You'll need to enter the LCD address manually." "WARNING" 50 | fi 51 | 52 | message "Installing the 'smbus' pkg for Py2 and Py3." "INFO" 53 | if ! apt install python-smbus -y; then 54 | message "Unable to install pkg 'python-smbus'." "WARNING" 55 | fi 56 | if ! apt install python3-smbus -y; then 57 | message "Unable to install pkg 'python3-smbus'." "WARNING" 58 | fi 59 | } 60 | 61 | # takes a package ($1) as arg 62 | dpkg_check_installed () { 63 | if dpkg -l "$1" > /dev/null 2>&1; then return 0; else return 1; fi 64 | } 65 | 66 | # find line that contains $1 from file $2 and delete it permanently 67 | delete_line () { 68 | sed -i '/'"$1"'/d' "$2" 2> /dev/null 69 | } 70 | 71 | # takes path to a file as first argument ($1) and creates a global 72 | # $list array with non-empty and non-commented out lines as elements 73 | line_to_list () { 74 | if [[ -z "$1" || ! -f "$1" ]]; then return 1; fi 75 | 76 | unset list; list=(); local file="$1"; local line 77 | 78 | while read -r line; do 79 | if [[ -n "$line" && ! "$line" =~ ^\# ]]; then list+=("$line"); fi 80 | done < "$file" 81 | 82 | if [[ -z "${list[*]}" ]]; then return 1; fi 83 | } 84 | 85 | # takes path to an old modules file as first argument ($1) and a new file as second arg ($2). 86 | # it parses new and old files, appending missing modules to the old file instead of overwriting 87 | modules () { 88 | if [[ -z "$1" || -z "$2" ]]; then return 1; fi 89 | 90 | local old_file="$1"; local new_file="$2" 91 | 92 | if [[ ! -f "$old_file" || -z $(cat "$old_file") ]]; then 93 | cp "$new_file" "$old_file" 94 | else 95 | if line_to_list "$new_file"; then 96 | for m in "${list[@]}"; do 97 | if [[ ! $(cat "$old_file") =~ $m ]]; then echo "$m" | tee -a "$old_file" > /dev/null; fi 98 | done 99 | fi 100 | fi 101 | } 102 | 103 | # parse /boot/config.txt and append i2c config if missing 104 | i2c_boot_config () { 105 | local line; local i2c_reconfig; local i2c_reconfig_line 106 | 107 | while read -r line; do 108 | if [[ "$line" =~ ^dtparam=i2c(_arm){0,1}(=on|=1){0,1}$ ]]; then 109 | i2c_reconfig='false'; break 110 | elif [[ "$line" =~ ^dtparam=i2c(_arm){0,1}(=off|=0){1}$ ]]; then 111 | i2c_reconfig='true'; i2c_reconfig_line="$line"; break 112 | fi 113 | done < "$RPI_CONFIG" 114 | 115 | # backup config.txt 116 | cp "$RPI_CONFIG" "$RPI_CONFIG".backup 117 | 118 | if [[ "$i2c_reconfig" == 'true' ]]; then 119 | # delete i2c=off config and append i2c=on config 120 | delete_line "$i2c_reconfig_line" "$RPI_CONFIG" 121 | echo "dtparam=i2c" | tee -a "$RPI_CONFIG" > /dev/null 122 | elif [[ -z "$i2c_reconfig" ]]; then 123 | # i2c config not found, append to file 124 | echo "dtparam=i2c" | tee -a "$RPI_CONFIG" > /dev/null 125 | fi 126 | 127 | message "Your $RPI_CONFIG was edited but there is a backup of the original file in $RPI_CONFIG.backup" 'INFO' 128 | } 129 | 130 | 131 | ############ 132 | # Main logic 133 | start 134 | 135 | # check again if user is root in case user is calling this script directly 136 | if [[ "$(id -u)" -ne 0 ]]; then message "User is not root." 'ERROR'; end 'Re-run as root or append sudo.' 1; fi 137 | 138 | trap "end 'Received a signal to stop.' 1" INT HUP TERM 139 | 140 | message 'Installing packages via APT.' 'INFO'; apt_install 141 | 142 | message "Checking Py 'smbus' installation." 'INFO' 143 | if dpkg_check_installed 'python-smbus' && dpkg_check_installed 'python3-smbus'; then 144 | message "You may use either 'python' or 'python3' to interface with the lcd." 'INFO' 145 | elif ! dpkg_check_installed 'python-smbus' && dpkg_check_installed 'python3-smbus'; then 146 | message "Use 'python3' to interface with the lcd." 'INFO' 147 | elif dpkg_check_installed 'python-smbus' && ! dpkg_check_installed 'python3-smbus'; then 148 | message "Use 'python' to interface with the lcd." 'INFO' 149 | elif ! dpkg_check_installed 'python-smbus' && ! dpkg_check_installed 'python3-smbus'; then 150 | # exit on lack of the 'smbus' pkg 151 | message "Unable to find either 'python-smbus' or 'python3-smbus' installed." 'ERROR' 152 | end 'Missing required Python package.' 1 153 | fi 154 | 155 | message "Checking enabled and blacklisted modules." 'INFO' 156 | if modules /etc/modules "$LCD_CONFIG_DIR"/modules; then 157 | message "Updated required modules in '/etc/modules.'" 'INFO' 158 | else 159 | message "There was an error while updating '/etc/modules. I2C might not work.'" 'WARNING' 160 | fi 161 | if modules /etc/modprobe.d/raspi-blacklist.conf "$LCD_CONFIG_DIR"/raspi-blacklist.conf; then 162 | message "Updated required modules in '/etc/modprobe.d/raspi-blacklist.conf.'" 'INFO' 163 | fi 164 | 165 | message "Enabling I2C on boot." 'INFO'; i2c_boot_config 166 | 167 | echo "#################################################################" 168 | echo "# All finished! Press any key to REBOOT now or Ctrl+c to abort. #" 169 | echo "#################################################################" 170 | 171 | read -n1 -s; reboot now 172 | -------------------------------------------------------------------------------- /drivers/i2c_dev.py: -------------------------------------------------------------------------------- 1 | from smbus import SMBus 2 | from RPi.GPIO import RPI_REVISION 3 | from time import sleep 4 | from re import findall, match 5 | from subprocess import check_output 6 | from os.path import exists 7 | 8 | # old and new versions of the RPi have swapped the two i2c buses 9 | # they can be identified by RPI_REVISION (or check sysfs) 10 | BUS_NUMBER = 0 if RPI_REVISION == 1 else 1 11 | 12 | # other commands 13 | LCD_CLEARDISPLAY = 0x01 14 | LCD_RETURNHOME = 0x02 15 | LCD_ENTRYMODESET = 0x04 16 | LCD_DISPLAYCONTROL = 0x08 17 | LCD_CURSORSHIFT = 0x10 18 | LCD_FUNCTIONSET = 0x20 19 | LCD_SETCGRAMADDR = 0x40 20 | LCD_SETDDRAMADDR = 0x80 21 | 22 | # flags for display entry mode 23 | LCD_ENTRYRIGHT = 0x00 24 | LCD_ENTRYLEFT = 0x02 25 | LCD_ENTRYSHIFTINCREMENT = 0x01 26 | LCD_ENTRYSHIFTDECREMENT = 0x00 27 | 28 | # flags for display on/off control 29 | LCD_DISPLAYON = 0x04 30 | LCD_DISPLAYOFF = 0x00 31 | LCD_CURSORON = 0x02 32 | LCD_CURSOROFF = 0x00 33 | LCD_BLINKON = 0x01 34 | LCD_BLINKOFF = 0x00 35 | 36 | # flags for display/cursor shift 37 | LCD_DISPLAYMOVE = 0x08 38 | LCD_CURSORMOVE = 0x00 39 | LCD_MOVERIGHT = 0x04 40 | LCD_MOVELEFT = 0x00 41 | 42 | # flags for function set 43 | LCD_8BITMODE = 0x10 44 | LCD_4BITMODE = 0x00 45 | LCD_2LINE = 0x08 46 | LCD_1LINE = 0x00 47 | LCD_5x10DOTS = 0x04 48 | LCD_5x8DOTS = 0x00 49 | 50 | # flags for backlight control 51 | LCD_BACKLIGHT = 0x08 52 | LCD_NOBACKLIGHT = 0x00 53 | SESSION_STATE_BACKLIGHT = '' 54 | 55 | En = 0b00000100 # Enable bit 56 | Rw = 0b00000010 # Read/Write bit 57 | Rs = 0b00000001 # Register select bit 58 | 59 | class I2CDevice: 60 | def __init__(self, addr=None, addr_default=None, bus=BUS_NUMBER): 61 | if not addr: 62 | # try autodetect address, else use default if provided 63 | try: 64 | self.addr = int('0x{}'.format( 65 | findall("[0-9a-z]{2}(?!:)", check_output(['/usr/sbin/i2cdetect', '-y', str(BUS_NUMBER)]).decode())[0]), base=16) \ 66 | if exists('/usr/sbin/i2cdetect') else addr_default 67 | except: 68 | self.addr = addr_default 69 | else: 70 | self.addr = addr 71 | self.bus = SMBus(bus) 72 | 73 | # write a single command 74 | def write_cmd(self, cmd): 75 | self.bus.write_byte(self.addr, cmd) 76 | sleep(0.0001) 77 | 78 | # write a command and argument 79 | def write_cmd_arg(self, cmd, data): 80 | self.bus.write_byte_data(self.addr, cmd, data) 81 | sleep(0.0001) 82 | 83 | # write a block of data 84 | def write_block_data(self, cmd, data): 85 | self.bus.write_block_data(self.addr, cmd, data) 86 | sleep(0.0001) 87 | 88 | # read a single byte 89 | def read(self): 90 | return self.bus.read_byte(self.addr) 91 | 92 | # read 93 | def read_data(self, cmd): 94 | return self.bus.read_byte_data(self.addr, cmd) 95 | 96 | # read a block of data 97 | def read_block_data(self, cmd): 98 | return self.bus.read_block_data(self.addr, cmd) 99 | 100 | 101 | class Lcd: 102 | def __init__(self, addr=None): 103 | self.addr = addr 104 | self.lcd = I2CDevice(addr=self.addr, addr_default=0x27) 105 | self.lcd_write(0x03) 106 | self.lcd_write(0x03) 107 | self.lcd_write(0x03) 108 | self.lcd_write(0x02) 109 | self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE) 110 | self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON) 111 | self.lcd_write(LCD_CLEARDISPLAY) 112 | self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT) 113 | sleep(0.2) 114 | 115 | # clocks EN to latch command 116 | def lcd_strobe(self, data): 117 | if SESSION_STATE_BACKLIGHT == 0: 118 | LCD = LCD_NOBACKLIGHT 119 | else: 120 | LCD = LCD_BACKLIGHT 121 | self.lcd.write_cmd(data | En | LCD) 122 | sleep(.0005) 123 | self.lcd.write_cmd(((data & ~En) | LCD)) 124 | sleep(.0001) 125 | 126 | def lcd_write_four_bits(self, data): 127 | if SESSION_STATE_BACKLIGHT == 0: 128 | LCD = LCD_NOBACKLIGHT 129 | else: 130 | LCD = LCD_BACKLIGHT 131 | self.lcd.write_cmd(data | LCD) 132 | self.lcd_strobe(data) 133 | 134 | # write a command to lcd 135 | def lcd_write(self, cmd, mode=0): 136 | self.lcd_write_four_bits(mode | (cmd & 0xF0)) 137 | self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0)) 138 | 139 | # put string function 140 | def lcd_display_string(self, string, line): 141 | if line == 1: 142 | self.lcd_write(0x80) 143 | if line == 2: 144 | self.lcd_write(0xC0) 145 | if line == 3: 146 | self.lcd_write(0x94) 147 | if line == 4: 148 | self.lcd_write(0xD4) 149 | for char in string: 150 | self.lcd_write(ord(char), Rs) 151 | 152 | # put extended string function. Extended string may contain placeholder like {0xFF} for 153 | # displaying the particular symbol from the symbol table 154 | def lcd_display_extended_string(self, string, line): 155 | if line == 1: 156 | self.lcd_write(0x80) 157 | if line == 2: 158 | self.lcd_write(0xC0) 159 | if line == 3: 160 | self.lcd_write(0x94) 161 | if line == 4: 162 | self.lcd_write(0xD4) 163 | # Process the string 164 | while string: 165 | # Trying to find pattern {0xFF} representing a symbol 166 | result = match(r'\{0[xX][0-9a-fA-F]{2}\}', string) 167 | if result: 168 | self.lcd_write(int(result.group(0)[1:-1], 16), Rs) 169 | string = string[6:] 170 | else: 171 | self.lcd_write(ord(string[0]), Rs) 172 | string = string[1:] 173 | 174 | # clear lcd and set to home 175 | def lcd_clear(self): 176 | self.lcd_write(LCD_CLEARDISPLAY) 177 | self.lcd_write(LCD_RETURNHOME) 178 | 179 | # backlight control (on/off) 180 | # options: lcd_backlight(1) = ON, lcd_backlight(0) = OFF 181 | def lcd_backlight(self, state): 182 | global SESSION_STATE_BACKLIGHT 183 | if state == 1: 184 | self.lcd.write_cmd(LCD_BACKLIGHT) 185 | elif state == 0: 186 | self.lcd.write_cmd(LCD_NOBACKLIGHT) 187 | 188 | if state == 1 or state == 0: # Save backlight settings 189 | SESSION_STATE_BACKLIGHT = state 190 | class CustomCharacters: 191 | def __init__(self, lcd): 192 | self.lcd = lcd 193 | # Data for custom character #1. Code {0x00}. 194 | self.char_1_data = ["11111", 195 | "10001", 196 | "10001", 197 | "10001", 198 | "10001", 199 | "10001", 200 | "10001", 201 | "11111"] 202 | # Data for custom character #2. Code {0x01} 203 | self.char_2_data = ["11111", 204 | "10001", 205 | "10001", 206 | "10001", 207 | "10001", 208 | "10001", 209 | "10001", 210 | "11111"] 211 | # Data for custom character #3. Code {0x02} 212 | self.char_3_data = ["11111", 213 | "10001", 214 | "10001", 215 | "10001", 216 | "10001", 217 | "10001", 218 | "10001", 219 | "11111"] 220 | # Data for custom character #4. Code {0x03} 221 | self.char_4_data = ["11111", 222 | "10001", 223 | "10001", 224 | "10001", 225 | "10001", 226 | "10001", 227 | "10001", 228 | "11111"] 229 | # Data for custom character #5. Code {0x04} 230 | self.char_5_data = ["11111", 231 | "10001", 232 | "10001", 233 | "10001", 234 | "10001", 235 | "10001", 236 | "10001", 237 | "11111"] 238 | # Data for custom character #6. Code {0x05} 239 | self.char_6_data = ["11111", 240 | "10001", 241 | "10001", 242 | "10001", 243 | "10001", 244 | "10001", 245 | "10001", 246 | "11111"] 247 | # Data for custom character #7. Code {0x06} 248 | self.char_7_data = ["11111", 249 | "10001", 250 | "10001", 251 | "10001", 252 | "10001", 253 | "10001", 254 | "10001", 255 | "11111"] 256 | # Data for custom character #8. Code {0x07} 257 | self.char_8_data = ["11111", 258 | "10001", 259 | "10001", 260 | "10001", 261 | "10001", 262 | "10001", 263 | "10001", 264 | "11111"] 265 | 266 | # load custom character data to CG RAM for later use in extended string. Data for 267 | # characters is hold in file custom_characters.txt in the same folder as i2c_dev.py 268 | # file. These custom characters can be used in printing of extended string with a 269 | # placeholder with desired character codes: 1st - {0x00}, 2nd - {0x01}, 3rd - {0x02}, 270 | # 4th - {0x03}, 5th - {0x04}, 6th - {0x05}, 7th - {0x06} and 8th - {0x07}. 271 | def load_custom_characters_data(self): 272 | self.chars_list = [self.char_1_data, self.char_2_data, self.char_3_data, 273 | self.char_4_data, self.char_5_data, self.char_6_data, 274 | self.char_7_data, self.char_8_data] 275 | 276 | # commands to load character adress to RAM srarting from desired base adresses: 277 | char_load_cmds = [0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78] 278 | for char_num in range(8): 279 | # command to start loading data into CG RAM: 280 | self.lcd.lcd_write(char_load_cmds[char_num]) 281 | for line_num in range(8): 282 | line = self.chars_list[char_num][line_num] 283 | binary_str_cmd = "0b000{0}".format(line) 284 | self.lcd.lcd_write(int(binary_str_cmd, 2), Rs) -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Community help is very much appreciated and there are many ways you can help developing and maintaining this repository. If you want to get involved, please read the following: 3 | - [Issues](#issues) 4 | - [Pull Requests](#pull-requests) 5 | 6 | --- 7 | 8 | ## Issues 9 | This repository allows users to create an issue in the [issues tab][issues-tab]. Any issue must be **directly related to a file hosted in this repo**, as opposed to an issue with LCD modules, wiring, the Raspberry Pi OS, and so on. 10 | 11 | ### Types of issues 12 | #### Bug 13 | Bugs refer to **replicable** issues directly related to **the latest version** of one or more of the following files: 14 | - [`configs/*`][configs-dir] 15 | - [`drivers/*`][drivers-dir] 16 | - [`install.sh`][install.sh] 17 | - [`setup.sh`][setup.sh] 18 | 19 | Before reporting a bug, make sure to double check the wiring and try different wires first, especially if the associated error is I/O related. Also, check the [issues tab][issues-tab] for open and closed issues that might be related to your own issue. 20 | 21 | When reporting a bug, it is crucial to add all the relevant information to allow other users, contributors, and maintainers to replicate the reported bug. At the very least, include the following information when reporting a bug: 22 | - A copy of the executed command and the entire error in [code blocks][gh-docs-code-blocks]. For example: 23 | ```sh 24 | python demo_clock.py 25 | ``` 26 | ```python 27 | Traceback (most recent call last): 28 | File "demo_clock.py", line 14, in display = drivers.Lcd() 29 | File "/home/pi/lcd/drivers/i2c_dev.py", line 103, in init self.lcd_write(0x03) 30 | File "/home/pi/lcd/drivers/i2c_dev.py", line 126, in lcd_write self.lcd_write_four_bits(mode | (cmd & 0xF0)) 31 | File "/home/pi/lcd/drivers/i2c_dev.py", line 121, in lcd_write_four_bits self.lcd.write_cmd(data | LCD_BACKLIGHT) 32 | File "/home/pi/lcd/drivers/i2c_dev.py", line 74, in write_cmd self.bus.write_byte(self.addr, cmd) 33 | IOError: [Errno 121] Remote I/O error 34 | ``` 35 | - Python version (`python --version`) 36 | - Information about the host machine and OS (`hostnamectl` or `lsb_release -a`) 37 | 38 | #### Compatibility 39 | The LCD driver was written for and tested with **Python 2.7**, as in the original [Youtube tutorial][youtube-tutorial] and scripts. Python 3.x may or may not work and even though you can still create an issue to describe a compatibility error, we cannot guarantee support. However, we're happy to incorporate any changes that adds compatibility with Python 3.x **as long as they do not break Python 2.7 usage**. Take a look at how to submit a PR in the [pull requests](#pull-requests) section if you want to help with that. 40 | 41 | #### Demo 42 | Demo issues are directly related to any of the hosted `demo_*.py` files. When creating such a demo issue, attach the same information as if you were creating a [bug](#bug) issue report. In addition, ping the user who introduced the demonstration file in the first place (e.g., for the `demo_netmonit.py`, refer to [@cgomesu][cgomesu]) or one of the demo contributors. (Of note, the maintainers and other contributors may not always be available to help with such issues because they are not always involved with the creation and maintenance of such demo files.) 43 | 44 | #### Documentation 45 | A documentation issue refers to typos or missing information in the [Wiki][wiki] or [README.md][readme] file. 46 | 47 | #### Other nature 48 | Any other issue that is directly related to this repository and was not covered before. 49 | 50 | [top :arrow_up:](#) 51 | 52 | --- 53 | 54 | ## Pull Requests 55 | A pull request (PR) allows you to push changes from your fork directly into [`the-raspberry-pi-guy/lcd`][lcd-repo] repository to make the changes available to other users. Multiple [types of PRs](#types-of-prs) can be submitted to this repository but regardless of the type, please observe the following: 56 | 57 | - **Ask before submitting a PR**. If it is a bug that you want to fix, then [create an issue](#issues) first, for example, and in addition to the usual [bug related information](#bug), append your suggested solution for it. Otherwise, you might waste time on something that the maintainers and contributors do not feel like merging into the project. 58 | 59 | - The maintainers try to keep the repo as compatible with the [original Youtube tutorial][youtube-tutorial] as possible. That is, avoid the introduction of changes that are **incompatible with the tutorial** at all cost. If that is not possible, it should be made explicit that the PR introduces changes incompatible with the tutorial and then it is your job to convince the maintainers that such changes are worthwhile. 60 | 61 | - Everything (from comments to code to documentation) should be **written in English**; 62 | 63 | - Always **fork** > **clone** your fork > configure the **remotes** > and then create a **new branch** from the latest `master` to work on the changes you want to make (e.g., `hotfix/i2c-bug`). This makes a world of difference when it is required to make additional changes to a PR and it helps keeping things organized. Here is an illustration of the standard procedure: 64 | 1. [Create a fork][gh-docs-fork] of the project using your GitHub account (``); 65 | 66 | 2. Clone your fork on your host machine, move into its root directory, and configure the upstream remote: 67 | ```sh 68 | git clone git@github.com:/lcd.git 69 | cd lcd 70 | git remote add upstream https://github.com/the-raspberry-pi-guy/lcd.git 71 | ``` 72 | 3. If it has been a while since you last cloned, fetch the latest changes from upstream and merge them to the master branch: 73 | ```sh 74 | git checkout master 75 | git pull upstream master 76 | ``` 77 | 4. From an up-to-date master, create a new branch (``) for working on your changes: 78 | ```sh 79 | git checkout -b 80 | ``` 81 | 5. **Do the work**. You can always check the current branch you are on as follows: 82 | ```sh 83 | git branch 84 | ``` 85 | 86 | - When making changes, **stick to the point** of your PR. That is, avoid making changes in other parts of the code if the remaining code works as intended when all is said and done; 87 | 88 | - [Write meaningful][commit-messages] commit messages to your changes and use formal language. Those messages are what will eventually become part of the repo's commit history and they help others understand what, how, and when. For this reason, make use of [`git rebase`][gh-docs-rebase] to organize your commits before pushing them anywhere else. In addition, please try to keep the number of commits to a minimum; 89 | 90 | - When you're done staging and committing the changes, catch up with the upstream (fetch and then merge/rebase) if you expect that there will be conflicts upon merging the changes from your PR (e.g., if someone has worked on the same file and code block as you after you branched out from master, then this will create a conflict later on when trying to merge the changes from your PR into the original repo's master): 91 | ```sh 92 | git pull --rebase upstream master 93 | ``` 94 | and if there are conflicts, [resolve them][gh-docs-conflicts]: 95 | 1. `git status` to show the conflict; 96 | 2. Edit the conflicting file to resolve the lines between `<<< || >>>` and save the file; 97 | 3. `git add ` to add the change (or `git rm ` if resolving the conflict means removing the file); 98 | 4. `git rebase --continue` to continue the rebasing; 99 | 5. Repeat the steps 2-4 until all conflicts are resolved. 100 | 101 | 102 | - When you're done resolving any possible conflicts, push your `` branch to your fork: 103 | ```sh 104 | git push origin 105 | ``` 106 | (If you had to resolve any conflicts before pushing your changes, you should see an error because you rewrote history. To solve this, simply append `--force-with-lease` to the previous command.) 107 | 108 | - Navigate to your fork:`TOPIC` branch and [open a PR][gh-docs-pr] to the `the-raspberry-pi-guy/lcd:master` branch. Then, at the very least, add the following to the PR: 109 | - **Clear title** for the main change(s); 110 | - Brief description **the reason** for the PR; 111 | - **Summary** of the changes. 112 | 113 | 114 | - The commit history in your PR should contain **only changes made by you**. Otherwise, do not submit the PR and instead, go back to your local `TOPIC` branch and fix the commit history before moving on with the PR. 115 | 116 | - Be mindful that the maintainers and contributors are not always available for multiple reasons. Therefore, it might take a few days until you get any sort of feedback on your PR. **Please be patient**. 117 | 118 | ### Types of PRs 119 | There are six types of PRs that can be submitted to this repository: 120 | 1. Demo submission: 121 | - Should introduce aspects not covered in previous demonstration files. See the `demo_*.py` files for reference. 122 | 2. Documentation: 123 | - Typo fix; 124 | - Any new (or improvement of existing) explanation of how to use the LCD driver. 125 | 3. Bug fix; 126 | 4. Compatibility improvement; 127 | 5. New feature; 128 | 6. New implementation. 129 | 130 | Of note, PRs that **introduce new features or implementations** should be well justified and explained in detail. 131 | 132 | ### Reviews 133 | If you have contributed to the repository before and noticed that there's a new PR without a reply from the maintainers, then you can help out by [writing a review][gh-docs-review]. The reviews do not need to be comprehensive and instead, you can focus on the aspects that you are more comfortable with but in such a case, be explicit about what part of the PR you chose to review. 134 | 135 | Keep in mind that English is not everyone's first language but everything should be written in English. Please be patient but do point out grammatical errors that should be fixed before merging changed files into the repo's `master` branch. 136 | 137 | [top :arrow_up:](#) 138 | 139 | --- 140 | 141 | Lastly, this should go without saying but **be nice** with other users. We welcome everyone from any sort of background. If you are an experienced user, please consider spending a few additional minutes to explain your rational and provide more information than usual. For many, this is an opportunity to get familiar with GNU/Linux, cli, git, github, and languages like Python. 142 | 143 | Thank you for taking the time to learn how to contribute! This is an old repository--the [initial commit][initial-commit] was made in 2015--and it has been maintained by and received contributions from people all over the globe. Feel free to reach out if you feel you can improve and build upon what has been done so far. 144 | 145 | :earth_americas::rocket: 146 | 147 | 148 | [cgomesu]: https://github.com/cgomesu 149 | [commit-messages]: https://chris.beams.io/posts/git-commit/ 150 | [configs-dir]: https://github.com/the-raspberry-pi-guy/lcd/tree/master/configs 151 | [drivers-dir]: https://github.com/the-raspberry-pi-guy/lcd/tree/master/drivers 152 | [gh-docs-code-blocks]: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks 153 | [gh-docs-conflicts]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line 154 | [gh-docs-fork]: https://docs.github.com/en/get-started/quickstart/fork-a-repo 155 | [gh-docs-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request 156 | [gh-docs-rebase]: https://docs.github.com/en/get-started/using-git/about-git-rebase 157 | [gh-docs-review]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews 158 | [initial-commit]: https://github.com/the-raspberry-pi-guy/lcd/commit/a0100b81c403a2f1b86e87e70ca1fe6b75a983df 159 | [install.sh]: https://github.com/the-raspberry-pi-guy/lcd/blob/master/install.sh 160 | [issues-tab]: https://github.com/the-raspberry-pi-guy/lcd/issues 161 | [lcd-repo]: https://github.com/the-raspberry-pi-guy/lcd 162 | [readme]: https://github.com/the-raspberry-pi-guy/lcd/blob/master/README.md 163 | [setup.sh]: https://github.com/the-raspberry-pi-guy/lcd/blob/master/setup.sh 164 | [youtube-tutorial]: https://www.youtube.com/watch?v=fR5XhHYzUK0 165 | [wiki]: https://github.com/the-raspberry-pi-guy/lcd/wiki 166 | -------------------------------------------------------------------------------- /demo_tiny_dashboard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import drivers 3 | from datetime import date 4 | from datetime import time 5 | from datetime import datetime 6 | import time 7 | import threading 8 | import requests 9 | import socket 10 | 11 | ''' 12 | This is a script that takes info from some apis and shows it in the 16*2 display. 13 | It uses the following apis: 14 | 15 | QUOTABLE.IO / https://github.com/lukePeavey/quotable 16 | Quotable is a free, open source quotations API that provides famous quotes from well known people. 17 | 18 | EXCHANGERATE-API.COM / FREE.CURRENCYCONVERTERAPI.COM 19 | There are a lot of currency apis but these ones offer free currency exchange info 20 | 21 | OPENWEATHERMAP.ORG 22 | Weather info, forecasts, etc. 23 | 24 | It also shows the last three characters from your ip address, the date in DDMM format 25 | and the hour in HH:MM format 26 | ''' 27 | 28 | #------------------------------USER VARIABLES--------------------------------------- 29 | 30 | # Get your api tokens from each site, then put them here, between the quotes. At the time of coding, 31 | # theysaidso can be freely used without the need of a token, given we respect their restrictions. 32 | api_OpenWeather_token="" 33 | api_freeCurrConv_token="" 34 | api_ExchangeRateAPI_token="" 35 | 36 | # minimum and maximum length of the quote obtained from quotable.io 37 | quote_minLength=0 38 | quote_maxLength=140 39 | 40 | # api_OpenWeather_yourCity : Search for your city in openweathermap.org, then put the code here with no space in between. 41 | api_OpenWeather_yourCity="London,GB" 42 | 43 | # Put the currency pair here to see the exchange rate. An amount of 1 curr1 will be converted to curr2 44 | # refer to each api for the list of supported currencies 45 | curr1="USD" 46 | curr2="GBP" 47 | 48 | #------------NOTHING ELSE NEEDS TO BE CHANGED BEYOND THIS POINT--------------------- 49 | 50 | # start the lcd driver 51 | display = drivers.Lcd() 52 | 53 | # Global variables go here. These store the info from the apis that we want to display. Do not put anything here. 54 | disp_string_quote="" 55 | disp_string_weatherInfo="" 56 | disp_string_convCur_value="" 57 | 58 | ### HERE WE GET THE INFO WE NEED FROM EITHER THE SYSTEM OR THE APIS ### 59 | def thread_get_quotable(): 60 | ''' 61 | This is a thread that gets a random quote from quotable.io every 10 minutes 62 | ''' 63 | quotable_baseurl="https://api.quotable.io/quotes/random?minLength={}&maxLength={}" 64 | 65 | while True: 66 | global disp_string_quote 67 | try: 68 | api_quotable_req=requests.get(quotable_baseurl.format(quote_minLength, quote_maxLength)) 69 | print("thread_get_quotable got response code: " + str(api_quotable_req.status_code)) # response 70 | api_quotable_json=api_quotable_req.json() # convert it to json 71 | disp_string_quote=api_quotable_json[0]['content'] + " - " + api_quotable_json[0]['author'] 72 | print(str(datetime.now()) + " " + "thread_get_quotable got a quote to display:\n" + "\t\t" + disp_string_quote) 73 | time.sleep(600) 74 | except KeyError: 75 | disp_string_quote="There is an error, we need to review log and debug" 76 | print(str(datetime.now()) + " " + "thread_get_quotable got an API error:\n" + "\t\t" + disp_string_quote) 77 | time.sleep(300) 78 | except ConnectionError: 79 | disp_string_quote="Connection Error while getting the quote. Will try again in 10 seconds." 80 | print(str(datetime.now()) + " " + "thread_get_quotable got a ConnectionError. will try again in 10 seconds.") 81 | time.sleep(10) 82 | except ValueError: 83 | disp_string_quote="JSON Decode Error while getting the quote. Will try again in 20 seconds." 84 | print(str(datetime.now()) + " " + "thread_get_quotable got a ValueError. will try again in 20 seconds.") 85 | time.sleep(20) 86 | except: 87 | disp_string_quote="There is an unknown error, we need to review log and debug" 88 | 89 | def thread_get_currency_conversion(tokenERA=api_ExchangeRateAPI_token, tokenFCC=api_freeCurrConv_token, c1=curr1, c2=curr2): 90 | ''' 91 | This thread gets the 1 usd to cop conversion. 92 | Using ExchangeRate API (ERA) as first option. 93 | Using Free Currency Convert (FCC) as fallback, 94 | Even though FCC updates every hour, it is slow. 95 | ERA updates once every day. 96 | We are prioritizing ERA over FCC. 97 | ''' 98 | print(str(datetime.now()) + " " + "starting the currency conversion request") 99 | while True: 100 | global disp_string_convCur_value 101 | try: 102 | try: 103 | base_url="https://v6.exchangerate-api.com/v6/{}/pair/{}/{}" 104 | api_ExchangeRateAPI_request=requests.get(base_url.format(tokenERA, c1, c2)) 105 | api_ExchangeRateAPI_json=api_ExchangeRateAPI_request.json() 106 | disp_string_convCur_value="1"+ c1 + ":" + str(round(api_ExchangeRateAPI_json['conversion_rate'],2)) + c2 107 | print(str(datetime.now()) + " " + "thr3_dollarconv got an update from ERA: " + disp_string_convCur_value) 108 | time.sleep(86400) 109 | except (ConnectionError, KeyError, ValueError) as e: 110 | base_url="https://free.currconv.com/api/v7/convert?q={}_{}&compact=ultra&apiKey={}" 111 | api_freeCurrConv_request=requests.get(base_url.format(c1, c2, tokenFCC)) 112 | api_freeCurrConv_json=api_freeCurrConv_request.json() 113 | fcc_rate= c1 + "_" + c2 114 | disp_string_convCur_value="1" + c1 + ":" + str(round(api_freeCurrConv_json[fcc_rate],2)) + c2 115 | print(str(datetime.now()) + " " + "thr3_dollarconv got an update from FCC: " + disp_string_convCur_value) 116 | time.sleep(3600) 117 | except KeyError: 118 | disp_string_convCur_value="JSON Key error on exchange rate response. Check log. Retrying in 5 min." 119 | print(str(datetime.now()) + " thr3_dollarconv got an API error. \nFCC:" + str(api_freeCurrConv_json) + "\nERA:" + str(api_ExchangeRateAPI_json)) 120 | time.sleep(300) 121 | except ConnectionError: 122 | disp_string_convCur_value="Connection Error while getting the exchange rate. Will try again in 10 seconds." 123 | print(str(datetime.now()) + " thr3_dollarconv got a ConnectionError. will try again in 10 seconds.") 124 | time.sleep(10) 125 | except ValueError: 126 | disp_string_convCur_value="JSON Decode Error while getting the exchange rate. Will try again in 20 seconds." 127 | print(str(datetime.now()) + " thr3_dollarconv got a ValueError. will try again in 20 seconds.") 128 | time.sleep(20) 129 | 130 | # We have to find the possible errors that can happen. 131 | 132 | def thread_get_weather_info(tokenOWM=api_OpenWeather_token, cityid=api_OpenWeather_yourCity): 133 | ''' 134 | get the weather info for my city 135 | ''' 136 | global disp_string_weatherInfo 137 | while True: 138 | try: 139 | base_url='https://api.openweathermap.org/data/2.5/weather?q={}&units=metric&appid={}' 140 | api_OpenWeather_request=requests.get(base_url.format(cityid, tokenOWM)) 141 | api_OpenWeather_json=api_OpenWeather_request.json() 142 | disp_string_weatherInfo=str(round(api_OpenWeather_json['main']['temp'])) + "￟C - " + api_OpenWeather_json['weather'][0]['description'] + " - " + api_OpenWeather_json['name'] 143 | print(str(datetime.now()) + " " + "thr4_weatherinfo got an update: " + disp_string_weatherInfo) 144 | time.sleep(600) 145 | except KeyError: 146 | disp_string_weatherInfo="JSON Key error on weather info response. Check log. Retrying in 5 min." 147 | print(str(datetime.now()) + " thr4_weatherinfo got an API error. \n" + str(api_OpenWeather_json)) 148 | time.sleep(360) 149 | except ConnectionError: 150 | disp_string_weatherInfo="Connection Error while getting the weather info. Will try again in 10 seconds." 151 | print(str(datetime.now()) + " thr4_weatherinfo got a ConnectionError. will try again in 10 seconds.") 152 | time.sleep(10) 153 | except ValueError: 154 | disp_string_weatherInfo="JSON Decode Error while getting the weather info. Will try again in 20 seconds." 155 | print(str(datetime.now()) + " thr4_weatherinfo got a ValueError. will try again in 20 seconds.") 156 | time.sleep(20) 157 | 158 | # Taken from the raspberry pi guy's sample, demo_scrollingtext.py 159 | def long_string(display, text='', num_line=2, num_cols=16, speed=0.1): 160 | """ 161 | Parameters: (driver, string to print, number of line to print, number of columns of your display) 162 | Return: This function send to display your scrolling string. 163 | """ 164 | if len(text) > num_cols: 165 | display.lcd_display_string(text[:num_cols], num_line) 166 | time.sleep(3) 167 | for i in range(len(text) - num_cols + 1): 168 | text_to_print = text[i:i+num_cols] 169 | display.lcd_display_string(text_to_print, num_line) 170 | time.sleep(speed) 171 | time.sleep(1) 172 | else: 173 | display.lcd_display_string(text, num_line) 174 | 175 | def get_ip(): 176 | '''function to get the ip of this machine 177 | source: https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib#answer-28950776''' 178 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 179 | try: 180 | # doesn't even have to be reachable 181 | s.connect(('10.255.255.255', 1)) 182 | IP = s.getsockname()[0] 183 | except: 184 | IP = '127.0.0.1' 185 | finally: 186 | s.close() 187 | return IP 188 | 189 | def first_line(): 190 | ''' 191 | The instruction that displays the info in the first line. 192 | I put it in this function to avoid repeating it. 193 | lcd_clear cleans the display so we need to repeat it. 194 | ''' 195 | # the date 196 | mytime=time.localtime() 197 | 198 | # This shows the last 3 characters of our IP, the day and the month, and lastly the hour, a semicolon and the minutes. 199 | my_ip = get_ip() 200 | display.lcd_display_string("i:" + my_ip[-3:] + " " + str(mytime.tm_mday).zfill(2) + str(mytime.tm_mon).zfill(2) + " " + str(mytime.tm_hour) + ":" + str(mytime.tm_min).zfill(2), 1) 201 | 202 | 203 | print("\nRPi TINY DASHBOARD FOR 16X2 DISPLAY.\n") 204 | 205 | # CREATING AND CALLING THREADS STARTS HERE 206 | if __name__=="__main__": 207 | # Start by declaring all these threads 208 | thr1_get_quotable=threading.Thread(target=thread_get_quotable, daemon=True) 209 | thr2_currconv=threading.Thread(target=thread_get_currency_conversion, daemon=True) 210 | thr3_weatherinfo=threading.Thread(target=thread_get_weather_info, daemon=True) 211 | 212 | # Then we start the threads that populate the variables 213 | # We wait while the global variables get populated, then start the threads. 214 | 215 | thr1_get_quotable.start() 216 | 217 | while disp_string_quote == "": 218 | # we wait for the quotes string to start the next thread 219 | pass 220 | else: 221 | thr2_currconv.start() 222 | 223 | while disp_string_convCur_value == "": 224 | # we wait for the conversion variable to get populated to start the next thread 225 | pass 226 | else: 227 | thr3_weatherinfo.start() 228 | 229 | # let's see what do we have 230 | while disp_string_weatherInfo == "": 231 | pass 232 | else: 233 | print("\nSENDING INITIAL INFO TO DISPLAY") 234 | 235 | # THIS IS WHERE WE SEND THE INFO TO THE DISPLAY 236 | try: 237 | while True: 238 | ''' display first line and quote ''' 239 | first_line() 240 | long_string(display, disp_string_quote, 2) 241 | time.sleep(2) 242 | display.lcd_clear() 243 | 244 | ''' display first line and weather info ''' 245 | first_line() 246 | long_string(display, disp_string_weatherInfo, 2) 247 | time.sleep(2) 248 | display.lcd_clear() 249 | 250 | ''' display first line and currency conversion ''' 251 | first_line() 252 | long_string(display, disp_string_convCur_value, 2) 253 | time.sleep(4) 254 | display.lcd_clear() 255 | 256 | except KeyboardInterrupt: 257 | print("\nCleaning the display!") 258 | display.lcd_clear() 259 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LCD 2 | 3 | This repository contains all the code for interfacing with a **16x2 character I2C liquid-crystal display (LCD)**. This accompanies my **Youtube tutorial**: [Raspberry Pi - Mini LCD Display Tutorial](https://www.youtube.com/watch?v=fR5XhHYzUK0). 4 | 5 |

6 | 7 | 8 | 9 |

10 | 11 | You can buy one of these great little I2C LCD on eBay or somewhere like [the Pi Hut](https://thepihut.com/search?type=product&q=lcd). 12 | 13 | ## Table of Contents 14 | 15 | 1. [Installation](#installation) 16 | 1. [Demos](#demos) 17 | - [Backlight control](#backlight-control) 18 | - [Custom characters](#custom-characters) 19 | - [Extended strings](#extended-strings) 20 | - [Forex](#forex) 21 | - [Home automation](#home-automation) 22 | - [IP address](#ip-address) 23 | - [LCD demo](#lcd-demo) 24 | - [NetMonitor](#netmonitor) 25 | - [Progress bar](#progress-bar) 26 | - [Tiny Dashboard](#tiny-dashboard) 27 | 1. [Implementation](#implementation) 28 | - [Systemd](#systemd) 29 | 1. [Contributions](#contributions) 30 | 31 | ## Installation 32 | 33 | - Install git 34 | 35 | ```sh 36 | sudo apt install git 37 | ``` 38 | 39 | - Clone the repo in your home directory 40 | 41 | ```sh 42 | cd /home/${USER}/ 43 | git clone https://github.com/the-raspberry-pi-guy/lcd.git 44 | cd lcd/ 45 | ``` 46 | 47 | - Run the automatic installation script with `sudo` permission 48 | 49 | ```sh 50 | sudo ./install.sh 51 | ``` 52 | 53 | - During the installation, pay attention to any messages about `python` and `python3` usage, as they inform which version you should use to interface with the LCD driver. For example: 54 | 55 | ```txt 56 | [LCD] [INFO] You may use either 'python' or 'python3' to interface with the lcd. 57 | ``` 58 | 59 | or alternatively, 60 | 61 | ```txt 62 | [LCD] [INFO] Use 'python3' to interface with the lcd. 63 | ``` 64 | 65 | - At the end of the installation script, you'll be prompted to reboot the RPi to apply the changes made to `/boot/config.txt` and `/etc/modules`. 66 | 67 | - After rebooting, try one of the [**demos**](#demos): 68 | 69 | ```sh 70 | ./home/${USER}/lcd/demo_clock.py 71 | ``` 72 | 73 | or 74 | 75 | ```sh 76 | python /home/${USER}/lcd/demo_clock.py 77 | ``` 78 | 79 | or 80 | 81 | ```sh 82 | python3 /home/${USER}/lcd/demo_clock.py 83 | ``` 84 | 85 | [top :arrow_up:](#table-of-contents) 86 | 87 | ## Demos 88 | 89 | A list of demonstration (demo) files that illustrate how to use the LCD driver. Demos are ordered alphabetically. 90 | 91 | ### Backlight Control 92 | 93 | - Author: [@Tomtom0201](https://github.com/Tomtom0201) 94 | 95 | This demo showcases the backlight control of the LCD, which is available on some hardware: 96 | 97 |

98 | 99 |

100 | 101 | ### Custom characters 102 | 103 | - Author: [@juvus](https://github.com/juvus) 104 | 105 | It is possible to define in CG RAM memory up to 8 custom characters. These characters can be prompted on LCD the same way as any characters from the [characters table](imgs/characters_table.png). Codes for the custom characters are unique and as follows: 106 | 107 | 1. `{0x00}` 108 | 2. `{0x01}` 109 | 3. `{0x02}` 110 | 4. `{0x03}` 111 | 5. `{0x04}` 112 | 6. `{0x05}` 113 | 7. `{0x06}` 114 | 8. `{0x07}` 115 | 116 | Please, see the comments and implementation in the [`demo_lcd_custom_characters.py`](demo_lcd_custom_characters.py) file for more details on how to use custom characters. Thanks to [@Jumitti](https://github.com/Jumitti), there is also [a web app you can use to generate custom characters](https://custom-characters-for-lcd16x2.streamlit.app/) by drawing them on a matrix. 117 | 118 |

119 | 120 |

121 | 122 | ### Extended strings 123 | 124 | - Author: [@juvus](https://github.com/juvus) 125 | 126 | This is demo showcases how extended strings could be used. Extended strings can contain special placeholders of form `{0xFF}`, that is, a hex code of the symbol wrapped within curly brackets. Hex codes of various symbols can be found in the following characters table: 127 | 128 |

129 | 130 |

131 | 132 | For example, the hex code of the symbol `ö` is `0xEF`, and so this symbol could be printed on the second row of the display by using the `{0xEF}` placeholder, as follows: 133 | 134 | ```Python 135 | display.lcd_display_extended_string("{0xEF}", 2) 136 | ``` 137 | 138 | If you want to combine placeholder to write a symbol `{0xFF}` with the native Python placeholder `{0}` for inserting dome data into text, escape the non-native placeholders. Here is an example: 139 | 140 | ```Python 141 | display.lcd_display_extended_string("Symbol:{{0xEF}} data:{0}".format(5), 2) 142 | ``` 143 | 144 |

145 | 146 |

147 | 148 | ### Forex 149 | 150 | - Author: [@bariskisir](https://github.com/bariskisir) 151 | - Additional Python package requirements: `pip`, `requests`, `bs4` 152 | 153 | To install the requirements, follow this procedure: 154 | 155 | - Install `pip` and use it to install the remaining packages 156 | 157 | ```sh 158 | sudo apt install python-pip 159 | pip install requests bs4 160 | ``` 161 | 162 |

163 | 164 |

165 | 166 | ### Home Automation 167 | 168 | - Author: [@Jumitti](https://github.com/Jumitti) 169 | - Repository: [Jumitti/lcd_home_automation](https://github.com/Jumitti/lcd_home_automation) 170 | 171 | This implementation shows how to use the LCD to display messages from temperature sensors and services such as Spotify and Trakt (see below). The implementation also features an integration with Telegram to turn the LCD backlight on/off and send a few other commands to control the host machine (e.g., get the temperature, reboot, shutdown). [@Jumitti](https://github.com/Jumitti) documented the project on its own [Home Automation repository](https://github.com/Jumitti/lcd_home_automation), so make sure to check it out if you want to learn more about it. 172 | 173 |

174 | 175 |

176 | 177 | ### IP Address 178 | 179 | - Author: [@Sierra007117](https://github.com/Sierra007117) 180 | 181 | Display your Pi's IP address, which is useful for `SSH` access and more! 182 | 183 |

184 | 185 |

186 | 187 | ### LCD demo 188 | 189 | - Author: [@Tomtom0201](https://github.com/Tomtom0201) 190 | 191 | This demo shows how simple strings could be displayed on the LCD. For extended usage, take a look at [Extended strings](#extended-strings) demo instead. 192 | 193 |

194 | 195 |

196 | 197 | ### NetMonitor 198 | 199 | - Author: [@cgomesu](https://github.com/cgomesu) 200 | 201 | This demo uses `ping` and `nc` (netcat) to monitor the network status of hosts and services, respectively. Hosts and services can be modified by editing their respective [dictionaries](https://docs.python.org/3/tutorial/datastructures.html#dictionaries): 202 | 203 | ```Python 204 | hosts = { 205 | 'Internet': '8.8.8.8', 206 | 'Firewall': '192.168.1.1', 207 | 'NAS': '192.168.1.2' 208 | } 209 | services = { 210 | 'Cameras': {'ip': '192.168.1.2', 'port': '8000'}, 211 | 'Plex': {'ip': '192.168.1.2', 'port': '32400'} 212 | } 213 | ``` 214 | 215 |

216 | 217 |

218 | 219 | ### Progress bar 220 | 221 | - Author: [@juvus](https://github.com/juvus) 222 | 223 | This is a demo of a graphical progress bar created with [custom characters](#custom-characters). This bar could be used, for example, for showing the current level of battery charge. 224 | 225 |

226 | 227 |

228 | 229 | ### Tiny dashboard 230 | 231 | - Author: [@jdarias](https://github.com/jdarias) 232 | 233 | This is a script that shows a famous quote, a currency conversion pair of your choice and the weather of a city. It also shows the last three characters from your ip address, the date in DDMM format and the hour in HH:MM format 234 | 235 |

236 | 237 |

238 | 239 | The script takes info from the following APIs: 240 | 241 | - [quotable.io](https://github.com/lukePeavey/quotable): Free public API that provides famous quotes from well known people. It has a public endpoint that doesn't require an API key. 242 | 243 | - [exchangerate-api.com](https://exchangerate-api.com) / [free.currencyconverterapi.com](https://free.currencyconverterapi.com): There are a lot of currency apis but these ones offer free currency exchange info. Both are used, one as main, the other as backup. Requires an API key to use. 244 | 245 | - [openweathermap.org](https://openweathermap.org): Provides Weather info, forecasts, etc. Requires an API key to use. 246 | 247 | In order to use the script, you need to get **API key tokens for both exchange rate services and the weather api**. Once you've done that, edit the script to put your tokens in the USER VARIABLES section. 248 | 249 | Also set a currency exchange pair. For currency support and the currency codes you need to use, see [exchangerate-api.com/docs/supported-currencies](https://www.exchangerate-api.com/docs/supported-currencies). 250 | 251 | A city/country string is also needed to show weather info for such city. Search for your city on [openweathermap.org](https://openweathermap.org) and take note of the `City,country` string and put it in the script.`London,gb` is given as an example. 252 | 253 | [top :arrow_up:](#table-of-contents) 254 | 255 | ## Implementation 256 | 257 | Once you are done editing a `demo_*.py` file or writing your own Python script, follow the instructions on this section to run the script in the background. First, however, ensure that the script (e.g., `script.py`) has at least permission to be executed, as follows: 258 | 259 | ```sh 260 | sudo chmod +x script.py 261 | ``` 262 | 263 | Similarly, file ownership can be configured via `chown`. For example, to set the user `${USER}` as owner of the file `script.py`, run the following: 264 | 265 | ```sh 266 | sudo chown ${USER} script.py 267 | ``` 268 | 269 | ## Systemd 270 | 271 | Use the following procedure to run any LCD Python script as a (systemd) service: 272 | 273 | 1. Create a new unit file in `/lib/systemd/system/` called `rpi-lcd.service`: 274 | 275 | ```sh 276 | sudo nano /lib/systemd/system/rpi-lcd.service 277 | ``` 278 | 279 | 2. Copy and paste the following in the new unit file: 280 | 281 | (*If your user is different than `pi`, remember to edit the `User=` entry.*) 282 | 283 | ```sh 284 | [Unit] 285 | Description=RPi Python script for a 16x2 LCD 286 | 287 | [Service] 288 | Type=simple 289 | ## Edit the following according to the script permissions 290 | User=pi 291 | #Group=users 292 | 293 | ## Edit the following with the full path to the compatible Python version and your script 294 | ExecStart=/usr/bin/python /path/to/script.py 295 | 296 | Restart=always 297 | RestartSec=5 298 | 299 | KillMode=process 300 | KillSignal=SIGINT 301 | 302 | [Install] 303 | WantedBy=multi-user.target 304 | ``` 305 | 306 | 3. Enable the service and start it: 307 | 308 | ```sh 309 | sudo systemctl enable rpi-lcd.service 310 | sudo systemctl start rpi-lcd.service 311 | ``` 312 | 313 | 4. Check that the LCD is displaying the correct information; otherwise, check the service status: 314 | 315 | ```sh 316 | systemctl status rpi-lcd.service 317 | ``` 318 | 319 | [top :arrow_up:](#table-of-contents) 320 | 321 | ## Contributions 322 | 323 | Thank you for you interest in learning how to contribute to this repository. We welcome contributions from novices to experts alike, so do not be afraid to give it a try if you are new to `git` and GitHub. First, however, take a few minutes to read our [CONTRIBUTING.md](CONTRIBUTING.md) guide to learn how to open **Issues** and the various sorts of **Pull Requests (PRs)** that are currently accepted. 324 | 325 | In addition, if you've never contributed to an open source project before, please take a look at the following resources: 326 | 327 | - [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) 328 | - [Proposing changes to your work with pull requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests) 329 | 330 | [top :arrow_up:](#table-of-contents) 331 | --------------------------------------------------------------------------------