基于Termux平台和Arduino及NodeRed开发软件的MQTT应用开发-Arduino中文社区 - Powered by Discuz! Archiver

armduino 发表于 2021-4-28 11:55

基于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();
}
}
*/


armduino 发表于 2021-4-28 12:08

安装好Termux之后
$apt update
$apt install coreutils nano nodejs
$apt install termux-api

armduino 发表于 2021-4-28 12:08

armduino 发表于 2021-4-28 12:09

包子一百号 发表于 2021-5-28 10:23

哇天!!!宝藏贴!!!!!谢谢lz
页: [1]
查看完整版本: 基于Termux平台和Arduino及NodeRed开发软件的MQTT应用开发