ESP-WROOM-02でLinuxサーバーを操作する

FTP + pyinotify GUI編


前回、FTPクラインとpyinotifyを 使って、 ESPからLinuxサーバー(RaspberryPiやOrangePi)を操作する方法を紹介しました。
そこで、タッチパネル操作でLinuxサーバーをリモートで操作してみることにします。
手順は前回と同様に以下の様になります。
RaspberryPi/OrangePi ESP8266
FTPサーバを立てる
pyinotifyで特定のディレクトリを監視する

タッチパネルのタッチされた場所を判定する

FTPクライアントを使ってファイルを書き込む
ファイル名に応じたコマンドを実行する

ハードウェアはこちらで紹介してるHSES- LCD-24を使いました。


ESP側のスケッチは前回のスケッチにタッチパネル用のGUIを追加しただけです。
FTPサーバーのユーザ名、パスワード、IPアドレス(192, 168, 10, 145)は、自分の環境に合わせて変更してください。
// File: WiWi_FTP_Client.ino for ESP8266 NodeMCU
/*
   Original Arduino: FTP passive client
   http://playground.arduino.cc/Code/FTP
   Modified 6 June 2015 by SurferTim

   You can pass flash-memory based strings to Serial.print() by wrapping them with F().

   2015-12-09 Rudolf Reuter, adapted to ESP8266 NodeMCU, with the help of Markus.
*/
#include <SPI.h>
#include "Adafruit_GFX.h" // https://github.com/adafruit/Adafruit-GFX-Library
#include "Adafruit_ILI9341.h" // https://github.com/adafruit/Adafruit_ILI9341
#include "Fontx.h" // https://github.com/h-nari/Fontx
#include "FontxGfx.h" // https://github.com/h-nari/FontxGfx
#include "Humblesoft_ILI9341.h" // https://github.com/h-nari/Humblesoft_ILI9341
#include "XPT2046_Touchscreen.h" //https://github.com/PaulStoffregen/XPT2046_Touchscreen

#include <ESP8266WiFi.h>
#include <FS.h>


//フォントファイルのパスは絶対パス
IMPORT_BIN("/fontx/ILGH16XB.FNT", font_h);
IMPORT_BIN("/fontx/ILGZ16XB.FNT", font_z);

extern uint8_t font_h[], font_z[];

Humblesoft_ILI9341 tft = Humblesoft_ILI9341();

#define CS_PIN  16
XPT2046_Touchscreen ts(CS_PIN);


// Set these to your desired softAP credentials. They are not configurable at runtime.
const char* ssid = "アクセスポイントのSSID";
const char* password = "アクセスポイントのパスワード";
const char* hostuser = "FTPサーバーのユーザ名";
const char* hostpasswd = "FTPサーバーの パスワード";

boolean debug = false;  // true = more messages
//boolean debug = true;

// LED is needed for failure signalling
//const short int BUILTIN_LED2 = 16;  //GPIO16 on NodeMCU (ESP-12)
const short int BUILTIN_LED2 = 0;

//unsigned long startTime = millis();

// provide text for the WiFi status
const char *str_status[]= {
  "WL_IDLE_STATUS",
  "WL_NO_SSID_AVAIL",
  "WL_SCAN_COMPLETED",
  "WL_CONNECTED",
  "WL_CONNECT_FAILED",
  "WL_CONNECTION_LOST",
  "WL_DISCONNECTED"
};

// provide text for the WiFi mode
const char *str_mode[]= { "WIFI_OFF", "WIFI_STA", "WIFI_AP", "WIFI_AP_STA" };

// change to your server
IPAddress server( 192, 168, 10, 145 );

WiFiClient client;
WiFiClient dclient;

char outBuf[128];
char outCount;

const char* ESP8266File = "/esp8266.txt";

// SPIFFS file handle
File fh;

// Display構造体
struct DISP {
  uint16_t rec_x;
  uint16_t rec_y;
  uint16_t rec_w;
  uint16_t rec_h;
  uint16_t rec_c;
  uint16_t text_x;
  uint16_t text_y;
  char     text[16];
  uint16_t text_c;
};

struct DISP design[2];
const int idNum = sizeof design /sizeof design[0];

#define FRAME 4

void signalError() {  // loop endless with LED blinking in case of error
  while(1) {
    if (BUILTIN_LED2 > 0) {
      digitalWrite(BUILTIN_LED2, LOW);
      delay(300); // ms
      digitalWrite(BUILTIN_LED2, HIGH);
      delay(300); // ms
    } // end if
  } // end while
}

//----------------------- WiFi handling
void connectWifi() {
  Serial.print("Connecting as wifi client to SSID: ");
  Serial.println(ssid);

  // use in case of mode problem
  WiFi.disconnect();
  // switch to Station mode
  if (WiFi.getMode() != WIFI_STA) {
    WiFi.mode(WIFI_STA);
  }

  WiFi.begin ( ssid, password );

  if (debug) WiFi.printDiag(Serial);

  // ... Give ESP 10 seconds to connect to station.
  unsigned long startTime = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  // Check connection
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.print("WiFi connect failed to ssid: ");
    Serial.println(ssid);
    Serial.print("WiFi password <");
    Serial.print(password);
    Serial.println(">");
    Serial.println("Check for wrong typing!");
  }
}  // connectWiFi()

//----------------- FTP fail
void efail() {
  byte thisByte = 0;

  client.println(F("QUIT"));

  while (!client.available()) delay(1);

  while (client.available()) {
    thisByte = client.read();
    Serial.write(thisByte);
  }

  client.stop();
  Serial.println(F("Command disconnected"));
  fh.close();
  Serial.println(F("SD closed"));
}  // efail

//-------------- FTP receive
byte eRcv() {
  byte respCode;
  byte thisByte;

  while (!client.available()) delay(1);

  respCode = client.peek();

  outCount = 0;

  while (client.available()) {
    thisByte = client.read();
    Serial.write(thisByte);

    if (outCount < 127) {
      outBuf[outCount] = thisByte;
      outCount++;
      outBuf[outCount] = 0;
    }
  }

  if (respCode >= '4') {
    efail();
    return 0;
  }
  return 1;
}  // eRcv()

//--------------- FTP handling
byte doFTP(char* HostFile) {
  char cmd[64];
 
  fh = SPIFFS.open(ESP8266File, "r");
  if (!fh) {
    Serial.println(F("SPIFFS open fail"));
    return 0;
  }

  if (!fh.seek((uint32_t)0, SeekSet)) {
    Serial.println(F("Rewind fail"));
    fh.close();
    return 0;
  }

  if (debug) Serial.println(F("SPIFFS opened"));

  if (client.connect(server, 21)) {  // 21 = FTP server
    Serial.println(F("Command connected"));
  } else {
    fh.close();
    Serial.println(F("Command connection failed"));
    return 0;
  }

  if (!eRcv()) return 0;
  if (debug) Serial.println("Send USER");
  sprintf(cmd, "USER %s",hostuser);
  client.println(cmd);

  if (!eRcv()) return 0;
  if (debug) Serial.println("Send PASSWORD");
  sprintf(cmd, "PASS %s",hostpasswd);
  client.println(cmd);

  if (!eRcv()) return 0;
  if (debug) Serial.println("Send SYST");
  client.println(F("SYST"));

  if (!eRcv()) return 0;
  if (debug) Serial.println("Send Type I");
  client.println(F("Type I"));

  if (!eRcv()) return 0;
  if (debug) Serial.println("Send PASV");
  client.println(F("PASV"));

  if (!eRcv()) return 0;

  char *tStr = strtok(outBuf, "(,");
  int array_pasv[6];
  for ( int i = 0; i < 6; i++) {
    tStr = strtok(NULL, "(,");
    array_pasv[i] = atoi(tStr);
    if (tStr == NULL) {
      Serial.println(F("Bad PASV Answer"));
    }
  }
  unsigned int hiPort, loPort;
  hiPort = array_pasv[4] << 8;
  loPort = array_pasv[5] & 255;

  if (debug) Serial.print(F("Data port: "));
  hiPort = hiPort | loPort;
  if (debug) Serial.println(hiPort);

  if (dclient.connect(server, hiPort)) {
    Serial.println(F("Data connected"));
  }
  else {
    Serial.println(F("Data connection failed"));
    client.stop();
    fh.close();
    return 0;
  }

  if (debug) Serial.println("Send STOR filename");
  client.print(F("STOR "));
  client.println(HostFile);

  if (!eRcv()) {
    dclient.stop();
    return 0;
  }

  if (debug) Serial.println(F("Writing"));
  // for faster upload increase buffer size to 1460
//#define bufSizeFTP 64
#define bufSizeFTP 1460
  uint8_t clientBuf[bufSizeFTP];
  //unsigned int clientCount = 0;
  size_t clientCount = 0;
 
  while (fh.available()) {
      clientBuf[clientCount] = fh.read();
      clientCount++;
      if (clientCount > (bufSizeFTP - 1)) {
        dclient.write((const uint8_t *) &clientBuf[0], bufSizeFTP);
        clientCount = 0;
        delay(1);
      }
  }
  if (clientCount > 0) dclient.write((const uint8_t *) &clientBuf[0], clientCount);

  dclient.stop();
  Serial.println(F("Data disconnected"));

  if (!eRcv()) return 0;

  client.println(F("QUIT"));
  if (!eRcv()) return 0;

  client.stop();
  Serial.println(F("Command disconnected"));

  fh.close();
  if (debug) Serial.println(F("SPIFS closed"));
  return 1;
}  // doFTP()


void display_Running_Sketch (void){
  String the_path = __FILE__;
  int slash_loc = the_path.lastIndexOf('/');
  String the_cpp_name = the_path.substring(slash_loc+1);
  int dot_loc = the_cpp_name.lastIndexOf('.');
  String the_sketchname = the_cpp_name.substring(0, dot_loc);

  Serial.print("\nArduino is running Sketch: ");
  Serial.println(the_sketchname);
  Serial.print("Compiled on: ");
  Serial.print(__DATE__);
  Serial.print(" at ");
  Serial.print(__TIME__);
  Serial.print("\n");
}

void drawScreen(struct DISP* hoge, int hnum) {
  struct DISP wk;
  for (int i=0; i<hnum; i++) {
    memcpy(&wk, hoge, sizeof(DISP));
    Serial.print(wk.rec_x);
    Serial.print(" ");
    Serial.print(wk.rec_y);
    Serial.print(" ");
    Serial.print(wk.rec_w);
    Serial.print(" ");
    Serial.println(wk.rec_h);
    uint16_t _rec_x = wk.rec_x;
    uint16_t _rec_y = wk.rec_y;
    uint16_t _rec_w = wk.rec_w;
    uint16_t _rec_h = wk.rec_h;
    for(int i=0; i<FRAME; i++) {
      tft.drawRect(_rec_x, _rec_y, _rec_w, _rec_h, wk.rec_c);
      _rec_x++;
      _rec_y++;
      _rec_w--;
      _rec_w--;
      _rec_h--;
      _rec_h--;
    }

    tft.setCursor(wk.text_x, wk.text_y);
    tft.setTextColor(wk.text_c);
    tft.print(wk.text);
   
    hoge++;
  } 
}

int IsTouched(struct DISP* hoge, int hnum) {
  if (ts.touched()) {
    TS_Point p = ts.getPoint();
    Serial.print("Pressure = ");
    Serial.print(p.z);
    Serial.print(", x = ");
    Serial.print(p.x);
    Serial.print(", y = ");
    Serial.print(p.y);
    Serial.println();

    uint16_t _x = p.x * tft.width() / 3800;
    uint16_t _y = p.y * tft.height() / 3800;
    Serial.print(_x);
    Serial.print(",");
    Serial.println(_y);

    struct DISP wk;
    for (int i=0; i<hnum; i++) {
      memcpy(&wk, hoge, sizeof(DISP));
      uint16_t _xmin = wk.rec_x;
      uint16_t _ymin = wk.rec_y;
      uint16_t _xmax = _xmin + wk.rec_w;
      uint16_t _ymax = _ymin + wk.rec_h;
      Serial.print(_xmin);
      Serial.print(",");
      Serial.print(_ymin);
      Serial.print(" ");
      Serial.print(_xmax);
      Serial.print(",");
      Serial.println(_ymax);
      if (_x >= _xmin && _x <= _xmax && _y > _ymin && _y <= _ymax) return i+1;
      hoge++;
    }
  }
  return 0;
}


void setup() {
  delay(1000);
  Serial.begin(9600);
  display_Running_Sketch();
  if (BUILTIN_LED2 > 0) {
    // signal start
    pinMode(BUILTIN_LED2, OUTPUT);
    digitalWrite(BUILTIN_LED2, LOW);
    delay(100); // ms
    digitalWrite(BUILTIN_LED2, HIGH);
    delay(300); // ms
  }

  Serial.println ( "Connect to Router requested" );
  connectWifi();
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("WiFi mode: ");
    Serial.println(str_mode[WiFi.getMode()]);
    Serial.print ("WiFi Status: " );
    Serial.println (str_status[WiFi.status()]);
    if (BUILTIN_LED2 > 0) {
      // signal WiFi connect
      digitalWrite(BUILTIN_LED2, LOW);
      delay(300); // ms
      digitalWrite(BUILTIN_LED2, HIGH);     
    }
  } else {
    Serial.println("");
    Serial.println("WiFi connect failed, push RESET button.");
    signalError();
  }

  if (!SPIFFS.begin()) {
     Serial.println("SPIFFS failed, needs formatting");
     signalError();
  }

  if (!SPIFFS.exists(ESP8266File)){
    Serial.println(F("SPIFFS File Create"));
    fh = SPIFFS.open(ESP8266File, "w");
    fh.close();
  }

  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(ILI9341_WHITE);
  tft.setTextSize(2);
  tft.setFontx(font_z, font_h);

  Serial.print("heigh=");
  Serial.print(tft.height());
  Serial.print(" width=");
  Serial.println(tft.width());
  design[0].rec_x = 40;
  design[0].rec_y = 40;
  design[0].rec_w = 240;
  design[0].rec_h = 60;
  design[0].rec_c = ILI9341_BLACK;
  design[0].text_x = 80;
  design[0].text_y = 60;
  strcpy(design[0].text, "FLUSH開始");
  design[0].text_c = ILI9341_BLUE;

  design[1].rec_x = 40;
  design[1].rec_y = 140;
  design[1].rec_w = 240;
  design[1].rec_h = 60;
  design[1].text_x = 80;
  design[1].text_y = 160;
  design[1].rec_c = ILI9341_BLACK;
  strcpy(design[1].text, "FLUSH終了");
  design[1].text_c = ILI9341_BLUE;

  Serial.print("idNum=");
  Serial.println(idNum);
  drawScreen(design, idNum); //画面表示

  ts.begin();

}  // setup()


void loop() {
  int inChar;
  char HostFile[64];

  inChar = IsTouched(design, idNum);
  if (inChar) {
    Serial.print("inChar=");
    Serial.println(inChar);
    if (inChar == 1) {
      design[0].text_c = ILI9341_WHITE; //文字消去
      drawScreen(design, idNum); //画面表示
      strcpy(HostFile, "inotify/start_led");
      Serial.print("HostFile=");
      Serial.print(HostFile);
      if (doFTP(HostFile)) Serial.println(F("FTP OK"));
      else Serial.println(F("FTP FAIL"));
      design[0].text_c = ILI9341_BLUE;
      drawScreen(design, idNum); //画面表示
    } 

    if (inChar == 2) {
      design[1].text_c = ILI9341_WHITE; //文字消去
      drawScreen(design, idNum); //画面表示
      strcpy(HostFile, "inotify/stop_led");
      Serial.print("HostFile=");
      Serial.print(HostFile);
      if (doFTP(HostFile)) Serial.println(F("FTP OK"));
      else Serial.println(F("FTP FAIL"));
      design[1].text_c = ILI9341_BLUE;
      drawScreen(design, idNum); //画面表示
    }
  }
}

Linux側のシェルを追加すれば、どのようなコマンドでも、ESP側からリモートで実行することができます。
例えば、ESP+雨量センサーと組み合わせて、雨が降ってきたらXXXXするなんていう使い方も、簡単にできるようになります。

続く....