Template coding for ST IoT Challenge 2019

Dependencies:   Cayenne-MQTT-mbed mbed X_NUCLEO_IKS01A2 X_NUCLEO_IDW01M1v2 NetworkSocketAPI

Revision:
18:aedf981aa752
Parent:
13:fc873da5b445
Child:
19:d2a08ee64c36
--- a/main.cpp	Wed Sep 27 15:48:21 2017 +0000
+++ b/main.cpp	Mon Mar 04 21:54:31 2019 +0000
@@ -1,44 +1,10 @@
-/**
- ******************************************************************************
- * @file    main.cpp
- * @author  CLab
- * @version V1.0.0
- * @date    2-December-2016
- * @brief   Simple Example application for using the X_NUCLEO_IKS01A1 
- *          MEMS Inertial & Environmental Sensor Nucleo expansion board.
- ******************************************************************************
- * @attention
- *
- * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *   1. Redistributions of source code must retain the above copyright notice,
- *      this list of conditions and the following disclaimer.
- *   2. Redistributions in binary form must reproduce the above copyright notice,
- *      this list of conditions and the following disclaimer in the documentation
- *      and/or other materials provided with the distribution.
- *   3. Neither the name of STMicroelectronics nor the names of its contributors
- *      may be used to endorse or promote products derived from this software
- *      without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- ******************************************************************************
-*/ 
-
 /* Includes */
 #include "mbed.h"
 #include "XNucleoIKS01A2.h"
+#include "MQTTTimer.h"
+#include "CayenneMQTTClient.h"
+#include "MQTTNetworkIDW01M1.h"
+#include "SpwfInterface.h"
 
 /* Instantiate the expansion board */
 static XNucleoIKS01A2 *mems_expansion_board = XNucleoIKS01A2::instance(D14, D15, D4, D5);
@@ -50,6 +16,21 @@
 static LSM6DSLSensor *acc_gyro = mems_expansion_board->acc_gyro;
 static LSM303AGRAccSensor *accelerometer = mems_expansion_board->accelerometer;
 
+// WiFi network info.
+char* ssid = "M514@unifi";
+char* wifiPassword = "1210EF0712";
+
+// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
+char* username = "4f3fbcb0-3796-11e9-ad96-c15442ccb423";
+char* password = "9e099f3d9aaedd7b76ca94044c6bb488c3999e3c";
+char* clientID = "a22a2090-3797-11e9-8f2a-a7100372a66b";
+
+SpwfSAInterface interface(D8, D2); // TX, RX
+MQTTNetwork<SpwfSAInterface> network(interface);
+CayenneMQTT::MQTTClient<MQTTNetwork<SpwfSAInterface>, MQTTTimer> mqttClient(network, username, password, clientID);
+
+DigitalOut led1(LED1);
+
 /* Helper function for printing floats & doubles */
 static char *print_double(char* str, double v, int decimalDigits=2)
 {
@@ -86,59 +67,218 @@
   return str;
 }
 
+/**
+* Print the message info.
+* @param[in] message The message received from the Cayenne server.
+*/
+void outputMessage(CayenneMQTT::MessageData& message)
+{
+    switch (message.topic)  {
+    case COMMAND_TOPIC:
+        printf("topic=Command");
+        break;
+    case CONFIG_TOPIC:
+        printf("topic=Config");
+        break;
+    default:
+        printf("topic=%d", message.topic);
+        break;
+    }
+    printf(" channel=%d", message.channel);
+    if (message.clientID) {
+        printf(" clientID=%s", message.clientID);
+    }
+    if (message.type) {
+        printf(" type=%s", message.type);
+    }
+    for (size_t i = 0; i < message.valueCount; ++i) {
+        if (message.getValue(i)) {
+            printf(" value=%s", message.getValue(i));
+        }
+        if (message.getUnit(i)) {
+            printf(" unit=%s", message.getUnit(i));
+        }
+    }
+    if (message.id) {
+        printf(" id=%s", message.id);
+    }
+    printf("\n");
+}
+
+/**
+* Handle messages received from the Cayenne server.
+* @param[in] message The message received from the Cayenne server.
+*/
+void messageArrived(CayenneMQTT::MessageData& message)
+{
+    int error = 0;
+    // Add code to process the message. Here we just ouput the message data.
+    outputMessage(message);
+
+    if (message.topic == COMMAND_TOPIC) {
+        switch(message.channel) {
+        case 0:
+            // Set the onboard LED state
+            led1 = atoi(message.getValue());
+            // Publish the updated LED state
+            if ((error = mqttClient.publishData(DATA_TOPIC, message.channel, NULL, NULL, message.getValue())) != CAYENNE_SUCCESS) {
+                printf("Publish LED state failure, error: %d\n", error);
+            }
+            break;
+        }
+        
+        // If this is a command message we publish a response. Here we are just sending a default 'OK' response.
+        // An error response should be sent if there are issues processing the message.
+        if ((error = mqttClient.publishResponse(message.id, NULL, message.clientID)) != CAYENNE_SUCCESS) {
+            printf("Response failure, error: %d\n", error);
+        }
+    }
+}
+
+/**
+* Connect to the Cayenne server.
+* @return Returns CAYENNE_SUCCESS if the connection succeeds, or an error code otherwise.
+*/
+int connectClient(void)
+{
+    int error = 0;
+    // Connect to the server.
+    printf("Connecting to %s:%d\n", CAYENNE_DOMAIN, CAYENNE_PORT);
+    while ((error = network.connect(CAYENNE_DOMAIN, CAYENNE_PORT)) != 0) {
+        printf("TCP connect failed, error: %d\n", error);
+        wait(2);
+    }
+
+    if ((error = mqttClient.connect()) != MQTT::SUCCESS) {
+        printf("MQTT connect failed, error: %d\n", error);
+        return error;
+    }
+    printf("Connected\n");
+
+    // Subscribe to required topics.
+    if ((error = mqttClient.subscribe(COMMAND_TOPIC, CAYENNE_ALL_CHANNELS)) != CAYENNE_SUCCESS) {
+        printf("Subscription to Command topic failed, error: %d\n", error);
+    }
+    if ((error = mqttClient.subscribe(CONFIG_TOPIC, CAYENNE_ALL_CHANNELS)) != CAYENNE_SUCCESS) {
+        printf("Subscription to Config topic failed, error:%d\n", error);
+    }
+
+    // Send device info. Here we just send some example values for the system info. These should be changed to use actual system data, or removed if not needed.
+    mqttClient.publishData(SYS_VERSION_TOPIC, CAYENNE_NO_CHANNEL, NULL, NULL, CAYENNE_VERSION);
+    mqttClient.publishData(SYS_MODEL_TOPIC, CAYENNE_NO_CHANNEL, NULL, NULL, "mbedDevice");
+    //mqttClient.publishData(SYS_CPU_MODEL_TOPIC, CAYENNE_NO_CHANNEL, NULL, NULL, "CPU Model");
+    //mqttClient.publishData(SYS_CPU_SPEED_TOPIC, CAYENNE_NO_CHANNEL, NULL, NULL, "1000000000");
+
+    return CAYENNE_SUCCESS;
+}
+
 /* Simple main function */
 int main() {
-  uint8_t id;
-  float value1, value2;
-  char buffer1[32], buffer2[32];
-  int32_t axes[3];
+    uint8_t id;
+    float value1, value2, value3, value4;
+    char buffer1[32], buffer2[32], buffer3[32], buffer4[32];
+    int32_t axes[3];
   
-  /* Enable all sensors */
-  hum_temp->enable();
-  press_temp->enable();
-  magnetometer->enable();
-  accelerometer->enable();
-  acc_gyro->enable_x();
-  acc_gyro->enable_g();
+    /* Enable all sensors */
+    hum_temp->enable();
+    press_temp->enable();
+    magnetometer->enable();
+    accelerometer->enable();
+    acc_gyro->enable_x();
+    acc_gyro->enable_g();
   
-  printf("\r\n--- Starting new run ---\r\n");
+    printf("\r\n--- Starting new run ---\r\n");
 
-  hum_temp->read_id(&id);
-  printf("HTS221  humidity & temperature    = 0x%X\r\n", id);
-  press_temp->read_id(&id);
-  printf("LPS22HB  pressure & temperature   = 0x%X\r\n", id);
-  magnetometer->read_id(&id);
-  printf("LSM303AGR magnetometer            = 0x%X\r\n", id);
-  accelerometer->read_id(&id);
-  printf("LSM303AGR accelerometer           = 0x%X\r\n", id);
-  acc_gyro->read_id(&id);
-  printf("LSM6DSL accelerometer & gyroscope = 0x%X\r\n", id);
+    hum_temp->read_id(&id);
+    printf("HTS221  humidity & temperature    = 0x%X\r\n", id);
+    press_temp->read_id(&id);
+    printf("LPS22HB  pressure & temperature   = 0x%X\r\n", id);
+    magnetometer->read_id(&id);
+    printf("LSM303AGR magnetometer            = 0x%X\r\n", id);
+    accelerometer->read_id(&id);
+    printf("LSM303AGR accelerometer           = 0x%X\r\n", id);
+    acc_gyro->read_id(&id);
+    printf("LSM6DSL accelerometer & gyroscope = 0x%X\r\n", id);
  
-  while(1) {
-    printf("\r\n");
+    // Initialize the network interface.
+    printf("Initializing interface\n");
+    interface.connect(ssid, wifiPassword, NSAPI_SECURITY_WPA2);
+
+    // Set the default function that receives Cayenne messages.
+    mqttClient.setDefaultMessageHandler(messageArrived);
+
+    // Connect to Cayenne.
+    if (connectClient() == CAYENNE_SUCCESS) {
+        // Start the countdown timer for publishing data every 5 seconds. Change the timeout parameter to publish at a different interval.
+        MQTTTimer timer(5000);
+
+        while (true) {
+            // Yield to allow MQTT message processing.
+            mqttClient.yield(1000);
 
-    hum_temp->get_temperature(&value1);
-    hum_temp->get_humidity(&value2);
-    printf("HTS221: [temp] %7s C,   [hum] %s%%\r\n", print_double(buffer1, value1), print_double(buffer2, value2));
+            // Check that we are still connected, if not, reconnect.
+            if (!network.connected() || !mqttClient.connected()) {
+                network.disconnect();
+                mqttClient.disconnect();
+                printf("Reconnecting\n");
+                while (connectClient() != CAYENNE_SUCCESS) {
+                    wait(2);
+                    printf("Reconnect failed, retrying\n");
+                }
+            }
+        
+            printf("\r\n");
+
+            hum_temp->get_temperature(&value1);
+            hum_temp->get_humidity(&value2);
+            printf("HTS221: [temp] %7s C,   [hum] %s%%\r\n", print_double(buffer1, value1), print_double(buffer2, value2));
     
-    press_temp->get_temperature(&value1);
-    press_temp->get_pressure(&value2);
-    printf("LPS22HB: [temp] %7s C, [press] %s mbar\r\n", print_double(buffer1, value1), print_double(buffer2, value2));
+            press_temp->get_temperature(&value3);
+            press_temp->get_pressure(&value4);
+            printf("LPS22HB: [temp] %7s C, [press] %s mbar\r\n", print_double(buffer1, value3), print_double(buffer2, value4));
 
-    printf("---\r\n");
+            printf("---\r\n");
 
-    magnetometer->get_m_axes(axes);
-    printf("LSM303AGR [mag/mgauss]:  %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+            magnetometer->get_m_axes(axes);
+            printf("LSM303AGR [mag/mgauss]:  %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
     
-    accelerometer->get_x_axes(axes);
-    printf("LSM303AGR [acc/mg]:  %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+            acc_gyro->get_x_axes(axes);
+            printf("LSM6DSL [acc/mg]:      %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
 
-    acc_gyro->get_x_axes(axes);
-    printf("LSM6DSL [acc/mg]:      %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+            accelerometer->get_x_axes(axes);
+            printf("LSM303AGR [acc/mg]:  %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+    
+            acc_gyro->get_g_axes(axes);
+            printf("LSM6DSL [gyro/mdps]:   %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+    
+            wait(1.5);
+    
+            Cayenne.virtualWrite(2, 25.2, "temp", "c");
+            
+            
+            // Publish some example data every few seconds. This should be changed to send your actual data to Cayenne.
+            if (timer.expired()) {
+                int error = 0;
+                if ((error = mqttClient.publishData(DATA_TOPIC, 1, TYPE_TEMPERATURE, UNIT_CELSIUS, value1)) != CAYENNE_SUCCESS) {
+                    printf("Publish temperature failed, error: %d\n", error);
+                }
+                if ((error = mqttClient.publishData(DATA_TOPIC, 2, TYPE_BAROMETRIC_PRESSURE, UNIT_PASCAL, value4)) != CAYENNE_SUCCESS) {
+                    printf("Publish luminosity failed, error: %d\n", error);
+                }
+                if ((error = mqttClient.publishData(DATA_TOPIC, 3, TYPE_PROXIMITY, UNIT_METER, axes[1])) != CAYENNE_SUCCESS) {
+                    printf("Publish barometric pressure failed, error: %d\n", error);
+                }
+                // Restart the countdown timer for publishing data every 5 seconds. Change the timeout parameter to publish at a different interval.
+                timer.countdown_ms(5000);
+            }
+        }    
+    }
+    else {
+        printf("Connection failed, exiting\n");
+    }
 
-    acc_gyro->get_g_axes(axes);
-    printf("LSM6DSL [gyro/mdps]:   %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
-
-    wait(1.5);
-  }
+    if (mqttClient.connected())
+        mqttClient.disconnect();
+    if (network.connected())
+        network.disconnect();
 }