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

Error from Arduino HTTP Client == Arduino_HTTP_Client::m_http_client' to be of abstract type 'HttpClient' #233

Open
lenokoe opened this issue Nov 3, 2024 · 4 comments · May be fixed by #231

Comments

@lenokoe
Copy link

lenokoe commented Nov 3, 2024

Hi, I am absolutely newbie in this field can trying to get a grasp about thingsboard. I made several sensors connected using my arduino ide 2.3.3. Currently I am trying to get those data into a dashboard, which i pick thingsboard. I already followed instructions from https://components.espressif.com/components/thingsboard/thingsboard/versions/0.14.0

I even copy pasted the code there and change the access accordingly. but i still get this error

cannot declare field 'Arduino_HTTP_Client::m_http_client' to be of abstract type 'HttpClient'

I found in issues no 17, there are similar case but it seems that those are quite old issues and not related anymore. Can anyone please help me with this issue ?

This is what i installed in arduino IDE 2.3.3:

  1. Thingsboard 0.14.0
  2. TBPubSubClient 2.10.0
  3. StreamUtils 1.9.0
  4. MQTTPubSubClient 0.3.2
  5. ArxTypeTraits 0.3.1
  6. ArxContainer 0.7.0
  7. Arduinojson 6.21.5
  8. ArduinoHttpClient 0.6.1
@MathewHDYT
Copy link
Contributor

Now sure why you are installing MQTTPubSubClient, doesn't seem like you require that library.

The rest of the libraries seems to be correct, so I'm not sure why it fails when it attempts to initalize an instance of the HttpClient abstract class. The only thing I could imagine is a naming conflict, but I'm not sure either.

Could you attach a simple version of your code? Because with the current information it is not possible to help any further.

@lenokoe
Copy link
Author

lenokoe commented Nov 5, 2024

Hi @MathewHDYT

My bad, MQTTPubSubClient was my trial and error for this case. The library should not be there.

Below i put my code

#if defined(ESP8266)
#include <ESP8266WiFi.h>
#define THINGSBOARD_ENABLE_PROGMEM 0
#elif defined(ESP32) || defined(RASPBERRYPI_PICO) || defined(RASPBERRYPI_PICO_W)
#include <WiFi.h>
#endif

#ifndef LED_BUILTIN
#define LED_BUILTIN 8
#endif

#include <Arduino_MQTT_Client.h>
#include <Server_Side_RPC.h>
#include <Attribute_Request.h>
#include <Shared_Attribute_Update.h>
#include <ThingsBoard.h>


constexpr char WIFI_SSID[] = "18XXXXXX18";
constexpr char WIFI_PASSWORD[] = "XXXXXXXX";

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


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


// MQTT port used to communicate with the server, 1883 is the default unencrypted MQTT port.
constexpr uint16_t THINGSBOARD_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 discarded
constexpr uint32_t MAX_MESSAGE_SIZE = 1024U;

// Baud rate for the debugging serial connection.
// If the Serial output is mangled, ensure to change the monitor speed accordingly to this variable
constexpr uint32_t SERIAL_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 subscribed
constexpr size_t MAX_ATTRIBUTES = 3U;

constexpr uint64_t REQUEST_TIMEOUT_MICROSECONDS = 5000U * 1000U;

// Attribute names for attribute request and attribute updates functionality

constexpr const char BLINKING_INTERVAL_ATTR[] = "blinkingInterval";
constexpr const char LED_MODE_ATTR[] = "ledMode";
constexpr const char LED_STATE_ATTR[] = "ledState";

// Initialize underlying client, used to establish a connection
WiFiClient wifiClient;

// Initalize the Mqtt client instance
Arduino_MQTT_Client mqttClient(wifiClient);

// Initialize used apis
Server_Side_RPC<3U, 5U> rpc;
Attribute_Request<2U, MAX_ATTRIBUTES> attr_request;
Shared_Attribute_Update<3U, MAX_ATTRIBUTES> shared_update;

const std::array<IAPI_Implementation*, 3U> apis = {
    &rpc,
    &attr_request,
    &shared_update
};

// Initialize ThingsBoard instance with the maximum needed buffer size, stack size and the apis we want to use
ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE, Default_Max_Stack_Size, apis);

// handle led state and mode changes
volatile bool attributesChanged = false;

// LED modes: 0 - continious state, 1 - blinking
volatile int ledMode = 0;

// Current led state
volatile bool ledState = false;

// Settings for interval in blinking mode
constexpr uint16_t BLINKING_INTERVAL_MS_MIN = 10U;
constexpr uint16_t BLINKING_INTERVAL_MS_MAX = 60000U;
volatile uint16_t blinkingInterval = 1000U;

uint32_t previousStateChange;

// For telemetry
constexpr int16_t telemetrySendInterval = 2000U;
uint32_t previousDataSend;

// List of shared attributes for subscribing to their updates
constexpr std::array<const char *, 2U> SHARED_ATTRIBUTES_LIST = {
  LED_STATE_ATTR,
  BLINKING_INTERVAL_ATTR
};

// List of client attributes for requesting them (Using to initialize device states)
constexpr std::array<const char *, 1U> CLIENT_ATTRIBUTES_LIST = {
  LED_MODE_ATTR
};

/// @brief Initalizes WiFi connection,
// will endlessly delay until a connection has been successfully established
void InitWiFi() {
  Serial.println("Connecting to AP ...");
  // Attempting to establish a connection to the given WiFi network
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    // Delay 500ms until a connection has been succesfully established
    delay(500);
    Serial.print(".");
  }
  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 again
const bool reconnect() {
  // Check to ensure we aren't connected yet
  const wl_status_t status = WiFi.status();
  if (status == WL_CONNECTED) {
    return true;
  }

  // If we aren't establish a new connection to the given WiFi network
  InitWiFi();
  return true;
}


/// @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 value
void processSetLedMode(const JsonVariantConst &data, JsonDocument &response) {
  Serial.println("Received the set led state RPC method");

  // Process data
  int new_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 mode
  response_doc["newMode"] = (int)ledMode;
  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 instead
const std::array<RPC_Callback, 1U> callbacks = {
  RPC_Callback{ "setLedMode", processSetLedMode }
};


/// @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 value
void processSharedAttributes(const JsonObjectConst &data) {
  for (auto it = data.begin(); it != data.end(); ++it) {
    if (strcmp(it->key().c_str(), BLINKING_INTERVAL_ATTR) == 0) {
      const uint16_t new_interval = it->value().as<uint16_t>();
      if (new_interval >= BLINKING_INTERVAL_MS_MIN && new_interval <= BLINKING_INTERVAL_MS_MAX) {
        blinkingInterval = new_interval;
        Serial.print("Blinking interval is set to: ");
        Serial.println(new_interval);
      }
    } else if (strcmp(it->key().c_str(), LED_STATE_ATTR) == 0) {
      ledState = it->value().as<bool>();
      if (LED_BUILTIN != 99) {
        digitalWrite(LED_BUILTIN, ledState);
      }
      Serial.print("LED state is set to: ");
      Serial.println(ledState);
    }
  }
  attributesChanged = true;
}

void processClientAttributes(const JsonObjectConst &data) {
  for (auto it = data.begin(); it != data.end(); ++it) {
    if (strcmp(it->key().c_str(), LED_MODE_ATTR) == 0) {
      const uint16_t new_mode = it->value().as<uint16_t>();
      ledMode = new_mode;
    }
  }
}

// Attribute request did not receive a response in the expected amount of microseconds 
void requestTimedOut() {
  Serial.printf("Attribute request timed out did not receive a response in (%llu) microseconds. Ensure client is connected to the MQTT broker and that the keys actually exist on the target device\n", REQUEST_TIMEOUT_MICROSECONDS);
}

const Shared_Attribute_Callback<MAX_ATTRIBUTES> attributes_callback(&processSharedAttributes, SHARED_ATTRIBUTES_LIST.cbegin(), SHARED_ATTRIBUTES_LIST.cend());
const Attribute_Request_Callback<MAX_ATTRIBUTES> attribute_shared_request_callback(&processSharedAttributes, REQUEST_TIMEOUT_MICROSECONDS, &requestTimedOut, SHARED_ATTRIBUTES_LIST);
const Attribute_Request_Callback<MAX_ATTRIBUTES> attribute_client_request_callback(&processClientAttributes, REQUEST_TIMEOUT_MICROSECONDS, &requestTimedOut, CLIENT_ATTRIBUTES_LIST);

void setup() {
  // Initialize serial connection for debugging
  Serial.begin(SERIAL_DEBUG_BAUD);
  if (LED_BUILTIN != 99) {
    pinMode(LED_BUILTIN, OUTPUT);
  }
  delay(1000);
  InitWiFi();
}

void loop() {
  delay(10);

  if (!reconnect()) {
    return;
  }

  if (!tb.connected()) {
    // Connect to the ThingsBoard
    Serial.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;
    }
    // Sending a MAC address as an attribute
    tb.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 (!rpc.RPC_Subscribe(callbacks.cbegin(), callbacks.cend())) {
      Serial.println("Failed to subscribe for RPC");
      return;
    }

    if (!shared_update.Shared_Attributes_Subscribe(attributes_callback)) {
      Serial.println("Failed to subscribe for shared attribute updates");
      return;
    }

    Serial.println("Subscribe done");

    // Request current states of shared attributes
    if (!attr_request.Shared_Attributes_Request(attribute_shared_request_callback)) {
      Serial.println("Failed to request for shared attributes");
      return;
    }

    // Request current states of client attributes
    if (!attr_request.Client_Attributes_Request(attribute_client_request_callback)) {
      Serial.println("Failed to request for client attributes");
      return;
    }
  }

  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;
    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);
    } else {
      digitalWrite(LED_BUILTIN, ledState);
    }
  }

  // Sending telemetry every telemetrySendInterval time
  if (millis() - previousDataSend > telemetrySendInterval) {
    previousDataSend = millis();
    tb.sendTelemetryData("temperature", random(10, 20));
    tb.sendAttributeData("rssi", WiFi.RSSI());
    tb.sendAttributeData("channel", WiFi.channel());
    tb.sendAttributeData("bssid", WiFi.BSSIDstr().c_str());
    tb.sendAttributeData("localIp", WiFi.localIP().toString().c_str());
    tb.sendAttributeData("ssid", WiFi.SSID().c_str());
  }

  tb.loop();
}

And the errors were :

In file included from c:\Users\XXXXX\Documents\Arduino\libraries\ThingsBoard\src\Arduino_HTTP_Client.cpp:2:
c:\Users\XXXXX\Documents\Arduino\libraries\ThingsBoard\src\Arduino_HTTP_Client.h:46:16: error: cannot declare field 'Arduino_HTTP_Client::m_http_client' to be of abstract type 'HttpClient'
   46 |     HttpClient m_http_client; // Underlying HTTP client instance used to send data
      |                ^~~~~~~~~~~~~
In file included from c:\Users\XXXXX\Documents\Arduino\libraries\ArduinoHttpClient\src/ArduinoHttpClient.h:8,
                 from c:\Users\XXXXX\Documents\Arduino\libraries\ThingsBoard\src\Arduino_HTTP_Client.h:10:
c:\Users\XXXXX\Documents\Arduino\libraries\ArduinoHttpClient\src/HttpClient.h:39:7: note:   because the following virtual functions are pure within 'HttpClient':
   39 | class HttpClient : public Client
      |       ^~~~~~~~~~
In file included from C:\Users\XXXXX\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.0-RC1\cores\esp32/Arduino.h:197,
                 from c:\Users\XXXXX\Documents\Arduino\libraries\ArduinoHttpClient\src/HttpClient.h:8:
C:\Users\XXXXX\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.0-RC1\cores\esp32/Client.h:29:15: note:     'virtual int Client::connect(IPAddress, uint16_t, int32_t)'
   29 |   virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) = 0;
      |               ^~~~~~~
C:\Users\XXXXX\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.0-RC1\cores\esp32/Client.h:31:15: note:     'virtual int Client::connect(const char*, uint16_t, int32_t)'
   31 |   virtual int connect(const char *host, uint16_t port, int32_t timeout) = 0;
      |               ^~~~~~~

exit status 1

Compilation error: exit status 1

Thanks for the help.

@MathewHDYT
Copy link
Contributor

Interesting it seems that the HttpClient is not up to date with the interface. The underlying client interface has an additional method that is pure virtual, meaning it has no implementation and need to be implemented by the abstract class.

And that implementation is not done by the HttpClient causing the aformentioned compilation issues. To fix that could you go into the HttpClient and write an implementation for the int connect(const char *host, uint16_t port, int32_t timeout); method.

If that fixed your issue you would probably need to forward this issue in the GitHub issues of the ArduinoHttpClient instead.

@lenokoe
Copy link
Author

lenokoe commented Nov 6, 2024

Noted. Thanks for the help but I think I will try another tool first before continuing with thingsboard...
in search for my optimal setting prototype :)

@MathewHDYT MathewHDYT linked a pull request Nov 6, 2024 that will close this issue
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