Parsing XML text on a MSP430F5529LP + CC3100Boost platform - c

I am working on a IOT project using MSP430F5529LP and CC3100Boost. The hardware is successfully connecting to the cloud and exchanging data. The response to the IOT devices is XML based. I am trying to parse the data. The following printf("\n%.*s\n", pch2-pch1-8, pch1 +8); extracts the data and prints to the console. Now in need to save this data to a variable. Here is my code snippet. The answer might be obvious, unfortunately I am failing to see it.
_i8 * databuffer;
char * pch0;
char * pch1;
char * pch2;
char data[7];
pch0 = strstr((char *)dataBuffer,"textResponse");
pch1 = strstr(pch0,"<text_1>");
pch2 = strstr(pch1,"</text_1>");
printf("\n%.*s\n", pch2-pch1-8, pch1 +8);
References:
Extract data between two delimiters
parsing the value in between two XML tags
MSP430G2121: looking for a xml library to parse xml text

Ensure that the data received is valid and of a length that will fit.
Print it to a string using sprintf() or equivalent function.
Print this string to the console with puts(the_string).

Related

Parsing the payload of the AT commands from the full response string

I want to parse the actual payload from the output of AT commands.
For instance: in the example below, I'd want to read only "2021/11/16,11:12:14-32,0"
AT+QLTS=1 // command
+QLTS: "2021/11/16,11:12:14-32,0" // response
OK
In the following case, I'd need to only read 12345678.
AT+CIMI // command
12345678 // example response
So the point is: not all commands have the same format for the output. We can assume the response is stored in a string array.
I have GetAtCmdRsp() already implemented which stores the response in a char array.
void GetPayload()
{
char rsp[100] = {0};
GetAtCmdRsp("AT+QLTS=1", rsp);
// rsp now contains +QLTS: "2021/11/16,11:12:14-32,0"
// now, I need to parse "2021/11/16,11:12:14-32,0" out of the response
memset(rsp, 0, sizeof(rsp));
GetAtCmdRsp("AT+CIMI", rsp);
// rsp now contains 12345678
// no need to do additional parsing since the output already contains the value I need
}
I was thinking of doing char *start = strstr(rsp, ":") + 1; to get the start of the payload but some responses may only contain the payload as it's the case with AT+CIMI
Perhaps could regex be a good idea to determine the pattern +<COMMAND>: in a string?
In order to parse AT command responses a good starting point is understanding all the possible formats they can have. So, rather than implementing a command specific routine, I would discriminate commands by "type of response":
Commands with no payload in their answers, for example
AT
OK
Commands with no header in their answers, such as
AT+CIMI
12345678
OK
Commands with a single header in their answers
AT+QLTS=1
+QLTS: "2021/11/16,11:12:14-32,0"
OK
Command with multi-line responses.Every line could of "single header" type, like in +CGDCONT:
AT+CDGCONT?
+CGDCONT: 1,"IP","epc.tmobile.com","0.0.0.0",0,0
+CGDCONT: 2,"IP","isp.cingular","0.0.0.0",0,0
+CGDCONT: 3,"IP","","0.0.0.0",0,0
OK
Or we could even have mixed types, like in +CGML:
AT+CMGL="ALL"
+CMGL: 1,"REC READ","+XXXXXXXXXX","","21/11/25,10:20:00+00"
Good morning! How are you?
+CMGL: 2,"REC READ","+XXXXXXXXXX","","21/11/25,10:33:33+00"
I'll come a little late. See you. Bruce Wayne
OK
(please note how it could have also "empty" lines, that is \r\n).
At the moment I cannot think about any other scenario.In this way you'll be able to define an enum like
typedef enum
{
AT_RESPONSE_TYPE_NO_RESPONSE,
AT_RESPONSE_TYPE_NO_HEADER,
AT_RESPONSE_TYPE_SINGLE_HEADER,
AT_RESPONSE_TYPE_MULTILINE,
AT_RESPONSE_TYPE_MAX
}
and pass it to your GetAtCmdRsp( ) function in order to parser the response accordingly. If implement the differentiation in that function, or after it (or in an external function is your choice.
A solution without explicit categorization
Once you have clear all the scenarios that might ever occur, you can think about a general algorithm working for all of them:
Get the full response resp after the command echo and before the closing OK or ERROR. Make sure that the trailing \r\n\r\nOK is removed (or \r\nERROR. Or \r\nNO CARRIER. Or whatever the terminating message of the response might be).Make also sure to remove the command echo
If strlen( resp ) == 0 we belong to the NO_RESPONSE category, and the job is done
If the response contains \r\ns in it, we have a MULTILINE answer. So, tokenize it and place every line into an array element resp_arr[i]. Make sure to remove trailing \r\n
For every line in the response (for every resp_arr[i] element), search for <CMD> : pattern (not only :, that might be contained in the payload as well!). Something like that:
size_t len = strlen( resp_cur_line );
char *payload;
if( strstr( "+YOURCMD: ", resp_cur_line) == NULL )
{
// We are in "NO_HEADER" case
payload = resp_cur_line;
}
else
{
// We are in "HEADER" case
payload = resp_cur_line + strlen( "+YOURCMD: " );
}
Now payload pointer points to the actual payload.
Please note how, in case of MULTILINE answer, after splitting the lines into array elements every loop will handle correctly also the mixed scenarios like the one in +CMGL, as you'll be able to distinguish the lines containing the header from those containing data (and from the empty lines, of course). For a deeper analysis about +CMGL response parsing have a look to this answer.

AWS IoT - JSON incorrect format

I am trying to send this JSON packet to AWS IoT, but it is not recognized by AWS. I am using the example ESP32 AWS FreeRTOS code, but cannot understand what would be the correct format for the JSON packet with the following code:
#define echoMAX_DATA_LENGTH 20
char cDataBuffer[ echoMAX_DATA_LENGTH ];
(void) snprintf(cDataBuffer, echoMAX_DATA_LENGTH, "{\"state\":{\"reported\":%.*d}, \"clientToken\":\"%d\"}", x, x, x);
/* Setup the publish parameters. */
memset( &( xPublishParameters ), 0x00, sizeof( xPublishParameters ) );
xPublishParameters.pucTopic = echoTOPIC_NAME;
xPublishParameters.pvData = cDataBuffer;
xPublishParameters.usTopicLength = ( uint16_t ) strlen( ( const char * ) echoTOPIC_NAME );
xPublishParameters.ulDataLength = ( uint32_t ) strlen( cDataBuffer );
xPublishParameters.xQoS = eMQTTQoS1;
The AWS test page, cannot display the message and has converted it to UTF-8 (this error message is below)
Increase echoMAX_DATA_LENGTH to be large enough to fit your entire JSON message.
The static part of the JSON in your code (without the values filled in by snprintf()) is 34 characters, so there's no way this could ever work with echoMAX_DATA_LENGTH set to 20 - it would always produce a fragment of JSON instead of an entire JSON object.
Remember that the length that snprintf() uses includes a byte for the C string terminating character '\0', so you'll want to make echoMAX_DATA_LENGTH be one greater than the maximum total JSON message length.
When you increase echoMAX_DATA_LENGTH, try adding a debug message after the snprintf() so that you can see the JSON you're generating. If your code is set up to use Serial already, add:
Serial.println(cDataBuffer);
after the snprintf() so you can confirm that you've generated the JSON properly.

Parse C char array sent via web server into (JSON or JavaScript) variables

Desired outcome: Send a struct of GPS data from C using a web server and have the data ( lat, long, etc.) display on a web page.
Using: Linux, libwebsockets (LWS) library for web server. C code is implemented as a standalone plugin in the LWSWS (web server) app.
Current understanding/work on project: Casting struct from plugin to a char array and trying to parse the array (using JSON) into the individual members of the original struct. Then display each variable on a web page.
I am new to Linux, LWS, HTML, JavaScript, and JSON. I can use JSON or Javascript to work with the variables, whichever makes more sense. I am not completely sure of my plan on either side (C/JS) of this part of the project.
What is the best way to transfer this information and convert for use on the web page?
Here is my struct:
struct per_session_data__gps_rcvr {
struct time_struct{
int month;
int day;
int year;
int hour;
int minute;
double second;
}time;
double latitude;
char lat_indicator;
double longitude;
char lon_indicator;
double heading;
int quality;
int satellites;
};
Sending struct out, cast to a char pointer:
// Write GPS data to GUI
n = lws_snprintf( (char *)p, sizeof(buf) - LWS_PRE, "%s", (char *)pss );
m = lws_write(wsi, p, n, LWS_WRITE_TEXT);
if (m < n) {
lwsl_err("ERROR %d writing to di socket\n", n);
return -1;
}
break;
The test code I am modifying appears to receive data in a variable called "msg". I am assuming I need to parse the data sent in the char array into chunks that correspond to the members of the original struct. Then I can display each piece of GPS data on the web page.
var socket_gps;
if (use_lws_meta)
socket_gps = lws_meta.new_ws("", "gps-rcvr-protocol");
else
socket_gps = new_ws(get_appropriate_ws_url(""), "gps-rcvr-protocol");
try {
socket_gps.onopen = function() {
document.getElementById("wsdi_statustd").style.backgroundColor = "#40ff40";
document.getElementById("wsdi_status").innerHTML =
" <b>websocket connection opened</b><br>" +
san(socket_gps.extensions);
}
socket_gps.onmessage =function got_packet(msg) {
document.getElementById(" **VARIABLE GOES HERE** ").textContent = msg.data + "\n";
}
socket_gps.onclose = function(){
document.getElementById("wsdi_statustd").style.backgroundColor = "#ff4040";
document.getElementById("wsdi_status").textContent = " websocket connection CLOSED ";
}
}
catch(exception) {
alert('<p>Error' + exception);
}
Please let me know if there is a better way to send the data out from C, and/or if I have correctly configured the data. In my research, it was mentioned that data could be sent as binary, but sending plain text messages and using JSON seemed like a better plan.
It looks like JSON can "stringify" info, but only if it's typed in or in JavaScript format already? I'm not sure. I have not seen any way to parse out a char array, a set number of characters at a time, and save those segments as variables on the HTML side. That is what I was hoping for.
Thank you for your suggestions!
Like this:
n = snprintf( (char *)p, sizeof(buf), "{\"NameofParam1\":\"%d\",\"NameofParam2\":\"%d\"}", param1, param2 );
That gives you your whole JSON string in one go (stored in buffer). Just use the appropriate flag (%s, %X, %c, %f, etc.) to capture your parameter.

Parse JSON message manually in C

I need to parse manually, without external libraries, a JSON message coming from a server, in C language.
The message coming from server would be like:
{[CR+LF]
"Tmg": "R",[CR+LF]
"STP": 72[CR+LF]
}[CR+LF]
or
{[CR+LF]
"Tmg": "R",[CR+LF]
"STP": 150[CR+LF]
}[CR+LF]
I need the number after STP:. The number is different in each message structure, so I need to get that number from the JSON structure. I can't use external libraries because this code is in an embedded system and exernal code is not allowed.
I tried this following:
int main (){
const char response_message[35] = "{\r\n\"Tmg\":\"R\",\r\n\"STP\":72,\r\n}";
const char needle[8] = "P\":";
char *ret;
ret = strstr(response_message, needle);
printf("The number is: %s\n", ret);
return 0;
}
But obviously, I am getting this result:
The number is: P":72,
}
So I need to only get the number, how can I get this?
Thanks
You can use a hacked solution. Use strstr () to find "STP": then find the following , or } and extract the digits in between.
And that's a hack. Not guaranteed to work. For something that's guaranteed to work, you use a JSON parser.

Using the string table and printing sections names

We recieved a homework assignment in which we need to take an ELF file and print its sections' names.
We are supposed to do all that using only the data we receive directly from the ELF header,
meaning we can't use any "high level" procedures - we need to go directly to the data we need.
So, im trying to print the first section's name. I know the names are supposed to be in the string table. This is what I have so far:
I'm getting the start of the ELF file using mmap...
elfhead =(Elf32_Ehdr *) mmap...
I'm getting the section offset using the members in the ELF header
sectionoffset = elfhead->e_shoff
then
section = (Elf32_Shdr*)(elfhead + sectionoffset)
nameoffset = section->sh_name
stringoffset = elfhead->e_shstrndx;
To be clear -
in elfhead i have the elf header
in section i have the section header
in stringoffset i have the index inside the section table where the
string table is supposed to be
in nameoffset i have the index in
the string table where the first section name is suppose to be.
How do I go to the first name and print it, given the code above?
Well first off you'd have to have access to the section's String Table, and since the header is the first thing in the ELF file:
char* stringTable = elfhead + (section + header->stringoffset)->sh_offset;
Once you have that, all you really have to do is print the first one using the nameoffset you already obtained, like so.
char* name = stringTable + nameoffset;
printf("%s\n",name);
FYI, printing the rest of the names would be a simple loop:
for(i=0;i<header->e_shnum;i++){
char* name = stringTable + nameoffset;
printf("%s\n",name);
section++;
}

Resources