@digixx: Namenskonventionen werden aber schon hier und da erwähnt, insbesondere in der Python-Dokumentation im Tutorial, denn PEP8 ist ja wie alle PEPs Teil des Informationsmaterials das direkt von den Python-Entwicklern kommt.
Noch ein paar Anmerkungen zum `cl_nextion`-Modul:
Die `const()`-Geschichten gehören nicht in die Klasse. Das ist sehr verwirrend weil die damit entgegen der normalen Python-Semantik als Konstanten plötzlich überall im Modul verfügbar sind obwohl sie auf Modulebene gar nicht definiert sind.
Literale Zeichenketten sind nicht zum auskommentieren von Code gedacht. Für Kommentare ist ausschliesslich ``#`` da. Jeder vernünftige Editor lässt einen damit auch Blöcke von Code ein- und auskommentieren.
Die `nextion.__repr__()` hält sich nicht an die Konventionen. Entweder ist das etwas was man in den Quelltext kopieren könnte und das wieder ein gleiches Objekt erzeugen würde, oder es ist etwas das in ”spitze Klammern” eingefasst ist, und dort als erstes den Klassennamen enthält. Die vorhandene `__repr__()` gibt da etwas zurück das so aussieht als könnte man es so im Quelltext schreiben, aber die `nextion.__init__()` könnte an der Stelle mit der Baudrate als Argument überhaupt nichts anfangen.
`_c2t()` und `_c2n()` sind keine Methoden sondern einfach nur Funktionen die in die Klasse gesteckt wurden. Entweder sollte man die als Funktionen herausziehen oder entsprechend als `staticmethod` definieren, damit das klarer ist und sich keiner wundert ob das so sein soll.
Bei den beiden Funktionen könnte man die „list comprehension“ zu einem Generatorausdrück machen. Aber selbst das könnte man sich sparen, weil man hier `map()` verwenden kann.
`_c2n()` macht fast das gleiche wie `_c2t()`, das braucht man also nicht in zwei Funktionen schreiben, sondern kann die eine aus der anderen heraus aufrufen. Wobei `_c2n` nirgends verwendet wird‽ Soll das so?
Bei ``if (name == "sys0" or name == "sys1" or name == "sys2"):`` sind die Klammern überflüssig und das kann man besser mit ``in`` ausdrücken: ``if name in ["sys0", "sys1", "sys2"]``.
Bei `_send()` verstehe ich die Schleife nicht wirklich. Man kann doch einfach den Buffer kodieren und senden‽
Die `_elemendDB` und die dazugehörigen Methoden sind schräg. Warum ist das so eine komische Liste mit Wörterbüchern als Elementen die immer genau zwei Schlüssel/Wert-Paare enthalten? Warum wird `dict.get()` verwendet, wo man sich doch sicher sein kann, dass die Schlüssel "n" und "v" immer existieren?
Wenn man mit `add_element()` den gleichen Namen mehr als einmal hinzufügt, dann wird von `get_element()` nur der erste Eintrag berücksichtigt, von `set_element()` aber jeder, also ggf. auch mehr als ein Refresh gesendet. Warum ist `_elemendDB` nicht einfach ein Wörterbuch?
Bei `set_element()` sollte `refresh` entweder `False` oder `True` sein, nicht `None` oder `True`.
Update gibt entweder ein Tupel mit zwei 0en zurück oder ein Tupel mit zwei Zeichenketten. Das ist eine komische API.
Ich würde da auch testen ob der Zustand einen ungültigen Wert angenommen hat und entsprechend reagieren. Sonst macht die Methode ja einfach immer gar nichts.
Die Methode ist für meinen Geschmack auch ein bisschen lang und die macht ja auch zwei Dinge die man ganz gut trennen kann: den Zustand verwalten und empfangene Daten ausführen.
Eine Liste für die empfangenen Bytes zu verwenden ist vielleicht nicht die beste Lösung. `bytearray` wäre hier sicher der passendere Datentyp.
Ungetestet:
Code: Alles auswählen
"""
PAGE Main (0)
dimlow.val #0000
dimhigh.val #0004
dimdelay.val #0008
templow.val #0012
temphigh.val #0016
press3h.val #0020
TempHighEn.val #0024
TempLowEn.val #0028
StormEn.val #0032
----
Data format set_num, set_txt
Header Command Name Delimiter Value Tail
['@'] [set_num/txt] ['Name'] ['$'] ['1000'] ['#']
Data format get_num, get_txt
Header Command Name Tail
['@'] [get_num/txt] ['Name'] ['#']
Data format get_page_data
Header Command Page Part Tail
['@'] [get_page_data] [0..9-A..Z] [0..9-A..Z] ['#']
Data format action_set / action_reset
Header Command Name Tail
['@'] [action_set] ['Name'] ['#']
['@'] [action_reset] ['Name'] ['#']
"""
import time
import board
import busio
from micropython import const
GET_PAGE_DATA = const(0x70) # Zeichen p
SET_NUM = const(0x4E) # Zeichen N / Nextion sends value to store
GET_NUM = const(0x6E) # Zeichen n
SET_TXT = const(0x54) # Zeichen T
GET_TXT = const(0x74) # Zeichen t
ACTION_SET = const(0x42) # Zeichen B
ACTION_RESET = const(0x62) # Zeichen b
COM_STATE_IDLE = const(0)
COM_STATE_READING = const(1)
COM_STATE_EXECUTE = const(2)
COM_HEAD = const(0x40) # Zeichen @
COM_DELIMITER = "$"
COM_TAIL = const(0x23) # Zeichen #
class Nextion:
def __init__(self, uart):
self._uart = uart
self._com_state = COM_STATE_IDLE
self._com_data = bytearray()
self._element_db = {}
def __repr__(self):
return "<{0.__class__.__name__} baudrate={0._uart.baudrate!r}>".format(
self
)
def _send(self, buffer):
self._uart.write(buffer.encode())
self._uart.write(b"\xff\xff\xff")
def _send_value(self, name, value):
if isinstance(value, str):
self._send('{}.txt="{}"'.format(name, value))
else:
self._send(
"{}{}={}".format(
name,
"" if name in ["sys0", "sys1", "sys2"] else ".val",
value,
)
)
def add_element(self, name, value):
self._element_db[name] = value
def get_element(self, name):
return self._element_db.get(name)
def set_element(self, name, value, refresh=False):
if name in self._element_db:
self._element_db[name] = value
if refresh:
self._send_value(name, value)
def refresh_element(self, name):
self._send_value(name, self.get_element(name))
def _execute_request(self, page, part):
command = self._com_data[0]
if command == GET_PAGE_DATA:
page = chr(self._com_data[1])
part = "0"
if len(self._com_data) == 3:
part = chr(self._com_data[2])
elif command in [SET_TXT, SET_NUM]:
name, delimiter, value = (
self._com_data[1:].decode("latin-1").partition(COM_DELIMITER)
)
if not delimiter:
raise ValueError(
"delimiter {!r} not found in {!r}".format(
COM_DELIMITER, self._com_data
)
)
self.set_element(name, int(value) if command == SET_NUM else value)
elif command == GET_TXT:
name = self._com_data[1:].decode("latin-1")
self._send_value(name, self.get_element(name))
elif command == GET_NUM:
name = self._com_data[1:].decode("latin-1")
self._send_value(name, self.get_element(name))
elif command == ACTION_SET:
name = self._com_data[1:].decode("latin-1")
self.set_element(name, 1)
print("ACTION_SET:", name)
elif command == ACTION_RESET:
name = self._com_data[1:].decode("latin-1")
self.set_element(name, 0)
print("ACTION_RESET:", name)
else:
print("Unknown request:", self._com_data)
return page, part
def update(self):
page = part = "0"
while self._uart.in_waiting:
byte_value = self._uart.read(1)[0]
if self._com_state == COM_STATE_IDLE and byte_value == COM_HEAD:
self._com_data.clear()
self._com_state = COM_STATE_READING
elif self._com_state == COM_STATE_READING:
if byte_value == COM_TAIL:
self._com_state = COM_STATE_EXECUTE
else:
self._com_data.append(byte_value)
else:
assert False, "unexpeted state {!r}".format(self._com_state)
if self._com_state == COM_STATE_EXECUTE:
self._com_state = COM_STATE_IDLE
if len(self._com_data) > 1:
page, part = self._execute_request(page, part)
return page, part
def main():
current_time = time.monotonic()
uart = busio.UART(board.TX, board.RX, stop=1, baudrate=115200, timeout=0.1)
nextion = Nextion(uart)
nextion.add_element("cTemp", "24.5")
nextion.add_element("cPress", "985.2")
nextion.add_element("b3", 0)
nextion.add_element("GPSrec", 0)
while True:
if current_time + 0.5 < time.monotonic():
current_time = time.monotonic()
print(nextion.update())
nextion.set_element("cTemp", "28", True)
nextion.refresh_element("cPress")
nextion.set_element(
"GPSrec", 1 if nextion.get_element("b3") == 1 else 0, True
)
if __name__ == "__main__":
main()