2018年7月25日 星期三

ESP8266 MQTT IoT應用範例介紹

如何把溫濕度sensor讀取到的資料傳送到雲端呢?如何從雲端來控制RGB LED呢?不需要自己架設雲端,使用免費的server及瞭解MQTT協定就可以實現物聯網IoT架構。 先準備硬體零件,電路圖如下圖示,並把電路接好。 1.NodeMCU x 1 pcs 2.DHT11 x 1 pcs 3.RGB LED x 1 pcs nodemcu_dht11.png 安裝ArduinoIDE 與註冊Server帳號 Arduino IDE https://www.arduino.cc/en/Main/Software 免費註冊Server帳號 https://www.adafruit.com/ NodeMCU的範例程式如下,用Arduino IDE去編譯,程式有幾各地方要去修改。 1.WLAN_SSID 請輸入你的wifi SSID 2.WLAN_PASS 請輸入你的wifi 密碼 3.AIO_USERNAME 請輸入註冊Server的帳號 https://www.adafruit.com/ 4.AIO_KEY 請先登入 https://io.adafruit.com ,點選View AIO Key,複製Active Key,如下圖示。 adafruit_1 程式內容就請大家自行研究了,不另外說明了。
關於MQTT協定,可以參考以下路徑的文章。
MQTT教學(一):認識MQTT
在編譯程式之前,請先在Arduino IDE自行匯入以下的程式庫(草稿碼->匯入程式庫->管理程式庫),這樣編譯才不會出現錯誤。 1.Adafruit_ESP8266 2.Adafruit_MQTT_Library 3.DHT_sensor_library
/****************************************************/
#include "ESP8266WiFi.h"
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "DHT.h"

/************************* LED *********************************************/
#define G_LED    2
#define B_LED    0
#define R_LED    4

/*********************** DHT11 **********************************************/

#define DHTPIN 5 // what digital pin we're connected to
#define DHTTYPE DHT11 // DHT 11
DHT dht(DHTPIN, DHTTYPE);

/************************* WiFi Access Point *********************************/

#define WLAN_SSID  "...your SSID..."
#define WLAN_PASS  "..your password..."

/************************* Adafruit.io Setup *********************************/

#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883 // use 1883 for SSL
#define AIO_USERNAME "...your AIO username (see https://accounts.adafruit.com)..."
#define AIO_KEY "...your AIO key..."

/************ Global State (you don't need to change this!) ******************/

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;
// or... use WiFiFlientSecure for SSL
//WiFiClientSecure client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

/****************************** Feeds ***************************************/

// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: /feeds/
Adafruit_MQTT_Publish Humidity = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Humidity");
Adafruit_MQTT_Publish Temperature = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Temperature");
// Setup a feed called 'onoff' for subscribing to changes.
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");

/*************************** Sketch Code ************************************/

// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
// for some reason (only affects ESP8266, likely an arduino-builder bug).
void MQTT_connect();

void setup() {
pinMode(G_LED, OUTPUT);
pinMode(B_LED, OUTPUT);
pinMode(R_LED, OUTPUT);
digitalWrite(G_LED, LOW);
digitalWrite(B_LED, LOW);
digitalWrite(R_LED, LOW);
Serial.begin(115200);
dht.begin();
delay(10);

Serial.println(F("Adafruit MQTT demo"));

// Connect to WiFi access point.
Serial.println(); Serial.println();
Serial.print("Connecting to ");
Serial.println(WLAN_SSID);

WiFi.begin(WLAN_SSID, WLAN_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();

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

// Setup MQTT subscription for onoff feed.
mqtt.subscribe(&onoffbutton);
digitalWrite(G_LED, HIGH);
digitalWrite(B_LED, HIGH);
digitalWrite(R_LED, HIGH);

}

//uint32_t x=0;
//uint32_t y=0;
void loop() {
// Ensure the connection to the MQTT server is alive (this will make the first
// connection and automatically reconnect when disconnected). See the MQTT_connect
// function definition further below.
MQTT_connect();

// this is our 'wait for incoming subscription packets' busy subloop
// try to spend your time here

Adafruit_MQTT_Subscribe *subscription;
while ((subscription = mqtt.readSubscription(5000))) {
if (subscription == &onoffbutton) {
Serial.print(F("Got: "));
Serial.println((char *)onoffbutton.lastread);
if( *onoffbutton.lastread == '1')
{
   digitalWrite(G_LED, LOW);
}
else if( *onoffbutton.lastread == '2')
{
   digitalWrite(B_LED, LOW);
}
else if( *onoffbutton.lastread == '3')
{
   digitalWrite(R_LED, LOW);
}
else if( *onoffbutton.lastread == '0')
{
   digitalWrite(G_LED, HIGH);
   digitalWrite(B_LED, HIGH);
   digitalWrite(R_LED, HIGH);
}
}
}

// Now we can publish stuff!
float h = dht.readHumidity();
Serial.print(F("\nHumidity : "));
Serial.print(h);
Serial.print("%");
if (! Humidity.publish(h)) {
Serial.println(F("\nSending Humidty is Failed"));
} else {
Serial.println(F("\nSending Humidty is OK!"));
}

//
float t = dht.readTemperature();
float hic = dht.computeHeatIndex(t, h, false);
Serial.print(F("\nTemperature : "));
Serial.print(hic);
Serial.print("*C");
if (! Temperature.publish(hic)) {
Serial.println(F("\nSending Temperature is Failed"));
} else {
Serial.println(F("\nSending Temperature is OK!"));
}
// ping the server to keep the mqtt connection alive
// NOT required if you are publishing once every KEEPALIVE seconds
/*
if(! mqtt.ping()) {
mqtt.disconnect();
}
*/
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
int8_t ret;

// Stop if already connected.
if (mqtt.connected()) {
return;
}

Serial.print("Connecting to MQTT... ");

uint8_t retries = 3;
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt.connectErrorString(ret));
Serial.println("Retrying MQTT connection in 5 seconds...");
mqtt.disconnect();
delay(5000); // wait 5 seconds
retries--;
if (retries == 0) {
// basically die and wait for WDT to reset me
while (1);
}
}
Serial.println("MQTT Connected!");
}

影片demo


2018年7月13日 星期五

NodeMCU (ESP8266) Timer and Ticker 範例-Arduino

ESP8266 Timer0和Timer1有兩個計時器,其WiFi功能使用一個計時器。我們只能使用一個計時器。為避免崩潰問題,我建議使用Ticker而不是Timer。Ticker執行與計時器相同的功能。我們以LED閃爍來做示範,電路圖如下。 nodemcu_LED

ESP8266 Ticker範例

Ticker是用於在一定時間內重複調用函數的庫,attach函數有兩種變體:attachattach_ms,第一個以秒為單位,第二個以毫秒為單位。

LED使用ESP8266 Ticker閃爍,每0.5秒閃一次

該程序演示了LED閃爍的範例。此函數啟動類似於附加中斷的定時器blinker.attach(0.5,changeState); 停止計時器使用blinker.detach(); 要使用Ticker os_timer,我們需要Ticker.h Timer Library
#include  <ESP8266WiFi.h>
#include  <Ticker.h>//Ticker Library

Ticker blinker;

#define R_LED 4 //On board LED

//======================================================
void changeState()
{
    digitalWrite(R_LED, !(digitalRead(R_LED))); 
    //Invert Current State of LED
}
//======================================================
// Setup
//======================================================
void setup()
{
    Serial.begin(115200);
    Serial.println("");
    pinMode(R_LED,OUTPUT);

    //Initialize Ticker every 0.5s
    blinker.attach(0.5, changeState);
}
//======================================================
// MAIN LOOP
//======================================================
void loop()
{
}
//======================================================

ESP8266 Timer範例

硬件Timer0由WiFi功能使用。我們只能使用Timer1。使用Timer而不是Ticker可以實現精確r計時,並且您可以在微秒內獲得定時器中斷。

LED使用ESP8266 Ticker閃爍,每0.5秒閃一次,每10ms執行Timer中斷副程式

設定每10ms執行ATTR onTimerISR(),副程式內counter計算到50次,也就是0.5秒,執行LED閃爍,程式如下。
#include <ESP8266WiFi.h>

uint16_t counter = 0 ;
#define R_LED 4
//======================================================
void ICACHE_RAM_ATTR onTimerISR(){
counter++;
if(counter >= 50 )
{
   counter = 0 ;
   digitalWrite(R_LED,!(digitalRead(R_LED))); //Toggle LED Pin
}
timer1_write(50000);//10ms
}
//======================================================
// Setup
//======================================================
void setup()
{
   Serial.begin(115200);
   Serial.println("");

   pinMode(R_LED,OUTPUT);
   timer1_attachInterrupt(onTimerISR);
   timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE);
   timer1_write(50000); //10ms
}
//=======================================================
// MAIN LOOP
//=======================================================
void loop()
{

}
//========================================================
示範結果