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

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 5289|回复: 4

基于Termux平台和Arduino及NodeRed开发软件的MQTT应用开发

[复制链接]
发表于 2021-4-28 11:55 | 显示全部楼层 |阅读模式
本帖最后由 armduino 于 2021-4-28 12:10 编辑

标题有点长。
软件:Termux,手机自行下载安装
IDE:Arduino, Node Red
传输协议:MQTT
MQTT代理服务器软件mosquitto,搭建MQTT服务
硬件:安卓手机,ESP8266模块,运行linux系统的电脑,温湿度及光照传感器,继电器模块
手机上需要安装:Termux,Node RED,mosquitto
在电脑上编辑Node RED,因为屏幕大用鼠标也方便。

具体程序

  1. /* 发布者armduino, 2021年4月28日
  2.   *基于MQTT协议的消息发布/订阅应用,手机作为MQTT broker.同时作为APP客户端.一切皆为客户.
  3.   *手机安装TERMUX,mosquitto以及Node-RED,
  4.   *启动Termux,输入ifconfig命令获取手机IP地址,比如192.168.0.102.运行node-red -- safe
  5.   *在Linux电脑后台运行Node-RED,Firefox浏览器网页输入http://192.168.0.102:1880,编辑节点流,deploy部署后即可在手机生成Dashboard
  6.   *手机左侧向右滑屏添加NEW SESSION,启动新的Termux命令窗口,运行mosquitto -v,即可接收程序信息
  7.   *然后打开手机360浏览器输入http://127.0.0.1:1880/ui ,或 http://localhost:1880/ui,出现dashboard显示控制面板
  8. */
  9. #include <ESP8266WiFi.h>
  10. #include <PubSubClient.h>  //属于MQTT的函数库
  11. #include <WiFiClient.h>
  12. #include <Wire.h>          //联接OLED和光照传感器需要用到该库
  13. #include <BH1750.h>        //光照传感器库
  14. //#include <Adafruit_GFX.h>
  15. //#include <Adafruit_SSD1306.h>
  16. //#define OLED_RESET  4  // Reset pin # (or -1 if sharing Arduino reset pin) -1会反复重启
  17. //Adafruit_SSD1306 display(128,32,&Wire,OLED_RESET);

  18. #include "DHT.h"
  19. #define DHTPIN1 D3 //GPIO0=D3 for Wemos d1
  20. #define DHTTYPE1 DHT11

  21. DHT dht1(DHTPIN1,DHTTYPE1);
  22. BH1750 lightMeter;
  23. WiFiClient espClient;     //实例化WiFi对象
  24. PubSubClient mqttClient;  //实例化MQTT Broker对象. MQTT对象既可以是发布者也可以是订阅者

  25. const char* ssid = "TP-LINK_XXXX";
  26. const char* password = "12345678";
  27. const char* mqtt_server ="192.168.0.102";  //手机IP

  28. const char* icolor = "Mini_Greenhouse";    //client ID 客户代号

  29. const char* pub_temp = "Greenhouse/Temp" ; //定义一个发布主题,该主体接收温度消息
  30. const char* pub_humd = "Greenhouse/Humd" ;
  31. const char* pub_hic = "Greenhouse/HIC" ;
  32. const char* pub_lux = "Greenhouse/Lux" ;

  33. const char* pub_lamp_status= "Greenhouse/LampStatus";//定义一个发布主题,把灯的开关状态(on/off)发布给客户端APP
  34. const char* pub_valv_status= "Greenhouse/ValvStatus";
  35. const char* pub_hvac_status= "Greenhouse/HvacStatus";

  36. const char* sub_lamp = "Greenhouse/Lamp" ;  //ESP或者别的MCU模块订阅\接收来自客户端发来的指令消息,实现控制功能(on/off)
  37. const char* sub_valv = "Greenhouse/Valve" ; //控制电磁阀
  38. const char* sub_hvac = "Greenhouse/Hvac" ;  //空调水泵或其它
  39. long lastMsg = 0;   //记录上次发送信息的时长
  40. long now=millis();

  41. int frequency500 = 500; //定义蜂鸣器频率
  42. int pinLamp = D1;  //GPIO5-D1
  43. int pinPump = D6;  //GPIO12-D6;
  44. int pinAlarm = D7; //GPIO13-D7
  45. int pinValve = D8; //GPIO15-D8 管脚D8连接到控制电磁阀继电器模块的信号脚
  46. String keeper;
  47. String message;

  48. //初始化参数
  49. void setup(){
  50.   pinMode(pinPump,  OUTPUT);   //初始化水泵的控制引脚
  51.   pinMode(pinValve, OUTPUT); //阀门
  52.   pinMode(pinLamp,  OUTPUT);  //照明设备
  53.   pinMode(pinAlarm, OUTPUT); //灯光报警  

  54.   Serial.begin(9600);
  55.   Wire.begin(2,14); //GPIO2-D4(SDA),GPIO14-D5(SCL)
  56.   Serial.printf("\nChip ID of ESP = %08X", ESP.getChipId());
  57.   Serial.print("\nESP Board MAC Address: ");
  58.   Serial.println(WiFi.macAddress());

  59.   Serial.println("This is a < GreenHouse Monitor System >");

  60.   /*
  61.    * 联接OLED可用
  62.   display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  63.     if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
  64.     Serial.println(F("SSD1306 allocation failed"));
  65.     for(;;); // Don't proceed, loop forever
  66.     }
  67.     else
  68.     Serial.println(F("OLED-SSD1306 allocation succeed!"));

  69.   // Show initial display buffer contents on the screen --
  70.   // the library initializes this with an Adafruit splash screen.
  71.   display.display();
  72.   delay(1000); // Pause for 2 seconds
  73.   display.clearDisplay();// Clear the buffer
  74.   */

  75.   dht1.begin();
  76.   lightMeter.begin();

  77.   Serial.println(F("BH1750 light sensor test begin..."));
  78.     if (lightMeter.begin()) {
  79.     Serial.println(F("BH1750 light sensor initialised 光照传感器初始化完成!"));
  80.     }
  81.     else {
  82.     Serial.println(F("Error initialising BH1750"));
  83.     }
  84.   setup_wifi();
  85.   mqttClient.setClient(espClient);        //对象调用类内函数setClient(),设置客户
  86.   mqttClient.setServer(mqtt_server, 1883);//设置服务器地址及端口
  87.   mqttClient.setCallback(mqtt_callback);  //设置回调函数,在收到订阅的消息时调用函数mqtt_callback
  88. }

  89. void setup_wifi(){
  90.    delay(10);
  91.    Serial.printf("\nConnecting to your SSID --> %s\n",ssid);
  92.    WiFi.mode(WIFI_AP_STA);
  93.    WiFi.begin(ssid, password);

  94.    while (WiFi.status() != WL_CONNECTED) {
  95.    delay(500);
  96.    Serial.print(".");
  97.    }
  98.    randomSeed(micros());
  99.    Serial.println(""); //回车换行
  100.    Serial.println("Great! WiFi connected!");
  101.    IPAddress ip=WiFi.localIP();
  102.    Serial.print("The IP address of your WiFi module: ");
  103.    Serial.println(ip);

  104.    Serial.println("WiFi status:");
  105.    WiFi.printDiag(Serial);
  106.    Serial.println();
  107. }

  108. void reconnect() {  
  109.      while (!mqttClient.connected()){ //如果与mqtt服务器(例如手机上安装的TERMUX-mosquitto)没有联接上,执行以下操作
  110.      Serial.print("Attempting MQTT connection...");
  111.      //String client_id = icolor;  
  112.      //client_id += String(random(0xffff), HEX);// Create a random client ID 程序每启动一次,产生一个随机客户编码.
  113.      //if (mqttClient.connect(client_id.c_str())){ //从网络中找到目标客户ID, c_str()函数返回一个客户程序可读不可改的指向字符数组的指针,不需要手动释放或删除这个指针
  114.      if (mqttClient.connect(icolor)){
  115.        Serial.printf("connected with %s\n",icolor);
  116.        // Once connected, publish an announcement...一旦联接成功,发布一个状态声明
  117.        // Duplicate these as needed
  118.        mqttClient.publish(pub_valv_status, "VALVEs are OK"); //客户端dashboard显示联接上的初始状态信息
  119.        mqttClient.publish(pub_lamp_status, "LAMPs are OK");  
  120.        mqttClient.publish(pub_hvac_status, "HVAC system is OK");
  121.        // ... and resubscribe
  122.        mqttClient.subscribe(sub_lamp);    //Set up Subscription for each value
  123.        mqttClient.subscribe(sub_valv);    //手机或PC客户端的dashboard控制按钮发出开关信号(on/off) to MQTT broker,and then to control WiFi module or MCU
  124.        mqttClient.subscribe(sub_hvac);
  125.         }
  126.     else{
  127.        Serial.print("failed, return code : ");
  128.        Serial.println(mqttClient.state());  //return value '-2' : MQTT_CONNECT_FAILED - the network connection failed
  129.        Serial.println("MQTT_CONNECT_FAILED,try again after 2 seconds");
  130.        delay(2000);
  131.         }
  132.     }
  133. }

  134. //通过回调函数,由客户端(手机APP按钮)向服务器发送操作消息(on/off):
  135. void mqtt_callback(String topic, byte* payload, unsigned int length) {  //用String topic替代char* topic,下面才可以用if(topic==sub_xxx)
  136.      Serial.print("From the topic <" + topic + "> message arrived: < ");
  137.      //Serial.print(topic);  //打印消息主题
  138.      //Serial.print("> message arrived: < ");
  139.      //payload[length]='\0';//空操作字符
  140.      //Serial.println((char*)payload);  // 打印消息体,CONNECT\SUBSCRIBE\SUBACK\UNSUBSCRIBE四种类型的消息有消息体

  141.      for (int i = 0; i < length; i++) { // 把收到的payload信息联结起来Used to concatenate the message
  142.      message += (char)payload[i];       //把二进制流转成字符,并把字符联接起来形成字符串(最后得到的是,比如on或者off)
  143.      Serial.print((char)payload[i]);
  144.      }
  145.      Serial.println(" >");

  146.      keeper = message;       // Store the message(比如on/off)
  147.      message = "";           // Clear the buffer

  148.      //String abc=topic; 如果定义为char* topic
  149.      if (topic==sub_lamp) {             // 如果MCU(通过MQTT broker中继服务器)收到客户订阅主题 "Greenhouse/Lamp". topic相当于门
  150.         if (keeper == "on") {           // 收到服务器(客户端)发来的该主题下的消息“1”或者别的字符串“on”. message相当于钥匙
  151.            digitalWrite(pinLamp, HIGH); // 应用端MCU的相对应引脚输出高电位,进而控制继电器执行动作
  152.            mqttClient.publish(pub_lamp_status,"打开灯"); //发布一个状态信息,把字符"打开"贴在主题pub_lamp_status后面,并在dashboard显示
  153.         }                                                //或者说把字符串"打开"发布到pub_lamp_status主题下
  154.      else if (keeper == "off") {        
  155.            digitalWrite(pinLamp, LOW);
  156.            mqttClient.publish(pub_lamp_status,"关闭灯");
  157.         }
  158.      }

  159.      if (topic == sub_valv) {              
  160.         if (keeper == "on") {         
  161.            digitalWrite(pinValve, HIGH);  
  162.            mqttClient.publish(pub_valv_status,"强制开阀");
  163.        }
  164.      else if (keeper == "off") {        
  165.            digitalWrite(pinValve, LOW);
  166.            mqttClient.publish(pub_valv_status,"强制关闭");
  167.        }
  168.      }

  169.      if (topic == sub_hvac) {            
  170.         if (keeper == "on") {         
  171.            digitalWrite(pinPump, HIGH);
  172.            mqttClient.publish(pub_hvac_status,"强制打开");
  173.        }
  174.      else if (keeper == "off") {        
  175.            digitalWrite(pinPump, LOW);
  176.            mqttClient.publish(pub_hvac_status,"强制关闭");
  177.        }
  178.      }
  179. }

  180. void loop() {
  181.      uint16_t lux = lightMeter.readLightLevel();
  182.      float h1=dht1.readHumidity();
  183.      float t1=dht1.readTemperature();// Computes temperature values in Celsius
  184.      float f1=dht1.readTemperature(true);
  185.      if (isnan(h1)||isnan(t1)||isnan(f1)){  //isnan(x),Returns whether x is a NaN (Not-A-Number) value.
  186.      Serial.println("Failed to read from DHT sensor");
  187.      return;
  188.      }

  189.      float hic = dht1.computeHeatIndex(t1, h1, false);

  190.      static char temperatureTemp[7];
  191.      static char hicTemp[7];
  192.      static char humidityTemp[7];
  193.      static char luxTemp[7];

  194.      dtostrf(t1, 6, 1, temperatureTemp);  //data to string function 把浮点数或整型数数转换成字符串,保留一位小数
  195.      dtostrf(h1, 6, 1, humidityTemp);
  196.      dtostrf(hic, 6, 1, hicTemp);
  197.      dtostrf(lux, 6, 1, luxTemp);
  198.      Serial.printf("Temp= %s",temperatureTemp);     //Serial.print(t1,1);
  199.      Serial.printf(", Humi= %s",humidityTemp);     //Serial.print(h1,1);
  200.      Serial.printf(", lux= %s\n",luxTemp);     //Serial.println(lux);

  201.      controla(t1,lux);  //调用控制函数
  202.      //displaychar(h1,t1,lux); //调用显示函数,需要配合OLED

  203.      if (!mqttClient.connected()){
  204.         reconnect();                //确保联接上才执行下面操作
  205.      }               
  206.      mqttClient.loop();             //周期性调用loop()函数,使客户处理获取的消息并保持与服务器的联接,循环获取向服务器订阅的特定主题的消息
  207.      if (!mqttClient.loop()) {      //调用 loop() 保持与Broker网络连接,loop函数主要的用途在于读取\写入接收缓存区的或者发送缓冲区中的数据,并调用对应的回调函数
  208.         mqttClient.connect(icolor); //连接Broker,the client ID to use when connecting to the server
  209.      }
  210.      now = millis();                //获取系统运行时间长度
  211.      if (now-lastMsg > 3000){       // 3seconds as a buffer
  212.         lastMsg = now;   

  213.         // Publishes Temperature and Humidity values
  214.         mqttClient.publish(pub_temp, temperatureTemp);  //向服务器发送温度数据(字符串),dashboard 显示
  215.         mqttClient.publish(pub_humd, humidityTemp);
  216.         mqttClient.publish(pub_hic, hicTemp);
  217.         mqttClient.publish(pub_lux, luxTemp);  
  218.      }
  219.      delay(1000);
  220. }

  221. //控制函数,根据传递的温度t1和光照lux数据进行控制
  222. void controla(float t1,uint16_t lux){

  223.      if (t1 >= 35.5) {
  224.      digitalWrite(pinPump,HIGH);    //降温水泵开始工作
  225.      digitalWrite(pinAlarm,HIGH);   //触发报警器

  226.      mqttClient.publish(pub_hvac_status, "温度高于设定35.5,自动开启");
  227.     }

  228.   else if (t1 <= 25.5){
  229.      digitalWrite(pinPump,LOW);
  230.      digitalWrite(pinAlarm,LOW);

  231.      mqttClient.publish(pub_hvac_status, "温度低,自动关闭中");
  232.     }
  233.   /*
  234.   tone(pinBuzzer, frequency500);  //蜂鸣器发声,1000毫秒
  235.   delay(1000);
  236.   noTone(pinValve);             //停止发声  
  237.   delay(500);
  238.   */  
  239.   //Serial.println("pinValve status:");
  240.   //Serial.print(digitalRead(pinValve));
  241. }

  242. /*
  243. //屏幕显示
  244. void  displaychar(float h1,float t1,uint16_t lux){
  245.   Serial.print("Humidity湿度: ");
  246.   Serial.print(h1,1);
  247.   Serial.print(" %,\t");

  248.   Serial.print("Temperature温度: ");
  249.   Serial.print(t1,1);
  250.   Serial.print(" *C,\t");

  251.   Serial.print("Light照度: ");
  252.   Serial.print(lux);
  253.   Serial.print(" lx");
  254.   if(lux<=300){
  255.     Serial.print(",光线不足!");
  256.   }
  257.   Serial.println("");

  258.   display.clearDisplay();
  259.   display.setTextSize(1);
  260.   display.setTextColor(WHITE);
  261.   display.setCursor(8,0);
  262.   display.println("GREENHOUSE MONITOR");
  263.   display.drawFastHLine(0,7,128,WHITE);

  264.   display.print("HUMDT:");
  265.   display.setCursor(40,8);
  266.   display.print(h1,1);
  267.   display.println(" %");

  268.   display.print("TEMPT:");
  269.   display.setCursor(40,16);
  270.   display.print(t1,1);
  271.   display.println(" C");

  272.   display.print("LIGHT:");
  273.   display.setCursor(40,24);
  274.   display.print(lux);  
  275.   display.println(" LUX");

  276.   display.display();   
  277.   delay(3000);

  278.   if (lux<=300){
  279.   display.clearDisplay();  
  280.   display.setCursor(8,0);
  281.   display.print(lux);
  282.   display.println(" lux");
  283.   display.drawFastHLine(0,7,128,WHITE);
  284.   display.setTextSize(2);
  285.   display.setCursor(16,10);
  286.   display.print(F("BAD Light"));
  287.   display.display();

  288.   display.startscrollright(0x01,0x03);
  289.   delay(2000);
  290.   display.startscrollleft(0x01,0x03);
  291.   delay(2000);
  292.   display.stopscroll();
  293.    }

  294.   else if(t1<=18.5){
  295.   display.clearDisplay();  
  296.   display.setCursor(8,0);
  297.   display.print(t1);
  298.   display.println("C of today");
  299.   display.drawFastHLine(0,7,128,WHITE);
  300.   display.setTextSize(2);
  301.   display.setCursor(16,10);
  302.   display.print(F("VERY COLD"));
  303.   display.display();

  304.   display.startscrollright(0x01,0x02);
  305.   delay(2000);
  306.   display.startscrollleft(0x01,0x02);
  307.   delay(2000);
  308.   display.stopscroll();
  309.   }
  310. }
  311. */
复制代码


 楼主| 发表于 2021-4-28 12:08 | 显示全部楼层
安装好Termux之后
$apt update
$apt install coreutils nano nodejs
$apt install termux-api
 楼主| 发表于 2021-4-28 12:08 | 显示全部楼层
Screenshot_20210217_212055_com.android.browser.jpg
 楼主| 发表于 2021-4-28 12:09 | 显示全部楼层
Screenshot_20210217_171427_com.android.browser.jpg
发表于 2021-5-28 10:23 | 显示全部楼层
哇天!!!宝藏贴!!!!!谢谢lz
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-12-1 05:01 , Processed in 0.120582 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表