Pelion Device Management example over 15.4 Thread for Thunderboard Sense 2 board

Dependencies:   ICM20648 BMP280 Si1133 Si7210 AMS_CCS811_gas_sensor SI7021

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 // Includes and declarations for Pelion DM Client to work
00002 #include "simple-mbed-cloud-client.h"
00003 #include "BlockDevice.h"
00004 #include "LittleFileSystem.h"
00005 #include "NetworkInterface.h"
00006 #include "Nanostack.h"
00007 #include "ns_file_system.h"
00008 
00009 // Thunderboard Sense 2 connects over 802.15.4 by default. Since the mesh stack is a bit iffy.
00010 // We'll register a 'network down' handler to act as a kind of watchdog for the Pelion DM Client.
00011 NetworkInterface *net = NetworkInterface::get_default_instance();
00012 
00013 // Thunderboard Sense 2 has a 1M external flash, which is being shared between upgrade storage and LittleFS.
00014 // LittleFS is instantiated at the start of storage, until the start address for upgrade storage.
00015 // Currently, this split is at 256/768 for FS/upgrade.
00016 BlockDevice* bd = BlockDevice::get_default_instance();
00017 SlicingBlockDevice sd(bd, 0, MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS);
00018 
00019 #if COMPONENT_SD || COMPONENT_NUSD
00020 // Use FATFileSystem for SD card type blockdevices
00021 FATFileSystem fs("fs");
00022 #else
00023 // Use LittleFileSystem for non-SD block devices to enable wear leveling and other functions
00024 LittleFileSystem fs("fs");
00025 #endif
00026 
00027 // Default User button for GET example and for resetting the storage
00028 InterruptIn button(BTN0);
00029 
00030 // How often to fetch sensor data (in seconds)
00031 #define SENSORS_POLL_INTERVAL 3.0
00032 
00033 // Send all sensor data or just limited (useful for when running out of memory)
00034 #define SEND_ALL_SENSORS
00035 
00036 // Sensors related includes and initialization
00037 #include "BMP280.h"
00038 #include "Si1133.h"
00039 #include "SI7021.h"
00040 #include "Si7210.h"
00041 #include "AMS_CCS811.h"
00042 #include "ICM20648.h"
00043 
00044 /* Turn on power supply to ENV sensor suite */
00045 DigitalOut env_en(PF9, 1);
00046 /* Turn on power to CCS811 sensor */
00047 DigitalOut ccs_en(PF14, 1);
00048 /* Turn on power to hall effect sensor */
00049 DigitalOut hall_en(PB10, 1);
00050 /* Turn on power to IMU */
00051 DigitalOut imu_en(PF8, 1);
00052  
00053 I2C env_i2c(PC4, PC5);
00054 BMP280 sens_press_temp(env_i2c);
00055 Si1133 sens_light(PC4, PC5);
00056 SI7021 sens_hum_temp(PC4, PC5, SI7021::SI7021_ADDRESS, 400000);
00057 I2C hall_i2c(PB8, PB9);
00058 silabs::Si7210 sens_hall(&hall_i2c);
00059 InterruptIn ccs_int(PF13);
00060 I2C ccs_i2c(PB6, PB7);
00061 AMS_CCS811 sens_aqs(&ccs_i2c, PF15);
00062 ICM20648 sens_imu(PC0, PC1, PC2, PC3, PF12);
00063 
00064 // Declaring pointers for access to Pelion Client resources outside of main()
00065 MbedCloudClientResource *res_button;
00066 MbedCloudClientResource *res_led;
00067 
00068 // Additional resources for sensor readings
00069 #ifdef SEND_ALL_SENSORS
00070 MbedCloudClientResource *res_light;
00071 MbedCloudClientResource *res_pressure;
00072 MbedCloudClientResource *res_temperature1;
00073 MbedCloudClientResource *res_humidity;
00074 MbedCloudClientResource *res_temperature2;
00075 MbedCloudClientResource *res_co2;
00076 MbedCloudClientResource *res_tvoc;
00077 MbedCloudClientResource *res_field;
00078 MbedCloudClientResource *res_temperature3;
00079 MbedCloudClientResource *res_accelerometer_x;
00080 MbedCloudClientResource *res_accelerometer_y;
00081 MbedCloudClientResource *res_accelerometer_z;
00082 MbedCloudClientResource *res_gyroscope_x;
00083 MbedCloudClientResource *res_gyroscope_y;
00084 MbedCloudClientResource *res_gyroscope_z;
00085 MbedCloudClientResource *res_temperature4;
00086 #endif /* SEND_ALL_SENSORS */
00087 
00088 // An event queue is a very useful structure to debounce information between contexts (e.g. ISR and normal threads)
00089 // This is great because things such as network operations are illegal in ISR, so updating a resource in a button's fall() function is not allowed.
00090 EventQueue eventQueue;
00091 
00092 // When the device is registered, this variable will be used to access various useful information, like device ID etc.
00093 static const ConnectorClientEndpointInfo* endpointInfo;
00094 
00095 /**
00096  * POST handler - prints the content of the payload
00097  * @param resource The resource that triggered the callback
00098  * @param buffer If a body was passed to the POST function, this contains the data.
00099  *               Note that the buffer is deallocated after leaving this function, so copy it if you need it longer.
00100  * @param size Size of the body
00101  */
00102 void blink_callback(MbedCloudClientResource *resource, const uint8_t *buffer, uint16_t size) {
00103     static int num_count = 0;
00104     static int event = 0;
00105 
00106     static DigitalOut led_ch_red(PD11, 1);
00107     static DigitalOut led_ch_green(PD12, 1);
00108     static DigitalOut led_ch_blue(PD13, 1);
00109 
00110     static DigitalOut led_com_0(PI0, 0);
00111     static DigitalOut led_com_1(PI1, 0);
00112     static DigitalOut led_com_2(PI2, 0);
00113     static DigitalOut led_com_3(PI3, 0);
00114 
00115     static DigitalOut led_rgb_en(PJ14, 0);
00116 
00117     if (buffer != NULL) {
00118         printf("POST received. POST data: %s\n", buffer);
00119 
00120         if (event > 0) {
00121             printf("Not blinking since previous blink still in progress\n");
00122             return;
00123         }
00124         num_count = 0;
00125         for (size_t i = 0, num_arg = 0; i < 20 || buffer[i] == 0; i++) {
00126             if (buffer[i] == ':') {
00127                 num_arg++;
00128                 continue;
00129             }
00130 
00131             if (buffer[i] >= '0' && buffer[i] <= '9') {
00132                 switch (num_arg) {
00133                     case 0:
00134                         if (buffer[i] == '1') {
00135                             led_ch_red = 1;
00136                         } else {
00137                             led_ch_red = 0;
00138                         }
00139                         break;
00140                     case 1:
00141                         if (buffer[i] == '1') {
00142                             led_ch_green = 1;
00143                         } else {
00144                             led_ch_green = 0;
00145                         }
00146                         break;
00147                     case 2:
00148                         if (buffer[i] == '1') {
00149                             led_ch_blue = 1;
00150                         } else {
00151                             led_ch_blue = 0;
00152                         }
00153                         break;
00154                     case 3:
00155                         num_count = ((buffer[i] - 0x30) * 2) - 1;
00156                         printf("blinking %d\n", num_count);
00157                         break;
00158                     default:
00159                         break;
00160                 }
00161             } else {
00162                 //garbage...
00163                 continue;
00164             }
00165             if (num_count > 0) {
00166                 break;
00167             }
00168         }
00169 
00170         if (num_count > 0) {
00171             led_rgb_en = 1;
00172             led_com_0 = 1;
00173             led_com_1 = 1;
00174             event = eventQueue.call_in(1000, blink_callback, resource, (const uint8_t *)NULL, 0);
00175             if (event == 0) {
00176                 led_rgb_en = 0;
00177                 num_count = 0;
00178             }
00179         }
00180     } else {
00181         num_count--;
00182         led_com_0 = (num_count & 1);
00183         led_com_1 = (num_count & 1);
00184 
00185         if (num_count == 0) {
00186             led_rgb_en = 0;
00187             event = 0;
00188         } else {
00189             event = eventQueue.call_in(1000, blink_callback, resource, (const uint8_t *)NULL, 0);
00190             if (event == 0) {
00191                 led_rgb_en = 0;
00192                 num_count = 0;
00193             }
00194         }
00195     }
00196 }
00197 
00198 /**
00199  * Button function triggered by the physical button press.
00200  */
00201 void button_press() {
00202     int v = res_button->get_value_int() + 1;
00203     res_button->set_value(v);
00204     printf("*** Button clicked %d times                                 \n", v);
00205 }
00206 
00207 /**
00208  * Notification callback handler
00209  * @param resource The resource that triggered the callback
00210  * @param status The delivery status of the notification
00211  */
00212 void button_callback(MbedCloudClientResource *resource, const NoticationDeliveryStatus status) {
00213     printf("*** Button notification, status %s (%d)                     \n", MbedCloudClientResource::delivery_status_to_string(status), status);
00214 }
00215 
00216 /**
00217  * Registration callback handler
00218  * @param endpoint Information about the registered endpoint such as the name (so you can find it back in portal)
00219  */
00220 void registered(const ConnectorClientEndpointInfo *endpoint) {
00221     printf("Registered to Pelion Device Management. Endpoint Name: %s\n", endpoint->internal_endpoint_name.c_str());
00222     endpointInfo = endpoint;
00223 }
00224 
00225 /**
00226  * Initialize sensors
00227  */
00228 void sensors_init() {
00229     sens_press_temp.initialize();
00230 
00231     SI7021::SI7021_status_t result = sens_hum_temp.SI7021_SoftReset();
00232     if (result == SI7021::SI7021_SUCCESS) {
00233         wait_ms(15);
00234         SI7021::SI7021_vector_data_t result_data;
00235         result = sens_hum_temp.SI7021_Conf(SI7021::SI7021_RESOLUTION_RH_11_TEMP_11,
00236                                         SI7021::SI7021_HTRE_DISABLED);
00237         result = sens_hum_temp.SI7021_GetElectronicSerialNumber(&result_data);
00238         result = sens_hum_temp.SI7021_GetFirmwareRevision(&result_data);
00239         printf("Si7021 Electronic Serial Number: %16x %16x, firmware rev %02x\n",
00240                 result_data.ElectronicSerialNumber_MSB,
00241                 result_data.ElectronicSerialNumber_LSB,
00242                 result_data.FirmwareRevision);
00243     }
00244 
00245     if (!sens_light.open()) {
00246         printf("ERROR: Failed to initialize sensor Si1133\n");
00247     }
00248 
00249     if (!sens_aqs.init()) {
00250         printf("ERROR: Failed to initialize sensor CCS811\n");
00251     } else {
00252         if (!sens_aqs.mode(AMS_CCS811::SIXTY_SECOND)) {
00253             printf("ERROR: Failed to set mode for sensor CCS811\n");
00254         }
00255 //        sens_aqs.enable_interupt(true);
00256     }
00257  
00258     if (!sens_imu.open()) {
00259         printf("ERROR: Failed to initialize sensor ICM20648\n");
00260     }
00261 }
00262 
00263 /**
00264  * Update sensors and report their values.
00265  * This function is called periodically.
00266  */
00267 void sensors_update() {
00268     SI7021::SI7021_status_t sens_hum_temp_reading, humidity_reading, temp2_reading;
00269     SI7021::SI7021_vector_data_t humidity_data, temp2_data;
00270 
00271     // BMP280 pressure and temperature (1)
00272     float pressure_value = sens_press_temp.getPressure();
00273     float temp1_value = sens_press_temp.getTemperature();
00274 
00275     // Si7021 humidity and temperature (2)
00276     sens_hum_temp_reading = sens_hum_temp.SI7021_TriggerHumidity(SI7021::SI7021_NO_HOLD_MASTER_MODE);
00277     if (sens_hum_temp_reading == SI7021::SI7021_SUCCESS) {
00278         wait_ms(30);
00279         humidity_reading = sens_hum_temp.SI7021_ReadHumidity(&humidity_data);
00280         temp2_reading = sens_hum_temp.SI7021_ReadTemperatureFromRH(&temp2_data);
00281         sens_aqs.env_data(humidity_data.RelativeHumidity, temp2_data.Temperature);
00282     }
00283 
00284     // Si1133 light and UV index
00285     float light_value, uv_index_value;
00286     bool light_reading = sens_light.get_light_and_uv(&light_value, &uv_index_value);
00287 
00288     // CCS811 air quality CO2 and TVoC
00289     sens_aqs.has_new_data();
00290     int co2_value = sens_aqs.co2_read();
00291     int tvoc_value = sens_aqs.tvoc_read();
00292 
00293     // Si7210 field and temperature (3)
00294     sens_hall.measureOnce();
00295     float field_value = (float)sens_hall.getFieldStrength() / 1000.0, temp3_value = (float)sens_hall.getTemperature() / 1000.0;
00296  
00297     // ICM20648 accelerometer and gyroscope
00298     float acc_x, acc_y, acc_z, gyr_x, gyr_y, gyr_z, temp4_value;
00299     sens_imu.get_accelerometer(&acc_x, &acc_y, &acc_z);
00300     sens_imu.get_gyroscope(&gyr_x, &gyr_y, &gyr_z);
00301     sens_imu.get_temperature(&temp4_value);
00302 
00303     printf("                                                                 \n");
00304     printf("BMP280 temp:   %8.3f C,   pressure: %8.3f [mbar]            \n", temp1_value, pressure_value);
00305     printf("Si7021 temp:   %8.3f C,   humidity: %8.3f %%                \n", temp2_data.Temperature, humidity_data.RelativeHumidity);
00306     printf("Si7210 temp:   %8.3f C,   field:    %8.3f [mT]              \n", temp3_value, field_value);
00307     printf("Si1133 light:  %8.3f lux, UV level: %8.3f                   \n", light_value, uv_index_value);
00308     printf("CCS811 CO2:    %8d ppm, VoC:      %8d ppb                   \n", co2_value, tvoc_value);
00309     printf("ICM20648 acc:  %8.3f x, %7.3f y,   %7.3f z [mg]            \n", acc_x, acc_y, acc_z);
00310     printf("ICM20648 gyro: %8.3f x, %7.3f y,   %7.3f z [mdps]          \n", gyr_x, gyr_y, gyr_z);
00311 
00312     printf("\r\033[8A");
00313 
00314     if (endpointInfo) {
00315 #ifdef SEND_ALL_SENSORS
00316         res_pressure->set_value(pressure_value);
00317         res_temperature1->set_value(temp1_value);
00318  
00319         if (humidity_reading == SI7021::SI7021_SUCCESS) {
00320             res_humidity->set_value(humidity_data.RelativeHumidity);
00321         }
00322         if (temp2_reading == SI7021::SI7021_SUCCESS) {
00323             res_temperature2->set_value(temp2_data.Temperature);
00324         }
00325 
00326         res_field->set_value(field_value);
00327         res_temperature3->set_value(temp3_value);
00328 
00329         if (light_reading) {
00330             res_light->set_value(light_value);
00331         }
00332 
00333         res_co2->set_value(co2_value);
00334         res_tvoc->set_value(tvoc_value);
00335 
00336         res_accelerometer_x->set_value(acc_x);
00337         res_accelerometer_y->set_value(acc_y);
00338         res_accelerometer_z->set_value(acc_z);
00339         res_gyroscope_x->set_value(gyr_x);
00340         res_gyroscope_y->set_value(gyr_y);
00341         res_gyroscope_z->set_value(gyr_z);
00342 #endif /* SEND_ALL_SENSORS */
00343     }
00344 }
00345 
00346 int main() {
00347     printf("\nStarting Simple Pelion Device Management Client example\n");
00348 
00349     int storage_status = fs.mount(&sd);
00350     if (storage_status != 0) {
00351         printf("Storage mounting failed.\n");
00352     }
00353     // If the User button is pressed ons start, then format storage.
00354     bool btn_pressed = (button.read() == MBED_CONF_APP_BUTTON_PRESSED_STATE);
00355     if (btn_pressed) {
00356         printf("User button is pushed on start...\n");
00357     }
00358 
00359     if (storage_status || btn_pressed) {
00360         printf("Formatting the storage...\n");
00361         int storage_status = StorageHelper::format(&fs, &sd);
00362         if (storage_status != 0) {
00363             printf("ERROR: Failed to reformat the storage (%d).\n", storage_status);
00364         }
00365     } else {
00366         printf("You can hold the user button during boot to format the storage and change the device identity.\n");
00367     }
00368 
00369     sensors_init();
00370 
00371     Nanostack::get_instance(); // ensure Nanostack is initialised
00372     ns_file_system_set_root_path("/fs/");
00373 
00374     // Connect to the internet (DHCP is expected to be on)
00375     printf("Connecting to the network using 802.15.4...\n");
00376 
00377     nsapi_error_t net_status = -1;
00378     for (int tries = 0; tries < 3; tries++) {
00379         net_status = net->connect();
00380         if (net_status == NSAPI_ERROR_OK) {
00381             break;
00382         } else {
00383             printf("Unable to connect to network. Retrying...\n");
00384         }
00385     }
00386 
00387     if (net_status != NSAPI_ERROR_OK) {
00388         printf("ERROR: Connecting to the network failed (%d)!\n", net_status);
00389         return -1;
00390     }
00391 
00392     printf("Connected to the network successfully. IP address: %s\n", net->get_ip_address());
00393 
00394     printf("Initializing Pelion Device Management Client...\n");
00395 
00396     /* Initialize Simple Pelion DM Client */
00397     SimpleMbedCloudClient client(net, &sd, &fs);
00398     int client_status = client.init();
00399     if (client_status != 0) {
00400         printf("ERROR: Pelion Client initialization failed (%d)\n", client_status);
00401         return -1;
00402     }
00403 
00404     /* Create resources */
00405     res_led = client.create_resource("3201/0/5853", "LED blinking (R:G:B:cnt)");
00406     res_led->observable(false);
00407     res_led->set_value("0:0:0:0");
00408     res_led->attach_post_callback(blink_callback);
00409     res_led->methods(M2MMethod::POST);
00410 
00411     res_button = client.create_resource("3200/0/5501", "button_count");
00412     res_button->set_value(0);
00413     res_button->methods(M2MMethod::GET);
00414     res_button->observable(true);
00415     res_button->attach_notification_callback(button_callback);
00416 
00417 #ifdef SEND_ALL_SENSORS 
00418     // Sensor BMP280
00419     res_pressure = client.create_resource("3323/0/5853", "Barometric pressure (hPa)");
00420     res_pressure->set_value(0);
00421     res_pressure->observable(true);
00422     res_pressure->methods(M2MMethod::GET);
00423  
00424     res_temperature1 = client.create_resource("3303/0/5853", "Temperature BMP280 (C)");
00425     res_temperature1->set_value(0);
00426     res_temperature1->observable(true);
00427     res_temperature1->methods(M2MMethod::GET);
00428  
00429     // Sensor Si7021
00430     res_humidity = client.create_resource("3304/0/5853", "Humidity (%)");
00431     res_humidity->set_value(0);
00432     res_humidity->observable(true);
00433     res_humidity->methods(M2MMethod::GET);
00434  
00435     res_temperature2 = client.create_resource("3303/1/5853", "Temperature Si7021 (C)");
00436     res_temperature2->set_value(0);
00437     res_temperature2->observable(true);
00438     res_temperature2->methods(M2MMethod::GET);
00439  
00440      // Sensor Si7210
00441     res_field = client.create_resource("33257/0/5853", "Magnetic Field (mT)");
00442     res_field->set_value(0);
00443     res_field->observable(true);
00444     res_field->methods(M2MMethod::GET);
00445  
00446     res_temperature3 = client.create_resource("3303/2/5853", "Temperature Si7210 (C)");
00447     res_temperature3->set_value(0);
00448     res_temperature3->observable(true);
00449     res_temperature3->methods(M2MMethod::GET);
00450 
00451     // Sensor Si1133
00452     res_light = client.create_resource("3301/0/5853", "LightIntensity (LUX)");
00453     res_light->set_value(0);
00454     res_light->observable(true);
00455     res_light->methods(M2MMethod::GET);
00456 
00457     // Sensor CCS811
00458     res_co2 = client.create_resource("33255/0/5853", "CO2 (ppm)");
00459     res_co2->set_value(0);
00460     res_co2->observable(true);
00461     res_co2->methods(M2MMethod::GET);
00462 
00463     res_tvoc = client.create_resource("33256/0/5853", "VOC (ppm)");
00464     res_tvoc->set_value(0);
00465     res_tvoc->observable(true);
00466     res_tvoc->methods(M2MMethod::GET);
00467  
00468     // Sensor ICM20648
00469     res_accelerometer_x = client.create_resource("3313/0/5702", "Accelerometer X");
00470     res_accelerometer_x->set_value(0);
00471     res_accelerometer_x->methods(M2MMethod::GET);
00472     res_accelerometer_x->observable(true);
00473  
00474     res_accelerometer_y = client.create_resource("3313/0/5703", "Accelerometer Y");
00475     res_accelerometer_y->set_value(0);
00476     res_accelerometer_y->methods(M2MMethod::GET);
00477     res_accelerometer_y->observable(true);
00478  
00479     res_accelerometer_z = client.create_resource("3313/0/5704", "Accelerometer Z");
00480     res_accelerometer_z->set_value(0);
00481     res_accelerometer_z->methods(M2MMethod::GET);
00482     res_accelerometer_z->observable(true);
00483  
00484     res_gyroscope_x = client.create_resource("3334/0/5702", "Gyroscope X");
00485     res_gyroscope_x->set_value(0);
00486     res_gyroscope_x->methods(M2MMethod::GET);
00487     res_gyroscope_x->observable(true);
00488  
00489     res_gyroscope_y = client.create_resource("3334/0/5703", "Gyroscope Y");
00490     res_gyroscope_y->set_value(0);
00491     res_gyroscope_y->methods(M2MMethod::GET);
00492     res_gyroscope_y->observable(true);
00493  
00494     res_gyroscope_z = client.create_resource("3334/0/5704", "Gyroscope Z");
00495     res_gyroscope_z->set_value(0);
00496     res_gyroscope_z->methods(M2MMethod::GET);
00497     res_gyroscope_z->observable(true);
00498  
00499     res_temperature4 = client.create_resource("3303/3/5853", "Temperature ICM20648 (C)");
00500     res_temperature4->set_value(0);
00501     res_temperature4->observable(true);
00502     res_temperature4->methods(M2MMethod::GET);
00503 #endif /* SEND_ALL_SENSORS */
00504 
00505     // Callback that fires when registering is complete
00506     client.on_registered(&registered);
00507 
00508     /* Register the device */
00509     client.register_and_connect();
00510 
00511     button.fall(eventQueue.event(&button_press));
00512 
00513     // The timer fires on an interrupt context, but debounces it to the eventqueue, so it's safe to do network operations
00514     Ticker timer;
00515     timer.attach(eventQueue.event(&sensors_update), SENSORS_POLL_INTERVAL);
00516 
00517     eventQueue.dispatch_forever();
00518 }