Raspberry Pico Datenübertragung UART/RS485

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
f91w
User
Beiträge: 6
Registriert: Dienstag 7. September 2021, 00:38

Hallo an Alle!

Ich lese schon eine Weile hier mit und nun habe ich eine Frage. Wie in der Überschrift steht will ich mittels Pico Messwerte von
DS18B20 Fühlern weiterleiten. Dazu habe ich mir für den Pico RS485 Module gekauft: https://eckstein-shop.de/WaveShare-2-Ch ... T-To-RS485
Bisher habe ich mit Raspberry Pi und Arduino/Nano in C und Python ein wenig gebastelt. Für die UART-Geschichte habe ich die Beispiele von
Waveshare als Basis genommen und erweitert. Es klappt erstmal, aber vielleicht kann hier jemand ein paar Tips geben worauf ich
achten sollte oder was man besser machen kann. Es sind 2 Picos mit jeweils einem RS485-Modul.
Firmware ist "MicroPython v1.19.1 on 2022-06-18; Raspberry Pi Pico with RP2040"
Ich stelle meine Skripte mal hier ein.

RS485_send.py

Code: Alles auswählen

from machine import UART, Pin
import time

uart1 = UART(1, baudrate=115200, tx=Pin(4), rx=Pin(5))

#uart0 = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1))
a=0
#txData = b'RS485 send test...\r\n'
#uart1.write(txData)
print('RS485 send test...')
time.sleep(0.1)
while True:
    a=a+1
    if a > 4:
        a = 1
    time.sleep(0.5) 
    print ('request...')#shell output
    uart1.write("{}\r".format(a))
    time.sleep(0.1)
    rxData = bytes()
    while uart1.any() > 0:
        rxData += uart1.read(1)
    print('Sensor {}:'.format(a),rxData.decode('utf-8'))
 
RS485_receive.py

Code: Alles auswählen

from machine import UART, Pin
import time

sensorlist = [10, 20, 30, 40] #Sensorsimulation
#uart1 = UART(1, baudrate=115200, tx=Pin(4), rx=Pin(5))

uart1 = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1))
flag = 1
#txData = 'RS485 receive test...\r\n'
#uart1.write(txData)
print('RS485 receive test')
time.sleep(0.1)

def sensorsend(rxData):
    zahl = int(float(rxData))
    return(sensorlist[zahl - 1])
    
while True:
    rxData = bytes()
    while uart1.any() > 0:
        rxData = uart1.read()
        sensorwert = sensorsend(rxData)
        if(flag == 1):
            time.sleep(0.05)
            flag=0
        #uart1.write("{}".format(rxData.decode('utf-8')))
        uart1.write('{}'.format(sensorwert))    
        #print(rxData.decode('utf-8'))
        if(uart1.any()==0):
            uart1.write("\r")
            flag=1
mfg

f91w
__deets__
User
Beiträge: 14544
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dieses while uart.any()-Muster ist ein Problem, das solltest du dir abgewöhnen. Im ersten Skript führt das klar zu einem Fehlverhalten, wenn aus irgendwelchen Gründen eine Antwort verspätet kommt. Sowas modelliert man andersherum - blockieren bis Daten da sind, oder ggf. explizite Timeouts nach denen eine ausgebliebene Antwort zu einem Fehlerzustand führt.
f91w
User
Beiträge: 6
Registriert: Dienstag 7. September 2021, 00:38

Danke für die Antwort.
Ich habe leider keine Ahnung von UART und wenig von Python.
Daher stehe ich ein wenig im Wald. Das Problem mit "any" ist jetzt klar, aber
ich weiss nicht wie man es umgeht. Muss ich mich mal mit beschäftigen...

mfg

f91w
__deets__
User
Beiträge: 14544
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst gar nichts umgehen. Sondern einfach blockierend einlesen. Solange kein timeout angegeben ist, kehrt read auch nicht frühzeitig zurück.
f91w
User
Beiträge: 6
Registriert: Dienstag 7. September 2021, 00:38

Hallo,

ich habe jetzt die "while uart1.any()" Zeile rausgenommen und ein paar Kleinigkeiten geändert.
Wird wohl ne Weile dauern bis ich mich in die Materie eingearbeitet habe.

mfg

f91w
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Beim Sende-Skript: ob das UART-1 oder UART-0 ist, sollte nicht im Variablennamen stehen.
Statt einer while-Schleife und händischem Zählen sollte das eine for-Schleife innerhalb der while-Schleife sein.
Wenn Du ein Zeilenbasiertes Protokoll hast, solltest Du auch exakt eine Zeile lesen.
Statt format benutzt man heutzutage f-Strings.

Code: Alles auswählen

from machine import UART, Pin
import time

def main():
    uart = UART(1, baudrate=115200, tx=Pin(4), rx=Pin(5))
    print('RS485 send test...')
    time.sleep(0.1)
    while True:
        for sensor_nummer in range(1, 5):
            time.sleep(0.5) 
            print ('request...')
            uart.write(f"{sensor_nummer}\r\n".encode('utf-8'))
            time.sleep(0.1)
            sensor_value = uart.readline().decode('utf-8').strip()
            print(f"Sensor {sensor_nummer}: {sensor_value}")

if __name__ == "__main__":
    main()
Für's receive-Skript gilt das selbe, hier ist der Variablenname sogar falsch, da URT-0 an uart1 gebunden wird. Variablen sollten dann initialisiert werden, wenn sie gebraucht werden, nicht irgendwo am Anfang des Programms.

Da Du Ganzzahlen sendest, warum benutzt Du `float`?
`return` und `if` sind keine Funktionen, die Klammern also eher verwirrend. Das Initialisieren von rxData mit bytes ist überflüssig, da der Wert sowieso gleich wieder überschrieben wird.

Warum sendest Du den Zeilenumbruch nur manchmal?

Code: Alles auswählen

from machine import UART, Pin
import time

SENSORS = [10, 20, 30, 40] #Sensorsimulation

def sensorsend(sensor_number):
    return SENSORS[int(sensor_number) - 1]

def main():
    uart = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1))
    print('RS485 receive test')
    time.sleep(0.1)
    while True:
        sensor_number = uart.readline()
        sensor_value = sensorsend(sensor_number)
        time.sleep(0.05)
        uart.write(f"{sensor_value}\r\n".encode('utf-8'))


if __name__ == "__main__":
    main()
f91w
User
Beiträge: 6
Registriert: Dienstag 7. September 2021, 00:38

Zunächst mal Vielen Dank für alle Antworten.
Ich hatte das Projekt aus Zeitmangel ein wenig vernachlässigt.
Der Code ist wie er ist weil ich es nicht besser weiss und basiert auf dem Demo-Code
des Herstellers der Hardware.
Ich werde mal wieder dran arbeiten und mich dann melden.

mfg

f91w
f91w
User
Beiträge: 6
Registriert: Dienstag 7. September 2021, 00:38

Ich habe mal ein wenig mit den beiden Skripten herumgespielt. Es hat nicht sofort geklappt, ich musste
etwas basteln.
Senden:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  pico_uart_sende.py
#  
#  
#  
# Pico/Micropython Sendeskript

from machine import UART, Pin
import time

def main():
    uart = UART(1, baudrate=115200, tx=Pin(4), rx=Pin(5))
    print('RS485 send test...')
    time.sleep(0.1)
    while True:
        for sensor_nummer in range(1, 5):
            time.sleep(5) 
            print ('request...')
            uart.write(f"{sensor_nummer}\r\n".encode('utf-8'))
            time.sleep(0.1)
            rxData = uart.readline()
            if rxData is not None:    ###ohne Abfrage Fehler in der naechsten Zeile
                print(f'Sensor {sensor_nummer}: {rxData.decode('utf-8')}')
                #sensor_value = uart.readline().decode('utf-8').strip()
                #print(f"Sensor {sensor_nummer}: {sensor_value}")
            else:
                print("no data..")   

if __name__ == "__main__":
    main()

Empfangen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  pico_uart_empfang.py
#  
#  
#Pico/Micropython Empfangsskript aus Python Forum

from machine import UART, Pin
import time

SENSORS = [10, 20, 30, 40] #Sensorsimulation

def sensorsend(sensor_number):
    return SENSORS[int(sensor_number) - 1]

def main():
    uart = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1))
    print('RS485 receive test')
    time.sleep(0.1)
    while True:
        sensor_number = uart.read()   ###readline() wirft Fehler in sensorsend()
        #print(sensor_number)
        if sensor_number is not None:  ###Wenn "sensor_number==None" dann Fehler in sensorsend()
            sensor_value = sensorsend(sensor_number)
            time.sleep(0.05)
            uart.write(f"{sensor_value}\r\n".encode('utf-8'))
            print("data sent...", sensor_value)
        else:
        #    print("No requests received!")
            time.sleep(0.05)

if __name__ == "__main__":
    main()

In dieser Form laufen die beiden Skripte. Sonst gab es z.B Fehler wenn das Sendeskript keine Antwort bekam.
"SENSORS[int(sensor_number) - 1]" löst einen Fehler aus wenn "sensor_number" "None" ist.


mfg

f91w
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

`readline` ist trotzdem die richtige Methode, denn so liest Du ja im Zweifel nicht die komplette Zahl, sondern nur das, was zufällig gerade im Buffer ist. Wenn dann ein None zurückkommt, dann gab es einen Timeout, und wie man damit umgehen muß, das hast Du ja jetzt eingebaut.
f91w
User
Beiträge: 6
Registriert: Dienstag 7. September 2021, 00:38

Habe ich schon korrigiert, Danke.

mfg

f91w
Antworten