diff --git a/.gitignore b/.gitignore index 7b05f121..e17ba71f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ core .idea/ .DS_Store *.swp +__pycache__ +src/web/current +src/oled_monitor/current diff --git a/src/oled_monitor/rascsi_oled_monitor.py b/src/oled_monitor/rascsi_oled_monitor.py index b2e3a639..38048815 100755 --- a/src/oled_monitor/rascsi_oled_monitor.py +++ b/src/oled_monitor/rascsi_oled_monitor.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # RaSCSI Updates: # Updates to output rascsi status to an OLED display @@ -29,92 +29,57 @@ # THE SOFTWARE. import time import os +import sys import datetime - -#import Adafruit_GPIO.SPI as SPI -import Adafruit_SSD1306 - -from PIL import Image -from PIL import ImageDraw -from PIL import ImageFont - +import board +import busio +import adafruit_ssd1306 +from PIL import Image, ImageDraw, ImageFont import subprocess +WIDTH = 128 +HEIGHT = 32 # Change to 64 if needed +BORDER = 5 + # How long to delay between each update delay_time_ms = 250 -# Raspberry Pi pin configuration: -RST = None # on the PiOLED this pin isnt used -# Note the following are only used with SPI: -DC = 23 -SPI_PORT = 0 -SPI_DEVICE = 0 +# Define the Reset Pin +oled_reset = None -# Beaglebone Black pin configuration: -# RST = 'P9_12' -# Note the following are only used with SPI: -# DC = 'P9_15' -# SPI_PORT = 1 -# SPI_DEVICE = 0 +# init i2c +i2c = board.I2C() # 128x32 display with hardware I2C: -disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST) +oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C, reset=oled_reset) -# 128x64 display with hardware I2C: -# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST) - -# Note you can change the I2C address by passing an i2c_address parameter like: -# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, i2c_address=0x3C) - -# Alternatively you can specify an explicit I2C bus number, for example -# with the 128x32 display you would use: -# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, i2c_bus=2) - -# 128x32 display with hardware SPI: -# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000)) - -# 128x64 display with hardware SPI: -# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000)) - -# Alternatively you can specify a software SPI implementation by providing -# digital GPIO pin numbers for all the required display pins. For example -# on a Raspberry Pi with the 128x32 display you might use: -# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, dc=DC, sclk=18, din=25, cs=22) - -print "Running with the following display:" -print disp -print -print "Will update the OLED display every " + str(delay_time_ms) + "ms (approximately)" - - -# Initialize library. -disp.begin() +print ("Running with the following display:") +print (oled) +print () +print ("Will update the OLED display every " + str(delay_time_ms) + "ms (approximately)") # Clear display. -disp.clear() -disp.display() +oled.fill(0) +oled.show() # Create blank image for drawing. # Make sure to create image with mode '1' for 1-bit color. -width = disp.width -height = disp.height -image = Image.new('1', (width, height)) +image = Image.new("1", (oled.width, oled.height)) # Get drawing object to draw on image. draw = ImageDraw.Draw(image) # Draw a black filled box to clear the image. -draw.rectangle((0,0,width,height), outline=0, fill=0) +draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0) # Draw some shapes. # First define some constants to allow easy resizing of shapes. padding = -2 top = padding -bottom = height-padding +bottom = HEIGHT-padding # Move left to right keeping track of the current x position for drawing shapes. x = 0 - # Load default font. font = ImageFont.load_default() @@ -125,10 +90,10 @@ font = ImageFont.load_default() while True: # Draw a black filled box to clear the image. - draw.rectangle((0,0,width,height), outline=0, fill=0) + draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0) cmd = "rasctl -l" - rascsi_list = subprocess.check_output(cmd, shell=True) + rascsi_list = subprocess.check_output(cmd, shell=True).decode(sys.stdout.encoding) y_pos = top # Draw all of the meaningful data to the 'image' @@ -146,19 +111,20 @@ while True: for line in rascsi_list.split('\n'): # Skip empty strings, divider lines and the header line... if (len(line) == 0) or line.startswith("+---") or line.startswith("| ID | UN"): - continue - else: - if line.startswith("| "): - fields = line.split('|') - output = str.strip(fields[1]) + " " + str.strip(fields[3]) + " " + os.path.basename(str.strip(fields[4])) - else: - output = "No image mounted!" - draw.text((x, y_pos), output, font=font, fill=255) - y_pos = y_pos + 8 + continue + else: + if line.startswith("| "): + fields = line.split('|') + output = str.strip(fields[1]) + " " + str.strip(fields[3]) + " " + os.path.basename(str.strip(fields[4])) + else: + output = "No image mounted!" + draw.text((x, y_pos), output, font=font, fill=255) + y_pos += 8 + # If there is still room on the screen, we'll display the time. If there's not room it will just be clipped draw.text((x, y_pos), datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"), font=font, fill=255) # Display image. - disp.image(image) - disp.display() + oled.image(image) + oled.show() time.sleep(1/delay_time_ms) diff --git a/src/oled_monitor/requirements.txt b/src/oled_monitor/requirements.txt new file mode 100644 index 00000000..eebfd0cd --- /dev/null +++ b/src/oled_monitor/requirements.txt @@ -0,0 +1,14 @@ +Adafruit-Blinka==6.3.2 +adafruit-circuitpython-busdevice==5.0.6 +adafruit-circuitpython-framebuf==1.4.6 +adafruit-circuitpython-ssd1306==2.11.1 +Adafruit-PlatformDetect==3.2.0 +Adafruit-PureIO==1.1.8 +Pillow==8.1.2 +pkg-resources==0.0.0 +pyftdi==0.52.9 +pyserial==3.5 +pyusb==1.1.1 +rpi-ws281x==4.2.5 +RPi.GPIO==0.7.0 +sysv-ipc==1.1.0 diff --git a/src/oled_monitor/start.sh b/src/oled_monitor/start.sh new file mode 100755 index 00000000..f0a37013 --- /dev/null +++ b/src/oled_monitor/start.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -e +# set -x # Uncomment to Debug + +cd $(dirname $0) +# verify packages installed +ERROR=0 +if ! command -v dpkg -l i2c-tools &> /dev/null ; then + echo "i2c-tools could not be found" + echo "Run 'sudo apt install i2c-tools' to fix." + ERROR=1 +fi +if ! command -v python3 &> /dev/null ; then + echo "python3 could not be found" + echo "Run 'sudo apt install python3' to fix." + ERROR=1 +fi +# Dep to build Pillow +if ! dpkg -l python3-dev &> /dev/null; then + echo "python3-dev could not be found" + echo "Run 'sudo apt install python3-dev' to fix." + ERROR=1 +fi +if ! dpkg -l libjpeg-dev &> /dev/null; then + echo "libjpeg-dev could not be found" + echo "Run 'sudo apt install libjpeg-dev' to fix." + ERROR=1 +fi +if ! dpkg -l libpng-dev &> /dev/null; then + echo "libpng-dev could not be found" + echo "Run 'sudo apt install libpng-dev' to fix." + ERROR=1 +fi +# Dep to build Pollow +if ! python3 -m venv --help &> /dev/null ; then + echo "venv could not be found" + echo "Run 'sudo apt install python3-venv' to fix." + ERROR=1 +fi +if [ $ERROR = 1 ] ; then + echo + echo "Fix errors and re-run ./start.sh" + exit 1 +fi + +if ! i2cdetect -y 1 &> /dev/null ; then + echo "i2cdetect -y 1 did not find a screen." + exit 2 +fi +if ! test -e venv; then + echo "Creating python venv for OLED Screen" + python3 -m venv venv + echo "Activating venv" + source venv/bin/activate + echo "Installing requirements.txt" + pip install -r requirements.txt + git rev-parse HEAD > current +fi + +source venv/bin/activate + +# Detect if someone updates - we need to re-run pip install. +if ! test -e current; then + git rev-parse > current +else + if [ "$(cat current)" != "$(git rev-parse HEAD)" ]; then + echo "New version detected, updating requirements.txt" + echo " This may take some time..." + pip install wheel + pip install -r requirements.txt + git rev-parse HEAD > current + fi +fi + +echo "Starting OLED Screen..." +python3 rascsi_oled_monitor.py