使用 Arduino 通过 MQTT 协议连接 HomeAssistant -- Arduino端

前言

在这个项目中,我同时用了 Arduino Pro Mini 和 Esp12E 模块。Arduino 与 ESP12E 通过串口通信,ESP12E 与树莓派同一个 WiFi 。

我使用Arduino Pro Mini 模块
我使用的ESP12E模块

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)) {
  //Serial.print("recv Data ");
  //Serial.print(strRecv);

  boolean isOK = client.publish(mqtt_topic, String(strRecv).c_str(), true);
  //Serial.print(" send state ");
  //
  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 就可以看到树莓派上相应的传感器会有相应的值变化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容