How to convert fetched string data to character array? - c++

I am doing this IoT based project on displaying data to connected display( I've used the MAX7219 module, in this case) with the help of nodeMCU. The idea here is that the string which is stored in my firebase database is to be display on the led display.
I've had no trouble in getting the value from the database to my nodeMCU but there is this little problem with converting that string to char array since the code i am using( Max72xx_Message_serial, which was available as an example with the max72xx library) has used char array but i can only fetch the stored data in string format. I've modified that code so as to connect with firebase but the main issue is to convert the string fetched from the database to char array.
I tried toCharArray() but it still shows conversion error.
void readfromfirebase(void)
{
static uint8_t putIndex = 0;
int n=1;
while (Firebase.available())
{
newMessage[putIndex] = (char)Firebase.getString("Submit Message"); // this line produces the error
if ((newMessage[putIndex] == '\n') || (putIndex >= BUF_SIZE-3)) // end of message character or full buffer
{
// put in a message separator and end the string
newMessage[putIndex++] = ' ';
newMessage[putIndex] = '\0';
// restart the index for next filling spree and flag we have a message waiting
putIndex = 0;
newMessageAvailable = true;
}
else if (newMessage[putIndex] != '\r')
// Just save the next char in next location
{putIndex++;}
n++;
}
}

I think you are confusing the types
getString returns a String object wich can be converted to a char[] using the methods of the String class.
I assume your newMessage is of type char[] or char*.
Then I would advise you to go for the String.c_str() method, because it returns a C style null-terminated string, meaning a char*.
See https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/c_str/ for reference.
It also sets the last character of the string to 0. So methods like strlen, strcmp etc will work.
! be carefull not to modify the array returned by c_str(), if you want to modify it you chould copy the char[] or use string.toCharArray(buf, len).
Your Code might then look like the following.
String msg = Firebase.getString("Submit Message");
newMessage = msg.c_str();
// rest of your code
If newMessage is a buffer storing multiple messages, meaning char* newMessage[3].
String msg = Firebase.getString("Submit Message");
newMessage[putIndex] = msg.c_str();
// rest of your code
Be careful, because you are storing multiple characters in an array, so use strcmp to compare these arrays!
If you are new to C I would recommend reading.
https://www.cprogramming.com/tutorial/c/lesson9.html
https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/ (as pointed out by #gre_gor)

Related

ArduinoJSON serialization returns an empty string when serializing to char*

I made a function that serializes settings and returns a char* containing serialized data.
First i'm packing all the values into a StaticJsonDocument, then determining size of the output string using measureJson, then allocating space for the output char out[strsize] and then serializing data into space allocated serializeJson(doc,out,strsize)
The problem is that output string remains empty for unknown reason.
Things i checked:
Json document is constructed properly and actually contains configurations settings
measureJson() function properly returns the size of output and space is being allocated, strsize is not 0
Code:
char* configSerialize(bool msgpack){
StaticJsonDocument<settsize> doc;
JsonArray ipk = doc.createNestedArray("ip");
JsonArray gateipk = doc.createNestedArray("gateip");
JsonArray dnsk = doc.createNestedArray("dns");
JsonArray mack = doc.createNestedArray("mac");
unsigned char i;
for(i=0;i<4;i++){
ipk.add(ip[i]);
gateipk.add(gateip[i]);
dnsk.add(dns[i]);
}
for(i=0;i<6;i++){
mack.add(mac[i]);
}
doc["subnet"] = subnet;
doc["dhcp"] = DHCP;
doc["alertbuzz"] = alertbuzz;
const size_t strsize = msgpack ? measureMsgPack(doc) : measureJson(doc);
char out[strsize];
if(msgpack) serializeMsgPack(doc,out,strsize);
else serializeJson(doc,out,strsize);
return out;
}
char out[strsize];
This is a local variable/array inside your configSerialize() function and is invalid once you return from that function.
One way would be to use new and delete to allocate/deallocate space on the heap, but I would not recommend that on Arduino.
Another way would be to use char out[FIXED_SIZE]; outside of your function - i.e. as a global variable.
Also, if you're planning to use out as a string pointer, you'll need to add a zero byte at the end (and allocate space for that extra byte).

Arduino function can't edit array

Background
I've written an Arduino function which receives strings in the format "a,b,c,d,e" over a serial connection, where a,b,c,d,e are integers, and I'm trying to update an array with these integers every time a new string is received. The data is received and parsed into individual integers fine, but the array won't update properly.
Attempt
Below is the code, I've left the getData() function out as all it does is receive the string from the serial connection and store it in an array of characters input (that part is working fine).
void setup() {
Serial.begin(9600);
}
void loop() {
getData();
if(parsed == false){
parseData(readings);
}
}
void parseData(int readings[]) {
x = 0;
char * split;
split = strtok(input,",");
while (split != NULL)
{
readings[x] = split;
split = strtok (NULL, ",");
x++;
}
parsed = true;
}
Problem
If I send a string like "6,7,8,9,0", the array readings[] is updated to [289,291,293,295,297] no matter what values I send, I have checked what values split takes inside the function and they are correct, however the line readings[x] = split; fails to update the array elements to anything other than those 5 numbers in that order. This is the case when the value of readings[n] is checked inside or outside the parseData function.
Also, if I send fewer than 5 integers in the string, e.g. a,b,c, only the first array elements will change and the others will remain at 0, e.g. [289,291,293,0,0]
Before I found out about passing array pointers to functions, the exact same thing was happening with slightly different code - when I called the function in the loop, I just used parseData();, and when I defined the function I just used void parseData(){
Question
Why isn't the array updating properly and how can I fix it?
Your readings is an array of integers.
And split is a pointer-to-char.
The statement
readings[x] = split
stores split, which is the address of a character, as an integer value.
If I write
const char *pointer = "42";
int address = pointer;
I am not storing the integer value 42 in address - I'm storing the number identifying the memory location of the first character in my string.
If you want to convert a string into an integer, you need to parse it with a function like strtol.

How to Concatenate Unicode string into a character string to pass into mysql call

I am trying to concatenate an array of strings into a character array - but one of the strings is in a foreign language (why I need UTF8). I can see the UTF8 string in their appropriate language in the debugger (Visual Studio) after I read it from the database and put it into the wxString array, but when I try to concatenate the string to the array, t never gets put in there.
I have tried variable.mb_str()
variable.mb.str().data(). Neither seems to work in the strcat
for my Language data. The other data is concatenated fine. All of the data comes from a MariaDB database call.
int i, numRows;
wxString query;
wxString sortby;
wxString group_list;
wxString *stringGroups;
char holdString[400];
/* Try UTF Force */
query.Printf(_("set names 'utf8'"));
mysql_query(mDb, query.mb_str());
result = mysql_store_result(mDb);
mysql_free_result(result);
query.Printf(_("select GROUP_NAME from USER_PERMS where USER_NAME =
\"%s\"
ORDER BY GROUP_NAME "), riv_getuser().c_str() );
mysql_query(mDb, query.mb_str());
result = mysql_store_result(mDb);
numRows = mysql_num_rows(result);
stringGroups = new wxString[numRows + 1];
i = 0;
while ((row = mysql_fetch_row(result)))
{
stringGroups[i] = wxString(row[0], wxConvUTF8);
i++;
}
mysql_free_result(result);
i = 0;
strcpy (holdString,"IN (\'");
while (i < numRows)
{
if (i != 0) strcat(holdString, "\', \'");
strcat(holdString, (const char *)stringGroups[i].mb_str().data());
i++;
}
strcat (holdString," \')");
-- END OF CODE --
--ACTUAL stringGroup that fails -- Debugger Watch Output
stringGroups[2] {m_impl=L"文字化け"...
I expect to get:
IN ( 'test' , 'test' , '文字化け' )
what I get
IN ( 'test','test2','' )
Don't use strcpy() and strcat() with wxString, this is just needlessly error-prone. If you use wxString in the first place, build the entire string you need and then utf8_str() method to get the buffer containing UTF-8 string contents which you can then pass to whatever function you need.
Do keep in mind that this buffer is temporary, so you can't rely on it continuing to exist if you don't make a copy of it or at least extend its lifetime, i.e.
auto const& buf = some_wx_string.utf8_str();
... now you can use buf.data() safely until the end of scope ...
To get UTF8 from wxString you need to call ToUTF8(). Similarly, for getting UTF8 into wxString there is FromUTF8(). Both are members of wxString and documented.
wxString::mb_str() converts to a multi-byte string in your current locale. Presumably the characters in your string aren't representable in your locale so the conversion fails and an empty string is returned.
You should pass wxConvUTF8 as a parameter or simply call utf8_str or ToUTF8 instead.

Function call resets passed global structs' fields

With reference to the code below: After calling the function CheckSMS and passing the struct * DB1, the fields are updated according the the strtok call. This function reads and parses a text message, storing its contents into char* fields of the DB struct.
In the main loop, I have called Serial.println(DB1.last_order) before and after calling the CheckSMS function. If I have received a text, the order is printed appropriately in the main loop, however on the next call to CheckSMS, DB1.last_order is cleared, replaced with a \n or a NULL or something. I cannot figure out why DB1.last_order does not retain its value, and rather, it is overwritten with every call to CheckSMS. Thanks for any help.
Note - All text messages contain "CMT+", therefore writing to DB1 only happens when a text is received. Calling CheckSMS when no text is received should simply skip through.
int CheckSMS(Robot *DB1) {
int j = 0;
char response[100];
char *pch;
unsigned long previous;
memset(response, '\0', 100);
while(Serial2.available()>0){
response[j] = Serial2.read();
j++;
Serial.println("inc");
}
delay(100);
if (strstr(response, "CMT:") != NULL){
DB1->new_message = strtok(response, " ,");
DB1->last_phone = strtok(NULL, " ,");
pch = strtok(NULL, " ,");
DB1->last_date = strtok(NULL, " ,");
DB1->last_time = strtok(NULL, " ,\n");
DB1->last_order = strtok(NULL," ,\n");
new_message = 1;
}
else{
}
return 0;
}
The strtok function returns pointers to the string you're tokenizing, the local array response in your case. When the function returns the response array goes out of scope and disappears, leaving your structure with pointers to a string that no longer exists, and giving you undefined behavior.
You have a couple of solutions:
Allocate the string dynamically using malloc, but then you must save it in the structure so you can free it when you're done with the structure
Make the response array static, but then the next call to the function will have the same array leading the old data to be updated
Pass in a string to store the response and use that string, the string must have a lifetime at least as long as the structure and don't change contents. The string can of course be a member of the structure itself
The answer Joachim gave is correct, I just want to add that you could also change the Robot structure to contain char arrays (like this: char new_message[MAX_BUF_CHARS]; and so on). Be sure to have enough space in them. Then instead of assigning the pointers returned from strtok, copy the strings in there.

How to read in only a particular number of characters

I have a small query regarding reading a set of characters from a structure. For example: A particular variable contains a value "3242C976*32" (char - type). How can I get only the first 8 bits of this variable. Kindly help.
Thanks.
Edit:
I'm trying to read in a signal:
For Ex: $ASWEER,2,X:3242C976*32
into this structure:
struct pg
{
char command[7]; // saves as $ASWEER,2,X:3242C976*32
char comma1[1]; // saves as ,2,X:3242C976*32
char groupID[1]; // saves as 2,X:3242C976*32
char comma2[1]; // etc
char handle[2]; // this is the problem, need it to save specifically each part, buts its not
char canID[8];
char checksum[3];
}m_pg;
...
When memcopying buffer into a structure, it works but because there is no carriage returns it saves the rest of the signal in each char variable. So, there is always garbage at the end.
you could..
convert your hex value in canID to float(depending on how you want to display it), e.g.
float value1 = HexToFloat(m_pg.canID); // find a conversion script for HexToFloat
CString val;
val.Format("0.3f",value1);
the garbage values aren't actually being stored in the structure, it only displays it as so, as there is no carriage return, so format the message however you want to and display it using the CString val;
If "3242C976*3F" is a c-string or std::string, you can just do:
char* str = "3242C976*3F";
char first_byte = str[0];
Or with an arbitrary memory block you can do:
SomeStruct memoryBlock;
char firstByte;
memcpy(&firstByte, &memoryBlock, 1);
Both copy the first 8bits or 1 byte from the string or arbitrary memory block just as well.
After the edit (original answer below)
Just copy by parts. In C, something like this should work (could also work in C++ but may not be idiomatic)
strncpy(m_pg.command, value, 7); // m.pg_command[7] = 0; // oops
strncpy(m_pg.comma, value+7, 1); // m.pg_comma[1] = 0; // oops
strncpy(m_pg.groupID, value+8, 1); // m.pg_groupID[1] = 0; // oops
strncpy(m_pg.comma2, value+9, 1); // m.pg_comma2[1] = 0; // oops
// etc
Also, you don't have space for the string terminator in the members of the structure (therefore the oopses above). They are NOT strings. Do not printf them!
Don't read more than 8 characters. In C, something like
char value[9]; /* 8 characters and a 0 terminator */
int ch;
scanf("%8s", value);
/* optionally ignore further input */
while (((ch = getchar()) != '\n') && (ch != EOF)) /* void */;
/* input terminated with ch (either '\n' or EOF) */
I believe the above code also "works" in C++, but it may not be idiomatic in that language
If you have a char pointer, you can just set str[8] = '\0'; Be careful though, because if the buffer is less than 8 (EDIT: 9) bytes, this could cause problems.
(I'm just assuming that the name of the variable that already is holding the string is called str. Substitute the name of your variable.)
It looks to me like you want to split at the comma, and save up to there. This can be done with strtok(), to split the string into tokens based on the comma, or strchr() to find the comma, and strcpy() to copy the string up to the comma.