|
本帖最后由 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_RESET 4 // 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[length]='\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[i]; //把二进制流转成字符,并把字符联接起来形成字符串(最后得到的是,比如on或者off)
- Serial.print((char)payload[i]);
- }
- 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[7];
- static char hicTemp[7];
- static char humidityTemp[7];
- static char luxTemp[7];
- 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));
- }
- /*
- //屏幕显示
- void displaychar(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();
- }
- }
- */
复制代码
|
|