Category Archives: ESP8266 Wemos (c) web

Wemos (c11) Weather Station

IoT-Cloud-Mobile Kitに、Weather Station機能を組み入れる。

I2CのOLED端子配列2種類があり:

  1. VCC,GND,SCL,SDA (Wemos, BMP180, BMP280バス直結可能)
  2. GND,VCC,SCL,SDA (NodeMcu直結可能)

1番目は、Wemos直結に適するので、試してみる。

接線 (Wiring) Wemos –> OLED

  1. Wemos 5V —> OLED Vcc
  2. Wemos GND –> OLED GND
  3. Wemos D4 —-> OLED SCL
  4. Wemos D3 —-> OLED SDA

また下記のセッティングは、自分の環境に合わせて設定。

* Begin Settings
const char* WIFI_SSID = "xxxx"; // your ssid
const char* WIFI_PWD = "xxxx"; // your password
const int I2C_DISPLAY_ADDRESS = 0x3c; // OLED I2C address
const int SDA_PIN = 13; // I2C OLED SDA
const int SDC_PIN = 14; // I2C OLED SCL
const float UTC_OFFSET = 9; // JST = +9
const String WUNDERGRROUND_API_KEY = "xxxxx"; // WUNDERGROUND API KEY
const String WUNDERGROUND_COUNTRY = "JP"; // japan
const String WUNDERGROUND_CITY = "Tokyo"; // tokyo
const String THINGSPEAK_CHANNEL_ID = "xxxxx"; // ThingSpeak CHANNEL
const String THINGSPEAK_API_READ_KEY = "xxxxx"; // ThingSpaek API KEY

それから、Piggyback (おんぶ) する形で、OLED接続する方法も上手くいく。

OLEDだけ繋ぐなら、こちら方法はコンパクトでいいでしょう。ピン・ソケット使わない、OLEDとWemos直結すればもっと薄くコンパクトな実装になる。

WeMos (c10) Web Clock

TM1637 7段4桁LED表示はできたけど、もっと何かWeb関係のものができないかと考えていて、時刻の表示くらいできると思って、やってみた。

WeMos (5) TM1637 7段4桁LED表示

WiFiManagerを利用してWiFiに接続。

結線ができるだけ省けると考えて、GPIOのPinから給電を試してみた。

プログラムはこれ、D4の隣はGNDだから、D4をVCCとして利用する様にプログラミングする。

#include <TM1637Display.h>
#include <time.h>
#include "WiFiManager.h"  
 
const int CLK = D2; //Set the CLK pin connection to the display
const int DIO = D3; //Set the DIO pin connection to the display
const int VCC = D4; //Set the VCC pin connection to the display
 
int numCounter = 0;
int numTime = 0;
String time_value;
 
TM1637Display display(CLK, DIO); //set up the 4-Digit Display.
 
void setup()
{
  Serial.begin(115200);
  Serial.println("Hello!");

  WiFiManager wifiManager;
  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);
  } 

  configTime(0 * 3600, 0, "pool.ntp.org", "time.nist.gov");

  pinMode(VCC, OUTPUT);
  digitalWrite(VCC, HIGH);
  display.setBrightness(0x04); //set the diplay to 0..7 brightness
}
 
 
void loop()
{
  for(numCounter = 0; numCounter < 1000; numCounter++) //Iterate numCounter
  {
    time_t now = time(nullptr);
    String time = String(ctime(&now));
    Serial.println("time:" + time);

    time_t utc, local;
    struct tm *tm_now;
    utc = now;
    local = utc + 9 *60 * 60;   // Tokyo time
    tm_now = localtime(&local);
    numTime = tm_now->tm_hour * 100 + tm_now->tm_min;
    Serial.printf("numTime: %d  ¥n", numTime);

    display.showNumberDecEx(numTime, 0x40); //Display the numCounter value;
    delay(500);
    display.showNumberDecEx(numTime, 0); //Display the numCounter value;
    delay(500);
  }
}

実動する画面:

デスクトップPCの肩に乗せた様子。

 

WeMos (c9) Web LED Matrix

最初はプレゼントタイマーを作るつもり。

しかし、TinyebDBの文字列を表示する方がはるかも簡単、汎用性もいい。

できたものはこれ:

電源が繋いたら、WiFiを自動で接続、それからTinyWebDBから文字列を取得と表示する。

これならプレゼントタイマーだけじゃなく、お知らせ、顔認証して名前で挨拶とかにも活用できそう。

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
#include <time.h>

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

WiFiClient client;

const char* resource = "http://tinydb.ml/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

HTTPClient http;

int pinCS = D4; // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI )
int numberOfHorizontalDisplays = 4;
int numberOfVerticalDisplays   = 1;
char time_value[20];

// LED Matrix Pin -> ESP8266 Pin
// Vcc            -> 3v  (3V on NodeMCU 3V3 on WEMOS)
// Gnd            -> Gnd (G on NodeMCU)
// DIN            -> D7  (Same Pin for WEMOS)
// CS             -> D4  (Same Pin for WEMOS)
// CLK            -> D5  (Same Pin for WEMOS)

Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);

int wait = 70; // In milliseconds

int spacer = 1;
int width  = 5 + spacer; // The font width is 5 pixels

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

void setup() {
  configTime(0 * 3600, 0, "pool.ntp.org", "time.nist.gov");
  matrix.setIntensity(10); // Use a value between 0 and 15 for brightness
  matrix.setRotation(0, 1);    // The first display is position upside down
  matrix.setRotation(1, 1);    // The first display is position upside down
  matrix.setRotation(2, 1);    // The first display is position upside down
  matrix.setRotation(3, 1);    // The first display is position upside down

  display_message("wifiManager autoConnect...");

  //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 :)");
}

void loop() {
  matrix.fillScreen(LOW);
  get_TinyWebDB("presentationtimer");
  delay(1000);
}

void display_message(String message) {
  if (message.length() > 5) scrool_message(message);
  else {
    for ( int i = 0 ; i < message.length(); i++ ) {
      matrix.drawChar(2 + i * width, 0, message[i], HIGH, LOW, 1); // 
    }
    matrix.write(); // Send bitmap to display
  }
}

void scrool_message(String message) {
  for ( int i = 0 ; i < width * message.length() + matrix.width() - spacer; i++ ) {
    //matrix.fillScreen(LOW);
    int letter = i / width;
    int x = (matrix.width() - 1) - i % width;
    int y = (matrix.height() - 8) / 2; // center the text vertically
    while ( x + width - spacer >= 0 && letter >= 0 ) {
      if ( letter < message.length() ) {
        matrix.drawChar(x, y, message[letter], HIGH, LOW, 1); // HIGH LOW means foreground ON, background off, reverse to invert the image
      }
      letter--;
      x -= width;
    }
    matrix.write(); // Send bitmap to display
    delay(wait / 2);
  }
}


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

    httpCode = TinyWebDBGetValue(tag);

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

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

    http.end();

    delay(1000);
}

// 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;
}

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);

    Serial.printf("[HTTP] %s\n", url);
    // configure targed server and url
    http.begin(url);
    
    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)
{
    Serial.printf("[TinyWebDB] %s\n", tag);
    Serial.printf("[TinyWebDB] %s\n", value);

    display_message(value);
    delay(1000);
    display_message(value);

    
    return 0;   
}

 

TinyWebDBに文字列を用意方法はいくつもある。

  1. HTMLフォームで送信
  2. App Inventor で簡単なアプリ開発
  3. cURLで送信
  4. TinyWebDBサイトを直接いじる。
<h1>TinyWebDB test program</h1>

<form action="http://tinydb.ml/api/storeavalue" method="post">
  <div>tag: <input type="text" name="tag" value="presentationtimer"></div>
  <div>value: <input type="text" name="value" value="12:13"></div>
  <input type="submit" value="submit">
  <input type="reset" value="reset">
</form>

 

 

WeMos (c8) Web Motor Shield Control

学園祭のため、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 (c7) RESTful API

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

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

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

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

下記のソースコードは、”IoT-Cloud-Mobile Study Kit”で動かし、温度、気圧センサーのデータをTinyWebDB-APIテストサーバ(http://tinydb.ml/api/)へ送信する。

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

Sensor_Home

{“sensor”:”bmp280″,”temperature”:”21.85″,”pressure_hpa”:”101534.25″}

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

// 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 (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に表示する

WeMos (c4) JSON

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

ライブラリの追加

JSONを扱うライブラリに「ArduinoJSON」というのがあるので、ライブラリの追加で、リストが出てくるので、JSONと入力し絞込。

ArduinoJSONの動作、JsonBuffer size、Parsing program、Serializing programは、下記のURLで確認しながら進める方が便利でしょう。

https://bblanchon.github.io/ArduinoJson/assistant/

動作確認

公式のサンプルJsonParserExampleをそのまんま。

// 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>

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    // wait serial port initialization
  }

  // Memory pool for JSON object tree.
  //
  // Inside the brackets, 200 is the size of the pool in bytes,
  // If the JSON object is more complex, you need to increase that value.
  // See https://bblanchon.github.io/ArduinoJson/assistant/
  StaticJsonBuffer<200> jsonBuffer;

  // StaticJsonBuffer allocates memory on the stack, it can be
  // replaced by DynamicJsonBuffer which allocates in the heap.
  //
  // DynamicJsonBuffer  jsonBuffer(200);

  // JSON input string.
  //
  // It's better to use a char[] as shown here.
  // If you use a const char* or a String, ArduinoJson will
  // have to make a copy of the input in the JsonBuffer.
  char json[] =
      "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";

  // Root of the object tree.
  //
  // It's a reference to the JsonObject, the actual bytes are inside the
  // JsonBuffer with all the other nodes of the object tree.
  // Memory is freed when jsonBuffer goes out of scope.
  JsonObject& root = jsonBuffer.parseObject(json);

  // Test if parsing succeeds.
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }

  // Fetch values.
  //
  // Most of the time, you can rely on the implicit casts.
  // In other case, you can do root["time"].as<long>();
  const char* sensor = root["sensor"];
  long time = root["time"];
  double latitude = root["data"][0];
  double longitude = root["data"][1];

  // Print values.
  Serial.println(sensor);
  Serial.println(time);
  Serial.println(latitude, 6);
  Serial.println(longitude, 6);
}

void loop() {
  // not used in this example
}

出力

gps
1351824120
48.756080
2.302038

参考

  • https://bblanchon.github.io/ArduinoJson/assistant/ — ArduinoJson Assistant
  • http://shuzo-kino.hateblo.jp/entry/2016/05/06/203603 — ArduinoでJSONを扱う「ArduinoJSON」

WeMos (c3) Web (BMP280+OLED)

いよいよ理想のIoT-Cloud-Mobile実験キットの形ができた。

まず、IoT側

WiFiManagerを組み込み。

/*
 * 
 */
#include <Wire.h> 
#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 <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> 
 
int ledPin = BUILTIN_LED;
WiFiServer server(80);
 
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);
 
 
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
 
  // Connect to WiFi network
  OLED.println("wifiManager autoConnect...");
  OLED.display(); //output 'display buffer' to screen  
 
  WiFiManager wifiManager;
  wifiManager.autoConnect();

  OLED.println("WiFi connected");
  OLED.display(); //output 'display buffer' to screen  
 
  // Start the server
  server.begin();
  OLED.println("Server started");
  
  // Print the IP address
  OLED.print("http://");
  OLED.print(WiFi.localIP());
  OLED.println("/");
 
  OLED.display(); //output 'display buffer' to screen  
  // OLED.startscrollleft(0x00, 0x0F); //make display scroll 

  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 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 ");
  // print the number of seconds since reset:
  OLED.setCursor(0,24);
  OLED.print(millis() / 1000);

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

void loop() {
  delay(500);
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    OLED_show();
    delay(1);
    return;
  }
 
  // Wait until the client sends some data
  OLED.println("new client");
  // while(!client.available()){
  //   delay(1);
  // }
 
  // Read the first line of the request
  String request = client.readStringUntil('\r');
  OLED.println(request);
  OLED.display(); //output 'display buffer' to screen  
  client.flush();
 
  // Match the request
 
  int value = LOW;
  if (request.indexOf("/LED=ON") != -1) {
    digitalWrite(ledPin, LOW);
    value = HIGH;
  } 
  if (request.indexOf("/LED=OFF") != -1){
    digitalWrite(ledPin, HIGH);
    value = LOW;
  }
 
 
 
  // Return the response
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println(""); //  do not forget this one
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
 
  client.print("Temp = ");
  client.print(bmp.readTemperature());
  client.println(" Celsius <br>");
  client.print("Pres = ");
  client.print(bmp.readPressure());
  client.println(" Pascal <br>");

  client.print("Led pin is now: ");
 
  if(value == HIGH) {
    client.print("On");  
  } else {
    client.print("Off");
  }
  client.println("<br><br>");
  client.println("Click <a href=\"/LED=ON\">here</a> turn the LED ON<br>");
  client.println("Click <a href=\"/LED=OFF\">here</a> turn the LED OFF<br>");
  client.println("</html>");
 
  delay(1);
  OLED.println("Client disconnected");
  OLED.println("");
  OLED.display(); //output 'display buffer' to screen  
 
}

 

WeMos (c2) WiFiManager

WeMos のWiFiが、前回のようにSSIDとPASSWORDをコードに書き込む方法の他に、オンライン変更できるような方法もある。

WiFiManager というライブラリを使うと簡単にできる

https://github.com/tzapu/WiFiManager

このソースを参考に試してみる。

まず、ライブラリマネージャーから、WiFiManagerを検索して、インストールする。

ライブラリを使える状態にすると,下記のサンプルで、接続する

#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino

//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() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  
  //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 :)");
 
}

void loop() {
  // put your main code here, to run repeatedly:

}

だけで上のユースケースが満たされて大変便利だ…。標準でWebUIもついてる。