Home

A simple Wi-Fi telnet server for Sonoff TH 10/16 and Arduino

With WPS/PBC and software modification for sensor Sonoff Si7021 too

Sonopff TH 16 device


// Telnet controller V1.20 / 2017-12-05 for SONOFF TH 10/16
// Wi-Fi Protected Setup with PBC ready
// Copyright 2017 Pavel Janko, www.fancon.cz
// MIT license, http://opensource.org/licenses/MIT
//
// Add DHT sensor library 1.3.0 by Adafruit from
// Arduino IDE - Sketch/Include Library/Manage Libraries.
// Tested with ARDUINO ESP8266 2.3.0, IDE 1.8.5
// Sonoff TH 10/16 has 1MB flash memory only !
// For flashing disconnect the AC power! Power Sonoff only from
// the USB to Serial converter for your safety !
// Uploading a new application to the Sonoff cancel original firmware !
// Set Generic ESP8266 or ESP8285 by chip types and 1M(64K SPIFFS)
// After flash restart the device.
//
// To connect to an access point, hold down the Sonoff button for about
// three seconds (blue LED is lit permanently) and activate WPS
// (Wi-Fi Protected Setup) with PBC (Push Button Configuration) on your
// Access point.
//
//   |--------------------------------------------------------------------------------|
//   |       Button       |         LED         |       Action                        |
//   |--------------------------------------------------------------------------------|
//   | short press        |      ON or OFF      | change relay state                  |
//   | long press > 3sec. |      ON             | wait for WPS from Access point      |
//   |      --            | flashes 1Hz         | connection to WiFi                  |
//   |      --            | flashes irregularly | receives data                       |
//   | before power on    |     --              | flash mode                          |
//   |--------------------------------------------------------------------------------|
//
//   |--------------------------------------------------------------------------------|
//   | Server command |                Response                                       |
//   |--------------------------------------------------------------------------------|
//   |  connect       | returns device ID as IDdddddd<CRLF> and prompt ">"            |
//   |  HUMI<CRLF>    | returns humidity    dd.d<CRLF> and prompt ">"                 |
//   |  TEMP<CRLF>    | returns temperature dd.d<CRLF> and prompt ">"                 |
//   |  RELE<CRLF>    | returns status of the relay 0<CRLF> or 1<CRLF> and prompt ">" |
//   |  REL1<CRLF>    | returns 1<CRLF> and prompt ">", relay ON,                     |
//   |  REL0<CRLF>    | returns 0<CRLF> and prompt ">", relay OFF                     |
//   |  RSSI<CRLF>    | returns -dd<CRLF> and prompt ">" RSSI [dBm]                   |
//   |  QUIT<CRLF>    | returns BYE<CRLF> and disconnect client                       |
//   |  else          | returns ERROR<CRLF> and prompt ">"                            |
//   |  Inactivity of |                                                               |
//   |  client 60 sec.| returns BYE<CRLF> and disconnect client                       |
//   |--------------------------------------------------------------------------------|
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// *For sensor Sonoff Si7021 modify in DHT sensor library 1.3.0
// file DHT.cpp, row 146:
//      delay(20);               disable this row !!!
//      delayMicroseconds(500);  add this new row !!!
#define DHTTYPE DHT21      // Sensor type for AM2301 / DHT21 / *Si7021
#define GRADE        false // Set scale false=Celsius, true=Fahrenheit 
#define SERVERPORT  2323
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
#define PATTERNHUMI "HUMI\r\n"    // command for read humidity. 
#define PATTERNTEMP "TEMP\r\n"    // command for read temperature.
#define PATTERNRELE "RELE\r\n"    // command for read relay state.
#define PATTERNRELEOFF "REL0\r\n" // command for set relay to off.
#define PATTERNRELEON  "REL1\r\n" // command for set relay to on.
#define PATTERNQUIT "QUIT\r\n"    // command for quit.
#define PATTERNRSSI "RSSI\r\n"    // command for WiFi RSSI.
#define BUTTONPIN    0     // Button pin on GPIO0
#define RELAYPIN    12     // GPIO12 Relay and red LED
#define LEDBLUEPIN  13     // GPIO13 blue LED, LOW = LED ON !
#define DHTPIN      14     // Set pin for AM2301/DHT21 sensor connecting
#define BUTTONTIME 0.25    // Set time for periodic read button in sec
#define SENSORTIME   5     // Set time for periodic read sensor in sec
#define KILLERTIME  10     // Set time for periodic kill clients in sec
#define KILLERLIMIT  6     // 6*10s = 60s time to kill for inactiv clients
#define MAX_SRV_CLIENTS 3  // Maximal client number is 5
#define MAX_BUF_LEN     30 // input buffer lenght
#define FLOATROUNDING   1
#include <DHT.h>
#include <DHT_U.h>
#include <Ticker.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
float  temperature = 0;
float  humidity = 0;
char ButtonCount = 0;
char ClientKiller[MAX_SRV_CLIENTS];
char Inbuff [MAX_SRV_CLIENTS][MAX_BUF_LEN];
char Pointer[MAX_SRV_CLIENTS];
bool SensorFlag     = true;
bool KillerFlag     = true;
bool ButtonFlag     = true;
bool RelayState     = false; // relay OFF
bool RafHo          = false; // WPS flag
bool LedBlueState   = true;  // GPIO13 blue LED OFF !
String ToSend;
String HowDelka;
String TermChars = "\r\n>>";
//----------------------------------------------------------------------
WiFiServer server(SERVERPORT);     // Telnet port set to 2323
WiFiClient serverClients[MAX_SRV_CLIENTS];
DHT dht(DHTPIN, DHTTYPE);
Ticker ButtonTick;           // Preparing for periodic button reading
Ticker SensorTick;           // Preparing for periodic sensor reading
Ticker KillerTick;           // Preparing for periodic client killer
//----------------------------------------------------------------------
void ButtonFlagSet(void) {
  ButtonFlag = true;
}
void SensorFlagSet(void) {
  SensorFlag = true;
}
void KillerFlagSet(void) {
  KillerFlag = true;
}
void GetButton(void) {
  /* short press butoon to change state of relay */
  if (digitalRead(BUTTONPIN) == false ) {
    ++ButtonCount;
  }
  if (digitalRead(BUTTONPIN) == true && ButtonCount > 1 && ButtonCount < 11 ) {
    RelayState = !RelayState;  // change relay state
    digitalWrite(RELAYPIN, RelayState);
    ButtonCount = 0;
  }
  /* long press button - WPS mode */
  if (ButtonCount > 12) {
    LedBlueState = false;  // Blue LED ON
    digitalWrite(LEDBLUEPIN, LedBlueState);
    RelayState = false;    // Relay OFF
    digitalWrite(RELAYPIN, RelayState);
    ButtonTick.detach();   // Stop Ticker
    SensorTick.detach();
    KillerTick.detach();
    /* Wait for release button */
    while (!digitalRead(BUTTONPIN)) {
      yield();
    }
    delay(100);
    WiFi.persistent(false);
    WiFi.mode(WIFI_STA);
    /* Wait for access point WPS */
    do {
      RafHo = WiFi.beginWPSConfig();
      //Serial.println("Wait for WPS");
      HowDelka = WiFi.SSID();
    }
    while (RafHo == false || HowDelka.length() == 0);
    //Serial.println("");
    //Serial.println(WiFi.SSID());
    //Serial.println(WiFi.psk());
    delay(500);
    ESP.restart();
  }
  if (digitalRead(BUTTONPIN) == true ) {
    ButtonCount = 0;
  }
  ButtonFlag = false;
}
/* Read humidity (percent)*/
/* Read temperature as Celsius or Fahrenheit*/
void GetSensor(void) {
  humidity = dht.readHumidity();
  temperature = dht.readTemperature(GRADE);
  /* If disconnected WiFi then restart */
  if (WiFi.status() != WL_CONNECTED) {
    ESP.restart();
  }
  SensorFlag = false;
}
/* Kill 60s inactive clients */
void GetKiller(void) {
  for (char k = 0; k < MAX_SRV_CLIENTS; k++) {
    ++ClientKiller[k];
    if (ClientKiller[k] >= KILLERLIMIT) {
      if (serverClients[k].connected()) {
        ToSend = String("BYE\r\n\n") ;
        SendDataFtp(k);
      }
      if (serverClients[k])
        serverClients[k].stop();
    }
  }
  KillerFlag = false;
}
/* send data to client */
void SendDataFtp(char x) {
  ClientKiller[x] = 0;
  size_t len = ToSend.length();
  uint8_t sbuf[len];
  ToSend.getBytes(sbuf, len);
  if (serverClients[x] && serverClients[x].connected()) {
    serverClients[x].write(sbuf, len - 1);
  }
}
void ParseIt(char i) {
  /* parse data from the telnet client */
  LedBlueState = false; // Blue LED ON
  digitalWrite(LEDBLUEPIN, LedBlueState);
  if (String(PATTERNHUMI) == String(Inbuff[i])) {
    ToSend = String(humidity, FLOATROUNDING) + TermChars;
    SendDataFtp(i);
  }
  else if (String(PATTERNTEMP) == String(Inbuff[i])) {
    ToSend = String(temperature, FLOATROUNDING) + TermChars;
    SendDataFtp(i);
  }
  else if (String(PATTERNRELE) == String(Inbuff[i])) {
    ToSend = String(RelayState) + TermChars;
    SendDataFtp(i);
  }
  else if (String(PATTERNRELEON) == String(Inbuff[i])) {
    RelayState = 1;
    digitalWrite(RELAYPIN, RelayState);
    ToSend = String(RelayState) + TermChars;
    SendDataFtp(i);
  }
  else if (String(PATTERNRELEOFF) == String(Inbuff[i])) {
    RelayState = 0;
    digitalWrite(RELAYPIN, RelayState);
    ToSend = String(RelayState) + TermChars;
    SendDataFtp(i);
  }
  else if (String(PATTERNQUIT ) == String(Inbuff[i])) {
    ToSend = String("BYE\r\n\n") ;
    SendDataFtp(i);
    serverClients[i].stop();
  }
  else if (String(PATTERNRSSI) == String(Inbuff[i])) {
    ToSend = String(WiFi.RSSI()) + TermChars;
    SendDataFtp(i);
  }
  else {
    ToSend = String("ERROR\r\n>>") ;
    SendDataFtp(i);
  }
  LedBlueState = true; // Blue LED OFF
  digitalWrite(LEDBLUEPIN, LedBlueState);
}
//----------------------------------------------------------------------
void setup(void) {
  /* set ports */
  pinMode(RELAYPIN, OUTPUT);
  digitalWrite(RELAYPIN, RelayState);
  pinMode(LEDBLUEPIN, OUTPUT);
  digitalWrite(LEDBLUEPIN, LedBlueState);
  pinMode(BUTTONPIN, INPUT);
  //Serial.begin(115200);
  WiFi.begin();
  while (WiFi.status() != WL_CONNECTED) {
    LedBlueState = !LedBlueState;
    digitalWrite(LEDBLUEPIN, LedBlueState);
    GetButton();
    delay(250);
    GetButton();
    delay(250);
    //Serial.print(".");
  }
  LedBlueState = true;
  digitalWrite(LEDBLUEPIN, LedBlueState);
  //Serial.println("");
  //Serial.print("Connected to ");
  //Serial.println(WiFi.SSID());
  //Serial.print("IP address: ");
  //Serial.println(WiFi.localIP());
  ButtonTick.attach(BUTTONTIME, ButtonFlagSet);
  SensorTick.attach(SENSORTIME, SensorFlagSet);
  KillerTick.attach(KILLERTIME, KillerFlagSet);
  server.begin();
  server.setNoDelay(true);
  //Serial.println("FTP server started!");
}
//----------------------------------------------------------------------
void loop(void) {
  char i;
  /* check if there are any new telnet clients */
  if (server.hasClient()) {
    for (i = 0; i < MAX_SRV_CLIENTS; i++) {
      /* find free or disconnected spot*/
      if (!serverClients[i] || !serverClients[i].connected()) {
        if (serverClients[i]) {
          serverClients[i].stop();
        }
        serverClients[i] = server.available();
        ToSend = "ID" + String(ESP.getChipId()) + TermChars;
        SendDataFtp(i);// Send "Hallo" for new client
        continue;
      }
    }
    /* no free/disconnected spot so reject new client */
    WiFiClient serverClient = server.available();
    serverClient.stop();
  }
  /* check telnet clients for data */
  for (i = 0; i < MAX_SRV_CLIENTS; i++) {
    if (serverClients[i] && serverClients[i].connected()) {
      if (serverClients[i].available()) {
        while (serverClients[i].available()) {
          if (Pointer[i] < MAX_BUF_LEN) {
            Inbuff[i][Pointer[i]] = serverClients[i].read();
            if (Pointer[i] > 0) {
              if (Inbuff[i][Pointer[i] - 1] == '\r' && Inbuff[i][Pointer[i]] == '\n') {
                ParseIt(i);
                Pointer[i] = 0;
                for (char j = 0; j < MAX_BUF_LEN; j++)Inbuff[i][j] = 0;
                continue;
              }
            }
          }
          else {
            Pointer[i] = 0;
            for (char j = 0; j < MAX_BUF_LEN; j++)Inbuff[i][j] = 0;
            continue;
          }
          ++Pointer[i];
        }
      }
    }
  }
  if (ButtonFlag == true)GetButton();
  if (SensorFlag == true)GetSensor();
  if (KillerFlag == true)GetKiller();
}

Here is Arduino sketch for download

Forum

Dec / 2017

UP