mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 09:19:51 +03:00
first commit
This commit is contained in:
915
lib/ESPHelper/ESPHelper.cpp
Normal file
915
lib/ESPHelper/ESPHelper.cpp
Normal file
@@ -0,0 +1,915 @@
|
||||
/*
|
||||
Based off :
|
||||
1) ESPHelper.cpp - Copyright (c) 2017 ItKindaWorks Inc All right reserved. github.com/ItKindaWorks
|
||||
2) https://github.com/JoaoLopesF/ESP8266-RemoteDebug-Telnet
|
||||
|
||||
*/
|
||||
|
||||
#include "ESPHelper.h"
|
||||
|
||||
WiFiServer telnetServer(TELNET_PORT);
|
||||
|
||||
//initializer with single netInfo network
|
||||
ESPHelper::ESPHelper(netInfo * startingNet) {
|
||||
//disconnect from and previous wifi networks
|
||||
WiFi.softAPdisconnect();
|
||||
WiFi.disconnect();
|
||||
|
||||
//setup current network information
|
||||
_currentNet = *startingNet;
|
||||
|
||||
//validate various bits of network/MQTT info
|
||||
|
||||
//network pass
|
||||
if (_currentNet.pass[0] == '\0') {
|
||||
_passSet = false;
|
||||
} else {
|
||||
_passSet = true;
|
||||
}
|
||||
|
||||
//ssid
|
||||
if (_currentNet.ssid[0] == '\0') {
|
||||
_ssidSet = false;
|
||||
} else {
|
||||
_ssidSet = true;
|
||||
}
|
||||
|
||||
//mqtt host
|
||||
if (_currentNet.mqttHost[0] == '\0') {
|
||||
_mqttSet = false;
|
||||
} else {
|
||||
_mqttSet = true;
|
||||
}
|
||||
|
||||
//mqtt port
|
||||
if (_currentNet.mqttPort == 0) {
|
||||
_currentNet.mqttPort = 1883;
|
||||
}
|
||||
|
||||
//mqtt username
|
||||
if (_currentNet.mqttUser[0] == '\0') {
|
||||
_mqttUserSet = false;
|
||||
} else {
|
||||
_mqttUserSet = true;
|
||||
}
|
||||
|
||||
//mqtt password
|
||||
if (_currentNet.mqttPass[0] == '\0') {
|
||||
_mqttPassSet = false;
|
||||
} else {
|
||||
_mqttPassSet = true;
|
||||
}
|
||||
|
||||
//disable hopping on single network
|
||||
_hoppingAllowed = false;
|
||||
|
||||
//disable ota by default
|
||||
_useOTA = false;
|
||||
}
|
||||
|
||||
//start the wifi & mqtt systems and attempt connection (currently blocking)
|
||||
//true on: parameter check validated
|
||||
//false on: parameter check failed
|
||||
bool ESPHelper::begin(const char * hostname) {
|
||||
#ifdef USE_SERIAL1
|
||||
Serial1.begin(115200);
|
||||
Serial1.setDebugOutput(true);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SERIAL
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
#endif
|
||||
|
||||
// set hostname first
|
||||
strcpy(_hostname, hostname);
|
||||
OTA_enable();
|
||||
|
||||
setBoottime("<unknown>");
|
||||
|
||||
if (_ssidSet) {
|
||||
strcpy(_clientName, hostname);
|
||||
|
||||
/*
|
||||
// Generate client name based on MAC address and last 8 bits of microsecond counter
|
||||
|
||||
_clientName += "esp-";
|
||||
uint8_t mac[6];
|
||||
WiFi.macAddress(mac);
|
||||
_clientName += macToStr(mac);
|
||||
*/
|
||||
|
||||
// set hostname
|
||||
// can ping by <hostname> or on Windows10 it's <hostname>.
|
||||
#if defined(ESP8266)
|
||||
WiFi.hostname(_hostname);
|
||||
#elif defined(ESP32)
|
||||
WiFi.setHostname(_hostname);
|
||||
#endif
|
||||
|
||||
//set the wifi mode to station and begin the wifi (connect using either ssid or ssid/pass)
|
||||
WiFi.mode(WIFI_STA);
|
||||
if (_passSet) {
|
||||
WiFi.begin(_currentNet.ssid, _currentNet.pass);
|
||||
} else {
|
||||
WiFi.begin(_currentNet.ssid);
|
||||
}
|
||||
|
||||
//as long as an mqtt ip has been set create an instance of PubSub for client
|
||||
if (_mqttSet) {
|
||||
//make mqtt client use either the secure or non-secure wifi client depending on the setting
|
||||
if (_useSecureClient) {
|
||||
client = PubSubClient(_currentNet.mqttHost,
|
||||
_currentNet.mqttPort,
|
||||
wifiClientSecure);
|
||||
} else {
|
||||
client = PubSubClient(_currentNet.mqttHost,
|
||||
_currentNet.mqttPort,
|
||||
wifiClient);
|
||||
}
|
||||
|
||||
//set the mqtt message callback if needed
|
||||
if (_mqttCallbackSet) {
|
||||
client.setCallback(_mqttCallback);
|
||||
}
|
||||
}
|
||||
|
||||
//define a dummy instance of mqtt so that it is instantiated if no mqtt ip is set
|
||||
else {
|
||||
//make mqtt client use either the secure or non-secure wifi client depending on the setting
|
||||
//(this shouldnt be needed if making a dummy connection since the idea would be that there wont be mqtt in this case)
|
||||
if (_useSecureClient) {
|
||||
client = PubSubClient("192.168.1.255",
|
||||
_currentNet.mqttPort,
|
||||
wifiClientSecure);
|
||||
} else {
|
||||
client = PubSubClient("192.168.1.255",
|
||||
_currentNet.mqttPort,
|
||||
wifiClient);
|
||||
}
|
||||
}
|
||||
|
||||
//ota event handlers
|
||||
ArduinoOTA.onStart([]() { /* ota start code */ });
|
||||
ArduinoOTA.onEnd([]() {
|
||||
//on ota end we disconnect from wifi cleanly before restarting.
|
||||
WiFi.softAPdisconnect();
|
||||
WiFi.disconnect();
|
||||
uint8_t timeout = 0;
|
||||
//max timeout of 2seconds before just dropping out and restarting
|
||||
while (WiFi.status() != WL_DISCONNECTED && timeout < 200) {
|
||||
timeout++;
|
||||
}
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress,
|
||||
unsigned int total) { /* ota progress code */ });
|
||||
ArduinoOTA.onError([](ota_error_t error) { /* ota error code */ });
|
||||
|
||||
//initially attempt to connect to wifi when we begin (but only block for 2 seconds before timing out)
|
||||
uint8_t timeout = 0; //counter for begin connection attempts
|
||||
while (((!client.connected() && _mqttSet) || WiFi.status() != WL_CONNECTED)
|
||||
&& timeout < 200) { //max 2 sec before timeout
|
||||
reconnect();
|
||||
timeout++;
|
||||
}
|
||||
|
||||
//attempt to start ota if needed
|
||||
OTA_begin();
|
||||
|
||||
// Initialize the telnet server
|
||||
telnetServer.begin();
|
||||
telnetServer.setNoDelay(true);
|
||||
|
||||
// init command buffer for console commands
|
||||
memset(_command, 0, sizeof(_command));
|
||||
|
||||
consoleShowHelp(); // show this at bootup
|
||||
|
||||
//mark the system as started and return
|
||||
_hasBegun = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//if no ssid was set even then dont try to begin and return false
|
||||
return false;
|
||||
}
|
||||
|
||||
//end the instance of ESPHelper (shutdown wifi, ota, mqtt)
|
||||
void ESPHelper::end() {
|
||||
// Stop telnet Client & Server
|
||||
if (telnetClient && telnetClient.connected()) {
|
||||
telnetClient.stop();
|
||||
}
|
||||
|
||||
// Stop server
|
||||
telnetServer.stop();
|
||||
|
||||
OTA_disable();
|
||||
WiFi.softAPdisconnect();
|
||||
WiFi.disconnect();
|
||||
|
||||
uint8_t timeout = 0;
|
||||
while (WiFi.status() != WL_DISCONNECTED && timeout < 200) {
|
||||
timeout++;
|
||||
}
|
||||
|
||||
#ifdef USE_SERIAL
|
||||
Serial.flush();
|
||||
#endif
|
||||
|
||||
#ifdef USE_SERIAL1
|
||||
Serial1.flush();
|
||||
#endif
|
||||
}
|
||||
|
||||
//main loop - should be called as often as possible - handles wifi/mqtt connection and mqtt handler
|
||||
//true on: network/server connected
|
||||
//false on: network or server disconnected
|
||||
int ESPHelper::loop() {
|
||||
if (_ssidSet) {
|
||||
//check for good connections and attempt a reconnect if needed
|
||||
if (((_mqttSet && !client.connected()) || setConnectionStatus() < WIFI_ONLY)
|
||||
&& _connectionStatus != BROADCAST) {
|
||||
reconnect();
|
||||
}
|
||||
|
||||
//run the wifi loop as long as the connection status is at a minimum of BROADCAST
|
||||
if (_connectionStatus >= BROADCAST) {
|
||||
//run the MQTT loop if we have a full connection
|
||||
if (_connectionStatus == FULL_CONNECTION) {
|
||||
client.loop();
|
||||
}
|
||||
|
||||
//check for whether we want to use OTA and whether the system is running
|
||||
if (_useOTA && _OTArunning) {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
|
||||
//if we want to use OTA but its not running yet, start it up.
|
||||
else if (_useOTA && !_OTArunning) {
|
||||
OTA_begin();
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
|
||||
// do the telnet stuff
|
||||
consoleHandle();
|
||||
|
||||
return _connectionStatus;
|
||||
}
|
||||
}
|
||||
|
||||
//return -1 for no connection because of bad network info
|
||||
return -1;
|
||||
}
|
||||
|
||||
//subscribe to a specific topic (does not add to topic list)
|
||||
//true on: subscription success
|
||||
//false on: subscription failed (either from PubSub lib or network is disconnected)
|
||||
bool ESPHelper::subscribe(const char * topic, uint8_t qos) {
|
||||
if (_connectionStatus == FULL_CONNECTION) {
|
||||
//set the return value to the output of subscribe
|
||||
bool returnVal = client.subscribe(topic, qos);
|
||||
|
||||
//loop mqtt client
|
||||
client.loop();
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
//if not fully connected return false
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//add a topic to the list of subscriptions and attempt to subscribe to the topic on the spot
|
||||
//true on: subscription added to list (does not guarantee that the topic was subscribed to, only that it was added to the list)
|
||||
//false on: subscription not added to list
|
||||
bool ESPHelper::addSubscription(const char * topic) {
|
||||
//default return value is false
|
||||
bool subscribed = false;
|
||||
|
||||
//loop through finding the next available slot for a subscription and add it
|
||||
for (uint8_t i = 0; i < MAX_SUBSCRIPTIONS; i++) {
|
||||
if (_subscriptions[i].isUsed == false) {
|
||||
_subscriptions[i].topic = topic;
|
||||
_subscriptions[i].isUsed = true;
|
||||
subscribed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if added to the list, subscribe to the topic
|
||||
if (subscribed) {
|
||||
subscribe(topic, _qos);
|
||||
}
|
||||
|
||||
return subscribed;
|
||||
}
|
||||
|
||||
//loops through list of subscriptions and attempts to subscribe to all topics
|
||||
void ESPHelper::resubscribe() {
|
||||
for (uint8_t i = 0; i < MAX_SUBSCRIPTIONS; i++) {
|
||||
if (_subscriptions[i].isUsed) {
|
||||
subscribe(_subscriptions[i].topic, _qos);
|
||||
yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//manually unsubscribes from a topic (This is basically just a wrapper for the pubsubclient function)
|
||||
bool ESPHelper::unsubscribe(const char * topic) {
|
||||
return client.unsubscribe(topic);
|
||||
}
|
||||
|
||||
//publish to a specified topic
|
||||
void ESPHelper::publish(const char * topic, const char * payload) {
|
||||
if (_mqttSet) {
|
||||
publish(topic, payload, false);
|
||||
}
|
||||
}
|
||||
|
||||
//publish to a specified topic with a given retain level
|
||||
void ESPHelper::publish(const char * topic, const char * payload, bool retain) {
|
||||
client.publish(topic, payload, retain);
|
||||
}
|
||||
|
||||
//set the callback function for MQTT
|
||||
void ESPHelper::setMQTTCallback(MQTT_CALLBACK_SIGNATURE) {
|
||||
_mqttCallback = callback;
|
||||
|
||||
//only set the callback if using mqtt AND the system has already been started. Otherwise just save it for later
|
||||
if (_hasBegun && _mqttSet) {
|
||||
client.setCallback(_mqttCallback);
|
||||
}
|
||||
_mqttCallbackSet = true;
|
||||
}
|
||||
|
||||
//sets a custom function to run when connection to wifi is established
|
||||
void ESPHelper::setWifiCallback(void (*callback)()) {
|
||||
_wifiCallback = callback;
|
||||
_wifiCallbackSet = true;
|
||||
}
|
||||
|
||||
//attempts to connect to wifi & mqtt server if not connected
|
||||
void ESPHelper::reconnect() {
|
||||
static uint8_t tryCount = 0;
|
||||
|
||||
if (_connectionStatus != BROADCAST
|
||||
&& setConnectionStatus() != FULL_CONNECTION) {
|
||||
logger(LOG_CONSOLE, "Attempting WiFi Connection...");
|
||||
//attempt to connect to the wifi if connection is lost
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
_connectionStatus = NO_CONNECTION;
|
||||
|
||||
//increment try count each time it cannot connect (this is used to determine when to hop to a new network)
|
||||
tryCount++;
|
||||
if (tryCount == 20) {
|
||||
//change networks (if possible) when we have tried to connect 20 times and failed
|
||||
changeNetwork();
|
||||
tryCount = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we are connected to WIFI before attempting to reconnect to MQTT
|
||||
//----note---- maybe want to reset tryCount whenever we succeed at getting wifi connection?
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
//if the wifi previously wasnt connected but now is, run the callback
|
||||
if (_connectionStatus < WIFI_ONLY && _wifiCallbackSet) {
|
||||
_wifiCallback();
|
||||
}
|
||||
|
||||
logger(LOG_CONSOLE, "---WiFi Connected!---");
|
||||
_connectionStatus = WIFI_ONLY;
|
||||
|
||||
//attempt to connect to mqtt when we finally get connected to WiFi
|
||||
if (_mqttSet) {
|
||||
static uint8_t timeout =
|
||||
0; //allow a max of 5 mqtt connection attempts before timing out
|
||||
if (!client.connected() && timeout < 5) {
|
||||
logger(LOG_CONSOLE, "Attempting MQTT connection...");
|
||||
|
||||
uint8_t connected = 0;
|
||||
|
||||
//connect to mqtt with user/pass
|
||||
if (_mqttUserSet) {
|
||||
connected = client.connect(_clientName,
|
||||
_currentNet.mqttUser,
|
||||
_currentNet.mqttPass);
|
||||
}
|
||||
|
||||
//connect to mqtt without credentials
|
||||
else {
|
||||
connected = client.connect(_clientName);
|
||||
}
|
||||
|
||||
//if connected, subscribe to the topic(s) we want to be notified about
|
||||
if (connected) {
|
||||
logger(LOG_CONSOLE, " -- Connected");
|
||||
|
||||
//if using https, verify the fingerprint of the server before setting full connection (return on fail)
|
||||
// removing this as not supported with ESP32, see https://github.com/espressif/arduino-esp32/issues/278
|
||||
/*
|
||||
if (wifiClientSecure.verify(_fingerprint,
|
||||
_currentNet.mqttHost)) {
|
||||
logger(LOG_CONSOLE,
|
||||
"Certificate Matches - SUCESS\n");
|
||||
} else {
|
||||
logger(LOG_CONSOLE,
|
||||
"Certificate Doesn't Match - FAIL\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
_connectionStatus = FULL_CONNECTION;
|
||||
resubscribe();
|
||||
timeout = 0;
|
||||
} else {
|
||||
logger(LOG_CONSOLE, " -- Failed\n");
|
||||
}
|
||||
timeout++;
|
||||
}
|
||||
|
||||
//if we still cant connect to mqtt after 10 attempts increment the try count
|
||||
if (timeout >= 5 && !client.connected()) {
|
||||
timeout = 0;
|
||||
tryCount++;
|
||||
if (tryCount == 20) {
|
||||
changeNetwork();
|
||||
tryCount = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//reset the reconnect metro
|
||||
//reconnectMetro.reset();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ESPHelper::setConnectionStatus() {
|
||||
//assume no connection
|
||||
uint8_t returnVal = NO_CONNECTION;
|
||||
|
||||
//make sure were not in broadcast mode
|
||||
if (_connectionStatus != BROADCAST) {
|
||||
//if connected to wifi set the mode to wifi only and run the callback if needed
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
if (_connectionStatus < WIFI_ONLY
|
||||
&& _wifiCallbackSet) { //if the wifi previously wasn't connected but now is, run the callback
|
||||
_wifiCallback();
|
||||
}
|
||||
returnVal = WIFI_ONLY;
|
||||
|
||||
//if mqtt is connected as well then set the status to full connection
|
||||
if (client.connected()) {
|
||||
returnVal = FULL_CONNECTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
returnVal = BROADCAST;
|
||||
}
|
||||
|
||||
//set the connection status and return
|
||||
_connectionStatus = returnVal;
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
//changes the current network settings to the next listed network if network hopping is allowed
|
||||
void ESPHelper::changeNetwork() {
|
||||
//only attempt to change networks if hopping is allowed
|
||||
if (_hoppingAllowed) {
|
||||
//change the index/reset to 0 if we've hit the last network setting
|
||||
_currentIndex++;
|
||||
if (_currentIndex >= _netCount) {
|
||||
_currentIndex = 0;
|
||||
}
|
||||
|
||||
//set the current netlist to the new network
|
||||
_currentNet = *_netList[_currentIndex];
|
||||
|
||||
//verify various bits of network info
|
||||
|
||||
//network password
|
||||
if (_currentNet.pass[0] == '\0') {
|
||||
_passSet = false;
|
||||
} else {
|
||||
_passSet = true;
|
||||
}
|
||||
|
||||
//ssid
|
||||
if (_currentNet.ssid[0] == '\0') {
|
||||
_ssidSet = false;
|
||||
} else {
|
||||
_ssidSet = true;
|
||||
}
|
||||
|
||||
//mqtt host
|
||||
if (_currentNet.mqttHost[0] == '\0') {
|
||||
_mqttSet = false;
|
||||
} else {
|
||||
_mqttSet = true;
|
||||
}
|
||||
|
||||
//mqtt username
|
||||
if (_currentNet.mqttUser[0] == '\0') {
|
||||
_mqttUserSet = false;
|
||||
} else {
|
||||
_mqttUserSet = true;
|
||||
}
|
||||
|
||||
//mqtt password
|
||||
if (_currentNet.mqttPass[0] == '\0') {
|
||||
_mqttPassSet = false;
|
||||
} else {
|
||||
_mqttPassSet = true;
|
||||
}
|
||||
|
||||
printf("Trying next network: %s\n", _currentNet.ssid);
|
||||
|
||||
//update the network connection
|
||||
updateNetwork();
|
||||
}
|
||||
}
|
||||
|
||||
void ESPHelper::updateNetwork() {
|
||||
logger(LOG_CONSOLE, "\tDisconnecting from WiFi");
|
||||
WiFi.disconnect();
|
||||
logger(LOG_CONSOLE, "\tAttempting to begin on new network...");
|
||||
|
||||
//set the wifi mode
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
//connect to the network
|
||||
if (_passSet && _ssidSet) {
|
||||
WiFi.begin(_currentNet.ssid, _currentNet.pass);
|
||||
} else if (_ssidSet) {
|
||||
WiFi.begin(_currentNet.ssid);
|
||||
} else {
|
||||
WiFi.begin("NO_SSID_SET");
|
||||
}
|
||||
|
||||
logger(LOG_CONSOLE, "\tSetting new MQTT server");
|
||||
//setup the mqtt broker info
|
||||
if (_mqttSet) {
|
||||
client.setServer(_currentNet.mqttHost, _currentNet.mqttPort);
|
||||
} else {
|
||||
client.setServer("192.168.1.3", 1883);
|
||||
}
|
||||
|
||||
logger(LOG_CONSOLE, "\tDone - Ready for next reconnect attempt");
|
||||
}
|
||||
|
||||
//enable use of OTA updates
|
||||
void ESPHelper::OTA_enable() {
|
||||
_useOTA = true;
|
||||
ArduinoOTA.setHostname(_hostname);
|
||||
OTA_begin();
|
||||
}
|
||||
|
||||
//begin the OTA subsystem but with a check for connectivity and enabled use of OTA
|
||||
void ESPHelper::OTA_begin() {
|
||||
if (_connectionStatus >= BROADCAST && _useOTA) {
|
||||
ArduinoOTA.begin();
|
||||
_OTArunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
//disable use of OTA updates
|
||||
void ESPHelper::OTA_disable() {
|
||||
_useOTA = false;
|
||||
_OTArunning = false;
|
||||
}
|
||||
|
||||
// Is CR or LF ?
|
||||
bool ESPHelper::isCRLF(char character) {
|
||||
return (character == '\r' || character == '\n');
|
||||
}
|
||||
|
||||
// handler for Telnet
|
||||
void ESPHelper::consoleHandle() {
|
||||
// look for Client connect trial
|
||||
if (telnetServer.hasClient()) {
|
||||
if (telnetClient && telnetClient.connected()) {
|
||||
// Verify if the IP is same than actual connection
|
||||
WiFiClient newClient;
|
||||
newClient = telnetServer.available();
|
||||
String ip = newClient.remoteIP().toString();
|
||||
|
||||
if (ip == telnetClient.remoteIP().toString()) {
|
||||
// Reconnect
|
||||
telnetClient.stop();
|
||||
telnetClient = newClient;
|
||||
} else {
|
||||
// Desconnect (not allow more than one connection)
|
||||
newClient.stop();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// New TCP client
|
||||
telnetClient = telnetServer.available();
|
||||
}
|
||||
|
||||
if (!telnetClient) { // No client yet ???
|
||||
return;
|
||||
}
|
||||
|
||||
// Set client
|
||||
telnetClient.setNoDelay(true); // faster
|
||||
telnetClient.flush(); // clear input buffer, to prevent strange characters
|
||||
|
||||
_lastTimeCommand = millis(); // To mark time for inactivity
|
||||
|
||||
// Show the initial message
|
||||
consoleShowHelp();
|
||||
|
||||
// Empty buffer in
|
||||
while (telnetClient.available()) {
|
||||
telnetClient.read();
|
||||
}
|
||||
}
|
||||
|
||||
// Is client connected ? (to reduce overhead in active)
|
||||
_telnetConnected = (telnetClient && telnetClient.connected());
|
||||
|
||||
// Get command over telnet
|
||||
if (_telnetConnected) {
|
||||
char last = ' '; // To avoid process two times the "\r\n"
|
||||
|
||||
while (telnetClient.available()) { // get data from Client
|
||||
|
||||
// Get character
|
||||
char character = telnetClient.read();
|
||||
|
||||
// Newline (CR or LF) - once one time if (\r\n)
|
||||
if (isCRLF(character) == true) {
|
||||
if (isCRLF(last) == false) {
|
||||
// Process the command
|
||||
if (strlen(_command) > 0) {
|
||||
consoleProcessCommand();
|
||||
}
|
||||
}
|
||||
// reset for next command
|
||||
memset(_command, 0, sizeof(_command));
|
||||
} else if (isPrintable(character)) {
|
||||
// Concat char to end of buffer
|
||||
uint16_t len = strlen(_command);
|
||||
_command[len] = character;
|
||||
_command[len + 1] = '\0';
|
||||
}
|
||||
|
||||
// Last char
|
||||
last = character;
|
||||
}
|
||||
|
||||
// Inactivity - close connection if not received commands from user in telnet to reduce overheads
|
||||
if ((millis() - _lastTimeCommand) > MAX_TIME_INACTIVE) {
|
||||
telnetClient.println("* Closing session due to inactivity");
|
||||
telnetClient.flush();
|
||||
telnetClient.stop();
|
||||
_telnetConnected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set help for commands over telnet setted by sketch
|
||||
void ESPHelper::consoleSetHelpProjectsCmds(String help) {
|
||||
_helpProjectCmds = help;
|
||||
}
|
||||
|
||||
// Set callback of sketch function to process project messages
|
||||
void ESPHelper::consoleSetCallBackProjectCmds(void (*callback)()) {
|
||||
_consoleCallbackProjectCmds = callback;
|
||||
}
|
||||
|
||||
// Set bootime received as a string from HA
|
||||
void ESPHelper::setBoottime(const char * boottime) {
|
||||
strcpy(_boottime, boottime);
|
||||
}
|
||||
|
||||
// overrides the write call to print to the telnet connection
|
||||
size_t ESPHelper::write(uint8_t character) {
|
||||
if (!_verboseMessages)
|
||||
return 0;
|
||||
|
||||
//static uint32_t elapsed = 0;
|
||||
|
||||
// If start of a new line, initiate a new string buffer with time counter as a prefix
|
||||
if (_newLine) {
|
||||
unsigned long upt = millis();
|
||||
sprintf(bufferPrint,
|
||||
"(%s%02d:%02d:%02d%s) ",
|
||||
COLOR_CYAN,
|
||||
(uint8_t)((upt / (1000 * 60 * 60)) % 24),
|
||||
(uint8_t)((upt / (1000 * 60)) % 60),
|
||||
(uint8_t)((upt / 1000) % 60),
|
||||
COLOR_RESET);
|
||||
_newLine = false;
|
||||
}
|
||||
|
||||
// Print ?
|
||||
bool doPrint = false;
|
||||
|
||||
// New line ?
|
||||
if (character == '\n') {
|
||||
_newLine = true;
|
||||
doPrint = true;
|
||||
} else if (strlen(bufferPrint) == BUFFER_PRINT - 1) { // Limit of buffer
|
||||
doPrint = true;
|
||||
}
|
||||
|
||||
// Add character to telnet buffer
|
||||
uint16_t len = strlen(bufferPrint);
|
||||
bufferPrint[len] = character;
|
||||
|
||||
if (_newLine) {
|
||||
// add additional \r for windows
|
||||
bufferPrint[++len] = '\r';
|
||||
}
|
||||
|
||||
// terminate string
|
||||
bufferPrint[++len] = '\0';
|
||||
|
||||
// Send the characters buffered by print.h
|
||||
if (doPrint) {
|
||||
if (_telnetConnected) {
|
||||
telnetClient.print(bufferPrint);
|
||||
}
|
||||
|
||||
// echo to Serial if enabled
|
||||
#ifdef USE_SERIAL
|
||||
Serial.print(bufferPrint);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SERIAL1
|
||||
Serial1.print(bufferPrint);
|
||||
#endif
|
||||
|
||||
// Empty the buffer
|
||||
bufferPrint[0] = '\0';
|
||||
}
|
||||
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
// Show help of commands
|
||||
void ESPHelper::consoleShowHelp() {
|
||||
// Show the initial message
|
||||
String help = "";
|
||||
|
||||
help.concat("*\n\r* Remote Debug for ESP8266/ESP32\n\r");
|
||||
help.concat("* Device hostname: ");
|
||||
#if defined(ESP8266)
|
||||
help.concat(WiFi.hostname());
|
||||
#else
|
||||
help.concat(WiFi.getHostname());
|
||||
#endif
|
||||
help.concat(", IP: ");
|
||||
help.concat(WiFi.localIP().toString());
|
||||
help.concat(", MAC address: ");
|
||||
help.concat(WiFi.macAddress());
|
||||
help.concat("\n\r* Connected to WiFi AP: ");
|
||||
help.concat(WiFi.SSID());
|
||||
|
||||
#if defined(ESP32)
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
sprintf(s,
|
||||
"\n* ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision %d\n",
|
||||
chip_info.cores,
|
||||
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
|
||||
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "",
|
||||
chip_info.revision);
|
||||
help.concat(s);
|
||||
#endif
|
||||
|
||||
help.concat("\n\r* Boot time: ");
|
||||
help.concat(_boottime);
|
||||
help.concat("\n\r* Free Heap RAM: ");
|
||||
help.concat(ESP.getFreeHeap());
|
||||
help.concat(" bytes\n\r");
|
||||
help.concat("*\n\r* Commands:\n\r* ?=help, *=quit, $=memory, !=reboot, "
|
||||
"&=toggle verbose messages");
|
||||
|
||||
if (_helpProjectCmds != "" && (_consoleCallbackProjectCmds)) {
|
||||
help.concat("\n\r* ");
|
||||
help.concat(_helpProjectCmds);
|
||||
}
|
||||
help.concat("\n\r");
|
||||
telnetClient.print(help);
|
||||
|
||||
#ifdef USE_SERIAL
|
||||
Serial.print(help);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SERIAL1
|
||||
Serial1.print(help);
|
||||
#endif
|
||||
}
|
||||
|
||||
// reset / restart
|
||||
void ESPHelper::resetESP() {
|
||||
telnetClient.println("* Reboot ESP...");
|
||||
telnetClient.flush();
|
||||
telnetClient.stop();
|
||||
// end();
|
||||
|
||||
// Reset
|
||||
ESP.restart();
|
||||
// ESP.reset(); // for ESP8266 only
|
||||
}
|
||||
|
||||
// Get last command received
|
||||
char * ESPHelper::consoleGetLastCommand() {
|
||||
return _command;
|
||||
}
|
||||
|
||||
// Process user command over telnet
|
||||
void ESPHelper::consoleProcessCommand() {
|
||||
// Set time of last command received
|
||||
_lastTimeCommand = millis();
|
||||
uint8_t cmd = _command[0];
|
||||
|
||||
if (!_verboseMessages) {
|
||||
telnetClient.println(
|
||||
"Warning, verbose messaging is off. Use v to toggle.");
|
||||
}
|
||||
|
||||
// Process the command
|
||||
if (cmd == '?') {
|
||||
consoleShowHelp(); // Show help
|
||||
} else if (cmd == '*') { // quit
|
||||
telnetClient.println("* Closing telnet connection...");
|
||||
telnetClient.stop();
|
||||
} else if (cmd == '$') {
|
||||
telnetClient.print("* Free Heap RAM (bytes): ");
|
||||
telnetClient.println(ESP.getFreeHeap());
|
||||
} else if (cmd == '!') {
|
||||
resetESP();
|
||||
} else if (cmd == '&') {
|
||||
_verboseMessages = !_verboseMessages; // toggle
|
||||
telnetClient.printf("Verbose messaging is %s\n",
|
||||
_verboseMessages ? "on" : "off");
|
||||
} else {
|
||||
// custom Project commands
|
||||
if (_consoleCallbackProjectCmds) {
|
||||
_consoleCallbackProjectCmds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Logger
|
||||
// LOG_CONSOLE sends to the Telnet session
|
||||
// LOG_HA sends to Telnet session plus a MQTT for Home Assistant
|
||||
// LOG_NONE turns off all logging
|
||||
void ESPHelper::logger(log_level_t level, const char * message) {
|
||||
// do we log to the telnet window?
|
||||
if ((level == LOG_CONSOLE) && (telnetClient && telnetClient.connected())) {
|
||||
telnetClient.println(message);
|
||||
telnetClient.flush();
|
||||
} else if (level == LOG_HA) {
|
||||
char s[100];
|
||||
sprintf(s,
|
||||
"%s: %s\n",
|
||||
_hostname,
|
||||
message); // add new line, for the debug telnet printer
|
||||
publish(MQTT_NOTIFICATION, s, false);
|
||||
}
|
||||
|
||||
// print to Serial if set in platform.io (requires recompile)
|
||||
#ifdef USE_SERIAL
|
||||
Serial.println(message);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SERIAL1
|
||||
Serial.println(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
// send specific command to HA via MQTT
|
||||
// format is: home/<hostname>/command/<cmd>
|
||||
void ESPHelper::sendHACommand(const char * cmd) {
|
||||
logger(LOG_CONSOLE, "Sending command to HA...");
|
||||
|
||||
char s[100];
|
||||
sprintf(s, "%s%s/%s", MQTT_BASE, _hostname, MQTT_TOPIC_COMMAND);
|
||||
|
||||
publish(s, cmd, false);
|
||||
}
|
||||
|
||||
// send specific start command to HA via MQTT, which returns the boottime
|
||||
// format is: home/<hostname>/start
|
||||
void ESPHelper::sendStart() {
|
||||
logger(LOG_CONSOLE, "Sending Start command to HA...");
|
||||
|
||||
char s[100];
|
||||
sprintf(s, "%s%s/%s", MQTT_BASE, _hostname, MQTT_TOPIC_START);
|
||||
|
||||
// send initial payload of "start" to kick things off
|
||||
publish(s, MQTT_TOPIC_START, false);
|
||||
}
|
||||
226
lib/ESPHelper/ESPHelper.h
Normal file
226
lib/ESPHelper/ESPHelper.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
ESPHelper.h
|
||||
Copyright (c) 2017 ItKindaWorks Inc All right reserved.
|
||||
github.com/ItKindaWorks
|
||||
|
||||
This file is part of ESPHelper
|
||||
|
||||
ESPHelper is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ESPHelper 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with ESPHelper. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ESP_HELPER_H
|
||||
#define __ESP_HELPER_H
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
#include <Print.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
|
||||
#include <ESP8266mDNS.h>
|
||||
#elif defined(ESP32)
|
||||
#include "esp_system.h"
|
||||
#include <ESPmDNS.h>
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#error Only for ESP8266 or ESP32
|
||||
#endif
|
||||
|
||||
// MQTT stuff
|
||||
#define DEFAULT_QOS 1 //at least once - devices are guarantee to get a message.
|
||||
#define MQTT_BASE "home/"
|
||||
#define MQTT_NOTIFICATION MQTT_BASE "notification"
|
||||
#define MQTT_TOPIC_COMMAND "command"
|
||||
#define MQTT_TOPIC_START "start"
|
||||
|
||||
#define MAX_SUBSCRIPTIONS 25 // max # of subscriptions
|
||||
#define MAX_TIME_INACTIVE 600000 // Max time for inactivity (ms) - 10 mins
|
||||
#define TELNET_PORT 23 // telnet port
|
||||
#define BUFFER_PRINT 500 // length of telnet buffer (default was 150)
|
||||
#define COMMAND_LENGTH 20 // length of a command
|
||||
|
||||
// ANSI Colors
|
||||
#define COLOR_RESET "\x1B[0m"
|
||||
#define COLOR_BLACK "\x1B[0;30m"
|
||||
#define COLOR_RED "\x1B[0;31m"
|
||||
#define COLOR_GREEN "\x1B[0;32m"
|
||||
#define COLOR_YELLOW "\x1B[0;33m"
|
||||
#define COLOR_BLUE "\x1B[0;34m"
|
||||
#define COLOR_MAGENTA "\x1B[0;35m"
|
||||
#define COLOR_CYAN "\x1B[0;36m"
|
||||
#define COLOR_WHITE "\x1B[0;37m"
|
||||
|
||||
// Logger
|
||||
typedef enum { LOG_NONE, LOG_CONSOLE, LOG_HA } log_level_t;
|
||||
|
||||
enum connStatus { NO_CONNECTION, BROADCAST, WIFI_ONLY, FULL_CONNECTION };
|
||||
|
||||
struct netInfo {
|
||||
const char * mqttHost;
|
||||
const char * mqttUser;
|
||||
const char * mqttPass;
|
||||
uint16_t mqttPort;
|
||||
const char * ssid;
|
||||
const char * pass;
|
||||
};
|
||||
typedef struct netInfo netInfo;
|
||||
|
||||
struct subscription {
|
||||
bool isUsed = false;
|
||||
const char * topic;
|
||||
};
|
||||
typedef struct subscription subscription;
|
||||
|
||||
// class ESPHelper {
|
||||
class ESPHelper : public Print {
|
||||
public:
|
||||
void consoleSetHelpProjectsCmds(String help);
|
||||
void consoleSetCallBackProjectCmds(void (*callback)());
|
||||
char * consoleGetLastCommand();
|
||||
void resetESP();
|
||||
void logger(log_level_t level, const char * message);
|
||||
|
||||
virtual size_t write(uint8_t);
|
||||
|
||||
ESPHelper(netInfo * startingNet);
|
||||
|
||||
bool begin(const char * hostname);
|
||||
void end();
|
||||
|
||||
void useSecureClient(const char * fingerprint);
|
||||
|
||||
int loop();
|
||||
|
||||
bool subscribe(const char * topic, uint8_t qos);
|
||||
bool addSubscription(const char * topic);
|
||||
bool removeSubscription(const char * topic);
|
||||
bool unsubscribe(const char * topic);
|
||||
bool addHASubscription(const char * topic);
|
||||
|
||||
void publish(const char * topic, const char * payload);
|
||||
void publish(const char * topic, const char * payload, bool retain);
|
||||
|
||||
bool setCallback(MQTT_CALLBACK_SIGNATURE);
|
||||
void setMQTTCallback(MQTT_CALLBACK_SIGNATURE);
|
||||
|
||||
void setWifiCallback(void (*callback)());
|
||||
|
||||
void sendHACommand(const char * s);
|
||||
void sendStart();
|
||||
|
||||
void reconnect();
|
||||
|
||||
void updateNetwork();
|
||||
|
||||
const char * getSSID();
|
||||
void setSSID(const char * ssid);
|
||||
|
||||
const char * getPASS();
|
||||
void setPASS(const char * pass);
|
||||
|
||||
const char * getMQTTIP();
|
||||
void setMQTTIP(const char * mqttIP);
|
||||
void setMQTTIP(const char * mqttIP, const char * mqttUser, const char * mqttPass);
|
||||
|
||||
uint8_t getMQTTQOS();
|
||||
void setMQTTQOS(uint8_t qos);
|
||||
|
||||
String getIP();
|
||||
IPAddress getIPAddress();
|
||||
|
||||
uint8_t getStatus();
|
||||
|
||||
void setNetInfo(netInfo newNetwork);
|
||||
void setNetInfo(netInfo * newNetwork);
|
||||
netInfo * getNetInfo();
|
||||
|
||||
void setHopping(bool canHop);
|
||||
|
||||
void listSubscriptions();
|
||||
|
||||
void OTA_enable();
|
||||
void OTA_disable();
|
||||
void OTA_begin();
|
||||
|
||||
void setBoottime(const char * boottime);
|
||||
|
||||
void consoleHandle();
|
||||
|
||||
private:
|
||||
netInfo _currentNet;
|
||||
PubSubClient client;
|
||||
WiFiClient wifiClient;
|
||||
WiFiClientSecure wifiClientSecure;
|
||||
const char * _fingerprint;
|
||||
bool _useSecureClient = false;
|
||||
char _clientName[40];
|
||||
void (*_wifiCallback)();
|
||||
bool _wifiCallbackSet = false;
|
||||
|
||||
#ifdef ESP8266
|
||||
std::function<void(char *, uint8_t *, uint8_t)> _mqttCallback;
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
void (*_mqttCallback)(char *, uint8_t *, uint8_t);
|
||||
#endif
|
||||
|
||||
bool _mqttCallbackSet = false;
|
||||
uint8_t _connectionStatus = NO_CONNECTION;
|
||||
uint8_t _netCount = 0;
|
||||
uint8_t _currentIndex = 0;
|
||||
bool _ssidSet = false;
|
||||
bool _passSet = false;
|
||||
bool _mqttSet = false;
|
||||
bool _mqttUserSet = false;
|
||||
bool _mqttPassSet = false;
|
||||
bool _useOTA = false;
|
||||
bool _OTArunning = false;
|
||||
bool _hoppingAllowed = false;
|
||||
bool _hasBegun = false;
|
||||
netInfo ** _netList;
|
||||
bool _verboseMessages = true;
|
||||
subscription _subscriptions[MAX_SUBSCRIPTIONS];
|
||||
char _hostname[64];
|
||||
uint8_t _qos = DEFAULT_QOS;
|
||||
IPAddress _apIP = IPAddress(192, 168, 1, 254);
|
||||
void changeNetwork();
|
||||
String macToStr(const uint8_t * mac);
|
||||
bool checkParams();
|
||||
void resubscribe();
|
||||
uint8_t setConnectionStatus();
|
||||
|
||||
char _boottime[20];
|
||||
|
||||
// console/telnet specific
|
||||
WiFiClient telnetClient;
|
||||
|
||||
bool _telnetConnected = false; // Client is connected ?
|
||||
bool _newLine = true; // New line write ?
|
||||
|
||||
char _command[COMMAND_LENGTH]; // Command received, includes options seperated by a space
|
||||
uint32_t _lastTimeCommand = millis(); // Last time command received
|
||||
|
||||
String _helpProjectCmds = ""; // Help of commands setted by project
|
||||
|
||||
void (*_consoleCallbackProjectCmds)(); // Callable for projects commands
|
||||
void consoleShowHelp();
|
||||
void consoleProcessCommand();
|
||||
bool isCRLF(char character);
|
||||
|
||||
char bufferPrint[BUFFER_PRINT];
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user