summaryrefslogtreecommitdiff
path: root/anzeige0
diff options
context:
space:
mode:
authorYves Fischer <yvesf-git@xapek.org>2018-08-02 17:21:14 +0200
committerYves Fischer <yvesf-git@xapek.org>2018-08-02 17:59:06 +0200
commit74973624deed05d1cc8b297fb1d877cadad50969 (patch)
tree8304000f99a4dc0e0fad263fc9928d8e5ad6366c /anzeige0
downloadesp8266-master.tar.gz
esp8266-master.zip
new commit-history root - cleaned off passwordsHEADmaster
Diffstat (limited to 'anzeige0')
-rw-r--r--anzeige0/Kconfig4
-rw-r--r--anzeige0/boot.py2
-rw-r--r--anzeige0/lcd_api.py195
-rw-r--r--anzeige0/main.py174
-rwxr-xr-xanzeige0/make.sh5
-rw-r--r--anzeige0/nodemcu_gpio_lcd.py168
6 files changed, 548 insertions, 0 deletions
diff --git a/anzeige0/Kconfig b/anzeige0/Kconfig
new file mode 100644
index 0000000..c588b68
--- /dev/null
+++ b/anzeige0/Kconfig
@@ -0,0 +1,4 @@
+mainmenu "configuration"
+
+source ../scripts/Kconfig-wifi
+
diff --git a/anzeige0/boot.py b/anzeige0/boot.py
new file mode 100644
index 0000000..0d30893
--- /dev/null
+++ b/anzeige0/boot.py
@@ -0,0 +1,2 @@
+config = dict(map(lambda x: (x[0], int(x[1]) if x[1].isdigit() else x[1][1:-1] if x[1].startswith('"') and x[1].endswith('"') else x[1]), map(lambda x: x.strip().split("=", 1), filter(lambda x: '=' in x, open(".config").readlines()))))
+
diff --git a/anzeige0/lcd_api.py b/anzeige0/lcd_api.py
new file mode 100644
index 0000000..38b60da
--- /dev/null
+++ b/anzeige0/lcd_api.py
@@ -0,0 +1,195 @@
+"""Provides an API for talking to HD44780 compatible character LCDs."""
+
+import time
+
+class LcdApi:
+ """Implements the API for talking with HD44780 compatible character LCDs.
+ This class only knows what commands to send to the LCD, and not how to get
+ them to the LCD.
+
+ It is expected that a derived class will implement the hal_xxx functions.
+ """
+
+ # The following constant names were lifted from the avrlib lcd.h
+ # header file, however, I changed the definitions from bit numbers
+ # to bit masks.
+ #
+ # HD44780 LCD controller command set
+
+ LCD_CLR = 0x01 # DB0: clear display
+ LCD_HOME = 0x02 # DB1: return to home position
+
+ LCD_ENTRY_MODE = 0x04 # DB2: set entry mode
+ LCD_ENTRY_INC = 0x02 # --DB1: increment
+ LCD_ENTRY_SHIFT = 0x01 # --DB0: shift
+
+ LCD_ON_CTRL = 0x08 # DB3: turn lcd/cursor on
+ LCD_ON_DISPLAY = 0x04 # --DB2: turn display on
+ LCD_ON_CURSOR = 0x02 # --DB1: turn cursor on
+ LCD_ON_BLINK = 0x01 # --DB0: blinking cursor
+
+ LCD_MOVE = 0x10 # DB4: move cursor/display
+ LCD_MOVE_DISP = 0x08 # --DB3: move display (0-> move cursor)
+ LCD_MOVE_RIGHT = 0x04 # --DB2: move right (0-> left)
+
+ LCD_FUNCTION = 0x20 # DB5: function set
+ LCD_FUNCTION_8BIT = 0x10 # --DB4: set 8BIT mode (0->4BIT mode)
+ LCD_FUNCTION_2LINES = 0x08 # --DB3: two lines (0->one line)
+ LCD_FUNCTION_10DOTS = 0x04 # --DB2: 5x10 font (0->5x7 font)
+ LCD_FUNCTION_RESET = 0x30 # See "Initializing by Instruction" section
+
+ LCD_CGRAM = 0x40 # DB6: set CG RAM address
+ LCD_DDRAM = 0x80 # DB7: set DD RAM address
+
+ LCD_RS_CMD = 0
+ LCD_RS_DATA = 1
+
+ LCD_RW_WRITE = 0
+ LCD_RW_READ = 1
+
+ def __init__(self, num_lines, num_columns):
+ self.num_lines = num_lines
+ if self.num_lines > 4:
+ self.num_lines = 4
+ self.num_columns = num_columns
+ if self.num_columns > 40:
+ self.num_columns = 40
+ self.cursor_x = 0
+ self.cursor_y = 0
+ self.backlight = True
+ self.display_off()
+ self.backlight_on()
+ self.clear()
+ self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
+ self.hide_cursor()
+ self.display_on()
+
+ def clear(self):
+ """Clears the LCD display and moves the cursor to the top left
+ corner.
+ """
+ self.hal_write_command(self.LCD_CLR)
+ self.hal_write_command(self.LCD_HOME)
+ self.cursor_x = 0
+ self.cursor_y = 0
+
+ def show_cursor(self):
+ """Causes the cursor to be made visible."""
+ self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
+ self.LCD_ON_CURSOR)
+
+ def hide_cursor(self):
+ """Causes the cursor to be hidden."""
+ self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
+
+ def blink_cursor_on(self):
+ """Turns on the cursor, and makes it blink."""
+ self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
+ self.LCD_ON_CURSOR | self.LCD_ON_BLINK)
+
+ def blink_cursor_off(self):
+ """Turns on the cursor, and makes it no blink (i.e. be solid)."""
+ self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
+ self.LCD_ON_CURSOR)
+
+ def display_on(self):
+ """Turns on (i.e. unblanks) the LCD."""
+ self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
+
+ def display_off(self):
+ """Turns off (i.e. blanks) the LCD."""
+ self.hal_write_command(self.LCD_ON_CTRL)
+
+ def backlight_on(self):
+ """Turns the backlight on.
+
+ This isn't really an LCD command, but some modules have backlight
+ controls, so this allows the hal to pass through the command.
+ """
+ self.backlight = True
+ self.hal_backlight_on()
+
+ def backlight_off(self):
+ """Turns the backlight off.
+
+ This isn't really an LCD command, but some modules have backlight
+ controls, so this allows the hal to pass through the command.
+ """
+ self.backlight = False
+ self.hal_backlight_off()
+
+ def move_to(self, cursor_x, cursor_y):
+ """Moves the cursor position to the indicated position. The cursor
+ position is zero based (i.e. cursor_x == 0 indicates first column).
+ """
+ self.cursor_x = cursor_x
+ self.cursor_y = cursor_y
+ addr = cursor_x & 0x3f
+ if cursor_y & 1:
+ addr += 0x40 # Lines 1 & 3 add 0x40
+ if cursor_y & 2:
+ addr += 0x14 # Lines 2 & 3 add 0x14
+ self.hal_write_command(self.LCD_DDRAM | addr)
+
+ def putchar(self, char):
+ """Writes the indicated character to the LCD at the current cursor
+ position, and advances the cursor by one position.
+ """
+ if char != '\n':
+ self.hal_write_data(ord(char))
+ self.cursor_x += 1
+ if self.cursor_x >= self.num_columns or char == '\n':
+ self.cursor_x = 0
+ self.cursor_y += 1
+ if self.cursor_y >= self.num_lines:
+ self.cursor_y = 0
+ self.move_to(self.cursor_x, self.cursor_y)
+
+ def putstr(self, string):
+ """Write the indicated string to the LCD at the current cursor
+ position and advances the cursor position appropriately.
+ """
+ for char in string:
+ self.putchar(char)
+
+ def custom_char(self, location, charmap):
+ """Write a character to one of the 8 CGRAM locations, available
+ as chr(0) through chr(7).
+ """
+ location &= 0x7
+ self.hal_write_command(self.LCD_CGRAM | (location << 3))
+ time.sleep_us(40)
+ for i in range(8):
+ self.hal_write_data(charmap[i])
+ time.sleep_us(40)
+ self.move_to(self.cursor_x, self.cursor_y)
+
+ def hal_backlight_on(self):
+ """Allows the hal layer to turn the backlight on.
+
+ If desired, a derived HAL class will implement this function.
+ """
+ pass
+
+ def hal_backlight_off(self):
+ """Allows the hal layer to turn the backlight off.
+
+ If desired, a derived HAL class will implement this function.
+ """
+ pass
+
+ def hal_write_command(self, cmd):
+ """Write a command to the LCD.
+
+ It is expected that a derived HAL class will implement this
+ function.
+ """
+ raise NotImplementedError
+
+ def hal_write_data(self, data):
+ """Write data to the LCD.
+
+ It is expected that a derived HAL class will implement this
+ function.
+ """
+ raise NotImplementedError
diff --git a/anzeige0/main.py b/anzeige0/main.py
new file mode 100644
index 0000000..c73e847
--- /dev/null
+++ b/anzeige0/main.py
@@ -0,0 +1,174 @@
+#!micropython
+print("Start main.py")
+import json
+import time
+import machine
+import socket
+import micropython as mp
+
+from nodemcu_gpio_lcd import GpioLcd
+from machine import Pin, PWM
+import network
+
+backlight = PWM(Pin(12), freq=500, duty=200)
+
+wlan = network.WLAN()
+
+lcd = GpioLcd(rs_pin=Pin(16), enable_pin=Pin(5),
+ d4_pin=Pin(4), d5_pin=Pin(0), d6_pin=Pin(2), d7_pin=Pin(14),
+ num_lines=2, num_columns=16)
+
+lcd.clear()
+lcd.putstr("Verbinde mit:\n{}".format(wlan.config('essid')))
+while not wlan.isconnected():
+ continue
+lcd.clear()
+lcd.putstr("Verbunden")
+
+HOST = b'www.localnet.cc'
+HEADERS = b'Host: www.localnet.cc\r\nConnection: close\r\n\r\n'
+
+displays = [
+ { # ################ ################ <- 16 chars
+ "name": "Zusammenfassung " + " ",
+ "text": "I: ____ E: ____ " + "W: ____ßS: ____ß",
+ "values" : [
+ """SELECT sum("d4_count")*30 FROM "stromzaehler3" WHERE time > now() - 2m""",
+ """SELECT sum("d1_count")*30 FROM "stromzaehler3" WHERE time > now() - 2m""",
+ """SELECT last(value) FROM temp0 WHERE sensor = '28-8B-DC-E9-16-13-01-D5'""",
+ """SELECT last(value) FROM temp0 WHERE sensor = '28-7F-69-16-17-13-01-A6'""",
+ ]
+ },
+ { # ################ ################ <- 16 chars
+ "name": "Stromz\xe1hler " + " Heute ",
+ "text": "\xf6 Import ____Wh" + "\xf6 Export ____Wh",
+ "values" : [
+ "SELECT last(sum) FROM (SELECT sum(d4_count) FROM stromzaehler3 WHERE time > now() - 1d GROUP BY time(1d) tz('Europe/Berlin'))",
+ "SELECT last(sum) FROM (SELECT sum(d1_count) FROM stromzaehler3 WHERE time > now() - 1d GROUP BY time(1d) tz('Europe/Berlin'))",
+ ],
+ },
+ { # ################ ################ <- 16 chars
+ "name": "Heizung Solar " + " ",
+ "text": "Von Dach: ____ß " + "Zum Dach: ____ß ",
+ "values" : [
+ "SELECT last(value) FROM temp0 WHERE sensor = '28-7F-69-16-17-13-01-A6'",
+ "SELECT last(value) FROM temp0 WHERE sensor = '28-99-38-EB-16-13-01-22'",
+ ]
+ },
+ { # ################ ################ <- 16 chars
+ "name": "BFT Rheinau " + " ",
+ "text": "S95-E5: __.___ " + "Diesel: __.___ ",
+ "values" : [
+ """SELECT last(value) FROM "tankerkoenig.SP95-E5" WHERE "name" = 'Rheinau-Freistett - BFT - Freistett'""",
+ """SELECT last(value) FROM "tankerkoenig.Diesel" WHERE "name" = 'Rheinau-Freistett - BFT - Freistett'""",
+ ]},
+ ]
+
+def query_one(db, select):
+ select_esc = select.replace(" ", "%20")
+ select_esc = select_esc.replace("\"", "%22")
+ addr = socket.getaddrinfo(HOST, 80)[0][-1]
+ s = socket.socket()
+ s.connect(addr)
+ s.send(b'GET /grafana/api/datasources/proxy/3/query?db={}&q={}&epoch=ms\r\n'.format(db, select_esc))
+ s.send(HEADERS)
+ data = json.load(s)
+ s.close()
+ return data['results'][0]['series'][0]['values'][0][1]
+
+current_display = 0
+def update():
+ global UPDATE_RUN, current_display, display, lcd
+
+ lcd.move_to(0, 0)
+ text = displays[current_display]["text"]
+
+ s = ""
+ cur = ""
+ pos = 0
+ i = 0
+ while i<len(text):
+ c = text[i]
+ cur_added = False
+ if c == '_' or ( len(cur) > 0 and c == '.' ):
+ cur += c
+ cur_added = True
+
+ if len(cur) > 0 and (not cur_added or i == len(text) - 1):
+ value_query = displays[current_display]["values"][pos]
+ v = query_one("data", value_query)
+ if isinstance(v, float):
+ if "." in cur:
+ precision = len(cur) - cur.index(".") - 1
+ else:
+ precision = 0
+ fmt_string = "{{: >{}.{}f}}".format(len(cur), precision)
+ elif isinstance(v, int):
+ fmt_string = "{{: >{}d}}".format(len(cur))
+ else:
+ raise Exception("Wrong type: {}".format(type(v)))
+ s += fmt_string.format(v)
+ lcd.putstr(s)
+ s = ""
+ cur = ""
+ pos += 1
+
+ if not cur_added:
+ s += c
+
+ if not cur_added and i == len(text)-1:
+ lcd.putstr(s)
+ s = ""
+
+ i += 1
+
+ lcd.putstr(s)
+
+last_update = 0
+
+def schedule_update(timer=None, force=False):
+ global last_update
+ def ex(none):
+ try:
+ update()
+ except Exception as e:
+ lcd.clear()
+ msg = "Error: {}".format(str(e)[0:(32-7)])
+ lcd.putstr(msg)
+ raise e
+
+ now = time.ticks_ms()
+ if force:
+ last_update = now
+ ex(None)
+ elif now - last_update > 5000:
+ last_update = now
+ mp.schedule(ex, None)
+
+timer = machine.Timer(-1)
+
+def switch_display(none):
+ global current_display, displays
+
+ current_display += 1
+ current_display %= len(displays)
+ lcd.move_to(0,0)
+ lcd.putstr(displays[current_display]['name'])
+
+ # trigger update in 1s
+ last_update = time.ticks_ms() - 4000
+
+last_switch = 0
+def switch_pressed(pin):
+ global last_switch
+ now = time.ticks_ms()
+ if pin.value() == 0 and now - last_switch > 200:
+ mp.schedule(switch_display, None)
+ last_switch = now
+
+switch = machine.Pin(13, machine.Pin.IN)
+switch.irq(switch_pressed)
+
+timer.init(period=500, mode=machine.Timer.PERIODIC, callback=schedule_update)
+
+print("main.py finished")
diff --git a/anzeige0/make.sh b/anzeige0/make.sh
new file mode 100755
index 0000000..ef68b45
--- /dev/null
+++ b/anzeige0/make.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+PY_SOURCES="boot.py main.py .config lcd_api.py nodemcu_gpio_lcd.py"
+
+. ${0%/*}/../scripts/make.inc.sh
+
diff --git a/anzeige0/nodemcu_gpio_lcd.py b/anzeige0/nodemcu_gpio_lcd.py
new file mode 100644
index 0000000..0bb839c
--- /dev/null
+++ b/anzeige0/nodemcu_gpio_lcd.py
@@ -0,0 +1,168 @@
+"""Implements a HD44780 character LCD connected via NodeMCU GPIO pins."""
+
+from lcd_api import LcdApi
+from machine import Pin
+from utime import sleep_ms, sleep_us
+
+
+class GpioLcd(LcdApi):
+ """Implements a HD44780 character LCD connected via NodeMCU GPIO pins."""
+
+ def __init__(self, rs_pin, enable_pin, d0_pin=None, d1_pin=None,
+ d2_pin=None, d3_pin=None, d4_pin=None, d5_pin=None,
+ d6_pin=None, d7_pin=None, rw_pin=None, backlight_pin=None,
+ num_lines=2, num_columns=16):
+ """Constructs the GpioLcd object. All of the arguments must be Pin
+ objects which describe which pin the given line from the LCD is
+ connected to.
+
+ When used in 4-bit mode, only D4, D5, D6, and D7 are physically
+ connected to the LCD panel. This function allows you call it like
+ GpioLcd(rs, enable, D4, D5, D6, D7) and it will interpret that as
+ if you had actually called:
+ GpioLcd(rs, enable, d4=D4, d5=D5, d6=D6, d7=D7)
+
+ The enable 8-bit mode, you need pass d0 through d7.
+
+ The rw pin isn't used by this library, but if you specify it, then
+ it will be set low.
+ """
+ self.rs_pin = rs_pin
+ self.enable_pin = enable_pin
+ self.rw_pin = rw_pin
+ self.backlight_pin = backlight_pin
+ self._4bit = True
+ if d4_pin and d5_pin and d6_pin and d7_pin:
+ self.d0_pin = d0_pin
+ self.d1_pin = d1_pin
+ self.d2_pin = d2_pin
+ self.d3_pin = d3_pin
+ self.d4_pin = d4_pin
+ self.d5_pin = d5_pin
+ self.d6_pin = d6_pin
+ self.d7_pin = d7_pin
+ if self.d0_pin and self.d1_pin and self.d2_pin and self.d3_pin:
+ self._4bit = False
+ else:
+ # This is really 4-bit mode, and the 4 data pins were just
+ # passed as the first 4 arguments, so we switch things around.
+ self.d0_pin = None
+ self.d1_pin = None
+ self.d2_pin = None
+ self.d3_pin = None
+ self.d4_pin = d0_pin
+ self.d5_pin = d1_pin
+ self.d6_pin = d2_pin
+ self.d7_pin = d3_pin
+ self.rs_pin.init(Pin.OUT)
+ self.rs_pin.value(0)
+ if self.rw_pin:
+ self.rw_pin.init(Pin.OUT)
+ self.rw_pin.value(0)
+ self.enable_pin.init(Pin.OUT)
+ self.enable_pin.value(0)
+ self.d4_pin.init(Pin.OUT)
+ self.d5_pin.init(Pin.OUT)
+ self.d6_pin.init(Pin.OUT)
+ self.d7_pin.init(Pin.OUT)
+ self.d4_pin.value(0)
+ self.d5_pin.value(0)
+ self.d6_pin.value(0)
+ self.d7_pin.value(0)
+ if not self._4bit:
+ self.d0_pin.init(Pin.OUT)
+ self.d1_pin.init(Pin.OUT)
+ self.d2_pin.init(Pin.OUT)
+ self.d3_pin.init(Pin.OUT)
+ self.d0_pin.value(0)
+ self.d1_pin.value(0)
+ self.d2_pin.value(0)
+ self.d3_pin.value(0)
+ if self.backlight_pin is not None:
+ self.backlight_pin.init(Pin.OUT)
+ self.backlight_pin.value(0)
+
+ # See about splitting this into begin
+
+ sleep_ms(20) # Allow LCD time to powerup
+ # Send reset 3 times
+ self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
+ sleep_ms(5) # need to delay at least 4.1 msec
+ self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
+ sleep_ms(1)
+ self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
+ sleep_ms(1)
+ cmd = self.LCD_FUNCTION
+ if not self._4bit:
+ cmd |= self.LCD_FUNCTION_8BIT
+ self.hal_write_init_nibble(cmd)
+ sleep_ms(1)
+ LcdApi.__init__(self, num_lines, num_columns)
+ if num_lines > 1:
+ cmd |= self.LCD_FUNCTION_2LINES
+ self.hal_write_command(cmd)
+
+ def hal_pulse_enable(self):
+ """Pulse the enable line high, and then low again."""
+ self.enable_pin.value(0)
+ sleep_us(1)
+ self.enable_pin.value(1)
+ sleep_us(1) # Enable pulse needs to be > 450 nsec
+ self.enable_pin.value(0)
+ sleep_us(100) # Commands need > 37us to settle
+
+ def hal_write_init_nibble(self, nibble):
+ """Writes an initialization nibble to the LCD.
+
+ This particular function is only used during initialization.
+ """
+ self.hal_write_4bits(nibble >> 4)
+
+ def hal_backlight_on(self):
+ """Allows the hal layer to turn the backlight on."""
+ if self.backlight_pin:
+ self.backlight_pin.value(1)
+
+ def hal_backlight_off(self):
+ """Allows the hal layer to turn the backlight off."""
+ if self.backlight_pin:
+ self.backlight_pin.value(0)
+
+ def hal_write_command(self, cmd):
+ """Writes a command to the LCD.
+
+ Data is latched on the falling edge of E.
+ """
+ self.rs_pin.value(0)
+ self.hal_write_8bits(cmd)
+ if cmd <= 3:
+ # The home and clear commands require a worst
+ # case delay of 4.1 msec
+ sleep_ms(5)
+
+ def hal_write_data(self, data):
+ """Write data to the LCD."""
+ self.rs_pin.value(1)
+ self.hal_write_8bits(data)
+
+ def hal_write_8bits(self, value):
+ """Writes 8 bits of data to the LCD."""
+ if self.rw_pin:
+ self.rw_pin.value(0)
+ if self._4bit:
+ self.hal_write_4bits(value >> 4)
+ self.hal_write_4bits(value)
+ else:
+ self.d3_pin.value(value & 0x08)
+ self.d2_pin.value(value & 0x04)
+ self.d1_pin.value(value & 0x02)
+ self.d0_pin.value(value & 0x01)
+ self.hal_write_4bits(value >> 4)
+
+ def hal_write_4bits(self, nibble):
+ """Writes 4 bits of data to the LCD."""
+ self.d7_pin.value(nibble & 0x08)
+ self.d6_pin.value(nibble & 0x04)
+ self.d5_pin.value(nibble & 0x02)
+ self.d4_pin.value(nibble & 0x01)
+ self.hal_pulse_enable()