Hi,
Following the serial basics tutorials
I'm trying to receive a string of bytes via RS485 that includes HEX 00 (null character).
I assume that I need to use Serial.readBytes() instead of Serial.read, but cant get it to work
An example of the string I need to read is:
53 54 3C 10 01 00 08 62 75 74 74 6F 6E 31 02 3E 45 54 62 01
The example given works, but stops reading after the 01, presumably because the next byte is a null character.
I modified the example as follows, but the compiler spits back the Serial1.readBytes call and I can't figure out why. I'm sure its something simple.
I changed the char array to byte, as well as the rc buffer.
// Example 2 - Receive with an end-marker
#include <AST_RS485.h> //RS485 library for hardware
const byte numChars = 60;
int readLen = 1;
//char receivedChars[numChars]; // an array to store the received data
byte receivedChars[numChars]; // Changed to byte
byte dummy[numChars];
boolean newData = false;
void setup() {
rsInit(); // Initialise hardware RS485 port (pulls /RE and /SHDN high to enable transciever)
Serial.begin(115200);
Serial1.begin(115200); // Hardware UART tied to RS485 transciever
Serial.println("<Arduino is ready>");
}
void loop() {
recvWithEndMarker();
showNewData();
}
void recvWithEndMarker() {
static byte ndx = 0;
//char endMarker = '\x3E';
//char endMarker = '\0';
//char endMarker = '\n';
//char rc;
char endMarker = '>';
byte rc;
while (Serial1.available() > 0 && newData == false) {
//rc = Serial1.read();
Serial1.readBytes(rc,readLen); // <<- throws error "Compilation error: no matching function for call to 'readBytes(byte&, int&)'"
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
Serial.println("end");
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
}
}
}
void showNewData() {
if (newData == true) {
Serial.print("This just in ... ");
Serial.write(receivedChars,60);
newData = false;
}
}
Serial.read() can read NULL characters. It can read any value in fact.
I believe that example will read all the characters, but the print will stop on the NULL character, because "strings" in C are expected to be NULL terminated.
The DATA payload is variable length, as given by LEN.
I only need up to the end of the DATA section. I'm trying to terminate on '>'
The current code below works the first time, however on subsequent reads the leftover "ET" and the CRC are printed at the beginning of the buffer.
// Example 2 - Receive with an end-marker
#include <AST_RS485.h>
const byte numChars = 30;
char receivedChars[numChars]; // an array to store the received data
boolean newData = false;
void setup() {
rsInit(); // Initialise rs485 port
Serial.begin(115200);
Serial1.begin(115200); // Hardware UART tied to RS485 transciever
Serial.println("<Arduino is ready>");
}
void loop() {
recvWithEndMarker();
showNewData();
}
void recvWithEndMarker() {
static byte ndx = 0;
char endMarker = '\x3E';
//char endMarker = '\0';
//char endMarker = '>';
//char endMarker = '\n';
char rc;
//byte rc;
while (Serial1.available() > 0 && newData == false) {
rc = Serial1.read();
//Serial1.readBytes(dummy,1);
//Serial1.readBytes(rc,1);
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
}
}
}
void showNewData() {
if (newData == true) {
//Serial.print("This just in ... ");
//Serial.write(receivedChars,30);
for (int i = 0; i <= 29; i++)
//Serial.write(receivedChars[i],1);
Serial.write(receivedChars[i]);
newData = false;
}
}
He did, and I changed the printout routine as shown above to print the buffer byte by byte, now I'm just trying to figure out why the portion of the data after the termination character is ending up in the beginning of the array on the next transmission.
I don't need to terminate on the '>', receiving the entire transmission into the buffer, including the CRC would be OK/preferred.
I just used '>' ('\x3E') as it's predictable and unique near the end of the string.
try scanning for the start marker '>', read the frame then check for the end marker - this makes sure it is a valid frame, e.g.
void recvWithEndMarker() {
char startMarker = 0x3C;
char endMarker = 0x3E;
while (Serial1.available() > 0)
if (Serial1.read() == startMarker) {
Serial.println("\nstart marker found");
int cmd = Serial1.read() << 8 | Serial1.read();
Serial.printf("cmd 0x%x\n", cmd);
int len = Serial1.read() << 8 | Serial1.read();
Serial.printf("len %d\ndata ", len);
for (int i = 0; i < len; i++)
Serial.printf("0x%x ", Serial1.read());
if (Serial1.read() == endMarker)
Serial.println("\nend marker found");
else Serial.println("\nend marker NOT found");
}
}
with the data stream "53 54 3C 10 01 00 08 62 75 74 74 6F 6E 31 02 3E 45 54 62 01" the serial monitor should display
start marker found
cmd 0x1001
len 8
data 0x62 0x75 0x74 0x74 0x6f 0x6e 0x31 0x2
end marker found
EDIT: if the the end marker is found OK you can then check the CRC
if the end marker is not found or the CRC fails start scanning again
in a more sophisticated system you could buffer the received bytes, on end marker/CRC fail you goback and start scanning from the character following the start marker
Thank you for the link to the sample code.
I had seen other stone sample code (including what shipped on a thumbdrive with the HMI) that did not compile.
I was able to use parts of that code to get a minimal send / receive framework working.
I'm sure it's not the most elegant code, but it's working: edit: added for loop in main loop to test dynamic updating of label
#include <AST_RS485.h>
#include <ArduinoJson.h>
const int HMI_MAX_DATA_LEN = 256;
byte data[HMI_MAX_DATA_LEN];
String str;
struct hmi_message {
uint16_t cmd; // Stone HMI CMD code
uint16_t len; // Stone HMI widget data len; only the data len
uint8_t widget[64]; //Stone HMI widget name
uint8_t data[HMI_MAX_DATA_LEN]; // Stone HMI cmd data,data type is int float or text
uint16_t crc; // Stone HMI cmd CRC value
} hmi_msg;
void setup() {
rsInit(); // Initialise rs485 port
Serial.begin(115200);
Serial1.begin(115200); // Hardware UART tied to RS485 transciever
}
void loop() {
Uart_HMI_Data_Receive();
for(byte i = 5; i <= 20; i++){
char tempChar[3];
String(i).toCharArray(tempChar, 3);
HMIsend("set_text", "label", "label1", tempChar);
delay(200);
}
}
uint16_t calculateCRC16Modbus(const uint8_t *data, size_t length)
{
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < length; i++) {
crc ^= (data[i]);
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
void Uart_HMI_Data_Receive()
{
int index=0;
unsigned int length =0; //The length of the complete data frame
int start = 0; //This parameter is used to determine the frame header(ST<) length
int end =0; //This parameter is used to determine the frame end(>ET) length
int crc_num = 0; //Used to determine whether two CRC check bits are received
while(Serial1.available()>0){
byte receiveByte = Serial1.read();
if(start==0&&receiveByte==0x53){
start = 1;
data[index++]=0x53;
}
else if(start==1&&receiveByte==0x54){
start = 2;
data[index++]=0x54;
}
else if(start==2&&receiveByte==0x3C){
start = 3;
data[index++]=0x3C;
}
else if(start==3&&receiveByte!=0x3E&&end==0){
data[index++] = receiveByte;
}
else if(start==3&&receiveByte==0x3E){
end = 1;
data[index++] = 0x3E;
}
else if(end==1&&receiveByte==0x45)
{
end = 2;
data[index++]=0x45;
}
else if(end==2&&receiveByte==0x54){
end = 3;
data[index++]=0x54;
}
else if(end==3&&crc_num<=2){
data[index++] = receiveByte;
crc_num++;
if(crc_num==2)
{
break;
}
}
else{
start = 0;
end = 0;
index = 0;
}
}
//Print the received data
if (index > 0) {
length = index;
for (int i = 0; i < length; i++) {
Serial.print(data[i],HEX);
Serial.print(" ");
}
Serial.println();
}
uint16_t expectedCRC = (data[length-2] << 8) | data[length-1]; //The received CRC check bit
uint16_t calculatedCRC = calculateCRC16Modbus(data, length-2); //The CRC check bit is calculated from the received data
//Print data message
if (length>0 && calculatedCRC == expectedCRC) {
Serial.println("CRC Check success!");
uint32_t num = data[3]<<8 | data[4];
Serial.print("CMD:");
Serial.print("0x");
Serial.println(num,HEX);
uint16_t data_length = data[5]<<8 | data[6];
Serial.print("DATA_LEN:");
Serial.print("0x");
Serial.println(data_length,HEX);
Serial.print("HEX_DATA: ");
for(int i=7;i<data_length+7;i++)
{
Serial.print(data[i],HEX);
Serial.print(" ");
}
Serial.println();
Serial.print("String_Data: ");
for(int i=7;i<data_length+7;i++)
{
Serial.print(char(data[i]));
}
Serial.println();
Serial.println();
//Store data to a structure
hmi_msg.len = data_length;
hmi_msg.crc = expectedCRC;
hmi_msg.cmd = num;
memcpy(hmi_msg.data, &data[7], data_length);
}
else if(length>0 && calculatedCRC != expectedCRC){
Serial.println("CRC Check failure!");
hmi_msg.len = 0;
}
else
{
hmi_msg.len = 0;
}
// Uart_HMI_Data_Analysis(data,length);
}
void HMIsend (char jCode[], char jType[], char jWidget[], char jText[]){
char Head[] = "ST<";
char Payload[128];
char Tail[] = ">ET";
// Allocate the JSON document
JsonDocument jDoc;
// Add values in the document
jDoc["cmd_code"] = jCode;
jDoc["type"] = jType;
jDoc["widget"] = jWidget;
jDoc["text"] = jText;
serializeJson(jDoc, Payload);
Serial.print (Head);
Serial.print (Payload);
Serial.println(Tail);
Serial1.print (Head);
Serial1.print (Payload);
Serial1.println(Tail);
}