Skip to main content

Direct command exchange without an Analyzer

AirNgin supports a direct command model for products whose firmware already understands the AirNgin logical operation/command structure. In this mode, no Analyzer is required to translate between the mobile application and the device.

The shared logical identity is the operationName that you define for the device in the producer panel. Each operation has one or more commands such as on, off, or another product-specific value.

Server-to-device topic

projectCode/ServerToDevice/{serial}

Device-to-server topic

projectCode/DeviceToServer

If AES security is enabled for an encrypted Direct Device topic, append /AES according to the security contract.

Send state from the device to the server

For DeviceToServer, send a JSON list of key/value pairs. key is the operationName; value is the command/current value. The list form lets one device report one or many changed operations in the same message.

One operation

[
{ "key": "operationName", "value": "command" }
]

Two operations

[
{ "key": "operationName", "value": "command" },
{ "key": "operationName", "value": "command" }
]

Relay and temperature example

[
{ "key": "ch1", "value": "on" },
{ "key": "temp", "value": "28" }
]

Receive a command from the server

A ServerToDevice/{serial} message contains the target deviceSerial and a data field. data contains the logical key and value for the requested operation.

{"deviceSerial":"AIRN0000000000","data":"{\"key\":\"OPERATION_NAME\",\"value\":\"COMMAND\"}"}

Example for channel 1:

{"deviceSerial":"AIRN0000000000","data":"{\"key\":\"ch1\",\"value\":\"on\"}"}

A typical firmware handler should:

  1. Verify that the received deviceSerial matches the local device identity.
  2. Parse data and resolve the key to the intended operation.
  3. Apply the requested value only if it is valid for that operation.
void callback(char* topic, byte* payload, unsigned int length) {
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}

StaticJsonDocument<256> doc;
if (deserializeJson(doc, message)) {
Serial.println("JSON parse error");
return;
}

const char* deviceSerial = doc["deviceSerial"];
if (strcmp(deviceSerial, myDeviceSerial) != 0) {
Serial.println("Message belongs to another device");
return;
}

message = doc["data"].as<String>();
if (deserializeJson(doc, message)) {
Serial.println("Inner JSON parse error");
return;
}

const char* key = doc["key"];
const char* value = doc["value"];

if (strcmp(key, "ch1") == 0) {
if (strcmp(value, "on") == 0) {
digitalWrite(relayPin, HIGH);
} else if (strcmp(value, "off") == 0) {
digitalWrite(relayPin, LOW);
}
}
}

Direct control