Parse the incoming packet - c
I am receiving a packet like 0xFA5D0D01.
Now i want to parce it like
FA is Header1
5D is Header2
0D is length and
01 is checksum.
const int data_availabe = Serial.available();
I am able to write to serial port but not able to parce it like
if i received FA is then print received Header1
const int data_availabe = Serial.available();
if (data_availabe <= 0)
{
return;
}
const int c = Serial.read();
Serial.print("Receive Status: ");
Serial.println(STATE_NAME[receiveState]);
Serial.print(c, HEX);
Serial.print(" ");
if (isprint(c)) //isprint checks whether it is printable character or not (e.g non printable char = \t)
{
Serial.write(c);
}
Serial.println();
Serial.println(receiveState);
switch (receiveState)
{
case WAITING_FOR_HEADER1:
if (c == HEADER1)
{
receiveState = WAITING_FOR_HEADER2;
}
break;
case WAITING_FOR_HEADER2:
if (c == HEADER2)
{
receiveState = WAITING_FOR_LENGTH;
}
break;
}
Where receiveState is enum changing as we are getting exptected data..
I assume the Arduino is receiving data from USB.
What is the if (data available <= 0) doing? If you want want to read data from the serial port while it is avalaible, you should better do if (Serial.avalaible() > 1) and then Serial.read() inside the {}.
If you initialize a const you won't be able to change its value over time...
What is readString and how is it initialized?
Have you tried to Serial.print(c) to see what's inside?
Once again, it would be easier for us if you could give us more context on why and when this piece of code is running.
EDIT:
#define HEADER_1 0xFA // here you define your headers, etc. You can also use variables.
uint8_t readByte[4]; // your packet is 4 bytes long. each byte is stored in this array.
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.avalaible() > 1) { // when there is data avalaible on the serial port
readByte[0] = Serial.read(); // we store the first incomming byte.
delay(10);
if (readByte[0] == HEADER_1) { // and check if that byte is equal to HEADER_1
for (uint8_t i = 1 ; i < 4 ; i++) { // if so we store the 3 last bytes into the array
readByte[i] = Serial.read();
delay(10);
}
}
}
//then you can do what you want with readByte[]... i.e. check if readByte[1] is equal to HEADER_2 and so on :)
}
Related
strcmp script reading serial monitor failing
I have a script reading the serial monitor and looking for an "OK" response. I am able to capture the OK response in a variable named message and print it to the serial monitor, but when I attempt to use the message variable in an if statement it is not performing as expected. When the variable message = OK the statement below is still giving a false. Does anyone know where the issue might be? if (strcmp (message,"OK") == 0) { Serial.println("true"); } else { Serial.println("false"); } The complete code: //#include <HardwareSerial.h> const unsigned int MAX_MESSAGE_LENGTH = 12; void setup() { Serial2.begin(115200,SERIAL_8N1); //open modem serial port Serial.println("serial ports are open"); } void loop() { Serial2.write("AT\r\n"); while (Serial2.available() > 0){ //Create a place to hold the incoming message static char message[MAX_MESSAGE_LENGTH]; static unsigned int message_pos = 0; //Read the next available byte in the serial receive buffer char inByte = Serial2.read(); //Message coming in (check not terminating character) and guard for over message size if ( inByte != '\n' && (message_pos < MAX_MESSAGE_LENGTH - 1) ) { //Add the incoming byte to our message message[message_pos] = inByte; message_pos++; } //Full message received... else { //Add null character to string message[message_pos] = '\0'; //Print the message (or do other things) Serial.println("loop"); delay(100); Serial.println(message); //Reset for the next message message_pos = 0; if (strcmp (message,"OK") == 0) { Serial.println("true"); } else { Serial.println("false"); } } } delay(5000); } Serial monitor looks like this: 22:11:51.970 -> serial ports are open 22:12:16.984 -> loop 22:12:17.078 -> AT 22:12:17.078 -> false 22:12:17.078 -> loop 22:12:17.171 -> OK 22:12:17.171 -> false 22:12:17.171 -> loop 22:12:17.266 -> AT 22:12:17.266 -> false 22:12:17.266 -> loop 22:12:17.360 -> OK 22:12:17.360 -> false
The received response is "OK\r\n", so the actual message is "OK\r". It helps for debugging if you temporarily print each received character. To ignore the echo of the command, insert another strcmp() for it and react accordingly, for example do nothing.
Array Data Reading Failed
I am reading the data from a "Torque Wrench" using "USB Host Shield2.0" and Arduino UNO. I am receiving correct data from my "Torque Wrench" Data is receiving in a array. But when I started reading data after "for" loop inside Void loop() I am receiving incorrect data. I attached Both output pictures correct and incorrect data. Basically I am read data from Torque Wrench and send to receiver using Nrf24l01. I am receiving incorrect data. My question is :- Why I am reading Incorrect data outside "for" loop. Correct Data inside "for" loop :- enter image description here Incorrect Data outside "for" loop :- enter image description here #include <SPI.h> // for SPI communication #include <nRF24L01.h> #include <RF24.h> #include <cdcacm.h> #include <usbhub.h> //#include "pgmstrings.h" // Satisfy the IDE, which needs to see the include statment in the ino too. #ifdef dobogusinclude #include <spi4teensy3.h> #endif #include <SPI.h> RF24 radio(7, 8); // CE, CSN const byte address[6] = {'R','x','A','A','A','B'}; // the address the the module class ACMAsyncOper : public CDCAsyncOper { public: uint8_t OnInit(ACM *pacm); }; uint8_t ACMAsyncOper::OnInit(ACM *pacm) { uint8_t rcode; // Set DTR = 1 RTS=1 rcode = pacm->SetControlLineState(3); if (rcode) { ErrorMessage<uint8_t>(PSTR("SetControlLineState"), rcode); return rcode; } LINE_CODING lc; lc.dwDTERate = 9600; lc.bCharFormat = 0; lc.bParityType = 0; lc.bDataBits = 8; rcode = pacm->SetLineCoding(&lc); if (rcode) ErrorMessage<uint8_t>(PSTR("SetLineCoding"), rcode); return rcode; } USB Usb; //USBHub Hub(&Usb); ACMAsyncOper AsyncOper; ACM Acm(&Usb, &AsyncOper); void setup() { Serial.begin(9600); radio.begin(); radio.openWritingPipe(address); radio.setPALevel(RF24_PA_MAX); radio.stopListening(); #if !defined(__MIPSEL__) while (!Serial); #endif Serial.println("Start"); if (Usb.Init() == -1) Serial.println("USB Not Connected"); delay( 200 ); } void loop() { Usb.Task(); if( Acm.isReady()) { uint8_t rcode; /* reading the keyboard */ if(Serial.available()) { uint8_t data= Serial.read(); /* sending to the phone */ rcode = Acm.SndData(1, &data); if (rcode) ErrorMessage<uint8_t>(PSTR("SndData"), rcode); } delay(10); uint8_t buf[64]; uint16_t rcvd = 64; char text[64]; rcode = Acm.RcvData(&rcvd, buf); if (rcode && rcode != hrNAK) ErrorMessage<uint8_t>(PSTR("Ret"), rcode); if ( rcvd ) { for(uint16_t i=0; i < rcvd; i++ ) { // Serial.print((char)buf[i]); // correct Data read from torque wrench text[i] = (char)buf[i]; } Serial.println(text); // reading wrong data here //radio.write(&text, sizeof(text)); //Serial.println(text); } delay(10); } }
Character arrays must be null-terminated to count as C strings. After the for loop, add text[rcvd] = '\0'; Also, your rcvd is fixed at 64. It needs to be one less than the array size for the null terminator to fit.
Working with byte array I get an "invalid conversion from char* to byte"
I am reading and writing to an RFID tag using MFRC522.h I can currently read the UID of a card and dump it to "UIDChar" The UID of a card typically is 8 characters. UID Example: 467EE9A9 I can use the mfrc522.MIFARE_SetUid function to write this UID to a new card. In order to do this I have to set the newUID to: 0x46,0x7E,0xE9,0xA9f I have written this into my code. What I am wanting to do is convert the UID string into a byte array so that I can use that in place of my manually written 0x46,0x7E,0xE9,0xA9. I use the convert function to convert the UID into that format. It can that be displayed with "buf". Serial.println(buf); Now my problem. If I replace the byte newUid[] = {0x46,0x7E,0xE9,0xA9f}; with byte newUid[] = {buf}; I get the error invalid conversion from 'char*' to 'byte {aka unsigned char}' How can I set my "newUid" as "buf"? #define SS_PIN 0 //D2 #define RST_PIN 2 //D1 #include <SPI.h> #include <MFRC522.h> /* For RFID */ MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. char buf[40]; // For string to byte array convertor void convert(char *s) { int i, j, k; buf[0] = 0x0; for (j = 0, i = 0, k = 0; j < strlen(s); j++) { if (i++ == 0) { buf[k++] = '0'; buf[k++] = 'x'; } buf[k++] = s[j]; if (i == 2) { if(j != strlen(s) -1) buf[k++] = ','; i = 0; } } buf[k] = 0x0; } void clone() { /* RFID Read */ // Look for new cards if ( ! mfrc522.PICC_IsNewCardPresent()) { return; } // Select one of the cards if ( ! mfrc522.PICC_ReadCardSerial()) { return; } //Show UID on serial monitor Serial.println(); Serial.print(" UID tag :"); // Very basic UID dump unsigned int hex_num; hex_num = mfrc522.uid.uidByte[0] << 24; hex_num += mfrc522.uid.uidByte[1] << 16; hex_num += mfrc522.uid.uidByte[2] << 8; hex_num += mfrc522.uid.uidByte[3]; // Get UID int NFC_id = (int)hex_num; Serial.print(NFC_id, HEX); // Convert UID to string using an int and a base (hexadecimal) String stringUID = String(NFC_id, HEX); char UIDChar[10]; stringUID.toCharArray(UIDChar,10); delay(1000); Serial.println(); // Convert to uppercase for (int i = 0; i < strlen(UIDChar); i++ ) { if ( UIDChar[i] == NULL ) break; UIDChar[i] = toupper(UIDChar[i]); } //Serial.print( &UIDChar[0] ); Serial.println(); convert(UIDChar); Serial.println(buf); /* RFID Write */ // Set new UID // Change your UID hex string to 4 byte array // I get error if I use byte newUid[] = {buf}; /* ERROR HERE */ byte newUid[] = {0x46,0x7E,0xE9,0xA9}; if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) { Serial.println( "Wrote new UID to card." ); } // Halt PICC and re-select it so DumpToSerial doesn't get confused mfrc522.PICC_HaltA(); if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) { return; } // Dump the new memory contents Serial.println( "New UID and contents:" ); mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); } void setup() { Serial.begin ( 115200 ); /* RFID */ SPI.begin(); // Initiate SPI bus mfrc522.PCD_Init(); // Initiate MFRC522 clone(); } void loop() { }
When you write byte newUid[] = {buf}; you are trying to initialise newUid with a single element (there's only one item inside your {}), and that element is buf, which is a char* (or a char[]). That's why you get the error - you are trying to assign an array with one char* to a variable whose elements are bytes. Without reading your full code in detail, I don't know why you are trying to do this assignment, rather than just use your buf array as it is. But to fix the problem, you probably just want to use byte* newUid = buf;
trying to work with strings and serial port on arduino, my sketch skips characters for some reason
I've been trying to achieve serial communication between my arduino-based project and my pc ,i need to send commands to arduino over serial and use "if and else" to call desired function (in parseMessage() function). I can't use delay() since I'm using interrupts for multiplexing and bit-angle modulation so i had to do serial communication another way around, this is closest I've got to succeess but still I'm getting character skips and unstability in general. and as you know coding is all great except for when you don't know what's wrong with your code, so help me please gods of the internet! ;) The reason I'm using '#' as end of the string declearation is that I can't be sure that all characters of my command sent to arduino is there when Serial.read() asks for it, there might be more on the way and since atmega328 is faster than serial port Serial.available() might actually return -1 in middle of transmission. ps : oh, and I can't use String class, It's very expensive, this atmega328 is already sweating under 8x8 RGBLED multiplexing and 4bit-angle modulation and he is gonna have to do even more in future. ps : and I'm still learning English so pardon me if there is something wrong with the grammer I'm using. void setup() { Serial.begin(9600); } bool dataRTP = false; // data ready to parse void loop() { readSerial(); } char message[64]; int index = 0; void readSerial() { if (Serial.available() > 0) while (Serial.available() > 0) if (Serial.peek() != '#') // i'm using '#' as end of string declearation. message[index++] = Serial.read(); else { message[index++] = '\n'; dataRTP = true; break; } while (Serial.read() != -1) {} // flushing any possible characters off of if (dataRTP) // UARTS buffer. parseMessage(); } void parseMessage() { // just testing here, actual code would be like : Serial.print(message); // if (!strcmp(message, "this expression")) index = 0; // callthisfunction(); dataRTP = false; // else ... etc }
Just managed to fix this code, seems flushing data off of serial UART wasn't a good idea after all. It's all solved. Here's how code looks now: void setup() { Serial.begin(9600); } bool dataRTP = false; void loop() { readSerial(); } char message[64]; int index = 0; void readSerial() { if (Serial.available() > 0) while (Serial.available() > 0) if (Serial.peek() == '#') { message[index++] = Serial.read(); message[index++] = '\0'; dataRTP = true; break; } else message[index++] = Serial.read(); if (dataRTP) parseMessage(); } void parseMessage() { Serial.println(message); index = 0; dataRTP = false; }
Parsing code for GPS NMEA string
i am trying to parse the incoming GPGGA NMEA GPS string using Arduino uno and below code. What i am trying to do is that i am using only GPGGA NMEA string to get the values of Latitude, longitude and altitude.In my below code, i had put certain checks to check if incoming string is GPGGA or not, and then store the further string in a array which can be further parsed suing strtok function and all the 3 GPS coordinates can be easily find out. But i am unable to figure out how to store only GPGGA string and not the further string.I am using a for loop but it isn't working. I am not trying to use any library.I had came across certain existing codes like this. Here is the GPGGA string information link i am trying to have following functionlity i) Check if incoming string is GPGGA ii) If yes, then store the following string upto EOL or upto * (followed by checksum for the array) in a array, array length is variable(i am unable to find out solution for this) iii) Then parse the stored array(this is done, i tried this with a different array) #include <SoftwareSerial.h> SoftwareSerial mySerial(10,11); // 10 RX / 11 TX void setup() { Serial.begin(9600); mySerial.begin(9600); } void loop() { uint8_t x; char gpsdata[65]; if((mySerial.available())) { char c = mySerial.read(); if(c == '$') {char c1 = mySerial.read(); if(c1 == 'G') {char c2 = mySerial.read(); if(c2 == 'P') {char c3 = mySerial.read(); if(c3 == 'G') {char c4 = mySerial.read(); if(c4 == 'G') {char c5 = mySerial.read(); if(c5 == 'A') {for(x=0;x<65;x++) { gpsdata[x]=mySerial.read(); while (gpsdata[x] == '\r' || gpsdata[x] == '\n') { break; } } } else{ Serial.println("Not a GPGGA string"); } } } } } } } Serial.println(gpsdata); } Edit 1: Considering Joachim Pileborg, editing the for loop in the code. I am adding a pic to show the undefined output of the code. Input for the code: $GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43 $GPGGA,092751.000,5321.6802,N,00630.3371,W,1,8,1.03,61.7,M,55.3,M,,*75 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,16,13,28,070,17,26,23,252,,04,14,186,15*77 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45
After a quick check of the linked article on the NMEA 0183 protocol, this jumped out at me: <CR><LF> ends the message. This means, that instead of just read indiscriminately from the serial port, you should be looking for that sequence. If found, you should terminate the string, and break out of the loop. Also, you might want to zero-initialize the data string to begin with, to easily see if there actually is any data in it to print (using e.g. strlen).
You could use some functions from the C library libnmea. Theres functions to split a sentence into values by comma and then parse them.
Offering this as a suggestion in support of what you are doing... Would it not be useful to replace all of the nested if()s in your loop with something like: EDIT added global string to copy myString into once captured char globalString[100];//declare a global sufficiently large to hold you results void loop() { int chars = mySerial.available(); int i; char *myString; if (chars>0) { myString = calloc(chars+1, sizeof(char)); for(i=0;i<chars;i++) { myString[i] = mySerial.read(); //test for EOF if((myString[i] == '\n') ||(myString[i] == '\r')) { //pick this... myString[i]=0;//strip carriage - return line feed(or skip) //OR pick this... (one or the other. i.e.,I do not know the requirements for your string) if(i<chars) { myString[i+1] = mySerial.read() //get remaining '\r' or '\n' myString[i+2]=0;//add null term if necessary } break; } } if(strstr(myString, "GPGGA") == NULL) { Serial.println("Not a GPGGA string"); //EDIT strcpy(globalString, "");//if failed, do not want globalString populated } else { //EDIT strcpy(globalString, myString); } } //free(myString) //somewhere when you are done with it } Now, the return value from mySerial.available() tells you exactly how many bytes to read, you can read the entire buffer, and test for validity all in one.
I have a project that will need to pull the same information out of the same sentence. I got this out of a log file import serial import time ser = serial.Serial(1) ser.read(1) read_val = ("nothing") gpsfile="gpscord.dat" l=0 megabuffer='' def buffThis(s): global megabuffer megabuffer +=s def buffLines(): global megabuffer megalist=megabuffer.splitlines() megabuffer=megalist.pop() return megalist def readcom(): ser.write("ati") time.sleep(3) read_val = ser.read(size=500) lines=read_val.split('\n') for l in lines: if l.startswith("$GPGGA"): if l[:len(l)-3].endswith("*"): outfile=open('gps.dat','w') outfile.write(l.rstrip()) outfile.close() readcom() while 1==1: readcom() answer=raw_input('not looping , CTRL+C to abort') The result is this: gps.dat $GPGGA,225714.656,5021.0474,N,00412.4420,W,0,00,50.0,0.0,M,18.0,M,0.0,0000*5B
Using "malloc" every single time you read a string is an enormous amount of computational overhead. (And didn't see the corresponding free() function call. Without that, you never get that memory back until program termination or system runs out of memory.) Just pick the size of the longest string you will ever need, add 10 to it, and declare that your string array size. Set once and done. There are several C functions for getting substrings out of a string, strtok() using the coma is probably the least overhead. You are on an embedded microcontroller. Keep it small, keep overhead down. :)
#include <stdio.h> #include <string.h> #define GNSS_HEADER_LENGTH 5 #define GNSS_PACKET_START '$' #define GNSS_TOKEN_SEPARATOR ',' #define bool int #define FALSE 0 #define TRUE 1 //To trim a string contains \r\n void str_trim(char *str){ while(*str){ if(*str == '\r' || *str == '\n'){ *str = '\0'; } str++; } } /** * To parse GNSS data by header and the index separated by comma * * $GPGSV,1,1,03,23,39,328,30,18,39,008,27,15,33,035,33,1*5A * $GNRMC,170412.000,V,,,,,,,240322,,,N,V*2D * $GNGGA,170412.000,,,,,0,0,,,M,,M,,*57 * * #data_ptr the pointer points to gps data * #header the header for parsing GPGSV * #repeat_index the header may repeat for many lines * so the header index is for identifying repeated header * #token_index is the index of the parsing data separated by "," * the start is 1 * #result to store the result of the parser input * * #result bool - parsed successfully **/ bool parse_gnss_token(char *data_ptr, char *header, int repeat_index, int token_index, char *result) { bool gnss_parsed_result = FALSE; // To check GNSS data parsing is success bool on_header = FALSE; // For header int header_repeat_counter = 0; int header_char_index = 0; // each char in header index // For counting comma int counted_token_index = 0; // To hold the result character index bool data_found = FALSE; char *result_start = result; char header_found[10]; while (*data_ptr) { // 1. Packet start if (*data_ptr == GNSS_PACKET_START) { on_header = TRUE; header_char_index = 0; // to index each character in header data_found = FALSE; // is data part found data_ptr++; } // 2. For header parsing if (on_header) { if (*data_ptr == GNSS_TOKEN_SEPARATOR || header_char_index >= GNSS_HEADER_LENGTH) { on_header = FALSE; } else { header_found[header_char_index] = *data_ptr; if (header_char_index == GNSS_HEADER_LENGTH - 1) { // Now Header found header_found[header_char_index + 1] = '\0'; on_header = FALSE; if (!strcmp(header, header_found)) { // Some headers may repeat - to identify it set the repeat index if (header_repeat_counter == repeat_index) { //printf("Header: %s\r\n", header_found ); data_found = TRUE; } header_repeat_counter++; } } header_char_index++; } } // 3. data found if (data_found) { // To get the index data separated by comma if (counted_token_index == token_index && *data_ptr != GNSS_TOKEN_SEPARATOR) { // the data to parse *result++ = *data_ptr; gnss_parsed_result = TRUE; } if (*data_ptr == GNSS_TOKEN_SEPARATOR) { // if , counted_token_index++; // The comma counter for index } // Break if the counted_token_index(token_counter) greater than token_index(search_token) if (counted_token_index > token_index) { break; } } // Appending \0 to the end *result = '\0'; // To trim the data if ends with \r or \n str_trim(result_start); // Input data data_ptr++; } return gnss_parsed_result; } int main() { char res[100]; char *nem = "\ $GNRMC,080817.000,A,0852.089246,N,07636.289920,E,0.00,139.61,270322,,,A,V*04\r\n\\r\n\ $GNGGA,080817.000,0852.089246,N,07636.289920,E,1,5,1.41,11.246,M,-93.835,M,,*5E\r\n\ $GNVTG,139.61,T,,M,0.00,N,0.00,K,A*2F\r\n\ $GNGSA,A,3,30,19,17,14,13,,,,,,,,1.72,1.41,0.98,1*0A\r\n\ $GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,3*02\r\n\ $GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,6*07\r\n\ $GPGSV,3,1,12,06,64,177,,30,60,138,15,19,51,322,18,17,42,356,27,1*68\r\n\ $GPGSV,3,2,12,14,36,033,17,07,34,142,17,13,32,267,17,02,21,208,,1*6C\r\n\ $GPGSV,3,3,12,15,05,286,,01,05,037,,03,03,083,,20,02,208,,1*6B\r\n\ $GAGSV,1,1,00,7*73\r\n\ $GIGSV,1,1,00,1*7D\r\n\ $GNGLL,0852.089246,N,07636.289920,E,080817.000,A,A*43\r\n\ $PQTMANTENNASTATUS,1,0,1*4F\r\n"; printf("Parsing GNRMC\r\n"); printf("===============\r\n"); for(int i=1;i<=16;i++){ parse_gnss_token(nem, "GNRMC", 0, i, res); printf("Index: %d, Result: %s\r\n", i, res); } printf("Parsing GNVTG (First Parameter)\r\n"); printf("================================"); // GNVTG - Header, 0 - Repeat Index(if header is repeating), 1 - Value Index, parse_gnss_token(nem, "GNVTG", 0, 1, res); printf("\r\nGNVTG: %s\r\n", res); return 0; }