Pelion Device Management example over 15.4 Thread for Thunderboard Sense 2 board
Dependencies: ICM20648 BMP280 Si1133 Si7210 AMS_CCS811_gas_sensor SI7021
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(®istered); 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 }
Generated on Sat Jul 16 2022 14:14:54 by 1.7.2