ESP32でOpenWeatherMapのAPIを使って現在の天気をLCD「AQM1602A」に表示させる

ESP32でOpenWeatherMapのAPIを使って現在の天気をLCD「AQM1602A」に表示させる

タイトルの通りです。
※この記事は以下の記事の組み合わせで作られています。理解できない箇所がある場合はそちらをまずご覧になり、このページのプロジェクトを見てください。

前提となる知識

動作する様子

回路


回路はESP32でI2C接続のLCD「AQM1602A」を使うと同じです。こちらを参照してください。
I2C接続をするためにESP32とLCDのSCL,SDAを接続し、LCDに電源を供給しただけのシンプルな回路です。

ソースコード(コピペで動く)

ssid,password,それからOpenWeatherMapのAPIkeyをご自分のものに変更してから書き込んでください。

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <stdio.h>

const char* ssid = "ssid";
const char* password =  "password";
 
const String endpoint = "http://api.openweathermap.org/data/2.5/weather?q=tokyo,jp&APPID=";
const String key = "yourAPIkey";

//LCD関連
#define LCD_ADRS 0x3E
char moji1[] = "Weather:";
char moji2[] = "Temp:";

//データ書き込み
void writeData(byte t_data){
  Wire.beginTransmission(LCD_ADRS);
  Wire.write(0x40);
  Wire.write(t_data);
  Wire.endTransmission();
  delay(1);
}
//コマンド書き込み
void writeCommand(byte t_command){
  Wire.beginTransmission(LCD_ADRS);
  Wire.write(0x00);
  Wire.write(t_command);
  Wire.endTransmission();
  delay(10);
}
//LCD初期化
void init_LCD(){
  delay(100);
  writeCommand(0x38);
  delay(20);
  writeCommand(0x39);
  delay(20);
  writeCommand(0x14);
  delay(20);
  writeCommand(0x73);
  delay(20);
  writeCommand(0x52);
  delay(20);
  writeCommand(0x6C);
  delay(20);
  writeCommand(0x38);
  delay(20);
  writeCommand(0x01);
  delay(20);
  writeCommand(0x0C);
  delay(20);
}
//LCD関連終わり

void setup() {
 
  Serial.begin(115200);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
 
  Serial.println("Connected to the WiFi network");
  //LCDセットアップ
  Wire.begin(21,22); //Wire.begin(SDA,SCL)
  init_LCD();
}
 
void loop() {
 
  if ((WiFi.status() == WL_CONNECTED)) {
 
    HTTPClient http;
 
    http.begin(endpoint + key); //URLを指定
    int httpCode = http.GET();  //GETリクエストを送信
 
    if (httpCode > 0) { //返答がある場合
 
        String payload = http.getString();  //返答(JSON形式)を取得
        Serial.println(httpCode);
        Serial.println(payload);

        //jsonオブジェクトの作成
        DynamicJsonBuffer jsonBuffer;
        String json = payload;
        JsonObject& weatherdata = jsonBuffer.parseObject(json);

        //パースが成功したかどうかを確認
        if(!weatherdata.success()){
          Serial.println("parseObject() failed");
        }

        //各データを抜き出し
        const char* weather = weatherdata["weather"][0]["main"].as<char*>();
        const double temp = weatherdata["main"]["temp"].as<double>();
        Serial.print("weather:");
        Serial.println(weather);
        Serial.print("temperature:");
        Serial.println(temp-273.15);

        //LCDに表示
        //天気を表示
        for(int i=0;i<sizeof(moji1)-1;i++){
          writeData(moji1[i]);
        }
        //weatherは動的配列なのでstrlenで長さを取得する
        for(int i=0;i<strlen(weather);i++){
          writeData(weather[i]);
        }

        //気温を表示
        //2行目へ移動
        writeCommand(0x40+0x80);
        for(int i=0;i<sizeof(moji2)-1;i++){
          writeData(moji2[i]);
        }
        char tempArray[16] = "";
        sprintf(tempArray,"%.1f",temp-273.15);
        
        for(int i=0;i<sizeof(tempArray)-1;i++){
          writeData(tempArray[i]);
        }
      }
 
    else {
      Serial.println("Error on HTTP request");
    }
 
    http.end(); //Free the resources
  }
 
  delay(30000);   //30秒おきに更新
 
}

少し解説

前提知識にはない部分を解説していきます。
今回の肝は取得した天気のデータなどをどうLCDに送るかという点です。

LCDに送る文字列の長さの問題

//LCDに表示
        //天気を表示
        for(int i=0;i<sizeof(moji1)-1;i++){
          writeData(moji1[i]);
        }
        //weatherは動的配列なのでstrlenで長さを取得する
        for(int i=0;i<strlen(weather);i++){
          writeData(weather[i]);
        }

loop()内のこの部分を解説します。
表示したい形式としては

Weather: 天気
Temp: 気温

です。最初のfor文内の

sizeof(moji1)

でmoji1、つまりWeatherという文字列の長さを取得します。moji1の長さはコンパイル時に決まっているので、これで7という数字が入りますね。それをfor文で回して

writeData(moji1[i]);

とすればLCDにWeatherが表示されます。こうすると、後からmoji1の内容を変えてもコードを変更する必要がないという利点があります。

次が少し厄介でして、天気の情報が入ったweatherは最初からサイズが決まっていない動的配列です。宣言部分を見てみましょう。

const char* weather = weatherdata["weather"][0]["main"].as<char*>();

ただのcharではなく、ポインタです。つまりweatherは文字列が入った場所を指示しているだけであり、sizeofを使ってもそのポインタ自体のサイズが出てしまいます。私の場合は4で固定でした。

中身のサイズを知るには、strlen()関数を使います。あとは上と同じで

for(int i=0;i<strlen(weather);i++){
          writeData(weather[i]);
}

というコードで天気情報を表示できました。これでcloudyでもrainyでもsunnyでも、どんな天気が来ても無事に表示できます。

次に2行目へ移動しますが、これは以下のコードで実現できます。
コマンドの詳細はAQM1602のデータシートを見ましょう。

writeCommand(0x40+0x80);

小数点以下を持つ数値を文字列に変換する

これは若干苦戦しましたが、c言語の標準ライブラリであるstdio.hにあるsprintf()関数を使えば実現できます。

sprintf(tempArray,"%.1f",temp-273.15);

使い方は簡単。c言語でありがちなprintfの出力を第一引数に代入してくれる関数です。以下の二つは同じ結果になります。

char tempArray[16] = "";
sprintf(tempArray,"%.1f",temp-273.15);
printf(tempArray)
printf("%.1f",temp-273.15);

%の部分に第3引数が入り、その結果を文字列として第1引数に代入してくれます。便利。これで文字列ができました。

因みにtemp-273.15としているのは、tempがケルビン単位で取得されているからです。

文字列になったのであとは同様に

for(int i=0;i<sizeof(tempArray)-1;i++){
  writeData(tempArray[i]);
}

でおしまい。

最後に

twitterアカウントの方で最新記事のお知らせや記事にならないような小さな情報を小出ししています。もしよろしければフォローお願いいたしますm(__)m

ご覧いただきありがとうございました!

2件のコメント

返信を残す

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