thingsboard / thingsboard-client-sdk

Client SDK to connect with ThingsBoard IoT Platform from IoT devices (Arduino, Espressif, etc.)
MIT License
157 stars 122 forks source link

RPC not work in esp-idf #200

Closed rayene01 closed 4 months ago

rayene01 commented 4 months ago

Hello, I made a RPC project in esp-idf, but it did not work. He showed me that he was subscribed to the RPC, but I got no response from the terminal when I sent an RPC from Thingsboard. but when I restart the esp32 and send immediately an RPC from the thingsboard, the terminal shows that RPC have been received just after he subscribed to it, and he stops receiving RPCs after that. I hope I found a solution and thank you. 

thingsboard version 12.2

`

include

include

include

include

include

// Whether the given script is using encryption or not, // generally recommended as it increases security (communication with the server is not in clear text anymore), // it does come with an overhead tough as having an encrypted session requires a lot of memory, // which might not be avaialable on lower end devices.

define ENCRYPTED false

include

include

// Examples using arduino used PROGMEM to save constants into flash memory, // this is not needed when using Espressif IDF because per default // all read only variables will be saved into DROM (flash memory). // See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/memory-types.html#drom-data-stored-in-flash // for more information about the aforementioned feature constexpr char WIFI_SSID[] = "YOUR_WIFI_SSID"; constexpr char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";

// See https://thingsboard.io/docs/getting-started-guides/helloworld/ // to understand how to obtain an access token constexpr char TOKEN[] = "YOUR_DEVICE_ACCESS_TOKEN";

// Thingsboard we want to establish a connection to constexpr char THINGSBOARD_SERVER[] = "demo.thingsboard.io";

// MQTT port used to communicate with the server, 1883 is the default unencrypted MQTT port, // whereas 8883 would be the default encrypted SSL MQTT port

if ENCRYPTED

constexpr uint16_t THINGSBOARD_PORT = 8883U;

else

constexpr uint16_t THINGSBOARD_PORT = 1883U;

endif

// 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 discarded constexpr uint16_t MAX_MESSAGE_SIZE = 256U;

if ENCRYPTED

// See https://comodosslstore.com/resources/what-is-a-root-ca-certificate-and-how-do-i-download-it/ // on how to get the root certificate of the server we want to communicate with, // this is needed to establish a secure connection and changes depending on the website. constexpr char ROOT_CERT[] = R"(-----BEGIN CERTIFICATE----- MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= -----END CERTIFICATE----- )";

endif

constexpr char RPC_TEMPERATURE_METHOD[] = "example_set_temperature"; constexpr char RPC_SWITCH_METHOD[] = "example_set_switch"; constexpr char RPC_TEMPERATURE_KEY[] = "temp"; constexpr char RPC_SWITCH_KEY[] = "switch"; constexpr char RPC_RESPONSE_KEY[] = "example_response";

// Initalize the Mqtt client instance Espressif_MQTT_Client mqttClient; // Initialize ThingsBoard instance with the maximum needed buffer size ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE);

// Statuses for subscribing to rpc bool subscribed = false;

static void on_got_ip(void arg, esp_event_base_t event_base, int32_t event_id, void event_data) { ESP_LOGI("RPC Example", "Got IP"); }

void InitWiFi() { ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default());

esp_netif_create_default_wifi_sta();

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL));

wifi_config_t wifi_config = {};
strcpy((char*)wifi_config.sta.ssid, WIFI_SSID);
strcpy((char*)wifi_config.sta.password, WIFI_PASSWORD);

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_connect());

}

RPC_Response processTemperatureChange(const RPC_Data &data) { float example_temperature = data[RPC_TEMPERATURE_KEY]; ESP_LOGI("RPC Example", "Received the set temperature RPC method: %f", example_temperature);

StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
doc[RPC_RESPONSE_KEY] = 42;
return RPC_Response(doc);

}

RPC_Response processSwitchChange(const RPC_Data &data) { bool switch_state = data[RPC_SWITCH_KEY]; ESP_LOGI("RPC Example", "Received the set switch method: %d", switch_state);

StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
doc[RPC_RESPONSE_KEY] = 22.02;
return RPC_Response(doc);

}

extern "C" void app_main(void) { InitWiFi();

const std::array<RPC_Callback, 2U> callbacks = {
    RPC_Callback{RPC_TEMPERATURE_METHOD, processTemperatureChange},
    RPC_Callback{RPC_SWITCH_METHOD, processSwitchChange}
};

while (true) {
    if (!tb.connected()) {
        ESP_LOGI("RPC Example", "Connecting to: %s with token %s", THINGSBOARD_SERVER, TOKEN);
        if (tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT)) {
            ESP_LOGI("RPC Example", "Successfully connected");
            if (!subscribed && tb.RPC_Subscribe(callbacks.begin(), callbacks.end())) {
                ESP_LOGI("RPC Example", "Subscribed to RPC commands");
                subscribed = true;
            }
        } else {
            ESP_LOGE("RPC Example", "Failed to connect");
        }
    }

    tb.loop();
    vTaskDelay(pdMS_TO_TICKS(1000));
}

} ` the code i get it from here #199

MathewHDYT commented 4 months ago

Previous version of the code had a bug, which is described in more detail in this issue #189.

Until my Pull Request #180 has been merged, you can simply move the StaticJsonDocument into global scope, which fixes the issue.

rayene01 commented 4 months ago

hey , i tried your pull request and the same problem even the arduino rpc exemple do the same when i received the only rpc after i subscribed i get this [TB] Received data from server over topic (v1/devices/me/rpc/request/272{"method":"example_set_switch","params":{"s1":false}0�u�2ƍl:��#�j�����x��BkW^T|e@#� ���h)

MathewHDYT commented 4 months ago

Do I understand you correctly the RPC is received but only at the start after you subscribe. But for all subsequent calls, the RPC method does not trigger anymore and the device does not print that it received any RPC messages?

Because the message you entered seems good to me (it prints some garbage afterwards, because the payload is uint8_t and therefore has not null termination byte), but that can be ignored.

rayene01 commented 4 months ago

Yes you right that's my issue I tried Arduino and esp-idf but i get the same problem

MathewHDYT commented 4 months ago

I had a look at the code and I have one idea what the issue could be. Not 100% sure if my inkling is correct tough.

Before the connection has been lost are your reconnecting to ThingsBoard? What I mean is are you specifically calling the connect() method on the ThingsBoard Instance.

And if you are would it be possible for you to add log messages to the Resubscribe_Topics() method in the Thingsboard.h file.

Simply adjust the method like this.

void Resubscribe_Topics() {
    if (!m_rpc_callbacks.empty()) {
        bool const result = m_client.subscribe(RPC_SUBSCRIBE_TOPIC);
        Serial.printf("Result of topic resubscription: %s \n", result ? "Success" : "Failure");
    }
    if (!m_shared_attribute_update_callbacks.empty()) {
        bool const result = mm_client.subscribe(ATTRIBUTE_TOPIC);
        Serial.printf("Result of topic resubscription: %s \n", result ? "Success" : "Failure");
    }
}
rayene01 commented 4 months ago

Hey, this is my main code, and in it, I just call tb.connect one time.  I fixed the issue. I added the function you wrote to me in the main, and it worked, but I'm still confused about why it wasn't called automatically. `extern "C" void app_main() {

ESP_LOGI("MAIN", "[APP] Startup..");
ESP_LOGI("MAIN", "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
ESP_LOGI("MAIN", "[APP] IDF version: %s", esp_get_idf_version());

esp_log_level_set("*", ESP_LOG_INFO);

ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Initialize GPIO and other peripherals
initialize();

if ENCRYPTED

mqttClient.set_server_certificate(ROOT_CERT);

endif // ENCRYPTED

InitWiFi();

for (;;) {
    // Wait until we connected to WiFi
    if (!wifi_connected) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        continue;
    }

    if (!tb.connected()) {
        tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT);

    }
    tb.setBufferSize(1024);
    if (!subscribed) {
    ESP_LOGI("MAIN", "Subscribing for RPC...");

    const std::array<RPC_Callback, 3U> callbacks = {
    // Requires additional memory in the JsonDocument for the JsonDocument that will be copied into the response
    RPC_Callback{ RPC_JSON_METHOD,           processGetJson },
    // Internal size can be 0, because if we use the JsonDocument as a JsonVariant and then set the value we do not require additional memory
    RPC_Callback{ RPC_SWITCH_METHOD,         processSwitchChange }
    };
// Perform a subscription. All consequent data processing will happen in
// processTemperatureChange() and processSwitchChange() functions,
// as denoted by callbacks array.
if (!tb.RPC_Subscribe(callbacks.cbegin(), callbacks.cend())) {
    ESP_LOGI("MAIN", "Failed to subscribe for RPC");

  return;
}
ESP_LOGI("MAIN", "Subscribe done");

subscribed = true;

}

tb.Resubscribe_Topics();

    tb.loop();

    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

}`

MathewHDYT commented 4 months ago

Thanks that's interesting, can you tell me what is printed on the serial, when the tb.Resubscribe_Topics() and tb.connect() method are called?

Additionally could you move the tb.Resubscribe_Topics() method into this block of your main function and see if it still works as well?

if (!tb.connected()) {
    tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT);
    tb.Resubscribe_Topics();
}
rayene01 commented 4 months ago

hey thats what i see in terminal: I (15343) Resubscribe: Result of topic resubscription: Success

I (16348) Resubscribe: Result of topic resubscription: Success

I (17353) Resubscribe: Result of topic resubscription: Success

I (18358) Resubscribe: Result of topic resubscription: Success

and no when i move tb.Resubscribe_Topics() to that block it stop working

MathewHDYT commented 4 months ago

Interesting so it does not seem to be because we lose connection with the MQTT broker and have to reconnect again. Because that was my inital assumption.

Somehow we loose the topic subscritption because of another reason, but I can't tell what could cause that to occur, because the library never implicitly unsubscribes server-side RPC or shared attribute updates.


Just to be sure you are sending for example one RPC request from the server to the client, that is still received and the next request will already not work again. It only works if you repeadatly call tb.Resubscribe_Topics();?.

Just to make sure can you add this log message to see if you disconnect, while you receive the last RPC message that is still handled by the client.

if (!tb.connected()) {
    tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT);
    Serial.printf("Lost connection with MQTT broker, attempting to reconnect");
}
rayene01 commented 4 months ago

I always ask why happen to me and i use the same code that others use Does the esp32 model i use can be the problem ? "ttgo lora32-oled v1"

MathewHDYT commented 4 months ago

You are not the only one, that has this issue as far as I know. But for the othere developer it's an ESP32-S3 and they do receive RPC after the first one, but after around 5 - 6 hours they don't anymore.

So I'm not 100% sure it is the same issue but it very well could be.


For now it is probably a good idea to add the tb.Resubscribe_Topics() call in the main loop. But that is more of a bandaid than anything else, but while I'll try to fix the actual underlying issue this should make the library work correctly for now at least.

rayene01 commented 4 months ago

Understood, thanks for your help i had two week trying to fix the issue hhhh. Any way i will keep this issue Open for now Until found a good solution