Added AP SSID/password configuration via debug port

This commit is contained in:
Ray Jones 2019-08-10 21:45:28 +10:00
parent d7e083b837
commit 79b6c06a2a
5 changed files with 369 additions and 96 deletions

View file

@ -1,12 +1,36 @@
# TELNET SERVER FOR ESP8266 / ESP32
<h3 align="center">Telnet server for ESP8266 / ESP32</h3>
Cloning the serial port via Telnet.
---
## DESCRIPTION
<p align="center"> Cloning the serial port via Telnet.
<br>
</p>
This module allows you "Debugging over the air". So if you already use ArduinoOTA this is a helpful extension for wireless development. Use "TelnetSpy" instead of "Serial" to send data to the serial port and a copy to a telnet connection. There is a circular buffer which allows to store the data while the telnet connection is not established. So its possible to collect data even when the Wifi and Telnet connections are still not established. Its also possible to create a telnet session only if it is neccessary: then you will get the already collected data as far as it is still stored in the circular buffer. Data send from telnet terminal to ESP8266 / ESP32 will be handled as data received by serial port. It is also possible to use more than one instance of TelnetSpy, for example to send control information on the first instance and data dumps on the second instance.
<div align="center">
## USAGE
[![GitHub Issues](https://img.shields.io/github/issues/yasheena/telnetspy.svg)](https://github.com/yasheena/telnetspy/issues)
[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/yasheena/telnetspy.svg)](https://github.com/yasheena/telnetspy/pulls)
</div>
## 📝 Table of Contents
- [Description](#description)
- [Usage](#usage)
- [General](#general)
- [Functions](#functions)
- [License](#license)
## 🎈 Description <a name = "description"></a>
- This module allows you to do "debugging over the air". So if you already use ArduinoOTA, this is a helpful extension for wireless development. Use ```TelnetSpy``` instead of ```Serial``` to send data to the serial port and a copy to a Telnet connection.
- There is a circular buffer which allows to store the data while the Telnet connection is not established. So it's possible to collect data even when the WiFi and Telnet connections are not yet established.
- It's also possible to create a Telnet session only if it is neccessary: then you will get the already collected data as far as it is still stored in the circular buffer. Data sent from Telnet terminal to ESP8266 / ESP32 will be handled as data received by serial port.
- It is also possible to use more than one instance of TelnetSpy. For example - To send control information on the first instance and data dumps on the second instance.
## 🚀 Usage <a name = "usage"></a>
### General <a name = "general"></a>
Add the following line to your sketch:
```
@ -14,117 +38,211 @@ Add the following line to your sketch:
TelnetSpy LOG;
```
Add the following line to your initialisation block ( void setup() ):
Add the following line to your initialisation block ```void setup()```:
```
LOG.begin();
```
Add the following line at the beginning of your main loop ( void loop() ):
Add the following line at the beginning of your main loop ```void loop()```:
```
LOG.handle();
```
### Use the following functions of the TelnetSpy object to modify behavior
### Use the following functions of the TelnetSpy object to modify behavior <a name = "functions"></a>
1. [void setPort()](#setPort)
2. [void setWelcomeMsg()](#setWelcomeMsg)
3. [void setRejectMsg()](#setRejectMsg)
4. [void setMinBlockSize()](#setMinBlockSize)
5. [void setCollectingTime()](#setCollectingTime)
6. [void setMaxBlockSize()](#setMaxBlockSize)
7. [bool setBufferSize()](#setBufferSize)
8. [uint16_t getBufferSize()](#getBufferSize)
9. [void setStoreOffline()](#setStoreOffline)
10. [bool getStoreOffline()](#getStoreOffline)
11. [void setPingTime()](#setPingTime)
12. [void setSerial()](#setSerial)
14. [bool isClientConnected()](#isClientConnected)
14. [void setCallbackOnConnect()](#setCallbackOnConnect)
15. [void setCallbackOnDisconnect()](#setCallbackOnDisconnect)
---
### 1. void setPort(uint16_t portToUse) <a name = "setPort"></a>
Change the port number of this Telnet server. If a client is already connected, it will be disconnected.
Change the port number of this telnet server. If a client is already connected it will be disconnected.\
Default: 23
```
void setPort(uint16_t portToUse);
```
Change the message which will be send to the telnet client after a session is established.\
Default: "Connection established via TelnetSpy.\n"
```
void setWelcomeMsg(char* msg);
```
Change the message which will be send to the telnet client if another session is already established.\
Default: "TelnetSpy: Only one connection possible.\n"
```
void setRejectMsg(char* msg);
```
Change the amount of characters to collect before sending a telnet block.\
Default: 64
```
void setMinBlockSize(uint16_t minSize);
```
Change the time (in ms) to wait before sending a telnet block if its size is less than \<minSize\> (defined by setMinBlockSize).\
Default: 100
```
void setCollectingTime(uint16_t colTime);
```
Change the maximum size of the telnet packets to send.\
Default: 512
```
void setMaxBlockSize(uint16_t maxSize);
```
Change the size of the ring buffer. Set it to 0 to disable buffering. Changing size tries to preserve the already collected data. If the new buffer size is too small the youngest data will be preserved only. Returns false if the requested buffer size cannot be set.\
Default: 3000
```
bool setBufferSize(uint16_t newSize);
```
This function returns the actual size of the ring buffer.
```
uint16_t getBufferSize();
```
Enable / disable storing new data in the ring buffer if no telnet connection is established. This function allows you to store important data only. You can do this by disabling "storeOffline" for sending less important data.\
Default: true
```
void setStoreOffline(bool store);
```
Get actual state of storing data when offline.
```
bool getStoreOffline();
```
If no data is sent via TelnetSpy the detection of a disconnected client has a long timeout. Use setPingTime to define the time (in ms) without traffic after which a ping (chr(0)) is sent to the telnet client to detect a disconnect earlier. Use 0 as parameter to disable pings.\
Default: 1500
```
void setPingTime(uint16_t pngTime);
```
Set the serial port you want to use with this object (especially for ESP32) or NULL if no serial port should be used (telnet only).\
Default: Serial
```
void setSerial(HardwareSerial* usedSerial);
```
This function returns true, if a telnet client is connected.
```
bool isClientConnected();
```
This function installs a callback function which will be called on every telnet connect of this object (except rejected connect tries). Use NULL to remove the callback.\
Default: NULL
```
void setCallbackOnConnect(void (*callback)());
```
This function installs a callback function which will be called on every telnet disconnect of this object (except rejected connect tries). Use NULL to remove the callback.\
Default: NULL
```
void setCallbackOnDisconnect(void (*callback)());
void setPort(uint16_t portToUse)
```
## HINT
### 2. void setWelcomeMsg(char* msg) <a name = "setWelcomeMsg"></a>
Change the message which will be sent to the Telnet client after a session is established.
Default: "Connection established via TelnetSpy.\n"
```
void setWelcomeMsg(char* msg)
```
### 3. void setRejectMsg(char* msg) <a name = "setRejectMsg"></a>
Change the message which will be sent to the Telnet client if another session is already established.
Default: "TelnetSpy: Only one connection possible.\n"
```
void setRejectMsg(char* msg)
```
### 4. void setMinBlockSize(uint16_t minSize) <a name = "setMinBlockSize"></a>
Change the amount of characters to collect before sending a Telnet block.
Default: 64
```
void setMinBlockSize(uint16_t minSize)
```
### 5. void setCollectingTime(uint16_t colTime) <a name = "setCollectingTime"></a>
Change the time (in ms) to wait before sending a Telnet block if it's size is less than <minSize> (defined by ```setMinBlockSize```).
Default: 100
```
void setCollectingTime(uint16_t colTime)
```
### 6. void setMaxBlockSize(uint16_t maxSize) <a name = "setMaxBlockSize"></a>
Change the maximum size of the Telnet packets to send.
Default: 512
```
void setMaxBlockSize(uint16_t maxSize)
```
### 7. bool setBufferSize(uint16_t newSize) <a name = "setBufferSize"></a>
Change the size of the ring buffer. Set it to ```0``` to disable buffering. Changing size tries to preserve the already collected data. If the new buffer size is too small, only the latest data will be preserved. Returns ```false``` if the requested buffer size cannot be set.
Default: 3000
```
bool setBufferSize(uint16_t newSize)
```
### 8. uint16_t getBufferSize() <a name = "getBufferSize"></a>
This function returns the actual size of the ring buffer.
```
uint16_t getBufferSize()
```
### 9. void setStoreOffline(bool store) <a name = "setStoreOffline"></a>
Enable / disable storing new data in the ring buffer if no Telnet connection is established. This function allows you to store important data only. You can do this by disabling ```storeOffline``` for sending unimportant data.
Default: true
```
void setStoreOffline(bool store)
```
### 10. bool getStoreOffline() <a name = "getStoreOffline"></a>
Get actual state of storing data when offline.
```
bool getStoreOffline()
```
### 11. void setPingTime(uint16_t pngTime) <a name = "setPingTime"></a>
If no data is sent via TelnetSpy, the detection of a disconnected client has a long timeout. Use ```setPingTime``` to define the time (in ms) without traffic after which a ping aka a ```chr(0)``` is sent to the Telnet client to detect a disconnect earlier. Use ```0``` as parameter to disable pings.
Default: 1500
```
void setPingTime(uint16_t pngTime)
```
### 12. void setSerial(HardwareSerial* usedSerial) <a name = "setSerial"></a>
Set the serial port you want to use with this object (especially for ESP32) or ```NULL``` if no serial port should be used (Telnet only).
Default: Serial
```
void setSerial(HardwareSerial* usedSerial)
```
### 13. bool isClientConnected() <a name = "isClientConnected"></a>
This function returns true if a Telnet client is connected.
```
bool isClientConnected()
```
### 14. void setCallbackOnConnect(void (*callback)()) <a name = "setCallbackOnConnect"></a>
This function installs a callback function which will be called on every Telnet connect of this object (except rejected connect tries). Use ```NULL``` to remove the callback.
Default: NULL
```
void setCallbackOnConnect(void (*callback)())
```
### 15. void setCallbackOnDisconnect(void (*callback)()) <a name = "setCallbackOnDisconnect"></a>
This function installs a callback function which will be called on every Telnet disconnect of this object (except rejected connect tries). Use ```NULL``` to remove the callback.
Default: NULL
```
void setCallbackOnDisconnect(void (*callback)())
```
## 💡 Hint <a name = "hint"></a>
Add the following lines to your sketch:
```
TelnetSpy SerialAndTelnet;
#define SERIAL SerialAndTelnet
//#define SERIAL Serial
// #define SERIAL Serial
```
Replace "Serial" with "SERIAL" in your sketch. Now you can switch between serial only and serial with telnet by changing the comments of the defines only.
Replace ```Serial``` with ```SERIAL``` in your sketch. Now you can switch between serial only and serial with Telnet by only changing the comments of the defines.
## IMPORTANT
## 🔔 Important <a name = "important"></a>
To connect to the telnet server you have to:
- establish the Wifi connection
- execute "SerialAndTelnet.begin(WhatEverYouWant);"
- To connect to the Telnet server you have to:
1. Establish the WiFi connection.
2. Execute ```SerialAndTelnet.begin(WhatEverYouWant)```.
The order is not important.
💡 **Note:** The order is not important.
All you do with "Serial" you can also do with "TelnetSpy", but remember: Transfering data also via telnet will need more performance than the serial port only. So time critical things may be influenced.
- Everything you do with ```Serial```, you can do with ```TelnetSpy``` too. But remember: Transfering data also via Telnet will need more performance than the serial port only. So time critical things may be influenced.
It is not possible to establish more than one telnet connection at the same time. But its possible to use more than one instance of TelnetSpy.
- It is not possible to establish more than one Telnet connection at the same time. But it's possible to use more than one instance of TelnetSpy.
If you have problems with low memory you may reduce the value of the define TELNETSPY_BUFFER_LEN for a smaller ring buffer on initialisation.
- If you have problems with low memory, you may reduce the value of the ```define TELNETSPY_BUFFER_LEN``` for a smaller ring buffer on initialisation.
Usage of void setDebugOutput(bool) to enable / disable of capturing of os_print calls when you have more than one TelnetSpy instance: That TelnetSpy object will handle this functionallity where you used setDebugOutput at last. On default TelnetSpy has the capturing of OS_print calls enabled. So if you have more instances the last created instance will handle the capturing.
- Usage of ```void setDebugOutput(bool)``` to enable / disable of capturing of os_print calls when you have more than one TelnetSpy instance: That TelnetSpy object will handle this functionality where you used ```setDebugOutput``` at last.
On default, TelnetSpy has the capturing of OS_print calls enabled. So if you have more instances the last created instance will handle the capturing.
## License
## 📖 License <a name = "license"></a>
This library is open-source and licensed under the [MIT license] (http://opensource.org/licenses/MIT). Do whatever you like with it, but contributions are appreciated.
This library is open-source and licensed under the [MIT license](http://opensource.org/licenses/MIT).
Do whatever you like with it, but contributions are appreciated!

View file

@ -23,8 +23,8 @@ extern "C" {
#endif
static TelnetSpy* actualObject = NULL;
/*
static void TelnetSpy_putc(char c) {
if (actualObject) {
actualObject->write(c);
@ -33,7 +33,6 @@ static void TelnetSpy_putc(char c) {
static void TelnetSpy_ignore_putc(char c) {;
}
*/
TelnetSpy::TelnetSpy() {
port = TELNETSPY_PORT;
@ -430,12 +429,16 @@ uint32_t TelnetSpy::baudRate(void) {
}
void TelnetSpy::sendBlock() {
CRITCAL_SECTION_START
uint16_t len = bufUsed;
if (len > maxBlockSize) {
len = maxBlockSize;
}
len = min(len, (uint16_t) (bufLen - bufRdIdx));
client.write(&telnetBuf[bufRdIdx], len);
uint16_t idx = bufRdIdx;
CRITCAL_SECTION_END
client.write(&telnetBuf[idx], len);
CRITCAL_SECTION_START
bufRdIdx += len;
if (bufRdIdx >= bufLen) {
bufRdIdx = 0;
@ -445,6 +448,7 @@ void TelnetSpy::sendBlock() {
bufRdIdx = 0;
bufWrIdx = 0;
}
CRITCAL_SECTION_END
waitRef = 0xFFFFFFFF;
if (pingRef != 0xFFFFFFFF) {
pingRef = (millis() & 0x7FFFFFF) + pingTime;
@ -455,6 +459,7 @@ void TelnetSpy::sendBlock() {
}
void TelnetSpy::addTelnetBuf(char c) {
CRITCAL_SECTION_START
telnetBuf[bufWrIdx] = c;
if (bufUsed == bufLen) {
bufRdIdx++;
@ -468,17 +473,20 @@ void TelnetSpy::addTelnetBuf(char c) {
if (bufWrIdx >= bufLen) {
bufWrIdx = 0;
}
CRITCAL_SECTION_END
}
char TelnetSpy::pullTelnetBuf() {
if (bufUsed == 0) {
return 0;
}
CRITCAL_SECTION_START
char c = telnetBuf[bufRdIdx++];
if (bufRdIdx >= bufLen) {
bufRdIdx = 0;
}
bufUsed--;
CRITCAL_SECTION_END
return c;
}
@ -486,7 +494,11 @@ char TelnetSpy::peekTelnetBuf() {
if (bufUsed == 0) {
return 0;
}
return telnetBuf[bufRdIdx];
CRITCAL_SECTION_START
char c = telnetBuf[bufRdIdx];
CRITCAL_SECTION_END
// return telnetBuf[bufRdIdx];
return c;
}
int TelnetSpy::telnetAvailable() {

View file

@ -161,10 +161,20 @@
#define TELNETSPY_WELCOME_MSG "Connection established via TelnetSpy.\r\n"
#define TELNETSPY_REJECT_MSG "TelnetSpy: Only one connection possible.\r\n"
#ifdef ESP8266
#include <ESP8266WiFi.h>
// empty defines, so on ESP8266 nothing will be changed
#define CRITCAL_SECTION_MUTEX
#define CRITCAL_SECTION_START
#define CRITCAL_SECTION_END
#else // ESP32
#include <WiFi.h>
// add spinlock for ESP32
#define CRITCAL_SECTION_MUTEX portMUX_TYPE AtomicMutex = portMUX_INITIALIZER_UNLOCKED;
// Non-static Data Member Initializers, see: https://web.archive.org/web/20160316174223/https://blogs.oracle.com/pcarlini/entry/c_11_tidbits_non_static
#define CRITCAL_SECTION_START portENTER_CRITICAL(&AtomicMutex);
#define CRITCAL_SECTION_END portEXIT_CRITICAL(&AtomicMutex);
#endif
#include <WiFiClient.h>
@ -222,6 +232,7 @@ class TelnetSpy : public Stream {
uint32_t baudRate(void);
protected:
CRITCAL_SECTION_MUTEX
void sendBlock(void);
void addTelnetBuf(char c);
char pullTelnetBuf();

View file

@ -1178,6 +1178,11 @@ bool isWebClientConnected()
void checkDebugCommands()
{
static uint8_t nGetString = 0;
static uint8_t nGetConf = 0;
static String pw1;
static String pw2;
// check for test commands received from PC Over USB
if(DebugPort.available()) {
#ifdef PROTOCOL_INVESTIGATION
@ -1192,6 +1197,94 @@ void checkDebugCommands()
return;
}
if(nGetConf) {
DebugPort.print(rxVal);
bool bSave = (rxVal == 'y') || (rxVal == 'Y');
if(!bSave) {
DebugPort.println(" ABORTED!");
nGetConf = 0;
return;
}
switch(nGetConf) {
case 1:
setSSID(PCline.Line);
break;
case 2:
setAPpassword(pw2.c_str());
break;
}
nGetConf = 0;
return;
}
else if(nGetString) {
if(rxVal < ' ') {
if(rxVal == 0x1b) { // ESCAPE
nGetString = 0;
DebugPort.println("\r\nABORTED!");
return;
}
if(rxVal == '\n' || rxVal == '\r') {
switch(nGetString) {
case 1:
if(PCline.Len <= 31) {
nGetConf = 1;
DebugPort.printf("\r\nSet AP SSID to %s? (y/n) - ", PCline.Line);
}
else {
DebugPort.println("\r\nNew name is longer than 31 characters - ABORTING");
}
nGetString = 0;
return;
case 2:
pw1 = PCline.Line;
PCline.clear();
pw2 = NVstore.getCredentials().APpassword;
if(pw1 != pw2) {
DebugPort.println("\r\nPassword does not match existing - ABORTING");
nGetString = 0;
}
else {
nGetString = 3;
DebugPort.print("\r\nPlease enter new password - ");
}
return;
case 3:
pw1 = PCline.Line;
if(PCline.Len <= 31) {
nGetString = 4;
DebugPort.print("\r\nPlease confirm new password - ");
}
else {
DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING");
nGetString = 0;
}
PCline.clear();
return;
case 4:
pw2 = PCline.Line;
PCline.clear();
if(pw1 != pw2) {
DebugPort.println("\r\nNew passwords do not match - ABORTING");
}
else {
nGetConf = 2;
DebugPort.print("\r\nSet new password (y/n) - ");
}
nGetString = 0;
return;
}
}
}
else {
if(nGetString == 1)
DebugPort.print(rxVal);
else
DebugPort.print('*');
PCline.append(rxVal);
return;
}
}
rxVal = toLowerCase(rxVal);
#ifdef PROTOCOL_INVESTIGATION
@ -1215,6 +1308,8 @@ void checkDebugCommands()
DebugPort.printf(" <W> - toggle reporting of blue wire timeout/recycling event, currently %s\r\n", bReportRecyleEvents ? "ON" : "OFF");
DebugPort.printf(" <O> - toggle reporting of OEM resync event, currently %s\r\n", bReportOEMresync ? "ON" : "OFF");
DebugPort.printf(" <S> - toggle reporting of state machine transits %s\r\n", CommState.isReporting() ? "ON" : "OFF");
DebugPort.printf(" <N> - change AP SSID, currently \"%s\"\r\n", NVstore.getCredentials().SSID);
DebugPort.println(" <P> - change AP password");
DebugPort.println(" <+> - request heater turns ON");
DebugPort.println(" <-> - request heater turns OFF");
DebugPort.println(" <R> - restart the ESP");
@ -1273,10 +1368,20 @@ void checkDebugCommands()
bReportRecyleEvents = !bReportRecyleEvents;
DebugPort.printf("Toggled blue wire recycling event reporting %s\r\n", bReportRecyleEvents ? "ON" : "OFF");
}
else if(rxVal == 'n') {
DebugPort.print("Please enter new SSID name for Access Point - ");
nGetString = 1;
PCline.clear();
}
else if(rxVal == 'o') {
bReportOEMresync = !bReportOEMresync;
DebugPort.printf("Toggled OEM resync event reporting %s\r\n", bReportOEMresync ? "ON" : "OFF");
}
else if(rxVal == 'p') {
DebugPort.print("Please enter current AP password - ");
nGetString = 2;
PCline.clear();
}
else if(rxVal == 's') {
CommState.toggleReporting();
}
@ -1617,3 +1722,28 @@ int sysUptime()
return Clock.get().secondstime() - BootTime;
}
void setSSID(const char* name)
{
sCredentials creds = NVstore.getCredentials();
strncpy(creds.SSID, name, 31);
creds.SSID[31] = 0;
NVstore.setCredentials(creds);
NVstore.save();
NVstore.doSave(); // ensure NV storage
DebugPort.println("Restarting ESP to invoke new network credentials");
delay(1000);
ESP.restart();
}
void setAPpassword(const char* name)
{
sCredentials creds = NVstore.getCredentials();
strncpy(creds.APpassword, name, 31);
creds.APpassword[31] = 0;
NVstore.setCredentials(creds);
NVstore.save();
NVstore.doSave(); // ensure NV storage
DebugPort.println("Restarting ESP to invoke new network credentials");
delay(1000);
ESP.restart();
}

View file

@ -86,6 +86,8 @@ extern float getGlowCurrent();
extern float getFanSpeed();
extern int sysUptime();
void setSSID(const char* name);
void setAPpassword(const char* name);
extern void ShowOTAScreen(int percent=0, eOTAmodes updateType=eOTAnormal);