LOGO Logo im Camper

Dazu sollte man Erfahrungen im Bereich ESP32-Programmierung mitbringen.
Also ich habe da auch etwas Erfahrung. Für den kleinen habe ich eine Slotcar Zeit Erfassung nur mit KI Programmiert. OLED Display, 4x4 Zahlenfeld, Ampeln, 2x IR Lichtschranke, Wifi Anbindung für die Bestzeiten auf einer Webseite. Das ganze war in 1h eigentlich fertig, mit teilen aus der Spielekiste :D. VSCode mit Roo, OpenRouter, PlatformIO. Am längsten hat es gedauert die Platine zu Zeichnen und die 3D Druck teile für die Bahn...

Kosten für den Code, ich meine 2-3$.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
ESP32 gehört schon zu den Sachen, die Spaß machen.
Hat man etwas Erfahrung in SCT / ST, dann ist die Programmierung absolut keine Hexerei.
Bei der Hardware hast du die Alternativen: Kaufen oder Selber designen.
Platinenlayout ist mittlerweile keine große Hexerei. Der Platinenfertiger in China macht die Platine und bestückt auf Wunsch sogar
Und das zu einem Spotpreis und in kurzer Zeit. Layout hochgeladen und nach 10 bis 20 Tagen hast du die fertigen Teile.
Gehäuse und Halterungen kommen aus dem 3D-Drucker.
Kann sich zu einem schönen Hobby entwickeln.

Dann aber bitte nicht die Dokumentation vergessen.
Mein Nachbar hat mal so einen verbastelten Wohnwagen gekauft.
Das macht keinen Spass.
 
Da stellt sich die Frage, wie viel Peter selbst entwickeln möchte bzw. wie viel Umwege zum Ziel er bereit ist zu gehen?

Mein persönlicher Favorit ist der Vorschlag von DeaD_EyE:

ESP32-S3-ETH-8DI-8RO

Für ca. 50 € ist das ein unschlagbares Preis-Leistungsverhältnis!

Fertiges Gerät, geschlossenes Gehäuse, mit isolierten Eingängen und Relais-Ausgängen.
Keine Leiterplattenentwicklung, viel weniger Bastelei und kein 3D Druck erforderlich.
Ersetzt den RPi, die LOGO und mit LVGL das TDE!

Im Gerät sind noch Pins für z.B. Analogwerkverarbeitung frei.
Über RS485 können die Module noch "verbusst" werden.

Aber so schnell wie mit LOGO und RPi, kommt er damit nicht ans Ziel.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich hatte für den mal Testcode in Micropython erstellt. CAN-Bus wird momentan noch nicht durch Micropython unterstützt, ist aber vorbereitet. Ich hab mir das selbst kompiliert. Wenn man kein Python kann, sollte man besser mit Micropython nicht anfangen und direkt in C/C++ programmieren. Für VSCode gibt es speziell für den ESP32 Erweiterungen.

Unter anderem hatte ich CAN-Bus verwendet, um zu überprüfen, ob es machbar ist, Endcodesignale über den CAN-Bus zu senden. Wenn ich mich richtig erinnere, hatte ich eine Verzögerungszeit (ESP32 <-[CAN]-> RPi0) von 500µs gemessen.

Hier der Micropython-Test-Code:
Python:
"""
Test Code for ESP32-S3-ETH-8DI-8RO-C

https://www.waveshare.com/wiki/ESP32-S3-ETH-8DI-8RO-C#Inteface_Description

This works:
- WLAN
- LAN
- BT LE
- WS2812B LED
- Inputs
- Outputs
- Buzzer
- CAN

Not tested yet:
- RTC

"""
import asyncio
import time
import os
import vfs

from aioespnow import AIOESPNow
from machine import Pin, PWM, Signal, I2C, SPI, SDCard, Encoder
from network import LAN, PHY_W5500
from neopixel import NeoPixel

# https://github.com/lewisxhe/PCF8563_PythonLibrary/blob/master/pcf8563.py
from pcf8563 import PCF8563 as PCF

from bt import ble_service
from umodbus.tcp import ModbusTCP

# https://github.com/straga/micropython-esp32-twai
import CAN

# TCA99554PWR is used to control the relay outputs
TCA_ADDR = 32
TCA_CONFIG = 0x03
TCA_OUTPUT = 0x01

# on/off for encoder input a/b
on = [1]
off = [0]

def irq_a1(pin):
    can.send(on if pin() else off, 0x10)

def irq_b1(pin):
    can.send(on if pin() else off, 0x11)

def irq_a2(pin):
    can.send(on if pin() else off, 0x12)

def irq_b2(pin):
    can.send(on if pin() else off, 0x13)

def irq_a3(pin):
    can.send(on if pin() else off, 0x14)

def irq_b3(pin):
    can.send(on if pin() else off, 0x15)

def setup_encoder() -> tuple[Encoder, Encoder, Encoder]:
    a1 = Pin(4, pull=Pin.PULL_UP)
    b1 = Pin(5, pull=Pin.PULL_UP)
    a2 = Pin(6, pull=Pin.PULL_UP)
    b2 = Pin(7, pull=Pin.PULL_UP)
    a3 = Pin(8, pull=Pin.PULL_UP)
    b3 = Pin(9, pull=Pin.PULL_UP)
    
    enc_x = Encoder(0, a1, b1, phases=4)
    enc_y = Encoder(1, a2, b2, phases=4)
    enc_z = Encoder(2, a3, b3, phases=4)
    
    a1.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=irq_a1)
    b1.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=irq_b1)
    a2.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=irq_a2)
    b2.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=irq_b2)
    a3.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=irq_a3)
    b3.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=irq_b3)
    
    return enc_x, enc_y, enc_z


def led():
    # LED at USB-C Port
    np = NeoPixel(Pin(38), 1)
    for c in [(255, 0, 0), (0, 255, 0), (0, 0, 255), (0, 0, 0)]:
        np[0] = c
        np.write()
        time.sleep_ms(150)


def init_i2c():
    # TCA: 32, RTC: 81
    return I2C(scl=Pin(41), sda=Pin(42), freq=400_000)

def init_outputs():
    # first set all outputs to 0
    i2c.writeto_mem(TCA_ADDR, TCA_OUTPUT, b'\x00')
    #then configure all gpios as outputs
    i2c.writeto_mem(TCA_ADDR, TCA_CONFIG, b'\x00')

def init_inputs():
    # GPIO-Inputs are from ESP32
    return [Signal(Pin(4 + n, mode=Pin.IN, pull=Pin.PULL_UP), invert=True) for n in range(8)]

def read_port(port) -> bool:
    """
    Read Port 1 - 8
    """
    return inputs[port - 1].value()

def set_output(port: int, value: bool):
    """
    Write to Port 1 - 8
    
    Chip: TCA99554PWR (I2C)
    """
    buffer = bytearray(1)
    i2c.readfrom_mem_into(TCA_ADDR, TCA_OUTPUT, buffer)
    
    if value:
        buffer[0] |= 1 << (port - 1)
    else:
        buffer[0] &= ~(1 << (port - 1))

    i2c.writeto_mem(TCA_ADDR, TCA_OUTPUT, buffer)

def init_lan():
    lan_spi = SPI(2, sck=Pin(15), mosi=Pin(13), miso=Pin(14), baudrate=20_000_000)
    lan_int = Pin(12, mode=Pin.IN)
    lan_sel = Pin(16, mode=Pin.OUT, value=1)
    lan_rst = Pin(39, mode=Pin.OUT)
    eth0 = LAN(spi=lan_spi, cs=lan_sel, int=lan_int, phy_type=PHY_W5500, phy_addr=0, reset=lan_rst)
    eth0.active(True)
    return eth0


def init_sdcard():
    return SDCard(
        slot=0,
        width=1,
        cmd=Pin(47),
        data=(Pin(45),),
        sck=Pin(48),
    )


def mount_sd(sd):
    os.mount(sd, "/sd")


def test1():
    # test 1
    for color in [(255,0,0), (0,255,0), (0,0,255)]:
        np[0] = color
        np.write()
        time.sleep(1)
    np[0] = (0,0,0)
    np.write()


def test2():
    for port in range(1, 9):
        if port > 1:
            set_output(port - 1, False)
        set_output(port, True)
        time.sleep(0.2)
    set_output(8, False)


async def recv_can():
    while True:
        if can.any():
            msg = can.recv()
            print("CAN:", msg)
            # CAN: (80, False, False, b'\x03')
            if msg[0] == 0x50 and len(msg[-1]) == 1:
                value = msg[-1][0]
                if value & 0x01:
                    print("Reset X Encoder")
                    enc_x.value(0)
                if value & 0x02:
                    print("Reset Y Encoder")
                    enc_y.value(0)
                if value & 0x04:
                    print("Reset Z Encoder")
                    enc_z.value(0)
            elif msg[0] == 0x51 and len(msg[-1]) == 1:
                value = msg[-1][0]
                for shift in range(8):
                    set_output(shift + 1, bool(value & (1 << shift)))
                    
        await asyncio.sleep(0.1)


async def send_can():
    while True:
        if can.info()["msgs_to_tx"] == 0:
            try:
                can.send([1,2,3], 0x82, timeout=1)
            except OSError:
                pass

        await asyncio.sleep(1)


def modbus_coils_set(address, val, reg_type):
    for addr, value in enumerate(val, address):
        print(f"Set coil {addr} to {value}")
        set_output(addr, value)


def modbus_coils_get(address, val, reg_type):
    print(address, val, reg_type)


async def espnow_client(espnow: AIOESPNow):
    print("Waiting for ESPNow messages")
    async for client, msg in espnow:
        if msg == b"ON":
            set_output(1, True)
        elif msg == b"OFF":
            set_output(1, False)


async def read_enc():
    last = None
    
    x_factor = 0.2989010989010989
    y_factor = 0.2974560673675727
    z_factor = 0.29498525073746323
    
    while True:
        current = (enc_x.value(), enc_y.value(), enc_z.value())
        
        if current != last:
            last = current
            print(f"X: {current[0] * x_factor:5.1f} mm | Y: {current[1] * y_factor:5.1f} mm | Z: {-current[2] * z_factor:5.1f} mm")
            if can.info()["msgs_to_tx"] == 0:
                try:
                    can.send([
                        current[0] & 0xFF, (current[0] >> 8) & 0xFF,
                        current[1] & 0xFF, (current[2] >> 8) & 0xFF,
                        current[2] & 0xFF, (current[2] >> 8) & 0xFF,
                        ],
                        0x20,
                        timeout=1,
                    )
                except OSError:
                    pass
                
        await asyncio.sleep_ms(100)


async def modbus_worker():
    modbus = ModbusTCP()
    modbus.setup_registers()
    modbus.bind("")
    
    for addr in range(1, 9):
        modbus.add_coil(
            addr,
            False,
            on_set_cb=modbus_coils_set,
            on_get_cb=modbus_coils_get,
        )
    
    while True:
        modbus.process()       
        await asyncio.sleep_ms(10)


async def main():
    print("Starting CAN receive task")
    asyncio.create_task(recv_can())
    print("Starting CAN send task")
    asyncio.create_task(send_can())
    
    print("Starting encoder read task")
    asyncio.create_task(read_enc())
    
    print("Starting BLE task")
    asyncio.create_task(ble_service(set_output))
    print("Starting ESPNow task")
    asyncio.create_task(espnow_client(espnow))
    
    print("Starting Modbus task")
    asyncio.create_task(modbus_worker())
    
    while True:
        await asyncio.sleep(5)



led()

# CAN blocks if msgs_to_tx == 2
can = CAN(0, tx=17, rx=18, mode=CAN.NORMAL, bitrate=2_000_000, extframe=0)
# CAN.NACK << test if this could help with blocking
# can = CAN(0, tx=17, rx=18, mode=CAN.NACK, bitrate=2_000_000, extframe=0)
enc_x, enc_y, enc_z = setup_encoder()

i2c = init_i2c()
inputs = init_inputs()
sd_card = init_sdcard()
mount_sd(sd_card)
init_outputs()
rtc = PCF(i2c)
lan = init_lan()
buzzer = PWM(Pin(46), freq=880, duty=0)
espnow = AIOESPNow()
espnow.active(True)
espnow.add_peer(bytes([255]*6))


register_definitions = {
    "COILS": {
        "COIL": {
            "register": 0,
            "len": 8,
            "val": 0
        }
    },
    "HREGS": {
        "EXAMPLE_HREG": {
            "register": 93,
            "len": 1,
            "val": 19
        }
    },
    "ISTS": {
        "EXAMPLE_ISTS": {
            "register": 67,
            "len": 1,
            "val": 0
        }
    },
    "IREGS": {
        "DIN": {
            "register": 0,
            "len": 1,
            "val": 0
        }
    }
}



print("Running mainloop")
asyncio.run(main())


Aber so schnell wie mit LOGO und RPi, kommt er damit nicht ans Ziel.
Sehe ich auch so. Da auf dem RPi ein OS ist, hat man viel mehr Möglichkeiten.
 
Zurück
Oben