standalone w/ web server for testing

This commit is contained in:
proddy
2020-07-20 15:54:57 +02:00
parent cbd3585276
commit 63e75c2d63
27 changed files with 2847 additions and 88 deletions

View File

@@ -39,6 +39,7 @@ static int __output_level[256];
int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) { int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) {
memset(__output_pins, 0, sizeof(__output_pins)); memset(__output_pins, 0, sizeof(__output_pins));
memset(__output_level, 0, sizeof(__output_level)); memset(__output_level, 0, sizeof(__output_level));
setup(); setup();
// loop(); // run once // loop(); // run once

View File

@@ -26,12 +26,15 @@
#include <string> #include <string>
#include <algorithm> // for count_if #include <algorithm> // for count_if
#include <iostream>
#define IPAddress std::string #define IPAddress std::string
#define ICACHE_FLASH_ATTR #define ICACHE_FLASH_ATTR
#define ICACHE_RAM_ATTR #define ICACHE_RAM_ATTR
#define os_event_t void #define os_event_t void
#define byte uint8_t #define byte uint8_t
#define ltoa itoa #define ltoa itoa
#define boolean bool
#define LOW 0 #define LOW 0
#define HIGH 1 #define HIGH 1
@@ -54,14 +57,11 @@ int digitalRead(uint8_t pin);
&__c[0]; \ &__c[0]; \
})) }))
class __FlashStringHelper; class __FlashStringHelper;
#define FPSTR(string_literal) (reinterpret_cast<const __FlashStringHelper *>(string_literal)) #define FPSTR(string_literal) (reinterpret_cast<const __FlashStringHelper *>(string_literal))
#define F(string_literal) (FPSTR(PSTR(string_literal))) #define F(string_literal) (FPSTR(PSTR(string_literal)))
#define strlen_P strlen
#define strncpy_P strncpy
#define strcmp_P strcmp
int snprintf_P(char * str, size_t size, const char * format, ...); int snprintf_P(char * str, size_t size, const char * format, ...);
int vsnprintf_P(char * str, size_t size, const char * format, va_list ap); int vsnprintf_P(char * str, size_t size, const char * format, va_list ap);
@@ -136,6 +136,7 @@ class Stream : public Print {
virtual int peek() = 0; virtual int peek() = 0;
}; };
/*
class String { class String {
public: public:
String(const char * data = "") String(const char * data = "")
@@ -150,9 +151,18 @@ class String {
return data_.c_str(); return data_.c_str();
} }
bool equals(String comp) {
return (data_ == comp.c_str());
}
bool isEmpty() {
return data_.empty();
}
private: private:
std::string data_; std::string data_;
}; };
*/
class NativeConsole : public Stream { class NativeConsole : public Stream {
public: public:
@@ -215,7 +225,10 @@ void yield(void);
void setup(void); void setup(void);
void loop(void); void loop(void);
size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize); size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize);
size_t strlcat(char * dst, const char * src, size_t siz); size_t strlcat(char * dst, const char * src, size_t siz);
#include "WString.h"
#endif #endif

View File

@@ -0,0 +1,163 @@
#ifndef ASYNC_JSON_H_
#define ASYNC_JSON_H_
#include <ArduinoJson.h>
#include <ESPAsyncWebServer.h>
// #include <Print.h>
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
constexpr const char * JSON_MIMETYPE = "application/json";
/*
* Json Response
* */
class ChunkPrint : public Print {
private:
uint8_t * _destination;
size_t _to_skip;
size_t _to_write;
size_t _pos;
public:
ChunkPrint(uint8_t * destination, size_t from, size_t len)
: _destination(destination)
, _to_skip(from)
, _to_write(len)
, _pos{0} {
}
virtual ~ChunkPrint() {
}
size_t write(uint8_t c) {
if (_to_skip > 0) {
_to_skip--;
return 1;
} else if (_to_write > 0) {
_to_write--;
_destination[_pos++] = c;
return 1;
}
return 0;
}
size_t write(const uint8_t * buffer, size_t size) {
return this->Print::write(buffer, size);
}
};
class AsyncJsonResponse {
protected:
DynamicJsonDocument _jsonBuffer;
JsonVariant _root;
bool _isValid;
public:
AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
: _jsonBuffer(maxJsonBufferSize)
, _isValid{false} {
if (isArray)
_root = _jsonBuffer.createNestedArray();
else
_root = _jsonBuffer.createNestedObject();
}
~AsyncJsonResponse() {
}
JsonVariant & getRoot() {
return _root;
}
bool _sourceValid() const {
return _isValid;
}
size_t setLength() {
return 0;
}
size_t getSize() {
return _jsonBuffer.size();
}
size_t _fillBuffer(uint8_t * data, size_t len) {
// ChunkPrint dest(data, 0, len);
// serializeJson(_root, dest);
return len;
}
};
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant & json)> ArJsonRequestHandlerFunction;
class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
private:
protected:
const String _uri;
WebRequestMethodComposite _method;
ArJsonRequestHandlerFunction _onRequest;
size_t _contentLength;
size_t _maxContentLength;
public:
AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
: _uri(uri)
, _method(HTTP_POST | HTTP_PUT | HTTP_PATCH)
, _onRequest(onRequest)
, _maxContentLength(16384) {
}
void setMethod(WebRequestMethodComposite method) {
_method = method;
}
void setMaxContentLength(int maxContentLength) {
_maxContentLength = maxContentLength;
}
void onRequest(ArJsonRequestHandlerFunction fn) {
_onRequest = fn;
}
virtual bool canHandle(AsyncWebServerRequest * request) override final {
if (!_onRequest)
return false;
if (!(_method & request->method()))
return false;
request->addInterestingHeader("ANY");
return true;
}
virtual void handleRequest(AsyncWebServerRequest * request) override final {
if (_onRequest) {
if (request->_tempObject != NULL) {
DynamicJsonDocument jsonBuffer(1024);
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject));
if (!error) {
JsonVariant json = jsonBuffer.as<JsonVariant>();
_onRequest(request, json);
return;
}
}
request->send(_contentLength > _maxContentLength ? 413 : 400);
} else {
request->send(500);
}
}
virtual void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) override final {
}
virtual void handleBody(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total) override final {
if (_onRequest) {
_contentLength = total;
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
request->_tempObject = malloc(total);
}
if (request->_tempObject != NULL) {
memcpy((uint8_t *)(request->_tempObject) + index, data, len);
}
}
}
virtual bool isRequestHandlerTrivial() override final {
return _onRequest ? false : true;
}
};
#endif

View File

@@ -0,0 +1,143 @@
#ifndef ASYNCMQTTCLIENT_H_
#define ASYNCMQTTCLIENT_H_
#include "Arduino.h"
#include <functional>
enum class AsyncMqttClientDisconnectReason : int8_t {
TCP_DISCONNECTED = 0,
MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1,
MQTT_IDENTIFIER_REJECTED = 2,
MQTT_SERVER_UNAVAILABLE = 3,
MQTT_MALFORMED_CREDENTIALS = 4,
MQTT_NOT_AUTHORIZED = 5,
ESP8266_NOT_ENOUGH_SPACE = 6,
TLS_BAD_FINGERPRINT = 7
};
struct AsyncMqttClientMessageProperties {
uint8_t qos;
bool dup;
bool retain;
};
namespace AsyncMqttClientInternals {
// user callbacks
typedef std::function<void(bool sessionPresent)> OnConnectUserCallback;
typedef std::function<void(AsyncMqttClientDisconnectReason reason)> OnDisconnectUserCallback;
typedef std::function<void(uint16_t packetId, uint8_t qos)> OnSubscribeUserCallback;
typedef std::function<void(uint16_t packetId)> OnUnsubscribeUserCallback;
typedef std::function<void(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)> OnMessageUserCallback;
typedef std::function<void(uint16_t packetId)> OnPublishUserCallback;
};
class AsyncMqttClient {
public:
AsyncMqttClient();
~AsyncMqttClient();
AsyncMqttClient& setKeepAlive(uint16_t keepAlive);
AsyncMqttClient& setClientId(const char* clientId);
AsyncMqttClient& setCleanSession(bool cleanSession);
AsyncMqttClient& setMaxTopicLength(uint16_t maxTopicLength);
AsyncMqttClient& setCredentials(const char* username, const char* password = nullptr);
AsyncMqttClient& setWill(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0) { return *this; }
AsyncMqttClient& setServer(IPAddress ip, uint16_t port);
AsyncMqttClient& setServer(const char* host, uint16_t port);
AsyncMqttClient& onConnect(AsyncMqttClientInternals::OnConnectUserCallback callback) { return *this; }
AsyncMqttClient& onDisconnect(AsyncMqttClientInternals::OnDisconnectUserCallback callback) { return *this; }
AsyncMqttClient& onSubscribe(AsyncMqttClientInternals::OnSubscribeUserCallback callback) { return *this; }
AsyncMqttClient& onUnsubscribe(AsyncMqttClientInternals::OnUnsubscribeUserCallback callback) { return *this; }
AsyncMqttClient& onMessage(AsyncMqttClientInternals::OnMessageUserCallback callback) { return *this; }
AsyncMqttClient& onPublish(AsyncMqttClientInternals::OnPublishUserCallback callback) { return *this; }
bool connected() const { return false; }
void connect() {}
void disconnect(bool force = false) {}
uint16_t subscribe(const char* topic, uint8_t qos) {return 0;}
uint16_t unsubscribe(const char* topic) {return 0;}
uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0, bool dup = false, uint16_t message_id = 0) {return 0;}
const char* getClientId() {return "12";}
private:
// AsyncClient _client;
bool _connected;
bool _connectPacketNotEnoughSpace;
bool _disconnectOnPoll;
bool _tlsBadFingerprint;
uint32_t _lastClientActivity;
uint32_t _lastServerActivity;
uint32_t _lastPingRequestTime;
char _generatedClientId[18 + 1]; // esp8266-abc123 and esp32-abcdef123456
IPAddress _ip;
const char* _host;
bool _useIp;
uint16_t _port;
uint16_t _keepAlive;
bool _cleanSession;
const char* _clientId;
const char* _username;
const char* _password;
const char* _willTopic;
const char* _willPayload;
uint16_t _willPayloadLength;
uint8_t _willQos;
bool _willRetain;
// std::vector<AsyncMqttClientInternals::OnConnectUserCallback> _onConnectUserCallbacks;
// std::vector<AsyncMqttClientInternals::OnDisconnectUserCallback> _onDisconnectUserCallbacks;
// std::vector<AsyncMqttClientInternals::OnSubscribeUserCallback> _onSubscribeUserCallbacks;
// std::vector<AsyncMqttClientInternals::OnUnsubscribeUserCallback> _onUnsubscribeUserCallbacks;
// std::vector<AsyncMqttClientInternals::OnMessageUserCallback> _onMessageUserCallbacks;
// std::vector<AsyncMqttClientInternals::OnPublishUserCallback> _onPublishUserCallbacks;
// AsyncMqttClientInternals::ParsingInformation _parsingInformation;
// AsyncMqttClientInternals::Packet* _currentParsedPacket;
// uint8_t _remainingLengthBufferPosition;
// char _remainingLengthBuffer[4];
// uint16_t _nextPacketId;
// std::vector<AsyncMqttClientInternals::PendingPubRel> _pendingPubRels;
// std::vector<AsyncMqttClientInternals::PendingAck> _toSendAcks;
// void _clear();
// void _freeCurrentParsedPacket();
// TCP
// void _onConnect(AsyncClient* client);
// void _onDisconnect(AsyncClient* client);
// static void _onError(AsyncClient* client, int8_t error);
// void _onTimeout(AsyncClient* client, uint32_t time);
// static void _onAck(AsyncClient* client, size_t len, uint32_t time);
// void _onData(AsyncClient* client, char* data, size_t len);
// void _onPoll(AsyncClient* client);
// // MQTT
// void _onPingResp();
// void _onConnAck(bool sessionPresent, uint8_t connectReturnCode);
// void _onSubAck(uint16_t packetId, char status);
// void _onUnsubAck(uint16_t packetId);
// void _onMessage(char* topic, char* payload, uint8_t qos, bool dup, bool retain, size_t len, size_t index, size_t total, uint16_t packetId);
// void _onPublish(uint16_t packetId, uint8_t qos);
// void _onPubRel(uint16_t packetId);
// void _onPubAck(uint16_t packetId);
// void _onPubRec(uint16_t packetId);
// void _onPubComp(uint16_t packetId);
// bool _sendPing();
// void _sendAcks();
// bool _sendDisconnect();
// uint16_t _getNextPacketId();
};
#endif

212
lib_standalone/AsyncTCP.h Normal file
View File

@@ -0,0 +1,212 @@
/*
Asynchronous TCP library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ASYNCTCP_H_
#define ASYNCTCP_H_
#include "Arduino.h"
#include <functional>
//If core is not defined, then we are running in Arduino or PIO
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
#endif
class AsyncClient;
#define ASYNC_MAX_ACK_TIME 5000
#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given)
#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react.
typedef std::function<void(void*, AsyncClient*)> AcConnectHandler;
typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)> AcAckHandler;
typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler;
typedef std::function<void(void*, AsyncClient*, void *data, size_t len)> AcDataHandler;
typedef std::function<void(void*, AsyncClient*, struct pbuf *pb)> AcPacketHandler;
typedef std::function<void(void*, AsyncClient*, uint32_t time)> AcTimeoutHandler;
struct tcp_pcb;
struct ip_addr;
class AsyncClient {
public:
AsyncClient(tcp_pcb* pcb = 0);
~AsyncClient();
AsyncClient & operator=(const AsyncClient &other);
AsyncClient & operator+=(const AsyncClient &other);
bool operator==(const AsyncClient &other);
bool operator!=(const AsyncClient &other) {
return !(*this == other);
}
bool connect(IPAddress ip, uint16_t port);
bool connect(const char* host, uint16_t port);
void close(bool now = false);
void stop();
int8_t abort();
bool free();
bool canSend();//ack is not pending
size_t space();//space available in the TCP window
size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending
bool send();//send all data added with the method above
//write equals add()+send()
size_t write(const char* data);
size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true
uint8_t state();
bool connecting();
bool connected();
bool disconnecting();
bool disconnected();
bool freeable();//disconnected or disconnecting
uint16_t getMss();
uint32_t getRxTimeout();
void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds
uint32_t getAckTimeout();
void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds
void setNoDelay(bool nodelay);
bool getNoDelay();
uint32_t getRemoteAddress();
uint16_t getRemotePort();
uint32_t getLocalAddress();
uint16_t getLocalPort();
//compatibility
IPAddress remoteIP();
uint16_t remotePort();
IPAddress localIP();
uint16_t localPort();
void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect
void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected
void onAck(AcAckHandler cb, void* arg = 0); //ack received
void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error
void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used)
void onPacket(AcPacketHandler cb, void* arg = 0); //data received
void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout
void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected
void ackPacket(struct pbuf * pb);//ack pbuf from onPacket
size_t ack(size_t len); //ack data that you have not acked using the method below
void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData
const char * errorToString(int8_t error);
const char * stateToString();
//Do not use any of the functions below!
static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb);
static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err);
static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err);
static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err);
static void _s_error(void *arg, int8_t err);
static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len);
static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);
int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err);
tcp_pcb * pcb(){ return _pcb; }
protected:
tcp_pcb* _pcb;
int8_t _closed_slot;
AcConnectHandler _connect_cb;
void* _connect_cb_arg;
AcConnectHandler _discard_cb;
void* _discard_cb_arg;
AcAckHandler _sent_cb;
void* _sent_cb_arg;
AcErrorHandler _error_cb;
void* _error_cb_arg;
AcDataHandler _recv_cb;
void* _recv_cb_arg;
AcPacketHandler _pb_cb;
void* _pb_cb_arg;
AcTimeoutHandler _timeout_cb;
void* _timeout_cb_arg;
AcConnectHandler _poll_cb;
void* _poll_cb_arg;
bool _pcb_busy;
uint32_t _pcb_sent_at;
bool _ack_pcb;
uint32_t _rx_ack_len;
uint32_t _rx_last_packet;
uint32_t _rx_since_timeout;
uint32_t _ack_timeout;
uint16_t _connect_port;
int8_t _close();
int8_t _connected(void* pcb, int8_t err);
void _error(int8_t err);
int8_t _poll(tcp_pcb* pcb);
int8_t _sent(tcp_pcb* pcb, uint16_t len);
int8_t _fin(tcp_pcb* pcb, int8_t err);
int8_t _lwip_fin(tcp_pcb* pcb, int8_t err);
void _dns_found(struct ip_addr *ipaddr);
public:
AsyncClient* prev;
AsyncClient* next;
};
class AsyncServer {
public:
AsyncServer(IPAddress addr, uint16_t port) : _port(port), _addr("poep") {};
AsyncServer(uint16_t port) : _port(port) {};
~AsyncServer() {};
void onClient(AcConnectHandler cb, void* arg);
void begin();
void end();
void setNoDelay(bool nodelay);
bool getNoDelay();
uint8_t status();
//Do not use any of the functions below!
static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err);
static int8_t _s_accepted(void *arg, AsyncClient* client);
protected:
uint16_t _port;
IPAddress _addr;
bool _noDelay;
tcp_pcb* _pcb;
AcConnectHandler _connect_cb;
void* _connect_cb_arg;
int8_t _accept(tcp_pcb* newpcb, int8_t err);
int8_t _accepted(AsyncClient* client);
};
#endif /* ASYNCTCP_H_ */

View File

@@ -2,13 +2,99 @@
#define ESP8266React_h #define ESP8266React_h
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h>
#include <AsyncJson.h>
#include <SecurityManager.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <AsyncMqttClient.h>
#include <StatefulService.h>
class DummySettings {
public:
uint8_t tx_mode;
uint8_t ems_bus_id;
bool system_heartbeat;
int8_t syslog_level; // uuid::log::Level
uint32_t syslog_mark_interval;
String syslog_host;
uint8_t master_thermostat;
bool shower_timer;
bool shower_alert;
uint16_t publish_time; // seconds
uint8_t mqtt_format; // 1=single, 2=nested, 3=ha, 4=custom
uint8_t mqtt_qos;
String hostname;
String jwtSecret;
String ssid;
String password;
static void read(DummySettings & settings, JsonObject & root){};
static void read(DummySettings & settings){};
static StateUpdateResult update(JsonObject & root, DummySettings & settings) {
return StateUpdateResult::CHANGED;
}
};
class DummySettingsService : public StatefulService<DummySettings> {
public:
DummySettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager){};
void begin();
void loop();
private:
};
#define WiFiSettings DummySettings
#define SecuritySettings DummySettings
#define MqttSettings DummySettings
class ESP8266React { class ESP8266React {
public: public:
ESP8266React(void* server, void* fs); ESP8266React(AsyncWebServer * server, FS * fs)
: _settings(server, fs, nullptr){};
void begin() {}; void begin(){};
void loop() {}; void loop(){};
SecurityManager * getSecurityManager() {
return nullptr;
}
AsyncMqttClient * getMqttClient() {
return _mqttClient;
}
StatefulService<DummySettings> * getWiFiSettingsService() {
return &_settings;
}
StatefulService<DummySettings> * getSecuritySettingsService() {
return &_settings;
}
StatefulService<DummySettings> * getMqttSettingsService() {
return &_settings;
}
private:
DummySettingsService _settings;
AsyncMqttClient * _mqttClient;
};
class EMSESPSettingsService {
public:
EMSESPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void begin();
private:
// HttpEndpoint<EMSESPSettings> _httpEndpoint;
// FSPersistence<EMSESPSettings> _fsPersistence;
}; };
#endif #endif

View File

@@ -0,0 +1,521 @@
#ifndef _ESPAsyncWebServer_H_
#define _ESPAsyncWebServer_H_
#include "Arduino.h"
#include <functional>
#include <AsyncTCP.h>
#include <ArduinoJson.h>
#define DEBUGF(...) //Serial.printf(__VA_ARGS__)
class AsyncWebServer;
class AsyncWebServerRequest;
class AsyncWebServerResponse;
class AsyncWebHeader;
class AsyncWebParameter;
class AsyncWebRewrite;
class AsyncWebHandler;
class AsyncStaticWebHandler;
class AsyncCallbackWebHandler;
class AsyncResponseStream;
class AsyncJsonResponse;
typedef enum {
HTTP_GET = 0b00000001,
HTTP_POST = 0b00000010,
HTTP_DELETE = 0b00000100,
HTTP_PUT = 0b00001000,
HTTP_PATCH = 0b00010000,
HTTP_HEAD = 0b00100000,
HTTP_OPTIONS = 0b01000000,
HTTP_ANY = 0b01111111,
} WebRequestMethod;
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
typedef uint8_t WebRequestMethodComposite;
typedef std::function<void(void)> ArDisconnectHandler;
/*
* PARAMETER :: Chainable object to hold GET/POST and FILE parameters
* */
class AsyncWebParameter {
private:
String _name;
String _value;
size_t _size;
bool _isForm;
bool _isFile;
public:
AsyncWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0)
: _name(name)
, _value(value)
, _size(size)
, _isForm(form)
, _isFile(file) {
}
const String & name() const {
return _name;
}
const String & value() const {
return _value;
}
size_t size() const {
return _size;
}
bool isPost() const {
return _isForm;
}
bool isFile() const {
return _isFile;
}
};
/*
* HEADER :: Chainable object to hold the headers
* */
class AsyncWebHeader {
private:
String _name;
String _value;
public:
AsyncWebHeader(const String & name, const String & value)
: _name(name)
, _value(value) {
}
AsyncWebHeader(const String & data)
: _name()
, _value() {
}
~AsyncWebHeader() {
}
const String & name() const {
return _name;
}
const String & value() const {
return _value;
}
String toString() const {
return _value;
}
};
/*
* REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
* */
typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;
typedef std::function<size_t(uint8_t *, size_t, size_t)> AwsResponseFiller;
typedef std::function<String(const String &)> AwsTemplateProcessor;
class AsyncWebServerRequest {
friend class AsyncWebServer;
friend class AsyncCallbackWebHandler;
private:
AsyncClient * _client;
AsyncWebServer * _server;
AsyncWebHandler * _handler;
AsyncWebServerResponse * _response;
ArDisconnectHandler _onDisconnectfn;
String _temp;
uint8_t _parseState;
uint8_t _version;
WebRequestMethodComposite _method;
String _url;
String _host;
String _contentType;
String _boundary;
String _authorization;
RequestedConnectionType _reqconntype;
void _removeNotInterestingHeaders();
bool _isDigest;
bool _isMultipart;
bool _isPlainPost;
bool _expectingContinue;
size_t _contentLength;
size_t _parsedLength;
uint8_t _multiParseState;
uint8_t _boundaryPosition;
size_t _itemStartIndex;
size_t _itemSize;
String _itemName;
String _itemFilename;
String _itemType;
String _itemValue;
uint8_t * _itemBuffer;
size_t _itemBufferIndex;
bool _itemIsFile;
void _onPoll();
void _onAck(size_t len, uint32_t time);
void _onError(int8_t error);
void _onTimeout(uint32_t time);
void _onDisconnect(){};
void _onData(void * buf, size_t len);
void _addParam(AsyncWebParameter *);
void _addPathParam(const char * param);
bool _parseReqHead();
bool _parseReqHeader();
void _parseLine();
void _parsePlainPostChar(uint8_t data);
void _parseMultipartPostByte(uint8_t data, bool last);
void _addGetParams(const String & params);
void _handleUploadStart();
void _handleUploadByte(uint8_t data, bool last);
void _handleUploadEnd();
public:
void * _tempObject;
AsyncWebServerRequest(AsyncWebServer *, AsyncClient *);
~AsyncWebServerRequest();
AsyncClient * client() {
return _client;
}
uint8_t version() const {
return _version;
}
WebRequestMethodComposite method() const {
return _method;
}
const String & url() const {
return _url;
}
const String & host() const {
return _host;
}
const String & contentType() const {
return _contentType;
}
size_t contentLength() const {
return _contentLength;
}
bool multipart() const {
return _isMultipart;
}
const char * methodToString() const;
const char * requestedConnTypeToString() const;
RequestedConnectionType requestedConnType() const {
return _reqconntype;
}
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
void onDisconnect(ArDisconnectHandler fn){};
//hash is the string representation of:
// base64(user:pass) for basic or
// user:realm:md5(user:realm:pass) for digest
bool authenticate(const char * hash);
bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
void requestAuthentication(const char * realm = NULL, bool isDigest = true);
void setHandler(AsyncWebHandler * handler) {
_handler = handler;
}
void addInterestingHeader(const String & name);
void redirect(const String & url);
void send(AsyncWebServerResponse * response){};
void send(AsyncJsonResponse * response){};
void send(int code, const String & contentType = String(), const String & content = String()){};
void send(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr);
void send(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
void sendChunked(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
void send_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr);
void send_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse * beginResponse(int code, const String & contentType = String(), const String & content = String());
AsyncWebServerResponse * beginResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse * beginResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncWebServerResponse * beginChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncResponseStream * beginResponseStream(const String & contentType, size_t bufferSize = 1460);
AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
size_t headers() const; // get header count
bool hasHeader(const String & name) const; // check if header exists
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
AsyncWebHeader * getHeader(const String & name) const;
AsyncWebHeader * getHeader(const __FlashStringHelper * data) const;
AsyncWebHeader * getHeader(size_t num) const;
size_t params() const; // get arguments count
bool hasParam(const String & name, bool post = false, bool file = false) const;
bool hasParam(const __FlashStringHelper * data, bool post = false, bool file = false) const;
AsyncWebParameter * getParam(const String & name, bool post = false, bool file = false) const;
AsyncWebParameter * getParam(const __FlashStringHelper * data, bool post, bool file) const;
AsyncWebParameter * getParam(size_t num) const;
size_t args() const {
return params();
} // get arguments count
const String & arg(const String & name) const; // get request argument value by name
const String & arg(const __FlashStringHelper * data) const; // get request argument value by F(name)
const String & arg(size_t i) const; // get request argument value by number
const String & argName(size_t i) const; // get request argument name by number
bool hasArg(const char * name) const; // check if argument exists
bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists
const String & pathArg(size_t i) const;
const String & header(const char * name) const; // get request header value by name
const String & header(const __FlashStringHelper * data) const; // get request header value by F(name)
const String & header(size_t i) const; // get request header value by number
const String & headerName(size_t i) const; // get request header name by number
String urlDecode(const String & text) const;
};
/*
* FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
* */
typedef std::function<bool(AsyncWebServerRequest * request)> ArRequestFilterFunction;
bool ON_STA_FILTER(AsyncWebServerRequest * request);
bool ON_AP_FILTER(AsyncWebServerRequest * request);
/*
* REWRITE :: One instance can be handle any Request (done by the Server)
* */
class AsyncWebRewrite {
protected:
String _from;
String _toUrl;
String _params;
ArRequestFilterFunction _filter;
public:
AsyncWebRewrite(const char * from, const char * to)
: _from(from)
, _toUrl(to)
, _params(String())
, _filter(NULL) {
}
virtual ~AsyncWebRewrite() {
}
AsyncWebRewrite & setFilter(ArRequestFilterFunction fn) {
_filter = fn;
return *this;
}
bool filter(AsyncWebServerRequest * request) const {
return _filter == NULL || _filter(request);
}
const String & from(void) const {
return _from;
}
const String & toUrl(void) const {
return _toUrl;
}
const String & params(void) const {
return _params;
}
};
/*
* HANDLER :: One instance can be attached to any Request (done by the Server)
* */
class AsyncWebHandler {
protected:
ArRequestFilterFunction _filter;
String _username;
String _password;
public:
AsyncWebHandler()
: _username("")
, _password("") {
}
AsyncWebHandler & setFilter(ArRequestFilterFunction fn) {
_filter = fn;
return *this;
}
AsyncWebHandler & setAuthentication(const char * username, const char * password) {
_username = String(username);
_password = String(password);
return *this;
};
bool filter(AsyncWebServerRequest * request) {
return _filter == NULL || _filter(request);
}
virtual ~AsyncWebHandler() {
}
virtual bool canHandle(AsyncWebServerRequest * request __attribute__((unused))) {
return false;
}
virtual void handleRequest(AsyncWebServerRequest * request __attribute__((unused))) {
}
virtual void handleUpload(AsyncWebServerRequest * request __attribute__((unused)),
const String & filename __attribute__((unused)),
size_t index __attribute__((unused)),
uint8_t * data __attribute__((unused)),
size_t len __attribute__((unused)),
bool final __attribute__((unused))) {
}
virtual void handleBody(AsyncWebServerRequest * request __attribute__((unused)),
uint8_t * data __attribute__((unused)),
size_t len __attribute__((unused)),
size_t index __attribute__((unused)),
size_t total __attribute__((unused))) {
}
virtual bool isRequestHandlerTrivial() {
return true;
}
};
/*
* RESPONSE :: One instance is created for each Request (attached by the Handler)
* */
typedef enum { RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED } WebResponseState;
class AsyncWebServerResponse {
protected:
int _code;
String _contentType;
size_t _contentLength;
bool _sendContentLength;
bool _chunked;
size_t _headLength;
size_t _sentLength;
size_t _ackedLength;
size_t _writtenLength;
WebResponseState _state;
const char * _responseCodeToString(int code);
public:
AsyncWebServerResponse();
virtual ~AsyncWebServerResponse();
virtual void setCode(int code);
virtual void setContentLength(size_t len);
virtual void setContentType(const String & type);
virtual void addHeader(const String & name, const String & value);
virtual String _assembleHead(uint8_t version);
virtual bool _started() const;
virtual bool _finished() const;
virtual bool _failed() const;
virtual bool _sourceValid() const;
virtual void _respond(AsyncWebServerRequest * request);
virtual size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time);
};
/*
* SERVER :: One instance
* */
typedef std::function<void(AsyncWebServerRequest * request)> ArRequestHandlerFunction;
typedef std::function<void(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final)> ArUploadHandlerFunction;
typedef std::function<void(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
class AsyncWebServer {
protected:
AsyncServer _server;
AsyncCallbackWebHandler * _catchAllHandler;
public:
// proddy
AsyncWebServer(uint16_t port)
: _server(port){};
// , _rewrites(LinkedList<AsyncWebRewrite*>([](AsyncWebRewrite* r){ delete r; }))
// , _handlers(LinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; }))
~AsyncWebServer(){};
void begin(){};
void end();
#if ASYNC_TCP_SSL_ENABLED
void onSslFileRequest(AcSSlFileHandler cb, void * arg);
void beginSecure(const char * cert, const char * private_key_file, const char * password);
#endif
AsyncWebRewrite & addRewrite(AsyncWebRewrite * rewrite);
bool removeRewrite(AsyncWebRewrite * rewrite);
AsyncWebRewrite & rewrite(const char * from, const char * to);
AsyncWebHandler & addHandler(AsyncWebHandler * handler);
bool removeHandler(AsyncWebHandler * handler);
/*
AsyncCallbackWebHandler & on(const char * uri, ArRequestHandlerFunction onRequest) {
AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
return *handler;
};
AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) {
AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
return handler;
};
AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) {
AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
return *handler;
};
AsyncCallbackWebHandler &
on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) {
AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
return *handler;
};
*/
void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){};
void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned
void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)
void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
void _handleDisconnect(AsyncWebServerRequest * request);
void _attachHandler(AsyncWebServerRequest * request);
void _rewriteRequest(AsyncWebServerRequest * request);
};
// class DefaultHeaders {
// headers_t _headers;
// DefaultHeaders()
// public:
// void addHeader(const String& name, const String& value){
// _headers.add(new AsyncWebHeader(name, value));
// }
// DefaultHeaders(DefaultHeaders const &) = delete;
// DefaultHeaders &operator=(DefaultHeaders const &) = delete;
// static DefaultHeaders &Instance() {
// static DefaultHeaders instance;
// return instance;
// }
// };
// #include "WebResponseImpl.h"
// #include "WebHandlerImpl.h"
// #include "AsyncWebSocket.h"
// #include "AsyncEventSource.h"
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant & json)> ArJsonRequestHandlerFunction;
#endif /* _AsyncWebServer_H_ */

10
lib_standalone/FS.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef FS_h
#define FS_h
class FS {
bool open() {
return true;
};
};
#endif

View File

@@ -0,0 +1,102 @@
#ifndef FSPersistence_h
#define FSPersistence_h
#include <StatefulService.h>
#include <FS.h>
template <class T>
class FSPersistence {
public:
FSPersistence(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService,
FS * fs,
char const * filePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)
, _fs(fs)
, _filePath(filePath)
, _bufferSize(bufferSize)
, _updateHandlerId(0) {
enableUpdateHandler();
}
void readFromFS() {
/*
File settingsFile = _fs->open(_filePath, "r");
if (settingsFile) {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
DeserializationError error = deserializeJson(jsonDocument, settingsFile);
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
JsonObject jsonObject = jsonDocument.as<JsonObject>();
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
settingsFile.close();
return;
}
settingsFile.close();
}
*/
// If we reach here we have not been successful in loading the config,
// hard-coded emergency defaults are now applied.
applyDefaults();
}
bool writeToFS() {
// create and populate a new json object
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
JsonObject jsonObject = jsonDocument.to<JsonObject>();
_statefulService->read(jsonObject, _stateReader);
// serialize it to filesystem
/*
File settingsFile = _fs->open(_filePath, "w");
// failed to open file, return false
if (!settingsFile) {
return false;
}
// serialize the data to the file
serializeJson(jsonDocument, settingsFile);
settingsFile.close();
*/
return true;
}
void disableUpdateHandler() {
if (_updateHandlerId) {
_statefulService->removeUpdateHandler(_updateHandlerId);
_updateHandlerId = 0;
}
}
void enableUpdateHandler() {
if (!_updateHandlerId) {
_updateHandlerId = _statefulService->addUpdateHandler([&](const String & originId) { writeToFS(); });
}
}
private:
JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService;
FS * _fs;
char const * _filePath;
size_t _bufferSize;
update_handler_id_t _updateHandlerId;
protected:
// We assume the updater supplies sensible defaults if an empty object
// is supplied, this virtual function allows that to be changed.
virtual void applyDefaults() {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
JsonObject jsonObject = jsonDocument.as<JsonObject>();
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
}
};
#endif // end FSPersistence

37
lib_standalone/Features.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef Features_h
#define Features_h
#define FT_ENABLED(feature) feature
// project feature off by default
#ifndef FT_PROJECT
#define FT_PROJECT 0
#endif
// security feature on by default
#ifndef FT_SECURITY
#define FT_SECURITY 0
#endif
// mqtt feature on by default
#ifndef FT_MQTT
#define FT_MQTT 0
#endif
// ntp feature on by default
#ifndef FT_NTP
#define FT_NTP 0
#endif
// mqtt feature on by default
#ifndef FT_OTA
#define FT_OTA 0
#endif
// upload firmware feature off by default
#ifndef FT_UPLOAD_FIRMWARE
#define FT_UPLOAD_FIRMWARE 0
#endif
#endif

View File

@@ -0,0 +1,173 @@
#ifndef HttpEndpoint_h
#define HttpEndpoint_h
#include <functional>
#include <AsyncJson.h>
#include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include <StatefulService.h>
#define HTTP_ENDPOINT_ORIGIN_ID "http"
template <class T>
class HttpGetEndpoint {
public:
HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
SecurityManager* securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) {
/*
server->on(servicePath.c_str(),
HTTP_GET,
securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1),
authenticationPredicate));
*/
}
HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) {
/*
server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1));
*/
}
protected:
JsonStateReader<T> _stateReader;
StatefulService<T>* _statefulService;
size_t _bufferSize;
void fetchSettings(AsyncWebServerRequest* request) {
// AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize);
// JsonObject jsonObject = response->getRoot().to<JsonObject>();
// _statefulService->read(jsonObject, _stateReader);
// response->setLength();
// request->send(response);
}
};
template <class T>
class HttpPostEndpoint {
public:
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
SecurityManager* securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader),
_stateUpdater(stateUpdater),
_statefulService(statefulService),
/*
_updateHandler(
servicePath,
securityManager->wrapCallback(
std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2),
authenticationPredicate),
bufferSize),
*/
_bufferSize(bufferSize) {
//_updateHandler.setMethod(HTTP_POST);
// server->addHandler(&_updateHandler);
}
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader),
_stateUpdater(stateUpdater),
_statefulService(statefulService),
/*
_updateHandler(servicePath,
std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2),
bufferSize),
*/
_bufferSize(bufferSize) {
// _updateHandler.setMethod(HTTP_POST);
// server->addHandler(&_updateHandler);
}
protected:
JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater;
StatefulService<T>* _statefulService;
//AsyncCallbackJsonWebHandler _updateHandler;
size_t _bufferSize;
void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) {
if (!json.is<JsonObject>()) {
// request->send(400);
return;
}
JsonObject jsonObject = json.as<JsonObject>();
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
if (outcome == StateUpdateResult::ERROR) {
// request->send(400);
return;
}
if (outcome == StateUpdateResult::CHANGED) {
// request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); });
}
// AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize);
// jsonObject = response->getRoot().to<JsonObject>();
// _statefulService->read(jsonObject, _stateReader);
// response->setLength();
// request->send(response);
}
};
template <class T>
class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> {
public:
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
SecurityManager* securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
HttpGetEndpoint<T>(stateReader,
statefulService,
server,
servicePath,
securityManager,
authenticationPredicate,
bufferSize),
HttpPostEndpoint<T>(stateReader,
stateUpdater,
statefulService,
server,
servicePath,
securityManager,
authenticationPredicate,
bufferSize) {
}
HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, bufferSize),
HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) {
}
};
#endif // end HttpEndpoint

View File

@@ -0,0 +1,102 @@
#ifndef SecurityManager_h
#define SecurityManager_h
#include <Arduino.h>
#include <Features.h>
#include <ESPAsyncWebServer.h>
// #include <ESPUtils.h>
#include <AsyncJson.h>
#include <list>
#ifndef FACTORY_JWT_SECRET
#define FACTORY_JWT_SECRET ESPUtils::defaultDeviceValue()
#endif
#define ACCESS_TOKEN_PARAMATER "access_token"
#define AUTHORIZATION_HEADER "Authorization"
#define AUTHORIZATION_HEADER_PREFIX "Bearer "
#define AUTHORIZATION_HEADER_PREFIX_LEN 7
#define MAX_JWT_SIZE 128
class User {
public:
String username;
String password;
bool admin;
public:
User(String username, String password, bool admin) : username(username), password(password), admin(admin) {
}
};
class Authentication {
public:
User* user;
boolean authenticated;
public:
Authentication(User& user) : user(new User(user)), authenticated(true) {
}
Authentication() : user(nullptr), authenticated(false) {
}
~Authentication() {
delete (user);
}
};
typedef std::function<boolean(Authentication& authentication)> AuthenticationPredicate;
class AuthenticationPredicates {
public:
static bool NONE_REQUIRED(Authentication& authentication) {
return true;
};
static bool IS_AUTHENTICATED(Authentication& authentication) {
return authentication.authenticated;
};
static bool IS_ADMIN(Authentication& authentication) {
return authentication.authenticated && authentication.user->admin;
};
};
class SecurityManager {
public:
#if FT_ENABLED(FT_SECURITY)
/*
* Authenticate, returning the user if found
*/
virtual Authentication authenticate(const String& username, const String& password) = 0;
/*
* Generate a JWT for the user provided
*/
virtual String generateJWT(User* user) = 0;
#endif
/*
* Check the request header for the Authorization token
*/
virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0;
/**
* Filter a request with the provided predicate, only returning true if the predicate matches.
*/
virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0;
/**
* Wrap the provided request to provide validation against an AuthenticationPredicate.
*/
virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest,
AuthenticationPredicate predicate) = 0;
/**
* Wrap the provided json request callback to provide validation against an AuthenticationPredicate.
*/
virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest,
AuthenticationPredicate predicate) = 0;
};
#endif // end SecurityManager_h

View File

@@ -0,0 +1,3 @@
#include <StatefulService.h>
update_handler_id_t StateUpdateHandlerInfo::currentUpdatedHandlerId = 0;

View File

@@ -0,0 +1,144 @@
#ifndef StatefulService_h
#define StatefulService_h
#include <Arduino.h>
#include <ArduinoJson.h>
#include <list>
#include <functional>
#ifndef DEFAULT_BUFFER_SIZE
#define DEFAULT_BUFFER_SIZE 1024
#endif
enum class StateUpdateResult {
CHANGED = 0, // The update changed the state and propagation should take place if required
UNCHANGED, // The state was unchanged, propagation should not take place
ERROR // There was a problem updating the state, propagation should not take place
};
template <typename T>
using JsonStateUpdater = std::function<StateUpdateResult(JsonObject& root, T& settings)>;
template <typename T>
using JsonStateReader = std::function<void(T& settings, JsonObject& root)>;
typedef size_t update_handler_id_t;
typedef std::function<void(const String& originId)> StateUpdateCallback;
typedef struct StateUpdateHandlerInfo {
static update_handler_id_t currentUpdatedHandlerId;
update_handler_id_t _id;
StateUpdateCallback _cb;
bool _allowRemove;
StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) :
_id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){};
} StateUpdateHandlerInfo_t;
template <class T>
class StatefulService {
public:
template <typename... Args>
#ifdef ESP32
StatefulService(Args&&... args) :
_state(std::forward<Args>(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) {
}
#else
StatefulService(Args&&... args) : _state(std::forward<Args>(args)...) {
}
#endif
update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) {
if (!cb) {
return 0;
}
StateUpdateHandlerInfo_t updateHandler(cb, allowRemove);
_updateHandlers.push_back(updateHandler);
return updateHandler._id;
}
void removeUpdateHandler(update_handler_id_t id) {
for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) {
if ((*i)._allowRemove && (*i)._id == id) {
i = _updateHandlers.erase(i);
} else {
++i;
}
}
}
StateUpdateResult update(std::function<StateUpdateResult(T&)> stateUpdater, const String& originId) {
beginTransaction();
StateUpdateResult result = stateUpdater(_state);
endTransaction();
if (result == StateUpdateResult::CHANGED) {
callUpdateHandlers(originId);
}
return result;
}
StateUpdateResult updateWithoutPropagation(std::function<StateUpdateResult(T&)> stateUpdater) {
beginTransaction();
StateUpdateResult result = stateUpdater(_state);
endTransaction();
return result;
}
StateUpdateResult update(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater, const String& originId) {
beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction();
if (result == StateUpdateResult::CHANGED) {
callUpdateHandlers(originId);
}
return result;
}
StateUpdateResult updateWithoutPropagation(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater) {
beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction();
return result;
}
void read(std::function<void(T&)> stateReader) {
beginTransaction();
stateReader(_state);
endTransaction();
}
void read(JsonObject& jsonObject, JsonStateReader<T> stateReader) {
beginTransaction();
stateReader(_state, jsonObject);
endTransaction();
}
void callUpdateHandlers(const String& originId) {
for (const StateUpdateHandlerInfo_t& updateHandler : _updateHandlers) {
updateHandler._cb(originId);
}
}
protected:
T _state;
inline void beginTransaction() {
#ifdef ESP32
xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY);
#endif
}
inline void endTransaction() {
#ifdef ESP32
xSemaphoreGiveRecursive(_accessMutex);
#endif
}
private:
#ifdef ESP32
SemaphoreHandle_t _accessMutex;
#endif
std::list<StateUpdateHandlerInfo_t> _updateHandlers;
};
#endif // end StatefulService_h

863
lib_standalone/WString.cpp Normal file
View File

@@ -0,0 +1,863 @@
/*
WString.cpp - String library for Wiring & Arduino
...mostly rewritten by Paul Stoffregen...
Copyright (c) 2009-10 Hernando Barragan. All rights reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "WString.h"
/*********************************************/
/* Constructors */
/*********************************************/
char * itoa(signed short value, char * result, const unsigned int base) {
// check that the base if valid
if (base < 2 || base > 36) {
*result = '\0';
return result;
}
char * ptr = result, *ptr1 = result, tmp_char;
signed short tmp_value;
do {
tmp_value = value;
value /= base;
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)];
} while (value);
// Apply negative sign
if (tmp_value < 0) {
*ptr++ = '-';
}
*ptr-- = '\0';
while (ptr1 < ptr) {
tmp_char = *ptr;
*ptr-- = *ptr1;
*ptr1++ = tmp_char;
}
return result;
}
char* ltoa(long value, char* result, int base) {
return itoa((int)value, result, base);
}
char* ultoa(unsigned long value, char* result, int base) {
return itoa((unsigned int)value, result, base);
}
char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
bool negative = false;
char* out = s;
int fillme = width; // how many cells to fill for the integer part
if (prec > 0) {
fillme -= (prec+1);
}
// Handle negative numbers
if (number < 0.0) {
negative = true;
fillme--;
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
// I optimized out most of the divisions
double rounding = 2.0;
for (unsigned int i = 0; i < prec; ++i)
rounding *= 10.0;
rounding = 1.0 / rounding;
number += rounding;
// Figure out how big our number really is
double tenpow = 1.0;
int digitcount = 1;
double nextpow;
while (number >= (nextpow = (10.0 * tenpow))) {
tenpow = nextpow;
digitcount++;
}
// minimal compensation for possible lack of precision (#7087 addition)
// number *= 1 + std::numeric_limits<decltype(number)>::epsilon();
number /= tenpow;
fillme -= digitcount;
// Pad unused cells with spaces
while (fillme-- > 0) {
*out++ = ' ';
}
// Handle negative sign
if (negative) *out++ = '-';
// Print the digits, and if necessary, the decimal point
digitcount += prec;
int8_t digit = 0;
while (digitcount-- > 0) {
digit = (int8_t)number;
if (digit > 9) digit = 9; // insurance
*out++ = (char)('0' | digit);
if ((digitcount == prec) && (prec > 0)) {
*out++ = '.';
}
number -= digit;
number *= 10.0;
}
// make sure the string is terminated
*out = 0;
return s;
}
String::String(const char *cstr)
{
init();
if (cstr) copy(cstr, strlen(cstr));
}
String::String(const String &value)
{
init();
*this = value;
}
String::String(const __FlashStringHelper *pstr)
{
init();
*this = pstr;
}
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String::String(String &&rval)
{
init();
move(rval);
}
String::String(StringSumHelper &&rval)
{
init();
move(rval);
}
#endif
String::String(char c)
{
init();
char buf[2];
buf[0] = c;
buf[1] = 0;
*this = buf;
}
String::String(unsigned char value, unsigned char base)
{
init();
char buf[1 + 8 * sizeof(unsigned char)];
itoa(value, buf, base);
*this = buf;
}
String::String(int value, unsigned char base)
{
init();
char buf[2 + 8 * sizeof(int)];
itoa(value, buf, base);
*this = buf;
}
String::String(unsigned int value, unsigned char base)
{
init();
char buf[1 + 8 * sizeof(unsigned int)];
itoa(value, buf, base);
*this = buf;
}
String::String(long value, unsigned char base)
{
init();
char buf[2 + 8 * sizeof(long)];
ltoa(value, buf, base);
*this = buf;
}
String::String(unsigned long value, unsigned char base)
{
init();
char buf[1 + 8 * sizeof(unsigned long)];
ultoa(value, buf, base);
*this = buf;
}
String::String(float value, unsigned char decimalPlaces)
{
init();
char buf[33];
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}
String::String(double value, unsigned char decimalPlaces)
{
init();
char buf[33];
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}
String::~String()
{
free(buffer);
}
/*********************************************/
/* Memory Management */
/*********************************************/
inline void String::init(void)
{
buffer = NULL;
capacity = 0;
len = 0;
}
void String::invalidate(void)
{
if (buffer) free(buffer);
buffer = NULL;
capacity = len = 0;
}
unsigned char String::reserve(unsigned int size)
{
if (buffer && capacity >= size) return 1;
if (changeBuffer(size)) {
if (len == 0) buffer[0] = 0;
return 1;
}
return 0;
}
unsigned char String::changeBuffer(unsigned int maxStrLen)
{
char *newbuffer = (char *)realloc(buffer, maxStrLen + 1);
if (newbuffer) {
buffer = newbuffer;
capacity = maxStrLen;
return 1;
}
return 0;
}
/*********************************************/
/* Copy and Move */
/*********************************************/
String & String::copy(const char *cstr, unsigned int length)
{
if (!reserve(length)) {
invalidate();
return *this;
}
len = length;
strcpy(buffer, cstr);
return *this;
}
/*
String & String::copy(const char *pstr, unsigned int length)
{
if (!reserve(length)) {
invalidate();
return *this;
}
len = length;
strcpy_P(buffer, pstr);
return *this;
}
*/
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
void String::move(String &rhs)
{
if (buffer) {
if (rhs && capacity >= rhs.len) {
strcpy(buffer, rhs.buffer);
len = rhs.len;
rhs.len = 0;
return;
} else {
free(buffer);
}
}
buffer = rhs.buffer;
capacity = rhs.capacity;
len = rhs.len;
rhs.buffer = NULL;
rhs.capacity = 0;
rhs.len = 0;
}
#endif
String & String::operator = (const String &rhs)
{
if (this == &rhs) return *this;
if (rhs.buffer) copy(rhs.buffer, rhs.len);
else invalidate();
return *this;
}
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String & String::operator = (String &&rval)
{
if (this != &rval) move(rval);
return *this;
}
String & String::operator = (StringSumHelper &&rval)
{
if (this != &rval) move(rval);
return *this;
}
#endif
String & String::operator = (const char *cstr)
{
if (cstr) copy(cstr, strlen(cstr));
else invalidate();
return *this;
}
String & String::operator = (const __FlashStringHelper *pstr)
{
// if (pstr) copy(pstr, strlen_P((PGM_P)pstr));
// else invalidate();
return *this;
}
/*********************************************/
/* concat */
/*********************************************/
unsigned char String::concat(const String &s)
{
return concat(s.buffer, s.len);
}
unsigned char String::concat(const char *cstr, unsigned int length)
{
unsigned int newlen = len + length;
if (!cstr) return 0;
if (length == 0) return 1;
if (!reserve(newlen)) return 0;
strcpy(buffer + len, cstr);
len = newlen;
return 1;
}
unsigned char String::concat(const char *cstr)
{
if (!cstr) return 0;
return concat(cstr, strlen(cstr));
}
unsigned char String::concat(char c)
{
char buf[2];
buf[0] = c;
buf[1] = 0;
return concat(buf, 1);
}
unsigned char String::concat(unsigned char num)
{
char buf[1 + 3 * sizeof(unsigned char)];
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(int num)
{
char buf[2 + 3 * sizeof(int)];
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(unsigned int num)
{
char buf[1 + 3 * sizeof(unsigned int)];
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(long num)
{
char buf[2 + 3 * sizeof(long)];
ltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(unsigned long num)
{
char buf[1 + 3 * sizeof(unsigned long)];
ultoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char String::concat(float num)
{
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
unsigned char String::concat(double num)
{
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
unsigned char String::concat(const __FlashStringHelper * str)
{
if (!str) return 0;
int length = strlen_P((const char *) str);
if (length == 0) return 1;
unsigned int newlen = len + length;
if (!reserve(newlen)) return 0;
strcpy_P(buffer + len, (const char *) str);
len = newlen;
return 1;
}
/*********************************************/
/* Concatenate */
/*********************************************/
StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(rhs.buffer, rhs.len)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, char c)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(c)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, int num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, long num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, float num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, double num)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(rhs)) a.invalidate();
return a;
}
/*********************************************/
/* Comparison */
/*********************************************/
int String::compareTo(const String &s) const
{
if (!buffer || !s.buffer) {
if (s.buffer && s.len > 0) return 0 - *(unsigned char *)s.buffer;
if (buffer && len > 0) return *(unsigned char *)buffer;
return 0;
}
return strcmp(buffer, s.buffer);
}
unsigned char String::isEmpty() const {
return (len == 0);
}
unsigned char String::equals(const String &s2) const
{
return (len == s2.len && compareTo(s2) == 0);
}
unsigned char String::equals(const char *cstr) const
{
if (len == 0) return (cstr == NULL || *cstr == 0);
if (cstr == NULL) return buffer[0] == 0;
return strcmp(buffer, cstr) == 0;
}
unsigned char String::operator<(const String &rhs) const
{
return compareTo(rhs) < 0;
}
unsigned char String::operator>(const String &rhs) const
{
return compareTo(rhs) > 0;
}
unsigned char String::operator<=(const String &rhs) const
{
return compareTo(rhs) <= 0;
}
unsigned char String::operator>=(const String &rhs) const
{
return compareTo(rhs) >= 0;
}
unsigned char String::equalsIgnoreCase( const String &s2 ) const
{
if (this == &s2) return 1;
if (len != s2.len) return 0;
if (len == 0) return 1;
const char *p1 = buffer;
const char *p2 = s2.buffer;
while (*p1) {
if (tolower(*p1++) != tolower(*p2++)) return 0;
}
return 1;
}
unsigned char String::startsWith( const String &s2 ) const
{
if (len < s2.len) return 0;
return startsWith(s2, 0);
}
unsigned char String::startsWith( const String &s2, unsigned int offset ) const
{
if (offset > len - s2.len || !buffer || !s2.buffer) return 0;
return strncmp( &buffer[offset], s2.buffer, s2.len ) == 0;
}
unsigned char String::endsWith( const String &s2 ) const
{
if ( len < s2.len || !buffer || !s2.buffer) return 0;
return strcmp(&buffer[len - s2.len], s2.buffer) == 0;
}
/*********************************************/
/* Character Access */
/*********************************************/
char String::charAt(unsigned int loc) const
{
return operator[](loc);
}
void String::setCharAt(unsigned int loc, char c)
{
if (loc < len) buffer[loc] = c;
}
char & String::operator[](unsigned int index)
{
static char dummy_writable_char;
if (index >= len || !buffer) {
dummy_writable_char = 0;
return dummy_writable_char;
}
return buffer[index];
}
char String::operator[]( unsigned int index ) const
{
if (index >= len || !buffer) return 0;
return buffer[index];
}
void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const
{
if (!bufsize || !buf) return;
if (index >= len) {
buf[0] = 0;
return;
}
unsigned int n = bufsize - 1;
if (n > len - index) n = len - index;
strncpy((char *)buf, buffer + index, n);
buf[n] = 0;
}
/*********************************************/
/* Search */
/*********************************************/
int String::indexOf(char c) const
{
return indexOf(c, 0);
}
int String::indexOf( char ch, unsigned int fromIndex ) const
{
if (fromIndex >= len) return -1;
const char* temp = strchr(buffer + fromIndex, ch);
if (temp == NULL) return -1;
return temp - buffer;
}
int String::indexOf(const String &s2) const
{
return indexOf(s2, 0);
}
int String::indexOf(const String &s2, unsigned int fromIndex) const
{
if (fromIndex >= len) return -1;
const char *found = strstr(buffer + fromIndex, s2.buffer);
if (found == NULL) return -1;
return found - buffer;
}
int String::lastIndexOf( char theChar ) const
{
return lastIndexOf(theChar, len - 1);
}
int String::lastIndexOf(char ch, unsigned int fromIndex) const
{
if (fromIndex >= len) return -1;
char tempchar = buffer[fromIndex + 1];
buffer[fromIndex + 1] = '\0';
char* temp = strrchr( buffer, ch );
buffer[fromIndex + 1] = tempchar;
if (temp == NULL) return -1;
return temp - buffer;
}
int String::lastIndexOf(const String &s2) const
{
return lastIndexOf(s2, len - s2.len);
}
int String::lastIndexOf(const String &s2, unsigned int fromIndex) const
{
if (s2.len == 0 || len == 0 || s2.len > len) return -1;
if (fromIndex >= len) fromIndex = len - 1;
int found = -1;
for (char *p = buffer; p <= buffer + fromIndex; p++) {
p = strstr(p, s2.buffer);
if (!p) break;
if ((unsigned int)(p - buffer) <= fromIndex) found = p - buffer;
}
return found;
}
String String::substring(unsigned int left, unsigned int right) const
{
if (left > right) {
unsigned int temp = right;
right = left;
left = temp;
}
String out;
if (left >= len) return out;
if (right > len) right = len;
char temp = buffer[right]; // save the replaced character
buffer[right] = '\0';
out = buffer + left; // pointer arithmetic
buffer[right] = temp; //restore character
return out;
}
/*********************************************/
/* Modification */
/*********************************************/
void String::replace(char find, char replace)
{
if (!buffer) return;
for (char *p = buffer; *p; p++) {
if (*p == find) *p = replace;
}
}
void String::replace(const String& find, const String& replace)
{
if (len == 0 || find.len == 0) return;
int diff = replace.len - find.len;
char *readFrom = buffer;
char *foundAt;
if (diff == 0) {
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
memcpy(foundAt, replace.buffer, replace.len);
readFrom = foundAt + replace.len;
}
} else if (diff < 0) {
char *writeTo = buffer;
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
unsigned int n = foundAt - readFrom;
memcpy(writeTo, readFrom, n);
writeTo += n;
memcpy(writeTo, replace.buffer, replace.len);
writeTo += replace.len;
readFrom = foundAt + find.len;
len += diff;
}
strcpy(writeTo, readFrom);
} else {
unsigned int size = len; // compute size needed for result
while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
readFrom = foundAt + find.len;
size += diff;
}
if (size == len) return;
if (size > capacity && !changeBuffer(size)) return;
int index = len - 1;
while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) {
readFrom = buffer + index + find.len;
memmove(readFrom + diff, readFrom, len - (readFrom - buffer));
len += diff;
buffer[len] = 0;
memcpy(buffer + index, replace.buffer, replace.len);
index--;
}
}
}
void String::remove(unsigned int index){
// Pass the biggest integer as the count. The remove method
// below will take care of truncating it at the end of the
// string.
remove(index, (unsigned int)-1);
}
void String::remove(unsigned int index, unsigned int count){
if (index >= len) { return; }
if (count <= 0) { return; }
if (count > len - index) { count = len - index; }
char *writeTo = buffer + index;
len = len - count;
strncpy(writeTo, buffer + index + count,len - index);
buffer[len] = 0;
}
void String::toLowerCase(void)
{
if (!buffer) return;
for (char *p = buffer; *p; p++) {
*p = tolower(*p);
}
}
void String::toUpperCase(void)
{
if (!buffer) return;
for (char *p = buffer; *p; p++) {
*p = toupper(*p);
}
}
void String::trim(void)
{
if (!buffer || len == 0) return;
char *begin = buffer;
while (isspace(*begin)) begin++;
char *end = buffer + len - 1;
while (isspace(*end) && end >= begin) end--;
len = end + 1 - begin;
if (begin > buffer) memcpy(buffer, begin, len);
buffer[len] = 0;
}
/*********************************************/
/* Parsing / Conversion */
/*********************************************/
long String::toInt(void) const
{
if (buffer) return atol(buffer);
return 0;
}
float String::toFloat(void) const
{
return float(toDouble());
}
double String::toDouble(void) const
{
if (buffer) return atof(buffer);
return 0;
}

236
lib_standalone/WString.h Normal file
View File

@@ -0,0 +1,236 @@
/*
WString.h - String library for Wiring & Arduino
...mostly rewritten by Paul Stoffregen...
Copyright (c) 2009-10 Hernando Barragan. All right reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef String_class_h
#define String_class_h
#define strlen_P strlen
#define strncpy_P strncpy
#define strcmp_P strcmp
#define strcpy_P strcpy
// #include "pgmspace.h"
// #include "noniso.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// When compiling programs with this class, the following gcc parameters
// dramatically increase performance and memory (RAM) efficiency, typically
// with little or no increase in code size.
// -felide-constructors
// -std=c++0x
class __FlashStringHelper;
// #define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
// An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations.
class StringSumHelper;
// The string class
class String
{
// use a function pointer to allow for "if (s)" without the
// complications of an operator bool(). for more information, see:
// http://www.artima.com/cppsource/safebool.html
typedef void (String::*StringIfHelperType)() const;
void StringIfHelper() const {}
public:
// constructors
// creates a copy of the initial value.
// if the initial value is null or invalid, or if memory allocation
// fails, the string will be marked as invalid (i.e. "if (s)" will
// be false).
String(const char *cstr = "");
String(const String &str);
String(const __FlashStringHelper *str);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String(String &&rval);
String(StringSumHelper &&rval);
#endif
explicit String(char c);
explicit String(unsigned char, unsigned char base=10);
explicit String(int, unsigned char base=10);
explicit String(unsigned int, unsigned char base=10);
explicit String(long, unsigned char base=10);
explicit String(unsigned long, unsigned char base=10);
explicit String(float, unsigned char decimalPlaces=2);
explicit String(double, unsigned char decimalPlaces=2);
~String(void);
// memory management
// return true on success, false on failure (in which case, the string
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const {return len;}
// creates a copy of the assigned value. if the value is null or
// invalid, or if the memory allocation fails, the string will be
// marked as invalid ("if (s)" will be false).
String & operator = (const String &rhs);
String & operator = (const char *cstr);
String & operator = (const __FlashStringHelper *str);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String & operator = (String &&rval);
String & operator = (StringSumHelper &&rval);
#endif
// concatenate (works w/ built-in types)
// returns true on success, false on failure (in which case, the string
// is left unchanged). if the argument is null or invalid, the
// concatenation is considered unsucessful.
unsigned char concat(const String &str);
unsigned char concat(const char *cstr);
unsigned char concat(char c);
unsigned char concat(unsigned char c);
unsigned char concat(int num);
unsigned char concat(unsigned int num);
unsigned char concat(long num);
unsigned char concat(unsigned long num);
unsigned char concat(float num);
unsigned char concat(double num);
unsigned char concat(const __FlashStringHelper * str);
// if there's not enough memory for the concatenated value, the string
// will be left unchanged (but this isn't signalled in any way)
String & operator += (const String &rhs) {concat(rhs); return (*this);}
String & operator += (const char *cstr) {concat(cstr); return (*this);}
String & operator += (char c) {concat(c); return (*this);}
String & operator += (unsigned char num) {concat(num); return (*this);}
String & operator += (int num) {concat(num); return (*this);}
String & operator += (unsigned int num) {concat(num); return (*this);}
String & operator += (long num) {concat(num); return (*this);}
String & operator += (unsigned long num) {concat(num); return (*this);}
String & operator += (float num) {concat(num); return (*this);}
String & operator += (double num) {concat(num); return (*this);}
String & operator += (const __FlashStringHelper *str){concat(str); return (*this);}
friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper & operator + (const StringSumHelper &lhs, char c);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, int num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, long num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, float num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, double num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs);
// comparison (only works w/ Strings and "strings")
operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; }
int compareTo(const String &s) const;
unsigned char equals(const String &s) const;
unsigned char equals(const char *cstr) const;
unsigned char operator == (const String &rhs) const {return equals(rhs);}
unsigned char operator == (const char *cstr) const {return equals(cstr);}
unsigned char operator != (const String &rhs) const {return !equals(rhs);}
unsigned char operator != (const char *cstr) const {return !equals(cstr);}
unsigned char operator < (const String &rhs) const;
unsigned char operator > (const String &rhs) const;
unsigned char operator <= (const String &rhs) const;
unsigned char operator >= (const String &rhs) const;
unsigned char equalsIgnoreCase(const String &s) const;
unsigned char startsWith( const String &prefix) const;
unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char endsWith(const String &suffix) const;
// character acccess
char charAt(unsigned int index) const;
void setCharAt(unsigned int index, char c);
char operator [] (unsigned int index) const;
char& operator [] (unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const
{ getBytes((unsigned char *)buf, bufsize, index); }
const char* c_str() const { return buffer; }
char* begin() { return buffer; }
char* end() { return buffer + length(); }
const char* begin() const { return c_str(); }
const char* end() const { return c_str() + length(); }
// search
int indexOf( char ch ) const;
int indexOf( char ch, unsigned int fromIndex ) const;
int indexOf( const String &str ) const;
int indexOf( const String &str, unsigned int fromIndex ) const;
int lastIndexOf( char ch ) const;
int lastIndexOf( char ch, unsigned int fromIndex ) const;
int lastIndexOf( const String &str ) const;
int lastIndexOf( const String &str, unsigned int fromIndex ) const;
String substring( unsigned int beginIndex ) const { return substring(beginIndex, len); };
String substring( unsigned int beginIndex, unsigned int endIndex ) const;
// modification
void replace(char find, char replace);
void replace(const String& find, const String& replace);
void remove(unsigned int index);
void remove(unsigned int index, unsigned int count);
void toLowerCase(void);
void toUpperCase(void);
void trim(void);
// parsing/conversion
long toInt(void) const;
float toFloat(void) const;
double toDouble(void) const;
unsigned char isEmpty() const;
protected:
char *buffer; // the actual char array
unsigned int capacity; // the array length minus one (for the '\0')
unsigned int len; // the String length (not counting the '\0')
protected:
void init(void);
void invalidate(void);
unsigned char changeBuffer(unsigned int maxStrLen);
unsigned char concat(const char *cstr, unsigned int length);
// copy and move
String & copy(const char *cstr, unsigned int length);
String & copy(const __FlashStringHelper *pstr, unsigned int length);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
void move(String &rhs);
#endif
};
class StringSumHelper : public String
{
public:
StringSumHelper(const String &s) : String(s) {}
StringSumHelper(const char *p) : String(p) {}
StringSumHelper(char c) : String(c) {}
StringSumHelper(unsigned char num) : String(num) {}
StringSumHelper(int num) : String(num) {}
StringSumHelper(unsigned int num) : String(num) {}
StringSumHelper(long num) : String(num) {}
StringSumHelper(unsigned long num) : String(num) {}
StringSumHelper(float num) : String(num) {}
StringSumHelper(double num) : String(num) {}
};
#endif // String_class_h

View File

@@ -16,8 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifdef EMSESP_STANDALONE
#include "emsuart_standalone.h" #include "emsuart_standalone.h"
#pragma GCC diagnostic push #pragma GCC diagnostic push
@@ -88,5 +86,3 @@ char * EMSuart::hextoa(char * result, const uint8_t value) {
} // namespace emsesp } // namespace emsesp
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif

View File

@@ -26,7 +26,7 @@ CXX_STANDARD := -std=c++11
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Defined Symbols # Defined Symbols
#---------------------------------------------------------------------- #----------------------------------------------------------------------
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DEMSESP_DEBUG -DEMSESP_STANDALONE DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_NO_LED
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Sources & Files # Sources & Files
@@ -66,6 +66,7 @@ CPPFLAGS += -Os
CFLAGS += $(CPPFLAGS) CFLAGS += $(CPPFLAGS)
CFLAGS += -Wall CFLAGS += -Wall
CFLAGS += -Wno-unused -Wno-restrict
CFLAGS += -Wextra CFLAGS += -Wextra
CXXFLAGS += $(CFLAGS) -MMD CXXFLAGS += $(CFLAGS) -MMD

View File

@@ -20,7 +20,7 @@
#include "emsesp.h" #include "emsesp.h"
#include "version.h" #include "version.h"
#ifdef EMSESP_DEBUG #ifdef EMSESP_STANDALONE
#include "test/test.h" #include "test/test.h"
#endif #endif
@@ -285,7 +285,7 @@ void Console::enter_custom_context(Shell & shell, unsigned int context) {
// each custom context has the common commands like log, help, exit, su etc // each custom context has the common commands like log, help, exit, su etc
void Console::load_standard_commands(unsigned int context) { void Console::load_standard_commands(unsigned int context) {
#ifdef EMSESP_DEBUG #ifdef EMSESP_STANDALONE
EMSESPShell::commands->add_command(context, EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN, CommandFlags::ADMIN,
flash_string_vector{F_(test)}, flash_string_vector{F_(test)},

View File

@@ -51,10 +51,6 @@ using uuid::log::Level;
// clang-format on // clang-format on
// common words // common words
#ifdef EMSESP_DEBUG
MAKE_PSTR_WORD(test)
#endif
MAKE_PSTR_WORD(exit) MAKE_PSTR_WORD(exit)
MAKE_PSTR_WORD(help) MAKE_PSTR_WORD(help)
MAKE_PSTR_WORD(settings) MAKE_PSTR_WORD(settings)

View File

@@ -135,10 +135,6 @@ void Solar::publish_values() {
doc["energytotal"] = (float)energyTotal_ / 10; doc["energytotal"] = (float)energyTotal_ / 10;
} }
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] Performing a solar module publish"));
#endif
Mqtt::publish("sm_data", doc); Mqtt::publish("sm_data", doc);
} }

View File

@@ -30,15 +30,17 @@ AsyncWebServer webServer(80);
#if defined(ESP32) #if defined(ESP32)
ESP8266React EMSESP::esp8266React(&webServer, &SPIFFS); ESP8266React EMSESP::esp8266React(&webServer, &SPIFFS);
EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &SPIFFS, EMSESP::esp8266React.getSecurityManager()); EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &SPIFFS, EMSESP::esp8266React.getSecurityManager());
#else #elif defined(ESP8266)
ESP8266React EMSESP::esp8266React(&webServer, &LittleFS); ESP8266React EMSESP::esp8266React(&webServer, &LittleFS);
EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager()); EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
#elif defined(EMSESP_STANDALONE)
FS dummyFS;
ESP8266React EMSESP::esp8266React(&webServer, &dummyFS);
EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
#endif #endif
EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
EMSESPScanDevicesService EMSESP::emsespScanDevicesService = EMSESPScanDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); EMSESPScanDevicesService EMSESP::emsespScanDevicesService = EMSESPScanDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
std::vector<std::unique_ptr<EMSdevice>> EMSESP::emsdevices; // array of all the detected EMS devices std::vector<std::unique_ptr<EMSdevice>> EMSESP::emsdevices; // array of all the detected EMS devices
@@ -622,7 +624,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data); Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data);
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
// get_uptime is only updated once per loop, does not give the right time // get_uptime is only updated once per loop, does not give the right time
LOG_DEBUG(F("[DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str()); LOG_TRACE(F("[DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
#endif #endif
return; // it's an echo return; // it's an echo
} }
@@ -720,7 +722,7 @@ void EMSESP::start() {
#endif #endif
esp8266React.begin(); // starts wifi, ap, ota, security, mqtt services esp8266React.begin(); // starts wifi, ap, ota, security, mqtt services
emsespSettingsService.begin(); // load settings emsespSettingsService.begin(); // load EMS-ESP specific settings from LittleFS
console_.start(); // telnet and serial console console_.start(); // telnet and serial console
system_.start(); // starts syslog, uart, sets version, initializes LED. Requires pre-loaded settings. system_.start(); // starts syslog, uart, sets version, initializes LED. Requires pre-loaded settings.
mqtt_.start(EMSESP::esp8266React.getMqttClient()); // mqtt init mqtt_.start(EMSESP::esp8266React.getMqttClient()); // mqtt init
@@ -732,10 +734,7 @@ void EMSESP::start() {
// loop de loop // loop de loop
void EMSESP::loop() { void EMSESP::loop() {
#ifndef EMSESP_STANDALONE esp8266React.loop(); // web
esp8266React.loop();
#endif
system_.loop(); // does LED and checks system health, and syslog service system_.loop(); // does LED and checks system health, and syslog service
mqtt_.loop(); // starts mqtt, and sends out anything in the queue mqtt_.loop(); // starts mqtt, and sends out anything in the queue
rxservice_.loop(); // process what ever is in the rx queue rxservice_.loop(); // process what ever is in the rx queue
@@ -753,7 +752,6 @@ void EMSESP::loop() {
#if defined(ESP32) #if defined(ESP32)
delay(1); delay(1);
#endif #endif
} }
} // namespace emsesp } // namespace emsesp

View File

@@ -64,7 +64,7 @@ class EMSESP {
static void publish_all_values(); static void publish_all_values();
#ifdef EMSESP_DEBUG #ifdef EMSESP_STANDALONE
static void run_test(uuid::console::Shell & shell, const std::string & command); // only for testing static void run_test(uuid::console::Shell & shell, const std::string & command); // only for testing
static void dummy_mqtt_commands(const char * message); static void dummy_mqtt_commands(const char * message);
static void rx_telegram(const std::vector<uint8_t> & data); static void rx_telegram(const std::vector<uint8_t> & data);

View File

@@ -27,9 +27,7 @@ MAKE_PSTR(logger_name, "mqtt")
namespace emsesp { namespace emsesp {
#ifndef EMSESP_STANDALONE
AsyncMqttClient * Mqtt::mqttClient_; AsyncMqttClient * Mqtt::mqttClient_;
#endif
// static parameters we make global // static parameters we make global
std::string Mqtt::hostname_; std::string Mqtt::hostname_;
@@ -90,10 +88,6 @@ void Mqtt::subscribe(const uint8_t device_id, const std::string & topic, mqtt_fu
if (!exists) { if (!exists) {
mqtt_functions_.emplace_back(device_id, std::move(full_topic), cb); // register a call back function for a specific telegram type mqtt_functions_.emplace_back(device_id, std::move(full_topic), cb); // register a call back function for a specific telegram type
} else {
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] Resubscribing to topic %s"), full_topic);
#endif
} }
queue_subscribe_message(topic); // add subscription to queue queue_subscribe_message(topic); // add subscription to queue
@@ -120,11 +114,7 @@ void Mqtt::subscribe(const std::string & topic, mqtt_function_p cb) {
// sends out top item on publish queue // sends out top item on publish queue
void Mqtt::loop() { void Mqtt::loop() {
// exit if MQTT is not enabled or if there is no WIFI // exit if MQTT is not enabled or if there is no WIFI
#ifndef EMSESP_STANDALONE
if (!connected()) { if (!connected()) {
#else
if (false) {
#endif
return; return;
} }
@@ -201,10 +191,7 @@ void Mqtt::on_message(char * topic, char * payload, size_t len) {
// convert payload to a null-terminated char string // convert payload to a null-terminated char string
char message[len + 2]; char message[len + 2];
strlcpy(message, payload, len + 1); strlcpy(message, payload, len + 1);
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] Received %s => %s (length %d)"), topic, message, len); LOG_DEBUG(F("[DEBUG] Received %s => %s (length %d)"), topic, message, len);
#endif
/* /*
// strip out everything until the last / // strip out everything until the last /
@@ -296,14 +283,13 @@ void Mqtt::start(AsyncMqttClient * mqttClient) {
mqtt_qos_ = mqttSettings.mqtt_qos; mqtt_qos_ = mqttSettings.mqtt_qos;
}); });
#ifndef EMSESP_STANDALONE
mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); }); mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); });
mqttClient_->setWill(make_topic(will_topic_, "status"), 1, true, "offline"); // with qos 1, retain true mqttClient_->setWill(make_topic(will_topic_, "status"), 1, true, "offline"); // with qos 1, retain true
mqttClient_->onMessage([this](char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { mqttClient_->onMessage([this](char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
on_message(topic, payload, len); on_message(topic, payload, len);
mqttClient_->onPublish([this](uint16_t packetId) { on_publish(packetId); }); mqttClient_->onPublish([this](uint16_t packetId) { on_publish(packetId); });
}); });
#endif
} }
void Mqtt::set_publish_time(uint16_t publish_time) { void Mqtt::set_publish_time(uint16_t publish_time) {
@@ -320,7 +306,9 @@ void Mqtt::on_connect() {
StaticJsonDocument<90> doc; StaticJsonDocument<90> doc;
doc["event"] = "start"; doc["event"] = "start";
doc["version"] = EMSESP_APP_VERSION; doc["version"] = EMSESP_APP_VERSION;
#ifndef EMSESP_STANDALONE
doc["ip"] = WiFi.localIP().toString(); doc["ip"] = WiFi.localIP().toString();
#endif
publish("info", doc, false); // send with retain off publish("info", doc, false); // send with retain off
publish("status", "online", true); // say we're alive to the Last Will topic, with retain on publish("status", "online", true); // say we're alive to the Last Will topic, with retain on
@@ -416,11 +404,7 @@ void Mqtt::process_queue() {
// if we're subscribing... // if we're subscribing...
if (message->operation == Operation::SUBSCRIBE) { if (message->operation == Operation::SUBSCRIBE) {
LOG_DEBUG(F("Subscribing to topic: %s"), full_topic); LOG_DEBUG(F("Subscribing to topic: %s"), full_topic);
#ifndef EMSESP_STANDALONE
uint16_t packet_id = mqttClient_->subscribe(full_topic, mqtt_qos_); uint16_t packet_id = mqttClient_->subscribe(full_topic, mqtt_qos_);
#else
uint16_t packet_id = 1;
#endif
if (!packet_id) { if (!packet_id) {
LOG_DEBUG(F("Error subscribing to %s, error %d"), full_topic, packet_id); LOG_DEBUG(F("Error subscribing to %s, error %d"), full_topic, packet_id);
} }
@@ -437,12 +421,7 @@ void Mqtt::process_queue() {
} }
// else try and publish it // else try and publish it
#ifndef EMSESP_STANDALONE
uint16_t packet_id = mqttClient_->publish(full_topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_); uint16_t packet_id = mqttClient_->publish(full_topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_);
#else
uint16_t packet_id = 1;
#endif
LOG_DEBUG(F("Publishing topic %s (#%02d, attempt #%d, pid %d)"), full_topic, mqtt_message.id_, mqtt_message.retry_count_ + 1, packet_id); LOG_DEBUG(F("Publishing topic %s (#%02d, attempt #%d, pid %d)"), full_topic, mqtt_message.id_, mqtt_message.retry_count_ + 1, packet_id);
if (packet_id == 0) { if (packet_id == 0) {

View File

@@ -27,9 +27,7 @@
#include <deque> #include <deque>
#include <functional> #include <functional>
#ifndef EMSESP_STANDALONE
#include <AsyncMqttClient.h> #include <AsyncMqttClient.h>
#endif
#include "helpers.h" #include "helpers.h"
#include "system.h" #include "system.h"
@@ -86,21 +84,13 @@ class Mqtt {
static void on_connect(); static void on_connect();
void disconnect() { void disconnect() {
#ifdef EMSESP_STANDALONE
return;
#else
mqttClient_->disconnect(); mqttClient_->disconnect();
#endif
} }
void incoming(char * topic, char * payload); // for testing void incoming(char * topic, char * payload); // for testing
static bool connected() { static bool connected() {
#ifdef EMSESP_STANDALONE
return true;
#else
return mqttClient_->connected(); return mqttClient_->connected();
#endif
} }
static uint32_t publish_fails() { static uint32_t publish_fails() {
@@ -126,9 +116,7 @@ class Mqtt {
}; };
static std::deque<QueuedMqttMessage> mqtt_messages_; static std::deque<QueuedMqttMessage> mqtt_messages_;
#ifndef EMSESP_STANDALONE
static AsyncMqttClient * mqttClient_; static AsyncMqttClient * mqttClient_;
#endif
static constexpr size_t MAX_MQTT_MESSAGES = 50; static constexpr size_t MAX_MQTT_MESSAGES = 50;
static size_t maximum_mqtt_messages_; static size_t maximum_mqtt_messages_;

View File

@@ -75,7 +75,6 @@ void System::mqtt_commands(const char * message) {
#endif #endif
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if (doc["D0"] != nullptr) { if (doc["D0"] != nullptr) {
const int8_t set = doc["D0"]; const int8_t set = doc["D0"];
pinMode(d0_, OUTPUT); pinMode(d0_, OUTPUT);
@@ -119,7 +118,6 @@ void System::mqtt_commands(const char * message) {
} }
LOG_INFO(F("Port D3 set to %d"), set); LOG_INFO(F("Port D3 set to %d"), set);
} }
#endif #endif
const char * command = doc["cmd"]; const char * command = doc["cmd"];
@@ -155,11 +153,10 @@ void System::restart() {
// format fs // format fs
// format the FS. Wipes everything. // format the FS. Wipes everything.
void System::format(uuid::console::Shell & shell) { void System::format(uuid::console::Shell & shell) {
auto msg = F("Resetting all settings to defaults"); auto msg = F("Formatting file system. This will also reset all settings to their defaults");
shell.logger().warning(msg); shell.logger().warning(msg);
shell.flush(); shell.flush();
#ifndef EMSESP_STANDALONE
EMSuart::stop(); EMSuart::stop();
#if defined(ESP8266) #if defined(ESP8266)
@@ -169,8 +166,6 @@ void System::format(uuid::console::Shell & shell) {
#endif #endif
System::restart(); System::restart();
#endif
} }
// return free heap mem as a percentage // return free heap mem as a percentage
@@ -226,10 +221,9 @@ void System::start() {
// fetch settings // fetch settings
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { tx_mode_ = settings.tx_mode; }); EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { tx_mode_ = settings.tx_mode; });
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { system_heartbeat_ = settings.system_heartbeat; }); EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { system_heartbeat_ = settings.system_heartbeat; });
syslog_init(); syslog_init(); // init SysLog
#if defined(ESP32) #if defined(ESP32)
LOG_INFO(F("System booted (EMS-ESP version %s ESP32)"), EMSESP_APP_VERSION); LOG_INFO(F("System booted (EMS-ESP version %s ESP32)"), EMSESP_APP_VERSION);

View File

@@ -34,6 +34,8 @@
#include "mqtt.h" #include "mqtt.h"
#include "emsesp.h" #include "emsesp.h"
MAKE_PSTR_WORD(test)
namespace emsesp { namespace emsesp {
class Test { class Test {