diff --git a/config.json b/config.json
new file mode 100644
index 0000000..35007ff
--- /dev/null
+++ b/config.json
@@ -0,0 +1,18 @@
+{
+"LOG_LEVEL": 2,
+"STARTUP_WELCOME_SHOW": true,
+"STARTUP_PROJECT_NAME":" UV-Belichter ",
+"STARTUP_MESSAGE_STARTING": "Starting...",
+"STARTUP_MESSAGE_FINISHED": " Started! ",
+"STARTUP_WELCOME_CYCLES": 1,
+"PIN_IN_BTN_1": {"pin": 15, "pull": "down"},
+"PIN_IN_BTN_2": {"pin": 14, "pull": "down"},
+"PIN_IN_SWITCH": {"pin": 13, "pull": "down"},
+"PIN_OUT_RELAIS": 21,
+"PIN_SDA": 8,
+"PIN_SCL": 9,
+"LCD_I2C_CH": 0,
+"LCD_I2C_ADDR": 39,
+"LCD_I2C_NUM_ROWS": 2,
+"LCD_I2C_NUM_COLS": 16,
+}
\ No newline at end of file
diff --git a/config.py b/config.py
deleted file mode 100644
index e1a870e..0000000
--- a/config.py
+++ /dev/null
@@ -1,65 +0,0 @@
-"""
-uv-belichter-software - Configuration file
-Copyright (C) 2024 Benjamin Burkhardt
-
-This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with this program. If not, see .
-"""
-
-
-"""
----------------------------
------ LOGGING SECTION -----
----------------------------
-"""
-LOG_LEVEL = 2 # there are three log levels: warn (0), info (1), debug (2)
- # this value defines which log messages to show
- # e.g. 2 means show [debug], [warn] and [info] messages
-
-"""
----------------------------
------ STARTUP SECTION -----
----------------------------
-"""
-STARTUP_PROJECT_NAME = " UV-Belichter " # the name to show at startup
-STARTUP_MESSAGE_STARTING = "Starting..." # the message to show at startup
-STARTUP_MESSAGE_FINISHED = " Started! " # the message to show at startup
-STARTUP_WELCOME_SHOW = True # show the name and a startup message
-STARTUP_WELCOME_CYCLES = 1 # how often shall "Starting..." run over the screen
-
-
-"""
---------------------------
------ PINOUT SECTION -----
---------------------------
-"""
-from machine import Pin
-
-BTN_1 = Pin(15, Pin.IN, Pin.PULL_DOWN) # input of the first btn
-BTN_2 = Pin(14, Pin.IN, Pin.PULL_DOWN) # input of the second btn
-SWITCH = Pin(13, Pin.IN, Pin.PULL_DOWN) # input of switch
-LCD_SDA = Pin(8) # just some standard I2C serial data (SDA) outputs (on I2C channel 0 on Pi Pico)
-LCD_SCL = Pin(9) # just some standard I2C serial clock (SCL) outputs (on I2C channel 0 on Pi Pico)
-#LCD_SDA = Pin(16) # another pinout (soldered on the original project's circuit board)
-#LCD_SCL = Pin(17) # another pinout (soldered on the original project's circuit board)
-RELAIS = Pin(21, Pin.OUT) # where the relais is connected (for the UV lights)
-
-
-"""
------------------------
------ LCD SECTION -----
------------------------
-"""
-from machine import I2C, Pin
-from lib.PCF8574T import I2C_LCD
-
-LCD_I2C_ADDR = 0x27 # the i2c adress of the display (yours might be different to this one)
-LCD_I2C_NUM_ROWS = 2 # how many rows for character display has the display?
-LCD_I2C_NUM_COLS = 16 # and how many characters can it display per row?
-LCD = I2C_LCD(I2C(0, sda=LCD_SDA, scl=LCD_SCL, freq=400000),
- LCD_I2C_ADDR,
- LCD_I2C_NUM_ROWS,
- LCD_I2C_NUM_COLS)
\ No newline at end of file
diff --git a/input_tests.py b/input_tests.py
index 1450fad..bf38ba3 100644
--- a/input_tests.py
+++ b/input_tests.py
@@ -9,13 +9,14 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
You should have received a copy of the GNU General Public License along with this program. If not, see .
"""
-import config as cfg
+from utils import Config
from time import sleep
+cfg = Config()
def run(endless_loop: bool = True, serial_output: bool = True):
while endless_loop:
- if cfg.BTN_1.value() and cfg.BTN_2.value() and cfg.SWITCH.value():
+ if cfg.PIN_IN_BTN_1.value() and cfg.PIN_IN_BTN_2.value() and cfg.PIN_IN_SWITCH.value():
cfg.LCD.move_to(0,0)
cfg.LCD.putstr("In: Y1 | G1 | S1 Exiting! ")
if serial_output:
@@ -23,9 +24,9 @@ def run(endless_loop: bool = True, serial_output: bool = True):
sleep(0.2)
break
cfg.LCD.move_to(0,0)
- cfg.LCD.putstr(f"In: Y{cfg.BTN_1.value()} | G{cfg.BTN_2.value()} | S{cfg.SWITCH.value()}Push all to exit")
+ cfg.LCD.putstr(f"In: Y{cfg.PIN_IN_BTN_1.value()} | G{cfg.PIN_IN_BTN_2.value()} | S{cfg.PIN_IN_SWITCH.value()}Push all to exit")
if serial_output:
- print(f"Y_BTN: {cfg.BTN_1.value()}; G_BTN: {cfg.BTN_2.value()}; Lever: {cfg.SWITCH.value()}")
+ print(f"Y_BTN: {cfg.PIN_IN_BTN_1.value()}; G_BTN: {cfg.PIN_IN_BTN_2.value()}; Lever: {cfg.PIN_IN_SWITCH.value()}")
sleep(0.05)
if __name__ == "__main__":
diff --git a/main.py b/main.py
index e6d5d51..665fc74 100644
--- a/main.py
+++ b/main.py
@@ -14,83 +14,87 @@ import utils
from lcdMenu import lcdMenu
from WelcomeScreen import WelcomeScreen
from time import sleep
-import gc # garbage collector for better memory performance
+from gc import collect # garbage collector for better memory performance
+config = utils.Config()
+btn_mapping = {"ok_btn": config.PIN_IN_BTN_1, "next_btn": config.PIN_IN_BTN_2} # the btn mapping for all lcdMenus
# extra functions to access the garbage collector
def manual():
- utils.cfg.LCD.clear()
- set_value = utils.cfg.RELAIS.value()
- utils.cfg.LCD.putstr(f"---- MANUAL ---- State: {set_value} ")
+ config.LCD.clear()
+ set_value = config.PIN_OUT_RELAIS.value()
+ config.LCD.putstr(f"---- MANUAL ---- State: {set_value} ")
while True:
- if set_value != utils.cfg.SWITCH.value():
- utils.cfg.RELAIS.value(utils.cfg.SWITCH.value())
- set_value = utils.cfg.RELAIS.value()
- utils.cfg.LCD.putstr(f"---- MANUAL ---- State: {set_value} ")
- if utils.cfg.BTN_1.value() == 1 or utils.cfg.BTN_2.value() == 1:
+ if set_value != config.PIN_IN_SWITCH.value():
+ config.PIN_OUT_RELAIS.value(config.PIN_IN_SWITCH.value())
+ set_value = config.PIN_OUT_RELAIS.value()
+ config.LCD.putstr(f"---- MANUAL ---- State: {set_value} ")
+ if config.PIN_IN_BTN_1.value() == 1 or config.PIN_IN_BTN_2.value() == 1:
return True # exit on press of these buttons
def timer():
# display WIP
- utils.cfg.LCD.clear()
- utils.cfg.LCD.putstr(" Still work-in-progress")
+ config.LCD.clear()
+ config.LCD.putstr(" Still work-in-progress")
sleep(3)
return True # disable the "Quitting" message from lcdMenu
def uv_on():
- utils.cfg.RELAIS.value(1)
- utils.cfg.LCD.clear()
- utils.cfg.LCD.putstr("------ UV ------ turned on ")
+ config.RELAIS.value(1)
+ config.LCD.clear()
+ config.LCD.putstr("------ UV ------ turned on ")
sleep(1)
return True # disable the "Quitting" message from lcdMenu
def uv_off():
- utils.cfg.RELAIS.value(0)
- utils.cfg.LCD.clear()
- utils.cfg.LCD.putstr("------ UV ------ turned off ")
+ config.RELAIS.value(0)
+ config.LCD.clear()
+ config.LCD.putstr("------ UV ------ turned off ")
sleep(1)
return True # disable the "Quitting" message from lcdMenu
def lcd_big_hello():
- import lcd_big_hello
- lcd_big_hello.run()
- gc.collect()
+ import lcd_big_hello as lbh
+ lbh.run()
+ del lbh
+ collect()
return True
def input_tests():
import input_tests as input_tests
input_tests.run(serial_output=False)
- gc.collect()
+ collect()
return True
def settings():
# display WIP
- utils.cfg.LCD.clear()
- utils.cfg.LCD.putstr(" Still work-in-progress")
+ config.LCD.clear()
+ config.LCD.putstr(" Still work-in-progress")
sleep(3)
return True
+def run_demo_menu():
+ demo_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="DEMOS")
+ demo_programs = [("LCD Demo", lcd_big_hello),
+ ("Input tests", input_tests),
+ ("Exit", demo_menu.stop)]
+ demo_menu.setup(demo_programs) # give it the callback list
+ ret = demo_menu.run()
+ del demo_menu, demo_programs
+ collect()
+ return ret
-
-if utils.cfg.STARTUP_WELCOME_SHOW:
- ws = WelcomeScreen(utils.cfg.LCD,
- interrupt_pins=[utils.cfg.BTN_1, utils.cfg.BTN_2, utils.cfg.SWITCH],
- subtitle=utils.cfg.STARTUP_PROJECT_NAME,
- starting_msg=utils.cfg.STARTUP_MESSAGE_STARTING,
- started_msg=utils.cfg.STARTUP_MESSAGE_FINISHED)
- ws.show(cycles=utils.cfg.STARTUP_WELCOME_CYCLES)
+if config.STARTUP_WELCOME_SHOW:
+ ws = WelcomeScreen(config.LCD,
+ interrupt_pins=[config.PIN_IN_BTN_1, config.PIN_IN_BTN_2, config.PIN_IN_SWITCH],
+ subtitle=config.STARTUP_PROJECT_NAME,
+ starting_msg=config.STARTUP_MESSAGE_STARTING,
+ started_msg=config.STARTUP_MESSAGE_FINISHED)
+ ws.show(cycles=config.STARTUP_WELCOME_CYCLES)
del ws
- gc.collect()
+ collect()
# create the menus
-btn_mapping = {"ok_btn": utils.cfg.BTN_1, "next_btn": utils.cfg.BTN_2} # the btn mapping for all menus
-
-demo_menu = lcdMenu(utils.cfg.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="DEMOS")
-demo_programs = [("LCD Demo", lcd_big_hello),
- ("Input tests", input_tests),
- ("Exit", demo_menu.stop)]
-demo_menu.setup(demo_programs) # give it the callback list
-
-main_menu = lcdMenu(utils.cfg.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="PROGRAMS")
+main_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="PROGRAMS")
main_programs = [("Timer", timer),
("Manual", manual),
("UV off", uv_off),
("UV on", uv_on),
- ("Demos", demo_menu.run),
+ ("Demos", run_demo_menu),
("Settings", settings)]
main_menu.setup(main_programs) # give it the callback list
diff --git a/utils.py b/utils.py
index 2ff585d..ad5b76c 100644
--- a/utils.py
+++ b/utils.py
@@ -9,9 +9,106 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
You should have received a copy of the GNU General Public License along with this program. If not, see .
"""
+from gc import collect
-import config as cfg
-from time import sleep
+"""
+A small wrapper class as storing machine.Pin, LCD and machine.I2C objects in a file is not that cool :)
+Now only pin numbers and strings etc. are stored on the uC, and the complex objects are generated on the fly
+"""
+class Config:
+ """
+ The initializer method
+ - config_file: the path to the config file laying on the uC
+ """
+ def __init__(self, config_file: str = "config.json"):
+ self._attr_list = ["LOG_LEVEL", # there are three log levels: warn (0), info (1), debug (2)
+ # this value defines which log messages to show
+ # e.g. 2 means show [debug], [warn] and [info] messages
+ "STARTUP_WELCOME_SHOW", # show the name and a startup message
+ "STARTUP_PROJECT_NAME", # the name to show at startup
+ "STARTUP_MESSAGE_STARTING", # the message to show at startup
+ "STARTUP_MESSAGE_FINISHED", # the message to show at startup
+ "STARTUP_WELCOME_CYCLES", # how often shall "Starting..." run over the screen
+ "PIN_IN_BTN_1", # input of the first btn
+ "PIN_IN_BTN_2", # input of the second btn
+ "PIN_IN_SWITCH", # input of the switch
+ "PIN_OUT_RELAIS", # where the relais is connected (for the UV lights)
+ "PIN_SDA", # just some standard I2C serial data (SDA) output
+ "PIN_SCL", # just some standard I2C serial clock (SCL) output
+ "LCD_I2C_CH", # where the relais is connected (for the UV lights)
+ "LCD_I2C_ADDR", # the i2c adress of the display
+ "LCD_I2C_NUM_ROWS", # how many rows for character display has the display?
+ "LCD_I2C_NUM_COLS", # and how many characters can it display per row?
+ "LCD"] # the actual lcd object (of the PCF8574T I2C_LCD class, see libraries)
+ self._config_file = config_file
+ self.load_config()
+
+ def load_config(self):
+ # prepare the class
+ with open(self._config_file, "r") as f:
+ from json import load
+ self._config = load(f)
+ del load
+ collect()
+
+ def save_config(self):
+ with open(self._config_file, "w") as f:
+ from json import dump
+ dump(self._config, f)
+ del dump
+ collect()
+
+
+ def __getattr__(self, name):
+ if name.startswith("_"): # make private attributes unaccessible
+ raise AttributeError(f"'Access to the private attribute '{name}' of the object '{self.__class__.__name__}' is forbidden")
+ elif name in self._attr_list: # valid attributes (only capital letters and -_ etc. are allowed)
+ try:
+ # now some if statements to check if the lcd or some pin object is asked
+ if name.startswith("PIN_"):
+ from machine import Pin
+ if name.startswith("PIN_IN"):
+ if self._config[name]["pull"].lower() == "down":
+ p = Pin(self._config[name]["pin"], Pin.IN, Pin.PULL_DOWN)
+ elif self._config[name]["pull"].lower() == "up":
+ p = Pin(self._config[name]["pin"], Pin.IN, Pin.PULL_UP)
+ else:
+ p = Pin(self._config[name]["pin"], Pin.IN)
+ elif name.startswith("PIN_OUT"):
+ p = Pin(self._config[name], Pin.OUT)
+ else:
+ p = Pin(self._config[name])
+ del Pin
+ collect()
+ return p
+ elif name == "LCD":
+ try:
+ return self._lcd
+ except:
+ from machine import I2C, Pin
+ from PCF8574T import I2C_LCD
+ self._lcd = I2C_LCD(I2C(self.LCD_I2C_CH, sda=self.PIN_SDA, scl=self.PIN_SCL, freq=400000),
+ self.LCD_I2C_ADDR,
+ self.LCD_I2C_NUM_ROWS,
+ self.LCD_I2C_NUM_COLS)
+ del I2C, Pin, I2C_LCD
+ collect()
+ return self._lcd
+ return self._config[name]
+ except KeyError:
+ raise AttributeError(f"Attribute '{name}' does not exist in the config file '{self._config_file}'")
+ else:
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
+
+
+ def __setattr__(self, name, value):
+ #print(f"Someone tried to edit my poor attributes! Affected: '{name}' should be set to '{value}'")
+ object.__setattr__(self, name, value)
+
+ def __delattr__(self, name):
+ raise AttributeError(f"You may not delete any attribute of the '{self.__class__.__name__}' object")
+
+cfg = Config()
"""