Browse Source

Telnet support

fastled
Xose Pérez 7 years ago
parent
commit
b946cf32dd
9 changed files with 287 additions and 11 deletions
  1. +2
    -0
      code/espurna/config/arduino.h
  2. +21
    -1
      code/espurna/config/general.h
  3. +10
    -2
      code/espurna/debug.ino
  4. +3
    -0
      code/espurna/espurna.ino
  5. +1
    -1
      code/espurna/ota.ino
  6. +78
    -0
      code/espurna/settings.h
  7. +25
    -3
      code/espurna/settings.ino
  8. +128
    -0
      code/espurna/telnet.ino
  9. +19
    -4
      code/espurna/wifi.ino

+ 2
- 0
code/espurna/config/arduino.h View File

@ -48,6 +48,7 @@
//#define ANALOG_SUPPORT 1 //#define ANALOG_SUPPORT 1
//#define COUNTER_SUPPORT 1 //#define COUNTER_SUPPORT 1
//#define DEBUG_SERIAL_SUPPORT 0 //#define DEBUG_SERIAL_SUPPORT 0
//#define DEBUG_TELNET_SUPPORT 0
//#define DEBUG_UDP_SUPPORT 1 //#define DEBUG_UDP_SUPPORT 1
//#define DHT_SUPPORT 1 //#define DHT_SUPPORT 1
//#define DOMOTICZ_SUPPORT 0 //#define DOMOTICZ_SUPPORT 0
@ -61,5 +62,6 @@
//#define NTP_SUPPORT 0 //#define NTP_SUPPORT 0
//#define RF_SUPPORT 1 //#define RF_SUPPORT 1
//#define SPIFFS_SUPPORT 1 //#define SPIFFS_SUPPORT 1
//#define TELNET_SUPPORT 0
//#define TERMINAL_SUPPORT 0 //#define TERMINAL_SUPPORT 0
//#define WEB_SUPPORT 0 //#define WEB_SUPPORT 0

+ 21
- 1
code/espurna/config/general.h View File

@ -9,6 +9,18 @@
#define ADMIN_PASS "fibonacci" // Default password (WEB, OTA, WIFI) #define ADMIN_PASS "fibonacci" // Default password (WEB, OTA, WIFI)
//------------------------------------------------------------------------------
// TELNET
//------------------------------------------------------------------------------
#ifndef TELNET_SUPPORT
#define TELNET_SUPPORT 1 // Enable telnet support by default
#endif
#define TELNET_ONLY_AP 1 // By default, allow only connections via AP interface
#define TELNET_PORT 23 // Port to listen to telnet clients
#define TELNET_MAX_CLIENTS 1 // Max number of concurrent telnet clients
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// DEBUG // DEBUG
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -36,10 +48,18 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifndef DEBUG_TELNET_SUPPORT
#define DEBUG_TELNET_SUPPORT TELNET_SUPPORT // Enable telnet debug log if telnet is enabled too
#endif
//------------------------------------------------------------------------------
// General debug options and macros // General debug options and macros
#define DEBUG_MESSAGE_MAX_LENGTH 80 #define DEBUG_MESSAGE_MAX_LENGTH 80
#define DEBUG_SUPPORT DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT || DEBUG_TELNET_SUPPORT
#if (DEBUG_SERIAL_SUPPORT==1) || (DEBUG_UDP_SUPPORT==1)
#if DEBUG_SUPPORT
#define DEBUG_MSG(...) debugSend(__VA_ARGS__) #define DEBUG_MSG(...) debugSend(__VA_ARGS__)
#define DEBUG_MSG_P(...) debugSend_P(__VA_ARGS__) #define DEBUG_MSG_P(...) debugSend_P(__VA_ARGS__)
#endif #endif


+ 10
- 2
code/espurna/debug.ino View File

@ -6,7 +6,7 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/ */
#if DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT
#if DEBUG_SUPPORT
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
@ -44,6 +44,10 @@ void debugSend(const char * format, ...) {
} }
#endif #endif
#if DEBUG_TELNET_SUPPORT
_telnetWrite(buffer, strlen(buffer));
#endif
} }
void debugSend_P(PGM_P format, ...) { void debugSend_P(PGM_P format, ...) {
@ -77,6 +81,10 @@ void debugSend_P(PGM_P format, ...) {
} }
#endif #endif
#if DEBUG_TELNET_SUPPORT
_telnetWrite(buffer, strlen(buffer));
#endif
} }
#endif // DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT
#endif // DEBUG_SUPPORT

+ 3
- 0
code/espurna/espurna.ino View File

@ -225,6 +225,9 @@ void setup() {
delay(500); delay(500);
wifiSetup(); wifiSetup();
otaSetup(); otaSetup();
#if TELNET_SUPPORT
telnetSetup();
#endif
// Do not run the next services if system is flagged stable // Do not run the next services if system is flagged stable
if (!systemCheck()) return; if (!systemCheck()) return;


+ 1
- 1
code/espurna/ota.ino View File

@ -43,7 +43,7 @@ void otaSetup() {
}); });
ArduinoOTA.onError([](ota_error_t error) { ArduinoOTA.onError([](ota_error_t error) {
#if DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT
#if DEBUG_SUPPORT
DEBUG_MSG_P(PSTR("\n[OTA] Error #%u: "), error); DEBUG_MSG_P(PSTR("\n[OTA] Error #%u: "), error);
if (error == OTA_AUTH_ERROR) DEBUG_MSG_P(PSTR("Auth Failed\n")); if (error == OTA_AUTH_ERROR) DEBUG_MSG_P(PSTR("Auth Failed\n"));
else if (error == OTA_BEGIN_ERROR) DEBUG_MSG_P(PSTR("Begin Failed\n")); else if (error == OTA_BEGIN_ERROR) DEBUG_MSG_P(PSTR("Begin Failed\n"));


+ 78
- 0
code/espurna/settings.h View File

@ -0,0 +1,78 @@
// -----------------------------------------------------------------------------
// Stream Injector
// -----------------------------------------------------------------------------
#pragma once
#define STREAM_INJECTOR_BUFFER_SIZE 32
class StreamInjector : public Stream {
public:
typedef std::function<void(uint8_t ch)> writeCallback;
StreamInjector(Stream& serial) : _stream(serial) {}
virtual void callback(writeCallback c) {
_callback = c;
}
virtual size_t write(uint8_t ch) {
if (_callback) _callback(ch);
return _stream.write(ch);
}
virtual int read() {
int ch = _stream.read();
if (ch == -1) {
if (_buffer_read != _buffer_write) {
ch = _buffer[_buffer_read];
_buffer_read = (_buffer_read + 1) % STREAM_INJECTOR_BUFFER_SIZE;
}
}
return ch;
}
virtual int available() {
unsigned int bytes = _stream.available();
if (_buffer_read > _buffer_write) {
bytes += (_buffer_write - _buffer_read + STREAM_INJECTOR_BUFFER_SIZE);
} else if (_buffer_read < _buffer_write) {
bytes += (_buffer_write - _buffer_read);
}
return bytes;
}
virtual int peek() {
int ch = _stream.peek();
if (ch == -1) {
if (_buffer_read != _buffer_write) {
ch = _buffer[_buffer_read];
}
}
return ch;
}
virtual void flush() {
_stream.flush();
_buffer_read = _buffer_write;
}
virtual void inject(char *data, size_t len) {
for (int i=0; i<len; i++) {
_buffer[_buffer_write] = data[i];
_buffer_write = (_buffer_write + 1) % STREAM_INJECTOR_BUFFER_SIZE;
}
}
private:
Stream& _stream;
unsigned char _buffer[STREAM_INJECTOR_BUFFER_SIZE];
unsigned char _buffer_write = 0;
unsigned char _buffer_read = 0;
writeCallback _callback = NULL;
};

+ 25
- 3
code/espurna/settings.ino View File

@ -11,10 +11,20 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#include "spi_flash.h" #include "spi_flash.h"
#include <StreamString.h> #include <StreamString.h>
#ifdef DEBUG_PORT
Embedis embedis(DEBUG_PORT);
#if TELNET_SUPPORT
#include "settings.h"
#ifdef DEBUG_PORT
StreamInjector _serial = StreamInjector(DEBUG_PORT);
#else
StreamInjector _serial = StreamInjector(Serial);
#endif
Embedis embedis(_serial);
#else #else
Embedis embedis(Serial);
#ifdef DEBUG_PORT
Embedis embedis(DEBUG_PORT);
#else
Embedis embedis(_serial);
#endif
#endif #endif
bool _settings_save = false; bool _settings_save = false;
@ -23,6 +33,12 @@ bool _settings_save = false;
// Settings // Settings
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if TELNET_SUPPORT
void settingsInject(void *data, size_t len) {
_serial.inject((char *) data, len);
}
#endif
size_t settingsMaxSize() { size_t settingsMaxSize() {
size_t size = EEPROM_SIZE; size_t size = EEPROM_SIZE;
if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE; if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE;
@ -82,6 +98,12 @@ void settingsSetup() {
EEPROM.begin(SPI_FLASH_SEC_SIZE); EEPROM.begin(SPI_FLASH_SEC_SIZE);
#if TELNET_SUPPORT
_serial.callback([](uint8_t ch) {
telnetWrite(ch);
});
#endif
Embedis::dictionary( F("EEPROM"), Embedis::dictionary( F("EEPROM"),
SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE,
[](size_t pos) -> char { return EEPROM.read(pos); }, [](size_t pos) -> char { return EEPROM.read(pos); },


+ 128
- 0
code/espurna/telnet.ino View File

@ -0,0 +1,128 @@
/*
TELNET MODULE
Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
Parts of the code have been borrowed from Thomas Sarlandie's NetServer
(https://github.com/sarfata/kbox-firmware/tree/master/src/esp)
*/
#if TELNET_SUPPORT
#include <ESPAsyncTCP.h>
AsyncServer * _telnetServer;
AsyncClient * _telnetClients[TELNET_MAX_CLIENTS];
// -----------------------------------------------------------------------------
// Private methods
// -----------------------------------------------------------------------------
void _telnetDisconnect(unsigned char clientId) {
_telnetClients[clientId]->free();
delete(_telnetClients[clientId]);
_telnetClients[clientId] = 0;
DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId);
}
bool _telnetWrite(unsigned char clientId, void *data, size_t len) {
if (_telnetClients[clientId] && _telnetClients[clientId]->connected()) {
return (_telnetClients[clientId]->write((const char*) data, len) > 0);
}
return false;
}
unsigned char _telnetWrite(void *data, size_t len) {
unsigned char count = 0;
for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
if (_telnetWrite(i, data, len)) ++count;
}
return count;
}
void _telnetData(unsigned char clientId, void *data, size_t len) {
// Capture close connection
char * p = (char *) data;
if (strncmp(p, "close", 5) == 0) {
_telnetClients[clientId]->close();
return;
}
// Inject into Embedis stream
settingsInject(data, len);
}
void _telnetNewClient(AsyncClient *client) {
client->onDisconnect([](void *s, AsyncClient *c) {
delete(c);
});
#if TELNET_ONLY_AP
if (client->localIP() != WiFi.softAPIP()) {
DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
client->stop();
}
#endif
for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
_telnetClients[i] = client;
client->onAck([i](void *s, AsyncClient *c, size_t len, uint32_t time) {
}, 0);
client->onData([i](void *s, AsyncClient *c, void *data, size_t len) {
_telnetData(i, data, len);
}, 0);
client->onDisconnect([i](void *s, AsyncClient *c) {
_telnetDisconnect(i);
}, 0);
client->onError([i](void *s, AsyncClient *c, int8_t error) {
DEBUG_MSG_P(PSTR("[TELNET] Error %s (%d) on client #%d\n"), c->errorToString(error), error, i);
}, 0);
client->onTimeout([i](void *s, AsyncClient *c, uint32_t time) {
DEBUG_MSG_P(PSTR("[TELNET] Timeout on client #%d at %i\n"), i, time);
c->close();
}, 0);
DEBUG_MSG_P(PSTR("[TELNET] Client #%d connected\n"), i);
return;
}
}
DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
client->stop();
}
// -----------------------------------------------------------------------------
// Public API
// -----------------------------------------------------------------------------
unsigned char telnetWrite(unsigned char ch) {
char data[1] = {ch};
return _telnetWrite(data, 1);
}
void telnetSetup() {
_telnetServer = new AsyncServer(TELNET_PORT);
_telnetServer->onClient([](void *s, AsyncClient* c) {
_telnetNewClient(c);
}, 0);
_telnetServer->begin();
DEBUG_MSG_P(PSTR("[TELNET] Listening on port %d\n"), TELNET_PORT);
}
#endif // TELNET_SUPPORT

+ 19
- 4
code/espurna/wifi.ino View File

@ -172,7 +172,7 @@ void wifiSetup() {
// Message callbacks // Message callbacks
jw.onMessage([](justwifi_messages_t code, char * parameter) { jw.onMessage([](justwifi_messages_t code, char * parameter) {
#if DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT
#if DEBUG_SUPPORT
if (code == MESSAGE_SCANNING) { if (code == MESSAGE_SCANNING) {
DEBUG_MSG_P(PSTR("[WIFI] Scanning\n")); DEBUG_MSG_P(PSTR("[WIFI] Scanning\n"));
@ -226,19 +226,34 @@ void wifiSetup() {
DEBUG_MSG_P(PSTR("[WIFI] Could not create access point\n")); DEBUG_MSG_P(PSTR("[WIFI] Could not create access point\n"));
} }
#endif // DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT
#endif // DEBUG_SUPPORT
// Configure mDNS // Configure mDNS
#if MDNS_SUPPORT #if MDNS_SUPPORT
if (code == MESSAGE_CONNECTED || code == MESSAGE_ACCESSPOINT_CREATED) { if (code == MESSAGE_CONNECTED || code == MESSAGE_ACCESSPOINT_CREATED) {
if (MDNS.begin(WiFi.getMode() == WIFI_AP ? APP_NAME : (char *) WiFi.hostname().c_str())) { if (MDNS.begin(WiFi.getMode() == WIFI_AP ? APP_NAME : (char *) WiFi.hostname().c_str())) {
MDNS.addService("http", "tcp", getSetting("webPort", WEB_PORT).toInt());
DEBUG_MSG_P(PSTR("[MDNS] OK\n"));
DEBUG_MSG_P(PSTR("[MDNS] OK\n"));
#if WEB_SUPPORT
MDNS.addService("http", "tcp", getSetting("webPort", WEB_PORT).toInt());
#endif
#if TELNET_SUPPORT
MDNS.addService("telnet", "tcp", TELNET_PORT);
#endif
if (code == MESSAGE_CONNECTED) mqttDiscover(); if (code == MESSAGE_CONNECTED) mqttDiscover();
} else { } else {
DEBUG_MSG_P(PSTR("[MDNS] FAIL\n")); DEBUG_MSG_P(PSTR("[MDNS] FAIL\n"));
} }
} }
#endif #endif
// NTP connection reset // NTP connection reset


Loading…
Cancel
Save