Edited at

WEMOS の ESP32 で クラウドの気象データを表示

WiFiにつながる安いESP32のボードで、クラウドに接続して、今の気象データを表示するIoTをしてみました。

IMG_20181008_072437.jpg


ESP32 WEMOS Lolin32

AKBONE Release 5 に採用して、いろいろ展開しようとしてある程度まとめて2017年に仕入れました。

AKBONE というのは、「日本Androidの会秋葉原支部ロボット部」で作っている電子工作とプログラミングのトレーニングキットです。

WEMOS Lolin 32 は安かったのだけれど、継続的な入手性がネックになり、今年のバージョンの AKBONE Release 6 ではこれではないボード(アオノドン)に乗り換えています。

本家のサイトはこれ。

https://www.wemos.cc/

本家ではないサイトでは、OLEDがついていたり、18650バッテリのホルダーがついていたり、いろいろとバリエーションがあります。

しかしながら、これらはどうやらパチもんらしく、品質がアレだという話も聞こえています。


ESP32-DevKitC との違い

espressif 公式の ESP32-DevKitC と WEMOS の Lolin32 はよく似ています。

DAz9V_OVoAAWIdE (1).jpg


比較


  • ヘッダピン


    • ESP32-DevKitC はんだづけ済

    • Lolin32 ピンは自分ではんだづけ

    • ピン配列は微妙に異なる



  • 形状


    • ESP32-DevKitC はちょっと広い

    • Lolin32 は 2.54mm だけ狭く、400Pのブレッドボードに刺した時に少し余裕ができる



  • その他の付属機能


    • ESP32-DevKitC Bootスイッチ付いている

    • Lolin32 LiPo充電ポート付いている



  • 価格


    • ESP32-DevKitC ¥1480 (秋月電子)

    • Lolin32 約半額



  • USBシリアル


    • ESP32-DevKitC は CP2102

    • Lolin32は CP2104



DAz_v9RU0AIEoqx.jpg

DAz_ydEU0AEfys-.jpg


Arduino開発環境

普通のESP32開発の設定を行うと、ボード選択で「WEMOS LOLIN32」が選べるようになります。

image


動作確認

image

BlinkでLチカできます。内蔵LEDは5番に割り当てられています。


OLEDをつけます。

手元にSPIのOLEDがあったので、つなげてみました。

接続は

GND --  GND

VDD --  5V

SCK --  14

SDA -- 13

RES -- 4

DC -- 21

CS -- 16

IMG_20181001_022515.jpg

ライブラリとして、


  • Adafruit_GFX.h

  • Adafruit_SSD1306.h

を使っています。

ライブラリをインストールすると出てくる、SSD1306_128x64_SPI を使い、

// using software SPI

#define OLED_MOSI 13
#define OLED_DC 21
#define OLED_CS 16
#define OLED_CLK 14
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

だけ書き換えています。


Wi-Fiにつなぐ

「スケッチの例」の「WiFi」「WiFiClientBasic」で動きました。

ssid と passwd 、を書き換えるだけで Wi-Fiに接続しました。(接続後の!client.connect(host, port)))はうまく動かないみたい)


お天気情報を取得する

openweathermap.org

image

からTokyoの天気を取得しました。

サインアップし、APIキーを取得します。

APIを使って、


http://api.openweathermap.org/data/2.5/weather?APPID=eac・・・(APIキー)・・・2e2&id=1850147&units=metric

id=1850147 は東京のコードです。都市の指定は、Tokyoとかの指定でもいいですがコードで指定するほうが無難ぽいです。

http://bulk.openweathermap.org/sample/ の city.list.json.gz に都市のリストがあります。

units=metric とすると温度が摂氏で得られます。

取得したJSONは以下のようになっていました。



{"coord":{"lon":139.69,"lat":35.69}
,"weather":[
{"id":701,"main":"Mist","description":"mist","icon":"50n"}
,{"id":721,"main":"Haze","description":"haze","icon":"50n"}
,{"id":520,"main":"Rain","description":"light intensity shower rain","icon":"09n"}
],"base":"stations"
,"main":{"temp":25.08,"pressure":980,"humidity":83,"temp_min":24,"temp_max":26},"visibility":3500
,"clouds":{"all":75},"dt":1538331240
,"wind":{"speed":9.8,"deg":210,"gust":24.7}
,"sys":{"type":1,"id":7615,"message":0.0068,"country":"JP","sunrise":1538253334,"sunset":1538295968}
,"id":1850147,"name":"Tokyo","cod":200
}";

ArduinoJsonを使い、以下のようにパースしています。

  #include <ArduinoJson.h>

・・・
Serial.println(json); # "json"にデータが入っている
JsonObject& root = jsonBuffer.parseObject(json);
if (!root.success()) {
Serial.println("parseObject() failed");
}
const char* weather = root["weather"][0]["main"];
const char* description = root["weather"][0]["description"];
long pressure = root["main"]["pressure"];
double temp = root["main"]["temp"];

["weather"][0]だけ取得していますが、先ほど示したように複数の値がやってくることがあります。とりあえずは一番最初のものだけを取得するようにしています。


お天気情報を表示する

1分ごとにフェッチして、表示するようにしてみました。

IMG_20181008_072501.jpg

簡単に、まとめたプログラムコードが以下です。


#include <ArduinoJson.h>

// 接続先のSSIDとパスワード
const char ssid[] = "hogehoge";
const char passwd[] = "hagehage";

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <HTTPClient.h>
#include <WiFi.h> // This liblaly cause error "min". Replace to "_min".

// using software SPI
#define OLED_MOSI 13
#define OLED_DC 21
#define OLED_CS 16
#define OLED_CLK 14
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

String accessHttpAPI(char host[]) {
HTTPClient http;
http.begin(host);
int httpCode = http.GET();
String result = "";
if (httpCode < 0) {
result = http.errorToString(httpCode);
} else if (http.getSize() < 0) {
result = "size is invalid";
} else {
result = http.getString();
}
http.end();
return result;
}

void setup() {
Serial.begin(115200);
WiFi.begin(ssid, passwd);
Serial.print("WiFi connecting...");
while(WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(100);
}
Serial.print(" connected. ");
Serial.println(WiFi.localIP());
display.begin(SSD1306_SWITCHCAPVCC);
display.display();
delay(2000);
display.clearDisplay();
}

void loop() {
StaticJsonBuffer<1500> jsonBuffer;
String json = accessHttpAPI("http://api.openweathermap.org/data/2.5/weather?APPID=eac・・・(APIキー)・・・2e&id=1850147&units=metric");
Serial.println(json);
JsonObject& root = jsonBuffer.parseObject(json);
if (!root.success()) {
Serial.println("parseObject() failed");
}
const char* weather = root["weather"][0]["main"];
const char* description = root["weather"][0]["description"];
long pressure = root["main"]["pressure"];
double temp = root["main"]["temp"];
Serial.println(weather);
Serial.println(description);
Serial.println(pressure);
Serial.println(temp);

display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println(weather);
display.setTextColor(BLACK, WHITE); // 'inverted' text
display.setTextSize(1);
display.println(description);
display.setTextColor(WHITE);
display.setTextSize(2);
display.print(pressure); display.println("hPa");
display.print("temp:");display.print(temp);
display.display();
delay(60000);
display.clearDisplay();
}