Webduino (5) Smart+DHT+OLED

Webduino Cloud Platformは、スケッチ保存できるクラウド環境。

(それまで、作ったスケッチは、保存できない!)

開始使用:https://cloud.webduino.io

Gmailのアカウントで簡単にログイン可能。

早速公開しているのスケッチ(Public Files から検索)を試す。意外に簡単。

Smart+OLED(圖片)

まずOLEDを増設、スケッチの通り結線しただけで表示ができた。

OLEDの接続と表示。

Smart+DHT+OLED

さらにDHT11を追加し、スケッチの通り結線しただけ、すごく気楽にできた。

DHT11を接続して、温度と湿度も簡単に表示できた。

Webduino (3) RGB LED

Webduino Smartに接続

Webduino Smartに接続方法は2つある

WiFi接続

WiFi接続はデバイスIDを利用して接続する。

この場合はWebduino Blockly ( https://blockly.webduino.io )を開いてプログラミングする。

WebSocket接続

WebSocket接続は、デバイスのローカルIPで接続する。

この場合、http://blockly.webduino.io/ を開いてプログラミングする。

ボタンでRGB LED点灯

Webduino Smartには、RGB LED内蔵なので、まず点灯してみる。

http://blockly.webduino.io/ を開いて、下記の通り、プログラミングする。

右上の “Run Blocks”をクリックして、Web Demo エリアのボタン1..5をクリックすると、対応色が点灯する。

因みに正面表記のGPIO番号で、ピン番号との対応は下記の通り:

static const uint8_t D0   = 16;
static const uint8_t D1   = 5;
static const uint8_t D2   = 4;
static const uint8_t D3   = 0;
static const uint8_t D4   = 2;
static const uint8_t D5   = 14;
static const uint8_t D6   = 12;
static const uint8_t D7   = 13;
static const uint8_t D8   = 15;
static const uint8_t D9   = 3;
static const uint8_t D10  = 1;

つまり、次のような対応関係になり、

TX RST
RX AD(Photocell)
05 D1 3.3
04(Switch) D2 D0 16
00 D3 D5 14
02 D4 D6 12(LED-Green)
15(LED-Red) D8 D7 13(LED-BLue)
GND ===================== VCC

Webduino (2) Update Firmware

ブラウザからデバイスにアクセスする場合、画面の左下に、Firmwareのバージョンが表示され(Ver 3.0.07)

セットアップが終わり、ファームウェアのアップデートを行うと勧められて、しかしどうしてもうまくいかないて、二日悩み中。

英語と中国語のtutorialsは、ここでマイクロスイッチを押し続け、ファームウェアのアップデートが自動的に行う。

  • https://webduino.io/en/tutorials/smart-02-setup.html
  • https://tutorials.webduino.io/zh-tw/docs/basic/board/smart-setup.html

しかし、いくら押し続けでも、LEDの反応がない、だがなぜかファームウェアのアップデートできない。ファームウェアのアップデートため、デバイスIDが取得できない。そして、Webduinoクラウドにも繋げられない。

悩んだ末、たまたまtutorialsのyoutubuを見てわかった。電源を再投入して、赤いLED点滅してる間に、マイクロスイッチを押し続ける

これてうまく行った!緑LED点灯など、しばらくして更新終了(無論更新中電源OFF駄目)、今度ブラウザからデバイスにアクセスする場合、デバイスIDが表示され、画面の左下に、更新後のFirmwareのバージョンが表示された。

Ver 3.1.4_0802

次にWebブラウザを使ったプログラミングを行う。

 

Webduino (1) Setup

About Webduino Smart

Webduino Smartとは、Web + Arduinoの意味合いをもち、ブラウザプログラミングができるESP8266モジュール。

https://webduino.io/

https://tutorials.webduino.io/zh-tw/docs/basic/board/smart-information.html

外形はESP8266でできたWemosと似ている、ただ基板の裏面は3色LED」と「明度センサー」が付いている。

Webduino Smart Wi-Fi Setup

このリンク先Webduino Smart Wi-Fi Setupを参考しながら進み。

まずはmicroUSBケーブルをさして電源を入れる。青LEDが点灯し、また裏面の3色LEDは赤点灯する。

この状態で、Webduino SmartはAPモードになる。

スマホなどからWiFi APを検索すると「Smart」というAPがいるので、そこに接続する。接続パスワードは「12345678」。

接続したらブラウザから「http://192.168.4.1」にアクセス。WiFi SSID/WiFI PWDは、自宅などのWiFI APを指定。また、Device IDは識別できるようなものを指定。「SUBMIT」ボタンをクリックし、変更を反映したら、電源を抜く。

電源を入れ直すと、裏面の3色LEDは、赤点灯したあと、一瞬緑点灯し、消灯する。

この状態で、自宅WiFI APに接続された状態となる。

Webdiunoに割り当てられたIPアドレスを確認するには、スマホなどでWiFi AP検索する。

上記の様に「Smart_<IPアドレス>」という形でIPアドレスが表示されるので確認する。

まずは動作確認として、パソコンから「http://<IPアドレス>」にアクセスできることを確認。

表示画面は、APモードでブラウザから「http://192.168.4.1」にアクセスする際と同じです。

 

WeMos (d2) Home Automation

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

ハードウェア

“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″}

データ受信

スマートフォンから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ライブラリを自作すると考える。