M5Stamp PICOで天気予報を表示 ~OpenWathe編~
M5Stamp PICOで天気予報サイト OpenWatherにアクセスして、天気予報データを取得しお天気アイコンを表示します。OpenWatherからの天気予報データ取得方法
OpenWatherのAPIを使用するにはAPI-KEYが必要なので、アカウントを作成してAPI-KEYを取得します。アカウントは無料と35GBP(約6千円/月)~700GBP(約12万円/月)の有料コースがあります。
無料アカウントでは利用でるAPIに制限があるほか、利用回数も60回/分、100回/日に制限されます。
取りあえずは、近い時間のお天気アイコンを表示するだけなので、無料アカウントで事足ります。
今回は、5 day / 3 hour forecastのAPIを利用します。
利用方法は API解説で説明されています。
api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}
観測地の緯度{lat}、経度{lon} の値は、Googlemapで観測地点を右クリックすれば表示されます。
表示された値をコピペすればOKです。
JSONとアイコン
APIにアクセスするとJSON形式で天気予報データが返ってきますJSONの読み取りにArduinoJSONライブラリを使用します。
ストリームから入力したテキストを渡すとJSONに展開してくれるので、使い方がとても簡単です。
基本、▼の操作だけで時刻ごとの天気IDが取り出せます。
HTTPClient http;
http.begin(endpoint + key); // URLを指定
int httpCode = http.GET(); // GETリクエストを送信
if (httpCode == 200) { // Request Result 正常終了
String watherData = http.getString(); //返答(JSON形式)を取得
deserializeJson(doc, watherDataStr);
}
int count = doc["cnt"];
for (int i=0;i<count;i++) {
time_t dateTime = doc["list"][i]["dt"]; // 予報時刻を取得
int watheId = doc["list"][i]["weather"][0]["id"]; // WatherIDを取得
}
お天気アイコンの作成
晴と、曇り以外は表示時にパーツを合成する事で実現しました。※作るのが面倒なので。作成したアイコン画像を lcd-image-converter で1ドット/1ByteのBITMAPデータに変換します。
ガジェットの立体配線図
SPIのCSとMISOは省略
CSはSSD1331のRSTに使用
MISOは入力専用ピンなので出力には使えません。
DHT-11のプルアップ抵抗は4.7KΩを使用
スケッチの構造
setup( )
起動時の初期後にNTPサーバから時刻を取得してRTCに設定
loop( )
1分毎に温湿度を取得して表示
毎時00分にOpwnWatherMapから天気予報を取得
3時間ごとの天気をアイコン表示
OpwnWather.zip
※ <>を全角<>に置き換えています。コピペの際は注意してください。
▼に表示しているのは、プログラムの一部です。全体はZIPファイルを解凍して参照してください。
/*
* M5Stamp PICO IoT実験
* 天気予報を取得
*/
#include <WiFi.h>
#include <SPI.h>
#include <Wire.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#define DEBUG true
#define BAUDRATE 9600
char str[120] ;
#include "ssd1331.h"
#include "sd1307.h"
#include "openWather.h"
// --- OLED ---
SSD1331 ssd1331 ;
// --- RTC 000
SD1307 sd1307 ;
// --- DHT-11
#define DHT_PIN 0
struct dht11_s {
int8_t humidityN ; // 湿度の整数部
int8_t humidityD ; // 湿度の小数部 ※は常に0
int8_t temperatureN; // 温度の整数部
int8_t temperatureD; // 温度の小数部
} dht11 ;
// --- OpenWatheMap ---
OPENWATHER openWather ;
// ----- NetWork Data ---
const char* ssid = "****";
const char* password = "********";
// --- 時刻調整用
#define SPANTIME 60 // データ取得間隔
unsigned long passTime;
int sendTime = 0 ;
struct tm timeInfo;
// --------------
// ----- NTP ----
// --------------
void connectWiFi( ) {
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect( ) ;
int i = 0 ;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
#if DEBUG
Serial.print(".");
#endif
i++ ;
if (i > 20) {
delay(30000);
return ;
}
}
#if DEBUG
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
#endif
}
}
void getNTP( ) {
connectWiFi() ;
configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");
getLocalTime(&timeInfo) ;
sd1307.yer = timeInfo.tm_year ; // 年
sd1307.mon = timeInfo.tm_mon + 1 ; // 月
sd1307.day = timeInfo.tm_mday ; // 日
sd1307.sec = timeInfo.tm_sec; // 秒
sd1307.mnt = timeInfo.tm_min ; // 分
sd1307.how = timeInfo.tm_hour ; // 時
sd1307.weekday = timeInfo.tm_wday + 1; // 曜日(1~7)
sd1307.setTime( ) ;
#if DEBUG
sprintf(str,"NEW %04d/%02d/%02d(%d) %02d:%02d:%02d",
timeInfo.tm_year + 1900 , timeInfo.tm_mon + 1 , timeInfo.tm_mday, timeInfo.tm_wday + 1 ,
timeInfo.tm_hour , timeInfo.tm_min , timeInfo.tm_sec) ;
Serial.println(str) ;
sd1307.getTime( ) ;
sprintf(str,"RTC 20%02d/%02d/%02d(%d) %02d:%02d:%02d",
sd1307.yer , sd1307.mon , sd1307.day, sd1307.weekday ,
sd1307.how , sd1307.mnt , sd1307.sec) ;
Serial.println(str) ;
#endif
}
// ----------------
// ----- DHT11 ----
// ----------------
bool readDHT11( ) {
uint8_t readData[5] ;
uint8_t checkSum = 0 ;
int setPos = 0 ;
int bitLen = 0 ;
int stat = HIGH ;
uint8_t bitData ;
unsigned long beginTime ;
unsigned long passTime ;
unsigned long zeroTime = 35 ; // 35μ秒以下なら0 以上なら1と判定 ※仕様では0は26~28μ秒
bool flagStart = false;
for (int i=0;i<5;i++) {
readData[i] = 0 ;
}
// ----- データ送信要求 -----
pinMode(DHT_PIN, OUTPUT); //開始信号
delay(20);
pinMode(DHT_PIN, INPUT);
while(digitalRead(DHT_PIN) == LOW) {
delayMicroseconds(5) ;
}
// ----- ACSEPT -----
flagStart = false;
passTime = 0 ;
beginTime = micros();
while (passTime < 1000) {
passTime = micros() - beginTime ;
if (digitalRead(DHT_PIN) != stat) {
if (stat == HIGH) {
stat = LOW ;
if (flagStart) {
break ;
}
} else {
beginTime = micros();
flagStart = true ;
stat = HIGH ;
}
}
}
// ----- データ受信 -----
bitData = 0x80 ;
flagStart = false;
passTime = 0 ;
beginTime = micros();
while (bitLen < 40 && passTime < 1000) {
passTime = micros() - beginTime ;
if (digitalRead(DHT_PIN) != stat) {
if (stat == HIGH) {
stat = LOW ;
if (flagStart) {
// ----- 1Bit 受信 -----
if (passTime > zeroTime) {
readData[setPos] |= bitData ;
}
if (bitData != 0x01) {
bitData >>= 1 ;
} else {
bitData = 0x80 ;
setPos ++ ;
}
// ----- ステート変数リセット -----
beginTime = micros();
flagStart = false;
bitLen ++ ;
}
} else {
beginTime = micros();
flagStart = true ;
stat = HIGH ;
}
}
}
// ----- 取得データ格納 -----
dht11.humidityN = readData[0] ; // 湿度の整数部 Range:5~95
dht11.humidityD = readData[1] ; // 湿度の小数部 ※は常に0
dht11.temperatureN = readData[2]; // 温度の整数部 -20 ~
dht11.temperatureD = readData[3]; // 温度の小数部
checkSum = readData[0] + readData[1] + readData[2] + readData[3] ;
return (bitLen == 40 && checkSum == readData[4]) ;
}
// ----------------------
// ----- セットアップ -----
// ----------------------
void setup() {
// --- 初期化 ---
#if DEBUG
Serial.begin(BAUDRATE);
Serial.println("\nWakeUp") ;
#endif
Wire.begin() ;
SPI.begin();//SPIを初期化、SCK、MOSI、SSの各ピンの動作は出力、SCK、MOSIはLOW、SSはHIGH
ssd1331.init( ) ;
// --- Netwaorkに接続
Serial.println("Networkに接続") ;
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
#if DEBUG
Serial.print(".");
#endif
}
#if DEBUG
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
#endif
// ----- 起動時 初期読み取り -----
getNTP( ) ;
}
// ----------------------
// ----- ループ-----
// ----------------------
void loop() {
sd1307.getTime( ) ;
sprintf(str,"%02d/%02d %02d:%02d", sd1307.mon , sd1307.day , sd1307.how , sd1307.mnt ) ;
ssd1331.dispStr(str,4,0) ;
if (sd1307.sec == 0 || openWather.watheData[0].dateTime == 0) {
if (readDHT11( )) {
sprintf(str,"%2d.%dC %2d%%", dht11.temperatureN ,dht11.temperatureD,dht11.humidityN ) ;
} else {
sprintf(str,"--.--C --%%") ;
}
ssd1331.dispStr(str,4,16) ;
}
if ((sd1307.mnt == 0 && sd1307.sec == 10) || openWather.watheData[0].dateTime == 0) {
if (sd1307.how == 0) {
getNTP( ) ;
}
openWather.getWatherData(sd1307.how) ;
struct tm tim = *localtime( &openWather.watheData[0].dateTime );
sprintf(str,"%02d:%02d",tim.tm_hour,tim.tm_min) ;
ssd1331.dispWatherIcon(openWather.watheData[0].watheId, 0,32,str) ;
tim = *localtime( &openWather.watheData[1].dateTime );
sprintf(str,"%02d:%02d",tim.tm_hour,tim.tm_min) ;
ssd1331.dispWatherIcon(openWather.watheData[1].watheId,32,32,str) ;
tim = *localtime( &openWather.watheData[2].dateTime );
sprintf(str,"%02d:%02d",tim.tm_hour,tim.tm_min) ;
ssd1331.dispWatherIcon(openWather.watheData[2].watheId,64,32,str) ;
}
// ループ間隔 10秒
delay(1000) ;
}