Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial luma.core support with luma.oled for ssd1306 and sh1106 devices #9

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 37 additions & 17 deletions Documentation/behavior.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
This file describes expected behavior of MobileMonitor.
This file describes expected behavior of MobileMonitor.

Startup:
### Startup:
The program will:
* Set all GPIO to default state
* Look for Kismet PID
* Initialize NeoPixels, show some output to each
* Look for Kismet PID, if not found start kismet. If kismet does not start, output information to i2c and blink all neopixels red.
* Attempt to connect to Kismet

### Buttons
* Hold a button to change i2c display to alternate information
* Press a button to shut down kismet, press again to restart kismet
* Press a button to run "I'm Home" tasks: Connect to known SSID, transfer collected data to somewhere: HTTPS, FTP, NFS, etc.
* Hold a button for 5 seconds to initiate system shutdown.

### i2c Display - Primary information (Kismet status)
* Websocket connection status
* GPS status
* Packet stream
* uptime

### i2c Display - Alternate information (System status)
* Alternate information display can be activated by holding a button
* CPU load, RAM usage (as %), disk usage (as %)
* CPU temperature
* Low voltage warning

### NeoPixels: (order is TBD)
Pixel 1: Websocket status
* RED, solid : Not connected
* GREEN, solid : Connected
* RED, solid : Not connected
* YELLOW, solid : Authentication failure
* GREEN, solid : Connected

Pixel 2: GPS status
* RED, solid : No connection to GPSD
Expand All @@ -18,28 +38,28 @@ Pixel 2: GPS status
* GREEN, solid : 3D Fix

Pixel 3: SSID alert
* RED, blink : Unencrpted SSID
* RED, blink : Unencrypted SSID
* YELLOW, blink : WEP SSID
* BLUE, blink : WPA/WPA2 SSID
* GREEN, blink : WPA3 SSID

Pixel 4: AP alert
* RED, blink :
* RED, blink :
* YELLOW, blink
* BLUE, blink :
* BLUE, blink : New AP found
* GREEN, blink :

Pixel 5: WiFi Device alert
* RED, blink :
* YELLOW, blink :
* BLUE, blink :
* GREEN, blink :

Pixel 6: Bluetooch device alert
* RED, blink :
* YELLOW, blink :
* BLUE, blink :
* GREEN, blink :
* YELLOW, blink :
* BLUE, blink :
* GREEN, blink : New device found

Pixel 6: Bluetooth device alert
* RED, blink :
* YELLOW, blink :
* BLUE, blink : New bluetooth device found
* GREEN, blink :

Pixel 7: Thermal alert
* RED, blink : Over 70c
Expand Down
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ local_gpio:
function: new_device
i2c_display:
enabled: true
driver: adafruit_ssd1306
driver: luma.oled:ssd1306
width: 128
height: 32
msg_disp_time: 1
Expand Down
212 changes: 105 additions & 107 deletions mobile_monitor_rpi4.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,36 +685,57 @@ def button_watcher(self, gpio_line, cb):

# class for display drawing and updating
class i2c_controller(object):
def __init__(self, width, height, events, jsn, wsc):
def __init__(self, driver, width, height, events, jsn, wsc):
# store connectors
self.jc = jsn
self.wc = wsc

# Create the I2C interface.
self.i2c = busio.I2C(SCL, SDA)

# no other drivers yet so use ssd1306
# Create the SSD1306 OLED class
self.disp = adafruit_ssd1306.SSD1306_I2C(width, height, self.i2c)

# do a quick test and then clear
self.disp.fill(1)
self.disp.show()
time.sleep(.2)
self.disp.fill(0)
self.disp.show()
self.driver = ""

if driver == "adafruit_ssd1306":
# adafruit driver
# Create the I2C interface.
self.i2c = busio.I2C(SCL, SDA)

# Create the SSD1306 OLED class
self.disp = adafruit_ssd1306.SSD1306_I2C(width, height, self.i2c)
self.driver = "adafruit"
elif driver.startswith("luma"):
# luma.core driver
# create I2C
self.i2c = i2c()
dlr = driver.find(":")
if driver[:dlr] == "luma.oled":
if driver[dlr+1:] == "ssd1306":
# create ssd1306 display
self.disp = ssd1306(self.i2c)
elif driver[dlr+1:] == "sh1106":
# create sh1106 display
self.disp = sh1106(self.i2c)
else:
print("i2c_controller: __init__ failed to find correct luma.core driver!")
sys.exit(1)
self.driver = "luma"
else:
print("i2c_controller: __init__ failed to find correct display driver!")
sys.exit(1)

# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
self.width = self.disp.width
self.height = self.disp.height
self.width = width
self.height = height
self.g_height = round((self.height // 2)*.8)
self.msg_cnt = ((self.height // 2) - 8) // 8
self.screen = Image.new("1", (self.width, self.height))

# Get drawing object to draw on image.
self.draw = ImageDraw.Draw(self.screen)

# do a quick test and then clear
self.draw.rectangle((0, 0, self.width, self.height), outline=1, fill=1)
self.show_screen()
time.sleep(.2)
self.clear_screen()

# Load default font.
self.font = ImageFont.load_default()

Expand Down Expand Up @@ -753,6 +774,16 @@ def __init__(self, width, height, events, jsn, wsc):
events.ws_event["new_disp_msg"].append(self.disp_msg)
events.ws_event["error_state"].append(self.error_state_change)

def show_screen(self):
if self.driver == "adafruit":
self.disp.image(self.screen)
self.disp.show()
elif self.driver == "luma":
self.disp.display(self.screen)
else:
print("i2c_controller: show_screen failed to find correct display driver!")
sys.exit(1)

def draw_screen(self):
# Draw a black filled box to clear the image.
self.draw.rectangle((0, 0, self.width, self.height), outline=0, fill=0)
Expand Down Expand Up @@ -793,16 +824,15 @@ def draw_screen(self):
self.screen.paste(self.graph_vec(self.min_vec, bar_w, self.g_height), (g_x, self.height-self.g_height))

# Display image.
self.disp.image(self.screen)
self.disp.show()
self.show_screen()
#time.sleep(self.DISPLAY_ON)
#self.disp.fill(0)
#self.disp.show()
#time.sleep(self.DISPLAY_OFF)

def clear_screen(self):
self.disp.fill(0)
self.disp.show()
self.draw.rectangle((0, 0, self.width, self.height), outline=0, fill=0)
self.show_screen()

def ws_state_change(self, event, init):
if config.debug: print("ws_state_change: {}".format(str(event)))
Expand Down Expand Up @@ -933,10 +963,6 @@ def set_uptime(self, uptime):
else:
self.ut_str = "Not Connected"

#####
#####
##### Start OLD CODE

#Look for kismet, start if not running, etc
def kismet_control():
kismet_PID = NULL
Expand All @@ -963,65 +989,6 @@ def find_process(processName):
pass
return listOfProcessObjects;

def rgb_control(rgb_color):
bus = smbus.SMBus(1)
addr = 0x0d
rgb_off_reg = 0x07
rgb_effect_reg = 0x04
rgb_speed_reg = 0x05
rgb_color_reg = 0x06
Max_LED = 3

def setRGB(num, r, g, b):
if num >= Max_LED:
bus.write_byte_data(addr, 0x00, 0xff)
bus.write_byte_data(addr, 0x01, r&0xff)
bus.write_byte_data(addr, 0x02, g&0xff)
bus.write_byte_data(addr, 0x03, b&0xff)
elif num >= 0:
bus.write_byte_data(addr, 0x00, num&0xff)
bus.write_byte_data(addr, 0x01, r&0xff)
bus.write_byte_data(addr, 0x02, g&0xff)
bus.write_byte_data(addr, 0x03, b&0xff)

def setRGBEffect(effect):
if effect >= 0 and effect <= 4:
bus.write_byte_data(addr, rgb_effect_reg, effect&0xff)
def setRGBSpeed(speed):
if speed >= 1 and speed <= 3:
bus.write_byte_data(addr, rgb_speed_reg, speed&0xff)
def setRGBColor(color):
if color >= 0 and color <= 6:
bus.write_byte_data(addr, rgb_color_reg, color&0xff)

bus.write_byte_data(addr, rgb_off_reg, 0x00)
#time.sleep(1)
#0-water light, 1-breathing light, 2-marquee, 3-rainbow lights, 4-colorful lights
setRGBEffect(1)
#1-low speed, 2-medium speed (default), 3-high speed
setRGBSpeed(3)
#0-red, 1-green (default), 2-blue, 3-yellow, 4-purple, 5-cyan, 6-white
setRGBColor(rgb_color)
#Turn lED 1 RED
#bus.write_byte_data(addr, 0x00, 0x00)
#bus.write_byte_data(addr, 0x01, 0xFF)
#bus.write_byte_data(addr, 0x02, 0x00)
#bus.write_byte_data(addr, 0x03, 0x00)
#Trun LED 2 GREEN
#bus.write_byte_data(addr, 0x00, 0x01)
#bus.write_byte_data(addr, 0x01, 0x00)
#bus.write_byte_data(addr, 0x02, 0xFF)
#bus.write_byte_data(addr, 0x03, 0x00)
#Trun LED 3 BLUE
#bus.write_byte_data(addr, 0x00, 0x02)
#bus.write_byte_data(addr, 0x01, 0x00)
#bus.write_byte_data(addr, 0x02, 0x00)
#bus.write_byte_data(addr, 0x03, 0xFF)

#####
#####
##### End OLD CODE

if __name__ == "__main__":
# load config
config = configuration()
Expand Down Expand Up @@ -1111,43 +1078,74 @@ def setRGBColor(color):

# setup i2c display
if config.i2c_display['enabled']:
try:
import busio, smbus
except:
print("Failed to load busio and/or smbus python3 modules, required for i2c display")
### TODO: add wrapper to fail gracefully
i2c_driver_loaded = False

# check config for display
if not 'width' in config.i2c_display.keys():
print("No i2c_display 'width' set.")
sys.exit(1)
if not 'height' in config.i2c_display.keys():
print("No i2c_display 'height' set.")
sys.exit(1)

try:
from PIL import Image, ImageDraw, ImageFont
except Exception as e:
print("Failed to load PIL python3 module, required for i2c display")
sys.exit(1)
try:
from board import SCL, SDA
except Exception as e:
print("Failed to load board python3 module, required for i2c display")
sys.exit(1)

# check display driver and load modules accordingly
if config.i2c_display['driver'] == 'adafruit_ssd1306':
# setup for adafruit's ssd1306 driver
try:
import busio, smbus
except:
print("Failed to load busio and/or smbus python3 modules, required for i2c display")
sys.exit(1)
try:
from board import SCL, SDA
except Exception as e:
print("Failed to load board python3 module, required for i2c display")
sys.exit(1)
try:
import adafruit_ssd1306
except Exception as e:
print("Failed to load adafruit_ssd1306 python3 module, required for i2c display driver")
sys.exit(1)
i2c_driver_loaded = True
elif config.i2c_display['driver'].startswith("luma"):
dlr = config.i2c_display['driver'].find(":")
if config.i2c_display['driver'][:dlr] == "luma.oled":
if config.i2c_display['driver'][dlr+1:] == "ssd1306":
try:
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
except Exception as e:
print("Failed to load luma.core or luma.oled python3 module, required for i2c display driver")
sys.exit(1)
i2c_driver_loaded = True
elif config.i2c_display['driver'][dlr+1:] == "sh1106":
try:
from luma.core.interface.serial import i2c
from luma.oled.device import sh1106
except Exception as e:
print("Failed to load luma.core or luma.oled python3 module, required for i2c display driver")
sys.exit(1)
i2c_driver_loaded = True

# create display
if i2c_driver_loaded:
try:
display = i2c_controller(config.i2c_display['driver'], config.i2c_display['width'], config.i2c_display['height'], events, jc, wsc)
except:
traceback.print_tb(err.__traceback__)
print(err)
print("ERROR: Failed creating display for i2c_display!")
sys.exit(1)
else:
print("Incorrect setting for i2c_display 'driver': '{}'".format(config.i2c_display['driver']))
print("Supported drivers are: 'adafruit_ssd1306'")
sys.exit(1)
if not 'width' in config.i2c_display.keys():
print("No i2c_display 'width' set.")
sys.exit(1)
if not 'height' in config.i2c_display.keys():
print("No i2c_display 'height' set.")
sys.exit(1)
try:
display = i2c_controller(config.i2c_display['width'], config.i2c_display['height'], events, jc, wsc)
except:
traceback.print_tb(err.__traceback__)
print(err)
print("ERROR: Failed creating display for i2c_display!")
print("Supported drivers are: 'adafruit_ssd1306', 'luma.oled:ssd1306', 'luma.oled:sh1106'")
sys.exit(1)
else:
display = None
Expand Down