2022年2月12日土曜日

LINEダッシュボタン、3Dプリンタ印刷完了を通知する機能の完成

3Dプリンタの印刷終了をLINEにポストする機能をケースに組み込みました。


内部はこんな感じです。暗号化したWiFiのSSIDやパスワード、LINE notifyのトークン、およびポストするメッセージを格納したmicroSDはケースの蓋を開けないと取り外しできないようにしました。写真左側は、フォトリフレクタを収めたセンサ部です。


3Dプリンタに設置した様子です。センサ部は3Dプリンタの裏側にあり、印刷が終了してヒートベッドが奥のホームポジションに戻ると、それを検知します。


印刷を開始するとヒートベッドが最初何度が行き来するので、それが終わってヒートベッドがセンサ部を覆わなくなったらLINEダッシュボタンの電源を入れます。すると、監視開始のメッセージがポストされ、正常に監視を始めたことが分かります。

数時間後、印刷が終わるとヒートベッドが再びセンサ部を覆うので、それをトリガに印刷終了メッセージがポストされ、スマホが受け取ります。写真ではスマホを隣に並べていますが、LINEが繋がる場所ならどこにいても届きます。

回路図です。三端子レギュレータは1Aのものを使いましたが、500mA取れれば大丈夫なようです。
一度、フォトリフレクタの検出用GPIOを12に変更したのですが、センサを繋いでいるとプログラムの書き込みができなくなりました。また、センサを抜いてプログラムしてもうまく動きません。ネットで調べるとGPIO 12は曲者で、プルアップして電源を入れるとビルトインBASIC(そんなのが組み込まれているなんて初めて知った)が動作するようです。しばらく悩んでしまいました。

microSDに書き込むデータと暗号化処理は、昨日のWindows版暗号化ESP32版暗号化 の記事を参照ください。

以下、ESP32のスケッチです。一応動いていますが、無保証です。
//---------------------------------------------------------------
//                                                2022.02.12 naka
// LINE Dash Button(フォトリフレクタ版)
//
// ・WiFi ssid,password,LINE notify tokenは、暗号化してmicroSDに格納
//  ファイル名:setup.txt
//     暗号化したWiFi ssid, password, LINE notify tokenをそれぞれ1行ずつ
//     順に記載し、4行目、5行目に送るメッセージを記載。
//  (例)
//    &D/)%X/C04S+#T'?+USM
//    *%/O-J_F$##5)CK`(%
//    #VGN-Z_*-FCG+VK1/E;M<47E1UCJ.C/`-U(S#F?C06CV)%C_0$$E8W$89(0[5(U
//    動作を開始しました。
//    ボタンが押されました。
//---------------------------------------------------------------
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <SD.h>

#define SD_CS 16 // SD card chip select
#define SW    GPIO_NUM_13
#define LED_G 25
#define LED_R 27

#define ON  1
#define OFF 0

#define FILENAME "/setup.txt"

// ssid,pw,tokenを暗号化したときのキー
char* crypt_key = "Open Sesame";

// LINE Notify
const char* host  = "notify-api.line.me";
char ssid[64],password[64],token[64],message1[128],message2[128];
int sw_newest;

void setup()
{
  pinMode(SW,INPUT_PULLUP);
  pinMode(LED_G,OUTPUT);
  pinMode(LED_R,OUTPUT);
  led_green(OFF);
  led_red(OFF);
  sw_newest = digitalRead(SW);
  if (read_setup_file(FILENAME)) {
    blink_red(10);
    esp_sleep_enable_ext0_wakeup(SW, LOW);
    esp_deep_sleep_start();
  }

  // GPIO割り込みピン設定
  gpio_wakeup_enable(SW, GPIO_INTR_LOW_LEVEL);
  // 動作確認のために動作開始メッセージを送る
  wifi_connect();          // WiFi接続
  send_msg2line(message1); // LINE notifyに1行目のメッセージを送る
  wifi_disconnect();       // WiFi接続解除
  led_green(ON);

  // GPIO割り込みでのsleep復帰を有効化
  esp_sleep_enable_gpio_wakeup();
}

void loop(){
  // スリープ開始(復帰はこの後ろ)
  esp_light_sleep_start();
  
  wifi_connect();          // WiFi接続
  send_msg2line(message2); // LINE notifyに2行目のメッセージを送る
  wifi_disconnect();       // WiFi接続解除
  led_green(ON);
  while(!digitalRead(SW));
  delay(100);              // チャタリング対策
}

void led_green(int onoff) {
  if (onoff==ON) digitalWrite(LED_G,LOW);
  else           digitalWrite(LED_G,HIGH);
}

void led_red(int onoff) {
  if (onoff==ON) digitalWrite(LED_R,LOW);
  else           digitalWrite(LED_R,HIGH);
}

void wifi_connect() {
    WiFi.begin(ssid, password);
    int onoff = 0;
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        if (onoff==0) led_green(ON);
        else          led_green(OFF);
        onoff ^= 1;
    }

    // 以下5行は、普通は不要なハズ
    IPAddress gateway(192,168,0,1);
    IPAddress subnet(255,255,255,0);
    IPAddress dns1(8, 8, 8, 8); // Google DNS primery server
    IPAddress dns2(8, 8, 4, 4); // Google DNS secondary server
    WiFi.config(WiFi.localIP(),gateway,subnet,dns1,dns2);    

    led_green(ON);
}

void wifi_disconnect() {
    WiFi.disconnect(true);
    led_green(OFF);
}

void send_msg2line(char* message) {
  WiFiClientSecure client;

  led_red(ON);

  client.setInsecure();
  if (!client.connect(host, 443)) {
    blink_red(10);
    led_red(OFF);
    return;
  }
  
  String query = String("message=") + String(message);
  String request = String("") +
               "POST /api/notify HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Authorization: Bearer " + token + "\r\n" +
               "Content-Length: " + String(query.length()) +  "\r\n" + 
               "Content-Type: application/x-www-form-urlencoded\r\n\r\n" +
                query + "\r\n";
  client.print(request);
 
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "\r") {
      break;
    }
  }
  
  String line = client.readStringUntil('\n');
  led_red(OFF);
}

void blink_red(int no) {
  int onoff = 1;
  int i;
  for (i=0; i<no*2 ;i++) {
    delay(100);
    if (onoff==0) led_red(ON);
    else          led_red(OFF);
    onoff ^= 1;
  }
}

int read_setup_file(char* fileName) {
  char  buff[128];

  if (!SD.begin(SD_CS, SPI, 24000000)) return 1;

  File setupFile = SD.open(fileName,FILE_READ);
  if (setupFile) {
    readLine(setupFile,buff);
    decrypt(ssid,buff,crypt_key);
    
    readLine(setupFile,buff);
    decrypt(password,buff,crypt_key);
    
    readLine(setupFile,buff);
    decrypt(token,buff,crypt_key);
    
    readLine(setupFile,message1);
    readLine(setupFile,message2);
    setupFile.close();
  }

  return 0;
}

// microSDから1行読み取る
int readLine(File fp, char *buff){
  int i = 0;
  if(!fp.available()) {
    return -1;
  }
  while (fp.available()) {
    char c = fp.read();
    if (c=='\r');
    else if (c=='\n') break;
    else buff[i++] = c;
  }
  buff[i] = 0;
  
  return i;
}

void decrypt( char*a, char*b, char* key) {
  char x[128];
  int i,j,k,m;
  // ascii文字をbinaryに戻す
  for(i=0; b[i]!=0; i++) {
    x[i] = b[i] - (int)'#';
  }
  m = i - 1;

  // 6bit -> 8bit
  j = 0;
  for(i=0; j<m ; i++) {
    switch (i%3) {
      case 0:
        a[i] = (x[j]&0x3F)<<2 | (x[j+1]&0x30)>>4;
        j++;
        break;
      case 1:
        a[i] = (x[j]&0x0F)<<4 | (x[j+1]&0x3C)>>2; 
        j++;
        break;
      case 2:
        a[i] = (x[j]&0x03)<<6 | x[j+1]&0x3F;
        j += 2;
        break;
    }
  }
  m = i;
  
  k = 0;
  int n = strlen(key);
  for (i=0; i<m; i++) {
    a[i] ^= key[k++];
    k %= n;
  }
  a[i]= 0;
}
//---------------------------------------------------------------
// EOF
//---------------------------------------------------------------

パーツ箱に昔買った焦電型赤外線センサを見つけたので、防犯センサ版を作ってみようかと思っています。

0 件のコメント:

コメントを投稿