Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Using Device shadow of AWS IoT Core via SORACOM Beam
/*
* Copyright (c) 2019 Kohei "Max" MATSUSHITA
* Released under the MIT license
* https://opensource.org/licenses/mit-license.php
*/
#include <string.h>
#include <stdio.h>
#include <WioLTEforArduino.h>
WioLTE Wio;
#include <WioLTEClient.h>
WioLTEClient WioClient(&Wio);
#include <PubSubClient.h> // https://github.com/SeeedJP/pubsubclient (based on 2.6, dont use official lib)
PubSubClient MqttClient;
#include <ArduinoJson.h> // 5.13.4 (dont use 6.x)
StaticJsonBuffer<2048> jsonBuffer;
#define __VERSION__ "1.0.0"
#define LOOP_INTERVAL (600000)
#define THING_NAME_METADATA_KEY "awsiotcore_thing_name"
String THING_NAME;
void apply(JsonObject &content) {
/* IMPL.; put your code for any operation to devices/peripherals */
int d38 = content["d38"] | 0; /* e.g. */
digitalWrite(WIOLTE_D38, d38); /* e.g. */
}
void disconnect() {
MqttClient.disconnect();
Wio.Deactivate();
Wio.PowerSupplyLTE(false);
}
/*
* get_metadata_by(Wio, "tag_key_name"[, "default value"]);
* => (String) [value of tag_key_name]
* [In case of 404 or 403}=> (String) "default value"
*/
String get_metadata_by(WioLTE &wio, const char* tag_key, const char* default_value = "") {
char url[1024];
sprintf(url, "http://metadata.soracom.io/v1/subscriber.tags.%s", tag_key);
char buf[1024];
wio.HttpGet(url, buf, sizeof(buf));
String content = String(buf);
content.trim();
if (content == "Specified key does not exist." || /* == 404 */
content == "You are not allowed to access Metadata Server.") { /* == 403 */
content = String(default_value);
content.trim();
}
return content;
}
/*
* char dest[src_str.length() + 1];
* util_string_cast_to_char(src_str, dest, sizeof(dest));
*/
void util_string_cast_to_char(String src, char* dest, int datasize) {
int src_len = src.length() + 1;
char s[src_len];
src.toCharArray(s, src_len);
strncpy(dest, s, datasize);
}
/*
* publish("/foo/bar", "{}");
*/
boolean publish(String topic, String payload) {
connection_check_and_reconnect();
SerialUSB.print("Publish to "); SerialUSB.println(topic);
SerialUSB.println(payload);
char buf_t[topic.length() + 1];
util_string_cast_to_char(topic, buf_t, sizeof(buf_t));
char buf_p[payload.length() + 1];
util_string_cast_to_char(payload, buf_p, sizeof(buf_p));
return MqttClient.publish(buf_t, buf_p);
}
/*
* subscribe("/foo/bar");
*/
boolean subscribe(String topic) {
SerialUSB.print("Subscribe to "); SerialUSB.println(topic);
char buf_t[topic.length() + 1];
util_string_cast_to_char(topic, buf_t, sizeof(buf_t));
return MqttClient.subscribe(buf_t);
}
/*
* get_shadow_topic("AnyName", "/foo/bar")
* => (String) "$aws/things/AnyName/shadow/foo/bar"
*/
String get_shadow_topic(String thing_name, String suffix) {
char buf_t[1024];
util_string_cast_to_char(thing_name, buf_t, sizeof(buf_t));
char buf_s[1024];
util_string_cast_to_char(suffix, buf_s, sizeof(buf_s));
char topic[1024];
sprintf(topic, "$aws/things/%s/shadow%s", buf_t, buf_s);
String r = String(topic);
return r;
}
void callback(char* topic, byte* payload, unsigned int length) {
String buf_t = String(topic);
SerialUSB.print("Incoming: "); SerialUSB.println(buf_t);
payload[length] = '\0'; /* https://hawksnowlog.blogspot.com/2017/06/convert-byte-array-to-string.html */
String buf_p = String((char*) payload);
SerialUSB.println(buf_p);
jsonBuffer.clear(); /* for avoid overflow */
JsonObject &root = jsonBuffer.parseObject(buf_p);
if (!root.success()) { SerialUSB.println("parse failed"); };
if (buf_t == get_shadow_topic(THING_NAME, "/get/accepted") || buf_t == get_shadow_topic(THING_NAME, "/update/delta")) {
JsonObject &state = root.get<JsonObject>("state");
if (state.containsKey("reported")) { /* sync with reported status */
JsonObject &reported = state.get<JsonObject>("reported");
apply(reported);
}
if (state.containsKey("delta")) { /* sync with remaining delta */
JsonObject &delta = state.get<JsonObject>("delta");
apply(delta);
}
if (buf_t == get_shadow_topic(THING_NAME, "/update/delta")) {
apply(state);
}
report_current_state(get_current_state());
}
}
void connect() {
SerialUSB.println("Wio.PowerSupplyLTE");
Wio.PowerSupplyLTE(true);
delay(500);
SerialUSB.println("Wio.TurnOnOrReset");
if (!Wio.TurnOnOrReset()) { Wio.SystemReset(); }
SerialUSB.println("Wio.Activate");
if (!Wio.Activate("soracom.io", "sora", "sora")) { Wio.SystemReset(); }
char or_imsi[16];
Wio.GetIMSI(or_imsi, sizeof(or_imsi));
THING_NAME = get_metadata_by(Wio, THING_NAME_METADATA_KEY, or_imsi);
SerialUSB.print("ThingName(mqtt_id): "); SerialUSB.println(THING_NAME);
if (THING_NAME == "") { Wio.SystemReset(); }
MqttClient.setServer("beam.soracom.io", 1883);
MqttClient.setClient(WioClient);
MqttClient.setCallback(callback);
SerialUSB.println("MqttClient.connect");
char mqtt_id[THING_NAME.length() + 1];
util_string_cast_to_char(THING_NAME, mqtt_id, sizeof(mqtt_id));
if (!MqttClient.connect(mqtt_id)) {
SerialUSB.println(MqttClient.state());
Wio.SystemReset();
}
subscribe(get_shadow_topic(THING_NAME, "/get/accepted"));
subscribe(get_shadow_topic(THING_NAME, "/update/delta"));
publish(get_shadow_topic(THING_NAME, "/get"), "{}");
}
void connection_check_and_reconnect() {
if (!MqttClient.connected()) {
SerialUSB.println(MqttClient.state());
disconnect();
connect();
/* Alt impl.; Wio.SystemReset(); */
}
}
JsonObject &get_current_state() {
jsonBuffer.clear(); /* for avoid overflow */
JsonObject& root = jsonBuffer.createObject();
JsonObject& state = root.createNestedObject("state");
state["desired"] = RawJson("null"); /* for erase in Device Shadow */
JsonObject& reported = state.createNestedObject("reported");
/* IMPL.; set current status with devices/periphrals */
reported["d38"] = digitalRead(WIOLTE_D38); /* e.g. */
return root;
}
void report_current_state(JsonObject &current_state) {
String json;
current_state.printTo(json);
publish(get_shadow_topic(THING_NAME, "/update"), json);
}
void setup() {
delay(500);
SerialUSB.println();
SerialUSB.println(__VERSION__);
Wio.Init();
/* IMPL.; put your initialize code below, to run at once */
pinMode(WIOLTE_D38, OUTPUT); /* e.g. */
connect();
}
void loop() {
connection_check_and_reconnect();
unsigned long next = millis();
while (millis() < next + LOOP_INTERVAL) {
MqttClient.loop();
}
/* IMPL.; put your main code below, to run repeatedly */
SerialUSB.println("loop");
report_current_state(get_current_state()); /* e.g.) likes ping */
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.