Can't read packets from Cygbot LIDAR properly - arduino

I'm trying to get an MCU to read data from a LIDAR via UART, but for some reason the checksum isn't correct. I'm using an ESP32-WROOM dev chip (using Arduino) and a Cygbot D1 LIDAR with this datasheet. I am able to read the 2D data packets, but not the 3D data packets, which are much longer.
After receiving a packet, I use a sequence of Serial.read() calls to read in data, byte by byte. The packet header, packet length bytes, and payload header are read correctly. The payload is 14,401 bytes long.
When I finish reading the data, the checksum doesn't match, and there are ~300 bytes left in the Serial buffer. After a lot of debugging I'm stuck and would appreciate any pointers or ideas for how to get un-stuck.
The relevant portions of my code:
bool readPacket() {
/*
* Call when we're expecting a packet. The top of the buffer
* must be the correct packet header.
*/
if (Serial2.available()) {
//Check the header, return false if it's wrong
if (not checkHeader()) {
return false;
}
//Next 2 bytes are the packet length
byte b0 = Serial2.read();
byte b1 = Serial2.read();
unsigned int payloadLen = combineBytesIntoInt(b0, b1);
//Read in the data based on the payload header.
byte payloadHeader = Serial2.read();
byte sum = b0 ^ b1 ^ payloadHeader; //Start computing checksum
if (payloadHeader == 0x08) {
sum ^= parse3D();
}
byte packetChecksum = Serial2.read();
if (packetChecksum != sum) {
Serial.println("Checksum error");
return false;
}
return true;
} else {
return false;
}
}
byte parse3D() {
/*
* Parse a 3D dataset, reading directly from the serial.
* Reading in batches of 3 due to the way data is formatted in the packet.
* Returns checksum.
*/
byte checksum = 0;
for (int i=0; i<N_MEASURES_3D; i+=2) {
int msb0 = -1;
//If data hasn't been sent, Serial2.read will return -1. Wait until we receive valid data before proceeding.
while (msb0 < 0) {
msb0 = Serial2.read();
}
int split = -1;
while (split < 0) {
split = Serial2.read();
}
int lsb1 = -1;
while (lsb1 < 0) {
lsb1 = Serial2.read();
}
checksum ^= byte(msb0) ^ byte(split) ^ byte(lsb1);
}
return checksum
}
void loop() {
//When the button is clicked, send a packet.
if (buttonState == 1) {
ct += 1;
if (ct % 2 == 0) {
Serial.println("Write packet: start 3d");
lidarStart3DMode();
}
}
bool readSuccess = readPacket();
if (readSuccess) {
lidarStop();
}
//This code just updates the button
if ((digitalRead(BUTTON_PIN) == 1) and (millis() - lastTime > BUTTON_DEBOUNCE_TIME)) {
lastTime = millis();
buttonState = 1;
} else {
buttonState = 0;
}
}

All the reads in your code here:
byte b0 = Serial2.read();
byte b1 = Serial2.read();
unsigned int payloadLen = combineBytesIntoInt(b0, b1);
//Read in the data based on the payload header.
byte payloadHeader = Serial2.read();
and here
byte packetChecksum = Serial2.read();
and possibly in checkHeader() (which you didn't provide), should be safe-guarded against buffer under-run, ie., reading bytes when none are available.
You could add
while (!Serial.available());
before each read().

Related

Detect pattern in incoming byte stream

I would like to detect a certain byte pattern in an incoming byte stream at the UART. I am using Arduino. Currently, the code detects a single \r character. On this character is detected, the byte stream before this character gets stored into a buffer. This is easy. This is my code;
int incomingByte = 0; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0 ) {
// read the incoming byte:
incomingByte = Serial.read();
if (incomingByte != '\r') {
buffer_incoming_data[index_buffer]=incomingByte;
index_buffer++;
} else {
index_buffer = 0;
}
}
}
Here is my problem. Instead of a single character \r, I would like to detect a byte pattern that looks like this 0xAA 0xBB 0xCC. When this byte pattern is detected, the byte stream before byte pattern gets stored into a buffer.
Guess you invoke loop() method inside real loop operator. I can suggest next way:
int incomingByte = 0; // for incoming serial data
int pattern [] = {0xAA, 0xBB, 0xCC};
int patternIndex = 0;
int patternSize = sizeof(pattern)/sizeof(pattern[0]);
void loop() {
// send data only when you receive data:
if (Serial.available() > 0 ) {
// read the incoming byte:
incomingByte = Serial.read();
if(pattern[patternIndex] != incomingByte){
// if we found begin of pattern but didn't reach the end of it
if(patternIndex>0){
for(int i=0; i<=patternIndex-1; i++){
buffer_incoming_data[index_buffer]=pattern[i];
index_buffer++;
}
}
patternIndex = 0;
}
if(pattern[patternIndex] == incomingByte){
patternIndex++;
}else{
buffer_incoming_data[index_buffer]=incomingByte;
index_buffer++;
}
//if we reached the end of pattern
if(patternIndex==patternSize){
//do something with buffer
patternIndex = 0;
index_buffer = 0;
}
}
}
As you can see, I didn't check index_buffer for "index out of range" exception, but you should. Hope I helped you.

How to check for a gap between serial data

I'm working on an interface that reads serial data from a single wire bus on a car (BMW IBUS). I'm using an Microchip MCP2025 LIN bus transceiver chip to convert the single line bus to regular Rx/Tx to feed into the hardware serial pins (0 & 1) of an Arduino (Nano V3).
BMW's IBUS protocol uses Serial 8E1 at 9600 baud. Messages are variable length (between 5 and 37 bytes) Bus messages are separated by a pause of around 11 milliseconds (considerably longer than the bus high time when receiving) and I need to use this gap to detect the end of one message and the beginning of the next.
1st byte is the Source ID,
2nd byte is length of the packet excluding the first two bytes (source & length),
3rd byte is the destination ID,
4th byte onwards is the actual data,
Last byte is the checksum
Checksum is generated by XOR of the entire packet excluding the checksum itself.
My problem is that I don't know how to measure the bus idle time between messages. I think I can use bitRead(PIND,0) to tell when the bus is idle (high), but I'm not sure how to time this and go and process the message after a suitable time has passed.
I'm currently using the following code to read messages from the IBUS. It works for the most part, but gets out of sync when connected to the car. It's doesn't really know when messages have finished, but relies on the second byte of each message (the length byte) to know how many bytes to read in. As long as it reads the length byte correctly, it's fine, but if it gets out of sync, and the length byte is wrong, it reads in the wrong number of bytes and eventually overruns the array and the Arduino resets. If I can detect the idle time between messages I will know for sure that I have a complete message.
void ReadIBUS()
{
boolean inSTATE = 0;
byte IBUSbyte[40];
while(Serial.available() > 0 && inSTATE == 0 )
{
IBUSbyte[0] = Serial.read(); //read source byte
while (inSTATE == 0)
{
if(Serial.available() > 0)
{
IBUSbyte[1] = Serial.read(); //read length byte
inSTATE = 1;
}
delay(10);
}
inSTATE == 0;
LENGTH = IBUSbyte[1];
int i = 2;
while(i <= LENGTH + 1)
{
if(Serial.available() > 0)
{
IBUSbyte[i] = Serial.read();
i++;
delay(10);
}
}
checksumBYTE = 0;
byteSTATE = 0;
for (int i = 0; i < LENGTH + 1; i++){
checksumBYTE ^= IBUSbyte[i];
}
if (IBUSbyte[LENGTH + 1] == checksumBYTE){
for(int i = 0; i <= LENGTH + 1; i++)
{
mySerial.print(IBUSbyte[i], HEX);
mySerial.print(" ");
}
mySerial.println();
}
else {
debug();
inSTATE = 0;
byteSTATE = 0;
}
}
}
First off, delay is rarely a good thing to use. In this case, you can use millis to measure how long it is between characters, but only if you lose the delay:
void ReadIBUS()
{
byte IBUSbyte[40];
do {
// First, make sure we're between frames, in the quiet interval
uint32_t last_rx = millis();
while (millis() - last_rx < 5) {
// Hasn't been long enough, see if more chars are coming
if (Serial.available()) {
Serial.read(); // throw away partial frame chars
last_rx = millis();
}
}
// Haven't received any chars for a while, we must be
// between frames. Now wait for a frame.
while (!Serial.available())
; // waiting...
IBUSbyte[0] = Serial.read(); //read source byte
while (!Serial.available())
; // waiting...
IBUSbyte[1] = Serial.read(); //read length byte
LENGTH = IBUSbyte[1];
// Check the LENGTH to make sure it's not bogus. If it is, try again.
} while (LENGTH > sizeof(IBUSbyte)-1);
// LENGTH is reasonable, wait for the payload
uint8_t bytes_received = 2;
while (bytes_received <= LENGTH + 1) {
if (Serial.available()) {
IBUSbyte[ bytes_received++ ] = Serial.read(); // read frame payload byte
}
}
// All here, verify the checksum
checksumBYTE = 0;
for (int i = 0; i < LENGTH + 1; i++) {
checksumBYTE ^= IBUSbyte[i];
}
if (IBUSbyte[LENGTH + 1] == checksumBYTE) {
for(int i = 0; i <= LENGTH + 1; i++) {
mySerial.print(IBUSbyte[i], HEX);
mySerial.print(" ");
}
mySerial.println();
} else {
debug();
}
}
And the state variables are no longer needed.
BTW, this routine "blocks" the rest of the program from doing anything until a complete frame is received. It should probably be rewritten so that it is called frequently (like from loop), and last_rx and bytes_received are remembered across calls. It could return true when a complete frame has been received. This'll work, it's just sub-optimal because it blocks anything else from happening, except interrupts.

HTTP GET too big for buffer

Hello. I am using this example from cooking hacks to make a HTTP GET call to my server.(bottom of the page)
http://www.cooking-hacks.com/documentation/tutorials/arduino-3g-gprs-gsm-gps
My end goal would be to get one word from a php page to my Arduino Uno.
But I am running in to problems because my Arduino has a limited buffer and the header size is bigger than its buffer.
My header size is 363
And my download size is 12
The max size is 64
Because of this my code is not working. I want to only get the 12 but don't know how to do this I hope someone can help me point me in the right direction.
* Description: This example shows how to do a GET method. So the buffer of
* is limited, we recommend to use the GET method with short answer for the
* requested webs.
* This example shows the AT commands (and the answers of the module) used
* to work with HTTP. For more information about the AT commands,
* refer to the AT command manual.
*
* Copyright (C) 2013 Libelium Comunicaciones Distribuidas S.L.
* http://www.libelium.com
*
* 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 <http://www.gnu.org/licenses/>.
*
* Version 0.2
* Author: Alejandro Gallego
*/
int8_t answer;
int onModulePin = 2, aux;
int data_size = 0;
int end_file = 0;
char aux_str[100];
char data[250];
int x = 0;
long previous;
char url[ ]="rubenvandijk.com";
int port= 80;
char request[ ]="GET /misc/twittertest5/get_tweet.php HTTP/1.1\r\nHost: rubenvandijk.com\r\n";
void setup(){
pinMode(onModulePin, OUTPUT);
Serial.begin(115200);
Serial.println("Starting...");
power_on();
delay(3000);
// sets the PIN code
sendATcommand("AT+CPIN=0000", "OK", 2000);
delay(3000);
while( (sendATcommand("AT+CREG?", "+CREG: 0,1", 500) ||
sendATcommand("AT+CREG?", "+CREG: 0,5", 500)) == 0 );
// sets APN, user name and password
sendATcommand("AT+CGSOCKCONT=1,\"IP\",\"prepaidinternet\"", "OK", 2000);
sendATcommand("AT+CSOCKAUTH=1,1,\"\",\"\"", "OK", 2000);
}
void loop(){
// request the url
sprintf(aux_str, "AT+CHTTPACT=\"%s\",%d", url, port);
answer = sendATcommand(aux_str, "+CHTTPACT: REQUEST", 60000);
if (answer == 1)
{
Serial.println(request);
//Serial.println(host);
//Serial.println("Content-Length: 0");
// Sends <Ctrl+Z>
aux_str[0] = 0x1A;
aux_str[1] = 0x00;
answer = sendATcommand(aux_str, "+CHTTPACT: DATA,", 60000);
x=0;
do{
if (answer == 1)
{
data_size = 0;
while(Serial.available()==0);
aux = Serial.read();
do{
data_size *= 10;
data_size += (aux-0x30);
while(Serial.available()==0);
aux = Serial.read();
}
while(aux != 0x0D);
Serial.print("Data received: ");
Serial.println(data_size);
if (data_size > 0)
{
while(Serial.available() < data_size);
Serial.read();
for (int y = 0; y < data_size; y++)
{
data[x] = Serial.read();
x++;
}
data[x] = '\0';
}
else
{
Serial.println("Download finished");
}
}
else
{
Serial.println("Error getting the url");
data_size = 0;
}
answer = sendATcommand2("", "+CHTTPACT: DATA,", "+CHTTPACT:0", 20000);
}while (answer != 1);
if (answer == 2)
{
Serial.print("Data received hallo:");
Serial.println(data);
}
else
{
Serial.println("Error getting data");
}
}
else
{
Serial.println("Error waiting the request");
}
delay(10000);
}
void power_on(){
uint8_t answer=0;
// checks if the module is started
answer = sendATcommand("AT", "OK", 2000);
if (answer == 0)
{
// power on pulse
digitalWrite(onModulePin,HIGH);
delay(3000);
digitalWrite(onModulePin,LOW);
// waits for an answer from the module
while(answer == 0){
// Send AT every two seconds and wait for the answer
answer = sendATcommand("AT", "OK", 2000);
}
}
}
int8_t sendATcommand(char* ATcommand, char* expected_answer1,
unsigned int timeout)
{
uint8_t x=0, answer=0;
char response[100];
unsigned long previous;
memset(response, '\0', 100); // Initialize the string
delay(100);
while( Serial.available() > 0) Serial.read(); // Clean the input buffer
Serial.println(ATcommand); // Send the AT command
x = 0;
previous = millis();
// this loop waits for the answer
do{
if(Serial.available() != 0){
response[x] = Serial.read();
x++;
// check if the desired answer is in the response of the module
if (strstr(response, expected_answer1) != NULL)
{
answer = 1;
}
}
// Waits for the asnwer with time out
}
while((answer == 0) && ((millis() - previous) < timeout));
return answer;
}
int8_t sendATcommand2(char* ATcommand, char* expected_answer1,
char* expected_answer2, unsigned int timeout)
{
uint8_t x=0, answer=0;
char response[100];
unsigned long previous;
memset(response, '\0', 100); // Initialize the string
delay(100);
while( Serial.available() > 0) Serial.read(); // Clean the input buffer
Serial.println(ATcommand); // Send the AT command
x = 0;
previous = millis();
// this loop waits for the answer
do{
if(Serial.available() != 0){
response[x] = Serial.read();
x++;
// check if the desired answer is in the response of the module
if (strstr(response, expected_answer1) != NULL)
{
answer = 1;
}
// check if the desired answer is in the response of the module
if (strstr(response, expected_answer2) != NULL)
{
answer = 2;
}
}
// Waits for the asnwer with time out
}
while((answer == 0) && ((millis() - previous) < timeout));
return answer;
}
It now returns
�Starting...
AT
AT+CPIN=0000
AT+CREG?
AT+CGSOCKCONT=1,"IP","prepaidinternet"
AT+CSOCKAUTH=1,1,"",""
AT+CHTTPACT="rubenvandijk.com",80
GET /misc/twittertest5/get_tweet.php HTTP/1.1
Host: rubenvandijk.com
Data received: 524
It gets stuck at
if (data_size > 0)
{
while(Serial.available() < data_size);
Serial.read();
I have several ideas you can try...
1) Process your incoming data one line at a time, don't buffer it all up to process.
2) Add some special characters to your web server response, toss all the data until you see that character or sequence. Say add a # symbol in front of the data you want, as bytes come in only buffer up those after the #.
3) use a circular buffer and let it overwrite till all the data is received, leaving you the last X bytes in the buffer, process by working backwards from the buffer tail.
Number 1 is the best answer, number 2 is the easiest.

Arduino code weird return with Serial.read()

Having issues with the Serial.read() command on my Arduino code. I have it connected to two 74HC595's shift registers connected to LED's.
I check if there is Serial data, then read two bytes. Then pass these bytes to a method that shifts them both out. When I check the bytes with Serial.print to print them out to the serial moniter I get for example
49
255
50
255
Why am I getting the two 255's I have read the documentation on arduino.cc and it says it reads a single byte only. Any ideas?
The end goal is to read two bytes on the serial line and shift them out to the shift reg's IE is the byte values of decimal 5 & 6 were passed the 1st 3rd LED's would light on one shift register then the 2nd and 3rd would on the other shift register
const int dataPin = 8;
const int latchPin = 9;
const int clockPin = 10;
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
byte low = Serial.read();
byte high = Serial.read();
Serial.println(low);
Serial.println(high);
sendBytes(low,high);
}
}
void sendBytes(byte l, byte h) {
digitalWrite(latchPin,LOW);
shiftOut(dataPin,clockPin,MSBFIRST,l);
shiftOut(dataPin,clockPin,MSBFIRST,h);
digitalWrite(latchPin,HIGH);
}
if (Serial.available() > 0) {
byte low = Serial.read();
byte high = Serial.read();
//...
This is a bug in your code. Very likely to trip, serial ports are not that fast. So high odds that Serial.available() only returns 1. You'll read the low byte okay but Serial.read() will then return -1 if there is no data to read. Which makes high equal to 0xff. The simple fix is:
if (Serial.available() >= 2)
I had the same problem reading from a program...
I had luck both letting the code loop until filled...
and before taking on new Serial data I flush the serial to get the latest bytes
While(Serial.read() != -1); //clears data in the PC Serial Port
Here's what it looks like all put together
int i = 0;
byte bytes[3];
interval = 150; //allows for this amount of time to read from serial
long previousMillis = 0;
void loop() {
unsigned long currentMillis = millis();
if(currentMillis - previousMillis < interval && i < 2) {
if(Serial.available() > 0) {
bytes[i] = Serial.read();
i++; }
}
if(currentMillis - previousMillis > 1000) { //waits 1 second to get new serial data
previousMillis = currentMillis; //resets the timer
i = 0;
While(Serial.read() != -1); //clears data in the PC Serial Port
}

Arduino : Check byte array for chars one at a time

I communicate with Arduino via Serial using a program that sends a series of bytes.
In order for the Arduino to realize it is receiving a message rather than junk, I have tagged the start of my byte array with the chars 'S' 'T' 'A' 'R' 'T'. After this will eventually follow a series of bytes that will be assigned to internal variables (not yet implemented).
The Arduino must read each byte sequentially and compare it to the byte array and if all are present in the correct order it will continue with the next part of the program, otherwise it will should discard current byte and wait for more bytes to arrive.
I am trying to implement it in the most efficient and readable way rather than using a series of nested if statements.
So far I have got:
byte inByte = 0;
byte handShake[] = {'S','T','A','R','T'};
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
for (int x =0; x < sizeof(handShake) ; x++)
{
inByte = Serial.read();
Serial.println(x);
if (inByte == handShake[x])
{
if (x == (sizeof(handShake)-1)) {setArduino();}
}
else break;
}
}
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
This however doesn't seem to get past the second byte and I'm not sure why.
Worked it out :
Here is the answer:
byte inByte = 0;
char handShake[] = {'S','T','A','R','T'};
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
for (int x =0; x < sizeof(handShake) ; x++)
{
inByte = Serial.read();
Serial.println(x);
if (inByte == handShake[x])
{
if (x == (sizeof(handShake)-1)) {setArduino();}
while(!Serial.available()) {delay(1);}
}
else {break;}
}
}
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
This may not be the most efficient way perhaps, but I can't see a problem with it currently.
Better answer : This allows the rest of the loop to iterate while waiting for the message to finish and if the full handshake message isn't received the counter will reset.
byte inByte = 0;
char handShake[] = {'S','T','A','R','T'};
int messageIndex = 0;
void setup() {
Serial.begin(9600);
}
void loop()
{
while (Serial.available())
{
inByte = Serial.read();
Serial.println(messageIndex);
if (inByte == handShake[messageIndex])
{
messageIndex++;
if (messageIndex == sizeof(handShake)) {messageIndex = 0; setArduino();}
}
else {messageIndex=0;}
}
// Other code while waiting for message to finish
Serial.println("tick");
}
void setArduino () {
Serial.println("Ready To Set Parameters");
}
You could try to calculate your message. CRC is old and good solution. I use it and it works perfect for me. I am not sure what kind of device are you communicating with.
//define
const uint32_t Polynomial = 0xEDB88320;
const uint16_t NumBytes = 256;
uint8_t data[NumBytes];
/// compute CRC32
uint32_t crc32_bitwise(const void* data, uint16_t length, uint32_t previousCrc32 = 0)
{
uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF
uint8_t* current = (uint8_t*) data;
while (length--)
{
crc ^= *current++;
for (uint8_t j = 0; j < 8; j++)
{
uint8_t lowestBit = crc & 1;
crc >>= 1;
if (lowestBit)
crc ^= Polynomial;
}
}
return ~crc; // same as crc ^ 0xFFFFFFFF
}
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
when you need to calculate CRC
uint32_t crc = crc32_bitwise(data_bytes, sizeof(data_bytes));
data_bytes is byte array.
Then you can get all settings or message in byte data[x] and calculate CRC. Then you can add CRC to the message and send message byte data[x+sizeof(CRC)]
P.S. Use byte instead of int. For ex. for(byte x =0; x<sizeof(handShake); x++)

Resources