mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
standalone w/ web server for testing
This commit is contained in:
@@ -39,6 +39,7 @@ static int __output_level[256];
|
||||
int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) {
|
||||
memset(__output_pins, 0, sizeof(__output_pins));
|
||||
memset(__output_level, 0, sizeof(__output_level));
|
||||
|
||||
setup();
|
||||
// loop(); // run once
|
||||
|
||||
|
||||
@@ -26,12 +26,15 @@
|
||||
#include <string>
|
||||
#include <algorithm> // for count_if
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define IPAddress std::string
|
||||
#define ICACHE_FLASH_ATTR
|
||||
#define ICACHE_RAM_ATTR
|
||||
#define os_event_t void
|
||||
#define byte uint8_t
|
||||
#define ltoa itoa
|
||||
#define boolean bool
|
||||
|
||||
#define LOW 0
|
||||
#define HIGH 1
|
||||
@@ -54,14 +57,11 @@ int digitalRead(uint8_t pin);
|
||||
&__c[0]; \
|
||||
}))
|
||||
|
||||
|
||||
class __FlashStringHelper;
|
||||
#define FPSTR(string_literal) (reinterpret_cast<const __FlashStringHelper *>(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 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;
|
||||
};
|
||||
|
||||
/*
|
||||
class String {
|
||||
public:
|
||||
String(const char * data = "")
|
||||
@@ -150,9 +151,18 @@ class String {
|
||||
return data_.c_str();
|
||||
}
|
||||
|
||||
bool equals(String comp) {
|
||||
return (data_ == comp.c_str());
|
||||
}
|
||||
|
||||
bool isEmpty() {
|
||||
return data_.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string data_;
|
||||
};
|
||||
*/
|
||||
|
||||
class NativeConsole : public Stream {
|
||||
public:
|
||||
@@ -215,7 +225,10 @@ void yield(void);
|
||||
void setup(void);
|
||||
void loop(void);
|
||||
|
||||
|
||||
size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize);
|
||||
size_t strlcat(char * dst, const char * src, size_t siz);
|
||||
|
||||
#include "WString.h"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
143
lib_standalone/AsyncMqttClient.h
Normal file
143
lib_standalone/AsyncMqttClient.h
Normal 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
212
lib_standalone/AsyncTCP.h
Normal 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_ */
|
||||
@@ -2,13 +2,99 @@
|
||||
#define ESP8266React_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 {
|
||||
public:
|
||||
ESP8266React(void* server, void* fs);
|
||||
ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
: _settings(server, fs, nullptr){};
|
||||
|
||||
void begin() {};
|
||||
void loop() {};
|
||||
void begin(){};
|
||||
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
|
||||
|
||||
@@ -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
10
lib_standalone/FS.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef FS_h
|
||||
#define FS_h
|
||||
|
||||
class FS {
|
||||
bool open() {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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
37
lib_standalone/Features.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
3
lib_standalone/StatefulService.cpp
Normal file
3
lib_standalone/StatefulService.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include <StatefulService.h>
|
||||
|
||||
update_handler_id_t StateUpdateHandlerInfo::currentUpdatedHandlerId = 0;
|
||||
144
lib_standalone/StatefulService.h
Normal file
144
lib_standalone/StatefulService.h
Normal 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
863
lib_standalone/WString.cpp
Normal 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
236
lib_standalone/WString.h
Normal 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
|
||||
@@ -16,8 +16,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
|
||||
#include "emsuart_standalone.h"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
@@ -88,5 +86,3 @@ char * EMSuart::hextoa(char * result, const uint8_t value) {
|
||||
} // namespace emsesp
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif
|
||||
|
||||
3
makefile
3
makefile
@@ -26,7 +26,7 @@ CXX_STANDARD := -std=c++11
|
||||
#----------------------------------------------------------------------
|
||||
# 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
|
||||
@@ -66,6 +66,7 @@ CPPFLAGS += -Os
|
||||
|
||||
CFLAGS += $(CPPFLAGS)
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wno-unused -Wno-restrict
|
||||
CFLAGS += -Wextra
|
||||
|
||||
CXXFLAGS += $(CFLAGS) -MMD
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "emsesp.h"
|
||||
#include "version.h"
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#ifdef EMSESP_STANDALONE
|
||||
#include "test/test.h"
|
||||
#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
|
||||
void Console::load_standard_commands(unsigned int context) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
#ifdef EMSESP_STANDALONE
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(test)},
|
||||
|
||||
@@ -51,10 +51,6 @@ using uuid::log::Level;
|
||||
// clang-format on
|
||||
|
||||
// common words
|
||||
#ifdef EMSESP_DEBUG
|
||||
MAKE_PSTR_WORD(test)
|
||||
#endif
|
||||
|
||||
MAKE_PSTR_WORD(exit)
|
||||
MAKE_PSTR_WORD(help)
|
||||
MAKE_PSTR_WORD(settings)
|
||||
|
||||
@@ -135,10 +135,6 @@ void Solar::publish_values() {
|
||||
doc["energytotal"] = (float)energyTotal_ / 10;
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG(F("[DEBUG] Performing a solar module publish"));
|
||||
#endif
|
||||
|
||||
Mqtt::publish("sm_data", doc);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,15 +30,17 @@ AsyncWebServer webServer(80);
|
||||
#if defined(ESP32)
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &SPIFFS);
|
||||
EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &SPIFFS, EMSESP::esp8266React.getSecurityManager());
|
||||
#else
|
||||
#elif defined(ESP8266)
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &LittleFS);
|
||||
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
|
||||
|
||||
EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
|
||||
EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&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
|
||||
@@ -622,7 +624,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data);
|
||||
#ifdef EMSESP_DEBUG
|
||||
// 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
|
||||
return; // it's an echo
|
||||
}
|
||||
@@ -720,7 +722,7 @@ void EMSESP::start() {
|
||||
#endif
|
||||
|
||||
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
|
||||
system_.start(); // starts syslog, uart, sets version, initializes LED. Requires pre-loaded settings.
|
||||
mqtt_.start(EMSESP::esp8266React.getMqttClient()); // mqtt init
|
||||
@@ -732,10 +734,7 @@ void EMSESP::start() {
|
||||
|
||||
// loop de loop
|
||||
void EMSESP::loop() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
esp8266React.loop();
|
||||
#endif
|
||||
|
||||
esp8266React.loop(); // web
|
||||
system_.loop(); // does LED and checks system health, and syslog service
|
||||
mqtt_.loop(); // starts mqtt, and sends out anything in the queue
|
||||
rxservice_.loop(); // process what ever is in the rx queue
|
||||
@@ -753,7 +752,6 @@ void EMSESP::loop() {
|
||||
#if defined(ESP32)
|
||||
delay(1);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -64,7 +64,7 @@ class EMSESP {
|
||||
|
||||
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 dummy_mqtt_commands(const char * message);
|
||||
static void rx_telegram(const std::vector<uint8_t> & data);
|
||||
|
||||
27
src/mqtt.cpp
27
src/mqtt.cpp
@@ -27,9 +27,7 @@ MAKE_PSTR(logger_name, "mqtt")
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
AsyncMqttClient * Mqtt::mqttClient_;
|
||||
#endif
|
||||
|
||||
// static parameters we make global
|
||||
std::string Mqtt::hostname_;
|
||||
@@ -90,10 +88,6 @@ void Mqtt::subscribe(const uint8_t device_id, const std::string & topic, mqtt_fu
|
||||
|
||||
if (!exists) {
|
||||
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
|
||||
@@ -120,11 +114,7 @@ void Mqtt::subscribe(const std::string & topic, mqtt_function_p cb) {
|
||||
// sends out top item on publish queue
|
||||
void Mqtt::loop() {
|
||||
// exit if MQTT is not enabled or if there is no WIFI
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!connected()) {
|
||||
#else
|
||||
if (false) {
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -201,10 +191,7 @@ void Mqtt::on_message(char * topic, char * payload, size_t len) {
|
||||
// convert payload to a null-terminated char string
|
||||
char message[len + 2];
|
||||
strlcpy(message, payload, len + 1);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG(F("[DEBUG] Received %s => %s (length %d)"), topic, message, len);
|
||||
#endif
|
||||
|
||||
/*
|
||||
// strip out everything until the last /
|
||||
@@ -296,14 +283,13 @@ void Mqtt::start(AsyncMqttClient * mqttClient) {
|
||||
mqtt_qos_ = mqttSettings.mqtt_qos;
|
||||
});
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); });
|
||||
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) {
|
||||
on_message(topic, payload, len);
|
||||
mqttClient_->onPublish([this](uint16_t packetId) { on_publish(packetId); });
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mqtt::set_publish_time(uint16_t publish_time) {
|
||||
@@ -320,7 +306,9 @@ void Mqtt::on_connect() {
|
||||
StaticJsonDocument<90> doc;
|
||||
doc["event"] = "start";
|
||||
doc["version"] = EMSESP_APP_VERSION;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
doc["ip"] = WiFi.localIP().toString();
|
||||
#endif
|
||||
publish("info", doc, false); // send with retain off
|
||||
|
||||
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 (message->operation == Operation::SUBSCRIBE) {
|
||||
LOG_DEBUG(F("Subscribing to topic: %s"), full_topic);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uint16_t packet_id = mqttClient_->subscribe(full_topic, mqtt_qos_);
|
||||
#else
|
||||
uint16_t packet_id = 1;
|
||||
#endif
|
||||
if (!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
|
||||
|
||||
#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_);
|
||||
#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);
|
||||
|
||||
if (packet_id == 0) {
|
||||
|
||||
12
src/mqtt.h
12
src/mqtt.h
@@ -27,9 +27,7 @@
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <AsyncMqttClient.h>
|
||||
#endif
|
||||
|
||||
#include "helpers.h"
|
||||
#include "system.h"
|
||||
@@ -86,21 +84,13 @@ class Mqtt {
|
||||
static void on_connect();
|
||||
|
||||
void disconnect() {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
return;
|
||||
#else
|
||||
mqttClient_->disconnect();
|
||||
#endif
|
||||
}
|
||||
|
||||
void incoming(char * topic, char * payload); // for testing
|
||||
|
||||
static bool connected() {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
return true;
|
||||
#else
|
||||
return mqttClient_->connected();
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t publish_fails() {
|
||||
@@ -126,9 +116,7 @@ class Mqtt {
|
||||
};
|
||||
static std::deque<QueuedMqttMessage> mqtt_messages_;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static AsyncMqttClient * mqttClient_;
|
||||
#endif
|
||||
|
||||
static constexpr size_t MAX_MQTT_MESSAGES = 50;
|
||||
static size_t maximum_mqtt_messages_;
|
||||
|
||||
@@ -75,7 +75,6 @@ void System::mqtt_commands(const char * message) {
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
if (doc["D0"] != nullptr) {
|
||||
const int8_t set = doc["D0"];
|
||||
pinMode(d0_, OUTPUT);
|
||||
@@ -119,7 +118,6 @@ void System::mqtt_commands(const char * message) {
|
||||
}
|
||||
LOG_INFO(F("Port D3 set to %d"), set);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const char * command = doc["cmd"];
|
||||
@@ -155,11 +153,10 @@ void System::restart() {
|
||||
// format fs
|
||||
// format the FS. Wipes everything.
|
||||
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.flush();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSuart::stop();
|
||||
|
||||
#if defined(ESP8266)
|
||||
@@ -169,8 +166,6 @@ void System::format(uuid::console::Shell & shell) {
|
||||
#endif
|
||||
|
||||
System::restart();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// return free heap mem as a percentage
|
||||
@@ -226,10 +221,9 @@ void System::start() {
|
||||
|
||||
// fetch settings
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { tx_mode_ = settings.tx_mode; });
|
||||
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { system_heartbeat_ = settings.system_heartbeat; });
|
||||
|
||||
syslog_init();
|
||||
syslog_init(); // init SysLog
|
||||
|
||||
#if defined(ESP32)
|
||||
LOG_INFO(F("System booted (EMS-ESP version %s ESP32)"), EMSESP_APP_VERSION);
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "mqtt.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
MAKE_PSTR_WORD(test)
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Test {
|
||||
|
||||
Reference in New Issue
Block a user