Connecting a Multi-Tech Systems Dragonfly™ to Twilio's Sync for IoT Quickstart. Blink a dev board LED.
Dependencies: MQTT MbedJSONValue mbed mtsas
Fork of DragonflyMQTT by
Code to connect a Multi-Tech® MultiConnect® Dragonfly™ to Twilio's Sync for IoT: https://www.twilio.com/docs/api/devices
Uses MQTT over TLS and subscribes to a topic where you can control an LED. See also our Quickstart using this code, here: https://www.twilio.com/docs/quickstart/sync-iot/mqtt-multi-tech-multiconnect-dragonfly-sync-iot
Diff: main.cpp
- Revision:
- 9:2d119fbe7482
- Parent:
- 8:f8a346582627
- Child:
- 10:e9abab84df23
diff -r f8a346582627 -r 2d119fbe7482 main.cpp --- a/main.cpp Thu Sep 14 08:14:18 2017 +0000 +++ b/main.cpp Fri Sep 15 22:41:22 2017 +0000 @@ -1,229 +1,141 @@ -#include "MTSCellularManager.hpp" -#include "TripDataReader.hpp" -#include "TlsMQTTClient.hpp" -#include "Certificates.h" -#include "config.example.hpp" #include <mbed.h> #include <mtsas.h> #include <ssl.h> - -// This line controls the regulator's battery charger. -// BC_NCE = 0 enables the battery charger -// BC_NCE = 1 disables the battery charger -DigitalOut bc_nce(PB_2); +#include <MbedJSONValue.h> +#include <string> +#include "MTSCellularManager.hpp" +#include "TlsMQTTClient.hpp" +#include "certificates.hpp" -DigitalOut ledNotEntered(D7); -DigitalOut ledCellular(D4); -DigitalOut ledGPS(D5); -DigitalOut ledOBD(D8); -DigitalOut ledSendingVehicleData(D6); -DigitalOut ledSendingVehicleDataFailed(D3); +/* + * Sync Settings + * + * Enter a Sync Key & Password, your document unique name, + * and the device name + */ +char* sync_key = "KYXXXXXXXXXXXXXXXXXXXX"; +char* sync_password = "SECRET_HERE"; +char* sync_document = "sync/docs/BoardLED"; +char* sync_device_name = "MST Dragonfly"; + +/* Sync server and MQTT setup; you probably don't have to change these. */ +const char* mqtt_server = "mqtt-sync.us1.twilio.com"; +const uint16_t mqtt_port = 8883; +const uint16_t maxMQTTpackageSize = 512; + +TlsMQTTClient client = TlsMQTTClient(); +DigitalOut led(D7); +DigitalOut bc_nce(PB_2); +const uint8_t MQTT_HEARTBEAT = 15; -static bool enableODB = false; -static Ticker vehicleDataSamplingTicker; -static const int VEHICLE_DATA_SAMPLING_PERIOD_MS = 50; -static const int VEHICLE_DATA_REPORTING_PERIOD_MS = 3000; -MTSSerial obd(D1, D0); -static TripDataReader* pTripDataReader = NULL; -static void vehicleDataSamplingCallback(); +/* + * Our Twilio Connected Devices message handling callback. This is passed as a + * callback function when we subscribe to the document, and any messages will + * appear here. + */ +void callback(MQTT::MessageData& data) +{ + if (data.message.payloadlen > maxMQTTpackageSize) { + return; + } + char buf[maxMQTTpackageSize + 1]; + strncpy(buf, (char*)data.message.payload, data.message.payloadlen); + buf[data.message.payloadlen] = '\0'; + + logDebug("Received new update %s", buf); + /* JSON Parse 'led' */ + MbedJSONValue parser; + parse(parser, buf); + + /* The parser will segfault and reset the board if "led" isn't contained. */ + if (parser.hasMember("led")) { + std::string led_str; + led_str = parser["led"][0].get<std::string>(); + + if (led_str.compare("ON") == 0) { + logDebug("Turning LED ON"); + led = 0; // Active LOW + } else { + logDebug("Turning LED OFF"); + led = 1; // Active LOW + } + } +} -static void sendVehicleData(const MTSCellularManager::GPSStatus& gpsStatus, - const TripDataReader::TripData& tripData); - -static bool exitCmd = false; -int main() { - // Disable the battery charger unless a battery is attached. - bc_nce = 1; +/* + * This function connects to Sync via MQTT. We connect using the key, password, + * and device name defined as constants above, and checks the server + * certificate. + * + * If everything works, we subscribe to the document topic and return. + */ +void connect_mqtt() +{ + MQTTPacket_connectData conn_data = MQTTPacket_connectData_initializer; + conn_data.clientID.cstring = sync_device_name; + conn_data.username.cstring = sync_key; + conn_data.password.cstring = sync_password; + int rc = client.connect( + mqtt_server, + mqtt_port, + MQTT_GATEWAY_PROD_ROOT_CA_PEM, + conn_data + ); + logInfo("MQTT connect result: %d", rc); + + rc = client.subscribe( + "sync/docs/BoardLED", + MQTT::QOS1, + callback + ); + logInfo("MQTT subscription result: %d", rc); +} - ledNotEntered = 1; - ledCellular = 1; - ledGPS = 1; - ledOBD = 1; - ledSendingVehicleData = 1; - ledSendingVehicleDataFailed = 1; - // Change the baud rate of the debug port from the default 9600 to 115200. +/* + * Very basic device loop - all we do is reconnect when disconnected + */ +void loop() +{ + if (client.isConnected()) { + client.yield(MQTT_HEARTBEAT/2.0); + wait(MQTT_HEARTBEAT); + } else { + wait(MQTT_HEARTBEAT*10); + connect_mqtt(); + } +} + + +/* + * In main, we configure our LEDs, connect to Twilio Programmable Wireless, + * and initialize CyaSSL. We then connect our MQTT client for the first time. + * + * When done, we pass control to the MQTT loop, which handles yield()s. + */ +int main() +{ + led = 1; // Active LOW + bc_nce = 1; Serial debug(USBTX, USBRX); debug.baud(115200); - - //Sets the log level to INFO, higher log levels produce more log output. - //Possible levels: NONE, FATAL, ERROR, WARNING, INFO, DEBUG, TRACE mts::MTSLog::setLogLevel(mts::MTSLog::TRACE_LEVEL); - logInfo("Program started"); - - logInfo("Vehicle ID: %s, TYPE: %s", VEHICLE_ID, VEHICLE_TYPE); - if (0 == strcmp(VEHICLE_TYPE, "CAR")) { - logInfo("Enable OBD for it is a car"); - enableODB = true; - } - - logInfo("Initializing cellular"); + // Be sure your SIM is registered in the Twilio console. + // https://www.twilio.com/console/wireless/sims/ + logInfo("Initializing Twilio Programmable Wireless"); MTSCellularManager cellularManager("wireless.twilio.com"); if (! cellularManager.init()) { while (true) { - logError("failed to initialize cellular radio"); - wait(1); - } - } - ledCellular = 0; - - { - logInfo("Initializing GPS"); - cellularManager.enableGps(); - logInfo("GPS Initialized"); - } - - TripDataReader tripDataReader(obd, ledOBD); - pTripDataReader = &tripDataReader; - while (enableODB) { - logInfo("Initializing OBD"); - int r = tripDataReader.init(); - logInfo("OBD Initialization result: %d", r); - if (0 == r) { - logInfo("Initializing OBD sampling ticker"); - vehicleDataSamplingTicker.attach( - &vehicleDataSamplingCallback, - VEHICLE_DATA_SAMPLING_PERIOD_MS / 1000.); - logInfo("OBD sampling ticker initialized"); - break; - } - wait_ms(100); - } - - logInfo("Initializing CyaSSL"); - CyaSSL_Init(); - - while (!exitCmd) { - wait_ms(VEHICLE_DATA_REPORTING_PERIOD_MS); - TripDataReader::TripData tripData; - if (enableODB) { - tripData = tripDataReader.getTripData(); - tripDataReader.resetAverageWindow(); - } - MTSCellularManager::GPSStatus gpsStatus = cellularManager.gpsPollStatus(); - if (gpsStatus.success) { - ledGPS = 0; - sendVehicleData(gpsStatus, tripData); - } else { - ledGPS = 1; + logError("failed to initialize cellular radio"); wait(10); } } - - logInfo("Cleaning up CyaSSL"); - CyaSSL_Cleanup(); - - logInfo("Shutting down cellular"); - cellularManager.uninit(); - - logInfo("Program finished"); - wait(1E12); - return 0; -} - -static void vehicleDataSamplingCallback() { - pTripDataReader->sample(); -} - -static void sendVehicleData(const MTSCellularManager::GPSStatus& gpsStatus, - const TripDataReader::TripData& tripData) { - ledSendingVehicleDataFailed = 1; - ledSendingVehicleData = 0; - - logInfo("Connecting MQTT Client"); - TlsMQTTClient client = TlsMQTTClient(); - MQTTPacket_connectData data = MQTTPacket_connectData_initializer; - data.clientID.cstring = VEHICLE_ID; - data.username.cstring = VEHICLE_KEY; - data.password.cstring = VEHICLE_SECRET; - int result = client.connect(MQTT_GATEWAY_HOST, MQTT_GATEWAY_PORT, MQTT_GATEWAY_PROD_ROOT_CA_PEM, data); - if (MQTT::SUCCESS == result) { - MQTT::Message message; - char buf[512]; - - logInfo("MQTT connected"); + CyaSSL_Init(); + connect_mqtt(); - if (enableODB) { - sprintf(buf, "{" - "\"runtime\": %d," - "\"miles\": %d," - "\"speed\": %f," - "\"minT\": %f," - "\"maxT\": %f," - "\"avgT\": %f," - "\"fuel\": %d," - "\"brake\": %d," - "\"lat\": %lf," - "\"lon\": %lf" - "}", - tripData.runtime, - tripData.distance, - tripData.averageSpeed, - tripData.minimumThrottle, - tripData.maximumThrottle, - tripData.averageThrottle, - tripData.fuel, - tripData.hardBrakeCount, - gpsStatus.latitudeVal, - gpsStatus.longitudeVal); - } else { - sprintf(buf, "{" - "\"speed\": %f," - "\"lat\": %lf," - "\"lon\": %lf" - "}", - gpsStatus.speedVal, - gpsStatus.latitudeVal, - gpsStatus.longitudeVal); - } - - message.qos = MQTT::QOS1; - message.payload = (void*)buf; - message.payloadlen = strlen(buf) + 1; - logInfo("MQTT message publishing buf: %s", buf); - int rc = client.publish("sync/lists/vehicle-" VEHICLE_ID "-data", message); - logInfo("MQTT message publish result: %d", rc); - - logInfo("MQTT disconnecting"); - client.disconnect(); - } else { - ledSendingVehicleDataFailed = 0; - logError("MQTT connection failed %d", result); + /* We're done; pass off control to the loop */ + while (1) { + loop(); } - ledSendingVehicleData = 1; -} - -/* -static void subscribeToTest2() { - rc = client.subscribe("sync/lists/test2", MQTT::QOS1, test2Handler); - logInfo("MQTT subscription result: %d", rc); -} - -static void test2Handler(MQTT::MessageData& data) { - static const size_t MAX_DISPLAY_MESSAGE_SIZE = 30; - char buf[MAX_DISPLAY_MESSAGE_SIZE + 1]; - if (data.message.payloadlen <= MAX_DISPLAY_MESSAGE_SIZE) { - strncpy(buf, (char*)data.message.payload, data.message.payloadlen); - buf[data.message.payloadlen] = '\0'; - } else { - strncpy(buf, (char*)data.message.payload, MAX_DISPLAY_MESSAGE_SIZE - 3); - buf[MAX_DISPLAY_MESSAGE_SIZE-3] = '.'; - buf[MAX_DISPLAY_MESSAGE_SIZE-2] = '.'; - buf[MAX_DISPLAY_MESSAGE_SIZE-1] = '.'; - buf[MAX_DISPLAY_MESSAGE_SIZE] = '\0'; - } - logDebug("topic %s payload received len %d data %s", data.topicName.lenstring.data, data.message.payloadlen, buf); - - // sync client can send binary data using payload format: - // { - // "payload": "ZXhpdA==", # base64 encoded "exit" - // "_iot_meta": { - // "payload_encoding": "base64", - // "payload_type": "application/octet-stream", - // } - // } - if (0 == strncmp((char*)data.message.payload, "exit", data.message.payloadlen)) { - exitCmd = true; - } -}*/ +} \ No newline at end of file