M5Stack Timer Camera F is a fisheye camera module based on ESP32-D0WDQ6-V3 with 8M PSRAM and 4M Flash on board.
3.0 megapixel camera (OV3660) with DFOV 120° and a maximum resolution of 2048x1536 photos can be captured.
The camera features an ultra-low-power design, and the internal integrated RTC (BM8563) draws out the IRQ signal, which can be used for sleep and timer wake-up (sleep current down to 2μA).
The built-in 270mAh battery provides more than one month of battery life with timed pictures (one per hour) enabled.
The module supports Wi-Fi image transfer and USB port debugging, and the HY2.0-4P output on the bottom can be used to expand other peripherals.
The on-board LED status indicator and reset button facilitate program development and debugging.
Next, go to Tools > Board > Board Manager and install the M5Stack by M5Stack Official board.
After the installation is complete, select the board by Board menu: Tools > Board > M5Stack > M5TimerCAM (Or M5Stack-Timer-CAM in older ESP-IDF versions).
Connect the device to computer using USB cable and select the port for the device: Tools > Port > /dev/ttyUSB0.
Port depends on operation system and may be different:
for Linux it will be /dev/ttyUSBX
for MacOS it will be usb.serialX.. or usb.modemX..
for Windows - COMX.
To install ThingsBoard Arduino SDK - we will need to do the following steps:
Go to “Tools” tab and click on “Manage libraries”.
Put “ThingsBoard” into the search box and press “INSTALL” button for the found library.
All provided code examples require ThingsBoard Library version 0.13.0 or above.
At this point, we have installed all required libraries and tools.
Connect device to ThingsBoard
To connect your device, you’ll first need to get its credentials. While ThingsBoard supports a variety of device credentials, for this guide, we will use the default auto-generated credentials, which is an access token.
Click on the device row in the table to open device details.
Click “Copy access token”. The token will be copied to your clipboard. Please save it in a safe place.
Now it’s time to program the board to connect to ThingsBoard.
To do this, you can use the code below. It contains all required functionality for this guide.
If you want to use demo.thingsboard.io, please notice that it has a size limit for the MQTT messages - 1024 bytes per message.
In this case you can reduce the resolution, quality and cut the encoded photo. To do this you can change the value of variable DEMO_MODE to 1:
#define DEMO_MODE 1
#include<Arduino_MQTT_Client.h>
#include"esp_camera.h"
#include<WiFi.h>
#include"soc/soc.h"
#include"soc/rtc_cntl_reg.h"#define DEMO_MODE 1
#include<ThingsBoard.h>
#include<esp_heap_caps.h>extern"C"{#include"libb64/cencode.h"}constexprcharWIFI_SSID[]="YOUR_WIFI_SSID";constexprcharWIFI_PASSWORD[]="YOUR_WIFI_PASSWORD";// See https://thingsboard.io/docs/getting-started-guides/helloworld/// to understand how to obtain an access tokenconstexprcharTOKEN[]="YOUR_ACCESS_TOKEN";// Thingsboard we want to establish a connection tooconstexprcharTHINGSBOARD_SERVER[]="demo.thingsboard.io";// MQTT port used to communicate with the server, 1883 is the default unencrypted MQTT port.constexpruint16_tTHINGSBOARD_PORT=1883U;// Maximum size packets will ever be sent or received by the underlying MQTT client,// if the size is to small messages might not be sent or received messages will be discardedconstexprsize_tMAX_MESSAGE_SIZE=100U*1024;// Baud rate for the debugging serial connection.// If the Serial output is mangled, ensure to change the monitor speed accordingly to this variableconstexpruint32_tSERIAL_DEBUG_BAUD=115200U;// Maximum amount of attributs we can request or subscribe, has to be set both in the ThingsBoard template list and Attribute_Request_Callback template list// and should be the same as the amount of variables in the passed array. If it is less not all variables will be requested or subscribedconstexprsize_tMAX_ATTRIBUTES=2U;// Definitions for camera pins#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 25
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 32
#define VSYNC_GPIO_NUM 22
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
// Initialize underlying client, used to establish a connectionWiFiClientwifiClient;// Initalize the Mqtt client instanceArduino_MQTT_ClientmqttClient(wifiClient);// Initialize ThingsBoard instance with the maximum needed buffer sizeThingsBoardSized<Default_Fields_Amount,Default_Subscriptions_Amount,MAX_ATTRIBUTES>tb(mqttClient,MAX_MESSAGE_SIZE);// Attribute names for attribute request and attribute updates functionalityconstexprcharBLINKING_INTERVAL_ATTR[]="blinkingInterval";constexprcharLED_MODE_ATTR[]="ledMode";constexprcharLED_STATE_ATTR[]="ledState";constexprcharPICTURE_ATTR[]="photo";// handle led state and mode changesvolatileboolattributesChanged=false;// LED modes: 0 - continious state, 1 - blinkingvolatileintledMode=0;// Current led statevolatileboolledState=false;// Settings for interval in blinking modeconstexpruint16_tBLINKING_INTERVAL_MS_MIN=10U;constexpruint16_tBLINKING_INTERVAL_MS_MAX=60000U;volatileuint16_tblinkingInterval=1000U;uint32_tpreviousStateChange;// For telemetryconstexprint16_ttelemetrySendInterval=2000U;uint32_tpreviousDataSend;// Picture bufferchar*imageBuffer;// Flag to send a picturevolatileboolsendPicture=false;// List of shared attributes for subscribing to their updatesconstexprstd::array<constchar*,2U>SHARED_ATTRIBUTES_LIST={LED_STATE_ATTR,BLINKING_INTERVAL_ATTR};// List of client attributes for requesting them (Using to initialize device states)constexprstd::array<constchar*,1U>CLIENT_ATTRIBUTES_LIST={LED_MODE_ATTR};/// @brief Initalizes WiFi connection,// will endlessly delay until a connection has been successfully establishedvoidInitWiFi(){Serial.println("Connecting to AP ...");// Attempting to establish a connection to the given WiFi networkWiFi.begin(WIFI_SSID,WIFI_PASSWORD);while(WiFi.status()!=WL_CONNECTED){// Delay 500ms until a connection has been succesfully establisheddelay(500);Serial.println(WiFi.status());Serial.println(WL_CONNECTED);Serial.println(".");}Serial.println("Connected to AP");}/// @brief Reconnects the WiFi uses InitWiFi if the connection has been removed/// @return Returns true as soon as a connection has been established againconstboolreconnect(){if(WiFi.status()==WL_CONNECTED){returntrue;}// If we aren't establish a new connection to the given WiFi networkInitWiFi();returntrue;}boolinitCamera(){camera_config_tconfig;config.ledc_channel=LEDC_CHANNEL_0;config.ledc_timer=LEDC_TIMER_0;config.pin_d0=Y2_GPIO_NUM;config.pin_d1=Y3_GPIO_NUM;config.pin_d2=Y4_GPIO_NUM;config.pin_d3=Y5_GPIO_NUM;config.pin_d4=Y6_GPIO_NUM;config.pin_d5=Y7_GPIO_NUM;config.pin_d6=Y8_GPIO_NUM;config.pin_d7=Y9_GPIO_NUM;config.pin_xclk=XCLK_GPIO_NUM;config.pin_pclk=PCLK_GPIO_NUM;config.pin_vsync=VSYNC_GPIO_NUM;config.pin_href=HREF_GPIO_NUM;config.pin_sscb_sda=SIOD_GPIO_NUM;config.pin_sscb_scl=SIOC_GPIO_NUM;config.pin_pwdn=PWDN_GPIO_NUM;config.pin_reset=RESET_GPIO_NUM;config.xclk_freq_hz=20000000;config.pixel_format=PIXFORMAT_JPEG;config.fb_count=1;if(DEMO_MODE==1){config.frame_size=FRAMESIZE_96X96;config.jpeg_quality=63;}else{config.frame_size=FRAMESIZE_240X240;config.jpeg_quality=10;}esp_err_terr=esp_camera_init(&config);if(err!=ESP_OK){Serial.printf("Camera init failed with error 0x%x",err);returnfalse;}sensor_t*s=esp_camera_sensor_get();// initial sensors are flipped vertically and colors are a bit saturateds->set_vflip(s,1);// flip it backs->set_brightness(s,1);// up the brightness just a bits->set_saturation(s,-2);// lower the saturationreturntrue;}boolcaptureImage(){camera_fb_t*fb=NULL;fb=esp_camera_fb_get();if(!fb){returnfalse;}encode((uint8_t*)fb->buf,fb->len);esp_camera_fb_return(fb);returntrue;}voidencode(constuint8_t*data,size_tlength){if(DEMO_MODE==1){length=756;}size_tsize=base64_encode_expected_len(length)+1;base64_encodestate_state;base64_init_encodestate(&_state);intlen=base64_encode_block((char*)&data[0],length,&imageBuffer[0],&_state);len=base64_encode_blockend((imageBuffer+len),&_state);}/// @brief Processes function for RPC call "setLedMode"/// RPC_Data is a JSON variant, that can be queried using operator[]/// See https://arduinojson.org/v5/api/jsonvariant/subscript/ for more details/// @param data Data containing the rpc data that was called and its current valuevoidprocessSetLedMode(constJsonVariantConst&data,JsonDocument&response){Serial.println("Received the set led state RPC method");// Process dataintnew_mode=data;Serial.print("Mode to change: ");Serial.println(new_mode);StaticJsonDocument<1>response_doc;if(new_mode!=0&&new_mode!=1){response_doc["error"]="Unknown mode!";response.set(response_doc);return;}ledMode=new_mode;attributesChanged=true;// Returning current moderesponse_doc["newMode"]=(int)ledMode;response.set(response_doc);}/// @brief Processes function for RPC call "setLedMode"/// RPC_Data is a JSON variant, that can be queried using operator[]/// See https://arduinojson.org/v5/api/jsonvariant/subscript/ for more details/// @param data Data containing the rpc data that was called and its current valuevoidprocessTakePicture(constJsonVariantConst&data,JsonDocument&response){Serial.println("Received the take picture RPC method");StaticJsonDocument<1>response_doc;if(!captureImage()){response_doc["error"]="Cannot take a picture!";response.set(response_doc);return;}sendPicture=true;// Returning picture sizeresponse_doc["size"]=strlen(imageBuffer);response.set(response_doc);}// Optional, keep subscribed shared attributes empty instead,// and the callback will be called for every shared attribute changed on the device,// instead of only the one that were entered insteadconststd::array<RPC_Callback,2U>callbacks={RPC_Callback{"setLedMode",processSetLedMode},RPC_Callback{"takePicture",processTakePicture}};/// @brief Update callback that will be called as soon as one of the provided shared attributes changes value,/// if none are provided we subscribe to any shared attribute change instead/// @param data Data containing the shared attributes that were changed and their current valuevoidprocessSharedAttributes(constJsonObjectConst&data){for(autoit=data.begin();it!=data.end();++it){if(strcmp(it->key().c_str(),BLINKING_INTERVAL_ATTR)==0){constuint16_tnew_interval=it->value().as<uint16_t>();if(new_interval>=BLINKING_INTERVAL_MS_MIN&&new_interval<=BLINKING_INTERVAL_MS_MAX){blinkingInterval=new_interval;Serial.print("Updated blinking interval to: ");Serial.println(new_interval);}}elseif(strcmp(it->key().c_str(),LED_STATE_ATTR)==0){ledState=it->value().as<bool>();digitalWrite(LED_BUILTIN,ledState?HIGH:LOW);Serial.print("Updated state to: ");Serial.println(ledState);}}attributesChanged=true;}voidprocessClientAttributes(constJsonObjectConst&data){for(autoit=data.begin();it!=data.end();++it){if(strcmp(it->key().c_str(),LED_MODE_ATTR)==0){constuint16_tnew_mode=it->value().as<uint16_t>();ledMode=new_mode;}}}constShared_Attribute_Callback<MAX_ATTRIBUTES>attributes_callback(&processSharedAttributes,SHARED_ATTRIBUTES_LIST.cbegin(),SHARED_ATTRIBUTES_LIST.cend());constAttribute_Request_Callback<MAX_ATTRIBUTES>attribute_shared_request_callback(&processSharedAttributes,SHARED_ATTRIBUTES_LIST.cbegin(),SHARED_ATTRIBUTES_LIST.cend());constAttribute_Request_Callback<MAX_ATTRIBUTES>attribute_client_request_callback(&processClientAttributes,CLIENT_ATTRIBUTES_LIST.cbegin(),CLIENT_ATTRIBUTES_LIST.cend());voidsetup(){WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG,0);ledcAttachPin(4,4);ledcSetup(4,5000,8);imageBuffer=(char*)ps_malloc(50U*1024);Serial.begin(SERIAL_DEBUG_BAUD);Serial.println("Camera initialization...");if(!initCamera()){Serial.println("Camera initialization failed!");ESP.restart();}pinMode(LED_BUILTIN,OUTPUT);delay(1000);InitWiFi();tb.connect(THINGSBOARD_SERVER,TOKEN,THINGSBOARD_PORT);tb.RPC_Subscribe(callbacks.cbegin(),callbacks.cend());tb.Shared_Attributes_Subscribe(attributes_callback);tb.Shared_Attributes_Request(attribute_shared_request_callback);tb.Client_Attributes_Request(attribute_client_request_callback);}voidloop(){delay(10);if(!reconnect()){return;}if(!tb.connected()){// Connect to the ThingsBoardSerial.print("Connecting to: ");Serial.print(THINGSBOARD_SERVER);Serial.print(" with token ");Serial.println(TOKEN);if(!tb.connect(THINGSBOARD_SERVER,TOKEN,THINGSBOARD_PORT)){Serial.println("Failed to connect");return;}Serial.println("Connection to server successful");// Sending a MAC address as an attributetb.sendAttributeData("macAddress",WiFi.macAddress().c_str());Serial.println("Subscribing for RPC...");// Perform a subscription. All consequent data processing will happen in// processSetLedState() and processSetLedMode() functions,// as denoted by callbacks array.if(!tb.RPC_Subscribe(callbacks.cbegin(),callbacks.cend())){Serial.println("Failed to subscribe for RPC");return;}if(!tb.Shared_Attributes_Subscribe(attributes_callback)){Serial.println("Failed to subscribe for shared attribute updates");return;}Serial.println("Subscribe done");// Request current states of shared attributesif(!tb.Shared_Attributes_Request(attribute_shared_request_callback)){Serial.println("Failed to request for shared attributes");return;}// Request current states of client attributesif(!tb.Client_Attributes_Request(attribute_client_request_callback)){Serial.println("Failed to request for client attributes");return;}}if(sendPicture){tb.sendTelemetryData(PICTURE_ATTR,imageBuffer);sendPicture=false;}if(attributesChanged){attributesChanged=false;if(ledMode==0){previousStateChange=millis();}tb.sendTelemetryData(LED_MODE_ATTR,ledMode);tb.sendTelemetryData(LED_STATE_ATTR,ledState);tb.sendAttributeData(LED_MODE_ATTR,ledMode);tb.sendAttributeData(LED_STATE_ATTR,ledState);}if(ledMode==1&&millis()-previousStateChange>blinkingInterval){previousStateChange=millis();ledState=!ledState;digitalWrite(LED_BUILTIN,ledState);tb.sendTelemetryData(LED_STATE_ATTR,ledState);tb.sendAttributeData(LED_STATE_ATTR,ledState);if(LED_BUILTIN==99){Serial.print("LED state changed to: ");Serial.println(ledState);}}// Sending telemetry every telemetrySendInterval timeif(millis()-previousDataSend>telemetrySendInterval){previousDataSend=millis();tb.sendTelemetryData("temperature",random(10,20));tb.sendAttributeData("rssi",WiFi.RSSI());tb.sendAttributeData("channel",WiFi.channel());tb.sendAttributeData("ssid",WIFI_SSID);tb.sendAttributeData("localIp",WiFi.localIP().toString().c_str());}tb.loop();}
Data, send by this device may require increasing of the allowed message size for MQTT on your ThingsBoard instance. To do this you can modify parameter NETTY_MAX_PAYLOAD_SIZE in thingsboard.yml file, default value on regular setup is 65535 bytes. Required size depends on chosen resolution and quality.
Click to see dependency between resolution and approximate message size
Framesize parameter value
Photo resolution
Approximate message size (in bytes)
FRAMESIZE_96X96
96x96
4608
FRAMESIZE_QQVGA
160x120
7200
FRAMESIZE_QCIF
176x144
9792
FRAMESIZE_HQVGA
240x176
12288
FRAMESIZE_240X240
240x240
14400
FRAMESIZE_QVGA
320x240
19200
FRAMESIZE_CIF
400x296
29760
FRAMESIZE_HVGA
480x320
34560
FRAMESIZE_VGA
640x480
76800
FRAMESIZE_SVGA
800x600
144000
FRAMESIZE_XGA
1024x768
294912
FRAMESIZE_HD
1280x720
345600
FRAMESIZE_SXGA
1280x1024
491520
FRAMESIZE_UXGA
1600x1200
921600
FRAMESIZE_FHD
1920x1080
933120
FRAMESIZE_P_HD
720x1280
1036800
FRAMESIZE_P_3MP
864x1536
1776384
FRAMESIZE_QXGA
2048x1536
4718592
FRAMESIZE_QHD
2560x1440
5529600
FRAMESIZE_WQXGA
2560x1600
6144000
FRAMESIZE_P_FHD
1080x1920
6220800
FRAMESIZE_QSXGA
2560x1920
7864320
Don’t forget to replace placeholders with your real WiFi network SSID, password, ThingsBoard device access token.
Necessary variables for connection:
Variable name
Default value
Description
WIFI_SSID
YOUR_WIFI_SSID
Your WiFi network name.
WIFI_PASSWORD
YOUR_WIFI_PASSWORD
Your WiFi network password.
TOKEN
YOUR_DEVICE_ACCESS_TOKEN
Access token from device. Obtaining process described in #connect-device-to-thingsboard
THINGSBOARD_SERVER
demo.thingsboard.io
Your ThingsBoard host or ip address.
THINGSBOARD_PORT
1883U
ThingsBoard server MQTT port. Can be default for this guide.
MAX_MESSAGE_SIZE
100U*1024
Maximal size of MQTT messages. Should be more than picture size + ~1024 or more.
SERIAL_DEBUG_BAUD
1883U
Baud rate for serial port. Can be default for this guide.
DEMO_MODE
1
If you want to use demo.thingsboard.io set this value to 1. This reduces the resolution, quality and cut the encoded photo to avoid size limit.
Then upload the code to the device by pressing Upload button or keyboard combination Ctrl+U.
Check data on ThingsBoard
ThingsBoard provides the ability to create and customize interactive visualizations (dashboards) for monitoring and managing data and devices.
Through ThingsBoard dashboards, you can efficiently manage and monitor your IoT devices and data. So, we will create the dashboard, for our device.
To add the dashboard to ThingsBoard, we need to import it. To import a dashboard, follow these steps:
...voidprocessSharedAttributes(constJsonObjectConst&data){for(autoit=data.begin();it!=data.end();++it){if(strcmp(it->key().c_str(),BLINKING_INTERVAL_ATTR)==0){constuint16_tnew_interval=it->value().as<uint16_t>();if(new_interval>=BLINKING_INTERVAL_MS_MIN&&new_interval<=BLINKING_INTERVAL_MS_MAX){blinkingInterval=new_interval;Serial.print("Updated blinking interval to: ");Serial.println(new_interval);}}elseif(strcmp(it->key().c_str(),LED_STATE_ATTR)==0){ledState=it->value().as<bool>();digitalWrite(LED_BUILTIN,ledState?HIGH:LOW);Serial.print("Updated state to: ");Serial.println(ledState);}}attributesChanged=true;}voidprocessClientAttributes(constJsonObjectConst&data){for(autoit=data.begin();it!=data.end();++it){if(strcmp(it->key().c_str(),LED_MODE_ATTR)==0){constuint16_tnew_mode=it->value().as<uint16_t>();ledMode=new_mode;}}}...constAttribute_Request_Callback<MAX_ATTRIBUTES>attribute_shared_request_callback(SHARED_ATTRIBUTES_LIST.cbegin(),SHARED_ATTRIBUTES_LIST.cend(),&processSharedAttributes);constAttribute_Request_Callback<MAX_ATTRIBUTES>attribute_client_request_callback(CLIENT_ATTRIBUTES_LIST.cbegin(),CLIENT_ATTRIBUTES_LIST.cend(),&processClientAttributes);...
We have two callbacks:
Shared Attributes Callback:
This callback is specific to shared attributes. Its primary function is to receive a response containing the blinking interval, which determines the appropriate blinking period.;
Client Attributes Callback:
This callback is specific to client attributes. It receives information regarding the mode and state of the LED. Once this data is received, the system saves and sets these parameters.
This functionality allows us to keep the actual state after rebooting.
Attribute requests:
1
2
3
4
5
6
7
8
9
10
11
12
13
...// Request current states of shared attributesif(!tb.Shared_Attributes_Request(attribute_shared_request_callback)){Serial.println("Failed to request for shared attributes");return;}// Request current states of client attributesif(!tb.Client_Attributes_Request(attribute_client_request_callback)){Serial.println("Failed to request for client attributes");return;}...
In order for our callbacks to receive the data, we have to send a request to ThingsBoard.
Control device using shared attributes
Also we can change the period of the blinking using shared attribute update functionality.
To change period of the blinking we just need to change the value on our dashboard.
After applying by pressing check mark you will see a confirmation message.
In order to change state when blinking is disabled - we can use the switch in the same widget:
It can be done only when the blinking mode is disabled.
To reach this, we have a variable “blinkingInterval” used in the following parts of the code:
...voidprocessSharedAttributes(constJsonObjectConst&data){for(autoit=data.begin();it!=data.end();++it){if(strcmp(it->key().c_str(),BLINKING_INTERVAL_ATTR)==0){constuint16_tnew_interval=it->value().as<uint16_t>();if(new_interval>=BLINKING_INTERVAL_MS_MIN&&new_interval<=BLINKING_INTERVAL_MS_MAX){blinkingInterval=new_interval;Serial.print("Updated blinking interval to: ");Serial.println(new_interval);}}elseif(strcmp(it->key().c_str(),LED_STATE_ATTR)==0){ledState=it->value().as<bool>();digitalWrite(LED_BUILTIN,ledState?HIGH:LOW);Serial.print("Updated state to: ");Serial.println(ledState);}}attributesChanged=true;}...constShared_Attribute_Callback<MAX_ATTRIBUTES>attributes_callback(SHARED_ATTRIBUTES_LIST.cbegin(),SHARED_ATTRIBUTES_LIST.cend(),&processSharedAttributes);...
Subscribing for shared attributes update:
1
2
3
4
5
6
...if(!tb.Shared_Attributes_Request(attribute_shared_request_callback)){Serial.println("Failed to request for shared attributes");return;}...
Part of code to blink:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...if(ledMode==1&&millis()-previousStateChange>blinkingInterval){previousStateChange=millis();ledState=!ledState;digitalWrite(LED_BUILTIN,ledState);tb.sendTelemetryData(LED_STATE_ATTR,ledState);tb.sendAttributeData(LED_STATE_ATTR,ledState);if(LED_BUILTIN==99){Serial.print("LED state changed to: ");Serial.println(ledState);}}...
You can change the logic to reach your goals and add processing for your attributes.
Control device using RPC
You can manually change state of the LED and change mode between continuous lightning and blinking.
To do this, you can use the following parts of our dashboard:
Change LED state using switch widget to continuous lightning.
Change LED state using round switch widget to blinking mode.
Please note that you can change the LED state only if blinking mode is disabled.
In the code example we have functionality to handle RPC commands.
To get ability to control the device we have used the following parts of the code:
...voidprocessSetLedMode(constJsonVariantConst&data,JsonDocument&response){Serial.println("Received the set led state RPC method");// Process dataintnew_mode=data;Serial.print("Mode to change: ");Serial.println(new_mode);StaticJsonDocument<1>response_doc;if(new_mode!=0&&new_mode!=1){response_doc["error"]="Unknown mode!";response.set(response_doc);return;}ledMode=new_mode;attributesChanged=true;response_doc["newMode"]=(int)ledMode;// Returning current moderesponse.set(response_doc);}...conststd::array<RPC_Callback,2U>callbacks={RPC_Callback{"setLedMode",processSetLedMode},RPC_Callback{"takePicture",processTakePicture}};...
Subscribing for RPC requests:
1
2
3
4
5
6
...if(!tb.RPC_Subscribe(callbacks.cbegin(),callbacks.cend())){Serial.println("Failed to subscribe for RPC");return;}...
Such as the board has included camera we can take a picture and see it on the dashboard.
You can take a picture from camera module, by pressing the button on ThingsBoard dashboard.
To take a picture we send “takePicture” RPC to the device.