Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RPC not work in esp-idf #200

Closed
rayene01 opened this issue May 13, 2024 · 12 comments · Fixed by #180
Closed

RPC not work in esp-idf #200

rayene01 opened this issue May 13, 2024 · 12 comments · Fixed by #180

Comments

@rayene01
Copy link

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 <esp_netif.h>
#include <esp_log.h>
#include <esp_wifi.h>
#include <nvs_flash.h>
#include <string.h>

// 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 <Espressif_MQTT_Client.h>
#include <ThingsBoard.h>

// 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
Copy link
Contributor

MathewHDYT commented May 13, 2024

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
Copy link
Author

rayene01 commented May 14, 2024

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
Copy link
Contributor

MathewHDYT commented May 15, 2024

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
Copy link
Author

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

@MathewHDYT
Copy link
Contributor

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
Copy link
Author

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
Copy link
Contributor

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
Copy link
Author

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
Copy link
Contributor

MathewHDYT commented May 18, 2024

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
Copy link
Author

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
Copy link
Contributor

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
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants