見出し画像

【SALZ製作日誌】2020.05.20 大きな問題に対する解決策

SALZ2はESP32をマイコンに採用しました。装置を組み上げてテストをしたところ、プログラムを少し直したいところが出てきました。

SALZ1で採用していたobnizはブラウザ上でプログラムを実行していたため、いつでも簡単にプログラムを修正することができました。
ところがESP32はマイコン内にプログラムを内蔵するため、プログラムの書き換えのためには、パソコンとEPS32をUSBで接続する必要があります。

棚場の環境は、私のようなベランダの場合でも、半屋外、戸建てのお宅では屋外です。そこにPCを持ち出すのは勇気がいります。水濡れ、土汚れなど危険が一杯です。

製作中はなるだけ手戻りにならないように、室内でプログラムを練り上げて、プログラムをESP32へ流し込み、装置を接続します。

画像1

画像2

写真の通り、電子工作初心者の私は配線の心得がありません。SALZの製作に当たって、苦手なはんだごてを使って回路を組んだり、基板と電子パーツをつないだりしなければならないのかと覚悟しておりましたが、電子工作の技術は私の少年時代から比べるとはるかに進んでいて、ブレッドボードとジャンパ線を用いると、はんだづけなしで装置が組めることが分かりました。

ところが、これはあくまでも仮組みで、結線の強さはほとんどないため、配線をいじっているうちにコネクタが外れること多く、一度正常に作動した後は線をあまりいじらないよう気を付けなければなりません。

テスト運用がしばらく続くとはいえ、なにか良い方法はないものか模索しました。すると、いくつかのアイデアが出てきました。

1.CayenneやBlynkサービスなどのIoTダッシュボードサービスを使う方法

ネットで情報収集してると、Cayenne(カイエン)やBlynk(ブリンク)というサービスがあることを知りました。マイコンからの情報をグラフ化したり、逆にダッシュボードから制御したりできるようなのです。非常に興味があり、チャレンジしたい内容でしたが、今回は見送ることにしました。これは、次のシステム改良の課題として残しておきたいと思います。

2.OTA(Over The Air)を試す。

これまた、素晴らしい技術に巡り合えました。プログラムの書き換えをUSBのシリアル通信ではなく、Wifiで行うやり方があるというのです。それが、ESP32の機能としてすでに備わっているとのこと。そう、探していたのはまさにこの機能だったのです。

このような図で説明されていて、とてもよく理解できました。

SPIFFS(SPI Flash File System)

最初の状態:
現在のスケッチ   SPIFFS
■■■■□□□□□□□□□□□●●●●●


アップデート中:
現在のスケッチ     SPIFFS
■■■■□□□□□□♦♦♦♦♦●●●●●
       新しいスケッチ

再起動後:
新しいスケッチ   SPIFFS
♦♦♦♦♦□□□□□□□□□□●●●●●

でも、考えてみれば、普段使っているスマホやプリンタなど、多くの機器で「ファームウェアの更新」という機能があります。目新しい機能というものではなかったのですが、是非ともSALZ2に搭載してみたいと思いました。

概念がわかってしまえば、あとは実装するのみです。幸いサンプルはネット上に山ほどありましたし、ArudinoIDEにもスケッチ例として収納されていました。現在のコードに、OTAの機能を埋め込み、試してみることにしました。

これがまたうまく行きません。


5回ぐらい試して、ようやく成功しました。
何が原因か分かりませんが、よく失敗し、
時々成功します。

テストを繰り返すうちにだんだんと気持ちが変わってきました。

横着しないで、EPS32を装置から取り外しやすくした方がよいのでは?

ということで、装置の接続を見直しました。

はんだごては苦手だけど、グルーガンは使えます。ジャンパーピンとブレッドボードをグルーガンで固定します。グルーは直射日光によって変化しやすいので、これも一時的なものかもしれませんが、装置の取り出しはしやすくなりました。

// SALZ2 r2020.05.16 bbd
// 参考資料:
// @masciiさん「ESP32からIFTTTを使ってLINE Notifyで通知を送ってみた」
//  https://qiita.com/mascii/items/4c366ad4709469d5fda9
// TomoSoftさん「IFTTTを用いてESP-WROOM-02からGoogleドライブ・スプレッドシートに書き込み」
//  https://tomosoft.jp/design/?p=7598
// ほか
 #include  <WiFi.h>
 #include  <WiFiClient.h>
 #include  <ESPmDNS.h>
 #include  <WiFiUdp.h>
 #include  <ArduinoOTA.h>
 #include  <Servo.h>


//テストモード(ON:1, OFF:0)
//#define TEST_MODE 1
 #define  TEST_MODE 0


const int DRY_LEVEL = 2200;
const int WD_TIME = (TEST_MODE) ? (5 * 1000) : (10 * 60 * 1000);
const int DOBU_TIME = (TEST_MODE) ? (5 * 1000) : (10 * 60 * 1000);
const int WAIT_TIME = (TEST_MODE) ? (10 * 1000) : (60 * 60 * 1000);

const char* server = "maker.ifttt.com";  // Server URL

// 接続先のSSIDとパスワード
const char* ssid      = "XXXXXXXX";
const char* password  = "XXXXXXXX";

// IFTTT
String makerEvent = "salz2_check"; // Maker Webhooks
String makerKey   = "XXXXXXXX"; // Maker Webhooks

int wsPin = 18; // 給水モータ用リレースイッチ
int wdPin = 19; // 排水弁用サーボモータ
int wlPin = 34; // 水分計センサ
float moistureLevel = 0.0;
int idLog = 0;

WiFiClient client;
Servo myServo;

void errorBlink() {
 Serial.println("errorBlink();");
}

void wifiBegin() {
 Serial.print("Attempting to connect to SSID: ");
 Serial.println(ssid);
 WiFi.begin(ssid, password);
}

bool checkWifiConnected() {
 // attempt to connect to Wifi network:
 int count = 0;
 while (WiFi.status() != WL_CONNECTED) {
   Serial.print(".");
   // wait 1 second for re-trying
   delay(1000);

   count++;
   if (count > 15) { // about 15s
     Serial.println("(wifiConnect) failed!");
     errorBlink();
     return false;
   }
 }

 Serial.print("Connected to ");
 Serial.println(ssid);

 return true;
}

void sendIFTTT(String s1, String s2, String s3) {
 while (!checkWifiConnected()) {
   wifiBegin();
 }

 Serial.println("\nStarting connection to server...");
 if (!client.connect(server, 80)) {
   Serial.println("Connection failed!");
 } else {
   Serial.println("Connected to server!");
   // Make a HTTP request:
   String url = "/trigger/" + makerEvent + "/with/key/" + makerKey;
   url += "?value1=";
   url += s1;
   url += "&value2=";
   url += s2;
   url += "&value3=";
   url += s3;
   
   client.println("GET " + url + " HTTP/1.1");
   client.print("Host: ");
   client.println(server);
   client.println("Connection: close");
   client.println();
   Serial.println("url:" + url);
   Serial.print("Waiting for response "); //WiFiClientSecure uses a non blocking implementation

   int count = 0;
   while (!client.available()) {
     delay(50); //
     Serial.print(".");

     count++;
     if (count > 20 * 20) { // about 20s
       Serial.println("(send) failed!");
       errorBlink();
       return;
     }
   }
   // if there are incoming bytes available
   // from the server, read them and print them:
   while (client.available()) {
     char c = client.read();
     Serial.write(c);
   }

   // if the server's disconnected, stop the client:
   if (!client.connected()) {
     Serial.println();
     Serial.println("disconnecting from server.");
     client.stop();
   }
 }
}

void sendData() {
 sendIFTTT(String(idLog++), String(moistureLevel), "");
}

void wdOpen() {
 //排水弁を開く
 Serial.println("排水弁を開く");
 myServo.attach(wdPin);
 myServo.write(170);
 delay(3000);
 myServo.detach();
}

void wdClose() {
 //排水弁を閉じる
 Serial.println("排水弁を閉じる");
 myServo.attach(wdPin);
 myServo.write(10);
 delay(3000);
 myServo.detach();
}

void wsOn() {
 //給水On
 Serial.println("給水On");
 digitalWrite(wsPin, LOW);
}

void wsOff() {
 //給水Off
 Serial.println("給水Off");
 digitalWrite(wsPin, HIGH);
}

void setup() {
 // Basic OTA Begin
 Serial.begin(115200);
 Serial.println("Booting");
 WiFi.mode(WIFI_STA);
 WiFi.begin(ssid, password);
 while (WiFi.waitForConnectResult() != WL_CONNECTED) {
   Serial.println("Connection Failed! Rebooting...");
   delay(5000);
   ESP.restart();
 }

 ArduinoOTA
   .onStart([]() {
     String type;
     if (ArduinoOTA.getCommand() == U_FLASH)
       type = "sketch";
     else // U_SPIFFS
       type = "filesystem";

     // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
     Serial.println("Start updating " + type);
   })
   .onEnd([]() {
     Serial.println("\nEnd");
   })
   .onProgress([](unsigned int progress, unsigned int total) {
     Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
   })
   .onError([](ota_error_t error) {
     Serial.printf("Error[%u]: ", error);
     if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
     else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
     else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
     else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
     else if (error == OTA_END_ERROR) Serial.println("End Failed");
   });

 ArduinoOTA.begin();

 Serial.println("Ready");
 Serial.print("IP address: ");
 Serial.println(WiFi.localIP());
 // Basic OTA End

 pinMode(wsPin, OUTPUT);
 pinMode(wdPin, OUTPUT);
 digitalWrite(wsPin, HIGH);

 sendIFTTT(String(idLog++), String(moistureLevel), "SALZ2 Start");

 for (int i = 0; i < 2; i++) {
   wdClose();
   wdOpen();
   wsOn();
   delay(1000);
   wsOff();
   delay(1000);
 }
}

void loop() {
 // Basic OTA Begin
 ArduinoOTA.handle();
 // Basic OTA End

 float val = 0.0;
 for (int i = 0; i < 5; i++) {
   val += analogRead(wlPin);
 }
 moistureLevel = val / 5;

 Serial.print("MOISTURE LEVEL:");
 Serial.println(moistureLevel);
 sendData();

 if(moistureLevel > DRY_LEVEL) {
   Serial.println("ドライ");
   wdClose();
   wsOn();
   delay(WD_TIME);

   wsOff();
   delay(DOBU_TIME);

   wdOpen();
 }
 delay(WAIT_TIME);
}

それで結局、装置を取り出し、PCと接続してプログラムを書き換えました。

変更内容は、閾値を3000から2200へ変えました。
これで、給水頻度が上がるはずです。


この記事が気に入ったらサポートをしてみませんか?