Deserialize Json object from mqtt payload using ArduinoJson library - c++

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()
}

Related

CPPUTest Mock getting data pointer data for testing?

I have a mock for a c function
int Eeprom_WriteBuffer(uint32_t address, uint8_t *data, uint16_t data_len)
{
return mock("eeprom").actualCall("Eeprom_WriteBuffer")
.withParameter("address", address)
.withParameter("data", data)
.withParameter("data_len", data_len)
.withOutputParameter("data", data)
.returnIntValueOrDefault(-1);
}
The function is that I provide it some byte array and it writes this to eeprom.
I want to be able to test that the data my man in the middle function passed down to the mock is what I expect.
eg
TEST (TestGroup, Test)
{
uint8_t data[424];
for (unsigned char & i : data)
i = rand() % UINT8_MAX;
uint8_t actual_data[sizeof(data)];
mock("eeprom").expectOneCall("Eepriom_WriteBuffer")
.withParameter("address", 0)
.<GET THE DATA OUT TO TEST>("data", actual_data, sizeof(actual_data))
.andReturnValue(sizeof(data));
ManInTheMiddle(data, sizeof(data));
MEMCMP_EQUAL(data, actual_data, sizeof(data));
}
Is this possible with this framework or am I going to have to create some buffers to be able to pull the actual data from?

How to compare a char pointer with a string [duplicate]

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.

How to send byte data over gRPC with C/C++?

So I'm using gRPC to store data in a key-value store.
The protos look like this:
syntax = "proto3";
package keyvaluestore;
service KeyValueStore {
rpc AddUser(Credentials) returns (Response) {}
rpc Get(Request) returns (Response) {}
rpc Put(Request) returns (Response) {}
rpc Cput(Request) returns (Response) {}
rpc Delete(Request) returns (Response) {}
}
message Credentials {
string user = 1;
string passwd = 2;
}
message Request {
string user = 1;
string key = 2;
bytes val = 3;
bytes val2 = 4;
string addr = 5;
string command = 6;
}
message Response {
bytes val = 1;
uint32 nbytes = 2;
string message = 3;
}
Right now, the issue is that if we send over say, an image as byte data which can include the null byte, then when the server receives it in the Request object, it treats it as a string; when it does this it only reads it up the the first null byte.
How we pack the Request object on the client side:
bool KeyValueStoreClient::Put(const string& user, const string& key, const char* val) {
Request req;
req.set_user(user);
req.set_key(key);
req.set_val(val);
ClientContext ctx;
Response res;
Status status = stub_->Put(&ctx, req, &res);
}
Server receives req->val() as a string instead of char*:
Status KeyValueStoreServiceImpl::Put(ServerContext* ctx, const Request* req, Response* res) {
// req->val() is a string
}
In your method Put the val argument is a const char*, not a std::string.
Protobuf documentation for C++ generated code (i.e., the interface to your messages) says that when setting a value using a const char* (the void set_foo(const char* value) overload) it uses the first \0 as a terminator.
Tell it the size explicitly by using the void set_foo(const char* value, int size) overload.

Cast an unsigned short/int to char *

I have a function like this
void UnitTestWorker::constructTestPayload(QByteArray &payload)
{
QString List = "127.0.0.1";
unsigned short Port = 12344;
unsigned int RequestId = 1;
memcpy(payload.data(),reinterpret_cast<char*>Port,sizeof(Port));
memcpy(payload.data()+sizeof(Port),reinterpret_cast<char*>RequestId ,sizeof(RequestId ));
}
But I am getting access violation error, it seems like I can't do something like reinterpret_cast<char*>Port or reinterpret_cast<char*>RequestId.
You have to ensure that QByteArray &payload has a sufficient size to receive the data you byte copy to it:
if (payload.size()<sizeof(Port)+sizeof(RequestId))
throw exception ("Ouch !! payload too small");
memcpy(payload.data(),reinterpret_cast<char*>(&Port),sizeof(Port));
memcpy(payload.data()+sizeof(Port),reinterpret_cast<char*>(&RequestId) ,sizeof(RequestId ));

QuickFix C++: getting total size of FIX::Message passed to FIX::Application::fromApp()

I’m using QFIX_1_13_3 and I have the a question regarding the C++ API.
Is FIX::Message::bodyLength() (with default arguments) the correct API to call within FIX::Application::fromApp() in order to obtain the total size of the incoming binary message FIX::Message? From here it looks like it is, but just wanted to confirm:
http://www.quickfixengine.org/quickfix/doc/html/_message_8h_source.html#l00062
00197 int bodyLength( int beginStringField = FIELD::BeginString,
00198 int bodyLengthField = FIELD::BodyLength,
00199 int checkSumField = FIELD::CheckSum ) const
00200 { return m_header.calculateLength(beginStringField, bodyLengthField, checkSumField)
00201 + calculateLength(beginStringField, bodyLengthField, checkSumField)
00202 + m_trailer.calculateLength(beginStringField, bodyLengthField, checkSumField);
00203 }
What I intend to do is memcpy into a memory mapped file the entire received message FIX::Message :
void fromApp( const FIX::Message& message, const FIX::SessionID& sessionID ) {
...
memcpy(persistFilePos, &message, message::bodyLength());
}
Does this make sense?