''' A program supposed to run on two RP2040 connected over two channel of an RS485 bus over TTL-UART-to-RS485 converters, and illustrates a simple ping-pong. Copyright (C) 2024 Benjamin Burkhardt This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ''' from machine import UART, Pin import time # CHANGE THIS to 'True' on one of the machines (as it # tells the rp2040 to do the first step in ping-pong communication ;) INITIALIZER = True # define the UART in-/outputs, named after their usage as either send or receive channels... # ...depending on wether this is the INITIALIZER or not. # sure, the in and out pins could be defined in other ways also, but this allows easy customizability... # ...as just one variable needs to be changed. print('[UART] Initializing UART for two channels...') if INITIALIZER: # then read on ch0 and write on ch1 receiver = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1)) sender = UART(1, baudrate=115200, tx=Pin(4), rx=Pin(5)) else: sender = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1)) receiver = UART(1, baudrate=115200, tx=Pin(4), rx=Pin(5)) print('[UART] Initialized UART for two channels.') # initialize the read/write channels and set their value print('[UART] Setting output/input for each channel...') if INITIALIZER: # then enable read on ch0 and write on ch1 receiver_channel_mode = Pin(2, Pin.OUT) # define pinout for the receiver/driver enable pins of channel 0 (shortened together) sender_channel_mode = Pin(3, Pin.OUT) # define pinout for the receiver/driver enable pins of channel 1 (shortened together) else: sender_channel_mode = Pin(2, Pin.OUT) receiver_channel_mode = Pin(3, Pin.OUT) receiver_channel_mode.low() sender_channel_mode.high() print(f'[UART] Set output/input for each channel. Pin 2 is now {"low" if INITIALIZER else "high"} (CH0 as {"input" if INITIALIZER else "output"}), Pin 3 is now {"high" if INITIALIZER else "low"} (CH1 as {"output" if INITIALIZER else "input"}).') # some logging print('[INFO] Set up RS485 ping-pong.') # now, the REAL PING-PONG mechanism begins # the counter variable will be increased with every ping # received (which is a pong from the other side which resulted out of a ping there ;) a = 0 # make the first step if INITIALIZER is True (see above at declaration) if INITIALIZER: sender.write(f'{a}\r\n') sender.flush() print(f'[UART] Sent initial ping: {a}') print('[INFO] Now waiting for incoming pings/pongs.') # now listen for a pong from the other side and answer to it rxDataConcatenated = '' # to combine partial received data to one full packet (actually just a string that ends with \n :) while True: rxData = receiver.readline() if rxData is not None: rxDataDecoded = rxData.decode('utf-8') if not '\n' in rxDataDecoded: # if the received data is not complete (does not end with a \n), just save the received part rxDataConcatenated += rxDataDecoded else: # if it's complete now, add the received part to the potentially saved parts and send the pong rxDataConcatenated += rxDataDecoded try: a = int(rxDataConcatenated) print(f"[UART] Received ping: {a}") # send pong after some waiting (could also be left out completely!) time.sleep(0.2) a += 1 sender.write(f'{a}\r\n') sender.flush() print(f'[UART] Sent pong: {a}') except: receiver_error_details = repr(rxDataConcatenated) print(f'[UART] Received invalid data? Wait until something good comes in! (received "{receiver_error_details}")') rxDataConcatenated = '' # reset the received parts storage (see comments above!)