Last active
October 29, 2019 04:50
-
-
Save ma2shita/5ed0e066975b3715f0f326bc0513a12d to your computer and use it in GitHub Desktop.
Using Device shadow of AWS IoT Core via SORACOM Beam
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright (c) 2019 Kohei "Max" MATSUSHITA ([email protected]) | |
| * 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.5 (dont use 6.x) | |
| StaticJsonBuffer<2048> jsonBuffer; | |
| #define __VERSION__ "1.0.1" | |
| #define THING_NAME_METADATA_KEY "awsiotcore_thing_name" | |
| #define LOOP_INTERVAL_METADATA_KEY "loop_interval_ms" | |
| String THING_NAME; | |
| long LOOP_INTERVAL; | |
| void disconnect() { | |
| MqttClient.disconnect(); | |
| Wio.Deactivate(); | |
| Wio.PowerSupplyLTE(false); | |
| } | |
| void apply(JsonObject &content) { | |
| int _running_ = content["_running_"] | 1; | |
| if(_running_ == 0) { | |
| SerialUSB.println("Reboot by Shadow"); | |
| disconnect(); | |
| Wio.SystemReset(); | |
| SerialUSB.println("Good-bye..."); | |
| } | |
| /* IMPL.; put your code for any operation to devices/peripherals */ | |
| int d38 = content["d38"] | 0; | |
| digitalWrite(WIOLTE_D38, d38); | |
| } | |
| /* | |
| * 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; | |
| } | |
| JsonObject &running_state() { | |
| jsonBuffer.clear(); /* for avoid overflow */ | |
| JsonObject& root = jsonBuffer.createObject(); | |
| JsonObject& state = root.createNestedObject("state"); | |
| state["desired"] = RawJson("null"); /* for clear in desired of Device Shadow */ | |
| JsonObject& reported = state.createNestedObject("reported"); | |
| reported["_running_"] = 1; | |
| return root; | |
| } | |
| JsonObject &get_current_state(boolean with_clearing_for_desired = true) { | |
| jsonBuffer.clear(); /* for avoid overflow */ | |
| JsonObject& root = jsonBuffer.createObject(); | |
| JsonObject& state = root.createNestedObject("state"); | |
| if (with_clearing_for_desired) { | |
| state["desired"] = RawJson("null"); /* for clear in desired of Device Shadow */ | |
| } | |
| JsonObject& reported = state.createNestedObject("reported"); | |
| reported["_running_"] = 1; | |
| /* IMPL.; set current status with devices/periphrals */ | |
| reported["d38"] = digitalRead(WIOLTE_D38); | |
| return root; | |
| } | |
| 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(); } | |
| LOOP_INTERVAL = get_metadata_by(Wio, LOOP_INTERVAL_METADATA_KEY, "60000").toInt(); | |
| SerialUSB.print("Loop interval (ms): "); SerialUSB.println(LOOP_INTERVAL); | |
| 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")); | |
| report_current_state(running_state()); | |
| publish(get_shadow_topic(THING_NAME, "/get"), "{}"); | |
| } | |
| void connection_check_and_reconnect() { | |
| if (!MqttClient.connected()) { | |
| SerialUSB.println(MqttClient.state()); | |
| Wio.SystemReset(); | |
| /* Alt impl.; disconnect(); connect(); */ | |
| } | |
| } | |
| void report_current_state(JsonObject ¤t_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