WeMos (d2) Home Automation

TinyWebDB-APIを利用した、Home Automationの例。

ハードウェア

“IoT-Cloud-Mobile Study Kit”を利用

データ送信

下記のは操作中、数分起き温度、気圧センサーのデータをTinyWebDB-APIテストサーバ(http://tinydb.ml/api/)へ送信する。

送信したデータは、http://tinydb.ml/status/で確認できる。

データ受信

スマートフォンからLED On/Off の指令は受信すると、ESP8266内蔵LEDは点/滅可能になった。

ソースコード

// Sample Arduino Json Web Client
// Downloads and parse http://jsonplaceholder.typicode.com/users/1
//
// Copyright Benoit Blanchon 2014-2017
// MIT License
//
// Arduino JSON library
// https://bblanchon.github.io/ArduinoJson/
// If you like this project, please add a star!

#include <ArduinoJson.h>
#include <Arduino.h>

#include <Adafruit_BMP280.h>
#define BMP_SCK 13
#define BMP_MISO 12
#define BMP_MOSI 11 
#define BMP_CS 10

Adafruit_BMP280 bmp; // I2C
//Adafruit_BMP280 bmp(BMP_CS); // hardware SPI
//Adafruit_BMP280 bmp(BMP_CS, BMP_MOSI, BMP_MISO,  BMP_SCK);

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
 
#define OLED_RESET 0  // GPIO0
Adafruit_SSD1306 OLED(OLED_RESET);

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

#define USE_SERIAL Serial

WiFiClient client;

const char* resource = "http://tinywebdb.cf/api/"; // http resource
const unsigned long BAUD_RATE = 9600;      // serial connection speed
const unsigned long HTTP_TIMEOUT = 10000;  // max respone time from server
const size_t MAX_CONTENT_SIZE = 512;       // max size of the HTTP response

#include "WiFiManager.h"          //https://github.com/tzapu/WiFiManager

void configModeCallback (WiFiManager *myWiFiManager) {
  OLED.println("Entered config mode");
  OLED.println(WiFi.softAPIP());
  //if you used auto generated SSID, print it
  OLED.println(myWiFiManager->getConfigPortalSSID());
  OLED.display(); //output 'display buffer' to screen  
}

HTTPClient http;

void setup() {
    OLED.begin();
    OLED.clearDisplay();
   
    //Add stuff into the 'display buffer'
    OLED.setTextWrap(false);
    OLED.setTextSize(1);
    OLED.setTextColor(WHITE);
    OLED.setCursor(0,0);
    delay(10);

    USE_SERIAL.begin(115200);
   // USE_SERIAL.setDebugOutput(true);

    USE_SERIAL.println();
    USE_SERIAL.println();
    USE_SERIAL.println();

    OLED.println("wifiManager autoConnect...");
    OLED.display(); //output 'display buffer' to screen  

    //WiFiManager
    //Local intialization. Once its business is done, there is no need to keep it around
    WiFiManager wifiManager;
    //reset settings - for testing
    //wifiManager.resetSettings();
  
    //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
    wifiManager.setAPCallback(configModeCallback);
  
    //fetches ssid and pass and tries to connect
    //if it does not connect it starts an access point with the specified name
    //here  "AutoConnectAP"
    //and goes into a blocking loop awaiting configuration
    if(!wifiManager.autoConnect()) {
      Serial.println("failed to connect and hit timeout");
      //reset and try again, or maybe put it to deep sleep
      ESP.reset();
      delay(1000);
    } 
  
    //if you get here you have connected to the WiFi
    Serial.println("connected...yeey :)");

    if (!bmp.begin(0x76)) 
    {
      OLED.println("Could not find BMP280");
      OLED.display(); //output 'display buffer' to screen  
      while (1) {}
    }
}

void OLED_show()
{

  OLED.clearDisplay();
  OLED.setCursor(0,0);
  // Print the IP address
  OLED.print("http://");
  OLED.print(WiFi.localIP());
  OLED.println("/");
  OLED.setCursor(0,8);
  OLED.print("Temp = ");
  OLED.print(bmp.readTemperature());
  OLED.println(" Celsius");
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  OLED.setCursor(0,16);
  OLED.print("Pres = ");
  OLED.print(bmp.readPressure());
  OLED.println(" Pascal ");

  OLED.display(); //output 'display buffer' to screen  
}


void loop() {
    OLED_show();
    get_TinyWebDB("Led1");
    delay(10000);
    sensor_TinyWebDB();
    delay(10000);
}

void sensor_TinyWebDB() {    
    int httpCode;
    char  tag[32];
    char  value[128];

    // read values from the sensor
    float pressure = bmp.readPressure();
    float temperature = bmp.readTemperature();

    const size_t bufferSize = JSON_ARRAY_SIZE(2) + JSON_OBJECT_SIZE(3);
    DynamicJsonBuffer jsonBuffer(bufferSize);
    
    JsonObject& root = jsonBuffer.createObject();
    root["sensor"] = "bmp280";
    root["temperature"] = String(temperature);
    root["pressure_hpa"] = String(pressure);
    root.printTo(value);

    USE_SERIAL.printf("[TinyWebDB] %s\n", value);
    USE_SERIAL.printf("ESP8266 Chip id = %08X\n", ESP.getChipId());
    sprintf(tag, "esp8266-%06x", ESP.getChipId());
    httpCode = TinyWebDBStoreValue(tag, value);
    // httpCode will be negative on error
    if(httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        USE_SERIAL.printf("[HTTP] POST... code: %d\n", httpCode);

        if(httpCode == HTTP_CODE_OK) {
            TinyWebDBValueStored();
        }
    } else {
        USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        TinyWebDBWebServiceError(http.errorToString(httpCode).c_str());
    }

    http.end();

    delay(10000);
}

void get_TinyWebDB(const char* tag0) {    
    int httpCode;
    char  tag[32];
    char  value[128];

    httpCode = TinyWebDBGetValue(tag0);

    // httpCode will be negative on error
    if(httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);

        if(httpCode == HTTP_CODE_OK) {
            String payload = http.getString();
            const char * msg = payload.c_str();
            USE_SERIAL.println(payload);
            if (TinyWebDBreadReponseContent(tag, value, msg)){
                TinyWebDBGotValue(tag, value);
            }
        }
    } else {
        USE_SERIAL.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
        TinyWebDBWebServiceError(http.errorToString(httpCode).c_str());
    }

    http.end();

    delay(10000);
}

int TinyWebDBWebServiceError(const char* message)
{
}

// ----------------------------------------------------------------------------------------
// Wp TinyWebDB API
// Action        URL                      Post Parameters  Response
// Get Value     {ServiceURL}/getvalue    tag              JSON: ["VALUE","{tag}", {value}]
// ----------------------------------------------------------------------------------------
int TinyWebDBGetValue(const char* tag)
{
    char url[64];

    sprintf(url, "%s%s?tag=%s", resource, "getvalue/", tag);

    USE_SERIAL.printf("[HTTP] %s\n", url);
    // configure targed server and url
    http.begin(url);
    
    USE_SERIAL.print("[HTTP] GET...\n");
    // start connection and send HTTP header
    int httpCode = http.GET();

    return httpCode;
}

int TinyWebDBGotValue(const char* tag, const char* value)
{
    USE_SERIAL.printf("[TinyWebDB] %s\n", tag);
    USE_SERIAL.printf("[TinyWebDB] %s\n", value);

    OLED.printf("[TinyWebDB] %s:%s\n", tag, value);
    OLED.display(); //output 'display buffer' to screen  
    
    return 0;   
}

// ----------------------------------------------------------------------------------------
// Wp TinyWebDB API
// Action        URL                      Post Parameters  Response
// Store A Value {ServiceURL}/storeavalue tag,value        JSON: ["STORED", "{tag}", {value}]
// ----------------------------------------------------------------------------------------
int TinyWebDBStoreValue(const char* tag, const char* value)
{
    char url[64];
  
    sprintf(url, "%s%s", resource, "storeavalue");
    USE_SERIAL.printf("[HTTP] %s\n", url);

    // POST パラメータ作る
    char params[128];
    sprintf(params, "tag=%s&value=%s", tag, value);
    USE_SERIAL.printf("[HTTP] POST %s\n", params);

    // configure targed server and url
    http.begin(url);

    // start connection and send HTTP header
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    int httpCode = http.POST(params);
    String payload = http.getString();                  //Get the response payload
    Serial.println(payload);    //Print request response payload

    http.end();
    return httpCode;
}

int TinyWebDBValueStored()
{
  
    return 0;   
}


// Parse the JSON from the input string and extract the interesting values
// Here is the JSON we need to parse
// [
//   "VALUE",
//   "LED1",
//   "on",
// ]
bool TinyWebDBreadReponseContent(char* tag, char* value, const char* payload) {
  // Compute optimal size of the JSON buffer according to what we need to parse.
  // See https://bblanchon.github.io/ArduinoJson/assistant/
  const size_t BUFFER_SIZE =
      JSON_OBJECT_SIZE(3)    // the root object has 3 elements
      + MAX_CONTENT_SIZE;    // additional space for strings

  // Allocate a temporary memory pool
  DynamicJsonBuffer jsonBuffer(BUFFER_SIZE);

  // JsonObject& root = jsonBuffer.parseObject(payload);
  JsonArray& root = jsonBuffer.parseArray(payload);
  JsonArray& root_ = root;

  if (!root.success()) {
    Serial.println("JSON parsing failed!");
    return false;
  }

  // Here were copy the strings we're interested in
  strcpy(tag, root_[1]);   // "led1"
  strcpy(value, root_[2]); // "on"

  return true;
}


// Pause for a 1 minute
void wait() {
  Serial.println("Wait 60 seconds");
  delay(60000);
}

 


参考:

  • TinyWebDB-API : https://wordpress.org/plugins/tinywebdb-api/
  • https://techtutorialsx.com/2016/07/21/esp8266-post-requests/

WeMos (d1) RESTful API

JSONデータの扱いできて、今度RESTful APIに取り込む。まずRESTful APIのGETとPOSTを実現する。自作のTinyWebDB-APIプラグインでテストすることにした。

GETは簡単にできたが、POSTがなかなかうまくいかない。受信したデータは空になっている。いろいろGoogleして、ソースを書き換えてもうまくいかない、今度サーバ側様子をみると思い、Cloud9でデバッグしようとしても、なんとCloud9で弾けられ。普通のVPSでデバッグを続けてるうちに、自信喪失、TinyWebDB-APIに問題があると懐疑的に、cURLで検証しても、受信したデータは空。困った。

Postmanで試したら、なんとうまくいく。何か違うかな?簡単のHTMLフォームもうまくいく。やはり送信側ESP8266プログラムの問題。さらにGoogleして、ソースを書き換えていく。

一週間苦労して、ついに成功。

簡単に利用するために、TinyWebDB-APIライブラリを自作すると考える。

WeMos Smart Car Motor Shield

学園祭のため、IoTぼいのスマート車を作る。

しかし、注文したMotor Shield とは、違うもの(base only)が来たので、急遽自作する。

そのためのコントロールするAppも作りたいが、時間がなくって、ネットからWebページでコントロールするものを探して、沢山手直して、動くようになった。

車数台用意して、各車のIPは固定にしたいので、WiFiManager使わない方法をとった。

Webページでコントロールするから、遅延は目たつ。

時間があったら、ちゃんとアクセルペダルなど追加して、アプリの形にしたい。

// include libraries
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

#define motor_lf D3
#define motor_lb D4
#define motor_rf D6
#define motor_rb D5

// configure server
ESP8266WebServer server(80);
const char *form = "<!DOCTYPE HTML>"
"<meta name='viewport' content='width=device-width'>"
"<html>"
"<center><form action='/'>"
"<button name='dir' type='submit' value='4'>Forward</button><p>"
"<button name='dir' type='submit' value='1'>Left</button> "
"<button name='dir' type='submit' value='2'>Right</button><p>"
"<button name='dir' type='submit' value='3'>Reverse</button><p><p>"
"<button name='dir' type='submit' value='5'>Stop</button>"
"</form></center>"
"</html>";
void stop(void)
{
    analogWrite(motor_lf, 0);
    analogWrite(motor_lb, 0);
    analogWrite(motor_rf, 0);
    analogWrite(motor_rb, 0);
}
void forward(void)
{
    analogWrite(motor_lf, 1023);
    analogWrite(motor_lb, 0);
    analogWrite(motor_rf, 1023);
    analogWrite(motor_rb, 0);
}
void backward(void)
{
    analogWrite(motor_lf, 0);
    analogWrite(motor_lb, 1023);
    analogWrite(motor_rf, 0);
    analogWrite(motor_rb, 1023);
}
void left(void)
{
    analogWrite(motor_lf, 0);
    analogWrite(motor_lb, 0);
    analogWrite(motor_rf, 1023);
    analogWrite(motor_rb, 0);
}
void right(void)
{
    analogWrite(motor_lf, 1023);
    analogWrite(motor_lb, 0);
    analogWrite(motor_rf, 0);
    analogWrite(motor_rb, 0);
}
void handle_form()
{
    // only move if we submitted the form
    if (server.arg("dir"))
    {
        // get the value of request argument "dir"
        int direction = server.arg("dir").toInt();
        // chose direction
        switch (direction)
        {
            case 1:
                left();
                break;
            case 2:
                right();
                break;
            case 3:
                backward();
                break;
            case 4:
                forward();
                break;
            case 5:
                stop();
                break;
        }
        // move for 300ms, gives chip time to update wifi also
        delay(300);
    }
    
    // in all cases send the response
    server.send(200, "text/html", form);
}
void setup()
{
    // connect to wifi network
    WiFi.begin("uislab003", "**password**");
    // static ip, gateway, netmask
    WiFi.config(IPAddress(192,168,11,10), IPAddress(192,168,11,1), IPAddress(255,255,255,0));
    // connect
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(200);
    }
  
    // set up the callback for http server
    server.on("/", handle_form);
    // start the webserver
    server.begin();
    pinMode(motor_lf, OUTPUT); // 
    pinMode(motor_lb, OUTPUT); // 
    pinMode(motor_rf, OUTPUT); // 
    pinMode(motor_rb, OUTPUT); // 

}
void loop()
{
    // check for client connections
    server.handleClient();
}

 

WeMos (c6) Thingspeak

数回JSON関連の実験をしたが、いざTinyWebDBのAPIの実験を始まると、また引っかかるところが多い。

色々と検索してところ、ThingspeakのAPIサンプルが見つかったので、ちょっと曲がり道して試すことに。

センサーの温度と気圧をThingspeakにアップして、動きを見て見る。

まずThingspeakのアカウントを申請して、THINGSPEAK_API_KEYを取得する。

https://thingspeak.com/users/sign_up

次はサンプルを見ながら、プログラミング。

#include <Adafruit_BMP280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
 
#define OLED_RESET 0  // GPIO0
Adafruit_SSD1306 OLED(OLED_RESET);

#define BMP_SCK 13
#define BMP_MISO 12
#define BMP_MOSI 11 
#define BMP_CS 10

Adafruit_BMP280 bmp; // I2C
//Adafruit_BMP280 bmp(BMP_CS); // hardware SPI
//Adafruit_BMP280 bmp(BMP_CS, BMP_MOSI, BMP_MISO,  BMP_SCK);


#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

/***************************
 * Begin Settings
 **************************/

const char* host = "http://api.thingspeak.com";
const char* THINGSPEAK_API_KEY = "***********";

// Update every 600 seconds = 10 minutes. Min with Thingspeak is ~20 seconds
const int UPDATE_INTERVAL_SECONDS = 600;

//needed for library
#include <DNSServer.h>
#include "WiFiManager.h"          //https://github.com/tzapu/WiFiManager

void configModeCallback (WiFiManager *myWiFiManager) {
  Serial.println("Entered config mode");
  Serial.println(WiFi.softAPIP());
  //if you used auto generated SSID, print it
  Serial.println(myWiFiManager->getConfigPortalSSID());
}

/***************************
 * End Settings
 **************************/
 
void setup() {
  OLED.begin();
  OLED.clearDisplay();
 
  //Add stuff into the 'display buffer'
  OLED.setTextWrap(false);
  OLED.setTextSize(1);
  OLED.setTextColor(WHITE);
  OLED.setCursor(0,0);
  delay(10);
  
  Serial.begin(115200);
  delay(10);

  // We start by connecting to a WiFi network

  // Connect to WiFi network
  OLED.println("wifiManager autoConnect...");
  OLED.display(); //output 'display buffer' to screen  
 
  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;
  //reset settings - for testing
  //wifiManager.resetSettings();

  //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
  wifiManager.setAPCallback(configModeCallback);

  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "AutoConnectAP"
  //and goes into a blocking loop awaiting configuration
  if(!wifiManager.autoConnect()) {
    Serial.println("failed to connect and hit timeout");
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(1000);
  } 

  // Print the IP address
  OLED.print("http://");
  OLED.print(WiFi.localIP());
  OLED.println("/");
  OLED.println("WiFi connected");
  OLED.display(); //output 'display buffer' to screen  
  
  //if you get here you have connected to the WiFi
  Serial.println("connected...yeey :)");

  if (!bmp.begin(0x76)) 
  {
    OLED.println("Could not find BMP180 or BMP085 sensor at 0x77");
    OLED.display(); //output 'display buffer' to screen  
    while (1) {}
  }

}

void loop() {      
  // read values from the sensor
  float pressure = bmp.readPressure();
  float temperature = bmp.readTemperature();
  
  // We now create a URI for the request
  String url = host;
  url += "/update?api_key=";
  url += THINGSPEAK_API_KEY;
  url += "&field1=";
  url += String(temperature);
  url += "&field2=";
  url += String(pressure);
  
  Serial.print("Requesting URL: ");
  Serial.println(url);

  HTTPClient http;
  Serial.print("[HTTP] begin...\n");
  // configure targed server and url
  http.begin(url);
  
  Serial.print("[HTTP] GET...\n");
  // start connection and send HTTP header
  int httpCode = http.GET();

  if(httpCode == HTTP_CODE_OK) {
    String buffer = http.getString();
    Serial.println(buffer);
  }

  Serial.println("closing connection");

  // Go back to sleep. If your sensor is battery powered you might
  // want to use deep sleep here
  delay(1000 * UPDATE_INTERVAL_SECONDS);
}

 

こちら問題なくデータの蓄積ができた。

WeMos (c5) JSON exchange rate

WeMosをRESPのクライアントとして機能するため、JSONと、HTTPClientを検証する。

前回ArduinoJsonというライブラリを使用したので、ESP8266ならではのWiFi機能を使い、インターネットから情報を取得して表示させてみる。

JSON データ形式

為替レートの情報は、

Foreign exchange rates and currency conversion JSON API

から取得する。

ドル円レートの情報を取得する場合、URLはhttp://api.fixer.io/latest?base=USD&symbols=JPY

応答は下記の通り

HTTP/1.1 200 OK
Server: nginx/1.13.6
Date: Sat, 04 Nov 2017 13:40:46 GMT
Content-Type: application/json
Content-Length: 57
Connection: close
Cache-Control: public, must-revalidate, max-age=900
Last-Modified: Fri, 03 Nov 2017 00:00:00 GMT
Vary: Origin
X-Content-Type-Options: nosniff
{"base":"USD","date":"2017-11-03","rates":{"JPY":113.94}}

closing connection

プログラム

下記の処理をする

  1.  WiFiManagerでWiFi自動接続
  2. get_exchange_rate処理
    1. api.fixer.io JSONデータ取得
    2. parse json data
    3. OLEDへ表示
  3. 60秒待ち
  4. 2へ続き
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
 
#define OLED_RESET 0  // GPIO0
Adafruit_SSD1306 OLED(OLED_RESET);

const char* URL = "http://api.fixer.io/latest?base=USD&symbols=JPY";  // http resource
//sample json data used in this sketch
// {"base":"USD","date":"2017-11-03","rates":{"JPY":113.94}}

//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include "WiFiManager.h"          //https://github.com/tzapu/WiFiManager

void configModeCallback (WiFiManager *myWiFiManager) {
  Serial.println("Entered config mode");
  Serial.println(WiFi.softAPIP());
  //if you used auto generated SSID, print it
  Serial.println(myWiFiManager->getConfigPortalSSID());
}

void setup() {
  OLED.begin();
  OLED.clearDisplay();
 
  //Add stuff into the 'display buffer'
  OLED.setTextWrap(false);
  OLED.setTextSize(1);
  OLED.setTextColor(WHITE);
  OLED.setCursor(0,0);
  delay(10);
  
  Serial.begin(115200);
  Serial.println("");
  delay(10);

  // Connect to WiFi network
  OLED.println("wifiManager autoConnect...");
  OLED.display(); //output 'display buffer' to screen  
 
  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;
  //reset settings - for testing
  //wifiManager.resetSettings();

  //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
  wifiManager.setAPCallback(configModeCallback);

  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "AutoConnectAP"
  //and goes into a blocking loop awaiting configuration
  if(!wifiManager.autoConnect()) {
    Serial.println("failed to connect and hit timeout");
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(1000);
  } 

  // Print the IP address
  OLED.print("http://");
  OLED.print(WiFi.localIP());
  OLED.println("/");
  OLED.println("WiFi connected");
  OLED.display(); //output 'display buffer' to screen  
  
  //if you get here you have connected to the WiFi
  Serial.println("connected...yeey :)");
 
}

void get_exchange_rate() {
  const int BUFFER_SIZE = JSON_OBJECT_SIZE(4) + JSON_ARRAY_SIZE(1);
  StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
  HTTPClient http;

  Serial.print("[HTTP] begin...\n");
  // configure targed server and url
  http.begin(URL);
  
  Serial.print("[HTTP] GET...\n");
  // start connection and send HTTP header
  int httpCode = http.GET();

  if(httpCode != HTTP_CODE_OK) {
    return;
  }
  String buffer = http.getString();
  Serial.println(buffer);

  //parse json data
  char json[buffer.length() + 1];
  buffer.toCharArray(json, sizeof(json));
  Serial.println(json);
  JsonObject& root = jsonBuffer.parseObject(json);
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }
  const char* date = root["date"];
  Serial.println(date);
  const char* base = root["base"];
  Serial.println(base);
  JsonObject& rates = root["rates"];
  rates.printTo(Serial);
  Serial.println();
  const char* rate = rates["JPY"];
  Serial.println(rate);
  Serial.println();
  
  Serial.println("closing connection");

  OLED.clearDisplay();
  OLED.setCursor(0,0);
  // Print the IP address
  OLED.print("http://");
  OLED.print(WiFi.localIP());
  OLED.println("/");

  OLED.setCursor(0, 16);
  OLED.print(date);
  OLED.setCursor(0, 24);
  OLED.print("JPY/");
  OLED.print(base);
  OLED.print(": ");
  OLED.print(rate);
  OLED.display(); //output 'display buffer' to screen  

}

void loop() {
  get_exchange_rate();
  delay(60000);
}

 

参考

  • http://blog.boochow.com/article/425016458.html — ESP8266版Arduinoでネットから情報を取ってきてLCDに表示する