String "Command received: " not shown on the serial monitor output

Hello,

I am trying to develop functionality that need data from this platform.
As I am relatively new to this, I probably miss something simple but quit importent to receive data form this plaform through JSON.

The workflow gives the result I expected, with the latest field having the expected value.

{
  "time": "2017-02-16T08:07:08.971Z",
  "data": {},
  "applicationId": "58a4095250530f0001031a72",
  "triggerId": "58a409f585f6e20001f725cb-Hkz8sFWFl",
  "triggerType": "virtualButton",
  "relayId": "58932b056b20be000123219d",
  "relayType": "user",
  "flowId": "58a409f585f6e20001f725cb",
  "flowName": "AVRI Kalender Led",
  "applicationName": "AVRI",
  "globals": {},
  "text": "groen"
}

The question is why it is not received by my device?

Here all the documentation I can provide (including my code through the Arduino IDE)
Application

My Sandbox / AVRI

ApplicationID

58a4095250530f0001031a72

DeviceID

58a4099c03c8370001bf8509

WorkflowID

58a409f585f6e20001f725cb

Status Connect

Connected

Device Type

Standalone

Device Attributes

Data Type = String
Name = text

Workflow Node: Device Command

Properties

Label = SendText
Select the device(s) to send command to. = Checked: Select specific devices and tags
Device IDs / Tags = AVRI Kalender Led
Command Name Template = setLed
Command Payload Type = JSON Template
Command Payload JSON Template = {“STR1”:“text”}

Debug (Node: DebugCommand)
Extra info: the debug info was just gennereted, but the device was disconnected.
The test upon this request is based was yesterday. But this is not relevant for the case!

{
“time”: “2017-02-16T08:07:08.971Z”,
“data”: {},
“applicationId”: “58a4095250530f0001031a72”,
“triggerId”: “58a409f585f6e20001f725cb-Hkz8sFWFl”,
“triggerType”: “virtualButton”,
“relayId”: “58932b056b20be000123219d”,
“relayType”: “user”,
“flowId”: “58a409f585f6e20001f725cb”,
“flowName”: “AVRI Kalender Led”,
“applicationName”: “AVRI”,
“globals”: {},
“text”: “groen”
}

Sourcecode

/**
*
*
*
*
*
*
*/

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <Losant.h>

// WiFi credentials.
const char* WIFI_SSID = "x...x";
const char* WIFI_PASS = "x...x";

// Losant credentials.
const char* LOSANT_DEVICE_ID = "x...x";
const char* LOSANT_ACCESS_KEY = "x...x";
const char* LOSANT_ACCESS_SECRET = "x...x";

#define BUTTON_PIN 5
#define MQTT_MAX_PACKET_SIZE 256

volatile bool buttonStateChanged = false;

WiFiClientSecure wifiClient;

LosantDevice device(LOSANT_DEVICE_ID);


void setup() {
  Serial.begin(115200);
  while(!Serial) { }
  
  pinMode(BUTTON_PIN, INPUT);

  // Attach an interrupt to the button input so we are
  // notified when it changes.
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonStateChangedHandler, CHANGE);

  // Register the command handler to be called when a command is received
  // from the Losant platform.
  device.onCommand(&handleCommand); 
  
  connect();

}


void loop() {
  
  bool toReconnect = false;

  if(WiFi.status() != WL_CONNECTED) {
    Serial.println("Disconnected from WiFi");
    toReconnect = true;
  }

  if(!device.connected()) {
    Serial.println("Disconnected from MQTT");
    Serial.println(device.mqttClient.state());
    toReconnect = true;
  }

  if(toReconnect) {
    connect();
  }

  if(buttonStateChanged) {
    bool value = digitalRead(BUTTON_PIN);
    reportState(value == 0 ? true : false);
    buttonStateChanged = false;
  }

  device.loop();
  
  delay(200);

}

/**
 * Connects to WiFi and then to the Losant platform.
 */
void connect() {

  // Connect to Wifi.
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WIFI_SSID);

  WiFi.begin(WIFI_SSID, WIFI_PASS);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println();

  Serial.print("Authenticating Device...");
  HTTPClient http;
  http.begin("http://api.losant.com/auth/device");
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Accept", "application/json");

  /* Create JSON payload to sent to Losant
  */

  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["deviceId"] = LOSANT_DEVICE_ID;
  root["key"] = LOSANT_ACCESS_KEY;
  root["secret"] = LOSANT_ACCESS_SECRET;
  String buffer;
  root.printTo(buffer);

  int httpCode = http.POST(buffer);

  if (httpCode > 0) {
    if (httpCode == HTTP_CODE_OK) {
      Serial.println("This device is authorized!");
    } else {
      Serial.println("Failed to authorize device to Losant.");
      if (httpCode == 400) {
        Serial.println("Validation error: The device ID, access key, or access secret is not in the proper format.");
      } else if (httpCode == 401) {
        Serial.println("Invalid credentials to Losant: Please double-check the device ID, access key, and access secret.");
      } else {
        Serial.println("Unknown response from API");
      }
    }
  } else {
    Serial.println("Failed to connect to Losant API.");

  }

  http.end();

  // Connect to Losant.
  Serial.println();
  Serial.print("Connecting to Losant...");

  device.connectSecure(wifiClient, LOSANT_ACCESS_KEY, LOSANT_ACCESS_SECRET);

  unsigned long connectionStart = millis();
  while(!device.connected()) {
    delay(500);
    Serial.print(".");

    // If we can't connect after 5 seconds, restart the board.
    if(millis() - connectionStart > 5000) {
      Serial.println("Failed to connect to Losant, restarting board.");
      ESP.restart();
    }
  }

  Serial.println("Connected!");
  Serial.println();
  Serial.println("This device is now ready for use!");
 
}

/**
 * Reports state to Losant whenever button is pressed.
 */
void reportState(bool isPressed) {
  // Build the JSON payload and send to Losant.
  // { "button" : <bool> }
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["button"] = isPressed ? 1 : 0;
  device.sendState(root);

  Serial.println(isPressed ? "Ingedrukt" : "Geen actie");
}

// Called whenever the device receives a command from the Losant platform.

void handleCommand(LosantCommand *command) {
  
  Serial.print("Command received: ");
  Serial.println(command->name);
  JsonObject& payload = *command->payload;
  
  //print payload
  Serial.print("Payload is: ");
  payload.printTo(Serial);
  Serial.println();

  if (strcmp(command->name, "setLed") == 0) {
      setLed();
  }
  
}

// Handles the setLed from payload.
void setLed() {
  Serial.println("Action...");
}

/**
 * Called whenever the button interrupt is triggered.
 */
void buttonStateChangedHandler() {
  buttonStateChanged = true;
}

Serial monitor output

Connecting to x…x
… did not make a copy, but as the device is authorized should be self explaining.
This device is authorized!

QUESTION (expected a string, but not shown)

Why is the string "Command received: " not shown on the serial monitor output?
Or: what am I missing?

There are a couple of things I would try:

  • Try increasing the packet size on the underlying pubsubclient: Sending commands with larger payloads Unfortunately the underlying library silently refuses commands if the packet is too large.

  • Does the command show up in the application communication log? This log is a live stream of all device communication. Open it another tab and then attempt to send the command. It should show up.

  • Try manually sending the command using the debug tab on the device page.

Hello Brandon,
Thanks for your quick respons.

  • Trying to increase the packet size.
    I have search the Arduino folder and the only place I found was in the file MQTTClient.h
    Printing the MAX_MQTT_PACKET_SIZE it give 128 as a result.
    The only place I see it is set is in MQTTClient.h. And there it is dynamically set; I don’t see the size 128 been set.
  • Application log
    The application log give expected results:
    Received Payload
▶
(root){} 3 keys
"name":"setLed"
▶
"payload":{} 1 key
"STR1":"text"
"time":"2017-02-17T18:53:47.603Z"
  • manually sending the command
    This shows the same outcome as the DeviceCommand (as expected), except for the “time”:
User Wim ten Brinke sent command setLed to Device AVRI Kalender Led
POST/applications/58a4095250530f0001031a72/devices/58a4099c03c8370001bf8509/command
Fri Feb 17 2017 20:12:11 GMT+01:00
Received Payload
▶
(root){} 3 keys
"name":"setLed"
▶
"payload":{} 1 key
"STR1":"text"
"time":"2017-02-17T19:12:11.983Z"

Looking forward to a reaction. Kind regards, Wim

Hello,
The last few days I tried to several options.

  • cleaned the code
  • bought a new device; a device can be defect
  • installed PlatformIO on Atom and added some Losant things, build the software and uploaded it to the new device.
    Unfortunately it does not make a difference.
    The function “void handleCommand(LosantCommand *command)” seems not activated; the text from the command "Serial.print("Command received: "); is not printed in the serial monitor (not on PlatformIO and not on the ArduinoIDE).

Is there anything I can do on my side to provide info that can lead to the solution?

I’ll install the source you provided above on a NodeMCU and test it out. Hopefully I’ll be able to reproduce the issue.

I flashed your exact code using Arduino IDE v1.6.12 and did not experience any issues. Here’s a screenshot of the serial monitor:

I sent the command from the device page:

Thanks a lot.
I test it again.
Can a router be causing trouble as far as you know?
Should not because of the HTTP, but may be there is an issue somewhere about this.

i am having a similar problem on an Arduino MKR Wifi 101.
Can push data to the dash board, and the dashboard button I created sends a POST back with dta I expect - I see that in the log, but the device never seems to get it.

Hi @Mark_van_der_Pol,

To assist with debugging, would you be able to post your Arduino code?

Thanks!
Julia

I took example code and made it like my code, and here it is, also not reporting Command Received even though the log shows a POST happened
User Mark van der Pol sent command Calibrate toDevice SteeleMkr

POST/applications/5xxbdd4904156e0008b023cc/devices/5xxe2ddee81e62000793b4fd/command

Fri May 10, 2019 10:43:40 GMT-07:00

Received Payload

  • :arrow_forward:

(root){} 3 keys

  • “name”:“Calibrate”
  • :arrow_forward:

“payload”:{} 2 keys
* “calibrate”:true
* “delta”:23

  • “time”:“2019-05-10T17:43:40.444Z”:

THE CODE

/**
 * Example that connects an Arduino Zero with Arduino WiFi Shield 101 to the
 * Losant IoT platform. This example reports state to Losant whenever
 * a button is pressed. It also listens for the "toggle" command to turn the
 * LED on and off.
 *
 * This example assumes the following connections:
 * Button connected to pin 14.
 * LED connected to pin 12.
 *
 * Copyright (c) 2016 Losant. All rights reserved.
 * http://losant.com
 */
#include "arduino_secrets.h"
// WiFiNINA - Version: 1.3.0
#include <WiFiNINA.h>

// PubSubClient - Version: 2.6.0  !!! Critical version. Version 2.7.0 fails to connect to Losant.
#include <PubSubClient.h>

// ArduinoJson - Version: 5.13.5    !!! Critical Version 6 alters the way JSON buffers are treated, and Losant sample code will fail.
#include <ArduinoJson.h>
#include <ArduinoJson.hpp>

// losant-mqtt-arduino - Version: Latest 

#include <Losant.h>

// WiFi credentials.
const char* WIFI_SSID = SECRET_SSID_2;
const char* WIFI_PASS = SECRET_PASS_2;

// Losant credentials.
const char* LOSANT_DEVICE_ID = SECRET_DEVICE_ID;
const char* LOSANT_ACCESS_KEY = SECRET_ACCESS_KEY;
const char* LOSANT_ACCESS_SECRET = SECRET_ACCESS_SECRET;

const int BUTTON_PIN = 14;
const int LED_PIN = LED_BUILTIN;

bool ledState = false;

WiFiSSLClient wifiClient;

// For an unsecured connection to Losant.
// WiFiClient wifiClient;

LosantDevice device(LOSANT_DEVICE_ID);

void toggle() {
  Serial.println("Toggling LED.");
  ledState = !ledState;
  digitalWrite(LED_PIN, ledState ? HIGH : LOW);
}

// Called whenever the device receives a command from the Losant platform.
void handleCommand(LosantCommand *command) {
  Serial.print("Command received: ");
  Serial.println(command->name);

  if(strcmp(command->name, "toggle") == 0) {
    toggle();
  }
}

void connect() {

  // Connect to Wifi.
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WIFI_SSID);

  WiFi.begin(WIFI_SSID, WIFI_PASS);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Connect to Losant.
  Serial.println();
  Serial.print("Connecting to Losant...");

device.connectSecure(wifiClient, LOSANT_ACCESS_KEY, LOSANT_ACCESS_SECRET);

  // For an unsecured connection.
  //device.connect(wifiClient, ACCESS_KEY, ACCESS_SECRET);

  while(!device.connected()) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("Connected!");
}

void setup() {
  Serial.begin(115200);
  while(!Serial) { }
  pinMode(BUTTON_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);

  // Register the command handler to be called when a command is received
  // from the Losant platform.
  device.onCommand(&handleCommand);

  connect();
}

void buttonPressed( boolean state) {
  Serial.println("Button Report");

  // Losant uses a JSON protocol. Construct the simple state object.
  // { "button" : true }
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["button"] = state;

  // Send the state to Losant.
  device.sendState(root);
}

int buttonState = 0;

void loop() {

  bool toReconnect = false;

  if(WiFi.status() != WL_CONNECTED) {
    Serial.println("Disconnected from WiFi");
    toReconnect = true;
  }

  if(!device.connected()) {
    Serial.println("Disconnected from Losant");
    Serial.println(device.mqttClient.state());
    toReconnect = true;
  }

  if(toReconnect) {
    connect();
  }

  device.loop();

  int currentRead = digitalRead(BUTTON_PIN);
  static int next_ka = 1000;
  int now;
  now = millis();
  if((currentRead != buttonState) || (now > next_ka))  {
    buttonState = currentRead;
    buttonPressed(buttonState);
    next_ka = now+15000; // 15 second keep alives sent
  }

  delay(100);
}

Hi @Mark_van_der_Pol,

I looked into this further and it seems as though your handleCommand function is skipping toggle() as your if statement is returning false. The Input Block command name is Calibrate, while the device is looking for toggle. Thus, the toggle() function is never being called:

if(strcmp(command->name, "toggle") == 0) { //strcmp compares the two strings, returns 0 if true
    toggle(); // the strings are not the same so this line is skipped
  }

To remedy this, you can change the name of the Command coming from Losant, or change “toggle” to “Calibrate” in your code.

If this does not solve it, please let me know and we will look again! :smile:

Thanks,
Julia

It may be skipping the command name as it is not a match, but it is not printing the unconditionnal ‘Command received’ or command name, either.

Is this a constraint in the on-handle command processing that serial access is thwarted as it is a different context?

I am looking further and thanks for looking!

|\/|ark

Also, putting in a counter to see how often handleCommand is invoked - it stays at 0.
So either the handleCommand is not properly registered with the onCommand method, or it is registered, and never invoked because a command is never received by the MQTT stack.

Next test was to take the LosantDevice and change the

device.commandTopic = String("#");
instead of the default losant/xxdevicexx/command
so it subscribes to all topics.

Now I get things sent to me, which are essentially echoes of the data that I report. Does mean that the call back is registering and invoked, at least sometimes. But not on commands when I trigger them.

I further hack device to put in my own commandReceived handler, and I can see the things pubsubclient sends me.
device.mqttClient.setCallback(my_commandReceived); // Register the command handler to be called when a command is received

Now I can see the topic received, and the raw payload. The payload is consistent with the data reports I send, as expected. What is really fascinating is that sometimes, in the tail of the buffer, I see parts of the command I am trying to send. So the broker is dispatching the commands, and someplace in the bowels of pubsubclient it gets the packet and decides to ignore it. Because buffers are heavily re-used, the data lingers and I get to see this evidence. If I change values in the command, the leaked buffer contents changes accordingly. So it is for sure server data getting to the client. Just the client is ignoring it.

WHY?

The leaked buffer also contains slightly different data than what the device log is showing on the app.losant.com devices pages. Also odd.
eg I see partially --}}rue,“delta”:25},“time”:{"$date":"2019-05-f4fc–
when the log shows the command was
“name”:“Activate”
“payload”:{} 2 keys
* “calibrate”:true
* “delta”:25
* “time”:“2019-05-11T06:24:07.329Z”

So the $date is not the same. Possibly as $tokens are special. Still an odd mangling of the packet.

Anyway, it seems commands are sneaking into the client somehow, and promptly ignored. The only place where the pubsubclient makes this decision is the device::loop method:
uint8_t type = buffer[0]&0xF0;
if (type == MQTTPUBLISH) {

So maybe the broker/server is not setting this correct?

|\/|ark

Hi @Mark_van_der_Pol,

I don’t see anything obvious that could be causing this, but I will continue to look into it. In the meantime, the couple of similar forum posts I could find were solved by increasing the MQTT packet size. If you have already done this, great! We can eliminate it as a possibility. If you have not, I would recommend giving it a try as it is a silent refusal by the underlying the library if the packet is too large.

I will continue testing and see what I can find.

Thanks!
Julia

Thanks for continuing to look, Julia!

The packet size has been set to 1024, so should be roomy enough for anything the Losant Server wants to send.

I cant see the head of the packet that does come in from the server, but the note in the debug log classifies it as a POST, not a PUBLISH. Messages from the device are classified as MQTT. Maybe nothing, and I will explore getting more info from the pubsubclient to debug this.

As well as exploring other MQTT brokers to see if it works as expected in other implementations.

Thanks,
|\/|ark.

1 Like

And - the packet size I set wasn’t actually being used in the library code…
Setting it for my self, before including the header pubsubclient.h file might work for my compilation unit, but not for the linked library. Duh.

Forcing an edit in the actual pubsubclient.h file fixed this issue. Still have to use version 2.6, though.

Thanks for the responsive support, and the hint that made it finally click!

Final mystery is the packet arrives as follows:
{
“name”: “Activate”,
“payload”: {
“calibrate”: true,
“delta”: 12
},
“time”: {
“$date”: “2019-05-14T00:52:08.168Z”
}
}

whereas the server log shows it sent
“name”:“Activate”
“payload”:{} 2 keys

  • “calibrate”:true
  • “delta”:12
  • “time”:“2019-05-14T00:52:08.168Z”

So the losant library code doesn’t properly pass on the time parameter to the callback. I have to replace
command.time = root["$time"];
with
command.time = root[“time”]["$date"];
in
void commandReceived(char* topic, byte* payload, unsigned int length)

|\/|ark