基于Termux平台和Arduino及NodeRed开发软件的MQTT应用开发
本帖最后由 armduino 于 2021-4-28 12:10 编辑标题有点长。
软件:Termux,手机自行下载安装
IDE:Arduino, Node Red
传输协议:MQTT
MQTT代理服务器软件mosquitto,搭建MQTT服务
硬件:安卓手机,ESP8266模块,运行linux系统的电脑,温湿度及光照传感器,继电器模块
手机上需要安装:Termux,Node RED,mosquitto
在电脑上编辑Node RED,因为屏幕大用鼠标也方便。
具体程序
/* 发布者armduino, 2021年4月28日
*基于MQTT协议的消息发布/订阅应用,手机作为MQTT broker.同时作为APP客户端.一切皆为客户.
*手机安装TERMUX,mosquitto以及Node-RED,
*启动Termux,输入ifconfig命令获取手机IP地址,比如192.168.0.102.运行node-red -- safe
*在Linux电脑后台运行Node-RED,Firefox浏览器网页输入http://192.168.0.102:1880,编辑节点流,deploy部署后即可在手机生成Dashboard
*手机左侧向右滑屏添加NEW SESSION,启动新的Termux命令窗口,运行mosquitto -v,即可接收程序信息
*然后打开手机360浏览器输入http://127.0.0.1:1880/ui ,或 http://localhost:1880/ui,出现dashboard显示控制面板
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>//属于MQTT的函数库
#include <WiFiClient.h>
#include <Wire.h> //联接OLED和光照传感器需要用到该库
#include <BH1750.h> //光照传感器库
//#include <Adafruit_GFX.h>
//#include <Adafruit_SSD1306.h>
//#define OLED_RESET4// Reset pin # (or -1 if sharing Arduino reset pin) -1会反复重启
//Adafruit_SSD1306 display(128,32,&Wire,OLED_RESET);
#include "DHT.h"
#define DHTPIN1 D3 //GPIO0=D3 for Wemos d1
#define DHTTYPE1 DHT11
DHT dht1(DHTPIN1,DHTTYPE1);
BH1750 lightMeter;
WiFiClient espClient; //实例化WiFi对象
PubSubClient mqttClient;//实例化MQTT Broker对象. MQTT对象既可以是发布者也可以是订阅者
const char* ssid = "TP-LINK_XXXX";
const char* password = "12345678";
const char* mqtt_server ="192.168.0.102";//手机IP
const char* icolor = "Mini_Greenhouse"; //client ID 客户代号
const char* pub_temp = "Greenhouse/Temp" ; //定义一个发布主题,该主体接收温度消息
const char* pub_humd = "Greenhouse/Humd" ;
const char* pub_hic = "Greenhouse/HIC" ;
const char* pub_lux = "Greenhouse/Lux" ;
const char* pub_lamp_status= "Greenhouse/LampStatus";//定义一个发布主题,把灯的开关状态(on/off)发布给客户端APP
const char* pub_valv_status= "Greenhouse/ValvStatus";
const char* pub_hvac_status= "Greenhouse/HvacStatus";
const char* sub_lamp = "Greenhouse/Lamp" ;//ESP或者别的MCU模块订阅\接收来自客户端发来的指令消息,实现控制功能(on/off)
const char* sub_valv = "Greenhouse/Valve" ; //控制电磁阀
const char* sub_hvac = "Greenhouse/Hvac" ;//空调水泵或其它
long lastMsg = 0; //记录上次发送信息的时长
long now=millis();
int frequency500 = 500; //定义蜂鸣器频率
int pinLamp = D1;//GPIO5-D1
int pinPump = D6;//GPIO12-D6;
int pinAlarm = D7; //GPIO13-D7
int pinValve = D8; //GPIO15-D8 管脚D8连接到控制电磁阀继电器模块的信号脚
String keeper;
String message;
//初始化参数
void setup(){
pinMode(pinPump,OUTPUT); //初始化水泵的控制引脚
pinMode(pinValve, OUTPUT); //阀门
pinMode(pinLamp,OUTPUT); //照明设备
pinMode(pinAlarm, OUTPUT); //灯光报警
Serial.begin(9600);
Wire.begin(2,14); //GPIO2-D4(SDA),GPIO14-D5(SCL)
Serial.printf("\nChip ID of ESP = %08X", ESP.getChipId());
Serial.print("\nESP Board MAC Address: ");
Serial.println(WiFi.macAddress());
Serial.println("This is a < GreenHouse Monitor System >");
/*
* 联接OLED可用
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
else
Serial.println(F("OLED-SSD1306 allocation succeed!"));
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(1000); // Pause for 2 seconds
display.clearDisplay();// Clear the buffer
*/
dht1.begin();
lightMeter.begin();
Serial.println(F("BH1750 light sensor test begin..."));
if (lightMeter.begin()) {
Serial.println(F("BH1750 light sensor initialised 光照传感器初始化完成!"));
}
else {
Serial.println(F("Error initialising BH1750"));
}
setup_wifi();
mqttClient.setClient(espClient); //对象调用类内函数setClient(),设置客户
mqttClient.setServer(mqtt_server, 1883);//设置服务器地址及端口
mqttClient.setCallback(mqtt_callback);//设置回调函数,在收到订阅的消息时调用函数mqtt_callback
}
void setup_wifi(){
delay(10);
Serial.printf("\nConnecting to your SSID --> %s\n",ssid);
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println(""); //回车换行
Serial.println("Great! WiFi connected!");
IPAddress ip=WiFi.localIP();
Serial.print("The IP address of your WiFi module: ");
Serial.println(ip);
Serial.println("WiFi status:");
WiFi.printDiag(Serial);
Serial.println();
}
void reconnect() {
while (!mqttClient.connected()){ //如果与mqtt服务器(例如手机上安装的TERMUX-mosquitto)没有联接上,执行以下操作
Serial.print("Attempting MQTT connection...");
//String client_id = icolor;
//client_id += String(random(0xffff), HEX);// Create a random client ID 程序每启动一次,产生一个随机客户编码.
//if (mqttClient.connect(client_id.c_str())){ //从网络中找到目标客户ID, c_str()函数返回一个客户程序可读不可改的指向字符数组的指针,不需要手动释放或删除这个指针
if (mqttClient.connect(icolor)){
Serial.printf("connected with %s\n",icolor);
// Once connected, publish an announcement...一旦联接成功,发布一个状态声明
// Duplicate these as needed
mqttClient.publish(pub_valv_status, "VALVEs are OK"); //客户端dashboard显示联接上的初始状态信息
mqttClient.publish(pub_lamp_status, "LAMPs are OK");
mqttClient.publish(pub_hvac_status, "HVAC system is OK");
// ... and resubscribe
mqttClient.subscribe(sub_lamp); //Set up Subscription for each value
mqttClient.subscribe(sub_valv); //手机或PC客户端的dashboard控制按钮发出开关信号(on/off) to MQTT broker,and then to control WiFi module or MCU
mqttClient.subscribe(sub_hvac);
}
else{
Serial.print("failed, return code : ");
Serial.println(mqttClient.state());//return value '-2' : MQTT_CONNECT_FAILED - the network connection failed
Serial.println("MQTT_CONNECT_FAILED,try again after 2 seconds");
delay(2000);
}
}
}
//通过回调函数,由客户端(手机APP按钮)向服务器发送操作消息(on/off):
void mqtt_callback(String topic, byte* payload, unsigned int length) {//用String topic替代char* topic,下面才可以用if(topic==sub_xxx)
Serial.print("From the topic <" + topic + "> message arrived: < ");
//Serial.print(topic);//打印消息主题
//Serial.print("> message arrived: < ");
//payload='\0';//空操作字符
//Serial.println((char*)payload);// 打印消息体,CONNECT\SUBSCRIBE\SUBACK\UNSUBSCRIBE四种类型的消息有消息体
for (int i = 0; i < length; i++) { // 把收到的payload信息联结起来Used to concatenate the message
message += (char)payload; //把二进制流转成字符,并把字符联接起来形成字符串(最后得到的是,比如on或者off)
Serial.print((char)payload);
}
Serial.println(" >");
keeper = message; // Store the message(比如on/off)
message = ""; // Clear the buffer
//String abc=topic; 如果定义为char* topic
if (topic==sub_lamp) { // 如果MCU(通过MQTT broker中继服务器)收到客户订阅主题 "Greenhouse/Lamp". topic相当于门
if (keeper == "on") { // 收到服务器(客户端)发来的该主题下的消息“1”或者别的字符串“on”. message相当于钥匙
digitalWrite(pinLamp, HIGH); // 应用端MCU的相对应引脚输出高电位,进而控制继电器执行动作
mqttClient.publish(pub_lamp_status,"打开灯"); //发布一个状态信息,把字符"打开"贴在主题pub_lamp_status后面,并在dashboard显示
} //或者说把字符串"打开"发布到pub_lamp_status主题下
else if (keeper == "off") {
digitalWrite(pinLamp, LOW);
mqttClient.publish(pub_lamp_status,"关闭灯");
}
}
if (topic == sub_valv) {
if (keeper == "on") {
digitalWrite(pinValve, HIGH);
mqttClient.publish(pub_valv_status,"强制开阀");
}
else if (keeper == "off") {
digitalWrite(pinValve, LOW);
mqttClient.publish(pub_valv_status,"强制关闭");
}
}
if (topic == sub_hvac) {
if (keeper == "on") {
digitalWrite(pinPump, HIGH);
mqttClient.publish(pub_hvac_status,"强制打开");
}
else if (keeper == "off") {
digitalWrite(pinPump, LOW);
mqttClient.publish(pub_hvac_status,"强制关闭");
}
}
}
void loop() {
uint16_t lux = lightMeter.readLightLevel();
float h1=dht1.readHumidity();
float t1=dht1.readTemperature();// Computes temperature values in Celsius
float f1=dht1.readTemperature(true);
if (isnan(h1)||isnan(t1)||isnan(f1)){//isnan(x),Returns whether x is a NaN (Not-A-Number) value.
Serial.println("Failed to read from DHT sensor");
return;
}
float hic = dht1.computeHeatIndex(t1, h1, false);
static char temperatureTemp;
static char hicTemp;
static char humidityTemp;
static char luxTemp;
dtostrf(t1, 6, 1, temperatureTemp);//data to string function 把浮点数或整型数数转换成字符串,保留一位小数
dtostrf(h1, 6, 1, humidityTemp);
dtostrf(hic, 6, 1, hicTemp);
dtostrf(lux, 6, 1, luxTemp);
Serial.printf("Temp= %s",temperatureTemp); //Serial.print(t1,1);
Serial.printf(", Humi= %s",humidityTemp); //Serial.print(h1,1);
Serial.printf(", lux= %s\n",luxTemp); //Serial.println(lux);
controla(t1,lux);//调用控制函数
//displaychar(h1,t1,lux); //调用显示函数,需要配合OLED
if (!mqttClient.connected()){
reconnect(); //确保联接上才执行下面操作
}
mqttClient.loop(); //周期性调用loop()函数,使客户处理获取的消息并保持与服务器的联接,循环获取向服务器订阅的特定主题的消息
if (!mqttClient.loop()) { //调用 loop() 保持与Broker网络连接,loop函数主要的用途在于读取\写入接收缓存区的或者发送缓冲区中的数据,并调用对应的回调函数
mqttClient.connect(icolor); //连接Broker,the client ID to use when connecting to the server
}
now = millis(); //获取系统运行时间长度
if (now-lastMsg > 3000){ // 3seconds as a buffer
lastMsg = now;
// Publishes Temperature and Humidity values
mqttClient.publish(pub_temp, temperatureTemp);//向服务器发送温度数据(字符串),dashboard 显示
mqttClient.publish(pub_humd, humidityTemp);
mqttClient.publish(pub_hic, hicTemp);
mqttClient.publish(pub_lux, luxTemp);
}
delay(1000);
}
//控制函数,根据传递的温度t1和光照lux数据进行控制
void controla(float t1,uint16_t lux){
if (t1 >= 35.5) {
digitalWrite(pinPump,HIGH); //降温水泵开始工作
digitalWrite(pinAlarm,HIGH); //触发报警器
mqttClient.publish(pub_hvac_status, "温度高于设定35.5,自动开启");
}
else if (t1 <= 25.5){
digitalWrite(pinPump,LOW);
digitalWrite(pinAlarm,LOW);
mqttClient.publish(pub_hvac_status, "温度低,自动关闭中");
}
/*
tone(pinBuzzer, frequency500);//蜂鸣器发声,1000毫秒
delay(1000);
noTone(pinValve); //停止发声
delay(500);
*/
//Serial.println("pinValve status:");
//Serial.print(digitalRead(pinValve));
}
/*
//屏幕显示
voiddisplaychar(float h1,float t1,uint16_t lux){
Serial.print("Humidity湿度: ");
Serial.print(h1,1);
Serial.print(" %,\t");
Serial.print("Temperature温度: ");
Serial.print(t1,1);
Serial.print(" *C,\t");
Serial.print("Light照度: ");
Serial.print(lux);
Serial.print(" lx");
if(lux<=300){
Serial.print(",光线不足!");
}
Serial.println("");
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(8,0);
display.println("GREENHOUSE MONITOR");
display.drawFastHLine(0,7,128,WHITE);
display.print("HUMDT:");
display.setCursor(40,8);
display.print(h1,1);
display.println(" %");
display.print("TEMPT:");
display.setCursor(40,16);
display.print(t1,1);
display.println(" C");
display.print("LIGHT:");
display.setCursor(40,24);
display.print(lux);
display.println(" LUX");
display.display();
delay(3000);
if (lux<=300){
display.clearDisplay();
display.setCursor(8,0);
display.print(lux);
display.println(" lux");
display.drawFastHLine(0,7,128,WHITE);
display.setTextSize(2);
display.setCursor(16,10);
display.print(F("BAD Light"));
display.display();
display.startscrollright(0x01,0x03);
delay(2000);
display.startscrollleft(0x01,0x03);
delay(2000);
display.stopscroll();
}
else if(t1<=18.5){
display.clearDisplay();
display.setCursor(8,0);
display.print(t1);
display.println("C of today");
display.drawFastHLine(0,7,128,WHITE);
display.setTextSize(2);
display.setCursor(16,10);
display.print(F("VERY COLD"));
display.display();
display.startscrollright(0x01,0x02);
delay(2000);
display.startscrollleft(0x01,0x02);
delay(2000);
display.stopscroll();
}
}
*/
安装好Termux之后
$apt update
$apt install coreutils nano nodejs
$apt install termux-api 哇天!!!宝藏贴!!!!!谢谢lz
页:
[1]