IOT再びーESP32編(14)ーーー静電容量水位計に向けてーーADS1115-

超音波センサーによる水位計は,測定が安定しないので,ひとまず休止して,静電容量測定による水位計をい
じってみたいと思います。それにともなって,ESP32は,ADC機能がいまいちということなので,ADC の基
盤,ADS1115をいじってみることにしました。

この基盤は,分解能が16ビットありますので,結構精密に変換できて,接続もI2Cなので,4本だけ接続す
ればいいので,お手軽です。また,ESP32 用にライブラリーもあるので,お手軽です。ただ,本当に理解す
るには,ライブラリーを使わないで,使われているIC に直接書き込みをした方がいいのでしょうね。ちょっ
と,それは気力がないので,ライブラリー使用です。
例のごとくブレッドボードで,ESP32,ADS1115,SSD1306,レベルシフター,を配線しました。
ADS1115とSSD1306はI2C接続なので,並列につなげればいいので,お手軽です。

それほど時間はかかりませんでしたが,ADS1115 の配線でちょっとはまりました。スケッチ例の注意には記
載があったのですが,測定できるのは,ADS1115の Vdd+0.3Vmax の記載があるのをみおとしてました。
5Vラインを測定しても,約 4.0V の表示かできないので,あれこれやりました。
すぐに原因判明しました。
SSD1306の電源に合わせて 3.3V で動作させてました。急遽,レベルシフターをいれて,5Vで動作させる
と,正しく測定できました。写真は,乾電池を測定してるところです。はっきりしませんが,OLED には,
1.63 Vと表示されてます。

まだ,やっていませんが,差動入力にすると,プラスマイナスを逆につないでも,マイナス何ボルトの表示
が可能です。
今回使ったスケッチですが,このスケッチは,測定に必要ない余分なヘッダーファイルをインクルードしていま
す。

// 必要なライブラリのインクルード
#include  <wifi.h>
#include  <wire.h>
#include  <adafruit_gfx.h>
#include  <adafruit_ssd1306.h>
#include  <adafruit_ads1x15.h>

//ADS1115 ADS(0x48);
Adafruit_ADS1115 ads;

byte hdr, data_h, data_l, chksum;
String inputString = "";
 

// ピンの定義
#define wifyOn 4 // wify 接続OK
#define wifyOff 2 // wify 未接続
#define OLED 18 // OLDE 表示・非表示

//OLED
#define OLED_RESET     -1 
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// WiFi接続情報--固定アドレス用
const char* ssid = "***********";
const char* password = "***********";
const IPAddress ip(192, 168, 2, 30);
const IPAddress gateway(192, 168, 2, 2);
const IPAddress subnet(255, 255, 255, 0);
const IPAddress dns1(192, 168, 2, 2);


// セットアップ関数
void setup() {
  //入出力ピン設定
  pinMode(wifyOn, OUTPUT);
  pinMode(wifyOff, OUTPUT);
  pinMode(OLED,INPUT_PULLUP);
  
  digitalWrite(wifyOn, LOW);
  digitalWrite(wifyOff, HIGH);

  Serial.begin(115200); // シリアル通信の開始
  Serial2.begin(9600,SERIAL_8N1,16,17);
  ads.begin();

//OLED開始
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();

 //OLED初期設定
  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.print(F("Wify set"));
  display.display(); 

 
  // WiFi接続
  if (!WiFi.config(ip,gateway,subnet,dns1)){
      Serial.println("Failed to configure!");
  }
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }

  digitalWrite(wifyOn, HIGH);
  digitalWrite(wifyOff, LOW);

  display.clearDisplay();
  display.setCursor(0, 0);
  display.print(F("Wify con"));
  display.display();

}

// メインループ
void loop() {

 digitalWrite(wifyOn, HIGH);
  digitalWrite(wifyOff, LOW);
  
  ads.setGain(GAIN_TWOTHIRDS);
  
  int16_t adc0;
  float volts0;
 
  adc0 = ads.readADC_SingleEnded(0);
  volts0 = ads.computeVolts(adc0);
  
  Serial.print("AIN0: "); 
  Serial.print(adc0);
  Serial.print("  ");
  Serial.print(volts0);
  Serial.println("v");

  display.clearDisplay();
  display.setCursor(0, 0);
  display.println(adc0);
  display.print(volts0);
  display.print("v");
  display.display();

  delay(500);

  digitalWrite(wifyOn, LOW);
  digitalWrite(wifyOff, HIGH);
  
  delay(500);
}


 

IOT再びーESP32編(14)ーーープログラムの修正(A0221AT)防水超音波センサー

テストを繰り返していますが,なかなか安定した測定結果がえられませんでした。データの変動の幅がおおき
かったり値が変化しなかったりとしたため,あれこれとやりました。夜茶の間いろいろやっていると,比較的
安定した結果になるようでしたが,その原因が分かりました。測定結果が安定してないので,センサー部分を
見ると,水滴がかぶっていました。元々防水なので,水滴は大丈夫なはずですが,水滴が付いているため,測
定に影響があるような気がします。もともと超音波センサーは,超音波を発して,その反射が帰ってくるまで
の時間を計測して,距離を算出してますので,水滴がついていると,超音波の発信,受信に影響があるのかも
しれません。
センサー部分の水滴を拭き取ってみると,ほぼ正確な反応が返るようになりました。あれこれスケッチをいじ
ましたが,平均をとらなくても大丈夫なようですので,平均はとらずに運用してみようと思います。
ただ,英語のマニュアルをみてみると,レスポンスが帰るまで,100~300msとあるので,書き込みをし
てから,300msの休止(delay(300);)をいれました。

 float measureDistance_2() {
  char m[50];
  int Sw;
  Serial2.write("S");
  delay(300); 
  if (Serial2.available())
  {
    hdr = (byte)Serial2.read();
    if (hdr == 255){
      data_h = (byte)Serial2.read();
      data_l = (byte)Serial2.read();
      chksum = (byte)Serial2.read();

      if (chksum == ((hdr + data_h + data_l)&0x00FF)){
          distance = data_h * 256 + data_l;//距離計算
          new_distance=distance/10;
          dtostrf(new_distance, 6, 1, m);//
          Serial.print(m);
          Serial.println(" cm");
          if(digitalRead(OLED)==HIGH){//OLED表示ONの時
            display.clearDisplay();
            display.setCursor(0, 0);
            display.println("rest= ");
            display.print(m);
            display.println(" cm");
            display.display();
          }       
      }
    }
  }
  delay(100); 
  return distance/10;//cmを返す
 
}

超音波センサーなので,明るさは関係ないと思うのですが,検証をするのは,蛍光灯の下が大いのですが,そ
の影響もあるのでしょかね。
茶の間で検証の様子です。水が噴霧機に入ってるので,おっちょこちょいの私は,こぼさないかヒヤヒヤして
ます。

IOT再びーESP32編(13)ーーープログラムの修正(A0221ATデータフォーマット)

スケッチを書き換えました。初期値を決定するのに,10回計測して,最大値と最小値を除いたものの平均を
とる関数を作成しました。

//初期値設定用 平均
float sokutei_heikin(int m){
  float Value[10];
  float Max;
  float Min;
  float Sum;
  
    for(int Ct=0;Ct<m;Ct++){
       Value[Ct]=measureDistance_1();//距離の測定関数を呼び出す。
       Serial.print(Ct);
       Serial.print(" =");
       Serial.println(Value[Ct]);
      if(Ct == 0) {//最初の測定値を最大・最小値として保存
          Max=Value[0];
          Min=Value[0];
      }
      else {
        if(Value[Ct] > Max){//取得したデータを最大値と比較して,大きければ,新たに保存
            Max=Value[Ct];
        }
        if(Value[Ct] < Min){////取得したデータを最小値と比較して,小さければ,新たに保存
            Min=Value[Ct];
        }
      }
     Sum+=Value[Ct];/*測定値を加算*///測定値を加算していく
  }

  Sum=(Sum-(Max+Min))/(m-2);//最大値と最小値を除いて,平均をとる。
  Serial.print("AVE");
  Serial.print(" =");
  Serial.println(Sum);

  return Sum;/*最大値・最小値を除いた平均値を返す*/
}

sokutei_heikin(10)ということで,10回の測定の平均をとる(最大・最小を除くので実際は8回)
設定で,動かして見ましたが,一回目の測定値が,きちんと測定されてないようなので,測定に入る
一回ダミーの測定をいれsて,実際は,11回の測定でやってみました。大きな変動もなく結果は良好
でした。
今回,最大・最小値を除いた平均を計算するのに,ちょっとなやみました。測定値を全部保存したうえ
ソートして,最大値と最小値を除こうかとも思いましたが,測定値をその都度一時保存の最大値と最小
値と比較するやり方が,よかったようです。

下記が,超音波センサーから,距離を測定するための関数です。

float measureDistance_1() {
  char m[50];
  Serial2.write("S");//RX端子をHIGHにするのに一文字書き込む。
  if (Serial2.available()){
    hdr = (byte)Serial2.read();
    if (hdr == 255){
      data_h = (byte)Serial2.read();
      data_l = (byte)Serial2.read();
      chksum = (byte)Serial2.read();
        if (chksum == ((hdr + data_h + data_l)&0x00FF)){
            distance = data_h * 256 + data_l;//距離計算
        }
     }
    }
  delay(100); 
  return distance;
 }

distance = data_h * 256 + data_l の部分が,距離を計算する部分です。計算するといっても,HIGHの
データとLOWのデータが,1バイトずつ送られてくるので,桁数を合わせる計算をしているだけです。
マニュアルには,下記のような記述がありました。
計測が開始されるとその結果が下記のように,4バイトのデータでおくられてきます。

0XFF 0X07 0XA1 0XA7

各データは,
0XFF はヘッダーデータ
0X07 は上位データ
0XA1 は下位データ
0XA7 はチェックサム
のようになってますので,
上位のデータと下位のデータを合わせると,
 07A1
という16進数が得られますので,これを10進数に直すと,1953mmというデータが得られるようです。

IOT再びーESP32編(12)ーーープログラムの修正

大きな不具合のなかった配線ですが,赤のLEDが点灯しなかったので,いろいろ調べてみると,LEDの足が断
線してました。また,組み込んでテストしてみると,大きな不都合はなかったものの,原因は分かりません
が,測定値が変動するようなので,その対策で,スケッチを修正することにしました。
オリジナルのスケッチでは,超音波の測定による距離をそのままWEBに渡していましたが,この部分を多少
変更します。
噴霧機満タンの状態での測定値を記憶して,それに基づいて,減り具合を加算するようにしたいと思いま
す。満タンの状態は,何度か測定して,平均をだして,初期値とするにあたって,誤差を抑えるために何回
か測定し,最大値と最小値を除いて,平均をとりたいと思います。
どうしたもんかとネット見ていると,下記のようなプログラムが見つかりました。C のものですが,これを
参考に修正しようと思います。

#include <stdio.h>
#define NumValue 5

int main(){
  int Value[NumValue];
  int Ct;
  int Max;
  int Min;
  int Sum;

  for(Ct = 0; Ct < NumValue; Ct ++) {    
   printf("Enter #%d → ", Ct + 1); 
   scanf("%d", &Value[Ct]);    
   
   if(Ct == 0) {      
      Max = Value[0];      
      Min = Value[0]; } 
   else {      
    if(Value[Ct] > Max) {
      Max = Value[Ct];
    }
    if(Value[Ct] < Min) {
      Min = Value[Ct];
     }
    }
 }

 
 printf("========\n");
 Sum = 0;

 for(Ct = 0; Ct < NumValue; Ct ++) {
  if(Value[Ct] != Max && Value[Ct] != Min) {
    Sum += Value[Ct];
  }
 }
 printf("%d\n", Sum);
}

IOT再びーESP32編(11)ーーーテストーー超音波センサー

やっと配線が終わり,本日,組み込み機での,テストを行いました。

誤配線等あって,動かないかなと思いましたが,すんなり動きました。
噴霧機にセットし,動かしてみると,底までの距離が229mmとほぼ正確に表示されました。あすは,実際
に水を入れてへりぐあいと数値の変化をテストしてみます。

実際の運用にあたっては,追加のスライドスイッチを,OLEDのON,OFF用にいれたので,多少プログラムを
変更する予定です。

IOT再びーESP32編(10)ーーー組み込みーー超音波センサー

間が開きましたが,防水超音波センサーを目的のものに組み込みました。目的のものは,電動(AC)噴霧機
です。当初の予定とはちがってきましたが,この噴霧機は,加圧式のものではなかったので,組み込みも比
較的容易でした。センサーの出口は,航空コネクターにしました。

防水超音波センサーも,うまい具合に組み込めました。

ただ,まだ実証は未実施ですが,うまく組み込めましたが,吸い込みのチューブが,干渉しないか多少不安が
残ります。
ESP32の方も,透明のケースに組み込むようにします。まだ,配線等はしていませんが,近日中に完成する
予定です。

IOT再びーESP32編(9)ーーー番外編

あれこれとネットサーフィンをしていて,WEBページとサーバーとのやりとりについて,おそまきながらわ
かってきました。この役割をになってるのが,ハンドラーと呼ばれる部分でした。その書き方や意味が少
しわかりました。

特に
   サーバー→→→WEB

の方法がいまいちでしたが,この役割のになってるのが,esp32 の processor関数 だとい言うことが
分かりました。
このやりとりをする前段階として,

AsyncWebServerRequest *request; #requestというオブジェクトを作成
request->send(SPIFFS,"/hogehoge.hoge",String(),false,processor);

記述をするようで,記述で,hogehoge.hoge(html記載されたページ)の%で囲まれた文字列がprocessor関
数で置きかえられるとのことでした。
実際には,

  WEBからサーバーへホームページ表示のrequest

が出されるなかで,いろいろ解釈されて,%で囲まれた文字列が,processor関数で置き換えられるとのこと
のようでした。これで,ちょっとだけ進歩かなと思います。

IOT再びーESP32編(8)ーESP32へ移植完了ーA0221AT(防水超音波センサー)+SSD1306OLE

あれこれ手こずりましたが,ESP32への移植完了しました。おまけに,手元にあったSSD1306OLEを
ディスプレイにくわえました。当初,BLEも加えようかと思いましたが,BLEをいれるとメモリーが足りな
なるので断念。ESP32への接続は次のようにしました。

 SSD1306OLE  SCL→→→→GPIO22
         SDA→→→→GPIO21
 A0221AT    RX(コネクター3)→→→→GPIO17
         TX(コネクター4)→→→→GPIO16
 LED       赤(WIFY接続 OFF)→→→→GPIO2
         緑(WIFY接続 ON)→→→→GPIO4

ESP32をブレッドボードで使うに当たって,通常の使い方では,端子が使えなくなってしまうので,秋月電子
から購入したブレッドボード BB-01P を二つ組み合わせて使ってます。
OLEは,I2Cのものですが,表示をいれると,通信に時間がかかるのか,WEB に反映されるのに,タイムラ
グがあるようです。実際の運用には,外した方がいいかもですね。

下記が今回使ったスケッチです,といっても,諸兄のものをちょっと改変しただけす。また,WEB用のhtml
ファイルは,ESP32編(3) のものを使ってます。本当は,WEB に距離を反映させたかったのですが,
javascript が手強くて,そのままにしてあります。

// 必要なライブラリのインクルード
#include <wifi.h> 
#include <spi.hamp>
#include <webserver.h>
#include <spiffs.h>
#include <adafruit_gfx.h>
#include <adafruit_ssd1306.h>


byte hdr, data_h, data_l, chksum;
String inputString = "";
float distance=0;
float new_distance=0;
float val=0.00f;

// ピンの定義
#define wifyOn 4 // wify 接続OK
#define wifyOff 2 // wify 未接続

//OLE
#define OLED_RESET     -1 
#define SCREEN_ADDRESS 0x3C //< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// WiFi接続情報(固定IP)
const char* ssid = "*************";
const char* password = "**********";
const IPAddress ip(192, 168, 2, 30);
const IPAddress gateway(192, 168, 2, 2);
const IPAddress subnet(255, 255, 255, 0);
const IPAddress dns1(192, 168, 2, 2);


// WebServerオブジェクトの初期化
WebServer server(80);

// セットアップ関数
void setup() {

  Serial.begin(115200); // シリアル通信の開始
  Serial2.begin(9600,SERIAL_8N1,16,17);

//OLE開始
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();

 //OLE初期設定
  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.print(F("wify-set"));
  display.display(); 

  pinMode(wifyOn, OUTPUT);
  pinMode(wifyOff, OUTPUT);

  digitalWrite(wifyOn, LOW);
  digitalWrite(wifyOff, HIGH);
  
   // SPIFFSの初期化
  if(!SPIFFS.begin(true)){
    Serial.println("SPIFFSのマウントに失敗しました");
    return;
  }

  // WiFi接続
  if (!WiFi.config(ip,gateway,subnet,dns1)){
      Serial.println("Failed to configure!");
  }
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }

  digitalWrite(wifyOn, HIGH);
  digitalWrite(wifyOff, LOW);

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

  // Webサーバーのルート設定
  server.on("/", HTTP_GET, []() {
    server.send(200, "text/html", SPIFFS.open("/index.html", "r").readString());
  });

  // '/distance'で距離を返す設定
  server.on("/distance", HTTP_GET, []() {
    server.send(200, "text/plain", String(measureDistance()));
  });

  server.begin(); // サーバーの開始
}

// メインループ
void loop() {
  server.handleClient(); // クライアントのリクエストを処理
}

// 距離測定関数
float measureDistance() {
  char m[50];
   Serial2.write("S");
  if (Serial2.available())
  {
    hdr = (byte)Serial2.read();
    if (hdr == 255){
      data_h = (byte)Serial2.read();
      data_l = (byte)Serial2.read();
      chksum = (byte)Serial2.read();

      if (chksum == ((hdr + data_h + data_l)&0x00FF)){
        distance = data_h * 256 + data_l;

        //if(distance!=new_distance){
          display.clearDisplay();
          new_distance=distance;
          dtostrf(new_distance, 6, 1, m);
          Serial.print(m);
          Serial.println(" mm");
          display.setCursor(0, 0);
          display.println("distance= ");
          display.print(m);
          display.println(" mm");
          display.display(); 
         // delay(100);
          //return new_distance;
        //}      
      }
    }
  }
   delay(100); 
  return distance;
 
}

IOT再びーーーESP32編(6)ーーー部品調達(防水 超音波センサー)-A0221AT

〇zonより,

超音波距離センサー、UART 制御距離検出器 3 ~ 450cm 低電力設計距離センサー、
IP67 防水設計 DC 3.3V ~ 5V ロボット用超音波距離検出器

を購入しました。たまたま,データシートをみつけ,GitHubにもサンプルのスケッチがのっていたので大丈夫
だろうと思っての購入でした。
取り合えず,arduino UNO で動かしてから,esp32に移植しようかと思いましたが,初期段階で,はまり
ました。何回やっても,シリアルポートが二つ開けないのです。結局勘違いでした。

    Srial.available()

の関数ですが,ポートが有効かどうかの判断の関数かと思いましたが,書き込みがあったかどうかの判定の
関数でした。どうりで,ポートが有効にならないはずです。

おまけに,届いた品物は,A0221AT というものですが,この型番のものは,測定を開始するのに,RX ピ
ンを一時ハイにしてやらないとだめなようで,GitHub のスケッチには,この部分が抜けていて,うごきませ
んでしたが,試しに,

  ss.write(“s”);(”S”を書き込みましたが,なんでもいみたいです)

を書き込んでやると無事うごきはじめました。

ちなみに,この防水センサーには,入出力の違いで,いくつか種類があるようで,UART 出力のものもに二種
類あるようでした。多分〇zonのものは,オートではないものなのかな,と思いました。
アリエクスプレスでは,きちんと,選択できるようになってます。

下記が,arduino UNO のスケッチで,loop(){ の次に,ss.write(“s”);を加筆してます。

/**
 * 
 * Author: Ritesh Talreja, Made in China, Warehouse: Shenzhen, Guangdong.
 * 
 * Components: Arduino UNO, DYPA02YYUM  v1.0
 * 
 * Arduino UNO +5V    --> DYPA02YYUM Pin 1 Red
 * Arduino UNO GND    --> DYPA02YYUM Pin 2 Black
 * Arduino UNO Pin 11 --> DYPA02YYUM Pin 3 or Floating
 * Arduino UNO Pin 10 --> DYPA02YYUM Pin 4
 * 
 * Since Arduino UNO does not have 2 hardware serial ports.
 * We are using 1 software serial port connected to the sensor.
 * All data from software serial port is copied onto hardware serial port to view in "Arduino IDE Serial Monitor".
 */

#include <SoftwareSerial.h>

SoftwareSerial ss (10, 11);   // RX, TX

byte hdr, data_h, data_l, chksum;
unsigned int distance;

void setup()
{
  Serial.begin(9600);
  while (!Serial);

  ss.begin(9600);
}

void loop()
{
  ss.write("s");
  if (ss.available())
  {
    hdr = (byte)ss.read();
    if (hdr == 255)
    {
      data_h = (byte)ss.read();
      data_l = (byte)ss.read();
      chksum = (byte)ss.read();

      if (chksum == ((hdr + data_h + data_l)&0x00FF))
      {
        Serial.print(hdr);
        Serial.print(",");
        Serial.print(data_h);
        Serial.print(",");
        Serial.print(data_l);
        Serial.print(",");
        Serial.print(chksum);
        
        Serial.print("=");
      
        Serial.print(hdr, HEX);
        Serial.print(",");
        Serial.print(data_h, HEX);
        Serial.print(",");
        Serial.print(data_l, HEX);
        Serial.print(",");
        Serial.print(chksum, HEX);
        Serial.print(" => ");
  
        distance = data_h * 256 + data_l;
        Serial.print(distance, HEX);
        Serial.print("=");
        Serial.print(distance, DEC);
        Serial.println(" mm");
      }
    }
  }
  delay(100);
}

写真が動かしたときの距離の様子です。結構正確に測定できてるようです。

久しぶりに UNO 引っ張りだしてきましたが,無事うごきました。

次は,esp32への移植ですが,レベルシフターをかまさないとでめでしょうかね。

IOT再びーーーESP32編(5)ーーー部品調達

アリエクスプレスから,部品の調達です。〇Zonでは,かなりするのに,ありでは,格安です。でも,届く
のが1ヶ月後ぐらいです。

注文時は,送料込みで,252円でしたが,今日みたら,657円になってました。まとめて買えばよかった。
ちなみに〇ZONでは,700円から2500円ぐらいと幅があります。