使用 Arduino 通過 MQTT 協議連接 HomeAssistant -- Arduino端

前言

在這個項目中,我同時用了 Arduino Pro Mini 和 Esp12E 模塊。Arduino 與 ESP12E 通過串口通信,ESP12E 與樹莓派同一個 WiFi 。

ESP12E 模塊已經自帶的有 數字輸入/輸出 口和模擬輸入口,為什麼還要用 Arduino? 因為我這裡使用的感測器模擬量輸入口需要的太多,ESP12E不夠用。當然如果 Arduino 上數字量埠不夠用的話,還可以用 ESP12 上的埠去代替。

Arduino Pro MiNi

Arduino 在這裡起到的作用是感測器信號的收集,Arduino 將所有感測器信號收集到一起,然後組合成 Json 格式的字元串,通過串口發送給 ESP12 模塊。

這裡默認你已經會使用Arduino讀取各種感測器的值,如果不會請在下方留言。

以下是我的 Arduino 程序。

#include <AM2320.h>
#include <SFE_BMP180.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SoftwareSerial.h>
#include "SR04.h"

#define Fire_AI 0
#define Brightness_AI 1
#define Fire_DI 2
#define Shock_DI 3
#define TempOnly_DI 4
#define Human1_DI 5
#define HCSR04_TRIG_PIN 6
#define HCSR04_ECHO_PIN 7
#define WIFI_RX 8
#define WIFI_TX 9

// 初始連接在單匯流排上的單匯流排設備
OneWire oneWire(TempOnly_DI);
SFE_BMP180 AirPresure;
DallasTemperature DS18B20sensors(&oneWire);
AM2320 Temp_Humi;
SR04 Obstacle = SR04(HCSR04_ECHO_PIN, HCSR04_TRIG_PIN);
SoftwareSerial MySerial(WIFI_RX, WIFI_TX);

float currentTemperature;
int currentBright = 0;
int bodySensor = 0;
int FireStatus = 0;
int FireDegree = 0;
int shockStatus = 0;
long DistanceObstance = 0;
char presureDelayTime;
double presureP, presureT;
long int lastSendTime, Now;
String JsonData = "{"Temp1":"TEMP_VALUE1","Temp2":"TEMP_VALUE2","Humidity":"HUMI_VALUE","Brightness":"BRIGHT_VALUE","Preasure":"PREA_VALUE","Atmosphere":"ATM_VALUE","Fire":"FIRE_VALUE","FireState":"FIRE_STATE","BodyState":"BODY_STATE","ObjectDistance":"DIST_VALUE","ShockState":"SHOCK_STATE"}";
SoftwareSerial mySerial(WIFI_RX, WIFI_TX); // RX, TX
bool lightOn = false;

void setup() {
mySerial.begin(9600);
mySerial.println("Hello, world?");
pinMode(Fire_DI, INPUT);
pinMode(Shock_DI, INPUT);
pinMode(TempOnly_DI, INPUT);
pinMode(Human1_DI, INPUT);
pinMode(HCSR04_TRIG_PIN, OUTPUT);
pinMode(HCSR04_ECHO_PIN, INPUT);

DS18B20sensors.begin();
AirPresure.begin();
Now = millis();
lastSendTime = Now;
}

void loop()
{
Now = millis();
if (Now - lastSendTime > 1000) {
JsonData = "{"Temp1":"TEMP_VALUE1","Temp2":"TEMP_VALUE2","Humidity":"HUMI_VALUE","Brightness":"BRIGHT_VALUE","Preasure":"PREA_VALUE","Atmosphere":"ATM_VALUE","Fire":"FIRE_VALUE","FireState":"FIRE_STATE","BodyState":"BODY_STATE","ObjectDistance":"DIST_VALUE","ShockState":"SHOCK_STATE"}";

//溫度感測器
DS18B20sensors.requestTemperatures();
currentTemperature = DS18B20sensors.getTempCByIndex(0);
JsonData.replace("TEMP_VALUE1", String(currentTemperature));
delay(2);

//溫濕度感測器
switch (Temp_Humi.Read())
{
case 2:
case 1:
JsonData.replace("TEMP_VALUE2", "CRC faild");
JsonData.replace("HUMI_VALUE", "CRC faild");
break;
case 0:
JsonData.replace("TEMP_VALUE2", String(Temp_Humi.t));
JsonData.replace("HUMI_VALUE", String(Temp_Humi.h));
break;
}
delay(2);

//亮度感測器
currentBright = analogRead(Brightness_AI);
JsonData.replace("BRIGHT_VALUE", String(currentBright));
delay(2);

//人體感測器
bodySensor = digitalRead(Human1_DI);
switch (bodySensor)
{
case 1:
JsonData.replace("BODY_STATE", "Yes");
break;
case 0:
JsonData.replace("BODY_STATE", "No");
break;
}
delay(2);

//距離感測器
DistanceObstance = Obstacle.Distance();
DistanceObstance = DistanceObstance * 10.0;
JsonData.replace("DIST_VALUE", String(DistanceObstance));
delay(2);

//震動感測器
shockStatus = digitalRead(Shock_DI);
switch (shockStatus)
{
case 1:
JsonData.replace("SHOCK_STATE", "Vibrating");
break;
case 0:
JsonData.replace("SHOCK_STATE", "Stable");
break;
}
delay(2);

//火焰感測器 狀態
FireStatus = digitalRead(Fire_DI);
switch (FireStatus)
{
case 1:
JsonData.replace("FIRE_STATE", "Safety");
break;
case 0:
JsonData.replace("FIRE_STATE", "Fired");
break;
}
delay(2);

//火焰感測器 紅外值
FireDegree = analogRead(Fire_AI);
JsonData.replace("FIRE_VALUE", String(FireDegree));
delay(2);

//大氣壓
presureDelayTime = AirPresure.startPressure(3);
if (presureDelayTime != 0)
{
delay(presureDelayTime);
presureDelayTime = AirPresure.getPressure(presureP, presureT);
if (presureDelayTime != 0)
{
JsonData.replace("PREA_VALUE", String(presureP));
JsonData.replace("ATM_VALUE", String(presureP / 1000.0));
}
else
{
JsonData.replace("PREA_VALUE", "ERROR");
JsonData.replace("ATM_VALUE", "ERROR");
}
}
else
{
JsonData.replace("PREA_VALUE", "ERROR");
JsonData.replace("ATM_VALUE", "ERROR");
}
delay(2);

mySerial.print(JsonData);
}
//開關燈設備寫在這裡
}

ps: 不要吐槽我就用了兩個模擬量,還有兩個設備走的是 I2C ,我在測試的時候還有其他的感測器,最終沒有加(放在室內的設備加個雨水感測器總覺得怪怪的)。

Arduino 的程序其實很簡單,就是讀取感測器,將感測器的值通過串口發送,這裡將感測器的值替換到 Json 的字元串中用的是replace 函數,這個函數的效率不太高,當然用其他方式也是可以的,只不過這個方式比較簡單。

通過程序可以看到,這裡感測器的值每隔 1 秒給 ESP12E 模塊發送一次。這個同步的頻率對於我來說是可以接受的,當然再快也是可以的,只不過可能給ESP12E 的程序處理帶來一定的麻煩,因為 ESP12E 的程序寫的也很簡單。

ESP12E

在這裡 ESP12E 模塊起到的作用是將從串口收到的 Arduino 數據,通過 WiFi 發送給樹莓派 。因為在這裡 Arduino 上的設備都是感測器,沒有執行機構,所以這裡的程序中只有發送,沒有寫相關的接收函數。(帶執行機構的 Arduino 和樹莓派程序,將會在隨後完成,敬請期待。)

以下是我的 ESP12E 的程序:

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

#define wifi_ssid "MyWiFi_SSID"
#define wifi_password "WIFI_Psssword"

#define mqtt_server "Raspberry_IpAddress" #這裡建議將 樹莓派設置為固定IP地址
#define mqtt_user "pi" #使用你的 MQTT 用戶名
#define mqtt_password "password" //MQTT 密碼
#define mqtt_topic "home-assistant/arduino/arduino1"

String strRecv = "";
long now = 0;
long lastRecv = 0;
bool newDataComing = false;
WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
Serial.begin(9600);
setup_wifi();
client.setServer(mqtt_server, 1883);
}

void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(wifi_ssid);

WiFi.begin(wifi_ssid, wifi_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 reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}

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

if (Serial.available() > 0) {
char str = char(Serial.read());
strRecv = strRecv + str;
lastRecv = millis();
newDataComing = true;
delay(2);
}
else {
now = millis();
if ((now - lastRecv > 100) && (newDataComing == true)) {

boolean isOK = client.publish(mqtt_topic, String(strRecv).c_str(), true);

Serial.println(isOK);

strRecv = "";
newDataComing = false;
}
}
}

程序下載完成後,將 Arduino 的 D8 (程序中定義 #define WIFI_RX 8 )接在ESP12E 的 TX 引腳上,將 Arduino 的 D9 (程序中定義 #define WIFI_TX 9)接在 ESP12E 的 RX 引腳上。注意:兩者之間是通過串口通信,所以兩者的供電一定要共地。

將 Arduino 和 ESP12E 所有的線都接好,連接 WiFi 就可以看到樹莓派上相應的感測器會有相應的值變化。


推薦閱讀:

TAG:Arduino | MQTT | 科技 |