mlesniew / PicoMQTT

ESP MQTT client and broker library
GNU Lesser General Public License v3.0
219 stars 25 forks source link

PicoMQTT Server - on connect, on subscribe #41

Closed andrzejbluszcz closed 2 weeks ago

andrzejbluszcz commented 3 weeks ago

Hi! How to know what are current subscriptions? Or, does Server notify about a new connection/subscription?

Thank you.

mlesniew commented 3 weeks ago

You can subclass PicoMQTT::Server and override on_connected and on_subscribe, see:

andrzejbluszcz commented 3 weeks ago

Thank you for the suggestion. I wish I knew C++ enough to make it. I see that in server.h the functins on_connected() and on_subscribe() are declared only. There are apparently no definitions for them in server.cpp. Does that mean that I should write those to suit my purpose?

mlesniew commented 3 weeks ago

No, on_connected and on_subscribe are both declared and defined in server.h, but their bodies are empty (note the {} after the function names).

Here's how you can override these functions and add a custom logic when a client connects or subscribes to a topic:

class CustomMQTTServer: public PicoMQTT::Server {
    protected:
        virtual void on_connected(const char * client_id) override {
            Serial.println("A new client connected");
        }

        virtual void on_subscribe(const char * client_id, const char * topic) override {
            Serial.println("A client set up a new subscription");
        }
};

CustomMQTTServer mqtt;

void setup() {
    Serial.begin(115200);
    mqtt.begin();
}

void loop() {
    mqtt.loop();
}

You can override any other function marked virtual in the same way.

andrzejbluszcz commented 2 weeks ago

Thank you very much for your help and patience. I think that closes the issue.

mhaberler commented 2 weeks ago

@mlesniew would you mind giving this a try? the above seems not to work for me

https://github.com/mlesniew/PicoMQTT/compare/master...mhaberler:PicoMQTT:custom-server-example

mhaberler commented 2 weeks ago

I stumbled on some c++ compiler issue - see updated example with devkit-c config from previous post this conatins a bleeding-edge Arduino platfrom and toolchain as per below:

Executing task: platformio run --environment custom-server-devkitc 

Processing custom-server-devkitc (platform: https://github.com/Jason2866/platform-espressif32/releases/download/2024.07.20/platform-espressif32.zip; board: esp32-s3-devkitc-1-32MB-8MB; framework: arduino)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32-s3-devkitc-1-32MB-8MB.html
PLATFORM: Espressif 32 (2024.7.20) > Espressif ESP32-S3-DevKitC-2-opi
HARDWARE: ESP32S3 240MHz, 320KB RAM, 32MB Flash
DEBUG: Current (esp-builtin) On-board (esp-builtin) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES: 
 - framework-arduinoespressif32 @ 3.0.2+sha.01c738d 
 - tool-esptoolpy @ 4.7.5 
 - tool-mklittlefs @ 3.2.0 
 - tool-openocd-esp32 @ 2.1100.20220706 (11.0) 
 - tool-riscv32-esp-elf-gdb @ 12.1.0+20221002 
 - tool-xtensa-esp-elf-gdb @ 12.1.0+20221002 
 - toolchain-riscv32-esp @ 13.2.0+20230928 
 - toolchain-xtensa-esp-elf @ 13.2.0+20230928
Converting custom_server.ino
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 28 compatible libraries
Scanning dependencies...
Dependency Graph
|-- PicoMQTT @ 0.3.8
Building in debug mode
Compiling .pio/build/custom-server-devkitc/src/custom_server.ino.cpp.o
Compiling .pio/build/custom-server-devkitc/lib932/PicoMQTT/PicoMQTT/connection.cpp.o
Compiling .pio/build/custom-server-devkitc/lib932/PicoMQTT/PicoMQTT/server.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/FunctionalInterrupt.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/HEXBuilder.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/HWCDC.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/HardwareSerial.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/IPAddress.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/MD5Builder.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/MacAddress.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/Print.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/SHA1Builder.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/Stream.cpp.o
Compiling .pio/build/custom-server-devkitc/FrameworkArduino/StreamString.cpp.o
lib/PicoMQTT/src/PicoMQTT/connection.cpp: In member function 'void PicoMQTT::Connection::wait_for_reply(PicoMQTT::Packet::Type, std::function<void(PicoMQTT::IncomingPacket&)>)':
lib/PicoMQTT/src/PicoMQTT/connection.cpp:67:24: error: cannot declare variable 'packet' to be of abstract type 'PicoMQTT::IncomingPacket'
   67 |         IncomingPacket packet(client);
      |                        ^~~~~~
In file included from lib/PicoMQTT/src/PicoMQTT/connection.h:8,
                 from lib/PicoMQTT/src/PicoMQTT/connection.cpp:2:
lib/PicoMQTT/src/PicoMQTT/incoming_packet.h:10:7: note:   because the following virtual functions are pure within 'PicoMQTT::IncomingPacket':
   10 | class IncomingPacket: public Packet, public Client {
      |       ^~~~~~~~~~~~~~
In file included from /Users/mah/.platformio/packages/framework-arduinoespressif32@src-c38c877b286c4329f2d3ff62747e7f12/cores/esp32/Arduino.h:197,
                 from lib/PicoMQTT/src/PicoMQTT/connection.h:5:
/Users/mah/.platformio/packages/framework-arduinoespressif32@src-c38c877b286c4329f2d3ff62747e7f12/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;
      |               ^~~~~~~
/Users/mah/.platformio/packages/framework-arduinoespressif32@src-c38c877b286c4329f2d3ff62747e7f12/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;
      |               ^~~~~~~
lib/PicoMQTT/src/PicoMQTT/connection.cpp: In member function 'virtual void PicoMQTT::Connection::loop()':
lib/PicoMQTT/src/PicoMQTT/connection.cpp:171:24: error: cannot declare variable 'packet' to be of abstract type 'PicoMQTT::IncomingPacket'
  171 |         IncomingPacket packet(client);
      |                        ^~~~~~
*** [.pio/build/custom-server-devkitc/lib932/PicoMQTT/PicoMQTT/connection.cpp.o] Error 1
lib/PicoMQTT/src/PicoMQTT/server.cpp: In member function 'virtual void PicoMQTT::BasicServer::Client::on_message(const char*, PicoMQTT::IncomingPacket&)':
lib/PicoMQTT/src/PicoMQTT/server.cpp:133:25: error: cannot declare variable 'incoming_publish' to be of abstract type 'PicoMQTT::BasicServer::IncomingPublish'
  133 |         IncomingPublish incoming_publish(packet, publish);
      |                         ^~~~~~~~~~~~~~~~
In file included from lib/PicoMQTT/src/PicoMQTT/server.cpp:3:
lib/PicoMQTT/src/PicoMQTT/server.h:55:15: note:   because the following virtual functions are pure within 'PicoMQTT::BasicServer::IncomingPublish':
   55 |         class IncomingPublish: public IncomingPacket {
      |               ^~~~~~~~~~~~~~~
In file included from /Users/mah/.platformio/packages/framework-arduinoespressif32@src-c38c877b286c4329f2d3ff62747e7f12/cores/esp32/Arduino.h:197,
                 from lib/PicoMQTT/src/PicoMQTT/server.h:6:
/Users/mah/.platformio/packages/framework-arduinoespressif32@src-c38c877b286c4329f2d3ff62747e7f12/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;
      |               ^~~~~~~
/Users/mah/.platformio/packages/framework-arduinoespressif32@src-c38c877b286c4329f2d3ff62747e7f12/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;
      |               ^~~~~~~
*** [.pio/build/custom-server-devkitc/lib932/PicoMQTT/PicoMQTT/server.cpp.o] Error 1
=================================================================================== [FAILED] Took 2.83 seconds ===================================================================================

Environment            Status    Duration
---------------------  --------  ------------
custom-server-devkitc  FAILED    00:00:02.825
mlesniew commented 2 weeks ago

From the build log it looks like you're not using the latest PicoMQTT version. Can you check if this also happens with version 1.1.1?

mhaberler commented 2 weeks ago

yes, sorry about that - switching to 1.1.1 adressed the first question

build succeeds with the 8.4 toolchain brought by platform = espressif32@6.7.0

PACKAGES: 
 - framework-arduinoespressif32 @ 3.20016.0 (2.0.16) 
 - tool-esptoolpy @ 1.40501.0 (4.5.1) 
 - tool-openocd-esp32 @ 2.1100.20220706 (11.0) 
 - toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 
 - toolchain-xtensa-esp32s3 @ 8.4.0+2021r2-patch5

second issue still remains but that is likely my low C++ fu - trying the (unsupported) platform package

platform = https://github.com/Jason2866/platform-espressif32/releases/download/2024.07.20/platform-espressif32.zip

brings in the 13.2.0 toolchain which seems to be more picky:

PACKAGES: 
 - framework-arduinoespressif32 @ 3.0.2+sha.01c738d 
 - tool-esptoolpy @ 4.7.5 
 - tool-mklittlefs @ 3.2.0 
 - tool-openocd-esp32 @ 2.1100.20220706 (11.0) 
 - tool-riscv32-esp-elf-gdb @ 12.1.0+20221002 
 - tool-xtensa-esp-elf-gdb @ 12.1.0+20221002 
 - toolchain-riscv32-esp @ 13.2.0+20230928 
 - toolchain-xtensa-esp-elf @ 13.2.0+20230928
In file included from lib/PicoMQTT/src/PicoMQTT/client.h:5,
                 from lib/PicoMQTT/src/PicoMQTT.h:19,
                 from /Users/mah/Ballon/src/BalloonWare/networking/PicoMQTT/examples/custom_server/custom_server.ino:5:
lib/PicoMQTT/src/PicoMQTT/connection.h:64:23: error: cannot declare field 'PicoMQTT::Connection::client' to be of abstract type 'PicoMQTT::ClientWrapper'
   64 |         ClientWrapper client;
      |                       ^~~~~~
In file included from lib/PicoMQTT/src/PicoMQTT/connection.h:8:
lib/PicoMQTT/src/PicoMQTT/client_wrapper.h:7:7: note:   because the following virtual functions are pure within 'PicoMQTT::ClientWrapper':
    7 | class ClientWrapper: public ::Client {
      |       ^~~~~~~~~~~~~

not sure it's worth investigating, I'd just like to understand why

mhaberler commented 2 weeks ago

that was too quick: with v1.1.1 and platform = espressif32@6.7.0 the example builds

however the callbacks are NOT executed

mlesniew commented 2 weeks ago

Try replacing CustomMQTTServer::Server mqtt;

With CustomMQTTServer mqtt;

mhaberler commented 2 weeks ago

that did it, sorry for the snafu

mhaberler commented 2 weeks ago

would you mind sketching how this would work in the multi_server example?

the subclassing as suggested above doesnt work in this case, looks like advanced templatology is required if I try like so:

CustomMQTTServer mqtt(mqtt_tcp_server, websocket_server);

 Compiling .pio/build/custom-server-devkitc/src/custom_server.ino.cpp.o
/Users/mah/Ballon/src/BalloonWare/networking/PicoMQTT/examples/custom_server/custom_server.ino:41:56: error: no matching function for call to 'CustomMQTTServer::CustomMQTTServer(WiFiServer&, WiFiServer&)'
 CustomMQTTServer mqtt(mqtt_tcp_server, websocket_server);
                                                        ^
/Users/mah/Ballon/src/BalloonWare/networking/PicoMQTT/examples/custom_server/custom_server.ino:20:7: note: candidate: 'CustomMQTTServer::CustomMQTTServer()'
 class CustomMQTTServer: public PicoMQTT::Server {
       ^~~~~~~~~~~~~~~~
/Users/mah/Ballon/src/BalloonWare/networking/PicoMQTT/examples/custom_server/custom_server.ino:20:7: note:   candidate expects 0 arguments, 2 provided
/Users/mah/Ballon/src/BalloonWare/networking/PicoMQTT/examples/custom_server/custom_server.ino:20:7: note: candidate: 'CustomMQTTServer::CustomMQTTServer(CustomMQTTServer&&)'
/Users/mah/Ballon/src/BalloonWare/networking/PicoMQTT/examples/custom_server/custom_server.ino:20:7: note:   candidate expects 1 argument, 2 provided
*** [.pio/build/custom-server-devkitc/src/custom_server.ino.cpp.o] Error 1
mlesniew commented 5 days ago

Sorry, I missed this question before.

There reason it's not working is that CustomMQTTServer doesn't have any constructor defined. This makes the compiler create a constructor automatically, which calls the default constructor ofPicoMQTT::Server.

To make CustomMQTTServer inherit its parent's constructors, we have to explicitly request it with the using keyword like this:

class CustomMQTTServer: public PicoMQTT::Server {

    using  PicoMQTT::Server::Server;
    ...
};