2019-07-25 11:27:57 +00:00
/*
* 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"
2019-08-10 11:45:28 +00:00
2019-07-25 11:27:57 +00:00
# ifdef ESP8266
# include <ESP8266WiFi.h>
2019-08-10 11:45:28 +00:00
// empty defines, so on ESP8266 nothing will be changed
# define CRITCAL_SECTION_MUTEX
# define CRITCAL_SECTION_START
# define CRITCAL_SECTION_END
2019-07-25 11:27:57 +00:00
# else // ESP32
# include <WiFi.h>
2019-08-10 11:45:28 +00:00
// 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);
2019-07-25 11:27:57 +00:00
# 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 :
2019-08-10 11:45:28 +00:00
CRITCAL_SECTION_MUTEX
2019-07-25 11:27:57 +00:00
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