Moved to json as the configuration format
This commit is contained in:
parent
533deb2833
commit
3c97b96435
18
config.json
Normal file
18
config.json
Normal file
@ -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,
|
||||||
|
}
|
65
config.py
65
config.py
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
---------------------------
|
|
||||||
----- 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)
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import config as cfg
|
from utils import Config
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
cfg = Config()
|
||||||
|
|
||||||
def run(endless_loop: bool = True, serial_output: bool = True):
|
def run(endless_loop: bool = True, serial_output: bool = True):
|
||||||
while endless_loop:
|
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.move_to(0,0)
|
||||||
cfg.LCD.putstr("In: Y1 | G1 | S1 Exiting! ")
|
cfg.LCD.putstr("In: Y1 | G1 | S1 Exiting! ")
|
||||||
if serial_output:
|
if serial_output:
|
||||||
@ -23,9 +24,9 @@ def run(endless_loop: bool = True, serial_output: bool = True):
|
|||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
break
|
break
|
||||||
cfg.LCD.move_to(0,0)
|
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:
|
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)
|
sleep(0.05)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
88
main.py
88
main.py
@ -14,83 +14,87 @@ import utils
|
|||||||
from lcdMenu import lcdMenu
|
from lcdMenu import lcdMenu
|
||||||
from WelcomeScreen import WelcomeScreen
|
from WelcomeScreen import WelcomeScreen
|
||||||
from time import sleep
|
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
|
# extra functions to access the garbage collector
|
||||||
def manual():
|
def manual():
|
||||||
utils.cfg.LCD.clear()
|
config.LCD.clear()
|
||||||
set_value = utils.cfg.RELAIS.value()
|
set_value = config.PIN_OUT_RELAIS.value()
|
||||||
utils.cfg.LCD.putstr(f"---- MANUAL ---- State: {set_value} ")
|
config.LCD.putstr(f"---- MANUAL ---- State: {set_value} ")
|
||||||
while True:
|
while True:
|
||||||
if set_value != utils.cfg.SWITCH.value():
|
if set_value != config.PIN_IN_SWITCH.value():
|
||||||
utils.cfg.RELAIS.value(utils.cfg.SWITCH.value())
|
config.PIN_OUT_RELAIS.value(config.PIN_IN_SWITCH.value())
|
||||||
set_value = utils.cfg.RELAIS.value()
|
set_value = config.PIN_OUT_RELAIS.value()
|
||||||
utils.cfg.LCD.putstr(f"---- MANUAL ---- State: {set_value} ")
|
config.LCD.putstr(f"---- MANUAL ---- State: {set_value} ")
|
||||||
if utils.cfg.BTN_1.value() == 1 or utils.cfg.BTN_2.value() == 1:
|
if config.PIN_IN_BTN_1.value() == 1 or config.PIN_IN_BTN_2.value() == 1:
|
||||||
return True # exit on press of these buttons
|
return True # exit on press of these buttons
|
||||||
def timer():
|
def timer():
|
||||||
# display WIP
|
# display WIP
|
||||||
utils.cfg.LCD.clear()
|
config.LCD.clear()
|
||||||
utils.cfg.LCD.putstr(" Still work-in-progress")
|
config.LCD.putstr(" Still work-in-progress")
|
||||||
sleep(3)
|
sleep(3)
|
||||||
return True # disable the "Quitting" message from lcdMenu
|
return True # disable the "Quitting" message from lcdMenu
|
||||||
def uv_on():
|
def uv_on():
|
||||||
utils.cfg.RELAIS.value(1)
|
config.RELAIS.value(1)
|
||||||
utils.cfg.LCD.clear()
|
config.LCD.clear()
|
||||||
utils.cfg.LCD.putstr("------ UV ------ turned on ")
|
config.LCD.putstr("------ UV ------ turned on ")
|
||||||
sleep(1)
|
sleep(1)
|
||||||
return True # disable the "Quitting" message from lcdMenu
|
return True # disable the "Quitting" message from lcdMenu
|
||||||
def uv_off():
|
def uv_off():
|
||||||
utils.cfg.RELAIS.value(0)
|
config.RELAIS.value(0)
|
||||||
utils.cfg.LCD.clear()
|
config.LCD.clear()
|
||||||
utils.cfg.LCD.putstr("------ UV ------ turned off ")
|
config.LCD.putstr("------ UV ------ turned off ")
|
||||||
sleep(1)
|
sleep(1)
|
||||||
return True # disable the "Quitting" message from lcdMenu
|
return True # disable the "Quitting" message from lcdMenu
|
||||||
def lcd_big_hello():
|
def lcd_big_hello():
|
||||||
import lcd_big_hello
|
import lcd_big_hello as lbh
|
||||||
lcd_big_hello.run()
|
lbh.run()
|
||||||
gc.collect()
|
del lbh
|
||||||
|
collect()
|
||||||
return True
|
return True
|
||||||
def input_tests():
|
def input_tests():
|
||||||
import input_tests as input_tests
|
import input_tests as input_tests
|
||||||
input_tests.run(serial_output=False)
|
input_tests.run(serial_output=False)
|
||||||
gc.collect()
|
collect()
|
||||||
return True
|
return True
|
||||||
def settings():
|
def settings():
|
||||||
# display WIP
|
# display WIP
|
||||||
utils.cfg.LCD.clear()
|
config.LCD.clear()
|
||||||
utils.cfg.LCD.putstr(" Still work-in-progress")
|
config.LCD.putstr(" Still work-in-progress")
|
||||||
sleep(3)
|
sleep(3)
|
||||||
return True
|
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 config.STARTUP_WELCOME_SHOW:
|
||||||
if utils.cfg.STARTUP_WELCOME_SHOW:
|
ws = WelcomeScreen(config.LCD,
|
||||||
ws = WelcomeScreen(utils.cfg.LCD,
|
interrupt_pins=[config.PIN_IN_BTN_1, config.PIN_IN_BTN_2, config.PIN_IN_SWITCH],
|
||||||
interrupt_pins=[utils.cfg.BTN_1, utils.cfg.BTN_2, utils.cfg.SWITCH],
|
subtitle=config.STARTUP_PROJECT_NAME,
|
||||||
subtitle=utils.cfg.STARTUP_PROJECT_NAME,
|
starting_msg=config.STARTUP_MESSAGE_STARTING,
|
||||||
starting_msg=utils.cfg.STARTUP_MESSAGE_STARTING,
|
started_msg=config.STARTUP_MESSAGE_FINISHED)
|
||||||
started_msg=utils.cfg.STARTUP_MESSAGE_FINISHED)
|
ws.show(cycles=config.STARTUP_WELCOME_CYCLES)
|
||||||
ws.show(cycles=utils.cfg.STARTUP_WELCOME_CYCLES)
|
|
||||||
del ws
|
del ws
|
||||||
gc.collect()
|
collect()
|
||||||
|
|
||||||
|
|
||||||
# create the menus
|
# create the menus
|
||||||
btn_mapping = {"ok_btn": utils.cfg.BTN_1, "next_btn": utils.cfg.BTN_2} # the btn mapping for all menus
|
main_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="PROGRAMS")
|
||||||
|
|
||||||
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_programs = [("Timer", timer),
|
main_programs = [("Timer", timer),
|
||||||
("Manual", manual),
|
("Manual", manual),
|
||||||
("UV off", uv_off),
|
("UV off", uv_off),
|
||||||
("UV on", uv_on),
|
("UV on", uv_on),
|
||||||
("Demos", demo_menu.run),
|
("Demos", run_demo_menu),
|
||||||
("Settings", settings)]
|
("Settings", settings)]
|
||||||
main_menu.setup(main_programs) # give it the callback list
|
main_menu.setup(main_programs) # give it the callback list
|
||||||
|
|
||||||
|
101
utils.py
101
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 <https://www.gnu.org/licenses/>.
|
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user