mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Telnet mode for the Terminal class
This commit is contained in:
parent
ef532b3eef
commit
25aeeb0383
@ -99,6 +99,8 @@ Shell::~Shell()
|
||||
* serial port or TCP network connection.
|
||||
* \param maxHistory The number of commands to allocate in the history
|
||||
* stack for scrolling back through using Up/Down arrow keys.
|
||||
* \param mode The terminal mode to operate in, Terminal::Serial or
|
||||
* Terminal::Telnet.
|
||||
* \return Returns true if the shell was initialized, or false if there
|
||||
* is insufficient memory for the history stack.
|
||||
*
|
||||
@ -114,10 +116,10 @@ Shell::~Shell()
|
||||
*
|
||||
* \sa end(), setPrompt()
|
||||
*/
|
||||
bool Shell::begin(Stream &stream, size_t maxHistory)
|
||||
bool Shell::begin(Stream &stream, size_t maxHistory, Terminal::Mode mode)
|
||||
{
|
||||
// Initialize the Terminal base class with the underlying stream.
|
||||
Terminal::begin(stream);
|
||||
Terminal::begin(stream, mode);
|
||||
|
||||
// Create the history buffer.
|
||||
this->maxHistory = maxHistory;
|
||||
|
@ -63,7 +63,7 @@ public:
|
||||
Shell();
|
||||
virtual ~Shell();
|
||||
|
||||
bool begin(Stream &stream, size_t maxHistory = 0);
|
||||
bool begin(Stream &stream, size_t maxHistory = 0, Terminal::Mode mode = Serial);
|
||||
void end();
|
||||
|
||||
void loop();
|
||||
|
106
libraries/Terminal/TelnetDefs.h
Normal file
106
libraries/Terminal/TelnetDefs.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef TELNET_DEFS_h
|
||||
#define TELNET_DEFS_h
|
||||
|
||||
// References:
|
||||
// https://tools.ietf.org/html/rfc854
|
||||
// http://www.iana.org/assignments/telnet-options/telnet-options.xhtml
|
||||
|
||||
namespace TelnetDefs
|
||||
{
|
||||
|
||||
/** Telnet commands */
|
||||
enum Command
|
||||
{
|
||||
EndOfFile = 236, /**< EOF */
|
||||
Suspend = 237, /**< Suspend process */
|
||||
Abort = 238, /**< Abort process */
|
||||
EndOfRecord = 239, /**< End of record command */
|
||||
SubEnd = 240, /**< End option sub-negotiation */
|
||||
NOP = 241, /**< No operation */
|
||||
DataMark = 242, /**< Data mark */
|
||||
Break = 243, /**< Break */
|
||||
Interrupt = 244, /**< Interrupt process */
|
||||
AbortOutput = 245, /**< Abort output */
|
||||
AreYouThere = 246, /**< Are you there? */
|
||||
EraseChar = 247, /**< Erase character */
|
||||
EraseLine = 248, /**< Erase line */
|
||||
GoAhead = 249, /**< Go ahead in half-duplex mode */
|
||||
SubStart = 250, /**< Option sub-negotiation */
|
||||
WILL = 251, /**< Will use option */
|
||||
WONT = 252, /**< Won't use option */
|
||||
DO = 253, /**< Do use option */
|
||||
DONT = 254, /**< Don't use option */
|
||||
IAC = 255 /**< Interpret As Command */
|
||||
};
|
||||
|
||||
/** Telnet options used in sub-negotiations */
|
||||
enum Option
|
||||
{
|
||||
Binary = 0, /**< Binary transmission */
|
||||
Echo = 1, /**< Echo */
|
||||
Reconnection = 2, /**< Reconnection */
|
||||
SuppressGoAhead = 3, /**< Suppress half-duplex go ahead signals */
|
||||
ApproxMsgSize = 4, /**< Approx message size negotiation */
|
||||
Status = 5, /**< Give status on prevailing options */
|
||||
TimingMark = 6, /**< Timing mark */
|
||||
RemoteTransmitEcho = 7, /**< Remote controlled transmit and echo */
|
||||
LineWidth = 8, /**< Line width */
|
||||
PageSize = 9, /**< Page size */
|
||||
CarriageReturn = 10, /**< Carriage return disposition */
|
||||
HorzTabStops = 11, /**< Horizontal tab stops */
|
||||
HorzTabStopDisp = 12, /**< Horizontal tab stop disposition */
|
||||
FormFeed = 13, /**< Form feed disposition */
|
||||
VertTabStops = 14, /**< Vertical tab stops */
|
||||
VertTabStopDisp = 15, /**< Vertical tab stop disposition */
|
||||
LineFeed = 16, /**< Line feed disposition */
|
||||
ExtendedASCII = 17, /**< Extended ASCII */
|
||||
Logout = 18, /**< Force logout */
|
||||
ByteMacro = 19, /**< Byte macro */
|
||||
DataEntryTerminal = 20, /**< Data entry terminal */
|
||||
SUPDUP = 21, /**< SUPDUP protocol */
|
||||
SUPDUPOutput = 22, /**< SUPDUP output */
|
||||
SendLocation = 23, /**< Send the user's location */
|
||||
TerminalType = 24, /**< Terminal type */
|
||||
EndOfRecordOption = 25, /**< End of record option */
|
||||
TACACSUserId = 26, /**< TACACS user identification */
|
||||
OutputMarking = 27, /**< Output marking */
|
||||
TerminalLocation = 28, /**< Terminal location number */
|
||||
Telnet3270Regime = 29, /**< Telnet 3270 regime */
|
||||
X3Pad = 30, /**< X.3 PAD */
|
||||
WindowSize = 31, /**< Window size */
|
||||
Speed = 32, /**< Terminal speed */
|
||||
RemoteFlowControl = 33, /**< Remote flow control */
|
||||
Linemode = 34, /**< Linemode option */
|
||||
XDisplay = 35, /**< X display location */
|
||||
EnvironmentOld = 36, /**< Environment variables (old version) */
|
||||
Authentication = 37, /**< Authentication */
|
||||
Encryption = 38, /**< Encryption */
|
||||
Environment = 39, /**< Environment variables (new version) */
|
||||
Extended = 255 /**< Extended options list */
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "Terminal.h"
|
||||
#include "TelnetDefs.h"
|
||||
|
||||
/**
|
||||
* \class Terminal Terminal.h <Terminal.h>
|
||||
@ -102,6 +103,13 @@
|
||||
#define STATE_ESC 2 // Last character was ESC.
|
||||
#define STATE_MATCH 3 // Matching an escape sequence.
|
||||
#define STATE_UTF8 4 // Recognizing a UTF-8 sequence.
|
||||
#define STATE_IAC 5 // Recognizing telnet command after IAC (0xFF).
|
||||
#define STATE_WILL 6 // Waiting for option code for WILL command.
|
||||
#define STATE_WONT 7 // Waiting for option code for WONT command.
|
||||
#define STATE_DO 8 // Waiting for option code for DO command.
|
||||
#define STATE_DONT 9 // Waiting for option code for DONT command.
|
||||
#define STATE_SB 10 // Option sub-negotiation.
|
||||
#define STATE_SB_IAC 11 // Option sub-negotiation, byte after IAC.
|
||||
|
||||
// Number of milliseconds to wait after an ESC character before
|
||||
// concluding that it is KEY_ESC rather than an escape sequence.
|
||||
@ -131,6 +139,8 @@ Terminal::Terminal()
|
||||
, offset(0)
|
||||
, state(STATE_INIT)
|
||||
, utf8len(0)
|
||||
, mod(Terminal::Serial)
|
||||
, flags(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -141,19 +151,42 @@ Terminal::~Terminal()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \enum Terminal::Mode
|
||||
* \brief Mode to operate in, Serial or Telnet.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var Terminal::Serial
|
||||
* \brief Operates the terminal in serial mode.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var Terminal::Telnet
|
||||
* \brief Operates the terminal in telnet mode.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Begins terminal operations on an underlying stream.
|
||||
*
|
||||
* \param stream The underlying stream, whether a serial port, TCP connection,
|
||||
* or some other stream.
|
||||
* \param mode The mode to operate in, either Serial or Telnet.
|
||||
*
|
||||
* \sa end()
|
||||
* If Telnet mode is selected, then embedded commands and options from the
|
||||
* telnet protocol (<a href="https://tools.ietf.org/html/rfc854">RFC 854</a>)
|
||||
* will be interpreted. This is useful if the underlying \a stream is a TCP
|
||||
* connection on port 23. The mode operates as a telnet server.
|
||||
*
|
||||
* \sa end(), mode()
|
||||
*/
|
||||
void Terminal::begin(Stream &stream)
|
||||
void Terminal::begin(Stream &stream, Mode mode)
|
||||
{
|
||||
_stream = &stream;
|
||||
ucode = -1;
|
||||
state = STATE_INIT;
|
||||
flags = 0;
|
||||
mod = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,6 +200,13 @@ void Terminal::end()
|
||||
_stream = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \fn Terminal::Mode Terminal::mode() const
|
||||
* \brief Returns the mode this terminal is operating in, Serial or Telnet.
|
||||
*
|
||||
* \sa begin()
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Returns the number of bytes that are available for reading.
|
||||
*
|
||||
@ -321,6 +361,10 @@ static bool escapeSequenceStart(int ch)
|
||||
* with unicodeKey() set to 0x0D. This ensures that all line ending
|
||||
* types are mapped to a single KEY_RETURN report.
|
||||
*
|
||||
* If the window size has changed due to a remote event, then KEY_WINSIZE
|
||||
* will be returned. This can allow the caller to clear and redraw the
|
||||
* window in the new size.
|
||||
*
|
||||
* \sa unicodeKey(), read()
|
||||
*/
|
||||
int Terminal::readKey()
|
||||
@ -378,6 +422,12 @@ int Terminal::readKey()
|
||||
if (ch == 0x0A) {
|
||||
ucode = -1;
|
||||
return -1;
|
||||
} else if (ch == 0x00 && mod == Telnet) {
|
||||
// In telnet mode, CR NUL is a literal carriage return,
|
||||
// separate from the newline sequence CRLF. Eat the NUL.
|
||||
// We already reported KEY_RETURN for the CR character.
|
||||
ucode = -1;
|
||||
return -1;
|
||||
}
|
||||
// Fall through to the next case.
|
||||
|
||||
@ -430,6 +480,9 @@ int Terminal::readKey()
|
||||
offset = ch & 0x07;
|
||||
utf8len = 4;
|
||||
state = STATE_UTF8;
|
||||
} else if (ch == 0xFF && mod == Telnet) {
|
||||
// Start of a telnet command (IAC byte).
|
||||
state = STATE_IAC;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -489,6 +542,170 @@ int Terminal::readKey()
|
||||
state = STATE_INIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_IAC:
|
||||
// Telnet command byte just after an IAC (0xFF) character.
|
||||
switch (ch) {
|
||||
case TelnetDefs::EndOfFile:
|
||||
// Convert EOF into CTRL-D.
|
||||
state = STATE_INIT;
|
||||
ucode = 0x04;
|
||||
return 0x04;
|
||||
|
||||
case TelnetDefs::EndOfRecord:
|
||||
// Convert end of record markers into CR.
|
||||
state = STATE_INIT;
|
||||
ucode = 0x0D;
|
||||
return KEY_RETURN;
|
||||
|
||||
case TelnetDefs::Interrupt:
|
||||
// Convert interrupt into CTRL-C.
|
||||
state = STATE_INIT;
|
||||
ucode = 0x03;
|
||||
return 0x03;
|
||||
|
||||
case TelnetDefs::EraseChar:
|
||||
// Convert erase character into DEL.
|
||||
state = STATE_INIT;
|
||||
ucode = 0x7F;
|
||||
return KEY_BACKSPACE;
|
||||
|
||||
case TelnetDefs::EraseLine:
|
||||
// Convert erase line into CTRL-U.
|
||||
state = STATE_INIT;
|
||||
ucode = 0x15;
|
||||
return 0x15;
|
||||
|
||||
case TelnetDefs::SubStart:
|
||||
// Option sub-negotiation.
|
||||
utf8len = 0;
|
||||
state = STATE_SB;
|
||||
break;
|
||||
|
||||
case TelnetDefs::WILL:
|
||||
// Option negotiation, WILL command.
|
||||
state = STATE_WILL;
|
||||
break;
|
||||
|
||||
case TelnetDefs::WONT:
|
||||
// Option negotiation, WONT command.
|
||||
state = STATE_WONT;
|
||||
break;
|
||||
|
||||
case TelnetDefs::DO:
|
||||
// Option negotiation, DO command.
|
||||
state = STATE_DO;
|
||||
break;
|
||||
|
||||
case TelnetDefs::DONT:
|
||||
// Option negotiation, DONT command.
|
||||
state = STATE_DONT;
|
||||
break;
|
||||
|
||||
case TelnetDefs::IAC:
|
||||
// IAC followed by IAC is the literal byte 0xFF,
|
||||
// but that isn't valid UTF-8 so we just drop it.
|
||||
state = STATE_INIT;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Everything else is treated as a NOP.
|
||||
state = STATE_INIT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_WILL:
|
||||
// Telnet option negotiation, WILL command. Note: We don't do any
|
||||
// loop detection. We assume that the client will eventually break
|
||||
// the loop as it probably has more memory than us to store state.
|
||||
if (ch == TelnetDefs::WindowSize ||
|
||||
ch == TelnetDefs::RemoteFlowControl) {
|
||||
// Send a DO command in response - we accept this option.
|
||||
telnetCommand(TelnetDefs::DO, ch);
|
||||
} else {
|
||||
// Send a DONT command in response - we don't accept this option.
|
||||
telnetCommand(TelnetDefs::DONT, ch);
|
||||
}
|
||||
if (!(flags & 0x01)) {
|
||||
// The first time we see a WILL command from the client we
|
||||
// send a request back saying that we will handle echoing.
|
||||
flags |= 0x01;
|
||||
telnetCommand(TelnetDefs::WILL, TelnetDefs::Echo);
|
||||
}
|
||||
state = STATE_INIT;
|
||||
break;
|
||||
|
||||
case STATE_WONT:
|
||||
case STATE_DONT:
|
||||
// Telnet option negotiation, WONT/DONT command. The other side
|
||||
// is telling us that it does not understand this option or wants
|
||||
// us to stop using it. For now there is nothing to do.
|
||||
state = STATE_INIT;
|
||||
break;
|
||||
|
||||
case STATE_DO:
|
||||
// Telnet option negotiation, DO command. Note: Other than Echo
|
||||
// we don't do any loop detection. We assume that the client will
|
||||
// break the loop as it probably has more memory than us to store state.
|
||||
if (ch == TelnetDefs::Echo) {
|
||||
// Special handling needed for Echo - don't say WILL again
|
||||
// when the client acknowledges us with a DO command.
|
||||
} else if (ch == TelnetDefs::SuppressGoAhead) {
|
||||
// Send a WILL command in response - we accept this option.
|
||||
telnetCommand(TelnetDefs::WILL, ch);
|
||||
} else {
|
||||
// Send a WONT command in response - we don't accept this option.
|
||||
telnetCommand(TelnetDefs::WONT, ch);
|
||||
}
|
||||
state = STATE_INIT;
|
||||
break;
|
||||
|
||||
case STATE_SB:
|
||||
// Telnet option sub-negotiation. Collect up all bytes and
|
||||
// then execute the option once "IAC SubEnd" is seen.
|
||||
if (ch == TelnetDefs::IAC) {
|
||||
// IAC byte, which will be followed by either IAC or SubEnd.
|
||||
state = STATE_SB_IAC;
|
||||
break;
|
||||
}
|
||||
if (utf8len < sizeof(sb))
|
||||
sb[utf8len++] = 0xFF;
|
||||
break;
|
||||
|
||||
case STATE_SB_IAC:
|
||||
// Telnet option sub-negotiation, byte after IAC.
|
||||
if (ch == TelnetDefs::IAC) {
|
||||
// Two IAC bytes in a row is a single escaped 0xFF byte.
|
||||
if (utf8len < sizeof(sb))
|
||||
sb[utf8len++] = 0xFF;
|
||||
state = STATE_SB;
|
||||
break;
|
||||
} else if (ch == TelnetDefs::SubEnd) {
|
||||
// End of the sub-negotiation field. Handle window size changes.
|
||||
if (utf8len >= 5 && sb[0] == TelnetDefs::WindowSize) {
|
||||
int width = (((int)(sb[1])) << 8) | sb[2];
|
||||
int height = (((int)(sb[3])) << 8) | sb[4];
|
||||
if (!width) // Zero width or height means "unspecified".
|
||||
width = ncols;
|
||||
if (!height)
|
||||
height = nrows;
|
||||
|
||||
// Filter out obviously bogus values.
|
||||
if (width >= 1 && height >= 1 && width <= 10000 && height <= 10000) {
|
||||
if (width != ncols || height != nrows) {
|
||||
// The window size has changed; notify the caller.
|
||||
ncols = width;
|
||||
nrows = height;
|
||||
ucode = -1;
|
||||
state = STATE_INIT;
|
||||
return KEY_WINSIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state = STATE_INIT;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we get here, then we're still waiting for a full sequence.
|
||||
@ -558,12 +775,15 @@ size_t Terminal::writeUnicode(long code)
|
||||
* This function should be used if the application has some information
|
||||
* about the actual window size. For serial ports, this usually isn't
|
||||
* available but telnet and ssh sessions can get the window size from
|
||||
* the remote host and set it using this function.
|
||||
* the remote host.
|
||||
*
|
||||
* The window size defaults to 80x24 which is the standard default for
|
||||
* terminal programs like PuTTY that emulate a VT100.
|
||||
*
|
||||
* \sa columns(), rows()
|
||||
* If the window size changes due to a remote event, readKey() will
|
||||
* return KEY_WINSIZE to inform the application.
|
||||
*
|
||||
* \sa columns(), rows(), readKey()
|
||||
*/
|
||||
void Terminal::setWindowSize(int columns, int rows)
|
||||
{
|
||||
@ -1177,3 +1397,18 @@ int Terminal::matchEscape(int ch)
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends a telnet command to the client.
|
||||
*
|
||||
* \param type The type of command: WILL, WONT, DO, or DONT.
|
||||
* \param option The telnet option the command applies to.
|
||||
*/
|
||||
void Terminal::telnetCommand(uint8_t type, uint8_t option)
|
||||
{
|
||||
uint8_t buf[3];
|
||||
buf[0] = (uint8_t)TelnetDefs::IAC;
|
||||
buf[1] = type;
|
||||
buf[2] = option;
|
||||
_stream->write(buf, 3);
|
||||
}
|
||||
|
@ -30,15 +30,26 @@
|
||||
// Special key code that indicates that unicodeKey() contains the actual code.
|
||||
#define KEY_UNICODE 0x1000
|
||||
|
||||
// Special key code that indicates that the window size has changed.
|
||||
#define KEY_WINSIZE 0x1001
|
||||
|
||||
class Terminal : public Stream
|
||||
{
|
||||
public:
|
||||
Terminal();
|
||||
virtual ~Terminal();
|
||||
|
||||
void begin(Stream &stream);
|
||||
enum Mode
|
||||
{
|
||||
Serial,
|
||||
Telnet
|
||||
};
|
||||
|
||||
void begin(Stream &stream, Mode mode = Serial);
|
||||
void end();
|
||||
|
||||
Terminal::Mode mode() const { return (Terminal::Mode)mod; }
|
||||
|
||||
virtual int available();
|
||||
virtual int peek();
|
||||
virtual int read();
|
||||
@ -123,8 +134,12 @@ private:
|
||||
uint16_t offset;
|
||||
uint8_t state;
|
||||
uint8_t utf8len;
|
||||
uint8_t mod;
|
||||
uint8_t sb[16];
|
||||
uint8_t flags;
|
||||
|
||||
int matchEscape(int ch);
|
||||
void telnetCommand(uint8_t type, uint8_t option);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
87
libraries/Terminal/examples/TelnetServer/TelnetServer.ino
Normal file
87
libraries/Terminal/examples/TelnetServer/TelnetServer.ino
Normal file
@ -0,0 +1,87 @@
|
||||
|
||||
/*
|
||||
This example demonstrates how to create a simple telnet server.
|
||||
|
||||
This example is placed into the public domain.
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <Shell.h>
|
||||
|
||||
byte macAddress[6] = {
|
||||
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
|
||||
};
|
||||
|
||||
int ledPin = 13;
|
||||
|
||||
EthernetServer server(23);
|
||||
EthernetClient client;
|
||||
bool haveClient = false;
|
||||
|
||||
Shell shell;
|
||||
|
||||
void cmdLed(Shell &shell, int argc, char *argv[])
|
||||
{
|
||||
if (argc > 1 && !strcmp(argv[1], "on"))
|
||||
digitalWrite(ledPin, HIGH);
|
||||
else
|
||||
digitalWrite(ledPin, LOW);
|
||||
}
|
||||
|
||||
void cmdExit(Shell &shell, int argc, char *argv[])
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
ShellCommand(led, "Turns the status LED on or off", cmdLed);
|
||||
ShellCommand(exit, "Exit and log out", cmdExit);
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Configure I/O.
|
||||
pinMode(ledPin, OUTPUT);
|
||||
digitalWrite(ledPin, LOW);
|
||||
|
||||
// Start the serial port for status messages.
|
||||
Serial.begin(9600);
|
||||
Serial.println();
|
||||
Serial.print("Acquiring IP address ... ");
|
||||
|
||||
// Start Ethernet running and get an IP address via DHCP.
|
||||
if (Ethernet.begin(macAddress))
|
||||
Serial.println(Ethernet.localIP());
|
||||
else
|
||||
Serial.println("failed");
|
||||
|
||||
// Listen on port 23 for incoming telnet connections.
|
||||
server.begin();
|
||||
|
||||
// Configure the shell. We call Shell::begin() once we have a connection.
|
||||
shell.setPrompt("$ ");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Maintain the DHCP lease over time.
|
||||
Ethernet.maintain();
|
||||
|
||||
// Handle new/disconnecting clients.
|
||||
if (!haveClient) {
|
||||
// Check for new client connections.
|
||||
client = server.available();
|
||||
if (client) {
|
||||
haveClient = true;
|
||||
shell.begin(client, 5, Terminal::Telnet);
|
||||
}
|
||||
} else if (!client.connected()) {
|
||||
// The current client has been disconnected. Shut down the shell.
|
||||
shell.end();
|
||||
client.stop();
|
||||
client = EthernetClient();
|
||||
haveClient = false;
|
||||
}
|
||||
|
||||
// Perform periodic shell processing on the active client.
|
||||
shell.loop();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user