ESP32_ChinaDieselHeater_Con.../lib/TelnetSpy/TelnetSpy.h

270 lines
9.5 KiB
C++

/*
* 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.
*/
/*
* DESCRIPTION
*
* 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.
*
* USAGE
*
* Add the following line to your sketch:
* #include <TelnetSpy.h>
* TelnetSpy LOG;
*
* 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() ):
* LOG.handle();
*
* Use the following functions of the TelnetSpy object to modify behavior
*
* 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)());
*
* HINT
*
* Add the following lines to your sketch:
* #define SERIAL TelnetSpy
* //#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.
*
* IMPORTANT
*
* To connect to the telnet server you have to:
* - establish the Wifi connection
* - execute "TelnetSpy.begin(WhatEverYouWant);"
*
* 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.
*
* 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.
*
* 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.
*/
#ifndef TelnetSpy_h
#define TelnetSpy_h
#define TELNETSPY_BUFFER_LEN 3000
#define TELNETSPY_MIN_BLOCK_SIZE 64
#define TELNETSPY_COLLECTING_TIME 100
#define TELNETSPY_MAX_BLOCK_SIZE 512
#define TELNETSPY_PING_TIME 1500
#define TELNETSPY_PORT 23
#define TELNETSPY_CAPTURE_OS_PRINT true
#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>
class TelnetSpy : public Stream {
public:
TelnetSpy();
~TelnetSpy();
void handle(void);
void setPort(uint16_t portToUse);
void setWelcomeMsg(char* msg);
void setRejectMsg(char* msg);
void setMinBlockSize(uint16_t minSize);
void setCollectingTime(uint16_t colTime);
void setMaxBlockSize(uint16_t maxSize);
bool setBufferSize(uint16_t newSize);
uint16_t getBufferSize();
void setStoreOffline(bool store);
bool getStoreOffline();
void setPingTime(uint16_t pngTime);
void setSerial(HardwareSerial* usedSerial);
bool isClientConnected();
void setCallbackOnConnect(void (*callback)());
void setCallbackOnDisconnect(void (*callback)());
// Functions offered by HardwareSerial class:
#ifdef ESP8266
void begin(unsigned long baud) { begin(baud, SERIAL_8N1, SERIAL_FULL, 1); }
void begin(unsigned long baud, SerialConfig config) { begin(baud, config, SERIAL_FULL, 1); }
void begin(unsigned long baud, SerialConfig config, SerialMode mode) { begin(baud, config, mode, 1); }
void begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin);
#else // ESP32
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false);
#endif
void end();
#ifdef ESP8266
void swap() { swap(1); }
void swap(uint8_t tx_pin);
void set_tx(uint8_t tx_pin);
void pins(uint8_t tx, uint8_t rx);
bool isTxEnabled(void);
bool isRxEnabled(void);
#endif
int available(void) override;
int peek(void) override;
int read(void) override;
int availableForWrite(void);
void flush(void) override;
size_t write(uint8_t) override;
inline size_t write(unsigned long n) { return write((uint8_t) n); }
inline size_t write(long n) { return write((uint8_t) n); }
inline size_t write(unsigned int n) { return write((uint8_t) n); }
inline size_t write(int n) { return write((uint8_t) n); }
using Print::write;
operator bool() const;
void setDebugOutput(bool);
uint32_t baudRate(void);
protected:
CRITCAL_SECTION_MUTEX
void sendBlock(void);
void addTelnetBuf(char c);
char pullTelnetBuf();
char peekTelnetBuf();
int telnetAvailable();
WiFiServer* telnetServer;
WiFiClient client;
uint16_t port;
HardwareSerial* usedSer;
bool storeOffline;
bool started;
bool listening;
bool firstMainLoop;
unsigned long waitRef;
unsigned long pingRef;
uint16_t pingTime;
char* welcomeMsg;
char* rejectMsg;
uint16_t minBlockSize;
uint16_t collectingTime;
uint16_t maxBlockSize;
bool debugOutput;
char* telnetBuf;
uint16_t bufLen;
uint16_t bufUsed;
uint16_t bufRdIdx;
uint16_t bufWrIdx;
bool connected;
void (*callbackConnect)();
void (*callbackDisconnect)();
};
#endif