ESP8266を使って自宅外の気象情報を監視する② – ソーラーパネルで動かしてみた

前回のブログにて、ミニ百葉箱の中にESP8266と温度センサー等を入れて、単四電池三本構成で動かしてみたところ、思いのほか短時間しか動作しませんでした。

配線を変更したり、レギュレーターを使用したり、コイン電池を使ってみたり、とりあえず測定間隔を延ばしてみたり、いろいろ試してみたのですが、なかなか期待する時間まで動作することができませんでした。

知識が足りないのはごもっともなのですが、基礎も含めてもう少し調べてみて、試してみたい一方で、「作ったものはなんとか早く動かしたい!」という思いもあり、であれば・・・ということで、自然のエネルギーを使って電力を起こす「ソーラーパネル」を使って電力供給してみた内容になります。

合わせて、前回試作したものを一部改良してみました。具体的には、温度/湿度測定で使っていたDHT11と同じく重複して温度/大気圧測定で使っていたBMP180をBME280に統合し、回線を少しシンプルにしました。

今回購入したBME280はこちら。前回のブログでも書かせていただきましたが、BMP280という、湿度センサーが付いていないものを間違えて購入しないように注意したほうがよいですね。

以下のモジュールであれば、問題なく動きました。

追加で用意したもの

ソーラーパネルを組み込むための追加用意したものです。

大きさや種類によって選択肢がありますが、今回は「6V 1W 長さ:110mm 幅:60mm」を購入しました。

充電モジュールである「TP4056」と充電池を格納するソケット付きになっています。もう一つ昇圧電源モジュールも付属していますが、今回は使用していません。

こちらは楽天で購入しました。3.7V、3000mA、保護回路基板付きのものを購入しています。

その他用意したもの

配置例

ユニバーサル基板での配置になります。今回、左側のものを太陽光からの電力をリチウムイオン電池に充電とするとともに、その電力をESP8266へ供給する流れにしています。

実際の組み立てでは、サイズをコンパクトにするために、左側のものを下、右側のものが上になるよう、2階建て構造でネジで固定します。

充電池からESP8266への電力供給はUSBを使って接続するようにしています。右側の測定回路のほうはDHT11とBMP180をBME280に置き換えしています。

参考サイト

Solar Powered WiFi Weather Station V1.0」のサイトが比較的わかりやすかったです。こちらのほうがまともな百葉箱を作って動かしていたみたいですね。

サンプルコード

サンプルコードは次の通りです。前回からの変更点として、DHT11、BMP180向けのコードは削除し、代わりにBME280向けのコードを追加、DHCPによる動的IPアドレス取得から静的IPアドレスを設定するコードを追加しています。それ以外については特に大きな変更はしていません。

I2Cで使用するBME280のアドレスは0x76のようです。

#include <Wire.h>
#include <BH1750.h>
#include <Adafruit_BME280.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
#include <time.h>

// LM393
#define RAIN_PIN_A A0
#define RAIN_PIN_D D7
#define RAIN_PIN_V 10

// LED OK/NG by HTTP status
#define LED_OK D5
#define LED_NG D6

// Constant values
#define JST     3600*9
#define BH1750_ADDRRESS 0x23
#define BME280_ADDRESS 0x76
#define SEALEVELPRESSURE_HPA (1013.25)

ADC_MODE(ADC_VCC);

BH1750 lm(BH1750_ADDRRESS);
Adafruit_BME280 bme;

// Wifi Credential
const char* ssid = "ssid";
const char* password = "password";

// Endpoint URL
const char* url = "https://upload-server.example.com";
// 署名証明書 Fingerprint, SHA-1
const uint8_t fingerprint[20] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

// Static IP address 適宜変えてください
IPAddress ip(192, 168, 1, 2); //ip
IPAddress gateway(192,168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress dns(192, 168, 1, 1);

void setup() {

  // Initialization
  Serial.begin(9600);
  delay(100);
  Serial.println("Start");

  // Preprocessing
  Wire.begin();
  connect();
  delay(3000);  // NTP時間反映待ち

  // BH1750
  lm.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
  // BME280
  bme.begin(BME280_ADDRESS);
  // LM393
  pinMode(RAIN_PIN_V, OUTPUT);
  digitalWrite(RAIN_PIN_V, LOW);
  // LEDs setup
  pinMode(LED_OK, OUTPUT);
  pinMode(LED_NG, OUTPUT);

  // Main
  captureAndSendData();

  // Post-processing
  disconnect();
  ESP.deepSleep(600 * 1000 * 1000, WAKE_RF_DEFAULT);  // 10分間deep sleep
  delay(1000); // Deep Sleepモード移行までの待ち

}

void loop() {
}

void captureAndSendData() {

  // 電源電圧取得
  String voltage = String(float(ESP.getVcc()) / 1000);
  Serial.println( "3.3V:>>" + voltage);

  // 実行時間取得, yyyyMMddhhmmss形式
  time_t t;
  struct tm *tm;
  t = time(NULL);
  tm = localtime(&t);

  char dt[14];
  sprintf(dt, "%04d%02d%02d%02d%02d%02d", (tm->tm_year+1900), (tm->tm_mon + 1), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
  Serial.printf("Date Time: %s\n", dt);

  // BH1750
  float lux = lm.readLightLevel();
  Serial.println("Light: " + String(lux) + " lx");

  // BME280
  float temp = bme.readTemperature();
  float humi = bme.readHumidity();
  double pres = double(bme.readPressure()) / 100;
  double altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
  Serial.println("Temperature:" + String(temp) + " °C");
  Serial.println("Humidity:" + String(humi) + " %");
  Serial.println("Pressure:" + String(pres) + " hPa");
  Serial.println("Approx. Altitude:" + String(altitude) + " m");

  // LM393
  digitalWrite(RAIN_PIN_V, HIGH);
  boolean raining = !(digitalRead(RAIN_PIN_D));
  if(raining){
    Serial.println("Raining");
  } else{
    Serial.println("No Raining");
  }
  // "ADC_MODE(ADC_VCC)"を使用しない場合は以下でセンサーの電圧を取得
  //int rainVal = analogRead(RAIN_PIN_A);
  //Serial.println("Level:" + String(rainVal));
  digitalWrite(RAIN_PIN_V, LOW);

  // 取得データの送信:JSONで送る場合
  // vo: voltage
  // dt: datetime
  // t:  temperature
  // h:  humidity
  // lx: light intensity
  // p:  pressure
  // alt: approx altitude
  // ir: is raining
  String data = String("") + "{\"vo\":\"" + voltage + "\",\"dt\":\"" + String(dt) + "\",\"t\":\"" + String(temp, 2) + "\",\"h\":\"" + String(humi, 2)
  + "\",\"lx\":\"" + String(lux, 2) + "\",\"p\":\"" + String(pres, 2) + "\",\"alt\":\"" + String(altitude, 2) + "\",\"ir\":\"" + String(raining)
  + "\"}";
  Serial.println("Data: " + data);
  putData(data);

}

void connect () {

  // 静的IPを設定
  WiFi.config(ip, gateway, subnet, dns);
  delay(100);
  WiFi.begin(ssid, password);
  while(WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(500);
  }
  Serial.println();
  Serial.printf("Connected, IP address: ");
  Serial.println(WiFi.localIP());
  configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");

}

void disconnect() {

  WiFi.disconnect(true);
  Serial.printf("Disconnected.");

}

void putData (String data) {

  std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
  client->setFingerprint(fingerprint);
  // 証明書チェックを無視する場合はこちらをコメントアウト
  // client->setInsecure();

  HTTPClient https;

  // Reset LED
  digitalWrite(LED_OK, LOW);
  digitalWrite(LED_NG, LOW);
  Serial.print("[HTTPS] begin...\n");

  if (https.begin(*client, url)) {  // HTTPS

    Serial.print("[HTTPS] POST...\n");

    https.addHeader("Content-Type", "application/json; charset=utf8");
    int httpCode = https.POST(data);

    if (httpCode > 0) {
      Serial.printf("[HTTPS] POST... code: %d\n", httpCode);
      String payload = https.getString();
      Serial.println(payload);
      if (httpCode == HTTP_CODE_OK) {
        digitalWrite(LED_OK, HIGH);
        digitalWrite(LED_NG, LOW);
      } else {
        digitalWrite(LED_OK, LOW);
        digitalWrite(LED_NG, HIGH);
      }
    } else {
      Serial.printf("[HTTPS] POST... failed, error: %s\n", https.errorToString(httpCode).c_str());
      digitalWrite(LED_OK, LOW);
      digitalWrite(LED_NG, HIGH);
    }
    https.end();
  } else {
    Serial.printf("[HTTPS] Unable to connect\n");
    digitalWrite(LED_OK, LOW);
    digitalWrite(LED_NG, HIGH);
  }

}

実際の組み立て

実際にはこんな感じで作りました。前回と比べると若干シンプルにはなっていると思います。

ソーラーパネルは1枚使用して、次のように長いジャンパーケーブルを使って、端子部分をはんだ付けします。裏面保護のために、適当なアクリル板等を使っています(アクリル板はホームセンター等で購入できます)

プラスチックケースはコードを通しやすいように穴を開けます(穴あけが綺麗ではないですが・・・)

機器は以下のような感じで収めます。ソーラーパネルはとりあえず、百葉箱の屋根左側にビニルテープで固定するようにしています。充電池はケースには収まらないため、外出しにして、防水のためにビニルテープで全体を覆いました。

収めるとこんな感じ。ケースと電池が大きいため、中が細々しています。

表の蓋を閉じて完成です。

これで、太陽が当たる玄関外において、電力が持続するかどうかテストしました。

結果

1日朝から電圧を測定した結果です。

あれ??

あれ?増えてない・・・

測定した日が冬場だったこともあり、発電量が若干弱いかもしれませんが、日中の時間帯(8時から15時ぐらい)であるにも関わらず、少しづつ電圧が低下して、最終的には翌日0時頃にはすっかり動かなくなってしまいました・・・

ということは、日中に充電池へ十分に蓄電されていなかったのかな?今回使用したソーラーパネルの電力は1Wの出力ですが、実際に供給される電力量に対して、今回のESP8266での電力消費量のほうが上回っていた可能性が高そうです。

最終的にどうしたか

最終的にはソーラーパネルを2枚並列に設置して動かしたことで、電圧低下がなくなりました。

一旦ベニヤ板につけてます

ソーラーパネル2枚構成にしたときの5日間(2022/2/14-2022/2/18)での測定結果です。

冬場での測定となりますが、夜間に消費した電力は翌日に蓄電されているため、全体的に電圧が2.9V〜3.1Vで安定しています。

ベニヤ板で突貫で取り付けたソーラーパネルは今後綺麗にするとして、どうやら今回のケースであれば、6V 1Wのソーラーパネル2枚搭載すれば、電池が切れることなく動かすことができそうです。

今回適当に6V 1Wのソーラーパネル2枚構成でうまくいきましたが、今後のことも考えて、なぜうまくいったのか、別のブログにて、実際に消費する電力量を測った上でその枚数で問題なかったのかどうか検証してみようと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です