Self Provisioning Device, API method

Hi,

I want to share with the community a way to make a self provisioning device, so you don’t need to create at the cloud to get connected, just add all the fields needed in the code and it will check if there is a device and will try to create if it doesn’t exists, note that if the device exists with different credentials, will check that the device exists but never will connected.

Besides thinger library, need aditionally the HTTPClient library.
It was done by:
Arduino IDE 1.8.12
Thinger.io 2.15.0
ESP32 1.0.4

#include <HTTPClient.h>

#define _DEBUG_
#define THINGER_SERVER "yourServerAddress"
#include <ThingerESP32.h>
String USERNAME  = "yourUsername";
String DEVICE_ID = "SelfProvisioningDevice";
String DEVICE_CREDENTIAL = "deviceCredentials";


String assetGroup = "TestAssetGroup";
String assetType = "TestAssetGroup";
String description = "TestDescription";
String name = "TestName";
String type = "Generic";
String token = "yourGeneratedToken";


#define SSID "TEST"
#define SSID_PASSWORD "11111111" //if you are in my neighborhood and get my wifi from this forum, be my guest

#define LED_BUILTIN 3 //just to avoid the error message on esp32

bool device = 0; //variable to check the device, after is verified will be 1

ThingerESP32 thing(USERNAME.c_str(), DEVICE_ID.c_str(), DEVICE_CREDENTIAL.c_str());

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  thing.add_wifi(SSID, SSID_PASSWORD);
  thing["led"] << digitalPin(LED_BUILTIN);
  thing["millis"] >> outputValue(millis());
}

void loop() {
  thing.handle();
  if (WiFi.status() == WL_CONNECTED && !device) {
    if (checkDevice(THINGER_SERVER, USERNAME, token, DEVICE_ID)) {
      Serial.println("Device already exists");
      device = 1;
    }
    else {
      Serial.println("Device doesn't exists, will try to create it");
      int response = createDevice(THINGER_SERVER, USERNAME, token, assetGroup, assetType, DEVICE_CREDENTIAL, description, DEVICE_ID, name, type);
      if (response == 200) {
        Serial.println("Device created successfully");
        device = 1;
      }
      else {
        Serial.print("Device not created, error code : ");
        Serial.println(response);
      }
    }
  }
}

int createDevice(String serverName, String user, String token, String assetGroup, String assetType, String credentials, String description, String device, String name, String type) {
  int httpCode;
  if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status
    HTTPClient http;
    String aux = "http://" + serverName + "/v1/users/" + user + "/devices";
    http.begin(aux);
    http.addHeader("Content-Type", "application/json");
    http.addHeader("Authorization", "Bearer " + token);
    aux = "{\"asset_group\": \"" + assetGroup + "\", \"asset_type\": \"" + assetType + "\",\"credentials\": \"" + credentials + "\",  \"description\": \"" + description + "\",  \"device\": \"" + device + "\",  \"name\": \"" + name + "\",  \"type\": \"" + type + "\"}";
    httpCode = http.POST(aux);
    http.end();
  }
  return httpCode;
}


bool checkDevice(String serverName, String user, token, String device) {
  String payload;
  String aux;
  if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status

    HTTPClient http;  //Declare an object of class HTTPClient
    aux = "http://" + serverName + "/v1/users/" + user + "/devices?name=" + device + "&authorization=" + token;
    http.begin(aux);  //Specify request destination
    int httpCode = http.GET();                                                                  //Send the request

    if (httpCode > 0) { //Check the returning code

      payload = http.getString();   //Get the request response payload
      //    Serial.println(payload);                     //Print the response payload
    }
    http.end();   //Close connection
  }
  aux = "\"" + device + "\"";
  if (payload.indexOf(aux) > 0) return true;
  else return false;
}

To check if the device exists, it make the request with the device name, and it gets all the devices that has that string into the “device” and “name” fields, and will compare the DEVICE_ID against those fields.

It can be optimized to work with thinger library and get the events, but it is a pretty basic example, fell free to adapt to your needs and share your achievements.

I guess something similar could be done with the buckets and dashboards.

Hope this helps.

3 Likes

Excellent idea!
:heart_eyes:
Surely a huge help for those needing hundreds of devices. Matching them manually is a pain.
It would probably be easier to check and auto-create the device at the server side from library calls over TCP/UDP instead of using all the overhead of HTTP, wouldn’t it?

Or even do it the other way around: have a pool of devices ready for use at the server side and the device
“subscribes” for one of them if uninitialized. That permits to use a standard device firmware code.

Creating buckets as well.
Just for dashboards… Hmm, are devices aware of dashboards?

The HTTP methods are used just once for every device’s boot, after that are not called at all, so I do not see as a big achievement to avoid using them, what you are proposing can be done maybe using NodeRED, but that’s another method.

This could be a part of a standard firmware, you just need to adapt and get the neccesary fields ie. from the wifimanager, and fill them for the first time when the device is installed.

For buckets and dashboards can be applied something pretty similar, need to modify according the need, just to check the api’s documentation and adapt the functions according the necessary fields.

Hope this helps.

“The HTTP methods are used just once…”
The problem is that you need to load the HTTP library, and that takes not less of the program space.

Yes of course it occupies memory, but it is totally neccesary for connect to server’s API.