Added local counter functionality via pin input

This commit is contained in:
Blue Fox 2023-11-02 18:01:51 +01:00
parent 97a56063ed
commit 7c0d2d114a
5 changed files with 215 additions and 13 deletions

96
counter.py Normal file
View File

@ -0,0 +1,96 @@
from machine import Pin
from time import sleep
from utime import ticks_ms
class Counter:
def __init__(self, name="C1", value = 0): # init_value may be smaller than 0
self.value = int(value)
self.name = str(name)
def __str__(self):
return str(self.name);
def __int__(self):
return int(self.value);
def __add__(self, x):
counter = Counter(value=self.value+int(x))
return counter
def __sub__(self, x):
counter = Counter(value=self.value-int(x))
return counter
def increment(self):
self.value += 1
"""
CounterArray:
provides an easy-to-use interface for the counter class.
"""
class CounterArray:
def __init__(self, counter_number = 1, counter_names = ["Counter 1"]):
self._counters = {}
if len(counter_names) < counter_number:
raise IndexError("counter_names list MUST contain names for all counters.")
elif len(counter_names) > counter_number:
print("[WARN] [CounterArray] More counter names are given than counters wanted. Ignoring the overflow.")
for i in range(counter_number):
self._counters[i] = Counter(counter_names[i])
def add_counter(self, counter: Counter):
self._counters[len(self._counters)] = counter
return True
def remove_counter(self, key):
self._counters.pop(key)
return True
def get_counter(self, key):
return self._counters[key]
def register_listener(self, pins: dict, changed_callback=lambda: None):
# Function:
# listens for btn presses on specified pins
# Parameter: pins
# should be a dict of the form {COUNTER_ID:PIN, ...}
# where COUNTER_ID is the key of the counter to increment in the
# internal _counters dict and PIN is the pin to listen on
# Parameter: changed_callback
# is called without parameters, every time a button was pressed
inputs = {}
for counter_id, pin in pins.items():
inputs[counter_id] = Pin(pin, Pin.IN, Pin.PULL_DOWN)
self.last_time = 0
def press_handler(pin, inputs, _counters, counterarray, changed_callback):
for counter_id, p in inputs.items():
if p == pin: # now we have the pin where the button got pressed
new_time = ticks_ms()
if (new_time - counterarray.last_time) > 150:
_counters[counter_id] += 1
counterarray.last_time = new_time
changed_callback()
for counter_id, pin in inputs.items():
try:
self._counters[counter_id]
pin.irq(trigger=Pin.IRQ_FALLING, handler=lambda p: press_handler(p, inputs, self._counters, self, changed_callback))
except KeyError:
print("[ERROR] [Counter().register_listener] Error while registering counter-pin-pair (counter_id=" + str(counter_id) + ", pin=" + str(pin) + "). Ignoring this pair.")

View File

@ -26,7 +26,6 @@ SHIFT_DATA = 4 # P4-P7
class LCD_API:
# 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.

95
lcd_screen.py Normal file
View File

@ -0,0 +1,95 @@
from machine import I2C, Pin, Timer
import time
class CounterScreen:
def __init__(self, lcd, counterArray):
self.lcd = lcd
self.lcd.clear()
self.counterArray = counterArray
self._zfill = lambda s, length: '{:0>{w}}'.format(s, w=length)
self.last_line1 = ""
self.last_line2 = ""
self.too_many_counters_warning_shown = False
def _too_many_counters_warning(self):
# Function:
# show a short message saying that not more that 5 counters can be displayed.
# WARNING:
# This is a INTERNAL class function, don't call from outside!
# Stops program flow for approx. 15s
self.lcd.move_to(0,0)
self.lcd.putstr(" WARNING! ")
message = "Can't display more than 5 counters. Cutting the overflow. "
message = message + " +++ " + message
while message != " ":
self.lcd.move_to(0,1)
self.lcd.putstr(message[:16])
message = message[1:]
time.sleep(0.07)
self.lcd.clear()
self.too_many_counters_warning_shown = True
def show_screen(self):
# Function:
# show a screen on the lcd, table layout: row 1 shows the short names of the counters,
# row 2 shows the values.
# WARNING:
# This function SHALL be called once at init of the class as it can stop execution for
# approx. 15s at first run!
line1 = ""
line2 = ""
number_counters = len(self.counterArray._counters)
if number_counters > 5:
if not self.too_many_counters_warning_shown:
self._too_many_counters_warning()
number_counters = 5
## show different views depending on how many counters exist
if number_counters < 1:
line1 = " WARNING! "
line2 = " No counters. "
elif number_counters == 1:
line1 = "* C1 "
line2 = "* " + str(list(self.counterArray._counters.items())[0][1].value)
elif number_counters == 2:
v0 = str(list(self.counterArray._counters.items())[0][1].value)
v1 = str(list(self.counterArray._counters.items())[1][1].value)
line1 = "* C1 * C2 "
line2 = "* " + v0 + (6-len(v0))*" " + "* " + v1 + (6-len(v1)) * " "
elif number_counters == 3:
v0 = str(list(self.counterArray._counters.items())[0][1].value)
v1 = str(list(self.counterArray._counters.items())[1][1].value)
v2 = str(list(self.counterArray._counters.items())[2][1].value)
line1 = "C1 * C2 * C3 "
line2 = v0 + (3-len(v0))*" " + " * " + v1 + (3-len(v1))*" " + " * " + v2 + (4-len(v2))*" "
elif number_counters == 4:
line1 = "|C1 |C2 |C3 |C4 "
for i in range(4):
line2 += "|" + self._zfill(list(self.counterArray._counters.items())[i][1].value, 3)[:3]
else:
line1 = "|C1|C2|C3|C4|C5|"
for i in range(5):
line2 += "|" + self._zfill(list(self.counterArray._counters.items())[i][1].value, 2)[:2]
line2 += "|"
# check if something changed, if so, redraw it
if line1 != self.last_line1:
self.lcd.putstr(line1[:16])
self.last_line1 = line1
self.lcd.move_to(0,1)
if line2 != self.last_line2:
self.lcd.putstr(line2[:16])
self.last_line2 = line2

22
micronec.py Normal file
View File

@ -0,0 +1,22 @@
from lcd_driver import I2C_LCD
from machine import I2C, Pin
from welcome import WelcomeScreen
from counter import CounterArray
from lcd_screen import CounterScreen
import time
I2C_ADDR = 0x27
I2C_NUM_ROWS = 2
I2C_NUM_COLS = 16
_i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
lcd = I2C_LCD(_i2c, I2C_ADDR, I2C_NUM_ROWS, I2C_NUM_COLS)
counterArray = CounterArray(2, ["Counter 1", "Counter 2"])
ws = WelcomeScreen(lcd)
cs = CounterScreen(lcd, counterArray)
# Real program
ws.show_welcome(1)
cs.show_screen()
counterArray.register_listener({0:2,1:3}, cs.show_screen)

View File

@ -1,18 +1,8 @@
from lcd_driver import I2C_LCD
from machine import I2C, Pin, Timer
import time
import machine
class WelcomeScreen:
def __init__(self):
self.I2C_ADDR = 0x27
self.I2C_NUM_ROWS = 2
self.I2C_NUM_COLS = 16
self._i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
self.lcd = I2C_LCD(self._i2c, self.I2C_ADDR, self.I2C_NUM_ROWS, self.I2C_NUM_COLS)
def __init__(self, lcd):
self.lcd = lcd
def show_welcome(self, loops=1): # loops says how often the Starting text goes through
if loops < 1: loops = 1