Connection handlers - arduino

I would like to setup a handler for when my ESP8266 is connected, so that it returns the IP address (among other things). I am calling a Network class method from my main class's setup function, and am not sure how to provide the necessary parameters to the handler. I attempted the following, but I get an error about how ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.
I would love any feedback,
Thanks!
/* Network.h */
class Network{
public:
void connect(const char* ssid, const char* password);
WiFiEventHandler deviceDidConnect;
void onDeviceConnected(const WiFiEventStationModeGotIP& evt);
}
/* Network.cpp */
void Network::connect(const char* ssid, const char* password) {
WiFi.persistent(false);
if (WiFi.status() == WL_DISCONNECTED) {
deviceDidConnect = WiFi.onStationModeGotIP(&onDeviceConnected);
// Not sure how to handle it here...
}
}
void onDeviceConnected(const WiFiEventStationModeGotIP& evt) {
Serial.print("Station connected, IP: ");
Serial.println(WiFi.localIP());
}
/* MyApp */
#include "Network.h"
void setup(){
Serial.begin(115200);
network.connect(SSID, PASSWORD);
}
Updated on 11/08/18:
My goal is to create an Arduino library to handle my connections.
I noticed that if I leave the connect method and the WifiEventHandler in the header file, but move the onDeviceConnected method signature (void onDeviceConnected(const WiFiEventStationModeGotIP& evt);) to Network.cpp (after I include Network.h), then the sketch compiles and works fine.

It works. I was under the misguided notion that all methods in a custom Arduino library should be exposed either as Public or Private in the header file, whereas only methods that need to be shared need be exposed. It makes sense.

Related

Sending a series of messages to a client from a QWebSocketServer in real-time

I have implemented a websocket server in a QCoreApplication. As soon as a connection is established with a client, I wish to send a series of messages to it immediately in real-time with a delay of 0.5 seconds between messages. However, the individual messages reach the client only after all the messages have been sent or right after sendMyMessages() method returns in the below implementation of my websocket server. If I have a huge number of messages to send, the client has to wait for a long time before getting all the messages in one go.
MyWebSocketServer.h
#ifndef MYWEBSOCKETSERVER_H
#define MYWEBSOCKETSERVER_H
#include <QtCore/QObject>
#include <QtCore/QList>
#include <QtCore/QByteArray>
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)
class MyWebSocketServer : public QObject
{
Q_OBJECT
public:
explicit MyWebSocketServer(quint16 port,
bool debug = false,
QObject *parent = nullptr);
~MyWebSocketServer();
Q_SIGNALS:
void closed();
private Q_SLOTS:
void onNewConnection();
void socketDisconnected();
private:
void sendMyMessages(QWebSocket *client);
QWebSocketServer *m_pWebSocketServer;
QList<QWebSocket *> m_clients;
bool m_debug;
};
#endif // MYWEBSOCKETSERVER_H
MyWebSocketServer.cpp
#include "MyWebSocketServer.h"
#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <QtWebSockets/qwebsocketserver.h>
#include <QtWebSockets/qwebsocket.h>
#include <QtCore/QDebug>
QT_USE_NAMESPACE
MyWebSocketServer::MyWebSocketServer(quint16 port,
bool debug,
QObject *parent)
: QObject(parent)
, m_pWebSocketServer(new QWebSocketServer(
QStringLiteral("My WebSocket Server"),
QWebSocketServer::NonSecureMode,
this))
, m_debug(debug)
{
connect(m_pWebSocketServer,
&QWebSocketServer::newConnection,
this,
&MyWebSocketServer::onNewConnection);
connect(m_pWebSocketServer,
&QWebSocketServer::closed,
this,
&MyWebSocketServer::closed);
m_pWebSocketServer->listen(QHostAddress::LocalHost, port);
}
MyWebSocketServer::~MyWebSocketServer()
{
m_pWebSocketServer->close();
qDeleteAll(m_clients.begin(), m_clients.end());
}
void MyWebSocketServer::onNewConnection()
{
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
connect(pSocket,
&QWebSocket::disconnected,
this,
&MyWebSocketServer::socketDisconnected);
m_clients << pSocket;
sendMyMessages(pSocket);
}
void MyWebSocketServer::sendMyMessages(QWebSocket *client)
{
std::fstream jsonStringFileHandler;
jsonStringFileHandler.open("my-messages.txt", std::ios::in);
if (jsonStringFileHandler.is_open())
{
std::string message;
while(getline(jsonStringFileHandler, message))
{
// Individual messages don't go through immediately
// Only after this method returns, all the messages show up on the client's end
// Is it possible to send the individual messages immediately? (probably with a 0.5 second delay)
client->sendTextMessage(QString::fromUtf8(message.c_str()));
}
jsonStringFileHandler.close();
}
}
void MyWebSocketServer::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (pClient)
{
m_clients.removeAll(pClient);
pClient->deleteLater();
}
}
Only after the sendMyMessages() returns, the client gets all the messages. It is not in real-time. I understand it would be possible to achieve what I am after using some asynchronous programming technique but I unable to figure out a way to set it up in my implementation of the websocket server.
Here is an implementation of the websocket server that worked for me in Python using the websockets and asyncio modules. However, I wish to implement the same logic in C++ using Qt.
import asyncio
import websockets
async def sendMyMessages(websocket, path):
with open("my-messages.txt") as fp:
lines = fp.readlines()
for line in lines:
await asyncio.sleep(0.5)
await websocket.send(line.strip())
start_server = websockets.serve(sendMyMessages, "127.0.0.1", 3000)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
One of the approaches I found uses QWebSocket::flush() immediately after a call to QWebSocket::sendTextMessage().
From the docs on QWebSocket::flush(),
This function writes as much as possible from the internal write buffer to the underlying network socket, without blocking. If any data was written, this function returns true; otherwise false is returned. Call this function if you need QWebSocket to start sending buffered data immediately. The number of bytes successfully written depends on the operating system. In most cases, you do not need to call this function, because QWebSocket will start sending data automatically once control goes back to the event loop.
However, I am not sure if this is the right approach as the doc indicates that the data may not be written to the underlying network socket reliably. I will be happy to know if there is a better technique out there!
client->sendTextMessage(QString::fromUtf8(message.c_str()));
client->flush();
std::this_thread::sleep_for(std::chrono::milliseconds(50));

libconnman-qt connect to wifi

Hi currently I am working on project that require to connect to wifi and I am using libconnman-qt.
Everything goes well (enable/disable wifi, list of wifi), until I found a problem to connect to the wifi. So when I connect the service to wifi by :
mCurrentNetworkService->setPassphrase(ui->linePassword->text());
mCurrentNetworkService->requestConnect();
Occurs an error that says : "Not Registered". I don't know what happen, since the lib not give any clue for me. Or maybe there is step that I missed?
You must first register an "agent" that can respond to requests for input from the connman daemon. Here is a simple example.
#include <networkservice.h>
#include <useragent.h>
class Wifi : public QObject {
Q_OBJECT
public:
Wifi(QObject *parent = 0) :
QObject(parent), m_agent(NULL), m_service(NULL) {
//Register an agent to handle requests from connmand
m_agent = new UserAgent(this);
//Connect to UserAgent signal
connect(m_agent, SIGNAL(userInputRequested(QString, QVariantMap)),
this, SLOT(agentRequestedUserInput(QString, QVariantMap)));
}
~Wifi() {}
public Q_SLOTS:
void agentRequestedUserInput(QString path, QVariantMap fields) {
Q_UNUSED(path)
QVariantMap reply;
reply.insert("Passphrase", QString("pass1234"));
m_agent->sendUserReply(reply);
}
void connectToService(QString servicePath) {
// Add logic to find NetworkService pointer for the service you will connect to
// pseudo code
// m_service = findService(servicePath);
m_service->requestConnect();
}
private:
UserAgent *m_agent;
NetworkService *m_service;
}

Emitting signal from callback

I am using RtMidi library to handle midi message in my Qt application and I am facing problem with slot trigger:
My PhMidiInput object is emiting signal from the RtMidi callback upon specific midi message but the slots are not always triggered.
Here is a part of the PhMidiInput class:
class PhMidiInput : QObject
{
Q_OBJECT
public:
void PhMidiInput() {}
signals:
void quarterFrame(unsigned char data);
private:
static void callback(double, std::vector< unsigned char > *message, void *userData ) {
PhMidiInput *midiInput = (PhMidiInput*)userData;
if(midiInput)
midiInput->onMessage(message);
}
void onMessage(std::vector<unsigned char> *message) {
...
emit quarterFrame(data);
...
}
}
Connecting to a lambda functor works:
PhMidiInput midiIn;
int quarterFrameCount;
connect(&midiIn, &PhMidiInput::quarterFrame, [&](unsigned char data) {
quarterFrameCount++;
});
Connecting to my application window works to:
// MyWindow inherits from QMainWindow
connect(_midiIn, &PhMidiInput::quarterFrame, this, &MyWindow::onQuarterFrame);
When trying to connect to a custom class (MidiTest) inheriting from QObject it does'nt trigger:
connect(_midiIn, &PhMidiInput::quarterFrame, this, &MidiTest::onQuarterFrame);
I was wondering if there was something around QObject::moveToThread() but since I don't create the thread myself (the signal is sent from a callback) I don't know if I need to use it or not.
It is as simple as calling emit obj->quarterFrame(data); from the callback. If the connection type is default then this will be perfectly thread safe.
Though you should create a QByteArray from data to pass around as data will likely not be valid by the time the slots get called.
void callback(..., void* user){
//user is the standard void* in most callbacks passed as reinterpret_cast<void*>(this)
unsigned char* data = ...;
QByteArray bytes(data);
emit reinterpret_cast<PhMidiInput>(user)->quarterFrame(bytes);//calling the signal which will behave as you'd expect
}
In the last connect() call you pass this and MidiTest::onQuarterFrame as the receiver object and method. I bet this is not an instance of MidiTest, is it?
The problem here is that you're passing SLOT method from MidiTest, while the receiver object is this, which is not instance of MidiTest. Change receiver from this to some instance of MidiTest.
I'm surprised this code doesn't crash your application when running.

How to create multiple instances of the same plugin in Qt using QPluginLoader?

and first of all sorry for my english
I have developed driver for fiscal printer using Qt. Driver specifics is communication between PC and device via RS232. Driver stored in shared object (Qt plugin) and server performs its loading by QPluginLoader.
So, about my problem... When i use one device and one driver instance all works fine, but when i attach many devices (3 for example), only last loaded device work. I have done many code checks, read many dumps of logs data, there is no error with devices and ports naming, but if i adress first device with command - 2 left devices receive the same command (according to log entries) and only last loaded device performs commands executing.
For example: from my server i send PrintReceipt command to device 1, at log file of device 1 i see entry : PrintReceipt, at log file of device 2 i see entry : PrintReceipt, : PrintReceipt, and at log file of device 3 i see 3 same entries. So, as i see my problem - QPluginLoader creates one instance for driver for first loaded device, then, when i try to load driver to second device - QPluginLoader creates new instance and replace driver for first device by recently created and so on for every device. So, at least, i have only one instance of driver (plugin) for many devices and my app logic crashes.
My question is: How to create multiple instances of the same plugin in Qt using QPluginLoader? My driver interface and loading code listed below.
class IPrinterDriver : public QObject
{
public:
// Printer driver initialization
virtual bool Init(QextSerialPort* port, const QString& deviceID) = 0;
// Driver identify name
virtual QString GetName() = 0;
// Gets current device state
virtual DeviceStates GetState() = 0;
// Gets device info and state test
virtual QString DeviceInfo() = 0;
// Set print mode to specified
virtual bool SetPrintMode(PrintModes mode) = 0;
// Get current print mode
virtual PrintModes PrintMode() const = 0;
// Sets device password from configuration
virtual void SetDevicePasswords(int operatorPassword, int adminPassword) = 0;
// Sets non-fiscal permissoin to device
virtual void SetNonFiscalModePermission(bool allowed) = 0;
// Gets device operator password
virtual int GetOperatorPassword() const = 0;
// Gets device administrator password
virtual int GetAdministratorPassword() const = 0;
// Gets non-fiscal mode permission
virtual bool GetNonFiscalModePermission() const = 0;
// Payment transaction
virtual bool PaymentTransaction(PaymentItem& item) = 0;
// Query device for X-Report
virtual bool GetXReport() = 0;
// Encashment transaction (Z-Report)
virtual bool Encash(bool fromBuffer) = 0;
// Print transaction
virtual bool Print(QString& text) = 0;
// Performs fiscal sale at device and returns receipt data
virtual FiscalReceiptData FPSSale(int requestID, int amount) = 0;
// Gets last fiscal receipt data
virtual FiscalReceiptData GetLastReceiptData() = 0;
// Gets serial port assigned to device
virtual QextSerialPort* GetDevicePort() = 0;
signals:
// Emits when device logging needed
virtual void DeviceLoggingNeeded(LogEntry entry, const QString& deviceID) = 0;
};
Q_DECLARE_INTERFACE(IPrinterDriver, "InfSys.Devices.IPrinterDriver/1.0")
And driver loading method:
// Performs loading specified driver for device at specified port
IPrinterDriver* PrintSystem::LoadDriver(PortConfiguration portConfig, const QString& driverName, const QString& adminPassword, const QString& operPassword, const QString& deviceID)
{
IPrinterDriver* result = NULL;
// Prepare plugin loader
QDir driversDir(_driversPath);
QStringList filesMask;
filesMask << tr("libdriver.%1.*").arg(driverName);
driversDir.setNameFilters(filesMask);
QStringList driversFiles = driversDir.entryList(QDir::Files);
// Load plugin with specified driver
foreach(QString driverFile, driversFiles)
{
// Load current driver;
QString driverFileName = driversDir.absoluteFilePath(driverFile);
QPluginLoader driversLoader(driverFileName);
// Try to init driver
QObject *driverObject = driversLoader.instance();
if (driverObject)
{
result = qobject_cast<IPrinterDriver *>(driverObject);
if (result && (result->GetName() == driverName))
{
QextSerialPort* devicePort = ConfigureSerialPort(portConfig);
if (devicePort == NULL)
{
driversLoader.unload();
return NULL;
}
// Init device
result->SetDevicePasswords(operPassword.toInt(), adminPassword.toInt());
result->SetNonFiscalModePermission(false);
result->SetPrintMode(Fiscal);
connect(result, SIGNAL(DeviceLoggingNeeded(LogEntry,QString)), App->LoggingModule, SLOT(OnDeviceLoggingNeeded(LogEntry,QString)), Qt::QueuedConnection);
bool initResult = result->Init(devicePort, deviceID);
if (!initResult)
{
driversLoader.unload();
return NULL;
}
}
else
driversLoader.unload();
}
}
return result;
}
I've been looking at this problem for a while now on my own project. I believe that there isn't any way to do this directly - QPluginLoader is working as designed.
The most straightforward way around the issue that I've come up with so far is to make the primary plugin interface function a factory for the objects you actually want.
In the example below, the thing I actually WANT is multiple IPlaybackDataSource objects. So I create a factory interface that the plugin implements. The plugin then returns as many objects of the type I desire as I want.
IPlaybackDataSource.h:
#include <QSharedPointer>
class IPlaybackDataSource {
public:
virtual bool open()=0;
};
// This is the interface that the plugin will implement
class IPlaybackDSFactory {
public:
virtual QSharedPointer<IPlaybackDataSource> newDataSource()=0;
};
Q_DECLARE_INTERFACE(IPlaybackDSFactory,
"com.moberg.DeviceSimulators.IPlaybackDSFactory/1.0")
TranFile.h:
#include <QtGui>
#include "TranFile_global.h"
#include "IPlaybackDataSource.h"
class TRANFILESHARED_EXPORT TranFile : public QObject, public IPlaybackDSFactory
{
Q_OBJECT
Q_INTERFACES(IPlaybackDSFactory)
public:
TranFile();
~TranFile();
virtual QSharedPointer<IPlaybackDataSource> newDataSource();
};
TranFile.cpp:
#include "TranFile.h"
Q_EXPORT_PLUGIN2(IPlaybackDSFactory, TranFile );
#include <QtCore>
#include <QDebug>
TranFile::TranFile(): QObject(), IPlaybackDSFactory() {}
TranFile::~TranFile() {}
QSharedPointer<IPlaybackDataSource> TranFile::newDataSource() {
return QSharedPointer<IPlaybackDataSource>();
}

Can't use attachInterrupt in a library

I'm writing a simple library for an ultrasonic distance sensor and thought i'd try using interrupts.
However i can't set my functions in the attachCallback method properly.
I want HCSR04Interrupt::echoHigh() and HCSR04Interrupt::echoLow() called when the pin goes high and low respectively.
I've Googled this to no avail. The Ardiuno IDE says the following:
./Arduino/libraries/HCSR04/HCSR04Interrupt.cpp: In member function 'void HCSR04Interrupt::getDistance()':
./Arduino/libraries/HCSR04/HCSR04Interrupt.cpp:31: error: argument of type 'void (HCSR04Interrupt::)()' does not match 'void (*)()'
./Arduino/libraries/HCSR04/HCSR04Interrupt.cpp: In member function 'void HCSR04Interrupt::echoHigh()':
./Arduino/libraries/HCSR04/HCSR04Interrupt.cpp:47: error: argument of type 'void (HCSR04Interrupt::)()' does not match 'void (*)()'
Here is my header:
#ifndef _HCSR04Interrupt_
#define _HCSR04Interrupt_
#include "Arduino.h"
#define HCSR04_CM_FACTOR 58.0
#define HCSR04_IN_FACTOR 148.0
#define HCSR04_CM_MODE 0
#define HCSR04_IN_MODE 1
class HCSR04Interrupt {
public:
double distance;
HCSR04Interrupt(int trigger_pin, int echo_pin, void (*callback)());
void setUnits(int units);
void getDistance();
private:
int _trigger_pin;
int _echo_pin;
int _units;
unsigned long _micros_start;
void (*_callback)();
void initialize();
void echoHigh();
void echoLow();
};
#endif
And my implementation (not complete since i cant get past the attachInterrupt step):
#include "Arduino.h"
#include "HCSR04Interrupt.h"
HCSR04Interrupt::HCSR04Interrupt(int trigger_pin, int echo_pin, void (*callback)()) {
_trigger_pin = trigger_pin;
_echo_pin = echo_pin;
_callback = callback;
initialize();
}
void HCSR04Interrupt::setUnits(int units) {
_units = units;
}
void HCSR04Interrupt::initialize() {
pinMode(_trigger_pin, OUTPUT);
pinMode(_echo_pin, INPUT);
digitalWrite(_trigger_pin, LOW);
}
void HCSR04Interrupt::getDistance() {
//Listen for the RISING interrupt
attachInterrupt(_echo_pin - 2, echoHigh, RISING);
//The trigger pin should be pulled high,
digitalWrite(_trigger_pin, HIGH);
//for 10 us.
delayMicroseconds(20);
//Then reset it.
digitalWrite(_trigger_pin, LOW);
}
void HCSR04Interrupt::echoHigh() {
_micros_start = micros();
detachInterrupt(_echo_pin - 2);
attachInterrupt(_echo_pin - 2, echoLow, FALLING);
}
void HCSR04Interrupt::echoLow() {
detachInterrupt(_echo_pin - 2);
unsigned long us = micros() - _micros_start;
distance = us;
(*_callback)();
}
So the compiler (not the IDE) tells you exactly what's wrong:
argument of type 'void (HCSR04Interrupt::)()' does not match 'void (*)()
So, while attachInterrupt() takes a function pointer of type void (*)(), you're trying to pass it a non-static member function, which you can't. You can try making the member function static and casting:
static void echoHigh();
// ...
attachInterrupt(_echo_pin - 2, reinterpret_cast<void (*)()>(&echoHigh), RISING);
Arduino interrupt handlers can only be functions. You are trying make method of an object an interrupt handler. Hence the compiler complains.
To be more precise about it, object methods are like functions, but it is as if they take a "hidden" parameter, which specifies the object instance. Therefore, they actually have different type signatures from plain functions. This disallows one to pass a method pointer when what a function is looking for is a plain function pointer.
The solution is to move your echoHigh() and echoLow() out of the HCSR04Interrupt class, and make them plain functions.
As I stumbled upon this question and it hasn't had an accepted answer, I write what I found, which worked for me:
The interrupt has to be called by a global wrapper. This wrapper needs to call a handleInterupt function of the class. Therefore it has to know the class. This can be done by storing it in a global variable. If multiple instances of the class should be used, multiple such global variables have to be used. But as the interrupt pins are just a few you can write a global variable and function for every pin:
MyClass theInstance_pin3 = NULL;
MyClass theInstance_pin7 = NULL;
// Somewhere, fill in an initialized copy of MyClass,
// and set theInstance_pin3 or theInstance_pin7 to it
void ISR_3()
{
if (theInstance_pin3)
theInstance_pin3->handleInterrupt();
}
void ISR_7()
{
if (theInstance_pin7)
theInstance_pin7->handleInterrupt();
}
as a reference see: http://forum.arduino.cc/index.php?topic=41713.0
or http://forum.arduino.cc/index.php?topic=160101.0
I got around this by making a singleton base class which represents the hardware as a whole (which kinda makes sense in this situation anyway).
Any function pointers can then be passed to the sub-component class, and handled by the singleton, whose member variables and methods are all static.
Example headers (untested):
// Sub-component
class LampButton {
public:
LampButton(int pin, void(*pushHandler)());
}
// Sub-component
class LampLed {
public:
LampLed(int pin);
void toggle();
}
// Singleton represents the hardware in it's entirety
class Lamp {
public:
// Call this instead of a constructor
static void initialize(int buttonPin, int ledPin);
// Function implemented inline for clarity - don't do this
static void handleButtonPush() {
led.toggle();
}
private:
static LampButton button;
static LampLed led;
}

Resources