ESP-WROOM-02でLinuxからのメッセージを表示する

アラートの表示


私は何台かRaspberryPiを常時動かしています。
1日1回、パッケージ・アップデートの有無をチェックし、アップデートがあるときは、オンボードのLEDをフラッシュさせていますが、
下駄箱の中(!!)や、机の下に設置しているRaspberryPiなどは、フラッシュサインを見落とすことが有ります。
そこで、アップデートがあるときはMQTT経由でTFTに表示してみることにしました。
ESPでTFTに表示すること自体は難しくないのですが、どうやって表示を消すかが悩ましい点でした。

HSES-LCD-24を使うと、簡単にタッチパネルが使えるようになります。
パッケージ・アップデートがあると、家中のRaspberryがこんな風に知らせてくれます。
一度に表示できるアラートは3つまでです。
4つ目のアラートが来たら無視しますが、1つ目のアラートを消して、繰り上げて表示するのは簡単に変更できます。
メッセージの消去はメッセージのダブルタップでできるようにしました。


例えば、2番目のメッセージをダブルタップすると、3番目のメッセージを繰り上げて表示します。


ESP側のスケッチは以下の様になります。
MQTTでSubscribeしたトピックとメッセージを整形して表示しているだけです。
MQTTのBrokerはローカルブローカを使っています。
ダブルタップと判定する時間は500mSecとしました。
/*
 * MQTTでSubscribeしたtopicとmessageを表示する
 * topicは以下の形式
 * syslog/yyyymmdd/hhmmdd/IPアドレス/ホスト名
 * [syslog/20170128/101719/192.168.10.108/raspberrypi]
 *
 * messageは半角16文字まで
 * 漢字も表示できるが漢字1文字がUFT8の3バイトなので5文字まで
*/

#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

#define CS_PIN  16

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.
const char* ssid = "アクセスポイントのSSID";
const char* password = "アクセスポイントのパスワード";
const char* mqtt_server = "192.168.10.40";
//const char* mqtt_server = "broker.hivemq.com";  // For TEST

WiFiClient espClient;
PubSubClient client(espClient);

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);

// Display情報の構造体
struct INFO {
  char t_date[11];
  char t_time[9];
  char t_ip[16];
  char t_name[20];
  char t_message[20];
};

// Display情報の構造体
struct DISP {
  int counter;
  struct INFO info[3];
};

struct DISP disp;

// Connect AP
void setup_wifi() {
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Wait for WiFi...");
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

// Receive MQTT topic
void callback(char* topic, byte* payload, unsigned int length) {
  char message[17];
 
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
//    Serial.print((char)payload[i]);
    if (i < 16) {
      message[i] = (char)payload[i];
      message[i+1] = 0;
    }
  }
//  Serial.println();
  Serial.println(message);
  addInfo(&disp, topic, message);
  showInfo(disp);
}

// Connect MQTT server
void server_connect() {
  char clientid[20];

  sprintf(clientid,"ESP8266-%6x",ESP.getChipId());
  Serial.print("clientid=");
  Serial.println(clientid);
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(clientid)) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      // ... and resubscribe
//      client.subscribe("#");
      client.subscribe("syslog/#");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void addInfo(DISP* hoge, char* topic, char* message) {
  int index = hoge->counter;
  Serial.print("addInfo>counter=");
  Serial.println(hoge->counter);
  if (index == 3) return;

  int i;
  for (i=0; i<strlen(topic); i++) {
    char ch = topic[i];
    if (ch == '/') break;
  }
 
  int pos = 0;
  int item = 0;
  char wk[4][20];
  memset(wk, 0, sizeof(wk));
  for (int j=i+1; j<strlen(topic); j++) {
    char ch = topic[j];
    if (ch == '/') {
      pos = 0; item++;
    } else {
      wk[item][pos++] = ch;
      wk[item][pos] = 0;
    }
  }
  for(int i=0;i<4;i++) {
    Serial.print("addInfo>wk=");
    Serial.println(wk[i]);
  }

  char dt[11];
  memset(dt,0,sizeof(dt));
  strncpy(dt, &wk[0][0], 4);
  strcat(dt, "/");
  strncat(dt, &wk[0][4], 2);
  strcat(dt, "/");
  strncat(dt, &wk[0][6], 2);
  strcpy(hoge->info[index].t_date, dt);
 
  memset(dt,0,sizeof(dt));
  strncpy(dt, &wk[1][0], 2);
  strcat(dt, ":");
  strncat(dt, &wk[1][2], 2);
  strcat(dt, ":");
  strncat(dt, &wk[1][4], 3);
  strcpy(hoge->info[index].t_time, dt);

  strcpy(hoge->info[index].t_ip, wk[2]);
  strcpy(hoge->info[index].t_name, wk[3]);
  strcpy(hoge->info[index].t_message, message);
  hoge->counter++;
}


void showInfo(DISP hoge) {
  Serial.print("dispInfo>counter=");
  Serial.println(hoge.counter);
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(0, 0);
 
  for (int i=0; i<hoge.counter; i++) {
    tft.setTextColor(ILI9341_WHITE);
    tft.setTextSize(1);
    tft.print(hoge.info[i].t_date);
    tft.print(" ");
    tft.println(hoge.info[i].t_time);

    tft.print(hoge.info[i].t_ip);
    tft.print(" ");
    tft.println(hoge.info[i].t_name);

    tft.setTextSize(2);
//    tft.setTextColor(ILI9341_BLUE);
    tft.println(hoge.info[i].t_message);
    tft.setTextSize(1);
    tft.println("");
  }
}


boolean delInfo(DISP* hoge, int pos) {
  Serial.print("delInfo>counter=");
  Serial.println(hoge->counter);
  if (pos >=  hoge->counter) return false;
  if (pos == 0) {
    memcpy(&hoge->info[0], &hoge->info[1], sizeof(INFO));
    memcpy(&hoge->info[1], &hoge->info[2], sizeof(INFO));
  } else if (pos ==1) {
    memcpy(&hoge->info[1], &hoge->info[2], sizeof(INFO));
  }
  (hoge->counter)--;
  return true;
}

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");
}

int getPosition(int y) {
  if (y < 1200) return 0;
  if (y < 2400) return 1;
  return 2; 
}


void setup() {
  delay(1000);
  Serial.begin(9600);
  display_Running_Sketch();

  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  server_connect();

  tft.begin();
  tft.setFontx(font_h,font_z);
  tft.setRotation(1);
  tft.fillScreen(ILI9341_BLACK);
  Serial.print("heigh=");
  Serial.print(tft.height());
  Serial.print(" width=");
  Serial.println(tft.width());

  disp.counter = 0;
//  addInfo(&disp, "20170127/101010/192.168.10.101/RaspberryPi","MESS1");
//  addInfo(&disp, "20170128/111111/192.168.10.102/RaspberryPi","MESS2");
//  addInfo(&disp, "20170129/121212/192.168.10.103/RaspberryPi","MESS3");
//  showInfo(disp);

  ts.begin();

}

void loop() {
  static unsigned long lastTime = 0;
  static int lastPosition;
  unsigned long diffTime;

  client.loop(); // Receive MQTT Event

  if ( lastTime > 0) {
    diffTime = millis() - lastTime;
    if (diffTime > 500) lastTime = 0;
  }

  if (ts.touched()) {
    TS_Point p = ts.getPoint();
    Serial.print("y = ");
    Serial.print(p.y);
    Serial.println();
    if (lastTime == 0) {
      lastTime = millis();
      lastPosition = getPosition(p.y);
    } else {
      Serial.print("Double Click ");
      Serial.println(lastPosition);
      if (delInfo(&disp, lastPosition)) showInfo(disp);
      lastTime = 0;
    }
  } // end if
}


Raspberry側では以下のスクリプトをcronで動かしています。
#!/bin/bash
sudo apt-get update > /tmp/apt.log
sudo apt-get -s upgrade > /tmp/apt.log
#grep -c "The following packages will be upgraded" /tmp/apt.log
check=`grep -c "The following packages will be upgraded" /tmp/apt.log`
#echo $check
rm /tmp/apt.log
if [ $check -ne 0 ] ; then
# Update package EXIST
  sudo sh -c "echo none >/sys/class/leds/led0/trigger"
  sudo sh -c "echo heartbeat >/sys/class/leds/led0/trigger"

  dt=`date +"%Y%m%d/%I%M%S"`
  ip=`sudo ifconfig wlan0 |grep "inet addr" |awk {'print $2'} |cut -f2 -d:`
  host=`hostname`
  topic="syslog/"${dt}"/"${ip}"/"${host}
  mosquitto_pub -h 192.168.10.40 -p 1883 -t ${topic} -m "Package Update"
else
# Update package NOT EXIST
  sudo sh -c "echo none >/sys/class/leds/led0/trigger"
  sudo sh -c "echo 0 >/sys/class/leds/led0/brightness"
fi

私の場合は、パッケージ・アップデートの有無を監視していますが、
CPU温度異常やディスクフルなどを監視して、異常時に表示するのもいいでしょう。