RobTillaart / RS485

Arduino library for RS485, half duplex communication
MIT License
39 stars 6 forks source link

Questions #12

Closed rozrabiak closed 1 year ago

rozrabiak commented 1 year ago

Hi, because I don't know how to add pull request I have some questions.

  1. Is this library compatible with STM32Duino - Serial1, Serial2 on STM32F4?
  2. Addressing of slaves and master are working now?
  3. Can I send & receive struct?
  4. what about collisions? For eg. slave1 send message and slave2 send message at this same time.
  5. checksum XOR must be implemented.

Maybe fork of this library (probably discontinued) https://github.com/rzeman9/Simple485

Thank's a lot for this work.

RobTillaart commented 1 year ago

Thanks for these questions. Has been some time since I used it. Some quick answers

  1. Not tested, from my head I expect it should work.
  2. Need to check this one
  3. Yes, it is not different from sending a struct over Serial.
  4. Not implemented. Note that rs485 is not equal to modbus et al.
  5. Could be, there are many things possible to extend the lib.
RobTillaart commented 1 year ago
  1. did a quick test with STM32F0 series, did compile with Serial1 an example. Serial2 and Serial3 failed so I guess it depends on the exact board used (and maybe the compile environment)
RobTillaart commented 1 year ago

Add 3. Depends on the size of the struct. This library has some protocol that has a 48 byte buffer so it should fit in (do not forget overhead etc).. Probably it makes sense to define your own protocol that runs on top of the basic sending of bytes.

Add 5 Checksum XOR is a weak checksum 0.4% chance of missing errors. Better use CRC16 which fails 1 in 65535 or CRC32.

Add 4. Protocol design is pretty difficult to get robust and not loose performance. Read a book about the implementation of TCP/IP which is a very reliable protocol.

rozrabiak commented 1 year ago

Now I test on BlackPill (STM32F401CCU6) using Serial1. Code for STM32 (and code for Arduino nano below):

#include <Arduino.h>
#include <RS485.h>

#define rs485Tx HIGH
#define rs485Rx LOW
#define rs485ReDe PB15

const uint8_t devID = 1;
const char msg[] = "23";
RS485 rs485(&Serial1, rs485ReDe, devID);
int status;

void setup() {

  Serial.begin(9600);
  Serial1.begin(9600);
  rs485.setMicrosPerByte(9600);

  delay(2000);
  Serial.println("Start");
  Serial.println("PCF8574");

}

void loop() {

rs485.send(3, (uint8_t *)msg, strlen(msg));

delay(100);

if(rs485.available() > 0)
{
    status = rs485.read();
}

if(status > 1)
{
    Serial.print("status ");
    Serial.println(status);
}

    Serial.print("devID ");
    Serial.println(rs485.getDeviceID());

delay(2000);
}
#include <Arduino.h>
#include <RS485.h>

//dla rs485
const uint8_t devID = 3;

#define rs485Tx HIGH
#define rs485Rx LOW
#define rs485ReDe 2
RS485 rs485(&Serial, rs485ReDe, devID);
int status;
const char msg[] = "52";

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  delay(100);
  Serial.println("sterownik kurnika");
  rs485.setMicrosPerByte(9600);

}

void loop()
{
  // put your main code here, to run repeatedly:

if(rs485.available() >0)
{
  status = rs485.read();

}

Serial.print("status ");
Serial.println(status); //always 0
    Serial.print("devID ");
    Serial.println(rs485.getDeviceID());

if(status == 23)
{
  rs485.send(1, (uint8_t *)msg, strlen(msg));
}

delay(2000);

}

Communication not working for me. On Arduino Nano "status" always return integer from 1 to 170 not what I send from STM32. No one library for rs485 works between Nano and STM32, I tested 4 libraries :-)

Have nice day!

RobTillaart commented 1 year ago

mmm, getting too late now, can't think properly, sorry.

RobTillaart commented 1 year ago

No one library for rs485 works between Nano and STM32, I tested 4 libraries :-)

I do not have the STM hardware to verify, this library worked between Nano's. to be continued later this week.

rozrabiak commented 1 year ago

Rob, receiving using this code:

if (rs485.receive(ID, arr, len))
  {
    arr[len] = 0;
    Serial.print("RECV:\t");
    Serial.print(ID);
    Serial.print("\t");
    Serial.println((char*) arr);
  }

Works with STM32 & Arduino Nano.

Thank's a lot! Have a nice day.

RobTillaart commented 1 year ago

(updated the code tags in your posts)

Works with STM32 & Arduino Nano.

If your problem is solved now, you may close the issue.

Thank's a lot!

you're welcome

rozrabiak commented 1 year ago

My luck was too short... Sending from stm32f4 to arduino nano works great. But when I'm trying to send from NANO to stm32 - stm32 not receiving any data.

So, i just try to add delay(100);

in function:

void RS485::send(uint8_t receiverID, uint8_t msg[], uint8_t len)
{
  uint8_t CHKSUM = 0;
  setTXmode();    //  transmit mode
delay(100);    //ADDED
  _stream->write(SOH);
  _stream->write(receiverID);  //  TO
  _stream->write(_deviceID);   //  FROM
  _stream->write(len);         //  LENGTH BODY
  _stream->write(STX);
  for (int i = 0; i < len; i++)
  {
    _stream->write(msg[i]);
    CHKSUM ^= msg[i];          //  Simple XOR checksum.
  }
  _stream->write(CHKSUM);
  _stream->write(ETX);
  _stream->write(EOT);
  delayMicroseconds((len + 8 + 2) * _microsPerByte);  // + 2 to be sure...
delay(100);   //ADDED
  setRXmode();
}

but it's help only for a few minutes, I don't know why, but after some time - stm32 not receiving any data.. :-( I'm sending data every 1500ms from NANO to stm32.

Any ideas?

[EDIT]: so, I removed

delay(100);

And added (after last write(EOT))

_stream->flush();

and now, from sended 147 messages only 5 was received by stm32 :-(

RobTillaart commented 1 year ago

Are you using a software serial on the STM32 side? RS485 works most reliable with a hardware serial (at least for receive, send() works fine with SWW).

Every node that is not sending should be in the receive mode.

Can you try to get it working with two NANO's?

rozrabiak commented 1 year ago

I'm tired. That was my mistake I'm running code in loop without any millis/delays. Communication works without problems. But _stream.flush(); - must be in code, without there is no communication. I'm not using software serial. On STM32 I'm using Serial1 for rs485, Serial for monitor on PC. On nano I'm sharing Serial for both.

But... when I'm sending structure like below - float values OR integer values are always received as 0.00 or 0 (zero). When I'm trying to send structure with String - program on receiver hangs. Is there any solution?

Thank's a lot for Your patience.

Sender:

struct sendPacket
{
  bool ssrState;    //ssr relay state
  bool socketState; //socket relay state
  bool mOkno;       //small windows
  bool dOkno;       //big window
  bool mDrzwi;      //small door
  bool dDrzwi;      //big door
  float temp = 25.22;       //temperature in chicken house
  int autoGrzanie = 0;  //auto heat
  float setTemp = 10;   
};

sendPacket _sendPacket;

rs485.send(1, (uint8_t*)&_sendPacket, sizeof(_sendPacket));

Receiver:


struct rcvKurPacket
{
  bool ssrState;    //ssr relay state
  bool socketState; //socket relay state
  bool mOkno;       //small windows
  bool dOkno;       //big window
  bool mDrzwi;      //small door
  bool dDrzwi;      //big door
  float temp;       //temperature in chicken house
  int autoGrzanie;  //auto heat
  float setTemp;  
};

rcvKurPacket _rcvKurPacket;

if (rs485.receive(ID, arr, len))
   {
    arr[len] = 0;
    rcvKurPacket* dtrs = reinterpret_cast<rcvKurPacket*>(arr);
    Serial.print("RECV:\t");
    Serial.print(ID);
    Serial.print("\t");
    Serial.println((char*) arr);
    Serial.print("dDrzwi ");
    Serial.println(dtrs->dDrzwi);
    Serial.print("mDrzwi ");
    Serial.println(dtrs->mDrzwi);
    Serial.print("mOkno ");
    Serial.println(dtrs->mOkno);
    Serial.print("dOkno ");
    Serial.println(dtrs->dOkno);
    Serial.println(dtrs->temp);
    }

EDIT: I try to change buffer size - nothing happens, float are 0.00 :-( uint8_t _buffer[100]; // internal receive buffer

RobTillaart commented 1 year ago

I'm tired.

Happens to everyone,

But... when I'm sending structure like below - float values OR integer values are always received as 0.00 or 0 (zero). When I'm trying to send structure with String - program on receiver hangs. Is there any solution?

make a HEX dump of what is received to analyze

if (rs485.receive(ID, arr, len))
{
  Serial.print(len);  //  should be 16..20 or so
  Serial.print("\t")
  for (int i = 0; i< len; i++)
  {
    if (arr[i] < 0x10) Serial.print(0, HEX);
    Serial.print(arr[i], HEX);
    Serial.print("\t");
  }
  Serial.println();
}

you might need to test on both platforms

rozrabiak commented 1 year ago

HEX dump:

16       00      01      01      01      00      01      AE    47       B3      41      FF      00      00      00      7F      43

and sizeof:

arduino nano:
float 4
bool 1
int 2

stm32:
float 4
bool 1
int 4

struct receiving on stm32f4.

EDIT: I try to send float without struct:

      float test = 25.47;
      rs485.send(1, (uint8_t*)&test, sizeof(test));

and receive:

    float* dtrs = reinterpret_cast<float*>(arr);
    float floatOut = dtrs[0];

That's works.

RobTillaart commented 1 year ago

Note you have a problem with sending the int. It is different in size on the two platforms.

You can replace int with int32_t which is forced 32 bit.

RobTillaart commented 1 year ago

Way past midnight, to be continued tomorrow

rozrabiak commented 1 year ago

So, I make some tests..

FIRST TRY:

struct sndPacket
{
  float temp;       //temperature in chicken house
  float setTemp = 10;   // temperatura zadana
  int autoGrzanie = 65;  //auto heat
  bool mDrzwi;      //small door
  bool dDrzwi;      //big door
};

 sndPacket _sndPacket;

void setup()
{
    _sndPacket.temp=25;
    _sndPacket.setTemp = 32;
    _sndPacket.autoGrzanie = 65;
    _sndPacket.dDrzwi = true;
    _sndPacket.mDrzwi = false;
}

SERIAL MONITOR ON STM32:

00:45:14.947 > dDrzwi 0
00:45:14.947 > mDrzwi 0
00:45:14.947 > 25.00
00:45:14.947 > 32.00
00:45:14.947 > 16777281

bool and int are not correct values.

SECOND TRY:

struct sndPacket
{
  float temp;       //temperature in chicken house
  float setTemp = 10;   // temperatura zadana
  int autoGrzanie = 65;  //auto heat
  byte mDrzwi;      //small door
  byte dDrzwi;      //big door
};
sndPacket _sndPacket;

void setup()
{
    _sndPacket.temp=25;
    _sndPacket.setTemp = 32;
    _sndPacket.autoGrzanie=65;
    _sndPacket.dDrzwi = 1;
    _sndPacket.mDrzwi=0;
}

SERIAL MONITOR ON STM32:

07:57:47.432 > dDrzwi 0
07:57:47.432 > mDrzwi 0
07:57:47.432 > 25.00
07:57:47.432 > 32.00
07:57:47.432 > 16777281

byte and int are not correct.

THIRD TRY:

struct sndPacket
{
  byte mDrzwi;      //small door
  byte dDrzwi;      //big door
};

 sndPacket _sndPacket;

void setup()
{
    _sndPacket.dDrzwi = 1;
    _sndPacket.mDrzwi = 0;
}

SERIAL MONITOR ON STM32:

08:02:11.531 > dDrzwi 1
08:02:11.531 > mDrzwi 0

all correct.

FOURTH TRY:

struct sndPacket
{
  float temp;       //temperature in chicken house
  float setTemp = 10;   // temperatura zadana
  int autoGrzanie = 65;  //auto heat
};

 sndPacket _sndPacket;

void setup()
{
    _sndPacket.temp=25;
    _sndPacket.setTemp = 32;
    _sndPacket.autoGrzanie=65;
}

SERIAL MONITOR ON STM32:

00:45:14.947 > 25.00
00:45:14.947 > 32.00
00:45:14.947 > 65

all correct.

FIFTH TRY:


struct sndPacket
{
    float temp;       //temperature in chicken house
    float setTemp = 10;   // temperatura zadana
    int autoGrzanie = 65;  //auto heat
    float mDrzwi;      //small door
    int dDrzwi;      //big door
};

sndPacket _sndPacket;

void setup()
{
    _sndPacket.temp = 25;
    _sndPacket.setTemp = 32;
    _sndPacket.autoGrzanie = 65;
    _sndPacket.dDrzwi = 125;
    _sndPacket.mDrzwi = 256;
}

SERIAL MONITOR ON STM32:

08:15:32.351 > dDrzwi 0
08:15:32.351 > mDrzwi 0.00
08:15:32.351 > temp 25.00
08:15:32.351 > setTemp 32.00
08:15:32.352 > autoGrzanie 65

dDrzwi & mDrzwi have incorrect values.

So, buffer size now I have hardcoded to 200:

  uint8_t _buffer[200];  //  internal receive buffer

and problem still exists. Have a nice day!

RobTillaart commented 1 year ago

Good morning The size of the buffer is not problem. The data is send as a stream of bytes, and every field of the struct starts at a certain position. This position depends on the size of the data types on the specific processor. That is why I said you should use int32 instead of int. That has the same size on all platforms.

rozrabiak commented 1 year ago

Rob! You write about int32_t in comment !! And I forgot about that :-( I change int to int32_t and it's work's! It's time to rest. Bike ride - it's a good idea. Thank's a lot! Have nice day.

RobTillaart commented 1 year ago

As I said before, designing a protocol is harder than most people think.

These are the most common areas that need attention (There are more, check the OSI 7 layers model.)

variable size

as discussed here

byte order

You have an int32 that you know that is 4 bytes, but in which order are those 4 bytes send (12 possibilities, in real live I have seen 3 or 4 version) This is seen sometimes e.g. in RGB images where colors suddenly swap.

bit order

OK and the bytes themselves, are they send MSG or LSB.

alignment

Some machines want every variable address at an even address or at an quad (div4) address

encoding 1

Are integers codes as binary (as usual) or as BCD (mainframe) Which char set is used?

encoding 2

Are Gray codes used? (prevents long runs of 0 or 1 bits) Is there an error detection and or correction scheme. Is there a compression scheme?

transmission errors

What is the chance of bit error for this communication line?

Maybe I should add this in a separate document one time.

rozrabiak commented 1 year ago

Rob, my programming skills are very poor. But I like electronics and programming.

What you do for the community, what others do by writing libraries is amazing and deserves great respect, appreciation! Again - thanks for your help!

RobTillaart commented 1 year ago

Rob, my programming skills are very poor. But I like electronics and programming.

I started when I was 8 or so with (hidden) electric switches and lights, and gradually knowledge build up. My first LED survived for 1 millisecond (no resistor) and several have followed, including displays, forgetting the diode near a relay, using wrong pins and debug until morning light etc. Programming is a similar story, "know how" is often "know how not to do things" A favorite quote: "You should make a mistake only once as there are enough others too choose from next time."

Programming is best done with pen and paper, draw the flow of information, timing diagrams etc Decompose the problem into parts and give every part its responsibility. Libraries are sort "software IC's", defining the interface is most important, how things are handled inside is another abstraction layer. Similar for software functions, you define what to put in, and what comes out and how it is implemented comes later.

Again - thanks for your help!

You're welcome