mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 17:29:50 +03:00
version 1.3.0
This commit is contained in:
@@ -550,9 +550,14 @@ void TelnetSpy::handle() {
|
||||
return;
|
||||
}
|
||||
if (!listening) {
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
if ((WiFi.status() == WL_DISCONNECTED) && (WiFi.getMode() & WIFI_AP)) {
|
||||
if (usedSer) {
|
||||
usedSer->println("[TELNET] in AP mode"); // added by Proddy
|
||||
}
|
||||
} else if (WiFi.status() != WL_CONNECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
telnetServer = new WiFiServer(port);
|
||||
telnetServer->begin();
|
||||
telnetServer->setNoDelay(bufLen > 0);
|
||||
|
||||
@@ -158,8 +158,8 @@
|
||||
#define TELNETSPY_PING_TIME 1500
|
||||
#define TELNETSPY_PORT 23
|
||||
#define TELNETSPY_CAPTURE_OS_PRINT true
|
||||
#define TELNETSPY_WELCOME_MSG "Connection established via TelnetSpy2.\n"
|
||||
#define TELNETSPY_REJECT_MSG "TelnetSpy: Only one connection possible.\n"
|
||||
#define TELNETSPY_WELCOME_MSG "Connection established via Telnet.\n"
|
||||
#define TELNETSPY_REJECT_MSG "Telnet: Only one connection possible.\n"
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
632
lib/TelnetSpy/TelnetSpy.xxx
Normal file
632
lib/TelnetSpy/TelnetSpy.xxx
Normal file
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
* TELNET SERVER FOR ESP8266 / ESP32
|
||||
* Cloning the serial port via Telnet.
|
||||
*
|
||||
* Written by Wolfgang Mattis (arduino@yasheena.de).
|
||||
* Version 1.1 / September 7, 2018.
|
||||
* MIT license, all text above must be included in any redistribution.
|
||||
*/
|
||||
|
||||
#ifdef ESP8266
|
||||
extern "C" {
|
||||
#include "user_interface.h"
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "TelnetSpy.h"
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef max
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
static TelnetSpy * actualObject = NULL;
|
||||
|
||||
static void TelnetSpy_putc(char c) {
|
||||
if (actualObject) {
|
||||
actualObject->write(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void TelnetSpy_ignore_putc(char c) {
|
||||
;
|
||||
}
|
||||
|
||||
TelnetSpy::TelnetSpy() {
|
||||
port = TELNETSPY_PORT;
|
||||
telnetServer = NULL;
|
||||
started = false;
|
||||
listening = false;
|
||||
firstMainLoop = true;
|
||||
usedSer = &Serial;
|
||||
storeOffline = true;
|
||||
connected = false;
|
||||
callbackConnect = NULL;
|
||||
callbackDisconnect = NULL;
|
||||
welcomeMsg = strdup(TELNETSPY_WELCOME_MSG);
|
||||
rejectMsg = strdup(TELNETSPY_REJECT_MSG);
|
||||
minBlockSize = TELNETSPY_MIN_BLOCK_SIZE;
|
||||
collectingTime = TELNETSPY_COLLECTING_TIME;
|
||||
maxBlockSize = TELNETSPY_MAX_BLOCK_SIZE;
|
||||
pingTime = TELNETSPY_PING_TIME;
|
||||
pingRef = 0xFFFFFFFF;
|
||||
waitRef = 0xFFFFFFFF;
|
||||
telnetBuf = NULL;
|
||||
bufLen = 0;
|
||||
uint16_t size = TELNETSPY_BUFFER_LEN;
|
||||
while (!setBufferSize(size)) {
|
||||
size = size >> 1;
|
||||
if (size < minBlockSize) {
|
||||
setBufferSize(minBlockSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
debugOutput = TELNETSPY_CAPTURE_OS_PRINT;
|
||||
if (debugOutput) {
|
||||
setDebugOutput(true);
|
||||
}
|
||||
}
|
||||
|
||||
TelnetSpy::~TelnetSpy() {
|
||||
end();
|
||||
}
|
||||
|
||||
// added by proddy
|
||||
void TelnetSpy::disconnectClient() {
|
||||
if (client.connected()) {
|
||||
client.flush();
|
||||
client.stop();
|
||||
}
|
||||
if (connected && (callbackDisconnect != NULL)) {
|
||||
callbackDisconnect();
|
||||
}
|
||||
connected = false;
|
||||
}
|
||||
|
||||
void TelnetSpy::setPort(uint16_t portToUse) {
|
||||
port = portToUse;
|
||||
if (listening) {
|
||||
if (client.connected()) {
|
||||
client.flush();
|
||||
client.stop();
|
||||
}
|
||||
if (connected && (callbackDisconnect != NULL)) {
|
||||
callbackDisconnect();
|
||||
}
|
||||
connected = false;
|
||||
telnetServer->close();
|
||||
delete telnetServer;
|
||||
telnetServer = new WiFiServer(port);
|
||||
if (started) {
|
||||
telnetServer->begin();
|
||||
telnetServer->setNoDelay(bufLen > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TelnetSpy::setWelcomeMsg(char * msg) {
|
||||
if (welcomeMsg) {
|
||||
free(welcomeMsg);
|
||||
}
|
||||
welcomeMsg = strdup(msg);
|
||||
}
|
||||
|
||||
void TelnetSpy::setRejectMsg(char * msg) {
|
||||
if (rejectMsg) {
|
||||
free(rejectMsg);
|
||||
}
|
||||
rejectMsg = strdup(msg);
|
||||
}
|
||||
|
||||
void TelnetSpy::setMinBlockSize(uint16_t minSize) {
|
||||
minBlockSize = min(max((uint16_t)1, minSize), maxBlockSize);
|
||||
}
|
||||
|
||||
void TelnetSpy::setCollectingTime(uint16_t colTime) {
|
||||
collectingTime = colTime;
|
||||
}
|
||||
|
||||
void TelnetSpy::setMaxBlockSize(uint16_t maxSize) {
|
||||
maxBlockSize = max(maxSize, minBlockSize);
|
||||
}
|
||||
|
||||
bool TelnetSpy::setBufferSize(uint16_t newSize) {
|
||||
if (telnetBuf && (bufLen == newSize)) {
|
||||
return true;
|
||||
}
|
||||
if (newSize == 0) {
|
||||
bufLen = 0;
|
||||
if (telnetBuf) {
|
||||
free(telnetBuf);
|
||||
telnetBuf = NULL;
|
||||
}
|
||||
if (telnetServer) {
|
||||
telnetServer->setNoDelay(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
newSize = max(newSize, minBlockSize);
|
||||
uint16_t oldBufLen = bufLen;
|
||||
bufLen = newSize;
|
||||
uint16_t tmp;
|
||||
if (!telnetBuf || (bufUsed == 0)) {
|
||||
bufRdIdx = 0;
|
||||
bufWrIdx = 0;
|
||||
bufUsed = 0;
|
||||
} else {
|
||||
if (bufLen < oldBufLen) {
|
||||
if (bufRdIdx < bufWrIdx) {
|
||||
if (bufWrIdx > bufLen) {
|
||||
tmp = min(bufLen, (uint16_t)(bufWrIdx - max(bufLen, bufRdIdx)));
|
||||
memcpy(telnetBuf, &telnetBuf[bufWrIdx - tmp], tmp);
|
||||
bufWrIdx = tmp;
|
||||
if (bufWrIdx > bufRdIdx) {
|
||||
bufRdIdx = bufWrIdx;
|
||||
} else {
|
||||
if (bufRdIdx > bufLen) {
|
||||
bufRdIdx = 0;
|
||||
}
|
||||
}
|
||||
if (bufRdIdx == bufWrIdx) {
|
||||
bufUsed = bufLen;
|
||||
} else {
|
||||
bufUsed = bufWrIdx - bufRdIdx;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (bufWrIdx > bufLen) {
|
||||
memcpy(telnetBuf, &telnetBuf[bufWrIdx - bufLen], bufLen);
|
||||
bufRdIdx = 0;
|
||||
bufWrIdx = 0;
|
||||
bufUsed = bufLen;
|
||||
} else {
|
||||
tmp = min(bufLen - bufWrIdx, oldBufLen - bufRdIdx);
|
||||
memcpy(&telnetBuf[bufLen - tmp], &telnetBuf[oldBufLen - tmp], tmp);
|
||||
bufRdIdx = bufLen - tmp;
|
||||
bufUsed = bufWrIdx + tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
char * temp = (char *)realloc(telnetBuf, bufLen);
|
||||
if (!temp) {
|
||||
return false;
|
||||
}
|
||||
telnetBuf = temp;
|
||||
if (telnetBuf && (bufLen > oldBufLen) && (bufRdIdx > bufWrIdx)) {
|
||||
tmp = bufLen - (oldBufLen - bufRdIdx);
|
||||
memcpy(&telnetBuf[tmp], &telnetBuf[bufRdIdx], oldBufLen - bufRdIdx);
|
||||
bufRdIdx = tmp;
|
||||
}
|
||||
if (telnetServer) {
|
||||
telnetServer->setNoDelay(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t TelnetSpy::getBufferSize() {
|
||||
if (!telnetBuf) {
|
||||
return 0;
|
||||
}
|
||||
return bufLen;
|
||||
}
|
||||
|
||||
void TelnetSpy::setStoreOffline(bool store) {
|
||||
storeOffline = store;
|
||||
}
|
||||
|
||||
bool TelnetSpy::getStoreOffline() {
|
||||
return storeOffline;
|
||||
}
|
||||
|
||||
void TelnetSpy::setPingTime(uint16_t pngTime) {
|
||||
pingTime = pngTime;
|
||||
if (pingTime == 0) {
|
||||
pingRef = 0xFFFFFFFF;
|
||||
} else {
|
||||
pingRef = (millis() & 0x7FFFFFF) + pingTime;
|
||||
}
|
||||
}
|
||||
|
||||
void TelnetSpy::setSerial(HardwareSerial * usedSerial) {
|
||||
usedSer = usedSerial;
|
||||
}
|
||||
|
||||
size_t TelnetSpy::write(uint8_t data) {
|
||||
if (telnetBuf) {
|
||||
if (storeOffline || client.connected()) {
|
||||
if (bufUsed == bufLen) {
|
||||
if (client.connected()) {
|
||||
sendBlock();
|
||||
}
|
||||
if (bufUsed == bufLen) {
|
||||
char c;
|
||||
while (bufUsed > 0) {
|
||||
c = pullTelnetBuf();
|
||||
if (c == '\n') {
|
||||
addTelnetBuf('\r');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (peekTelnetBuf() == '\r') {
|
||||
pullTelnetBuf();
|
||||
}
|
||||
}
|
||||
}
|
||||
addTelnetBuf(data);
|
||||
/*
|
||||
if (data == '\n') {
|
||||
addTelnetBuf('\r'); // added by proddy, fix for Windows
|
||||
}
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
if (client.connected()) {
|
||||
client.write(data);
|
||||
}
|
||||
}
|
||||
if (usedSer) {
|
||||
return usedSer->write(data);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int TelnetSpy::available(void) {
|
||||
if (usedSer) {
|
||||
int avail = usedSer->available();
|
||||
if (avail > 0) {
|
||||
return avail;
|
||||
}
|
||||
}
|
||||
if (client.connected()) {
|
||||
return telnetAvailable();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TelnetSpy::read(void) {
|
||||
int val;
|
||||
if (usedSer) {
|
||||
val = usedSer->read();
|
||||
if (val != -1) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
if (client.connected()) {
|
||||
if (telnetAvailable()) {
|
||||
val = client.read();
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
int TelnetSpy::peek(void) {
|
||||
int val;
|
||||
if (usedSer) {
|
||||
val = usedSer->peek();
|
||||
if (val != -1) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
if (client.connected()) {
|
||||
if (telnetAvailable()) {
|
||||
val = client.peek();
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void TelnetSpy::flush(void) {
|
||||
if (usedSer) {
|
||||
usedSer->flush();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
|
||||
void TelnetSpy::begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin) {
|
||||
if (usedSer) {
|
||||
usedSer->begin(baud, config, mode, tx_pin);
|
||||
}
|
||||
started = true;
|
||||
}
|
||||
|
||||
#else // ESP32
|
||||
|
||||
void TelnetSpy::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert) {
|
||||
if (usedSer) {
|
||||
usedSer->begin(baud, config, rxPin, txPin, invert);
|
||||
}
|
||||
started = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void TelnetSpy::end() {
|
||||
if (debugOutput) {
|
||||
setDebugOutput(false);
|
||||
}
|
||||
if (usedSer) {
|
||||
usedSer->end();
|
||||
}
|
||||
if (client.connected()) {
|
||||
client.flush();
|
||||
client.stop();
|
||||
}
|
||||
if (connected && (callbackDisconnect != NULL)) {
|
||||
callbackDisconnect();
|
||||
}
|
||||
connected = false;
|
||||
telnetServer->close();
|
||||
delete telnetServer;
|
||||
telnetServer = NULL;
|
||||
listening = false;
|
||||
started = false;
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
|
||||
void TelnetSpy::swap(uint8_t tx_pin) {
|
||||
if (usedSer) {
|
||||
usedSer->swap(tx_pin);
|
||||
}
|
||||
}
|
||||
|
||||
void TelnetSpy::set_tx(uint8_t tx_pin) {
|
||||
if (usedSer) {
|
||||
usedSer->set_tx(tx_pin);
|
||||
}
|
||||
}
|
||||
|
||||
void TelnetSpy::pins(uint8_t tx, uint8_t rx) {
|
||||
if (usedSer) {
|
||||
usedSer->pins(tx, rx);
|
||||
}
|
||||
}
|
||||
|
||||
bool TelnetSpy::isTxEnabled(void) {
|
||||
if (usedSer) {
|
||||
return usedSer->isTxEnabled();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TelnetSpy::isRxEnabled(void) {
|
||||
if (usedSer) {
|
||||
return usedSer->isRxEnabled();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int TelnetSpy::availableForWrite(void) {
|
||||
if (usedSer) {
|
||||
return min(usedSer->availableForWrite(), bufLen - bufUsed);
|
||||
}
|
||||
return bufLen - bufUsed;
|
||||
}
|
||||
|
||||
TelnetSpy::operator bool() const {
|
||||
if (usedSer) {
|
||||
return (bool)*usedSer;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TelnetSpy::setDebugOutput(bool en) {
|
||||
debugOutput = en;
|
||||
if (debugOutput) {
|
||||
actualObject = this;
|
||||
#ifdef ESP8266
|
||||
os_install_putc1((void *)TelnetSpy_putc); // Set system printing (os_printf) to TelnetSpy
|
||||
system_set_os_print(true);
|
||||
#else // ESP32 \
|
||||
// ToDo: How can be done this for ESP32 ?
|
||||
#endif
|
||||
} else {
|
||||
if (actualObject == this) {
|
||||
#ifdef ESP8266
|
||||
system_set_os_print(false);
|
||||
os_install_putc1((void *)TelnetSpy_ignore_putc); // Ignore system printing
|
||||
#else // ESP32 \
|
||||
// ToDo: How can be done this for ESP32 ?
|
||||
#endif
|
||||
actualObject = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TelnetSpy::baudRate(void) {
|
||||
if (usedSer) {
|
||||
return usedSer->baudRate();
|
||||
}
|
||||
return 115200;
|
||||
}
|
||||
|
||||
void TelnetSpy::sendBlock() {
|
||||
uint16_t len = bufUsed;
|
||||
if (len > maxBlockSize) {
|
||||
len = maxBlockSize;
|
||||
}
|
||||
len = min(len, (uint16_t)(bufLen - bufRdIdx));
|
||||
client.write(&telnetBuf[bufRdIdx], len);
|
||||
bufRdIdx += len;
|
||||
if (bufRdIdx >= bufLen) {
|
||||
bufRdIdx = 0;
|
||||
}
|
||||
bufUsed -= len;
|
||||
if (bufUsed == 0) {
|
||||
bufRdIdx = 0;
|
||||
bufWrIdx = 0;
|
||||
}
|
||||
waitRef = 0xFFFFFFFF;
|
||||
if (pingRef != 0xFFFFFFFF) {
|
||||
pingRef = (millis() & 0x7FFFFFF) + pingTime;
|
||||
if (pingRef > 0x7FFFFFFF) {
|
||||
pingRef -= 0x80000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TelnetSpy::addTelnetBuf(char c) {
|
||||
telnetBuf[bufWrIdx] = c;
|
||||
if (bufUsed == bufLen) {
|
||||
bufRdIdx++;
|
||||
if (bufRdIdx >= bufLen) {
|
||||
bufRdIdx = 0;
|
||||
}
|
||||
} else {
|
||||
bufUsed++;
|
||||
}
|
||||
bufWrIdx++;
|
||||
if (bufWrIdx >= bufLen) {
|
||||
bufWrIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char TelnetSpy::pullTelnetBuf() {
|
||||
if (bufUsed == 0) {
|
||||
return 0;
|
||||
}
|
||||
char c = telnetBuf[bufRdIdx++];
|
||||
if (bufRdIdx >= bufLen) {
|
||||
bufRdIdx = 0;
|
||||
}
|
||||
bufUsed--;
|
||||
return c;
|
||||
}
|
||||
|
||||
char TelnetSpy::peekTelnetBuf() {
|
||||
if (bufUsed == 0) {
|
||||
return 0;
|
||||
}
|
||||
return telnetBuf[bufRdIdx];
|
||||
}
|
||||
|
||||
int TelnetSpy::telnetAvailable() {
|
||||
int n = client.available();
|
||||
while (n > 0) {
|
||||
if (0xff == client.peek()) { // If esc char for telnet NVT protocol data remove that telegram:
|
||||
client.read(); // Remove esc char
|
||||
n--;
|
||||
if (0xff == client.peek()) { // If esc sequence for 0xFF data byte...
|
||||
return n; // ...return info about available data (just this 0xFF data byte)
|
||||
}
|
||||
client.read(); // Skip the rest of the telegram of the telnet NVT protocol data
|
||||
client.read();
|
||||
n--;
|
||||
n--;
|
||||
} else { // If next char is a normal data byte...
|
||||
return n; // ...return info about available data
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TelnetSpy::isClientConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
void TelnetSpy::setCallbackOnConnect(telnetSpyCallback callback) {
|
||||
callbackConnect = callback;
|
||||
}
|
||||
|
||||
void TelnetSpy::setCallbackOnDisconnect(telnetSpyCallback callback) {
|
||||
callbackDisconnect = callback;
|
||||
}
|
||||
|
||||
void TelnetSpy::handle() {
|
||||
if (firstMainLoop) {
|
||||
firstMainLoop = false;
|
||||
// Between setup() and loop() the configuration for os_print may be changed so it must be renewed
|
||||
if (debugOutput && (actualObject == this)) {
|
||||
setDebugOutput(true);
|
||||
}
|
||||
}
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
if (!listening) {
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
// unless AP
|
||||
//if (!(WiFi.getMode() & WIFI_AP)) { // proddy
|
||||
// return;
|
||||
// }
|
||||
telnetServer = new WiFiServer(port);
|
||||
telnetServer->begin();
|
||||
telnetServer->setNoDelay(bufLen > 0);
|
||||
listening = true;
|
||||
if (usedSer) {
|
||||
usedSer->println("[TELNET] Telnet server started"); // added by Proddy
|
||||
}
|
||||
}
|
||||
|
||||
if (telnetServer->hasClient()) {
|
||||
if (client.connected()) {
|
||||
WiFiClient rejectClient = telnetServer->available();
|
||||
if (strlen(rejectMsg) > 0) {
|
||||
rejectClient.write((const uint8_t *)rejectMsg, strlen(rejectMsg));
|
||||
}
|
||||
rejectClient.flush();
|
||||
rejectClient.stop();
|
||||
} else {
|
||||
client = telnetServer->available();
|
||||
if (strlen(welcomeMsg) > 0) {
|
||||
client.write((const uint8_t *)welcomeMsg, strlen(welcomeMsg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (client.connected()) {
|
||||
if (!connected) {
|
||||
connected = true;
|
||||
if (pingTime != 0) {
|
||||
pingRef = (millis() & 0x7FFFFFF) + pingTime;
|
||||
}
|
||||
if (callbackConnect != NULL) {
|
||||
callbackConnect();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (connected) {
|
||||
connected = false;
|
||||
client.flush();
|
||||
client.stop();
|
||||
pingRef = 0xFFFFFFFF;
|
||||
waitRef = 0xFFFFFFFF;
|
||||
if (callbackDisconnect != NULL) {
|
||||
callbackDisconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (client.connected() && (bufUsed > 0)) {
|
||||
if (bufUsed >= minBlockSize) {
|
||||
sendBlock();
|
||||
} else {
|
||||
unsigned long m = millis() & 0x7FFFFFF;
|
||||
if (waitRef == 0xFFFFFFFF) {
|
||||
waitRef = m + collectingTime;
|
||||
if (waitRef > 0x7FFFFFFF) {
|
||||
waitRef -= 0x80000000;
|
||||
}
|
||||
} else {
|
||||
if (!((waitRef < 0x20000000) && (m > 0x60000000)) && (m >= waitRef)) {
|
||||
sendBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (client.connected() && (pingRef != 0xFFFFFFFF)) {
|
||||
unsigned long m = millis() & 0x7FFFFFF;
|
||||
if (!((pingRef < 0x20000000) && (m > 0x60000000)) && (m >= pingRef)) {
|
||||
addTelnetBuf(0);
|
||||
sendBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,31 +3,32 @@
|
||||
*
|
||||
* Paul Derbyshire - December 2018
|
||||
*
|
||||
* Some ideas from https://github.com/JoaoLopesF/ESP8266-RemoteDebug-Telnet
|
||||
* Ideas from Espurna https://github.com/xoseperez/espurna
|
||||
* Ideas borrowed from Espurna https://github.com/xoseperez/espurna
|
||||
*/
|
||||
|
||||
#include "MyESP.h"
|
||||
|
||||
// constructor
|
||||
MyESP::MyESP() {
|
||||
_app_hostname = strdup("MyESP");
|
||||
_app_name = strdup("MyESP");
|
||||
_app_version = strdup("1.0.0");
|
||||
_boottime = strdup("unknown");
|
||||
_extern_WIFICallback = NULL;
|
||||
_extern_WIFICallbackSet = false;
|
||||
_consoleCallbackProjectCmds = NULL;
|
||||
_helpProjectCmds = NULL;
|
||||
_helpProjectCmds_count = 0;
|
||||
_mqtt_host = NULL;
|
||||
_mqtt_password = NULL;
|
||||
_mqtt_username = NULL;
|
||||
_wifi_password = NULL;
|
||||
_wifi_ssid = NULL;
|
||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
||||
_suspendMessages = true;
|
||||
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
||||
_app_hostname = strdup("MyESP");
|
||||
_app_name = strdup("MyESP");
|
||||
_app_version = strdup("1.0.0");
|
||||
_boottime = strdup("unknown");
|
||||
_extern_WIFICallback = NULL;
|
||||
_extern_WIFICallbackSet = false;
|
||||
_telnetcommand_callback = NULL;
|
||||
_telnet_callback = NULL;
|
||||
_helpProjectCmds = NULL;
|
||||
_helpProjectCmds_count = 0;
|
||||
_mqtt_host = NULL;
|
||||
_mqtt_password = NULL;
|
||||
_mqtt_username = NULL;
|
||||
_wifi_password = NULL;
|
||||
_wifi_ssid = NULL;
|
||||
_mqttbase = NULL;
|
||||
_suspendOutput = false;
|
||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
||||
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
||||
}
|
||||
|
||||
MyESP::~MyESP() {
|
||||
@@ -41,10 +42,9 @@ void MyESP::end() {
|
||||
jw.disconnect();
|
||||
}
|
||||
|
||||
|
||||
// general debug to the telnet or serial channels
|
||||
void MyESP::myDebug(const char * format, ...) {
|
||||
if (!_suspendMessages)
|
||||
if (_suspendOutput)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
@@ -62,6 +62,9 @@ void MyESP::myDebug(const char * format, ...) {
|
||||
|
||||
// for flashmemory. Must use PSTR()
|
||||
void MyESP::myDebug_P(PGM_P format_P, ...) {
|
||||
if (_suspendOutput)
|
||||
return;
|
||||
|
||||
char format[strlen_P(format_P) + 1];
|
||||
memcpy_P(format, format_P, sizeof(format));
|
||||
|
||||
@@ -85,7 +88,7 @@ void MyESP::myDebug_P(PGM_P format_P, ...) {
|
||||
|
||||
// called when WiFi is connected, and used to start MDNS
|
||||
void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
if ((code == MESSAGE_CONNECTED) || (code == MESSAGE_ACCESSPOINT_CREATED)) {
|
||||
if ((code == MESSAGE_CONNECTED)) {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
String hostname = String(WiFi.getHostname());
|
||||
#else
|
||||
@@ -102,13 +105,6 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
myDebug_P(PSTR("[WIFI] DNS %s"), WiFi.dnsIP().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] HOST %s"), hostname.c_str());
|
||||
|
||||
if (WiFi.getMode() & WIFI_AP) {
|
||||
myDebug_P(PSTR("[WIFI] MODE AP --------------------------------------"));
|
||||
myDebug_P(PSTR("[WIFI] SSID %s"), jw.getAPSSID().c_str());
|
||||
myDebug_P(PSTR("[WIFI] IP %s"), WiFi.softAPIP().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] MAC %s"), WiFi.softAPmacAddress().c_str());
|
||||
}
|
||||
|
||||
// start MDNS
|
||||
if (MDNS.begin((char *)hostname.c_str())) {
|
||||
myDebug_P(PSTR("[MDNS] OK"));
|
||||
@@ -118,7 +114,18 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
|
||||
// call any final custom settings
|
||||
if (_extern_WIFICallbackSet) {
|
||||
// myDebug_P(PSTR("[WIFI] calling custom wifi settings function"));
|
||||
_extern_WIFICallback(); // call callback to set any custom things
|
||||
}
|
||||
}
|
||||
|
||||
if (code == MESSAGE_ACCESSPOINT_CREATED) {
|
||||
myDebug_P(PSTR("[WIFI] MODE AP --------------------------------------"));
|
||||
myDebug_P(PSTR("[WIFI] SSID %s"), jw.getAPSSID().c_str());
|
||||
myDebug_P(PSTR("[WIFI] IP %s"), WiFi.softAPIP().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] MAC %s"), WiFi.softAPmacAddress().c_str());
|
||||
|
||||
// call any final custom settings
|
||||
if (_extern_WIFICallbackSet) {
|
||||
_extern_WIFICallback(); // call callback to set any custom things
|
||||
}
|
||||
}
|
||||
@@ -160,9 +167,9 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
||||
topic = topic_magnitude + 1;
|
||||
}
|
||||
|
||||
// check for bootime, something specific I fetch as an acknolwegdemtn from Home Assistant
|
||||
// check for bootime, something specific I fetch as an acknowledgement from Home Assistant
|
||||
if (strcmp(topic, MQTT_TOPIC_START) == 0) {
|
||||
myDebug_P(PSTR("[MQTT] boottime: %s"), message);
|
||||
myDebug_P(PSTR("[MQTT] received boottime: %s"), message);
|
||||
setBoottime(message);
|
||||
return;
|
||||
}
|
||||
@@ -172,20 +179,22 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
||||
}
|
||||
|
||||
// MQTT subscribe
|
||||
// to MQTT_BASE/app_hostname/topic
|
||||
void MyESP::mqttSubscribe(const char * topic) {
|
||||
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
||||
char s[100];
|
||||
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, topic);
|
||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
||||
unsigned int packetId = mqttClient.subscribe(s, MQTT_QOS);
|
||||
myDebug_P(PSTR("[MQTT] Subscribing to %s (PID %d)"), s, packetId);
|
||||
}
|
||||
}
|
||||
|
||||
// MQTT unsubscribe
|
||||
// to MQTT_BASE/app_hostname/topic
|
||||
void MyESP::mqttUnsubscribe(const char * topic) {
|
||||
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
||||
char s[100];
|
||||
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, topic);
|
||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
||||
unsigned int packetId = mqttClient.unsubscribe(s);
|
||||
myDebug_P(PSTR("[MQTT] Unsubscribing to %s (PID %d)"), s, packetId);
|
||||
}
|
||||
@@ -194,7 +203,7 @@ void MyESP::mqttUnsubscribe(const char * topic) {
|
||||
// MQTT Publish
|
||||
void MyESP::mqttPublish(const char * topic, const char * payload) {
|
||||
char s[MQTT_MAX_SIZE];
|
||||
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, topic);
|
||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
||||
// myDebug_P(PSTR("[MQTT] Sending pubish to %s with payload %s"), s, payload);
|
||||
mqttClient.publish(s, MQTT_QOS, false, payload);
|
||||
}
|
||||
@@ -211,11 +220,11 @@ void MyESP::_mqttOnConnect() {
|
||||
|
||||
// send specific start command to HA via MQTT, which returns the boottime
|
||||
char s[48];
|
||||
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, MQTT_TOPIC_START);
|
||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, MQTT_TOPIC_START);
|
||||
mqttClient.publish(s, MQTT_QOS, false, MQTT_TOPIC_START_PAYLOAD);
|
||||
#endif
|
||||
|
||||
// call custom
|
||||
// call custom function to handle mqtt receives
|
||||
(_mqtt_callback)(MQTT_CONNECT_EVENT, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -265,7 +274,7 @@ void MyESP::_wifi_setup() {
|
||||
jw.enableAP(false);
|
||||
jw.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
|
||||
jw.setReconnectTimeout(WIFI_RECONNECT_INTERVAL);
|
||||
jw.enableAPFallback(true); // AP mode only as fallback, but disabled
|
||||
jw.enableAPFallback(true); // AP mode only as fallback
|
||||
jw.enableSTA(true); // Enable STA mode (connecting to a router)
|
||||
jw.enableScan(false); // Configure it to scan available networks and connect in order of dBm
|
||||
jw.cleanNetworks(); // Clean existing network configuration
|
||||
@@ -299,6 +308,10 @@ void MyESP::_mdns_setup() {
|
||||
|
||||
// OTA Setup
|
||||
void MyESP::_ota_setup() {
|
||||
if (!_wifi_ssid) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArduinoOTA.setPort(OTA_PORT);
|
||||
ArduinoOTA.setHostname(_app_hostname);
|
||||
ArduinoOTA.onStart([this]() { myDebug_P(PSTR("[OTA] Start")); });
|
||||
@@ -336,25 +349,38 @@ void MyESP::setBoottime(char * boottime) {
|
||||
_boottime = strdup(boottime);
|
||||
}
|
||||
|
||||
// returns boottime
|
||||
char * MyESP::getBoottime() {
|
||||
return _boottime;
|
||||
// sets boottime
|
||||
void MyESP::setMQTTbase(char * mqttbase) {
|
||||
if (_mqttbase) {
|
||||
free(_mqttbase);
|
||||
}
|
||||
_mqttbase = strdup(mqttbase);
|
||||
}
|
||||
|
||||
// Set callback of sketch function to process project messages
|
||||
void MyESP::consoleSetCallBackProjectCmds(command_t * cmds, uint8_t count, void (*callback)()) {
|
||||
_helpProjectCmds = cmds; // command list
|
||||
_helpProjectCmds_count = count; // number of commands
|
||||
_consoleCallbackProjectCmds = callback; // external function to handle commands
|
||||
void MyESP::setTelnetCommands(command_t * cmds, uint8_t count, telnetcommand_callback_f callback) {
|
||||
_helpProjectCmds = cmds; // command list
|
||||
_helpProjectCmds_count = count; // number of commands
|
||||
_telnetcommand_callback = callback; // external function to handle commands
|
||||
}
|
||||
|
||||
void MyESP::setTelnetCallback(telnet_callback_f callback) {
|
||||
_telnet_callback = callback;
|
||||
}
|
||||
|
||||
void MyESP::_telnetConnected() {
|
||||
myDebug_P(PSTR("[TELNET] Telnet connection established"));
|
||||
_consoleShowHelp(); // Show the initial message
|
||||
if (_telnet_callback) {
|
||||
(_telnet_callback)(TELNET_EVENT_CONNECT); // call callback
|
||||
}
|
||||
}
|
||||
|
||||
void MyESP::_telnetDisconnected() {
|
||||
myDebug_P(PSTR("[TELNET] Telnet connection closed"));
|
||||
if (_telnet_callback) {
|
||||
(_telnet_callback)(TELNET_EVENT_DISCONNECT); // call callback
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the telnet server
|
||||
@@ -375,37 +401,48 @@ void MyESP::_telnet_setup() {
|
||||
|
||||
// Show help of commands
|
||||
void MyESP::_consoleShowHelp() {
|
||||
SerialAndTelnet.printf("\n\r* Connected to: %s version %s\n\r", _app_name, _app_version);
|
||||
|
||||
if (WiFi.getMode() & WIFI_AP) {
|
||||
SerialAndTelnet.printf("* ESP8266 is in AP mode with SSID %s\n\r", jw.getAPSSID().c_str());
|
||||
} else {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
String hostname = String(WiFi.getHostname());
|
||||
String hostname = String(WiFi.getHostname());
|
||||
#else
|
||||
String hostname = WiFi.hostname();
|
||||
String hostname = WiFi.hostname();
|
||||
#endif
|
||||
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s\n\r",
|
||||
hostname.c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.macAddress().c_str());
|
||||
SerialAndTelnet.printf("* Connected to WiFi SSID: %s\n\r", WiFi.SSID().c_str());
|
||||
SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime);
|
||||
}
|
||||
|
||||
|
||||
SerialAndTelnet.println("*********************************");
|
||||
SerialAndTelnet.println("* Console and Log Monitoring *");
|
||||
SerialAndTelnet.println("*********************************");
|
||||
SerialAndTelnet.printf("* %s version %s\n\r", _app_name, _app_version);
|
||||
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s\n\r",
|
||||
hostname.c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.macAddress().c_str());
|
||||
SerialAndTelnet.printf("* Connected to WiFi AP: %s\n\r", WiFi.SSID().c_str());
|
||||
SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime);
|
||||
SerialAndTelnet.printf("* Free RAM: %d bytes\n\r", ESP.getFreeHeap());
|
||||
#ifdef DEBUG_SUPPORT
|
||||
SerialAndTelnet.println("* !! in DEBUG_SUPPORT mode !!\n\r");
|
||||
SerialAndTelnet.println("* Warning: in DEBUG_SUPPORT mode!");
|
||||
#endif
|
||||
|
||||
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=this help, CTRL-D=quit, $=show free memory, !=reboot ESP, &=suspend all messages");
|
||||
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=help, CTRL-D=quit, !=reboot");
|
||||
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
|
||||
SerialAndTelnet.println(FPSTR("* set erase"));
|
||||
SerialAndTelnet.println(FPSTR("*"));
|
||||
|
||||
// print custom commands if available. Take from progmem
|
||||
if (_consoleCallbackProjectCmds) {
|
||||
if (_telnetcommand_callback) {
|
||||
// find the longest key length so we can right align it
|
||||
uint8_t max_len = 0;
|
||||
for (uint8_t i = 0; i < _helpProjectCmds_count; i++) {
|
||||
if (strlen(_helpProjectCmds[i].key) > max_len)
|
||||
max_len = strlen(_helpProjectCmds[i].key);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < _helpProjectCmds_count; i++) {
|
||||
SerialAndTelnet.print(FPSTR("* "));
|
||||
SerialAndTelnet.print(FPSTR(_helpProjectCmds[i].key));
|
||||
for (uint8_t j = 0; j < (8 - strlen(_helpProjectCmds[i].key)); j++) {
|
||||
SerialAndTelnet.print(FPSTR(" ")); // padding
|
||||
for (uint8_t j = 0; j < ((max_len + 5) - strlen(_helpProjectCmds[i].key)); j++) { // account for longest string length
|
||||
SerialAndTelnet.print(FPSTR(" ")); // padding
|
||||
}
|
||||
SerialAndTelnet.println(FPSTR(_helpProjectCmds[i].description));
|
||||
}
|
||||
@@ -424,90 +461,207 @@ void MyESP::resetESP() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get last command received
|
||||
char * MyESP::consoleGetLastCommand() {
|
||||
return _command;
|
||||
}
|
||||
|
||||
// Process user command over telnet
|
||||
void MyESP::consoleProcessCommand() {
|
||||
uint8_t cmd = _command[0];
|
||||
|
||||
// Process the command
|
||||
if (cmd == '?') {
|
||||
_consoleShowHelp(); // Show help
|
||||
} else if (cmd == '$') {
|
||||
myDebug("* Free RAM (bytes): %d", ESP.getFreeHeap());
|
||||
} else if (cmd == '!') {
|
||||
resetESP();
|
||||
} else if (cmd == '&') {
|
||||
myDebug("Suspend all messages is %s", !_suspendMessages ? "disabled" : "enabled");
|
||||
_suspendMessages = !_suspendMessages; // toggle
|
||||
} else {
|
||||
// custom Project commands
|
||||
if (_consoleCallbackProjectCmds) {
|
||||
_consoleCallbackProjectCmds();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_suspendMessages) {
|
||||
myDebug("Warning, all log messages have been supsended. Use & to re-enable.");
|
||||
}
|
||||
}
|
||||
|
||||
// sends a MQTT notification message to Home Assistant
|
||||
// sends a MQTT notification message to Home Assistant (HA)
|
||||
void MyESP::sendHANotification(const char * message) {
|
||||
char payload[48];
|
||||
snprintf(payload, sizeof(payload), "%s : %s", _app_hostname, message);
|
||||
myDebug_P(PSTR("[MQTT] Sending HA notification %s"), payload);
|
||||
mqttClient.publish(MQTT_NOTIFICATION, MQTT_QOS, false, payload);
|
||||
mqttClient.publish(MQTT_HA_NOTIFICATION, MQTT_QOS, false, payload);
|
||||
}
|
||||
|
||||
// send specific command to HA via MQTT
|
||||
// send specific command to Home Assistant (HA) via MQTT
|
||||
// format is: home/<hostname>/command with payload <cmd>
|
||||
void MyESP::sendHACommand(const char * cmd) {
|
||||
myDebug_P(PSTR("[MQTT] Sending HA command %s"), cmd);
|
||||
char topic[48];
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", MQTT_BASE, _app_hostname, MQTT_TOPIC_COMMAND);
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", _mqttbase, _app_hostname, MQTT_TOPIC_COMMAND);
|
||||
mqttClient.publish(topic, MQTT_QOS, false, cmd);
|
||||
}
|
||||
|
||||
char * MyESP::_telnet_readWord() {
|
||||
char * word = strtok(NULL, ", \n");
|
||||
return word;
|
||||
}
|
||||
|
||||
// change settings - always as strings
|
||||
// messy code but effective since we don't have too many settings
|
||||
void MyESP::_changeSetting(const char * setting, const char * value) {
|
||||
bool ok = false;
|
||||
|
||||
// validate 2nd argument
|
||||
if (strcmp(setting, "erase") == 0) {
|
||||
_fs_eraseConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "wifi_ssid") == 0) {
|
||||
if (_wifi_ssid)
|
||||
free(_wifi_ssid);
|
||||
_wifi_ssid = NULL; // just to be sure
|
||||
if (value) {
|
||||
_wifi_ssid = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "wifi_password") == 0) {
|
||||
if (_wifi_password)
|
||||
free(_wifi_password);
|
||||
_wifi_password = NULL; // just to be sure
|
||||
if (value) {
|
||||
_wifi_password = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "mqtt_host") == 0) {
|
||||
if (_mqtt_host)
|
||||
free(_mqtt_host);
|
||||
_mqtt_host = NULL; // just to be sure
|
||||
if (value) {
|
||||
_mqtt_host = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "mqtt_username") == 0) {
|
||||
if (_mqtt_username)
|
||||
free(_mqtt_username);
|
||||
_mqtt_username = NULL; // just to be sure
|
||||
if (value) {
|
||||
_mqtt_username = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "mqtt_password") == 0) {
|
||||
if (_mqtt_password)
|
||||
free(_mqtt_password);
|
||||
_mqtt_password = NULL; // just to be sure
|
||||
if (value) {
|
||||
_mqtt_password = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
SerialAndTelnet.println("\nInvalid parameter for set command.");
|
||||
return;
|
||||
}
|
||||
|
||||
// check for 2 params
|
||||
if (value == nullptr) {
|
||||
SerialAndTelnet.printf("%s setting deleted\n\r", setting);
|
||||
} else {
|
||||
// 3 params
|
||||
SerialAndTelnet.printf("%s changed to %s\n\r", setting, value);
|
||||
}
|
||||
|
||||
if (_fs_saveConfig()) {
|
||||
SerialAndTelnet.println("Changes will have effect after the next restart. Please reboot using ! command");
|
||||
}
|
||||
}
|
||||
|
||||
void MyESP::_telnetCommand(char * commandLine) {
|
||||
// count the number of arguments
|
||||
char * str = commandLine;
|
||||
bool state = false;
|
||||
unsigned wc = 0;
|
||||
while (*str) {
|
||||
if (*str == ' ' || *str == '\n' || *str == '\t') {
|
||||
state = false;
|
||||
} else if (state == false) {
|
||||
state = true;
|
||||
++wc;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
|
||||
// check first for reserved commands
|
||||
char * temp = strdup(commandLine); // because strotok kills original string buffer
|
||||
char * ptrToCommandName = strtok((char *)temp, ", \n");
|
||||
if (strcmp(ptrToCommandName, "set") == 0) {
|
||||
if (wc == 1) {
|
||||
SerialAndTelnet.println("\n\Stored settings:");
|
||||
SerialAndTelnet.printf(" wifi_ssid=%s\n\r", (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
|
||||
SerialAndTelnet.printf(" wifi_password=");
|
||||
if (!_wifi_password) {
|
||||
SerialAndTelnet.print("<not set>");
|
||||
} else {
|
||||
for (uint8_t i = 0; i < strlen(_wifi_password); i++)
|
||||
SerialAndTelnet.print("*");
|
||||
}
|
||||
SerialAndTelnet.printf("\n\r mqtt_host=%s\n\r", (!_mqtt_host) ? "<not set>" : _mqtt_host);
|
||||
SerialAndTelnet.printf(" mqtt_username=%s\n\r", (!_mqtt_username) ? "<not set>" : _mqtt_username);
|
||||
SerialAndTelnet.printf(" mqtt_password=");
|
||||
if (!_mqtt_password) {
|
||||
SerialAndTelnet.print("<not set>");
|
||||
} else {
|
||||
for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
|
||||
SerialAndTelnet.print("*");
|
||||
}
|
||||
SerialAndTelnet.println("\n\r\n\rUsage: set <setting> <value>");
|
||||
} else if (wc == 2) {
|
||||
char * setting = _telnet_readWord();
|
||||
_changeSetting(setting, NULL);
|
||||
} else if (wc == 3) {
|
||||
char * setting = _telnet_readWord();
|
||||
char * value = _telnet_readWord();
|
||||
_changeSetting(setting, value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// call callback function
|
||||
(_telnetcommand_callback)(wc, commandLine);
|
||||
}
|
||||
|
||||
// handler for Telnet
|
||||
void MyESP::_telnetHandle() {
|
||||
SerialAndTelnet.handle();
|
||||
|
||||
char last = ' '; // To avoid processing double "\r\n"
|
||||
|
||||
static uint8_t charsRead = 0;
|
||||
// read asynchronously until full command input
|
||||
while (SerialAndTelnet.available()) {
|
||||
char character = SerialAndTelnet.read(); // Get character
|
||||
|
||||
// check for ctrl-D (EOF) or EOT
|
||||
if ((character == 0xEC) || (character == 0x04)) {
|
||||
SerialAndTelnet.disconnectClient();
|
||||
}
|
||||
|
||||
// if we reached our buffer limit, send what we have
|
||||
if (strlen(_command) >= TELNET_MAX_COMMAND_LENGTH) {
|
||||
consoleProcessCommand(); // Process the command
|
||||
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH); // reset for next command
|
||||
}
|
||||
|
||||
// Check for newline (CR or LF)
|
||||
if (_isCRLF(character) == true) {
|
||||
if (_isCRLF(last) == false) {
|
||||
if (strlen(_command) > 0) {
|
||||
consoleProcessCommand(); // Process the command
|
||||
}
|
||||
char c = SerialAndTelnet.read();
|
||||
#ifdef DEBUG_SUPPORT
|
||||
Serial.print(c);
|
||||
#endif
|
||||
switch (c) {
|
||||
case '\r': // likely have full command in buffer now, commands are terminated by CR and/or LF
|
||||
case '\n':
|
||||
_command[charsRead] = '\0'; // null terminate our command char array
|
||||
if (charsRead > 0) {
|
||||
charsRead = 0; // is static, so have to reset
|
||||
_suspendOutput = false;
|
||||
_telnetCommand(_command);
|
||||
}
|
||||
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH); // reset for next command
|
||||
|
||||
} else if (isPrintable(character)) {
|
||||
// Concat char to end of buffer
|
||||
uint16_t len = strlen(_command);
|
||||
_command[len] = character;
|
||||
_command[len + 1] = '\0';
|
||||
break;
|
||||
case '\b': // handle backspace in input: put a space in last char
|
||||
if (charsRead > 0) { // and adjust commandLine and charsRead
|
||||
_command[--charsRead] = '\0';
|
||||
SerialAndTelnet << byte('\b') << byte(' ') << byte('\b'); //no idea how this works, found it on the Internet
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
_consoleShowHelp();
|
||||
break;
|
||||
case '!':
|
||||
resetESP();
|
||||
break;
|
||||
case 0x04: // EOT
|
||||
myDebug_P(PSTR("* exiting telnet session"));
|
||||
SerialAndTelnet.disconnectClient();
|
||||
break;
|
||||
default:
|
||||
_suspendOutput = true;
|
||||
c = tolower(c);
|
||||
if (charsRead < TELNET_MAX_COMMAND_LENGTH) {
|
||||
_command[charsRead++] = c;
|
||||
}
|
||||
_command[charsRead] = '\0'; // just in case
|
||||
break;
|
||||
}
|
||||
last = character; // remember last char
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,11 +676,6 @@ void MyESP::setMQTTCallback(mqtt_callback_f callback) {
|
||||
_mqtt_callback = callback;
|
||||
}
|
||||
|
||||
// Is CR or LF ?
|
||||
bool MyESP::_isCRLF(char character) {
|
||||
return (character == '\r' || character == '\n');
|
||||
}
|
||||
|
||||
// ensure we have a connection to MQTT broker
|
||||
void MyESP::_mqttConnect() {
|
||||
if (!_mqtt_host || mqttClient.connected() || (WiFi.status() != WL_CONNECTED)) {
|
||||
@@ -560,19 +709,7 @@ void MyESP::_mqttConnect() {
|
||||
}
|
||||
|
||||
// Setup everything we need
|
||||
void MyESP::setup(char * app_hostname,
|
||||
char * app_name,
|
||||
char * app_version,
|
||||
char * wifi_ssid,
|
||||
char * wifi_password,
|
||||
char * mqtt_host,
|
||||
char * mqtt_username,
|
||||
char * mqtt_password) {
|
||||
// get general params first
|
||||
_app_hostname = strdup(app_hostname);
|
||||
_app_name = strdup(app_name);
|
||||
_app_version = strdup(app_version);
|
||||
|
||||
void MyESP::setConnection(char * wifi_ssid, char * wifi_password, char * mqtt_host, char * mqtt_username, char * mqtt_password) {
|
||||
// Check SSID too long or missing
|
||||
if (!wifi_ssid || *wifi_ssid == 0x00 || strlen(wifi_ssid) > 31) {
|
||||
_wifi_ssid = NULL;
|
||||
@@ -581,7 +718,7 @@ void MyESP::setup(char * app_hostname,
|
||||
}
|
||||
|
||||
// Check PASS too long
|
||||
if (wifi_password && strlen(wifi_password) > 63) {
|
||||
if (!wifi_password || *wifi_ssid == 0x00 || strlen(wifi_password) > 31) {
|
||||
_wifi_password = NULL;
|
||||
} else {
|
||||
_wifi_password = strdup(wifi_password);
|
||||
@@ -607,9 +744,120 @@ void MyESP::setup(char * app_hostname,
|
||||
} else {
|
||||
_mqtt_password = strdup(mqtt_password);
|
||||
}
|
||||
}
|
||||
|
||||
// print contents of file
|
||||
void MyESP::_fs_printConfig() {
|
||||
File configFile = SPIFFS.open("/config.json", "r");
|
||||
|
||||
myDebug_P(PSTR("[FS] Contents...."));
|
||||
|
||||
while (configFile.available()) {
|
||||
SerialAndTelnet.print((char)configFile.read());
|
||||
}
|
||||
SerialAndTelnet.println();
|
||||
configFile.close();
|
||||
}
|
||||
|
||||
// format File System
|
||||
void MyESP::_fs_eraseConfig() {
|
||||
myDebug_P(PSTR("[FS] Erasing settings. Please wait. ESP will automatically restart when finished."));
|
||||
|
||||
if (SPIFFS.format()) {
|
||||
resetESP();
|
||||
}
|
||||
}
|
||||
|
||||
// load from spiffs
|
||||
bool MyESP::_fs_loadConfig() {
|
||||
File configFile = SPIFFS.open("/config.json", "r");
|
||||
if (!configFile) {
|
||||
myDebug_P(PSTR("[FS] Failed to open config file"));
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size = configFile.size();
|
||||
if (size > 1024) {
|
||||
myDebug_P(PSTR("[FS] Config file size is too large"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// assign buffer
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
|
||||
// use configFile.readString
|
||||
configFile.readBytes(buf.get(), size);
|
||||
|
||||
StaticJsonBuffer<300> jsonBuffer; // https://arduinojson.org/v5/assistant/
|
||||
JsonObject & json = jsonBuffer.parseObject(buf.get());
|
||||
|
||||
const char * value;
|
||||
|
||||
value = json["wifi_ssid"];
|
||||
_wifi_ssid = (value) ? strdup(value) : NULL;
|
||||
|
||||
value = json["wifi_password"];
|
||||
_wifi_password = (value) ? strdup(value) : NULL;
|
||||
|
||||
value = json["mqtt_host"];
|
||||
_mqtt_host = (value) ? strdup(value) : NULL;
|
||||
|
||||
value = json["mqtt_username"];
|
||||
_mqtt_username = (value) ? strdup(value) : NULL;
|
||||
|
||||
value = json["mqtt_password"];
|
||||
_mqtt_password = (value) ? strdup(value) : NULL;
|
||||
|
||||
configFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// save settings to spiffs
|
||||
bool MyESP::_fs_saveConfig() {
|
||||
StaticJsonBuffer<200> jsonBuffer;
|
||||
JsonObject & json = jsonBuffer.createObject();
|
||||
|
||||
json["wifi_ssid"] = _wifi_ssid;
|
||||
json["wifi_password"] = _wifi_password;
|
||||
json["mqtt_host"] = _mqtt_host;
|
||||
json["mqtt_username"] = _mqtt_username;
|
||||
json["mqtt_password"] = _mqtt_password;
|
||||
|
||||
File configFile = SPIFFS.open("/config.json", "w");
|
||||
if (!configFile) {
|
||||
myDebug_P(PSTR("[FS] Failed to open config file for writing"));
|
||||
return false;
|
||||
}
|
||||
|
||||
json.printTo(configFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// init the SPIFF file system and load the config
|
||||
// if it doesn't exist try and create it
|
||||
void MyESP::_fs_setup() {
|
||||
if (!SPIFFS.begin()) {
|
||||
myDebug_P(PSTR("[FS] Failed to mount the file system"));
|
||||
return;
|
||||
}
|
||||
|
||||
// load the config file. if it doesn't exist create it with anything that was specified
|
||||
if (!_fs_loadConfig()) {
|
||||
_fs_saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// register new instance
|
||||
void MyESP::begin(char * app_hostname, char * app_name, char * app_version) {
|
||||
_app_hostname = strdup(app_hostname);
|
||||
_app_name = strdup(app_name);
|
||||
_app_version = strdup(app_version);
|
||||
|
||||
// call setup of the services...
|
||||
_telnet_setup(); // Telnet setup
|
||||
_fs_setup(); // SPIFFS setup
|
||||
_wifi_setup(); // WIFI setup
|
||||
_mqtt_setup(); // MQTT Setup
|
||||
_mdns_setup(); // MDNS setup
|
||||
@@ -620,8 +868,13 @@ void MyESP::setup(char * app_hostname,
|
||||
* Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc
|
||||
*/
|
||||
void MyESP::loop() {
|
||||
jw.loop(); // WiFi
|
||||
_telnetHandle(); // Telnet/Debugger
|
||||
jw.loop(); // WiFi
|
||||
_telnetHandle(); // Telnet/Debugger
|
||||
|
||||
if (WiFi.getMode() & WIFI_AP) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArduinoOTA.handle(); // OTA
|
||||
_mqttConnect(); // MQTT
|
||||
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
#include <AsyncMqttClient.h> // https://github.com/marvinroger/async-mqtt-client
|
||||
#include <DNSServer.h>
|
||||
#include <ESPAsyncTCP.h> // https://github.com/me-no-dev/ESPAsyncTCP
|
||||
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
||||
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
||||
#include <FS.h>
|
||||
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
||||
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
||||
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include <ESPmDNS.h>
|
||||
@@ -31,12 +33,12 @@
|
||||
#define OTA_PORT 8266 // OTA port
|
||||
|
||||
// MQTT
|
||||
#define MQTT_BASE "home/"
|
||||
#define MQTT_NOTIFICATION MQTT_BASE "notification"
|
||||
#define MQTT_TOPIC_COMMAND "command"
|
||||
#define MQTT_TOPIC_START "start"
|
||||
#define MQTT_TOPIC_START_PAYLOAD "start"
|
||||
#define MQTT_HA MQTT_BASE "ha"
|
||||
#define MQTT_HA "/home/ha" // HA specific
|
||||
#define MQTT_HA_NOTIFICATION "home/notification" // HA specific
|
||||
#define MQTT_TOPIC_COMMAND "command" // HA specific
|
||||
#define MQTT_TOPIC_START "start" // HA specific
|
||||
#define MQTT_TOPIC_START_PAYLOAD "start" // HA specific
|
||||
|
||||
#define MQTT_PORT 1883 // MQTT port
|
||||
#define MQTT_QOS 1
|
||||
#define MQTT_RECONNECT_DELAY_MIN 5000 // Try to reconnect in 5 seconds upon disconnection
|
||||
@@ -50,6 +52,8 @@
|
||||
|
||||
// Telnet
|
||||
#define TELNET_MAX_COMMAND_LENGTH 80 // length of a command
|
||||
#define TELNET_EVENT_CONNECT 1
|
||||
#define TELNET_EVENT_DISCONNECT 0
|
||||
#define COLOR_RESET "\x1B[0m"
|
||||
#define COLOR_BLACK "\x1B[0;30m"
|
||||
#define COLOR_RED "\x1B[0;31m"
|
||||
@@ -61,12 +65,16 @@
|
||||
#define COLOR_WHITE "\x1B[0;37m"
|
||||
|
||||
typedef struct {
|
||||
char key[10];
|
||||
char description[400];
|
||||
char key[30];
|
||||
char description[100];
|
||||
} command_t;
|
||||
|
||||
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
|
||||
|
||||
typedef std::function<void(uint8_t, const char *)> telnetcommand_callback_f;
|
||||
|
||||
typedef std::function<void(uint8_t)> telnet_callback_f;
|
||||
|
||||
// calculates size of an 2d array at compile time
|
||||
template <typename T, size_t N>
|
||||
constexpr size_t ArraySize(T (&)[N]) {
|
||||
@@ -81,7 +89,6 @@ class MyESP {
|
||||
|
||||
// wifi
|
||||
void setWIFICallback(void (*callback)());
|
||||
void setMQTTCallback(mqtt_callback_f callback);
|
||||
|
||||
// ha
|
||||
void sendHACommand(const char * cmd);
|
||||
@@ -91,28 +98,22 @@ class MyESP {
|
||||
void mqttSubscribe(const char * topic);
|
||||
void mqttUnsubscribe(const char * topic);
|
||||
void mqttPublish(const char * topic, const char * payload);
|
||||
void setMQTTbase(char * mqttbase);
|
||||
void setMQTTCallback(mqtt_callback_f callback);
|
||||
|
||||
// debug & telnet
|
||||
void myDebug(const char * format, ...);
|
||||
void myDebug_P(PGM_P format_P, ...);
|
||||
void consoleSetCallBackProjectCmds(command_t * cmds, uint8_t count, void (*callback)());
|
||||
char * consoleGetLastCommand();
|
||||
void consoleProcessCommand();
|
||||
void myDebug(const char * format, ...);
|
||||
void myDebug_P(PGM_P format_P, ...);
|
||||
void setTelnetCommands(command_t * cmds, uint8_t count, telnetcommand_callback_f callback);
|
||||
void setTelnetCallback(telnet_callback_f callback);
|
||||
|
||||
// general
|
||||
void end();
|
||||
void loop();
|
||||
void setup(char * app_hostname,
|
||||
char * app_name,
|
||||
char * app_version,
|
||||
char * wifi_ssid,
|
||||
char * wifi_password,
|
||||
char * mqtt_host,
|
||||
char * mqtt_username,
|
||||
char * mqtt_password);
|
||||
|
||||
char * getBoottime();
|
||||
void setBoottime(char * boottime);
|
||||
void resetESP();
|
||||
void begin(char * app_hostname, char * app_name, char * app_version);
|
||||
void setConnection(char * wifi_ssid, char * wifi_password, char * mqtt_host, char * mqtt_username, char * mqtt_password);
|
||||
void setBoottime(char * boottime);
|
||||
void resetESP();
|
||||
|
||||
private:
|
||||
// mqtt
|
||||
@@ -128,6 +129,8 @@ class MyESP {
|
||||
char * _mqtt_username;
|
||||
char * _mqtt_password;
|
||||
char * _boottime;
|
||||
bool _suspendOutput;
|
||||
char * _mqttbase;
|
||||
|
||||
// wifi
|
||||
DNSServer dnsServer; // For Access Point (AP) support
|
||||
@@ -145,19 +148,27 @@ class MyESP {
|
||||
void _ota_setup();
|
||||
|
||||
// telnet & debug
|
||||
TelnetSpy SerialAndTelnet;
|
||||
void _telnetConnected();
|
||||
void _telnetDisconnected();
|
||||
void _telnetHandle();
|
||||
void _telnet_setup();
|
||||
char * _command; // the input command from either Serial or Telnet
|
||||
command_t * _helpProjectCmds; // Help of commands setted by project
|
||||
uint8_t _helpProjectCmds_count; // # available commands
|
||||
void _consoleShowHelp();
|
||||
void (*_consoleCallbackProjectCmds)(); // Callable for projects commands
|
||||
void _consoleProcessCommand();
|
||||
bool _isCRLF(char character);
|
||||
bool _suspendMessages;
|
||||
TelnetSpy SerialAndTelnet;
|
||||
void _telnetConnected();
|
||||
void _telnetDisconnected();
|
||||
void _telnetHandle();
|
||||
void _telnetCommand(char * commandLine);
|
||||
char * _telnet_readWord();
|
||||
void _telnet_setup();
|
||||
char * _command; // the input command from either Serial or Telnet
|
||||
command_t * _helpProjectCmds; // Help of commands setted by project
|
||||
uint8_t _helpProjectCmds_count; // # available commands
|
||||
void _consoleShowHelp();
|
||||
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
||||
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
||||
void _changeSetting(const char * setting, const char * value);
|
||||
|
||||
// fs
|
||||
void _fs_setup();
|
||||
bool _fs_saveConfig();
|
||||
bool _fs_loadConfig();
|
||||
void _fs_printConfig();
|
||||
void _fs_eraseConfig();
|
||||
|
||||
// general
|
||||
char * _app_hostname;
|
||||
|
||||
Reference in New Issue
Block a user