Arduino uno RS485 communication

Thread Starter

rt694157

Joined Dec 15, 2019
78
I am looking for a sample code which can read the sensor via Rs485 communication

For example

rs485 .png

I just want to understand code for RS485 communication
 

MrChips

Joined Oct 2, 2009
26,077
RS-485 communication is no different from serial UART communication.
State the model number of the device and post links to the description and user manual.
 

John P

Joined Oct 14, 2008
1,973
RS-485 communication is no different from serial UART communication.
State the model number of the device and post links to the description and user manual.
That really isn't true. RS-485 is only half-duplex, which means you have to enable transmitting to send anything, and disable it to receive. The connections for doing that are shown in the original diagram. It's easy enough to enable transmission--just set the control line high, then start sending characters--but knowing when to set it low again can be tricky. If you know that the unit on the other end of the line won't make its own transmission for some time, you can just use a timeout after the processor transmits. But if it might respond quickly, you may have to watch for the USART Transmit Complete flag, or get an interrupt from it, and change from transmit to receive when it occurs. It's the TXC0 bit in the UCSR0A register.
 

dendad

Joined Feb 20, 2016
4,134
And you will really need to look into what protocol the sensor talks. Is it just plain serial, MODBUS, custom or other.
Getting the RS485 code working is just your first step.
 

Beau Schwabe

Joined Nov 7, 2019
116
Something to note with the Arduino serial communication that might save you a lot of headache.
In the infinite wisdom of Arduino developers, in effort to make things more streamline from a code perspective.
When you send serial data it loads a buffer that is sent in the background allowing you to perform other tasks with your code.
How does this affect RS485? When you enable the RS485 transmitter pin, send your data, and disable the RS485 transmitter pin, the data buffer might not be done sending data by the time you disable the RS485 transmitter pin. This would effectivly result in cutting off the end of your data packet. One solution is to wait after you transmit before disabling the pin. Or monitor the outgoing buffer and disable the transmit when it is done. (The latter is a bit more tricky, and I don't have an elegant solution. Perhaps someone here could address this? )
 

MrChips

Joined Oct 2, 2009
26,077
TS did not mention anything about transmitting a signal, only receiving data from a module. We know nothing about how the module communicates and what protocol is used.
 

Sensacell

Joined Jun 19, 2012
2,997
Half-Duplex is not trivial, managing the switchover from TX to RX requires some tricky timing.
I see this detail overlooked constantly - the consequence is major frustration.

The trick I use with on PIC chip is to have the RX active when transmitting, then it's easy to know exactly when the last character has been sent using the RX interrupt.
You just throw the received data away, but count the characters to know when the whole packet is complete.
 

John P

Joined Oct 14, 2008
1,973
There is yet another issue specific to the Arduino. The serial port is shared between the external pins and the USB port, and if you connect to the RX pin, anything you add there have priority over the USB link unless you put the connected item in Hi-Z mode. If you're using it for RS-485 and you need to reprogram the Arduino, or if you want to use the USB port to connect a computer, it needs some design work to get it functioning. You might not be able to connect the DE and RE pins together, for instance. When USB is in use, you'd want them both to be inactive; you wouldn't be able to simply say "When I'm not transmitting I'll receive".
 
Last edited:

Beau Schwabe

Joined Nov 7, 2019
116
Use alternate pins for RS485 instead of the dedicated pins tied to the USB. Keep the receive enabled at all times and just control the transmit.... 1651246686686.png
 

John P

Joined Oct 14, 2008
1,973
I didn't look carefully enough at the circuit diagram. The original posting's text didn't mention it, but the diagram shows an Arduino Mega, which has multiple serial ports, so the issue of sharing a port between USB and RS-485 isn't a concern at all. Enabling and disabling the bus transmission still has to happen though (unless it's receive-only, which wasn't mentioned).
 

nsaspook

Joined Aug 27, 2009
9,717
Something to note with the Arduino serial communication that might save you a lot of headache.
In the infinite wisdom of Arduino developers, in effort to make things more streamline from a code perspective.
When you send serial data it loads a buffer that is sent in the background allowing you to perform other tasks with your code.
How does this affect RS485? When you enable the RS485 transmitter pin, send your data, and disable the RS485 transmitter pin, the data buffer might not be done sending data by the time you disable the RS485 transmitter pin. This would effectivly result in cutting off the end of your data packet. One solution is to wait after you transmit before disabling the pin. Or monitor the outgoing buffer and disable the transmit when it is done. (The latter is a bit more tricky, and I don't have an elegant solution. Perhaps someone here could address this? )
I usually program this (ring-buffered serial data) in a serial data SEND/RECV state machine loop so there are no busy waits for T/R delay timing or serial data.

For example on the PIC32 with Harmony serial drivers you need to be sure to check the shift register flag for correct RS485 duplex switching.

Simple modbus master functions
C:
/*
 * check if we are done with interrupt background buffered transmission of serial data with FIFO
 * 
 * TRMT: Transmit Shift Register is Empty bit (read-only)
 * 1 = Transmit shift register is empty and transmit buffer is empty (the last transmission has completed)
 * 0 = Transmit shift register is not empty, a transmission is in progress or queued in the transmit buffer
 * 
 * • 8-level deep First-In-First-Out (FIFO) transmit data buffer, • 8-level deep FIFO receive data buffer
 * Interrupt is generated and asserted while the transmit buffer is empty
 * 
 * so this will return 'true' after the buffer is empty 'interrupt' and after the last bit is on the wire
 */

static bool u6_trmt(void)
{
    return !(U6STA & _U6STA_TRMT_MASK); // note, we invert the TRMT bit so it's true while transmitting
}

// switch RS transceiver to transmit mode and wait if not tx

static void half_dup_tx(bool delay)
{
    if (DERE_Get()) {
        return;
    }
    DERE_Set(); // enable modbus transmitter
    if (delay) {
        delay_ms(2);
    }
}

// switch RS transceiver to receive mode and wait if not rx

static void half_dup_rx(bool delay)
{
    if (!DERE_Get()) {
        return;
    }
    if (delay) {
        delay_ms(2);
    }
    DERE_Clear(); // enable modbus receiver    
}

// ISR function for TMR9

void timer_2ms_tick(uint32_t status, uintptr_t context)
{
    M.clock_500hz++;
    M.clock_10hz++;
}

uint32_t get_500hz(uint8_t mode)
{
    static uint32_t tmp = 0;

    if (mode) {
        return tmp;
    }

    tmp = M.clock_500hz;
    return tmp;
}

void clear_500hz(void)
{
    M.clock_500hz = 0;
}
Simple modbus master state machine code fragment
C:
int8_t master_controller_work(C_data * client)
{
// ...
    case INIT:
        client->trace = 7;
        /*
         * MODBUS master query speed
         */
#ifdef    FASTQ 
        if (get_10hz(false) >= CDELAY) {
#else
        if (get_2hz(FALSE) >= QDELAY) {
#endif
            half_dup_tx(false); // no delays here
            M.recv_count = 0;
            client->cstate = SEND;
            clear_500hz();
            client->trace = 71;
        }
        break;
    case SEND:
        client->trace = 8;
        if (get_500hz(false) >= TDELAY) {  // RS485 duplex delay state machine counter timer
            client->trace = 9;
            UART6_Write((uint8_t*) & cc_buffer, client->req_length);
            UART3_Write((uint8_t*) "M\r\n", 3); // MODBUS trace signal to serial debug port
            client->trace = 91;
            client->cstate = RECV;
            clear_500hz(); // state machine execute background timer clear
            client->trace = 10;
        }
        break;
    case RECV:
        client->trace = 11;
        if (u6_trmt()) { // check for serial UART transmit shift register and buffer empty
            clear_500hz(); // keep clearing timer
        }
        if (get_500hz(false) >= TDELAY) { //  // RS485 duplex delay state machine counter timer
            uint16_t c_crc, c_crc_rec;

            client->trace = 12;
            BSP_LED3_Set();
            half_dup_rx(false); // no delays here

            /*
             * check received response data for size and format for each command sent
             */
            switch (client->modbus_command) {
            case G_SET: // check for controller error codes
                client->trace = 13;
                client->req_length = sizeof(i400_freset);
// ...
}
PXL_20220429_183041152.jpg
 
Top