カテゴリー「ESP-WROOM-02」の記事

ESP-WROOM-02でMQTTを使う

IoT関連のプロトコルであるMQTTに興味があったのですが、ESP-WROOM-02 Arduino開発環境で試してみました。

準備

ESP-WROOM-02からMQTT broker(test.mosquitto.org)にmessageをpublishし、Raspberry Piでmessageをsubscribeします。Raspberry Piでmessageをsubscribeするためのパッケージを以下の通りインストールします。(subscribeするだけなら、mosquitto-clientsのみで良いかもしれません)。

$ sudo apt-get install mosquitto
$ sudo apt-get install mosquitto-clients

Arduino環境用のライブラリーArduino Client for MQTTをダウンロードしてlibraryフォルダーにコピーします。


サンプルコード

Arduino Client for MQTT (pubsubclient) のサンプルコードにmqtt_esp8266があり、このコードを若干修正して使っています。

/*
 Basic ESP8266 MQTT example

 This sketch demonstrates the capabilities of the pubsub library in combination
 with the ESP8266 board/library.

 It connects to an MQTT server then:
  - publishes "hello world" to the topic "outTopic" every two seconds
  - subscribes to the topic "inTopic", printing out any messages
    it receives. NB - it assumes the received payloads are strings not binary
  - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
    else switch it off

 It will reconnect to the server if the connection is lost using a blocking
 reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
 achieve the same result without blocking the main loop.

 To install the ESP8266 board, (using Arduino 1.6.4+):
  - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
       http://arduino.esp8266.com/stable/package_esp8266com_index.json
  - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
  - Select your ESP8266 in "Tools -> Board"

*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

#define LED 12

// Update these with values suitable for your network.

const char* ssid = "ssid";
const char* password = "password";
const char* mqtt_server = "test.mosquitto.org";

WiFiClient espClient;
PubSubClient client(espClient);

char mTopic[]="test-topc-ESP-WROOM-02/sensor";
long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {
  pinMode(LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the LED if an 1 was received as first character
  if ((char)payload[0] == '1') {
    digitalWrite(LED, HIGH);   // Turn the LED on (Note that LOW is the voltage level
    // but actually the LED is on; this is because
    // it is acive low on the ESP-01)
  } else {
    digitalWrite(LED, LOW);  // Turn the LED off by making the voltage HIGH
  }

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      // ... and resubscribe
      client.subscribe("inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  long now = millis();
  if (now - lastMsg > 2000) {
    lastMsg = now;
    ++value;
    snprintf (msg, 75, "ESP-WROOM-02 #%ld", value);
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish(mTopic, msg);
  }
}

MessageのSubscribe

RaspberryPiに以下のコマンドを入力することでMessageをSubscribeします。

$ mosquitto_sub -d -h test.mosquitto.org -t test-topc-ESP-WROOM-02/sensor

Received CONNACK
Received SUBACK
Subscribed (mid: 1): 0
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #1
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #2
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #3
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #4
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #5
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #6
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #7
Received PUBLISH (d0, q0, r0, m0, 'test-topc-ESP-WROOM-02/sensor', ... (15 bytes))
ESP-WROOM-02 #8

参考資料

ESP-WROOM-02で読み取ったセンサ値をスマホに表示する

Blynkというクラウドサービスを使って、ESP-WROOM-02で読み取ったセンサ値をスマホ(iPhone)に表示してみました。このネタは、ITproの記事「1000円以下で自作できるモバイル対応IoTシステム」を参考にしています(というか、記事を自分で再現してみただけですが…)

Blynkの設定

Blynkとは、スマホ(iPhone/Android)からArduino/Raspberry Piをインターネット経由で制御できるクラウドサービスで、ESP-WROOM-02(ESP8266)もサポート対象になっています。

まず、AppStoreやGoogle PlayからBlynk Appをダウンロードしてスマホにインストールします。アプリを起動しアカウントを作ったら、以下の画面から部品(スイッチや値の表示ボックス)を選んで画面に配置します。


IMG_0783.jpg

値の表示ボックス(Value Display)には以下のようにVirtual Pinを割り当てます。ボタンはgp12(IO12)に接続しました。


IMG_0784.jpg


ESP-WROOM-02のコード

ESP-WROOM-02でI2Cセンサーを使用する」に示したコードを使ってBME280のセンサー値を読み取ってBlynk Appに表示してみます。コード全体を以下に示します。

/**************************************************************
 * Blynk is a platform with iOS and Android apps to control
 * Arduino, Raspberry Pi and the likes over the Internet.
 * You can easily build graphic interfaces for all your
 * projects by simply dragging and dropping widgets.
 *
 *   Downloads, docs, tutorials: http://www.blynk.cc
 *   Blynk community:            http://community.blynk.cc
 *   Social networks:            http://www.fb.com/blynkapp
 *                               http://twitter.com/blynk_app
 *
 * Blynk library is licensed under MIT license
 * This example code is in public domain.
 *
 **************************************************************
 * This example runs directly on ESP8266 chip.
 *
 * You need to install this for ESP8266 development:
 *   https://github.com/esp8266/Arduino
 * 
 * Please be sure to select hte right ESP8266 module
 * in the Tools -> Board menu!
 *
 * Change WiFi ssid, pass, and Blynk auth token to run :)
 *
 **************************************************************/

#define BLYNK_PRINT Serial    // Comment this out to disable prints and save space
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <BME280_MOD-1022.h>
#include <Wire.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "Your Aouth Token";

unsigned long lastCheck = 0;
double tempMostAccurate, humidityMostAccurate, pressureMostAccurate;
char buff[50];

void setup()
{
  Serial.begin(115200);
  Blynk.begin(auth, "ssid", "PassCode");
  Wire.begin(); 
  pinMode(12, OUTPUT);

  // need to read the NVM compensation parameters
  BME280.readCompensationParams();

  BME280.writeStandbyTime(tsb_0p5ms);         // tsb = 0.5ms
  BME280.writeFilterCoefficient(fc_16);       // IIR Filter coefficient 16
  BME280.writeOversamplingPressure(os16x);    // pressure x16
  BME280.writeOversamplingTemperature(os2x);  // temperature x2
  BME280.writeOversamplingHumidity(os1x);     // humidity x1

  BME280.writeMode(smNormal);
}

void formattedFloat(float x, uint8_t precision, char *buff) {
  dtostrf(x, 7, precision, buff);
}

BLYNK_READ(V0) 
{ 
  tempMostAccurate = BME280.getTemperatureMostAccurate();
  formattedFloat(tempMostAccurate, 2, buff);
  Blynk.virtualWrite(V0, buff);
}

BLYNK_READ(V1) 
{ 
  humidityMostAccurate = BME280.getHumidityMostAccurate();
  formattedFloat(humidityMostAccurate, 2, buff);
  Blynk.virtualWrite(V1, buff);
}

BLYNK_READ(V2) 
{ 
  pressureMostAccurate = BME280.getPressureMostAccurate();
  formattedFloat(pressureMostAccurate, 0, buff);
  Blynk.virtualWrite(V2, buff);
}

void loop()
{
  Blynk.run();

  int diff = millis() - lastCheck;
  if (diff > 1000) {
    while (BME280.isMeasuring()) {

    }
    // read out the data - must do this before calling the getxxxxx routines
    BME280.readMeasurements();
    lastCheck = millis();
  } else if (diff < 0) {
    lastCheck = 0;
  }
}

65行目以降のコードでVirtual Pin V0に温度センサーの値を書き込んでいます。この処理でBlynk AppのV0表示ボックスに読み取った値が表示されます。

Blynk Appにセンサーの値を表示したイメージを以下に示します。温度・湿度は、センサーの実装位置やノイズの問題等で誤差が大きくあてになりません。GPIOピンの制御は、ボタンに制御対象のピンを割り付けるだけで、コードを書かなくてもBlynk Appのボタンを押すことでESP-WROOM-02に接続したLEDのOn/Offを制御できました。


IMG_0782.jpg


参考資料


ESP-WROOM-02でI2Cセンサーを使用する

ESP-WROOM-02のArduino環境で、I2Cセンサーをつないでみました。一点ハマったところがあるので備忘録を兼ねてポストします。
(10月4日追記)デフォルトのI2Cピンがあることご指摘いただき、I2Cピンをデフォルトに変更しました。

接続構成

センサーはスイッチサイエンスさんのBME280搭載 温湿度・気圧センサーモジュールです。ピン接続は以下の通り:

  • Vio — 3.3V
  • Vore — 3.3V
  • GND — GND
  • CSB — 3.3V
  • SDI (SDA) — ESP-WROOM-02 IO4(10KΩでプルアップ)
  • SCK (SCL) — ESP-WROOM-02 IO14 IO5(10KΩでプルアップ)
  • SDO — GND

ESP-WROOM-02 (ESP8266)のデフォルトのI2Cピンアサインは、SDA: IO04, SCL: IO05でした。(@chobicanさんのご指摘)


IMG_0781.jpg


コード

BME280のライブラリは、Embedded Adventuresのコードを使用。Arduino用のサンプルコードがほぼ無修正で動きます。ほぼと書いたのは、一箇所だけ修正が必要で、99行目のWire.beginにSDA, SCLピンをパラメーターとして指定します。今回の例では、Wire.begin(4. 14)になります。ESP-WROOM-O2 (ESP8266)のデフォルトI2Cピンを使えば、Wire.beginのピン指定は不要です。

/*

Copyright (c) 2015, Embedded Adventures
All rights reserved.

Contact us at source [at] embeddedadventures.com
www.embeddedadventures.com

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.

- Neither the name of Embedded Adventures nor the names of its contributors
  may be used to endorse or promote products derived from this software
  without specific prior written permission.
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
THE POSSIBILITY OF SUCH DAMAGE.

*/

// BME280 MOD-1022 weather multi-sensor Arduino demo
// Written originally by Embedded Adventures

#include <BME280_MOD-1022.h>

#include <Wire.h>

// Arduino needs this to pring pretty numbers

void printFormattedFloat(float x, uint8_t precision) {
char buffer[10];

  dtostrf(x, 7, precision, buffer);
  Serial.print(buffer);

}


// print out the measurements

void printCompensatedMeasurements(void) {

float temp, humidity,  pressure, pressureMoreAccurate;
double tempMostAccurate, humidityMostAccurate, pressureMostAccurate;
char buffer[80];

  temp      = BME280.getTemperature();
  humidity  = BME280.getHumidity();
  pressure  = BME280.getPressure();
  
  pressureMoreAccurate = BME280.getPressureMoreAccurate();  // t_fine already calculated from getTemperaure() above
  
  tempMostAccurate     = BME280.getTemperatureMostAccurate();
  humidityMostAccurate = BME280.getHumidityMostAccurate();
  pressureMostAccurate = BME280.getPressureMostAccurate();
  Serial.println("                Good  Better    Best");
  Serial.print("Temperature  ");
  printFormattedFloat(temp, 2);
  Serial.print("         ");
  printFormattedFloat(tempMostAccurate, 2);
  Serial.println();
  
  Serial.print("Humidity     ");
  printFormattedFloat(humidity, 2);
  Serial.print("         ");
  printFormattedFloat(humidityMostAccurate, 2);
  Serial.println();

  Serial.print("Pressure     ");
  printFormattedFloat(pressure, 2);
  Serial.print(" ");
  printFormattedFloat(pressureMoreAccurate, 2);
  Serial.print(" ");
  printFormattedFloat(pressureMostAccurate, 2);
  Serial.println();
}


// setup wire and serial

void setup()
{
  Wire.begin();     // Wire.begin(sda, scl)
  pinMode(12, OUTPUT);
  Serial.begin(115200);
}

// main loop

void loop()
{

  uint8_t chipID;
  
  Serial.println();
  Serial.println();
  Serial.println("Welcome to the BME280 MOD-1022 weather multi-sensor test sketch!");
  Serial.println("Embedded Adventures (www.embeddedadventures.com)");
  chipID = BME280.readChipId();
  
  // find the chip ID out just for fun
  Serial.print("ChipID = 0x");
  Serial.println(chipID, HEX);
  
 
  // need to read the NVM compensation parameters
  BME280.readCompensationParams();
  
  // Need to turn on 1x oversampling, default is os_skipped, which means it doesn't measure anything
  BME280.writeOversamplingPressure(os1x);  // 1x over sampling (ie, just one sample)
  BME280.writeOversamplingTemperature(os1x);
  BME280.writeOversamplingHumidity(os1x);
  
  // example of a forced sample.  After taking the measurement the chip goes back to sleep
  BME280.writeMode(smForced);
  while (BME280.isMeasuring()) {
    Serial.println("Measuring...");
    delay(50);
  }
  Serial.println("Done!");
  
  // read out the data - must do this before calling the getxxxxx routines
  BME280.readMeasurements();
  Serial.print("Temp=");
  Serial.println(BME280.getTemperature());  // must get temp first
  Serial.print("Humidity=");
  Serial.println(BME280.getHumidity());
  Serial.print("Pressure=");
  Serial.println(BME280.getPressure());
  Serial.print("PressureMoreAccurate=");
  Serial.println(BME280.getPressureMoreAccurate());  // use int64 calculcations
  Serial.print("TempMostAccurate=");
  Serial.println(BME280.getTemperatureMostAccurate());  // use double calculations
  Serial.print("HumidityMostAccurate=");
  Serial.println(BME280.getHumidityMostAccurate()); // use double calculations
  Serial.print("PressureMostAccurate=");
  Serial.println(BME280.getPressureMostAccurate()); // use double calculations
  
  // Example for "indoor navigation"
  // We'll switch into normal mode for regular automatic samples
  
  BME280.writeStandbyTime(tsb_0p5ms);        // tsb = 0.5ms
  BME280.writeFilterCoefficient(fc_16);      // IIR Filter coefficient 16
  BME280.writeOversamplingPressure(os16x);    // pressure x16
  BME280.writeOversamplingTemperature(os2x);  // temperature x2
  BME280.writeOversamplingHumidity(os1x);     // humidity x1
  
  BME280.writeMode(smNormal);
   
  while (1) {
    digitalWrite(12, HIGH);
    while (BME280.isMeasuring()) {

    }
    
    // read out the data - must do this before calling the getxxxxx routines
    BME280.readMeasurements();
    printCompensatedMeasurements();
    
    delay(1000);
    Serial.println();

    digitalWrite(12, LOW);
    delay(1000);
  }
}

参考資料

ESP-WROOM-02を直接プログラムしてIFTTTにイベントをポストする

久しぶりの投稿です。

表題の通り、今話題の(もう古い?)WiFiモジュールESP-WROOM-02をmbedやArduinoといった外付けのMCUを使わず、直接プログラムしてIFTTTへのイベントポストを行いました。

環境構築手順

ESP-WROOM-02(ESP8266)の直接プログラムは、Arduino IDEにAdditional Board ManagerとしてESP8266のコンパイラ等を導入することで実現できます。手順は、以下に示す先人のサイトを参考にしました:

ESP-WROOM-02は最近秋月さんが発売した、ESP-WROOM-02 DIP化キットを購入しました。ESP-WROOM-02に、プログラム書き込み用のFT232RLやリセット・プログラム書き込みスイッチを取り付けます。

IMG_0778

IFTTTイベントポストプログラム

コードを以下に示します。WiFiアクセスポイントへ接続し、IFTTTのMaker Channelにイベント名”ESP8266"を送信します。イベントのポスト後はLチカを行うだけです(センサーの接続などはさぼっています)。

 
#include <ESP8266WiFi.h>

#define IFTTT_MAX_SIZE_STRING    512

const char* ssid     = "ssid";
const char* password = "password";
 
const char* host = "maker.ifttt.com";
 
void setup() {
  Serial.begin(115200);
  pinMode(12, OUTPUT);
  delay(100);
 
  // We start by connecting to a WiFi network
 
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Connect to IFTTT
  Serial.print("connecting to ");
  Serial.println(host);
 
  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }

  // This will send POST to IFTTT server
  char str[IFTTT_MAX_SIZE_STRING] = {0};
  char v1[16] = "";
  char v2[16] = "";
  char v3[16] = "";
  char header[100] = "POST /trigger/ESP8266/with/key/YourSecretKey HTTP/1.1\r\n";
  const char * host = "Host: maker.ifttt.com\r\n";
  char contentLen[50] = {0};
  const char * contentType = "Content-Type: application/json\r\n\r\n";
  char valueData [150] = {0};
  sprintf(valueData,"{\"value1\":\"%s\",\"value2\":\"%s\",\"value3\":\"%s\"}\r\n",v1,v2,v3);
  sprintf(contentLen,"Content-Length: %d\r\n",strlen(valueData));
  sprintf(str,"%s%s%s%s%s",header,host,contentLen,contentType,valueData);
  
  client.print(str);
  delay(10);
}
 
void loop() {
  digitalWrite(12, HIGH);
  delay(500);
  digitalWrite(12, LOW);
  delay(500);
}

IFTTTの通知

電源を入れるか、リセットを行うと、IFTTTのMaker Channelにイベントが通知されます。IFTTTのレシピを作っておくことで以下のようにiOSデバイスのNortificationを発行することができます。(レシピの作り方は先人のサイトが参考になるので割愛)。

IMG_0780

2023年6月
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30  
無料ブログはココログ