Elektronisches Preisschild Sammelbestellung - Ansteuerung

Der chaotische Hauptfaden

Moderatoren: Heaterman, Finger, Sven, TDI, Marsupilami72, duese

duese
Beiträge: 6059
Registriert: So 11. Aug 2013, 17:56

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von duese »

Hightech hat geschrieben: Mo 24. Okt 2022, 06:44 Die Schrifart ist Geschmacksache
Diese einfache Form zb geht recht gut:
https://www.1001fonts.com/advanced-pixel-7-font.html
Gerade Linien in 90° gehen am besten.

Oder eine einfache ttf Font
https://www.dafont.com/bitstream-vera-mono.font
Danke!

Noch eine Frage: Wo hast Du das converts.py her? Ich hab eine Konvertierung ins PIL-Format gefunden, mit den konvertierten Datein funktioniert dann aber

Code: Alles auswählen

length_name=font.getbbox(Name)
nicht.
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Ja, muss ich raussuchen.
getbbox funktioniert da nicht, müsste mit getbtext oder so gehen, schau ich mal nach.
duese
Beiträge: 6059
Registriert: So 11. Aug 2013, 17:56

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von duese »

Ok, passt. Die andere Funktion hab ich auch gefunden, klappt also (dann aber nicht mehr mit TTF).

Ich dachte nur, da gibts es noch nen Trick, nachdem in Deinem Schildergenerator PIL mit getbbox kombiniert ist (was aber offenbar nicht geht).
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Eine aktuelle Station.py mit Farbe und Discovery.
Der Discovery-Modus ändert nichts an der Verbingung oder am Paaring. Es wird nur dafür gesorgt, das jedes neue Display eine Grafik bekommt.
Zusätzlich hab ich den Labermodus --l eingeführt. Ohne --l kommen nur die wesentlichen Infos.
Ist übersichtlicher

Code: Alles auswählen

#!/usr/bin/python3
import timaccop
import bmp2grays
from Cryptodome.Cipher import AES
from collections import namedtuple
import struct
import os, sys
import logging
from PIL import Image, ImageDraw, ImageFont
import time

parameter=sys.argv
discover=False
laber=False

if "--d" in parameter:
    discover=True
    print ("DISCOVER-MODE")

if "--l" in parameter:
    discover=True
    print ("Labermodus")

Display_Size_x=296
Display_Size_y=128

masterkey = bytearray.fromhex("D306D9348E29E5E358BF2934812002C1")

PORT = os.environ.get("EPS_PORT", default="/dev/ttyACM0")
EXTENDED_ADDRESS = [ int(addr.strip(), 16) for addr in os.environ.get("EPS_EXTENDED_ADDRESS", default="0x00, 0x12, 0x4B, 0x00, 0x14, 0xD9, 0x49, 0x35").split(",") ]
PANID = [ int(panid.strip(), 16) for panid in os.environ.get("EPS_PANID", default="0x47, 0x44").split(",") ]
CHANNEL = int(os.environ.get("EPS_CHANNEL", default="11"))
IMAGE_DIR = os.environ.get("EPS_IMAGE_DIR", default="./")
IMAGE_WORKDIR = os.environ.get("EPS_IMAGE_WORKDIR", default="/tmp/")

CHECKIN_DELAY = int(os.environ.get("EPS_CHECKIN_DELAY", default="900000")) # 900s
RETRY_DELAY = int(os.environ.get("EPS_RETRY_DELAY", default="1000")) # 1s
FAILED_CHECKINS_TILL_BLANK = int(os.environ.get("EPS_FAILED_CHECKINS_TILL_BLANK", default="2"))
FAILED_CHECKINS_TILL_DISSOC = int(os.environ.get("EPS_FAILED_CHECKINS_TILL_DISSOC", default="2"))

PKT_ASSOC_REQ                   = (0xF0)
PKT_ASSOC_RESP                  = (0xF1)
PKT_CHECKIN                             = (0xF2)
PKT_CHECKOUT                    = (0xF3)
PKT_CHUNK_REQ                   = (0xF4)
PKT_CHUNK_RESP                  = (0xF5)

VERSION_SIGNIFICANT_MASK                                = (0x0000ffffffffffff)

HW_TYPE_42_INCH_SAMSUNG                                 = (1)
HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST    = (0xEFF8)
HW_TYPE_74_INCH_DISPDATA                                = (2)
HW_TYPE_74_INCH_DISPDATA_FRAME_MODE             = (3)
HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST   = (0x008b)
HW_TYPE_ZBD_EPOP50                                              = (4)
HW_TYPE_ZBD_EPOP50_ROM_VER_OFST                 = (0x008b)
HW_TYPE_ZBD_EPOP900                                             = (5)
HW_TYPE_ZBD_EPOP900_ROM_VER_OFST                = (0x008b)
HW_TYPE_29_INCH_DISPDATA                                = (6)
HW_TYPE_29_INCH_DISPDATA_FRAME_MODE             = (7)
HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST   = (0x008b)
HW_TYPE_29_INCH_ZBS_026                                 = (8)
HW_TYPE_29_INCH_ZBS_026_FRAME_MODE              = (9)
HW_TYPE_29_INCH_ZBS_025                                 = (10)
HW_TYPE_29_INCH_ZBS_025_FRAME_MODE              = (11)
HW_TYPE_154_INCH_ZBS_033                                = (18)
HW_TYPE_154_INCH_ZBS_033_FRAME_MODE             = (19)
HW_TYPE_42_INCH_ZBS_026                                 = (28)
HW_TYPE_42_INCH_ZBS_026_FRAME_MODE              = (29)
HW_TYPE_29_INCH_ZBS_ROM_VER_OFST                = (0x008b)



TagInfo = namedtuple('TagInfo', """
protoVer,
swVer,
hwType,
batteryMv,
rfu1,
screenPixWidth,
screenPixHeight,
screenMmWidth,
screenMmHeight,
compressionsSupported,
maxWaitMsec,
screenType,
rfu
""")

AssocInfo = namedtuple('AssocInfo', """
checkinDelay,
retryDelay,
failedCheckinsTillBlank,
failedCheckinsTillDissoc,
newKey,
rfu
""")

CheckinInfo = namedtuple('CheckinInfo', """
swVer,
hwType,
batteryMv,
lastPacketLQI,
lastPacketRSSI,
temperature,
rfu,
""")

PendingInfo = namedtuple('PendingInfo', """
imgUpdateVer,
imgUpdateSize,
osUpdateVer,
osUpdateSize,
nextCheckinDelay,
rfu
""")

ChunkReqInfo = namedtuple('ChunkReqInfo', """
versionRequested,
offset,
len,
osUpdatePlz,
rfu,
""")

ChunkInfo = namedtuple('ChunkInfo', """
offset,
osUpdatePlz,
rfu,
""")

logging.basicConfig(format='%(asctime)s %(message)s')
logger = logging.getLogger(__name__)

dsn = 0

def print(*args):
    msg = ""
    for arg in args:
        msg += str(arg) + " "
    logger.warning(msg)

def decrypt(hdr, enc, tag, nonce):
    cipher = AES.new(masterkey, AES.MODE_CCM, nonce, mac_len=4)
    cipher.update(hdr)
    plaintext = cipher.decrypt(enc)
    if laber:
        print("rcvd_packet:", plaintext.hex())
        print("rcvhdr:", hdr.hex())
    try:
        cipher.verify(tag)
        return plaintext
    except:
        return None

def send_data(dst, data):
    global dsn
    dsn += 1
    if dsn > 255:
        dsn = 0
    hdr = bytearray.fromhex("41cc")
    hdr.append(dsn)
    hdr.extend(PANID)
    hdr.extend(reversed(dst))
    hdr.extend(EXTENDED_ADDRESS)
    if laber:
        print("hdr:", hdr.hex())

    cntr = int(time.time())
    cntrb = struct.pack('<L', cntr)

    nonce = bytearray(cntrb)
    nonce.extend(EXTENDED_ADDRESS)
    nonce.append(0)
    if laber:
        print("nonce:", nonce.hex())

    cipher = AES.new(masterkey, AES.MODE_CCM, nonce, mac_len=4)
    cipher.update(hdr)
    ciphertext, tag = cipher.encrypt_and_digest(data)

    out = ciphertext+tag+cntrb
    timaccop.mac_data_req(dst, PANID, 12, dsn, out)

def process_assoc(pkt, data):
    ti = TagInfo._make(struct.unpack('<BQHHBHHHHHHB11s',data))
    print(ti)

    ai = AssocInfo(
            checkinDelay=CHECKIN_DELAY,
            retryDelay=RETRY_DELAY,
            failedCheckinsTillBlank=FAILED_CHECKINS_TILL_BLANK,
            failedCheckinsTillDissoc=FAILED_CHECKINS_TILL_DISSOC,
            newKey=masterkey,
            rfu=bytearray(8*[0])
    )
    print(ai)
    ai_pkt = bytearray([ PKT_ASSOC_RESP ]) + bytearray(struct.pack('<LLHH16s8s', *ai))

    send_data(pkt['src_add'], ai_pkt)

def prepare_image(client):
    is_bmp = False
    base_name = os.path.join(IMAGE_DIR, bytes(client).hex())
    filename = base_name + ".png"
    print("Reading image file:" + base_name + ".bmp/.png")

    if os.path.isfile(filename):
        print("Using "+filename+" file")
    elif os.path.isfile(base_name + ".bmp"):
        is_bmp = True
        filename = base_name + ".bmp"
        print("Using .bmp file")
    elif discover == True:
        generate_discover_image(filename)
        print("NEW DEVICE")
        print(filename)
    else:
        print("Device not found")
        return(0,0)


    modification_time = os.path.getmtime(filename)
    creation_time = os.path.getctime(filename)
    imgVer = int(modification_time)<<32|int(creation_time) # This uses the mofidication time of the image to look for the newest one

    file_conv = os.path.join(IMAGE_WORKDIR, bytes(client).hex().upper() + "_" + str(imgVer) + ".bmp") # also use the MAC in case 1 images are created within 1 second

    if not os.path.isfile(file_conv):
        if is_bmp:
            bmp2grays.convertImage(1, "1bppR", filename, file_conv)
        else:
            Image.open(filename).convert("RGB").save(os.path.join(IMAGE_WORKDIR, "tempConvert.bmp"))
            bmp2grays.convertImage(1, "1bppR", os.path.join(IMAGE_WORKDIR, "tempConvert.bmp"), file_conv)

    imgLen = os.path.getsize(file_conv)

    return (imgVer, imgLen)

def get_firmware_offset(hwType):
    if hwType == HW_TYPE_42_INCH_SAMSUNG:
        return HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST
    if hwType == HW_TYPE_74_INCH_DISPDATA:
        return HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST
    if hwType == HW_TYPE_74_INCH_DISPDATA_FRAME_MODE:
        return HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST
    if hwType == HW_TYPE_29_INCH_DISPDATA:
        return HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST
    if hwType == HW_TYPE_29_INCH_DISPDATA_FRAME_MODE:
        return HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST
    if hwType == HW_TYPE_ZBD_EPOP50:
        return HW_TYPE_ZBD_EPOP50_ROM_VER_OFST
    if hwType == HW_TYPE_ZBD_EPOP900:
        return HW_TYPE_ZBD_EPOP900_ROM_VER_OFST
    if hwType == HW_TYPE_29_INCH_ZBS_026 or hwType == HW_TYPE_29_INCH_ZBS_026_FRAME_MODE or hwType == HW_TYPE_29_INCH_ZBS_025 or hwType == HW_TYPE_29_INCH_ZBS_025_FRAME_MOD
E or hwType == HW_TYPE_154_INCH_ZBS_033 or hwType == HW_TYPE_154_INCH_ZBS_033_FRAME_MODE or hwType == HW_TYPE_42_INCH_ZBS_026 or hwType == HW_TYPE_42_INCH_ZBS_026_FRAME_MOD
E:
        return HW_TYPE_29_INCH_ZBS_ROM_VER_OFST

def prepare_firmware(hwType):
    filename = 'UPDT{0:0{1}X}.BIN'.format(hwType,4)
    if laber:
        print("Reading firmware file:", filename)

    if not os.path.isfile(filename):
        if laber:
            print("No Firmware file available")
        return (0,0)

    f = open(filename,mode='rb')
    f.seek(get_firmware_offset(hwType))
    firmwareVersionData = f.read(8)
    f.close()

    osVer = int.from_bytes(firmwareVersionData, "little") & VERSION_SIGNIFICANT_MASK | hwType << 48
    osLen = os.path.getsize(filename)

    return (osVer, osLen)

def get_image_data(imgVer, offset, length):
    filename = os.path.join(IMAGE_WORKDIR, imgVer + ".bmp")
    if laber:
        print("Reading image file:", filename)

    f = open(filename,mode='rb')
    f.seek(offset)
    image_data = f.read(length)
    f.close()

    return image_data

def get_fw_data(hwType, offset, length):
    filename = 'UPDT{0:0{1}X}.BIN'.format(hwType,4)
    if laber:
        print("Reading firmware file:", filename)

    f = open(filename,mode='rb')
    f.seek(offset)
    fw_data = f.read(length)
    f.close()

    return fw_data

def process_checkin(pkt, data):
    ci = CheckinInfo._make(struct.unpack('<QHHBBB6s',data))

    print(ci)

    imgVer = 0
    imgLen = 0

    try:
        imgVer, imgLen = prepare_image(pkt['src_add'])
    except Exception as e :
        print("Unable to prepare image data for client", pkt['src_add'])
        print(e)

    osVer = 0
    osLen = 0

    try:
        osVer, osLen = prepare_firmware(ci.hwType)
    except Exception as e :
        print("Unable to prepare firmware data for client", pkt['src_add'])
        print(e)

    pi = PendingInfo(
        imgUpdateVer = imgVer,
        imgUpdateSize = imgLen,
        osUpdateVer = osVer,
        osUpdateSize = osLen,
        nextCheckinDelay = 0,
        rfu=bytearray(4*[0])
    )
    print(pi)

    pi_pkt = bytearray([ PKT_CHECKOUT ]) + bytearray(struct.pack('<QLQLL4s', *pi))

    send_data(pkt['src_add'], pi_pkt)

def process_download(pkt, data):
    cri = ChunkReqInfo._make(struct.unpack('<QLBB6s',data))
    if laber:
        print(cri)

    ci = ChunkInfo(
        offset = cri.offset,
        osUpdatePlz = cri.osUpdatePlz,
        rfu = 0,
    )
    if laber:
        print(ci)

    try:
        if ci.osUpdatePlz:
            fdata = get_fw_data(cri.versionRequested>>48, cri.offset, cri.len)
        else:
            fdata = get_image_data(bytes(pkt['src_add']).hex().upper() + "_" + str(cri.versionRequested), cri.offset, cri.len)
    except Exception as e :
        print("Unable to get data for version", cri.versionRequested)
        print(e)
        return

    outpkt = bytearray([ PKT_CHUNK_RESP ]) + bytearray(struct.pack('<LBB', *ci)) + bytearray(fdata)
    if laber:
        print("sending chunk", len(outpkt), outpkt[:10].hex() ,"...")

    send_data(pkt['src_add'], outpkt)

def generate_discover_image(filename):
    muster=Image.new("RGB",(296,128),color=(255,255,255))
    named_tuple = time.localtime() # get struct_time
    time_string = time.strftime("%m/%d/%Y, %H:%M:%S", named_tuple)
    im = ImageDraw.Draw(muster)
    font = ImageFont.load_default()
    length_name=font.getbbox("Aktivierung")
    im.text((((Display_Size_x/2)-length_name[2]/2),5),"Aktivierung" ,fill=(0, 0, 0),font=font)
    length_name=font.getbbox(filename)
    im.text((((Display_Size_x/2)-length_name[2]/2),45),filename ,fill=(0, 0, 0),font=font)
    box=font.getbbox(time_string)

    tboy1=90
    tboy2=box[3]+tboy1
    tbox1=((Display_Size_x-box[2])/2)
    tbox2=((Display_Size_x-box[2])/2)+box[2]

    im.rectangle((tbox1-6,tboy1-2,tbox2+6,tboy2+2),fill=(255,0,0),outline=(0,0,0),width=2)
    im.text((tbox1,tboy1),time_string,fill=(255,255,255),font=font)
    muster=muster.rotate(angle=90,expand="true")
    muster.save(filename)


def generate_pkt_header(pkt): #hacky- timaccop cannot provide header data
    bcast = True
    if pkt['dst_add'] == b'\xff\xff': #broadcast assoc
        hdr = bytearray.fromhex("01c8")
    else:
        hdr = bytearray.fromhex("41cc")
        bcast = False
    hdr.append(pkt['dsn'])
    hdr.extend(pkt['dst_pan_id'])
    hdr.extend(pkt['dst_add'])
    if bcast:
        hdr.extend(pkt['src_pan_id'])
    hdr.extend(reversed(pkt['src_add']))

    return hdr


def process_pkt(pkt):
    hdr = generate_pkt_header(pkt)
    if len(pkt['data']) < 10:
        print("Received a too short paket")
        print("data", pkt['data'].hex())
        return

    nonce = bytearray(pkt['data'][-4:])
    nonce.extend(reversed(pkt['src_add']))
    nonce.extend(b'\x00')

    tag = pkt['data'][-8:-4]

    ciphertext = pkt['data'][:-8]

    plaintext = decrypt(hdr, ciphertext, tag, nonce)
    if not plaintext:
        print("data", pkt['data'].hex())
        print("hdr", hdr.hex())
        print("ciph", ciphertext.hex())
        print("nonce", nonce.hex())
        print("tag", tag.hex())
        print("packet is NOT authentic")
        return

    typ = plaintext[0]

    if typ == PKT_ASSOC_REQ:
        print("Got assoc request")
        process_assoc(pkt, plaintext[1:])
    elif typ == PKT_CHECKIN:
        print("Got checkin request")
        process_checkin(pkt, plaintext[1:])
    elif typ == PKT_CHUNK_REQ:
        if laber:
            print("Got chunk request")
        process_download(pkt, plaintext[1:])
    else:
        print("Unknown request", typ)

timaccop.init(PORT, PANID, CHANNEL, EXTENDED_ADDRESS, process_pkt)
print("Station started")

timaccop.run()

Hier einmal die convert.py
Die umgewandelten .bfd Dateien müssen aber mit im Verzeichniss liegen.

Code: Alles auswählen

#!/usr/bin/python3
#
# The Python Imaging Library
# $Id$
#
# PIL raster font compiler
#
# history:
# 1997-08-25 fl   created
# 2002-03-10 fl   use "from PIL import"
#

from __future__ import print_function

import glob
import sys

# drivers
from PIL import BdfFontFile
from PIL import PcfFontFile

VERSION = "0.4"

if len(sys.argv) <= 1:
    print("PILFONT", VERSION, "-- PIL font compiler.")
    print()
    print("Usage: pilfont fontfiles...")
    print()
    print("Convert given font files to the PIL raster font format.")
    print("This version of pilfont supports X BDF and PCF fonts.")
    sys.exit(1)

files = []
for f in sys.argv[1:]:
    files = files + glob.glob(f)

for f in files:

    print(f + "...", end=' ')

    try:

        fp = open(f, "rb")

        try:
            p = PcfFontFile.PcfFontFile(fp)
        except SyntaxError:
            fp.seek(0)
            p = BdfFontFile.BdfFontFile(fp)

        p.save(f)

    except (SyntaxError, IOError):
        print("failed")

    else:
        print("OK")
Benutzeravatar
Finger
Administrator
Beiträge: 7392
Registriert: Di 12. Jun 2012, 20:16
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Finger »

Ich esle mich gerade das erste mal mit Python ab.

station.py erzeugt mir diese Ausgabe:
Traceback (most recent call last):
File "C:\Users\Fingeria\Desktop\epaper-station-master\epaper-station-master\station.py", line 3, in <module>
from Cryptodome.Cipher import AES
ModuleNotFoundError: No module named 'Cryptodome'
In der IDE findet sich hingegen das hier:
import Crypto
print(Crypto.__version__)
3.15.0
Soweit so gut. Und nun? Sollte ich erwähnen, das ich zum Testen Windowns nutze? Was mach ich denn da? Ich hab mich an das hier gehalten: https://www.youtube.com/watch?v=qRuMf6yXXyA "How to Install Pycryptodome (Crypto) in Python 3.10"
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Hier steht die ganze Geschichte über die Schilder.
Wie, Was Warum und überhaupt. Alle schmutzigen Details und tricks.
https://dmitry.gr/index.php?r=05.Projec ... ice%20Tags
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Ah, das muss man wissen:
Man muss die aktuellen Versionen von dem Python
Zeug installieren
Auch die Pillow Sachen müssen aktuell sein.

duese hat geschrieben: Mo 24. Okt 2022, 09:46 Ok, passt. Die andere Funktion hab ich auch gefunden, klappt also (dann aber nicht mehr mit TTF).

Ich dachte nur, da gibts es noch nen Trick, nachdem in Deinem Schildergenerator PIL mit getbbox kombiniert ist (was aber offenbar nicht geht).
Benutzeravatar
Zabex
Beiträge: 632
Registriert: Di 2. Jul 2013, 08:45
Wohnort: Aldenhoven
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Zabex »

Hm, ich bekomme den Stick nicht geflashed. Habe als Programmieradapter zuerst einen Arduino-Nano genommen (mit CCloader Firmware). Anschluss über 4 Kabel (GND, DD,DS,RST), Spannungsversorgung durch einstecken des Sticks in USB-Buchse.
Nachdem das einfach nicht klappen wollte, habe ich meinen ASIX PRESTO Programmer mit UP.exe Software rausgekramt und festgestellt, dass der den CC2531F256 kennt. 4 Drähte + Vdd-Sense (Pin2). Spannungsversorgung wie gehabt über USB. Vdd-Sense wird vom Presto als "größer 2V" gemessen. Fuse Bits lassen sich auslesen. Erase geht auch. Wenn ich flashen will, wird gemeckert, dass die ID 1111 1111 1111 1.." ist. Flashen läuft durch, verify ergibt jedoch, dass alle Bytes "FF" sind - also nix geflashed.

Was kann es sein:
- Muss man vielleicht eine der beiden Tasten auf dem Stick drücken?
- Kann der Chip nur 3.3V und der Arduino hat mit seinen 5V auf DD/DS/RST den Chip gehimmelt?
- Braucht der Chip vielleicht 5V an Pin 2?
- vielleicht habe ich zwischen 2 Leitungen versehentlich einen Kurzschluss fabriziert, so dass die bidirektionale Leitung Schaden genomen hat.

Ich nehme an, inzwischen ist der Stick defekt. Habe 2 neue bestellt und frage mich, was ich dann wohl besser zum flashen nehme. Empfehlungen?


Gruß,
Zabex
Kerko
Beiträge: 336
Registriert: Mo 12. Aug 2013, 22:18

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Kerko »

Arduino Nano hat bei mir auch nicht funktioniert. Habe dann ein ESP2866 genommen und bin so wie im Video vorgegangen. Musste aber zwei mal flashen bis es geklappt hat. Stromversorgung habe ich über die 3,3V vom ESP Board gemacht.
Gary
Beiträge: 4852
Registriert: Mo 12. Aug 2013, 01:02

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Gary »

Von Hightech auf Seite 1

Info:
Nicht mit einem 5V Arduino flashen, damit killt man den Controller.
Wenn, dann die VCC auf 3,3V und die Datenleitungen mit einem Spannungsteiler an den Output Pins.
Besser einen ESP32 3,3V verwenden.
Benutzeravatar
ProgBernie
Beiträge: 586
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von ProgBernie »

Ich hab's mir diesmal einfach gemacht und gleich einen TI CC-Debugger-clone geholt. Von hier:

https://www.ebay.de/itm/372918614163

Hat mit der TI-Software ohne Probleme funktioniert.
caprivi
Beiträge: 585
Registriert: Mi 9. Mär 2016, 14:44
Wohnort: Am ehemaligen Schorbaer Berg.

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von caprivi »

Edith meint, hier stehen zu viele naive Fragen, und schlägt vor, gleich weiterzuscrollen.

Ich versuche gerade zu verstehen, was ich genau tun muss, um aus den Schildern eine billigen stromsparenden Arduino-ansteuerbaren Display zu machen. Die Doku bei Github scheint mir ein Bisschen für Leute, die schon tief in der Materie drin stecken und jede Menge implizit vorausgesetzten Hintergrund verstehen. Ich fremdele allgemein ein Bisschen mit dem Thema Programmierung, bin aber tapfer und will das hinbekommen. Ich versuch das also mal rückwärts aufzudröseln:

Als Display am Arduino (um in irgendeinem Sketch Grafiken zu malen oder Text zu schreiben) kann man offenbar diesen Sketch hier verwenden.
https://github.com/atc1441/E-Paper_Pric ... y_HINK.ino
Diese Quelle hier (https://github.com/atc1441/E-Paper_Pricetags) sagt zu unseren Preisschildern "...or use the "EinkSD_1.54_2.9_4.2_Standalone_Library_HINK" Arduino code to drive them." Das heisst, mit dem *.ino-Sketch müsste das gehen.

Frage: Wie/wo ist eigentlich der Font definiert, den ich hier bei "display.println("Was geht so");" angezeigt bekäme?

Noch eine Frage: Wenn ich den Arduino wie oben gleich auch benutzen möchte, um über die UART Text entgegenzunehmen und anzuzeigen (á la "serial.read() / display.println()"), wäre dann der gleiche Sketch geeignet? Dort gibt es ja das Statement "Serial.begin(115200);" für das ich ansonsten keine Verwendung sehe.

(Nebenfrage: Unter https://github.com/atc1441/ZBS_Flasher/ ... m-firmware findet man verschiedene Sketche, die mit UART... beginnen, aber ich vermute, das ist nicht für Text, sondern man muss dann dem Arduino, auf dem der Sketch läuft, ein Bild pixelweise über die Schnittstelle verfüttern? Ich sehe allerdings da kein Codebeispiel, wie man die Befüllung von aussen anschliessend praktisch handhaben würde. Hab ich das übersehen?)

Und schliesslich mal angenommen, ich müsste eine neue Firmware auf das Schild schreiben, dann ...
  • hätte ich noch keine Idee, welche Firmware ich bräuchte, um den Anzeige-Sketch von weiter oben nutzen zu können. Ich nehme an, dass ich das Preisschild vorher mit einer Firmware ausstatten muss, die die Benutzung des Skripts erlaubt ... korrekt?

Für die Programmierung des Preisschildes mit einer neuen Firmware müsste ich ein Programmiergerät (ein ESP32 oder ein Arduino nano) entsprechend dieser Zeichnung hier... https://github.com/atc1441/ZBS_Flasher/ ... pinout.jpg an das Display anschliessen und das *.bin-File auf das Preisschild laden.
Ich würde dabei gern den ESP32 und platform.io zum Programmieren des Programmieradapters vermeiden und in der Arduino-Welt bleiben, gibt es da einen Weg?
Ich sehe in der Grafik oben mehr Pins als die mir geläufigen ISP-Adapter haben. Werden hier tatsächlich mehr Anschlüsse gebraucht? Denn sonst müsste doch eigentlich jedes ISP-Programmiergerät funktionieren; es geht doch "nur" um die Übertragung eines *.bin-Files, oder bin ich hier falsch?
Zuletzt geändert von caprivi am Do 27. Okt 2022, 18:43, insgesamt 1-mal geändert.
caprivi
Beiträge: 585
Registriert: Mi 9. Mär 2016, 14:44
Wohnort: Am ehemaligen Schorbaer Berg.

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von caprivi »

Wuste ich's doch, hab noch was vergessen: Die Anschlüsse im Display-Sketch...

Code: Alles auswählen

// CLK 18
// MOSI 23
#define OLED_DC    17
#define OLED_CS    5
#define OLED_RESET 16
#define OLED_BUSY 4
EinkDisplay display(SPI, OLED_DC, OLED_RESET, OLED_CS, OLED_BUSY);
...finden sich nicht in dem Bild wieder, die den Anschluss des Programmieradapters beschreiben. Und andere Anschlüsse gibt es auf der Platine nicht.

Werden die Programmieranschlüsse nach dem Laden der neuen Firmware umgewidmet, oder müsste man dann direkt an den Flachbandverbinder ran?

Ich glaube, MSG hatte ganz zu Anfang des Fadens schon mal die gleiche Frage gestellt.
Benutzeravatar
Finger
Administrator
Beiträge: 7392
Registriert: Di 12. Jun 2012, 20:16
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Finger »

Station.py läuft jetzt auch, macht mir aber eine Fehlermeldung:

2022-10-25 20:57:08,982 Got checkin request
2022-10-25 20:57:08,983 CheckinInfo(swVer=1172526071808, hwType=8, batteryMv=2450, lastPacketLQI=83, lastPacketRSSI=203, temperature=150, rfu=b'\x00\x00\x00\x00\x00\x00')
2022-10-25 20:57:08,986 Reading image file:./000002855d4e3b14.bmp/.png
2022-10-25 20:57:08,988 Using .png file
2022-10-25 20:57:08,993 Unable to prepare image data for client [0, 0, 2, 133, 93, 78, 59, 20]
2022-10-25 20:57:08,994 [Errno 2] No such file or directory: '/tmp/tempConvert.bmp'
2022-10-25 20:57:08,996 Reading firmware file: UPDT0008.BIN
2022-10-25 20:57:08,997 No Firmware file available
2022-10-25 20:57:08,999 PendingInfo(imgUpdateVer=0, imgUpdateSize=0, osUpdateVer=0, osUpdateSize=0, nextCheckinDelay=0, rfu=bytearray(b'\x00\x00\x00\x00'))

Groß und Kleinschreibung beim Dateinamnen hat keinen Einfluß auf diese Meldung. Das Verzeichnis tmp\ existiert. Auflösung des Bildes sind 128x296px, senkrecht. Irfanview und Paint können die Datei öffnen. PNG oder BMP erzeugen das gleiche Problem. Was sagt mir denn das? Oder: an welcher Schraube muss ich denn da drehen?
manawyrm
Beiträge: 133
Registriert: Sa 3. Okt 2015, 23:09
Wohnort: Kiel
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von manawyrm »

Das Verzeichnis tmp\ existiert
'/tmp/tempConvert.bmp'
/tmp ist ein temporärer Ordner (und häufig eine RAM-Disk) auf Unix-ähnlichen Systemen. Pfade, die mit / anfangen sind absolute Pfadangaben.
Wenn du da zufällig gerade ein Windows oder ähnliches hast, wird der mit dem Pfad so gar nichts anfangen können und dann nie was sinnvolles machen.

Code: Alles auswählen

IMAGE_WORKDIR = os.environ.get("EPS_IMAGE_WORKDIR", default="/tmp/")
Ich würde also entweder als Umgebungsvariable EPS_IMAGE_WORKDIR oder einfach direkt im Code einen absoluten Pfad zu einem Ordner mit Schreibrechten übergeben und es so versuchen.

Der Code macht aber auch noch einige andere Sachen, wie die Pfadzusammensetzung und Serial-Port Geschichten, wo unter Windows evtl. noch das ein oder andere knirschen könnte.
/dev/ttyACM0 musst du dann vermutlich auch noch durch deinen COM* ersetzen.
Benutzeravatar
Finger
Administrator
Beiträge: 7392
Registriert: Di 12. Jun 2012, 20:16
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Finger »

Letztes hatte ich schon angepasst, die Tags sprechen ja auch mit dem Ding. Ich hab jetzt mal stumpf im Root \tmp\ angelegt. Mal schauen was das macht. Siehtse, das tuts tatsächlich. Danke für den Tip!
caprivi
Beiträge: 585
Registriert: Mi 9. Mär 2016, 14:44
Wohnort: Am ehemaligen Schorbaer Berg.

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von caprivi »

Ich hatte weiter oben einen Sack Fragen gestellt, weil ich der Annahme war, dass man die e-Ink-Displays recht unkompliziert auch als Ausgabemedium für kleinere Mikrocontrollerprojekte benutzen könnte.
Ich habe mich in den letzten Tagen etwas dazu belesen, und komme zum Schluss, dass das leider nicht der Fall ist.

Eine einfache Ansteuerung von e-Ink-Displays, z.B. als Low-Cost-Display-Alternative an einem Arduino ist aus zwei Gründen schwierig:

1. Man braucht genau bemessene Ansteuerungskurven für die e-Ink-Partikel mit positivem und negativem Vorzeichen, die i.d.R. auf die jeweiligen Displays und ihre Anatomie (schwarz/weiss oder dreifarbig) abgestimmt sind, weil die Physik hinter der gezielten Bewegung dieser z.T. unterschiedlich grossen, elektrisch geladenen Kullern im Ölbad recht tückisch ist. Eine sehr unterhaltsame Erklärung findet man hier (es lohnt sich aber oben anzufangen, um den Teil nicht zu verpassen, der sich mit der Informations- und Produktpolitik eines bestimmten Controllerherstellers beschäftigt. Ich hab mich sehr gekugelt. Jedenfalls - diese "waveforms" muss man kennen, sonst sieht man keine ordentlichen Bilder oder beschädigt im Extremfall sogar die Displays. Es gibt Bibliotheken, die diesen Teil übernehmen, zum Beispiel bei Adafruit oder - abgeleitet davon - bei Aaron Christophel.

2. Man hat den anzuzeigenden Pixeldatensatz idealerweise im RAM vorliegen. Viele Mikrocontroller haben aber nicht genug RAM, was bedeutet, dass man jedes anzuzeigende Bild zunächst im Flash speichern müsste, was die Lebensdauer der Controller beeinträchtigt. Das heisst, eine Lösung mit Raspi mag funktionieren, eine mit Arduino Uno vielleicht auch noch, eine Lösung mit Arduino Pro Mini wird nicht funktionieren. Und dieser Punkt ist am Ende das KO-Kriterium: Die Displays sind vom Datenaufwand her nicht mehr mit so was Kleinem wie ATTiny oder ATMega zu beherrschen.

Selbst wenn man diese Nachteile in Kauf nimmt, muss man die e-Ink-Displays an Adapterplatinen/Breakout-Boards hängen, die es z.B. bei Waveshare oder Adafuit gibt. Sie stellen die Spannungsquellen für die Ansteuerung und z.T. RAM zur Zwischenspeicherung bereit. Bei Waveshare gibt es ausserdem eine recht informative Seite zu Anschluss- und Ansteuerfragen; diese Angaben wird man z.B. brauchen, um die oben genannten Bibliotheken zur Ansteuerung zu nutzen. Schaut man sich aber die Preise dieser Adapterboards an, summiert sich alles recht schnell, und der ursprüngliche Vorteil, für zweifünfzig gewaschen und gekämmt zu sein ist dahin, und man kann auch gleich den "Umweg" über die jetzt hier im Faden etablierte Lösung mit Zigbee-Stick und Python-Skript gehen.
Benutzeravatar
Andreas_P
Beiträge: 1400
Registriert: Mo 12. Aug 2013, 11:35
Wohnort: Lohr am Main
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Andreas_P »

Letzte Woche am Freitag kam ein Päckchen mit einen vertrauten Klebeband von der Post.
Wie es zu erwarten war waren das die Preisschilder, danke Hightech dafür.
Nach dem dann unter der Woche Zigbee USB Stick, Adapterplatine und Pogo Pins eingetroffen sind, konnte ich am Mittwoch anfangen
die passende FW auf dem USB Stick zu übertragen. Mit Windows wolte das bei mir überhaupt nicht funktionieren.
Raspberry Pi und Linux lösten das Problem in kurzer Zeit. Leider hatte ich zu kurze Pogo Pins bestellt, also habe ich längere bestellt.

Zum Flashen der Preisschilder möchte ich mir einen Stecker drucken den ich auf die Kontakteinflächen der Preisschilder drauf halte,
dazu fehlt mir die Info in welchen Abstand die einzelnen Kontaktflächen zu einander haben. Hat dazu jemand Informationen ?
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Man kann die Displays ohne weiteres ohne Adapter an einen ESP32 hängen, wenn man die Displayplatine von dem Controller befreit.
Die für das Display benötigten Spannungen werden von den Bauteilen auf der Platine erzeugt.
Dafür gibt es eine eigene Bibliothek.
Benutzeravatar
Bastelbruder
Beiträge: 11483
Registriert: Mi 14. Aug 2013, 18:28

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Bastelbruder »

Es muß nicht unbedingt die ganze Bitmap im Ram liegen, Kompressionsalgorithmen wie RLC (Lauflängencodierung), speziell dynamisch angepaßter Code kann da durchaus noch etwas sparen.

Wenn man 1 byte in 2 bit Farbe und 6 bit Länge aufteilt, lassen sich bis zu 64 Pixel in einem byte verstecken. Und da gibt es sicher noch deutlich effektivere Varianten.
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Man kann einen EPS32 direkt dran hängen.
https://github.com/atc1441/E-Paper_Pric ... brary_HINK
Dazu habe ich den Controller herunter gelötet und die RFID-Antenne abgeschnitten.

Zu Beachten ist :
Es werden die Leitungen vom Display direkt an den ESP gelötet.
Die Signale SCL und SDA kommen am ESP an die Anschlüsse
SCL = SCK(4) und SDA = MOSI (6)
Die anderen Signale sind egal wo die dran kommen.

Pin 8 kommt schlicht an Masse.

Bild
https://cdn.sparkfun.com/assets/1/c/5/9 ... x128-n.pdf


#define OLED_DC 8
#define OLED_CS 5
#define OLED_RESET 10
#define OLED_BUSY 1
EinkDisplay display(SPI, OLED_DC, OLED_RESET, OLED_CS, OLED_BUSY);

Ich hab im Moment nur ein Problem mit dem CS Singal, das Display tut nur was, wenn ich einen 1M Widertstand am CS Pin habe....

Bild
Benutzeravatar
ProgBernie
Beiträge: 586
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von ProgBernie »

Andreas_P hat geschrieben: Do 27. Okt 2022, 19:42 Zum Flashen der Preisschilder möchte ich mir einen Stecker drucken den ich auf die Kontakteinflächen der Preisschilder drauf halte,
dazu fehlt mir die Info in welchen Abstand die einzelnen Kontaktflächen zu einander haben. Hat dazu jemand Informationen ?
Nimm doch eines der vorhandenen STLs und schneide da nur den Bereich der Kontaktierung raus. In OpenScad ist das ein Dreizeiler.

Code: Alles auswählen

intersection() {
  translate([-6,48,0]) import("ZBS243_Pogo_Flasher_main_body.stl");
  cube([20,12,10]);
}
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Da ich den Eindruck habe, das der Stick mir das WLAN zubrüllt mit Müll, hab ich den mal in Messing eingesperrt. .
53DBE93E-2149-4547-8846-E9F4F7271036.jpeg
Benutzeravatar
sukram
Beiträge: 3063
Registriert: Sa 10. Mär 2018, 18:27
Wohnort: Leibzsch

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von sukram »

Natürlich macht der das. Zigbee ist auch nur auf dem 2,4GHz ISM Band unterwegs. Man kann aber idR die Kanäle so legen, dass WLAN z.b. von Ch 5-13 liegt und Zigbee auf Ch 11. Es zählen die Kanalnummern noch mal anders, weil die weniger Bandbreite haben.

Hier was in neuhochplatt zum Thema: https://www.metageek.com/training/resou ... existence/
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

sukram hat geschrieben: Sa 29. Okt 2022, 12:12 Natürlich macht der das. Zigbee ist auch nur auf dem 2,4GHz ISM Band unterwegs. Man kann aber idR die Kanäle so legen, dass WLAN z.b. von Ch 5-13 liegt und Zigbee auf Ch 11. Es zählen die Kanalnummern noch mal anders, weil die weniger Bandbreite haben.

Hier was in neuhochplatt zum Thema: https://www.metageek.com/training/resou ... existence/
Ich meinte auch eher den E-Smog vom Controller und so. Wer weiss was der so an Schmutz außerhalb vom Kanal 11 macht.
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Hat denn schon jemand was gebastelt um die Teile per ZigBee2MQTT anzusprechen?

Einen CC2531 habe ich (noch) nicht da, aber einen CC1352 inkl. ZigBee Firmware. Das muss doch auch irgendwie gehen.
Benutzeravatar
phettsack
Beiträge: 1186
Registriert: Mo 12. Aug 2013, 18:17

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von phettsack »

Probier doch einfach mal das Zigbee2Mqtt auf Anlernen zu schalten und das Schild (Batterien raus, Batterien rein) dann ganz ganz dicht neben die Antenne zu packen und das Logfile zu beobachten ob da irgendwas kommt.
Ich bin mir aber nicht sicher ob zigbee2mqtt überhaupt eine Funktion hat um Nutzdaten (das Bild) zu übertragen. Das war ursprünglich gedacht um Sensordaten zu empfangen.

Bei ganz wenigen wird auch was gesendet: https://www.zigbee2mqtt.io/devices/mTouch_One.html
Da gäbe es dann display_text , dafür müsste das Schild dann vermutlich eine andere Firmware haben die aus zugesandtem Text ein Bild rendert.
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Tja, wenn das so einfach wäre...

Also Pairing passiert nicht, selbst im Debug Modus passiert nichts. Ich sehe aber, dass ein Paket (eigentlich zwei) vom Display gesendet wird, dann aber bereits vom Zigbee Stack verworfen wird, weil es kein gültiges ZigBee Paket sei. Das heißt das Paket geht gar nichts bis zum Rechner und ZigBee2MQTT.

Wenn ich mir die station.py anschauen, sieht mir das aber so aus, als ob die ganze Kommunikation verschlüsselt ist, auch das Pairing?! Hier steige ich aber noch nicht durch wie ich das ZigBee2MQTT klar machen soll. Ein

Code: Alles auswählen

network_key: [ 0xD3,0x06,0xD9,0x34,0x8E, 0x29, 0xE5, 0xE3, 0x58, 0xBF, 0x29, 0x34, 0x81, 0x20, 0x02, 0xC1 ]
führt auch nicht zu Funktion.
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Das Preisschild läuft definitiv nur mit dem Stick auf dem die TiMac Firmware drauf ist.
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Also kein ZigBee, sondern nur sowas ähnliches auf 802.15.4 basierend? Schade...
Benutzeravatar
michabe
Beiträge: 61
Registriert: Sa 28. Mai 2022, 15:29
Wohnort: JO52dc

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von michabe »

Andreas_P hat geschrieben: Do 27. Okt 2022, 19:42 Zum Flashen der Preisschilder möchte ich mir einen Stecker drucken den ich auf die Kontakteinflächen der Preisschilder drauf halte,
dazu fehlt mir die Info in welchen Abstand die einzelnen Kontaktflächen zu einander haben. Hat dazu jemand Informationen ?
Ich hab da mal was messen lassen:
Bild
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Das lässt mir ja doch keine Ruhe... Ich habe jetzt einen 802.15.4 Stack am laufen und bekomme auch Daten, jetzt möchte ich aber nicht den ganzen TIMAC reverse engineeren. Kann mir jemand die Ausgabe die beim ersten pairen von station.py --l --d ausgegeben wird senden?
Idealerweise noch print("pkt", pkt.hex()) am Anfang der def process_pkt(pkt): Funktion einfügen.

Sollte dann so irgendwie aussehen:

Code: Alles auswählen

python3 station.py  --l --d
DISCOVER-MODE
Labermodus
2022-11-04 19:41:59,818 pkt 1f3b10c36c0200000300ffff0000000000000200fe54a90035004744ffff7000c500000000000000ff00000000000000000000000000000000000000585600202f000000ffffffff975f0100e4550020dfdf000080350020e12f01000000 
2022-11-04 19:41:59,821 data 00000000000000000000000000585600202f000000ffffffff975f0100e4550020dfdf000080350020e12f01000000 
2022-11-04 19:41:59,821 hdr 41cc00ff7000000000000200fe6c0200000300ffff 
2022-11-04 19:41:59,821 ciph 00000000000000000000000000585600202f000000ffffffff975f0100e4550020dfdf00008035 
2022-11-04 19:41:59,821 nonce 010000006c0200000300ffff00 
2022-11-04 19:41:59,822 tag 0020e12f 
2022-11-04 19:41:59,822 packet is NOT authentic 
Hintergrund: TIMAC sendet irgendwie wohl keinen Header, ich habe aber einen, daher würde ich gerne die Unterschiede sehen. Irgendwie dürfte ein CC1352 mit ordentlicher Antenne auch etwas besser sein.
Benutzeravatar
ProgBernie
Beiträge: 586
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von ProgBernie »

Ich habe station.py wie folgt abgeändert:

Code: Alles auswählen

def process_pkt(pkt):
    if laber:
        print("pkt", pkt)
        print("pkt['data']", pkt['data'].hex())
Damit erhalte ich bei einem reset des Schildes:

Code: Alles auswählen

pi@TEApi:~/estation $ python station.py --l --d
DISCOVER-MODE
Labermodus
2022-11-04 21:20:54,113 Station started
2022-11-04 21:23:43,993 pkt {'type': 'parse_mac_data_ind', 'src_add_mode': 3, 'src_add': [0, 0, 2, 109, 108, 153, 59, 26], 'dst_add_mode': 2, 'dst_add': b'\xff\xff', 'timestamp': b';\x1a\x08\x00', 'timestamp2': b'\xb2\x04', 'src_pan_id': b'GD', 'dst_pan_id': b'\xff\xff', 'link_quality': 89, 'correlation': 107, 'rssi': 203, 'dsn': 0, 'key_source': b'\x00\x00\x00\x00\x00\x00\x00\x00', 'security_level': 0, 'key_id_mode': 0, 'key_index': 0, 'length': 47, 'data': b'\xcd\xd6\x94\t\x1c+9\x14\xb1h\xa1O^\xa9?\x02U\xfd)\xcd\xc0\x08\x03PC\xafR\xc0*E\x19\x08\xfcV\xaf\xd1\xffHBp\xc2\xc8T\x00\x00\x00\x00'}
2022-11-04 21:23:43,995 pkt['data'] cdd694091c2b3914b168a14f5ea93f0255fd29cdc008035043af52c02a451908fc56afd1ff484270c2c85400000000
2022-11-04 21:23:44,028 rcvd_packet: f00000000000130100000800280a00800028011d0043000200c800100000000000000000000000
2022-11-04 21:23:44,029 rcvhdr: 01c800ffffffff47441a3b996c6d020000
2022-11-04 21:23:44,036 Got assoc request
2022-11-04 21:23:44,037 TagInfo(protoVer=0, swVer=1181116006400, hwType=8, batteryMv=2600, rfu1=0, screenPixWidth=128, screenPixHeight=296, screenMmWidth=29, screenMmHeight=67, compressionsSupported=2, maxWaitMsec=200, screenType=16, rfu=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
2022-11-04 21:23:44,038 AssocInfo(checkinDelay=900000, retryDelay=1000, failedCheckinsTillBlank=2, failedCheckinsTillDissoc=2, newKey=bytearray(b'\xd3\x06\xd94\x8e)\xe5\xe3X\xbf)4\x81 \x02\xc1'), rfu=bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00'))
2022-11-04 21:23:44,038 hdr: 41cc0147441a3b996c6d0200003549d914004b1200
2022-11-04 21:23:44,039 nonce: d07465633549d914004b120000
2022-11-04 21:24:10,325 pkt {'type': 'parse_mac_data_ind', 'src_add_mode': 3, 'src_add': [0, 0, 2, 109, 108, 153, 59, 26], 'dst_add_mode': 3, 'dst_add': b'5I\xd9\x14\x00K\x12\x00', 'timestamp': b'\xa9[\t\x00', 'timestamp2': b'\x01%', 'src_pan_id': b'GD', 'dst_pan_id': b'GD', 'link_quality': 89, 'correlation': 107, 'rssi': 203, 'dsn': 0, 'key_source': b"\x08\x00'\x1e\x00\x1a;\x99", 'security_level': 0, 'key_id_mode': 63, 'key_index': 0, 'length': 30, 'data': b'\xcf\xd6\x94\t\x1c8+\x15\xb1`\xa9\xb9\x7f\xa3?\x1aU\xd5(\xd0\xc0K\xfeK\x16E\x00\x00\x00\x00'}
2022-11-04 21:24:10,326 pkt['data'] cfd694091c382b15b160a9b97fa33f1a55d528d0c04bfe4b164500000000
2022-11-04 21:24:10,329 rcvd_packet: f200000000130100000800f609000098000000000000
2022-11-04 21:24:10,329 rcvhdr: 41cc0047443549d914004b12001a3b996c6d020000
2022-11-04 21:24:10,331 Got checkin request
2022-11-04 21:24:10,332 CheckinInfo(swVer=1181116006400, hwType=8, batteryMv=2550, lastPacketLQI=0, lastPacketRSSI=0, temperature=152, rfu=b'\x00\x00\x00\x00\x00\x00')
2022-11-04 21:24:10,333 Reading image file:./0000026d6c993b1a.bmp/.png
2022-11-04 21:24:10,334 Using ./0000026d6c993b1a.png file
2022-11-04 21:24:10,335 Reading firmware file: UPDT0008.BIN
2022-11-04 21:24:10,336 No Firmware file available
2022-11-04 21:24:10,337 PendingInfo(imgUpdateVer=7161838112113685969, imgUpdateSize=9542, osUpdateVer=0, osUpdateSize=0, nextCheckinDelay=0, rfu=bytearray(b'\x00\x00\x00\x00'))
2022-11-04 21:24:10,338 hdr: 41cc0247441a3b996c6d0200003549d914004b1200
2022-11-04 21:24:10,338 nonce: ea7465633549d914004b120000
2022-11-04 21:24:10,554 pkt {'type': 'parse_mac_data_ind', 'src_add_mode': 3, 'src_add': [0, 0, 2, 109, 108, 153, 59, 26], 'dst_add_mode': 3, 'dst_add': b'5I\xd9\x14\x00K\x12\x00', 'timestamp': b'u^\t\x00', 'timestamp2': b'\xb4\x16', 'src_pan_id': b'GD', 'dst_pan_id': b'GD', 'link_quality': 89, 'correlation': 107, 'rssi': 203, 'dsn': 1, 'key_source': b'\t\x00j\x06\x00\x00K\x12', 'security_level': 0, 'key_id_mode': 59, 'key_index': 0, 'length': 29, 'data': b'd4T\x0eGD\x8e\xab\x16P5\x07q\x8e$\xcf\xee\x83\xab\x9ex\x1c\xd0\xf9v\x01\x00\x00\x00'}
2022-11-04 21:24:10,555 pkt['data'] 6434540e47448eab16503507718e24cfee83ab9e781cd0f97601000000
2022-11-04 21:24:10,558 rcvd_packet: f4d1f56363d1f56363000000005800000000000000
2022-11-04 21:24:10,559 rcvhdr: 41cc0147443549d914004b12001a3b996c6d020000
2022-11-04 21:24:10,560 Got chunk request
2022-11-04 21:24:10,561 ChunkReqInfo(versionRequested=7161838112113685969, offset=0, len=88, osUpdatePlz=0, rfu=b'\x00\x00\x00\x00\x00\x00')
2022-11-04 21:24:10,562 ChunkInfo(offset=0, osUpdatePlz=0, rfu=0)
2022-11-04 21:24:10,563 Reading image file: /tmp/0000026D6C993B1A_7161838112113685969.bmp
2022-11-04 21:24:10,567 sending chunk 95 f5000000000000424d46 ...
2022-11-04 21:24:10,568 hdr: 41cc0347441a3b996c6d0200003549d914004b1200
2022-11-04 21:24:10,569 nonce: ea7465633549d914004b120000
2022-11-04 21:24:10,659 pkt {'type': 'parse_mac_data_ind', 'src_add_mode': 3, 'src_add': [0, 0, 2, 109, 108, 153, 59, 26], 'dst_add_mode': 3, 'dst_add': b'5I\xd9\x14\x00K\x12\x00', 'timestamp': b'\xc1_\t\x00', 'timestamp2': b'\x9f%', 'src_pan_id': b'GD', 'dst_pan_id': b'GD', 'link_quality': 89, 'correlation': 107, 'rssi': 203, 'dsn': 2, 'key_source': b'\t\x00?\x1d\x00\x9e;\xcd', 'security_level': 0, 'key_id_mode': 121, 'key_index': 0, 'length': 29, 'data': b'z\xb2\xeb\xdd`\x1b.\xfco\xcd\x00\xbfM\xd1\xa5|\xc67\x837\xc1\rI\xbf\x81\x02\x00\x00\x00'}
2022-11-04 21:24:10,660 pkt['data'] 7ab2ebdd601b2efc6fcd00bf4dd1a57cc6378337c10d49bf8102000000
2022-11-04 21:24:10,662 rcvd_packet: f4d1f56363d1f56363580000005800000000000000
2022-11-04 21:24:10,663 rcvhdr: 41cc0247443549d914004b12001a3b996c6d020000
2022-11-04 21:24:10,665 Got chunk request
2022-11-04 21:24:10,665 ChunkReqInfo(versionRequested=7161838112113685969, offset=88, len=88, osUpdatePlz=0, rfu=b'\x00\x00\x00\x00\x00\x00')
2022-11-04 21:24:10,666 ChunkInfo(offset=88, osUpdatePlz=0, rfu=0)
2022-11-04 21:24:10,666 Reading image file: /tmp/0000026D6C993B1A_7161838112113685969.bmp
2022-11-04 21:24:10,667 sending chunk 95 f5580000000000555555 ...
2022-11-04 21:24:10,668 hdr: 41cc0447441a3b996c6d0200003549d914004b1200
2022-11-04 21:24:10,668 nonce: ea7465633549d914004b120000
2022-11-04 21:24:10,760 pkt {'type': 'parse_mac_data_ind', 'src_add_mode': 3, 'src_add': [0, 0, 2, 109, 108, 153, 59, 26], 'dst_add_mode': 3, 'dst_add': b'5I\xd9\x14\x00K\x12\x00', 'timestamp': b'\xf9`\t\x00', 'timestamp2': b"\xea'", 'src_pan_id': b'GD', 'dst_pan_id': b'GD', 'link_quality': 86, 'correlation': 107, 'rssi': 202, 'dsn': 3, 'key_source': b'\t\x00?\x07\x00\x9e;\xcd', 'security_level': 0, 'key_id_mode': 121, 'key_index': 0, 'length': 29, 'data': b'z\x11\x17"\xb8\xfeN#j\xcc"\xc41\xf0[\x95\x15\x8a!\x16~\x9b\xcc\xd2\xfc\x03\x00\x00\x00'}
2022-11-04 21:24:10,761 pkt['data'] 7a111722b8fe4e236acc22c431f05b95158a21167e9bccd2fc03000000
2022-11-04 21:24:10,763 rcvd_packet: f4d1f56363d1f56363b00000005800000000000000
2022-11-04 21:24:10,764 rcvhdr: 41cc0347443549d914004b12001a3b996c6d020000
2022-11-04 21:24:10,765 Got chunk request
2022-11-04 21:24:10,766 ChunkReqInfo(versionRequested=7161838112113685969, offset=176, len=88, osUpdatePlz=0, rfu=b'\x00\x00\x00\x00\x00\x00')
2022-11-04 21:24:10,767 ChunkInfo(offset=176, osUpdatePlz=0, rfu=0)
2022-11-04 21:24:10,767 Reading image file: /tmp/0000026D6C993B1A_7161838112113685969.bmp
2022-11-04 21:24:10,768 sending chunk 95 f5b00000000000555555 ...
2022-11-04 21:24:10,769 hdr: 41cc0547441a3b996c6d0200003549d914004b1200
2022-11-04 21:24:10,770 nonce: ea7465633549d914004b120000
...
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Danke, das muss ich in Ruhe verdauen. Da sind Ähnlichkeiten und Unterschiede...
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Die Info hat geholfen, dass die Pakete vom PC nun richtig empfangen werden. Das Beantworten passt noch nicht so ganz, da muss ich mir einen zweiten CC1352 besorgen, um mit zuschneiden was wirklich gesendet wird. Bisher ignoriert das Display leider die Antwort vom PC.
Benutzeravatar
ProgBernie
Beiträge: 586
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von ProgBernie »

Wenn es Dir hilft kann ich einen gesamten Upload und die hochgeladene Bilddatei zur Verfügung stellen.

Ach ja: Unter 2.5V meldet sich das Display noch mit einem Assoc Request, aber bleibt dann stehen. Ob es die Antwort nicht mehr liest oder was da schief läuft weiß ich nicht, es bleibt jedenfalls mit ASSOCIATE READY im Display stehen. Batterien raus/rein lässt das ganze wieder erneut starten.
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Der Ausschnitt reicht erstmal, danke. Der Hinweis mit den 2,5 V ist gut, das Display bekommt hier aber externe 3.3 V, daher schließe ich das momentan noch aus,

Laut UART Ausgabe wird nach dem Senden schlafen gegangen.

Code: Alles auswählen

booted at 0x1aec Reset reason: 00
Booting FW ver 0x0000011300000000
 -> FW v1.19.0.0
MAC 00:00:02:6C:C3:10:3B:1F
temp: 20
eeprom has 59 image slots
Erz IMAGES
Erz UPD
Erz SETTINGS
MESSAGE 'BOOTING'
booted at 0x1aec Reset reason: 01
Booting FW ver 0x0000011300000000
 -> FW v1.19.0.0
MAC 00:00:02:6C:C3:10:3B:1F
temp: 20
eeprom has 59 image slots
Associate is displayed
MESSAGE 'ASSOCIATE READY'
booted at 0x1aec Reset reason: 01
Booting FW ver 0x0000011300000000
 -> FW v1.19.0.0
MAC 00:00:02:6C:C3:10:3B:1F
temp: 21
eeprom has 59 image slots
Associate is not displayed
try ch 11
sleep: 791015
Vielleicht sollte ich einfach auf den bestellten passenden CC Stick warten, dann lässt sich das einfacher umbauen.
Benutzeravatar
sukram
Beiträge: 3063
Registriert: Sa 10. Mär 2018, 18:27
Wohnort: Leibzsch

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von sukram »

Der Sourcecode der Displaysoftware hilft da wohl nicht weiter? Da müsste doch grob drinstehen, was das Funkmodul hören will. Übersehe ich etwas?

https://github.com/atc1441/E-Paper_Pricetags
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Ja doch schon. Wenn ich das richtig verstanden haben sollte das so ablaufen:

Code: Alles auswählen

- ...
- ASSOCIATE READY anzeigen
- 4x Kommunikationsversuch
  - Nachricht senden
  - Delay
  - Empfang auswerten
    - Wenn was sinnvolles, pairing fortsetzen, ...
    - Ansonsten zumindest irgendwelche Meldungen
  - nochmal
- Schlafen gehen
- ...
Wie gesagt, ich warte einfach bis der Stick da ist, dann sollte sich das ganz einfach lösen lassen.
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Außen Temperaturanzeige aus der Influxdb gesaugt.

897095CB-1351-4407-981B-CD98EEFF98C1.jpeg
Benutzeravatar
ProgBernie
Beiträge: 586
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von ProgBernie »

omega hat geschrieben: Sa 5. Nov 2022, 20:52 Der Ausschnitt reicht erstmal, danke. Der Hinweis mit den 2,5 V ist gut, das Display bekommt hier aber externe 3.3 V, daher schließe ich das momentan noch aus,
Ich denke es sind nicht die 2.5V, aber die Displays die beim Booten nur noch 2.5V zeigen laden hier kein Bild mehr, die Zellen sind wohl zu hochohmig. Beim Laden der Bild-Chunks werden am Labornetzteil 40mA gezogen, das wird gemittelt sein und der Funkkram zieht ordentlich Strom. Das Umschalten der Displays zieht nur etwa 2mA.
Benutzeravatar
ProgBernie
Beiträge: 586
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von ProgBernie »

Hightech hat geschrieben: Sa 5. Nov 2022, 22:55 Außen Temperaturanzeige aus der Influxdb gesaugt.


897095CB-1351-4407-981B-CD98EEFF98C1.jpeg
Nett. Ich häkel gerade eine Wetteranzeige aus OWM zusammen. Bin schon am Bilder malen.
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Aaron bietet uns Adaperplatinen für das direkte Ansteuern über Arduino an.
Stückpreis 5Euro.

https://www.pcbway.com/project/sharepro ... a845c.html

Seine Email Adresse zum bestellen:
adapter@43u.de
C9845D34-3670-4607-90B5-1F3BF17ABDED.jpeg
Benutzeravatar
Hightech
Beiträge: 11307
Registriert: So 11. Aug 2013, 18:37

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Hightech »

Das Reichweitenproblem kann man erschlagen mit einem Stick, der eine SMA-Buchse hat und eine externe Antenne:

https://de.aliexpress.com/item/40006400 ... pt=glo2deu
Damit komme ich durch das ganze Haus.
Benutzeravatar
omega
Beiträge: 520
Registriert: So 11. Aug 2013, 14:36
Kontaktdaten:

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von omega »

Mit dem CC1352 bin ich noch nicht weitergekommen, da mein CC1352 einfach nichts zu senden scheint, also auch mit anderem CC1352 als Empfänger und TI SmartRF Studio getestet. Diese Baustelle muss aber erstmals ruhen.

In der Zwischenzeit habe ich etwas gebastelt, das anzeigt, welche Mülltonne rausgestellt werden muss. Jeweils am Tag vor der Abholung erscheint eine Meldung:
restmuell.jpg
Dazu müssen die Abholtermine als Kalenderdatei in abfuhrtermine.ics gespeichert sein und das Skript regelmässig (z.B. per Cron) aufgerufen werden. Die in destination_file konfigurierte Datei wird dann entsprechend aktualisiert.

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 12 12:18:43 2022

@author: omega
"""
# imports
from icalendar import Calendar, Event, vCalAddress, vText
from datetime import datetime, timedelta, time
from PIL import Image, ImageDraw, ImageFont
from pathlib import Path
from collections import namedtuple
import os
import pytz
import time
 
# init the calendar
cal = Calendar()

Display_Size_x=296
Display_Size_y=128
destination_file = '../epaper-station/0000023902443b13.png'

def generate_abhol_image(summary, datum):
    muster=Image.new("RGB",(296,128),color=(255,255,255))
    named_tuple = time.localtime() # get struct_time
    time_string = time.strftime("%d.%m.%Y, %H:%M:%S", named_tuple)
    im = ImageDraw.Draw(muster)
    font = ImageFont.load('VeraMono30.pil')
    font_klein = ImageFont.load('VeraMono8.pil')
    

    _, _, w, h = im.textbbox((0, 0), summary, font=font)
    im.text((((Display_Size_x/2)-w/2),Display_Size_y/2-h/2-30), summary ,fill=(0, 0, 0),font=font)
    
    _, _, w, h = im.textbbox((0, 0), datum, font=font_klein)
    im.text((((Display_Size_x/2)-w/2),80), datum ,fill=(0, 0, 0),font=font_klein)
    
    _, _, w, h = im.textbbox((0, 0), time_string, font=font_klein)
    im.text((((Display_Size_x/2)-w/2),100), time_string ,fill=(0, 0, 0),font=font_klein)
    
    muster=muster.rotate(angle=90,expand="true")
    muster.save(destination_file)


def generate_empty_image():
    muster=Image.new("RGB",(296,128),color=(255,255,255))
    named_tuple = time.localtime() # get struct_time
    time_string = time.strftime("%d.%m.%Y, %H:%M:%S", named_tuple)
    im = ImageDraw.Draw(muster)
    font_klein = ImageFont.load('VeraMono8.pil')
    
    _, _, w, h = im.textbbox((0, 0), time_string, font=font_klein)
    im.text((((Display_Size_x/2)-w/2),100), time_string ,fill=(0, 0, 0),font=font_klein)
    
    muster=muster.rotate(angle=90,expand="true")
    muster.save(destination_file)


generate_empty_image()
today = datetime.today().date()
e = open('abfuhrtermine.ics', 'rb')
ecal = cal.from_ical(e.read())
for component in ecal.walk():
    #print(component.name)
    if component.name == "VEVENT":
        #print (component.get('summary'))
        #print (component.get('dtstart').dt)
        nachrichttag = component.get('dtstart').dt - timedelta(days=1)
        if(today==nachrichttag):
            #print (component.get('summary'))
            #print (component.get('dtstart').dt)
            generate_abhol_image(str(component.get('summary')), str(component.get('dtstart').dt))
        
        
e.close()
Benutzeravatar
T5000
Beiträge: 140
Registriert: Mo 19. Aug 2013, 11:57
Wohnort: Lichtenau

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von T5000 »

Ich hab jetzt meine PriceTags auch endlich angesteuert bekommen,
Aktuell erstelle ich noch meine PNG Dateien mit Paint.
Mich würde nur mal interessieren wie ich denn die rote Farbe vom Preisschild ansteuern kann?
Ist das ein definierter Rot-Farbton?

Gruss Thomas
duese
Beiträge: 6059
Registriert: So 11. Aug 2013, 17:56

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von duese »

Rot halt. In RGB: 255,0,0 bzw 100%,0%,0%
Benutzeravatar
T5000
Beiträge: 140
Registriert: Mo 19. Aug 2013, 11:57
Wohnort: Lichtenau

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von T5000 »

hm, also wenn ich rot (255,0,0) verwende im PNG bleibt es leider grau.
Ich hatte hier gelesen das es mit der station.py von Hightech gehen soll, die Variante die ich hier gefunden habe bringt mir leider einen Fehler beim Start.
Ich nutze das pyhton aktuell noch unter Windows.

Ausgabe station.py von Hightech: (die die ich hier irgendwo gefunden habe)
station_py_hightech.PNG

Code: Alles auswählen

import timaccop
import bmp2grays
from Cryptodome.Cipher import AES
from collections import namedtuple
import struct
import os, sys
import logging
from PIL import Image, ImageDraw, ImageFont
import time

parameter=sys.argv
discover=False
laber=False

if "--d" in parameter:
    discover=True
    print ("DISCOVER-MODE")

if "--l" in parameter:
    discover=True
    print ("Labermodus")

Display_Size_x=296
Display_Size_y=128

masterkey = bytearray.fromhex("D306D9348E29E5E358BF2934812002C1")

PORT = com4("EPS_PORT", default="/dev/ttyACM0")
EXTENDED_ADDRESS = [ int(addr.strip(), 16) for addr in os.environ.get("EPS_EXTENDED_ADDRESS", default="0x00, 0x12, 0x4B, 0x00, 0x14, 0xD9, 0x49, 0x35").split(",") ]
PANID = [ int(panid.strip(), 16) for panid in os.environ.get("EPS_PANID", default="0x47, 0x44").split(",") ]
CHANNEL = int(os.environ.get("EPS_CHANNEL", default="11"))
IMAGE_DIR = os.environ.get("EPS_IMAGE_DIR", default="./")
IMAGE_WORKDIR = os.environ.get("EPS_IMAGE_WORKDIR", default="/tmp/")

CHECKIN_DELAY = int(os.environ.get("EPS_CHECKIN_DELAY", default="900000")) # 900s
RETRY_DELAY = int(os.environ.get("EPS_RETRY_DELAY", default="1000")) # 1s
FAILED_CHECKINS_TILL_BLANK = int(os.environ.get("EPS_FAILED_CHECKINS_TILL_BLANK", default="2"))
FAILED_CHECKINS_TILL_DISSOC = int(os.environ.get("EPS_FAILED_CHECKINS_TILL_DISSOC", default="2"))

PKT_ASSOC_REQ                   = (0xF0)
PKT_ASSOC_RESP                  = (0xF1)
PKT_CHECKIN                             = (0xF2)
PKT_CHECKOUT                    = (0xF3)
PKT_CHUNK_REQ                   = (0xF4)
PKT_CHUNK_RESP                  = (0xF5)

VERSION_SIGNIFICANT_MASK                                = (0x0000ffffffffffff)

HW_TYPE_42_INCH_SAMSUNG                                 = (1)
HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST    = (0xEFF8)
HW_TYPE_74_INCH_DISPDATA                                = (2)
HW_TYPE_74_INCH_DISPDATA_FRAME_MODE             = (3)
HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST   = (0x008b)
HW_TYPE_ZBD_EPOP50                                              = (4)
HW_TYPE_ZBD_EPOP50_ROM_VER_OFST                 = (0x008b)
HW_TYPE_ZBD_EPOP900                                             = (5)
HW_TYPE_ZBD_EPOP900_ROM_VER_OFST                = (0x008b)
HW_TYPE_29_INCH_DISPDATA                                = (6)
HW_TYPE_29_INCH_DISPDATA_FRAME_MODE             = (7)
HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST   = (0x008b)
HW_TYPE_29_INCH_ZBS_026                                 = (8)
HW_TYPE_29_INCH_ZBS_026_FRAME_MODE              = (9)
HW_TYPE_29_INCH_ZBS_025                                 = (10)
HW_TYPE_29_INCH_ZBS_025_FRAME_MODE              = (11)
HW_TYPE_154_INCH_ZBS_033                                = (18)
HW_TYPE_154_INCH_ZBS_033_FRAME_MODE             = (19)
HW_TYPE_42_INCH_ZBS_026                                 = (28)
HW_TYPE_42_INCH_ZBS_026_FRAME_MODE              = (29)
HW_TYPE_29_INCH_ZBS_ROM_VER_OFST                = (0x008b)



TagInfo = namedtuple('TagInfo', """
protoVer,
swVer,
hwType,
batteryMv,
rfu1,
screenPixWidth,
screenPixHeight,
screenMmWidth,
screenMmHeight,
compressionsSupported,
maxWaitMsec,
screenType,
rfu
""")

AssocInfo = namedtuple('AssocInfo', """
checkinDelay,
retryDelay,
failedCheckinsTillBlank,
failedCheckinsTillDissoc,
newKey,
rfu
""")

CheckinInfo = namedtuple('CheckinInfo', """
swVer,
hwType,
batteryMv,
lastPacketLQI,
lastPacketRSSI,
temperature,
rfu,
""")

PendingInfo = namedtuple('PendingInfo', """
imgUpdateVer,
imgUpdateSize,
osUpdateVer,
osUpdateSize,
nextCheckinDelay,
rfu
""")

ChunkReqInfo = namedtuple('ChunkReqInfo', """
versionRequested,
offset,
len,
osUpdatePlz,
rfu,
""")

ChunkInfo = namedtuple('ChunkInfo', """
offset,
osUpdatePlz,
rfu,
""")

logging.basicConfig(format='%(asctime)s %(message)s')
logger = logging.getLogger(__name__)

dsn = 0

def print(*args):
    msg = ""
    for arg in args:
        msg += str(arg) + " "
    logger.warning(msg)

def decrypt(hdr, enc, tag, nonce):
    cipher = AES.new(masterkey, AES.MODE_CCM, nonce, mac_len=4)
    cipher.update(hdr)
    plaintext = cipher.decrypt(enc)
    if laber:
        print("rcvd_packet:", plaintext.hex())
        print("rcvhdr:", hdr.hex())
    try:
        cipher.verify(tag)
        return plaintext
    except:
        return None

def send_data(dst, data):
    global dsn
    dsn += 1
    if dsn > 255:
        dsn = 0
    hdr = bytearray.fromhex("41cc")
    hdr.append(dsn)
    hdr.extend(PANID)
    hdr.extend(reversed(dst))
    hdr.extend(EXTENDED_ADDRESS)
    if laber:
        print("hdr:", hdr.hex())

    cntr = int(time.time())
    cntrb = struct.pack('<L', cntr)

    nonce = bytearray(cntrb)
    nonce.extend(EXTENDED_ADDRESS)
    nonce.append(0)
    if laber:
        print("nonce:", nonce.hex())

    cipher = AES.new(masterkey, AES.MODE_CCM, nonce, mac_len=4)
    cipher.update(hdr)
    ciphertext, tag = cipher.encrypt_and_digest(data)

    out = ciphertext+tag+cntrb
    timaccop.mac_data_req(dst, PANID, 12, dsn, out)

def process_assoc(pkt, data):
    ti = TagInfo._make(struct.unpack('<BQHHBHHHHHHB11s',data))
    print(ti)

    ai = AssocInfo(
            checkinDelay=CHECKIN_DELAY,
            retryDelay=RETRY_DELAY,
            failedCheckinsTillBlank=FAILED_CHECKINS_TILL_BLANK,
            failedCheckinsTillDissoc=FAILED_CHECKINS_TILL_DISSOC,
            newKey=masterkey,
            rfu=bytearray(8*[0])
    )
    print(ai)
    ai_pkt = bytearray([ PKT_ASSOC_RESP ]) + bytearray(struct.pack('<LLHH16s8s', *ai))

    send_data(pkt['src_add'], ai_pkt)

def prepare_image(client):
    is_bmp = False
    base_name = os.path.join(IMAGE_DIR, bytes(client).hex())
    filename = base_name + ".png"
    print("Reading image file:" + base_name + ".bmp/.png")

    if os.path.isfile(filename):
        print("Using "+filename+" file")
    elif os.path.isfile(base_name + ".bmp"):
        is_bmp = True
        filename = base_name + ".bmp"
        print("Using .bmp file")
    elif discover == True:
        generate_discover_image(filename)
        print("NEW DEVICE")
        print(filename)
    else:
        print("Device not found")
        return(0,0)


    modification_time = os.path.getmtime(filename)
    creation_time = os.path.getctime(filename)
    imgVer = int(modification_time)<<32|int(creation_time) # This uses the mofidication time of the image to look for the newest one

    file_conv = os.path.join(IMAGE_WORKDIR, bytes(client).hex().upper() + "_" + str(imgVer) + ".bmp") # also use the MAC in case 1 images are created within 1 second

    if not os.path.isfile(file_conv):
        if is_bmp:
            bmp2grays.convertImage(1, "1bppR", filename, file_conv)
        else:
            Image.open(filename).convert("RGB").save(os.path.join(IMAGE_WORKDIR, "tempConvert.bmp"))
            bmp2grays.convertImage(1, "1bppR", os.path.join(IMAGE_WORKDIR, "tempConvert.bmp"), file_conv)

    imgLen = os.path.getsize(file_conv)

    return (imgVer, imgLen)

def get_firmware_offset(hwType):
    if hwType == HW_TYPE_42_INCH_SAMSUNG:
        return HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST
    if hwType == HW_TYPE_74_INCH_DISPDATA:
        return HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST
    if hwType == HW_TYPE_74_INCH_DISPDATA_FRAME_MODE:
        return HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST
    if hwType == HW_TYPE_29_INCH_DISPDATA:
        return HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST
    if hwType == HW_TYPE_29_INCH_DISPDATA_FRAME_MODE:
        return HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST
    if hwType == HW_TYPE_ZBD_EPOP50:
        return HW_TYPE_ZBD_EPOP50_ROM_VER_OFST
    if hwType == HW_TYPE_ZBD_EPOP900:
        return HW_TYPE_ZBD_EPOP900_ROM_VER_OFST
    if hwType == HW_TYPE_29_INCH_ZBS_026 or hwType == HW_TYPE_29_INCH_ZBS_026_FRAME_MODE or hwType == HW_TYPE_29_INCH_ZBS_025 or hwType == HW_TYPE_29_INCH_ZBS_025_FRAME_MOD
E or hwType == HW_TYPE_154_INCH_ZBS_033 or hwType == HW_TYPE_154_INCH_ZBS_033_FRAME_MODE or hwType == HW_TYPE_42_INCH_ZBS_026 or hwType == HW_TYPE_42_INCH_ZBS_026_FRAME_MOD
E:
        return HW_TYPE_29_INCH_ZBS_ROM_VER_OFST

def prepare_firmware(hwType):
    filename = 'UPDT{0:0{1}X}.BIN'.format(hwType,4)
    if laber:
        print("Reading firmware file:", filename)

    if not os.path.isfile(filename):
        if laber:
            print("No Firmware file available")
        return (0,0)

    f = open(filename,mode='rb')
    f.seek(get_firmware_offset(hwType))
    firmwareVersionData = f.read(8)
    f.close()

    osVer = int.from_bytes(firmwareVersionData, "little") & VERSION_SIGNIFICANT_MASK | hwType << 48
    osLen = os.path.getsize(filename)

    return (osVer, osLen)

def get_image_data(imgVer, offset, length):
    filename = os.path.join(IMAGE_WORKDIR, imgVer + ".bmp")
    if laber:
        print("Reading image file:", filename)

    f = open(filename,mode='rb')
    f.seek(offset)
    image_data = f.read(length)
    f.close()

    return image_data

def get_fw_data(hwType, offset, length):
    filename = 'UPDT{0:0{1}X}.BIN'.format(hwType,4)
    if laber:
        print("Reading firmware file:", filename)

    f = open(filename,mode='rb')
    f.seek(offset)
    fw_data = f.read(length)
    f.close()

    return fw_data

def process_checkin(pkt, data):
    ci = CheckinInfo._make(struct.unpack('<QHHBBB6s',data))

    print(ci)

    imgVer = 0
    imgLen = 0

    try:
        imgVer, imgLen = prepare_image(pkt['src_add'])
    except Exception as e :
        print("Unable to prepare image data for client", pkt['src_add'])
        print(e)

    osVer = 0
    osLen = 0

    try:
        osVer, osLen = prepare_firmware(ci.hwType)
    except Exception as e :
        print("Unable to prepare firmware data for client", pkt['src_add'])
        print(e)

    pi = PendingInfo(
        imgUpdateVer = imgVer,
        imgUpdateSize = imgLen,
        osUpdateVer = osVer,
        osUpdateSize = osLen,
        nextCheckinDelay = 0,
        rfu=bytearray(4*[0])
    )
    print(pi)

    pi_pkt = bytearray([ PKT_CHECKOUT ]) + bytearray(struct.pack('<QLQLL4s', *pi))

    send_data(pkt['src_add'], pi_pkt)

def process_download(pkt, data):
    cri = ChunkReqInfo._make(struct.unpack('<QLBB6s',data))
    if laber:
        print(cri)

    ci = ChunkInfo(
        offset = cri.offset,
        osUpdatePlz = cri.osUpdatePlz,
        rfu = 0,
    )
    if laber:
        print(ci)

    try:
        if ci.osUpdatePlz:
            fdata = get_fw_data(cri.versionRequested>>48, cri.offset, cri.len)
        else:
            fdata = get_image_data(bytes(pkt['src_add']).hex().upper() + "_" + str(cri.versionRequested), cri.offset, cri.len)
    except Exception as e :
        print("Unable to get data for version", cri.versionRequested)
        print(e)
        return

    outpkt = bytearray([ PKT_CHUNK_RESP ]) + bytearray(struct.pack('<LBB', *ci)) + bytearray(fdata)
    if laber:
        print("sending chunk", len(outpkt), outpkt[:10].hex() ,"...")

    send_data(pkt['src_add'], outpkt)

def generate_discover_image(filename):
    muster=Image.new("RGB",(296,128),color=(255,255,255))
    named_tuple = time.localtime() # get struct_time
    time_string = time.strftime("%m/%d/%Y, %H:%M:%S", named_tuple)
    im = ImageDraw.Draw(muster)
    font = ImageFont.load_default()
    length_name=font.getbbox("Aktivierung")
    im.text((((Display_Size_x/2)-length_name[2]/2),5),"Aktivierung" ,fill=(0, 0, 0),font=font)
    length_name=font.getbbox(filename)
    im.text((((Display_Size_x/2)-length_name[2]/2),45),filename ,fill=(0, 0, 0),font=font)
    box=font.getbbox(time_string)

    tboy1=90
    tboy2=box[3]+tboy1
    tbox1=((Display_Size_x-box[2])/2)
    tbox2=((Display_Size_x-box[2])/2)+box[2]

    im.rectangle((tbox1-6,tboy1-2,tbox2+6,tboy2+2),fill=(255,0,0),outline=(0,0,0),width=2)
    im.text((tbox1,tboy1),time_string,fill=(255,255,255),font=font)
    muster=muster.rotate(angle=90,expand="true")
    muster.save(filename)


def generate_pkt_header(pkt): #hacky- timaccop cannot provide header data
    bcast = True
    if pkt['dst_add'] == b'\xff\xff': #broadcast assoc
        hdr = bytearray.fromhex("01c8")
    else:
        hdr = bytearray.fromhex("41cc")
        bcast = False
    hdr.append(pkt['dsn'])
    hdr.extend(pkt['dst_pan_id'])
    hdr.extend(pkt['dst_add'])
    if bcast:
        hdr.extend(pkt['src_pan_id'])
    hdr.extend(reversed(pkt['src_add']))

    return hdr


def process_pkt(pkt):
    hdr = generate_pkt_header(pkt)
    if len(pkt['data']) < 10:
        print("Received a too short paket")
        print("data", pkt['data'].hex())
        return

    nonce = bytearray(pkt['data'][-4:])
    nonce.extend(reversed(pkt['src_add']))
    nonce.extend(b'\x00')

    tag = pkt['data'][-8:-4]

    ciphertext = pkt['data'][:-8]

    plaintext = decrypt(hdr, ciphertext, tag, nonce)
    if not plaintext:
        print("data", pkt['data'].hex())
        print("hdr", hdr.hex())
        print("ciph", ciphertext.hex())
        print("nonce", nonce.hex())
        print("tag", tag.hex())
        print("packet is NOT authentic")
        return

    typ = plaintext[0]

    if typ == PKT_ASSOC_REQ:
        print("Got assoc request")
        process_assoc(pkt, plaintext[1:])
    elif typ == PKT_CHECKIN:
        print("Got checkin request")
        process_checkin(pkt, plaintext[1:])
    elif typ == PKT_CHUNK_REQ:
        if laber:
            print("Got chunk request")
        process_download(pkt, plaintext[1:])
    else:
        print("Unknown request", typ)

timaccop.init(PORT, PANID, CHANNEL, EXTENDED_ADDRESS, process_pkt)
print("Station started")

timaccop.run()

Meine aktuelle station.py ohne rot:

Code: Alles auswählen

import timaccop 
from Cryptodome.Cipher import AES
from collections import namedtuple
import struct
import os
import logging
from PIL import Image
import binascii
import time
from io import BytesIO

masterkey = bytearray.fromhex("D306D9348E29E5E358BF2934812002C1")

PORT = "COM5"
EXTENDED_ADDRESS = [ 0x00, 0x12, 0x4B, 0x00, 0x14, 0xD9, 0x49, 0x35 ]
PANID = [ 0x47, 0x44 ]
CHANNEL = 11
IMAGE_WORKDIR = "tmp/"

PKT_ASSOC_REQ			= (0xF0)
PKT_ASSOC_RESP			= (0xF1)
PKT_CHECKIN				= (0xF2)
PKT_CHECKOUT			= (0xF3)
PKT_CHUNK_REQ			= (0xF4)
PKT_CHUNK_RESP			= (0xF5)


TagInfo = namedtuple('TagInfo', """
protoVer,
swVer,
hwType,
batteryMv,
rfu1,
screenPixWidth,
screenPixHeight,
screenMmWidth,
screenMmHeight,
compressionsSupported,
maxWaitMsec,
screenType,
rfu
""")

AssocInfo = namedtuple('AssocInfo', """
checkinDelay,
retryDelay,
failedCheckinsTillBlank,
failedCheckinsTillDissoc,
newKey,
rfu
""")

CheckinInfo = namedtuple('CheckinInfo', """
swVer,
hwType,
batteryMv,
lastPacketLQI,
lastPacketRSSI,
temperature,
rfu,
""")

PendingInfo = namedtuple('PendingInfo', """
imgUpdateVer,
imgUpdateSize,
osUpdateVer,
osUpdateSize,
nextCheckinDelay,
rfu
""")

ChunkReqInfo = namedtuple('ChunkReqInfo', """
versionRequested,
offset,
len,
osUpdatePlz,
rfu,
""")

ChunkInfo = namedtuple('ChunkInfo', """
offset,
osUpdatePlz,
rfu,
""")

logging.basicConfig(format='%(asctime)s %(message)s')
logger = logging.getLogger(__name__)

dsn = 0

def print(*args):
    msg = ""
    for arg in args:
        msg += str(arg) + " "
    logger.warning(msg)

def decrypt(hdr, enc, tag, nonce):
    cipher = AES.new(masterkey, AES.MODE_CCM, nonce, mac_len=4)
    cipher.update(hdr)
    plaintext = cipher.decrypt(enc)
    #print("rcvd_packet:", plaintext.hex())
    #print("rcvhdr:", hdr.hex())
    try:
        cipher.verify(tag)
        return plaintext
    except:
        return None

def send_data(dst, data):
    global dsn
    dsn += 1
    if dsn > 255:
        dsn = 0
    hdr = bytearray.fromhex("41cc")
    hdr.append(dsn)
    hdr.extend(PANID)
    hdr.extend(reversed(dst))
    hdr.extend(EXTENDED_ADDRESS)
    #print("hdr:", hdr.hex())

    cntr = int(time.time())
    cntrb = struct.pack('<L', cntr)

    nonce = bytearray(cntrb)
    nonce.extend(EXTENDED_ADDRESS)
    nonce.append(0)
    #print("nonce:", nonce.hex())

    cipher = AES.new(masterkey, AES.MODE_CCM, nonce, mac_len=4)
    cipher.update(hdr)
    ciphertext, tag = cipher.encrypt_and_digest(data)

    out = ciphertext+tag+cntrb
    timaccop.mac_data_req(dst, PANID, 12, dsn, out)

def process_assoc(pkt, data):
    ti = TagInfo._make(struct.unpack('<BQHHBHHHHHHB11s',data))
    print(ti)

    ai = AssocInfo(
	    checkinDelay=300000, #check each 900sec 
	    retryDelay=1000, #retry delay 1000ms
	    failedCheckinsTillBlank=2,
	    failedCheckinsTillDissoc=0,
	    newKey=masterkey,
	    rfu=bytearray(8*[0])
    )
    print(ai)
    ai_pkt = bytearray([ PKT_ASSOC_RESP ]) + bytearray(struct.pack('<LLHH16s8s', *ai))

    send_data(pkt['src_add'], ai_pkt)

def prepare_image(client):
    filename = bytes(client).hex() + ".png"
    print("Reading image file:", filename)

    modification_time = os.path.getmtime(filename)
    creation_time = os.path.getctime(filename)
    
    pf = open(filename,mode='rb')
    imgData = pf.read()
    imgVer = int(modification_time)<<32|int(creation_time) # This uses the mofidication time of the image to look for the newest one
    #imgVer = binascii.crc32(imgData) # This uses the CRC of the image to look for a newer one but can fail as on "stock" custom firmware the highest number is used
    pf.close()

    file_conv = IMAGE_WORKDIR + str(imgVer) + ".bmp"

    if not os.path.isfile(file_conv):
        pngdata = BytesIO(imgData)

        im = Image.open(pngdata)
        im_L = im.convert("1")
        im_L.save(file_conv)

    imgLen = os.path.getsize(file_conv)

    return (imgVer, imgLen)

def get_image_data(imgVer, offset, length):
    filename = IMAGE_WORKDIR + str(imgVer) + ".bmp"
    print("Reading image file:", filename)

    f = open(filename,mode='rb')
    f.seek(offset)
    image_data = f.read(length)
    f.close()

    return image_data

def process_checkin(pkt, data):
    ci = CheckinInfo._make(struct.unpack('<QHHBBB6s',data))
    print(ci)

    try:
        imgVer, imgLen = prepare_image(pkt['src_add'])
    except Exception as e :
        print("Unable to prepare image data for client", pkt['src_add'])
        print(e)
        return

    pi = PendingInfo(
        imgUpdateVer = imgVer,
        imgUpdateSize = imgLen,
        osUpdateVer = ci.swVer,
        osUpdateSize = 0,
        nextCheckinDelay = 0,
        rfu=bytearray(4*[0])
    )
    print(pi)

    pi_pkt = bytearray([ PKT_CHECKOUT ]) + bytearray(struct.pack('<QLQLL4s', *pi))

    send_data(pkt['src_add'], pi_pkt)

def process_download(pkt, data):
    cri = ChunkReqInfo._make(struct.unpack('<QLBB6s',data))
    print(cri)

    ci = ChunkInfo(
        offset = cri.offset,
        osUpdatePlz = 0,
        rfu = 0,
    )
    print(ci)

    try:
        fdata = get_image_data(cri.versionRequested, cri.offset, cri.len)
    except Exception as e :
        print("Unable to get image data for version", cri.versionRequested)
        print(e)
        return

    outpkt = bytearray([ PKT_CHUNK_RESP ]) + bytearray(struct.pack('<LBB', *ci)) + bytearray(fdata)

    print("sending chunk", len(outpkt), outpkt[:10].hex() ,"...")

    send_data(pkt['src_add'], outpkt)

def generate_pkt_header(pkt): #hacky- timaccop cannot provide header data
    bcast = True
    if pkt['dst_add'] == b'\xff\xff': #broadcast assoc
        hdr = bytearray.fromhex("01c8")
    else:
        hdr = bytearray.fromhex("41cc")
        bcast = False
    hdr.append(pkt['dsn'])
    hdr.extend(pkt['dst_pan_id'])
    hdr.extend(pkt['dst_add'])
    if bcast:
        hdr.extend(pkt['src_pan_id'])
    hdr.extend(reversed(pkt['src_add']))

    return hdr


def process_pkt(pkt):
    hdr = generate_pkt_header(pkt)
    
    if len(pkt['data']) < 10:
        print("Received a too short paket")
        print("data", pkt['data'].hex())
        return

    nonce = bytearray(pkt['data'][-4:])
    nonce.extend(reversed(pkt['src_add']))
    nonce.extend(b'\x00')

    tag = pkt['data'][-8:-4]

    ciphertext = pkt['data'][:-8]

    plaintext = decrypt(hdr, ciphertext, tag, nonce)
    if not plaintext:
        print("data", pkt['data'].hex())
        print("hdr", hdr.hex())
        print("ciph", ciphertext.hex())
        print("nonce", nonce.hex())
        print("tag", tag.hex())
        print("packet is NOT authentic")
        return

    typ = plaintext[0]

    if typ == PKT_ASSOC_REQ:
        print("Got assoc request")
        process_assoc(pkt, plaintext[1:])
    elif typ == PKT_CHECKIN:
        print("Got checkin request")
        process_checkin(pkt, plaintext[1:])
    elif typ == PKT_CHUNK_REQ:
        print("Got chunk request")
        process_download(pkt, plaintext[1:])
    else:
        print("Unknown request", typ)

timaccop.init(PORT, PANID, CHANNEL, EXTENDED_ADDRESS, process_pkt)
print("Station started")

timaccop.run()

kann mir jemand weiterhelfen?
Lokaro
Beiträge: 16
Registriert: So 20. Nov 2022, 23:18

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von Lokaro »

Benutze am besten die aktuellste aus dem Repo, ROT habe ich letztens erst mit eingebaut.

Sieht so aus als nutzt du noch die ältere version
Benutzeravatar
T5000
Beiträge: 140
Registriert: Mo 19. Aug 2013, 11:57
Wohnort: Lichtenau

Re: Elektronisches Preisschild Sammelbestellung - Ansteuerung

Beitrag von T5000 »

hm, ich hab mal die aktuelle stations.py ausprobiert.
Ich sehe die Grafik auf dem Display jetzt diagonal verzerrt. Das rot scheint jetzt aber mit angezeigt zu werden.
Öfters erhalte ich aber auf dem Display nur noch den Blank-Screen.
Antworten