0

I am trying to perform OTA update using ESP32 and @thingsboard community version. I am using mqtt in esp32 to connect with Thingsboard. What I have achieved is: When I am updating firmware in Thingsboard DEVICE PROFILE which is attached with my device and device to dashboard, I am getting notification that payload load arrived from subscribed topic and showing payload as well as it is showing that OTA Initialized. But actual update is not happening because in no way esp32 getting the .bin file from Thingsboard. How can I achieve actual firmware update?

In thingsboard: I have added edge rule chain and attached that with root rule chain. Setup of device profile, device and dashboard is also done.

ESP32 script: (I know I have not included update.h and other dependencies because .bin not coming up.)

OUTPUT:

Payload: {"fw_title":"OTA","fw_version":"1.4","fw_tag":"OTA 1.4","fw_size":823792,"fw_checksum_algorithm":"SHA256","fw_checksum":"a5a0c30171ae48e611efd254cd164f94427d128f57a0114d6e1eac7ba89bd7ec"}
// printing the received pyload
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoOTA.h>

// WiFi credentials
const char* ssid = "CURRENT";
const char* password = "11111111";

// ThingsBoard MQTT broker configuration
const char* mqttServer = "111.93.67.82";
const int mqttPort = 7045;
const char* mqttAccessToken = "gGnF3jGV0pGp7uvuFlHz"; // Replace with your device access token
const char* mqttTopic = "v1/devices/me/attributes";
const char* mqttPassword = ""; // Leave empty if no password is required

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

bool otaUpdateRequested = false;

// OTA update started event handler
void handleOTAStart() {
  Serial.println("OTA update started");
}

// OTA update in progress event handler
void handleOTAProgress(unsigned int progress, unsigned int total) {
  Serial.printf("OTA update progress: %u%%\n", (progress / (total / 100)));
}

// OTA update completed event handler
void handleOTAEnd() {
  Serial.println("OTA update completed");
}

// OTA update error event handler
void handleOTAError(ota_error_t error) {
  Serial.printf("OTA update error[%u]: ", error);
  if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
  else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
  else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
  else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
  else if (error == OTA_END_ERROR) Serial.println("End Failed");
}

void setupOTA() {
  // Set the port for OTA updates
  ArduinoOTA.setPort(3232);

  // Set the hostname for the ESP32 device
  ArduinoOTA.setHostname("ESP32_OTA-DEVICE");

  // Set OTA event handlers
  ArduinoOTA.onStart(handleOTAStart);
  ArduinoOTA.onProgress(handleOTAProgress);
  ArduinoOTA.onEnd(handleOTAEnd);
  ArduinoOTA.onError(handleOTAError);

  // Uncomment and set a password if needed
  // ArduinoOTA.setPassword("YOUR_OTA_PASSWORD");

  // Start OTA server
  ArduinoOTA.begin();
}

void setupMQTT() {
  mqttClient.setServer(mqttServer, mqttPort);
  mqttClient.setCallback(mqttCallback);
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] Payload: ");
  // Print the payload
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  
  // Check if the topic is for OTA update
  if (strcmp(topic, "v1/devices/me/attributes") == 0) {
    if (length > 0) {
      // Process the received binary data here
      // For simplicity, we'll just set a flag to indicate OTA update
      Serial.println("OTA update requested");
      otaUpdateRequested = true;
    } else {
      Serial.println("Empty payload received");
    }
  } else {
    // Handle other topics if necessary
  }
}

void reconnectMQTT() {
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (mqttClient.connect(mqttAccessToken, mqttAccessToken, mqttPassword)) {
      Serial.println("connected");
      mqttClient.subscribe(mqttTopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

// setup() and loop() functions...
void setup() {
  Serial.begin(115200);
  Serial.println("Booting");

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  setupOTA();
  setupMQTT();
}

void loop() {
  if (!mqttClient.connected()) {
    reconnectMQTT();
  }
  mqttClient.loop();
  
  if (otaUpdateRequested) {
    Serial.println("Initiating OTA update...");
    otaUpdateRequested = false; // Reset the flag
    ArduinoOTA.begin(); // Start OTA update
  }
  ArduinoOTA.handle(); // Handle OTA update process
  // Your main application code goes here
}

2
  • 1
    There is nothing in your code that would deal with getting an update from ThingsBoard. You start ArduinoOTA when your code receives an update notification. ArduinoOTA expects to have an update pushed to it from a computer on the same network as the device. It doesn't know anything about ThingsBoard and you never provide any OTA code with a source for the update. Why do you think that this would do anything? Commented Mar 6, 2024 at 17:20
  • Thankyou so much @romkey for your comment. I have already mentioned that I know the fact that it is not communicating with thingsboard and in no way it is receiving .bin/source file from thingsboard. What my task is when I update the firmware in thingsboard device profile. ESP32 should automatically update it's firmware according to that. Could you please help. Commented Mar 7, 2024 at 9:11

2 Answers 2

0

Try the logic from this code. Maybe it would be of some use to you.

`

#include <Arduino.h>
#include <ArduinoJson.h>
#include <WiFi.h>

#include <HTTPClient.h>
#include <ESP32httpUpdate.h>

#define USE_SERIAL Serial

TaskHandle_t Task1;

int updateRetryCount = 3;

String gsCurr_Title = "<title>";
String gsCurr_Version = "<version>";
String deviceCred= "<Access Token>";

String giServerName = "<serverURL>";
void setup() {

  USE_SERIAL.begin(115200);
  pinMode(2, OUTPUT);
  // USE_SERIAL.setDebugOutput(true);
  USE_SERIAL.println();
  USE_SERIAL.println();
  USE_SERIAL.println();

  gvconnectwifi();
  OTA_Update();
}

void gvconnectwifi() {
  WiFi.begin("<SSID>", "<Password>");
  Serial.println("Connected to wifi:");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }

  Serial.println("Connected to the WiFi network");
}

void OTA_Update() {

  String tsPayload;
  if ((WiFi.status() == WL_CONNECTED)) {

    HTTPClient http;
    String tsServerPath_getinfo = giServerName + "/api/v1/"+deviceCred+"/attributes?sharedKeys=fw_title,fw_version,fw_location";
    http.begin(tsServerPath_getinfo.c_str());

    int tiHttpResponseCode = http.GET();

    if (tiHttpResponseCode > 0) {
      Serial.print("HTTP Response code: ");
      Serial.println(tiHttpResponseCode);
      tsPayload = http.getString();
    }
    else {
      Serial.print("Error code: ");
      Serial.println(tiHttpResponseCode);
    }
    http.end();

    StaticJsonDocument<2048> doc;
    deserializeJson(doc, tsPayload);

    String tsTitle = doc["shared"]["fw_title"];
    String tsVersion = doc["shared"]["fw_version"];

    Serial.println(tsTitle);
    Serial.println(tsVersion);
    String tsServerPath_Firmware = giServerName + "/api/v1/"+deviceCred+"/firmware?title=" + tsTitle + "&version=" + tsVersion;

    if (gsCurr_Version != tsVersion || gsCurr_Location != tsLocation) {
      t_httpUpdate_return ret = ESPhttpUpdate.update(tsServerPath_Firmware.c_str());

      switch (ret) {
        case HTTP_UPDATE_FAILED:
          USE_SERIAL.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
          Serial.println("");
          gvsend_info(tsTitle, tsVersion, tsLocation, "FAILED");
          if (updateRetryCount > 0) {
            updateRetryCount--;
            USE_SERIAL.printf("Retrying update, attempts left: %d", updateRetryCount);
            delay(5000); // Add a delay before retrying (adjust as needed)
            OTA_Update();
          } else {
            USE_SERIAL.println("Max retry attempts reached. Giving up.");
          }
          break;

        case HTTP_UPDATE_OK:
          USE_SERIAL.println("HTTP_UPDATE_OK");
          gvsend_info(tsTitle, tsVersion, tsLocation, "UPDATED");
          break;
      }
    } else {
      gvsend_info(gsCurr_Title, gsCurr_Version, gsCurr_Location, "UPDATED");
    }
  }
}

void gvsend_info(String tsTitle, String tsVersion, String tsLocation, String State) {

  HTTPClient http;
  String tsServerPath_sendinfo = giServerName + "/api/v1/" + deviceCred + "/telemetry";
  http.begin(tsServerPath_sendinfo.c_str());
  DynamicJsonDocument doc_SendInfo(2048);

  if (State == "UPDATED") {
    doc_SendInfo["current_fw_title"] = tsTitle;
    doc_SendInfo["current_fw_version"] = tsVersion;
  } else {
    doc_SendInfo["fw_error"] = "Firmwate Update Failed";
  }

  doc_SendInfo["fw_state"] = State;

  char buff[256];
  serializeJson(doc_SendInfo, buff );

  int tiHttpResponseCode_send = http.POST(buff);

  Serial.print("HTTP_send Response code: ");
  Serial.println(tiHttpResponseCode_send);
  Serial.print("Sending buffer: ");
  Serial.println(buff);

  http.end();
}

void loop() {
  digitalWrite(2, HIGH);
  USE_SERIAL.println("HIGH");
  delay(2000);

  digitalWrite(2, LOW);
  USE_SERIAL.println("LOW");
  delay(500);
}

`

Note: This is a sample code that only shows the logic. The version modified to my needs works so it should work for you too. Please let me know if there's anything you need me to clarify.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for sharing this code! I would like a bit more detail about the ThingsBoard setup as well. Ultimately, my goal is to send a .bin file from the ThingsBoard Community Edition without relying on any external storage or dashboard. An update on what I have accomplished so far: I am able to initiate ArduinoOTA once triggered from ThingsBoard and successfully load the code into the ESP32. However, the issue with ArduinoOTA is that I have to keep the ESP32 and my laptop on the same network
May I ask where did you setup your instance of thingsboard community edition? Just to clarify some things.
On our private server
That's great! And I see that you're trying to communicate using MQTT. Did you try using the MQTT client Library and go with the logic I presented instead of using ArduinoOTA? As far as I know, ArduinoOTA is only used for Local Networks hence the reason you have to keep your ESP32 and Laptop on the same network.
Thank for your code which contains Arduino OTA. using ArduinoOTA library it is working fine. Now I want irrespective of any network. If both laptop(@ server) and ESP have internet connection then it must take the code/mqtt request... Can you please guide...? Actually I want that, If i upload the bin file in Thingsboard, esp will fetch that bin file and update it's own firmware. Being on different network too.
|
0

What you received from ThingsBoard is a message with an attributes update. Then you need to check if it's a new version and request new firmware from the server.

See this documentation for more information:
https://thingsboard.io/docs/reference/mqtt-api/#firmware-api

Also, there is a ThingsBoard Client SDK. It's a wrapper on the MQTT protocol. It's available in Arduino IDE.
You can see OTA update implementation here OTA_Firmware_Update.h or how to use this library here.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.