How to compare a char pointer with a string [duplicate] - c++

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Closed last year.
Right now I need to create an if statement that can compare a char pointer with a string like the following statement:
if (Start == "on"){
Serial.println("virker");
}
The problem is that this simple sentence does not work. The variable Start is a string containing the word on that I get from a web page that sends a JSON object via a AJAX request. The object looks like this when I receive it:
{"start":"on","relay":"off","computer_alert":"off","esp_alert":"off","alarm1":{"tilstand":"off","tid":"null"},"alarm2":{"tilstand":"off","tid":"null"},"alarm3":{"tilstand":"off","tid":"null"}}
I've tried to give Start a value inside the program and that works. My entire code can be seen below:
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";
const int buzzer = 0;
const int relay = 6;
const char* Start;
int d;
const char* test = "on";
const char* PARAM_INPUT_1 = "Json";
AsyncWebServer server(80);
void ekstern() {
const int buzzer = 0;
const int relay = 6;
pinMode(relay and buzzer, OUTPUT);
}
void setup() {
ESP.eraseConfig();
Serial.begin(9600);
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
if(!SPIFFS.begin()){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/HTML.html");
});
server.on("/JQ", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/JQ.js");
});
server.on("/CSS", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/CSS.css");
});
server.on("/GET", HTTP_GET, [] (AsyncWebServerRequest *request) {
String json;
if (request->hasParam(PARAM_INPUT_1)) {
json = request->getParam(PARAM_INPUT_1)->value();
Serial.println(json);
}
request->send(200, "text/plain", "OK");
StaticJsonDocument<384> doc;
DeserializationError error = deserializeJson(doc, json);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
Start = doc["start"]; // "off"
const char* relay = doc["relay"]; // "off"
const char* computer_alert = doc["computer_alert"]; // "off"
const char* esp_alert = doc["esp_alert"]; // "off"
const char* alarm1_tilstand = doc["alarm1"]["tilstand"]; // "off"
long alarm1_tid = doc["alarm1"]["tid"]; // 3184358
const char* alarm2_tilstand = doc["alarm2"]["tilstand"]; // "off"
long alarm2_tid = doc["alarm2"]["tid"]; // 3184358
const char* alarm3_tilstand = doc["alarm3"]["tilstand"]; // "off"
long alarm3_tid = doc["alarm3"]["tid"]; // 3244358
Serial.println(alarm3_tid);
Serial.println(alarm3_tilstand);
});
server.begin();
}
void loop(){
if (Start == "on"){
Serial.println("virker");
}
Serial.println(Start);
Serial.println("hallo");
delay(5000);
}
I don't think it makes any difference, but I am using the ESP8266.

Your doc is a stack variable that will vanish as just you leave the request handler GET. This means that you will absolutely definitely access to already dangling pointer, which Start variable stores, in loop function because it points to the already not existing doc["start"]. You have to preserve data from doc["start"] rather than the pointer doc["start"] contains. For that you need to define Start as an array of chars and then use strncpy to copy characters from the doc["start"] into Start variable. Further, in loop function you need to use strcmp or strncmp to compare "on" with characters in the variable Start. As easy as it gets
UPD: Also GET request MUST NOT change the state of your server conventionally, therefore Start variable is not supposed to be altered

You can convert the value in the pointer to a String by useing the following command,
String();
For me it did not work when i used the following command,
strcmp();
The command just gave a exception (28) error. I feel a little bit stupid because i am pretty sure i used the String() before and it didnt work, but i must have done something else wrong because it wroks now. The solution was to write the if statement like this,
if(String(Start)=="on"){
Serial.println("virker");
}
UPD: The methode I described above does work, but stackoverflow user dpronin made me aware that the methode has some problems. The problem came from when the program received the JSON object because just saving the pointer made the program buggy. The solution was useing this command,
strncpy();
Which copies the string to another variable instead of just pointing to the memory addresse. When I changed to this methode my code worked. I will also say that it may be the reason why the following command didnt work,
strcmp();
I have tested it again after the changes.

Related

Deserialize Json object from mqtt payload using ArduinoJson library

I'm trying to deserialize a Json object using the ArduinoJson 6 library. The object is passing through a mqtt callback using the PubSubClient library. The payload contains the following example: "{\"action\":\"message\",\"amount\":503}" but I am unable to retrieve the amount value. Only zeros are returned when using the following:
void messageReceived(char *topic, byte *payload, unsigned int length)
{
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload, length);
const int results = doc["amount"];
Serial.print(results);
}
This works and returns 503 as needed:
DynamicJsonDocument doc(1024);
char json[] = "{\"action\":\"message\",\"amount\":503}";
deserializeJson(doc, json);
const int results = doc["amount"];
Serial.print(results);
I see the results of the payload when I use the following method:
void messageReceived(char *topic, byte *payload, unsigned int length)
{
for (unsigned int i = 0; i < length; i++)
{
Serial.print((char)payload[i]);
}
}
What is preventing me from being able to parse out the amount value from the first method?
When programming in C++, it always need to be aware the type of data that you are dealing with. The payload is a byte array, which is not what deserializeJson(doc, payload, length); is expecting, see the function signature of deserializeJson().
void messageReceived(char *topic, byte *payload, unsigned int length)
{
DynamicJsonDocument doc(128);
deserializeJson(doc, (char*) payload, length);
Serial.print(doc["amount"]);
}
Update & resolution:
The first method in my original post worked fine once I fixed the data that was being sent from the Lambda function to the IOT side. (Something I didn't include in my original question and didn't think it was relevant. Turns out it was.) Here is a snippet from the Lambda function that is receiving the data. The issue was that the data was being sent as a string and not parsed. Once I stringified the response and then parsed the output, it worked. Thank you #hcheung for the assistance and helpful info. Your suggestion works as well but only after I fixed the Lambda function.
async function sendToIOT(response) {
const data = JSON.stringify(response);
const iotResponseParams = {
topic: 'mythingname/subscribe',
payload: JSON.parse(data)
};
return iotdata.publish(iotResponseParams).promise()
}

How to run Redis sadd commands with hiredis

My code contains a head file redis.h and a c++ source file redis.cpp.
This is a demo of sadd opeaion in redis. All the operations fail, becase of WRONGTYPE Operation against a key holding the wrong kind of value. I don't know what happened.
Please give me some suggestions.
//redis.h
#ifndef _REDIS_H_
#define _REDIS_H_
#include <iostream>
#include <string.h>
#include <string>
#include <stdio.h>
#include <hiredis/hiredis.h>
using namespace std;
class Redis{
public:
Redis(){}
~Redis(){
this->_connect =NULL;
this->_reply=NULL;
}
bool connect(string host, int port){
this->_connect = redisConnect(host.c_str(), port);
if(this->_connect != NULL && this->_connect->err){
printf("connect error: %s\n", this->_connect->errstr);
return 0;
}
return 1;
}
string set(string key, string value){
this->_reply = (redisReply*)redisCommand(this->_connect, "sadd %s %s", key.c_str(), value.c_str());
string str = this->_reply->str;
return str;
}
string output(string key){
this->_reply = (redisReply*)redisCommand(this->_connect, "smembers %s", key.c_str());
string str = this->_reply->str;
freeReplyObject(this->_reply);
return str;
}
private:
redisContext * _connect;
redisReply* _reply;
};
#endif //_REDIS_H
//redis.cpp
#include "redis.h"
int main(){
Redis *r = new Redis();
if(!r->connect("127.0.0.1", 6379)){
printf("connect error!\n");
return 0;
}
printf("Sadd names Andy %s\n", r->set("names", "Andy").c_str());
printf("Sadd names Andy %s\n", r->set("names", "Andy").c_str());
printf("Sadd names Alice %s\n", r->set("names", "Alice").c_str());
printf("names members: %s\n", r->output("names").c_str());
delete r;
return 0;
}
The result:
Sadd names Andy WRONGTYPE Operation against a key holding the wrong kind of value
Sadd names Andy WRONGTYPE Operation against a key holding the wrong kind of value
Sadd names Alice WRONGTYPE Operation against a key holding the wrong kind of value
names members: WRONGTYPE Operation against a key holding the wrong kind of value
WRONGTYPE Operation against a key holding the wrong kind of value
This means the key, i.e. names, has already been set, and its type is NOT a SET. You can run TYPE names with redis-cli to see the type of the key.
Also, your code has several problems:
redisConnect might return null pointer
you did not call redisFree to free the resource of redisReply in your set method
sadd and smembers do NOT return string reply, so you cannot get the correct reply
Since you're using C++, you can try redis-plus-plus, which is based on hiredis, and have more C++ friendly interface:
try {
auto r = sw::redis::Redis("tcp://127.0.0.1:6379");
r.sadd("names", "Andy");
r.sadd("names", "Alice");
std::vector<std::string> members;
r.smembers("names", std::back_inserter(members));
} catch (const sw::redis::Error &e) {
// error handle
}
Disclaimer: I'm the author of redis-plus-plus.

Pointer to pointer comparision breaks when while(1) removed - why?

Trying to build a menu system but running into some issues with pointers - which I don't have much experience with.
I don't understand why removing the while(1) makes the comparison fail between mainmenu_table[1][i] == &option6 but for some reason it does.
What am I doing wrong? Using visual studio and an atmega328p. Thanks
Serial output with original code:
Serial begins
MeNu6
MeNu6
Starting compare loop
it worked
Serial output with while(1) removed.
Serial begins
MeNu6
MeNu6
Starting compare loop
the end
Original code (with while(1) included)
const char option1[] PROGMEM = "Menu1";
const char option2[] PROGMEM = "MEnu2";
const char option3[] PROGMEM = "MeNu3";
const char option4[] PROGMEM = "Menu4";
const char option5[] PROGMEM = "MEnu5";
const char option6[] PROGMEM = "MeNu6";
const char option7[] PROGMEM = "menu7";
const char* const submenu1_table[] PROGMEM = { option1, option2, option3 }; // array of pointers to chars stored in flash
const char* const submenu2_table[] PROGMEM = { option4, option5, option6, option7 };
const char** const mainmenu_table[] PROGMEM = { submenu1_table, submenu2_table }; //array of pointers to pointers to chars in flash
// The setup() function runs once each time the micro-controller starts
void setup()
{
Serial.begin(9600);
delay(100);
Serial.println("Serial begins");
Serial.println((const __FlashStringHelper*)(mainmenu_table[1][2])); // prints "Menu6" as expected
Serial.println((const __FlashStringHelper*)option6); // also prints "Menu6"
Serial.println("Starting compare loop");
for (int i = 0; i < 4; i++) {
if ( mainmenu_table[1][i] == &option6 ) { //
Serial.println("it worked");
while (1); // COMMENTING THIS OUT MEANS DOESN'T COMPARE SUCCESSFULLY.
}
}
Serial.println("the end");
}
// Add the main program code into the continuous loop() function
void loop()
{
}
According to Arduino description of PROGMEM, you cannot access the data through pointers to it directly as with plain pointers. You need to use the proper macros/functions to access the data.
In your code, the pointer tables themselves are located in the PROGMEM, so, to extract the individual pointers, you are supposed to do something like:
const char** submenu = (const char**)pgm_read_word(&(mainmenu_table[1]));
const char* option = (const char*)pgm_read_word(&(submenu[i]));
if (option == option6) {
//...
This code is based on the string table example from the first link.

How to use a void* in C++ to hold the value of an uint32_t when the pointer is passed as a reference to a method

I am having issues storing values into a void* and successfully retrieving what I stored in initially. The below is my pseudo code/train of thought:
Inside method 1 on a client
StatusCode doSomething() {
string filename = "somefile.txt";
void* server_checksum;
//Stat signature (string &filename, void* file_status)
StatusCode fileStatus = Stat(filename, &server_checksum); //Passing the address of the pointer
//We received the fileStatus from Stat, I expect the value of server_checksum to match what the server sent
//However, this prints a completely different number, and I do not know how to ensure it holds the right value
cout << *((uint32_t *)serverCrc) << endl;
return StatusCode::OK;
}
Inside the Stat method on the client, there's a protobuf via grpc that has the checksum for the file on the server:
StatusCode Stat(string &filename, void* file_status) {
//call the grpc method on the server (abstracted)
.
.
.
//Contains the checksum of the file on the server - this works fine
uint32_t s_crc = response.server_crc();
// I print it in both the server and the client to confirm it is the same value - this works fine
cout << s_crc << endl;
//In my understanding, here I am assigning the value of s_crc to the void * file status, which I passed the address for inside of method 1 - this works fine
file_status = (uint32_t *) &s_crc;
// I print file_status to make sure it still matches the value the server sent - this works fine
cout<<"file_status " << *((uint32_t *)file_status) << endl;
return StatusCode::OK; -> Continues inside method 1 above
}
There's no reason to use a void* here at all. C++ has a type system; you should use it.
Instead of declaring your out parameter as a void*, declare it to be either a pointer or reference to the type you want to write. In this case that appears to be uint32_t:
StatusCode Stat(const std::string& filename, uint32_t& file_status) {
//call the grpc method on the server (abstracted)
// ...
//Contains the checksum of the file on the server - this works fine
file_status = response.server_crc();
return StatusCode::OK;
}
And then you can call it without doing any special gymnastics:
StatusCode doSomething() {
std::string filename = "somefile.txt";
uint32_t server_checksum;
StatusCode fileStatus = Stat(filename, server_checksum);
std::cout << server_checksum << std::endl;
return StatusCode::OK;
}
Live Demo
If there's some reason you must use a void* and thus explicitly give up the protections offered by the type system then the pointer still has to point to something. In the end the code will look very similar, just with an extra cast and significantly more opportunity to mess up and wander into the realm of undefined behavior:
StatusCode Stat(const std::string& filename, void* file_status) {
//call the grpc method on the server (abstracted)
// ...
// cast to the appropriate pointer type
uint32_t* status_ptr = static_cast<uint32_t*>(file_status);
// now write the status to the object pointed to by the pointer passed to us
*status_ptr = response.server_crc();
return StatusCode::OK;
}
Not much extra is needed when calling the function, since any pointer-to-object type can be implicitly converted to void*:
StatusCode doSomething() {
std::string filename = "somefile.txt";
uint32_t server_checksum;
StatusCode fileStatus = Stat(filename, &server_checksum);
std::cout << server_checksum << std::endl;
return StatusCode::OK;
}
Live Demo

Using Wire.onRequest by passing a class method?

I have an Arduino sketch that will be working on an Arduino UNO and I am trying to get uno to communicate over the i2c connection with a raspberry pi.
Problem is using wire.h library where method Wire.onRequest is working just fine when I use it like this.
#include <Wire.h>
#define COMM_DELAY 50
#define SLAVE_ADDRESS 0x04
int current_rule = 0;
void initI2c() {
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onReceive(receiveData);
}
// callback for received data
void receiveData(int byteCount) {
while (Wire.available()) {
current_rule = Wire.read();
}
}
but when I try to make this exact result with a class method, I get an error :
invalid use of non-static member function
(with Wire.onRequest(this->receiveData) line gets to be marked red)
Just like this:
void (*funptr)();
typedef void (*Callback)(byte);
class Comm{
public:
int callback_list_size = 0;
bool option_debug;
byte option_address;
int option_comm_delay;
void(*callback_list[256]);
byte *rules;
// function for receiving data. raspberry -> arduino
// Whenever the master sends new data, this method will call the appropriate callback.
void receiveData()
{
byte data;
Serial.println("[INFO] Received new data from master");
while (Wire.available())
{
data = Wire.read();
}
for (int i = 0; i < callback_list_size; i++)
{
if (rules[i] == data){
funptr = callback_list[i];
funptr();
}
}
}
// function for sending data. Called when raspberry request data. arduino -> raspberry
// Whenever the master requests data, this method will be called. For now we don't need this but anyway.
void sendData(int s)
{
if (option_debug)
Serial.println("[INFO] Master requests data!");
}
/* Constructor that takes 3 parameters at max. Only the adress is mandatory others are optional and will be filled with default values
:address - adress of slave(arduino) - Example 0x04
:delay - a delay is needed because I2C clock is quite slow compared to the CPU clock - 50
:debug - for debug purposes if true debug info will be sent to Serial interface - true/false
*/
Comm(byte address, int delay = 50, bool debug = false)
{
option_address = address;
option_comm_delay = delay;
option_debug = debug;
if (debug)
Serial.println("[INFO] Comm Object Created!");
}
// Function needs to be called to initialize the communication channel.
void initI2c()
{
Wire.begin(option_address);
Wire.onReceive(this->sendData);
Wire.onRequest(this->receiveData);
if (option_debug)
Serial.println("[INFO] I2C channel initialized");
}
// Function to add new callback for a rule.
// This function returns id of passed callback
int addCallback(Callback func, byte rule)
{
callback_list_size++;
// Enlarge rules array to keep 1 more byte
byte *temp = new byte[callback_list_size]; // create new bigger array.
for (int i = 0; i + 1 < callback_list_size; i++) // reason fo i+1 is if callback_list_size is 1 than this is the first initializition so we don't need any copying.
{
temp[i] = rules[i]; // copy rules to newer array.
}
delete[] rules; // free old array memory.
rules = temp; // now rules points to new array.
callback_list[callback_list_size - 1] = &func;
rules[callback_list_size - 1] = rule;
return callback_list_size;
}
};
Comm *i2c_comm;
void loop()
{
}
void setup()
{
Serial.begin(9600);
initI2C();
}
void initI2C()
{
i2c_comm = new Comm(0x04, 50, true);
i2c_comm->initI2c();
//Callback Definitions
i2c_comm->addCallback(&rule_1, 0x01);
i2c_comm->addCallback(&rule_2, 0x02);
i2c_comm->addCallback(&rule_3, 0x03);
i2c_comm->addCallback(&rule_4, 0x04);
}
I also tried to make the receiveData method to be static.
But in this case I have an error like this:
invalid use of member Com::callback_list_size in static member function
which makes sense to me as static method won't know which callback_list_size I am talking about.
so I am quite confused about how I can handle such a problem?
You're almost there. Generally speaking in C++ you need to pass a static class method for callback functions.
The error you received after changing your method to static is expected as you're trying to access a member of an instance of the class Comm which cannot be done in a static method in which there is no 'this'.
Here's one of many techniques to consider, but please read over the SO post Using a C++ class member function as a C callback function.
Anyway the approach here is to leverage a static pointer to an instance.
class Comm {
private:
static Comm* pSingletonInstance;
static void OnReceiveHandler() {
if (pSingletonInstance)
pSingletonInstance->receiveData();
}
static void OnSendHandler(int s) {
if (pSingletonInstance)
pSingletonInstance->sendData(s);
}
void initI2c() {
Comm::pSingletonInstance = this; // Assign the static singleton used in the static handlers.
Wire.onReceive(Comm::OnSendHandler);
Wire.onRequest(Comm::OnReceiveHandler);
Wire.begin(option_address);
}
}
// static initializer for the static member.
Comm* Comm::pSingletonInstance = 0;
Again there are many ways to get around this issue but above is an easy one and likely suitable for your project. If you need to manage multiple instances of Comm, you'll have to do something quite different.
Good luck!