Information wants to be free...

SSD1306 Wi-Fi Status

Here is another use of the SSD1306 miniature OLED display. Instead of loading an image like my previous project, this code contains character data and can display text. I have combined this with some simple hacks to retrieve and display Wi-Fi status information from the host. This is also meant to be connected to a Raspberry Pi 3.

Take a look:

#!/usr/bin/python
import smbus
import subprocess
import re
import time

chardata = dict()
chardata['@'] = "\x3c\x66\x6e\x6e\x60\x62\x3c\x00"
chardata['A'] = "\x18\x3c\x66\x7e\x66\x66\x66\x00"
chardata['B'] = "\x7c\x66\x66\x7c\x66\x66\x7c\x00"
chardata['C'] = "\x3c\x66\x60\x60\x60\x66\x3c\x00"
chardata['D'] = "\x78\x6c\x66\x66\x66\x6c\x78\x00"
chardata['E'] = "\x7e\x60\x60\x78\x60\x60\x7e\x00"
chardata['F'] = "\x7e\x60\x60\x78\x60\x60\x60\x00"
chardata['G'] = "\x3c\x66\x60\x6e\x66\x66\x3c\x00"
chardata['H'] = "\x66\x66\x66\x7e\x66\x66\x66\x00"
chardata['I'] = "\x3c\x18\x18\x18\x18\x18\x3c\x00"
chardata['J'] = "\x1e\x0c\x0c\x0c\x0c\x6c\x38\x00"
chardata['K'] = "\x66\x6c\x78\x70\x78\x6c\x66\x00"
chardata['L'] = "\x60\x60\x60\x60\x60\x60\x7e\x00"
chardata['M'] = "\x63\x77\x7f\x6b\x63\x63\x63\x00"
chardata['N'] = "\x66\x76\x7e\x7e\x6e\x66\x66\x00"
chardata['O'] = "\x3c\x66\x66\x66\x66\x66\x3c\x00"
chardata['P'] = "\x7c\x66\x66\x7c\x60\x60\x60\x00"
chardata['Q'] = "\x3c\x66\x66\x66\x66\x3c\x0e\x00"
chardata['R'] = "\x7c\x66\x66\x7c\x78\x6c\x66\x00"
chardata['S'] = "\x3c\x66\x60\x3c\x06\x66\x3c\x00"
chardata['T'] = "\x7e\x18\x18\x18\x18\x18\x18\x00"
chardata['U'] = "\x66\x66\x66\x66\x66\x66\x3c\x00"
chardata['V'] = "\x66\x66\x66\x66\x66\x3c\x18\x00"
chardata['W'] = "\x63\x63\x63\x6b\x7f\x77\x63\x00"
chardata['X'] = "\x66\x66\x3c\x18\x3c\x66\x66\x00"
chardata['Y'] = "\x66\x66\x66\x3c\x18\x18\x18\x00"
chardata['Z'] = "\x7e\x06\x0c\x18\x30\x60\x7e\x00"
chardata['['] = "\x3c\x30\x30\x30\x30\x30\x3c\x00"
chardata[']'] = "\x3c\x0c\x0c\x0c\x0c\x0c\x3c\x00"
chardata[' '] = "\x00\x00\x00\x00\x00\x00\x00\x00"
chardata['!'] = "\x18\x18\x18\x18\x00\x00\x18\x00"
chardata['"'] = "\x66\x66\x66\x00\x00\x00\x00\x00"
chardata['#'] = "\x66\x66\xff\x66\xff\x66\x66\x00"
chardata['$'] = "\x18\x3e\x60\x3c\x06\x7c\x18\x00"
chardata['%'] = "\x62\x66\x0c\x18\x30\x66\x46\x00"
chardata['&'] = "\x3c\x66\x3c\x38\x67\x66\x3f\x00"
chardata['\''] = "\x06\x0c\x18\x00\x00\x00\x00\x00"
chardata['('] = "\x0c\x18\x30\x30\x30\x18\x0c\x00"
chardata[')'] = "\x30\x18\x0c\x0c\x0c\x18\x30\x00"
chardata['*'] = "\x00\x66\x3c\xff\x3c\x66\x00\x00"
chardata['+'] = "\x00\x18\x18\x7e\x18\x18\x00\x00"
chardata[','] = "\x00\x00\x00\x00\x00\x18\x18\x30"
chardata['-'] = "\x00\x00\x00\x7e\x00\x00\x00\x00"
chardata['.'] = "\x00\x00\x00\x00\x00\x18\x18\x00"
chardata['/'] = "\x00\x03\x06\x0c\x18\x30\x60\x00"
chardata['0'] = "\x3c\x66\x6e\x76\x66\x66\x3c\x00"
chardata['1'] = "\x18\x18\x38\x18\x18\x18\x7e\x00"
chardata['2'] = "\x3c\x66\x06\x0c\x30\x60\x7e\x00"
chardata['3'] = "\x3c\x66\x06\x1c\x06\x66\x3c\x00"
chardata['4'] = "\x06\x0e\x1e\x66\x7f\x06\x06\x00"
chardata['5'] = "\x7e\x60\x7c\x06\x06\x66\x3c\x00"
chardata['6'] = "\x3c\x66\x60\x7c\x66\x66\x3c\x00"
chardata['7'] = "\x7e\x66\x0c\x18\x18\x18\x18\x00"
chardata['8'] = "\x3c\x66\x66\x3c\x66\x66\x3c\x00"
chardata['9'] = "\x3c\x66\x66\x3e\x06\x66\x3c\x00"
chardata[':'] = "\x00\x00\x18\x00\x00\x18\x00\x00"
chardata[';'] = "\x00\x00\x18\x00\x00\x18\x18\x30"
chardata['<'] = "\x0e\x18\x30\x60\x30\x18\x0e\x00"
chardata['='] = "\x00\x00\x7e\x00\x7e\x00\x00\x00"
chardata['>'] = "\x70\x18\x0c\x06\x0c\x18\x70\x00"
chardata['?'] = "\x3c\x66\x06\x0c\x18\x00\x18\x00"
chardata['a'] = "\x00\x00\x3c\x06\x3e\x66\x3e\x00"
chardata['b'] = "\x00\x60\x60\x7c\x66\x66\x7c\x00"
chardata['c'] = "\x00\x00\x3c\x60\x60\x60\x3c\x00"
chardata['d'] = "\x00\x06\x06\x3e\x66\x66\x3e\x00"
chardata['e'] = "\x00\x00\x3c\x66\x7e\x60\x3c\x00"
chardata['f'] = "\x00\x0e\x18\x3e\x18\x18\x18\x00"
chardata['g'] = "\x00\x00\x3e\x66\x66\x3e\x06\x7c"
chardata['h'] = "\x00\x60\x60\x7c\x66\x66\x66\x00"
chardata['i'] = "\x00\x18\x00\x38\x18\x18\x3c\x00"
chardata['j'] = "\x00\x06\x00\x06\x06\x06\x06\x3c"
chardata['k'] = "\x00\x60\x60\x6c\x78\x6c\x66\x00"
chardata['l'] = "\x00\x38\x18\x18\x18\x18\x3c\x00"
chardata['m'] = "\x00\x00\x66\x7f\x7f\x6b\x63\x00"
chardata['n'] = "\x00\x00\x7c\x66\x66\x66\x66\x00"
chardata['o'] = "\x00\x00\x3c\x66\x66\x66\x3c\x00"
chardata['p'] = "\x00\x00\x7c\x66\x66\x7c\x60\x60"
chardata['q'] = "\x00\x00\x3e\x66\x66\x3e\x06\x06"
chardata['r'] = "\x00\x00\x7c\x66\x60\x60\x60\x00"
chardata['s'] = "\x00\x00\x3e\x60\x3c\x06\x7c\x00"
chardata['t'] = "\x00\x18\x7e\x18\x18\x18\x0e\x00"
chardata['u'] = "\x00\x00\x66\x66\x66\x66\x3e\x00"
chardata['v'] = "\x00\x00\x66\x66\x66\x3c\x18\x00"
chardata['w'] = "\x00\x00\x63\x6b\x7f\x3e\x36\x00"
chardata['x'] = "\x00\x00\x66\x3c\x18\x3c\x66\x00"
chardata['y'] = "\x00\x00\x66\x66\x66\x3e\x0c\x78"
chardata['z'] = "\x00\x00\x7e\x0c\x18\x30\x7e\x00"

class SSD1306(object):
    def __init__(self, bus=1, address=0x3c):
        self._address = address
        self._bus = smbus.SMBus(bus)

        self._command(0xae) # Display off.

        self._command(0xa8) # Multiplex ratio...
        self._command(0x3f) # ...63
        self._command(0xd3) # Display offset...
        self._command(0x00) # ...0
        self._command(0x40) # Display start line at 0.
        self._command(0xa1) # Segment Re-map with column 127 mapped to SEG0.
        self._command(0xc8) # Remapped mode, scan from COM[N-1] to COM0.
        self._command(0xda) # COM pins hardware configuration...
        self._command(0x32) # ...Alternative and Left/Right
        self._command(0xa4) # Entire display ON.
        self._command(0xa6) # Inverse display mode.
        self._command(0xd5) # Display clock...
        self._command(0x80) # ...No clock divide ratio and max frequency.
        self._command(0x8d) # Charge pump...
        self._command(0x14) # ...Enabled.
        self._command(0x20) # Memory addressing mode...
        self._command(0x20) # ...Horizontal.

        self._command(0xaf) # Display on.
    
    def _command(self, command_byte):
        self._bus.write_byte_data(self._address, 0x00, command_byte)

    def _data(self, data_byte):
        self._bus.write_byte_data(self._address, 0x40, data_byte)

    def reset_cursor(self):
        self._command(0x21) # Column address...
        self._command(0x00) # ...start at 0...
        self._command(0x7f) # ...end at 127.
        self._command(0x22) # Page address...
        self._command(0x00) # ...start at 0...
        self._command(0x07) # ...end at 7.

    def putc(self, char):
        if char in chardata:
            for column in range(0, 8):
                byte  = ((ord(chardata[char][0]) >> (7 - column)) & 1) << 1
                byte += ((ord(chardata[char][1]) >> (7 - column)) & 1) << 0
                byte += ((ord(chardata[char][2]) >> (7 - column)) & 1) << 3
                byte += ((ord(chardata[char][3]) >> (7 - column)) & 1) << 2
                byte += ((ord(chardata[char][4]) >> (7 - column)) & 1) << 5
                byte += ((ord(chardata[char][5]) >> (7 - column)) & 1) << 4
                byte += ((ord(chardata[char][6]) >> (7 - column)) & 1) << 7
                byte += ((ord(chardata[char][7]) >> (7 - column)) & 1) << 6
                self._data(byte)

    def puts(self, string):
        for char in string:
            self.putc(char)

class WiFiStatus(object):
    def __init__(self, interface):
        self._interface = interface

    def _get_status(self):
        iwconfig = subprocess.check_output("iwconfig %s" % (self._interface), shell=True)
        ifconfig = subprocess.check_output("ifconfig %s" % (self._interface), shell=True)

        match = re.search(r'inet ([^\ ]+?) ', ifconfig, flags=re.MULTILINE)
        if match:
            inet_address = match.group(1)
        else:
            inet_address = ""

        match = re.search(r'RX packets ([^\ ]+?) ', ifconfig, flags=re.MULTILINE)
        if match:
            rx_packets = match.group(1)
        else:
            rx_packets = ""

        match = re.search(r'TX packets ([^\ ]+?) ', ifconfig, flags=re.MULTILINE)
        if match:
            tx_packets = match.group(1)
        else:
            tx_packets = ""

        match = re.search(r'ESSID:"([^"]+?)"', iwconfig, flags=re.MULTILINE)
        if match:
            essid = match.group(1)
        else:
            essid = ""

        match = re.search(r'Link Quality=(\d+/\d+)', iwconfig, flags=re.MULTILINE)
        if match:
            link_quality = match.group(1)
        else:
            link_quality = ""

        match = re.search(r'Signal level=(-\d+ dBm)', iwconfig, flags=re.MULTILINE)
        if match:
            signal_level = match.group(1)
        else:
            signal_level = ""

        match = re.search(r'Bit Rate=([\.\d]+ Mb/s)', iwconfig, flags=re.MULTILINE)
        if match:
            bit_rate = match.group(1)
        else:
            bit_rate = ""

        match = re.search(r'Tx-Power=(\d+ dBm)', iwconfig, flags=re.MULTILINE)
        if match:
            tx_power = match.group(1)
        else:
            tx_power = ""

        return (inet_address, rx_packets, tx_packets, essid, link_quality, signal_level, bit_rate, tx_power)

    def loop(self):
        display = SSD1306()

        while True:
            (ia, rx, tx, si, lq, sl, br, tp) = self._get_status()
            display.reset_cursor()
            display.puts((si + "                ")[:16])
            display.puts((ia + "                ")[:16])
            display.puts(("Link: " + lq + "          ")[:16])
            display.puts(("Sig: " + sl + "           ")[:16])
            display.puts(("Rate: " + br + "          ")[:16])
            display.puts(("Pow: " + tp + "           ")[:16])
            display.puts(("Rx: " + rx + "            ")[:16])
            display.puts(("Tx: " + tx + "            ")[:16])
            time.sleep(3)

if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print "Usage: %s <interface>" % (sys.argv[0])
        sys.exit(1)

    ws = WiFiStatus(sys.argv[1])
    ws.loop()
          


Topic: Scripts and Code, by Kjetil @ 05/10-2019, Article Link