- #include <ESP8266WiFi.h> //esp8266wifi模块库
- #include <ESP8266WiFiMulti.h> //自动链接最强wifi库
- #include <DNSServer.h> //WiFiManager库要用的
- #include <PubSubClient.h> //MQTT协议中的库
- #include <Ticker.h> //时间记录库
- #include <ArduinoJson.h> //调用Json数据库
- #include <Wire.h>
- #include <stdlib.h> //接收数据的转化
- #include <stdio.h>
- #include "TDR.h" //土壤盐分传感器
- ////////////////////////////////////////////////////////
- #define PRODUCT_ID "450667" //Onenote产品名(需修改)
- #define API_KEY "OqwCIDaMoJUzNb1iG5TcZmLF1pU="//"CDglbagwPMllUURDk9mO=qKTozU="//产品密钥(需修改)
- #define DEVICE_ID "789756707" //设备名(需修改),与开发板没有关系,由平台生成的
- const char* mqttServer = "183.230.40.39"; //onenet地址(不修改)
- const uint16_t mqttPort = 6002; //mqtt接口端口(不修改)
- #define TOPIC "ceshitopic1" //订阅主题
- ////////////////////////////////////////////////////////////
- ESP8266WiFiMulti wifiMulti; //自动链接wifi
- WiFiClient wifiClient; //
- PubSubClient mqttClient(wifiClient); //
- Ticker ticker; //用于记录时间
- //////////////////////////////////////////////
- int count=0,aa =0,bt=0,ii=0,nn=5,n=20,m=1; //一分钟发nn=20 //定义参数变量
- char msgJson[1000];//msgJson数据长度
- char msgJson1[1000];//msgJson数据长度
- char msg_buf[1000];//
- char msg_buf1[1000];//
- char dataTemplate0[] = "{"rt_Soil_Moisture":%.2f, "rt_Soil_Temp":%.2f, "rt_Dielectric_Constant":%.2f, "rt_Soil_Volume_EC":%d, "rt_Soil_Pore_Water_EC":%d, "m":%d}"; //rt代表real time 实时数据
- char dataTemplate1[] = "{"Soil_Moisture":%.2f, "Soil_Temp":%.2f, "Dielectric_Constant":%.2f, "Soil_Volume_EC":%d, "Soil_Pore_Water_EC":%.2f}"; //间隔指定时间的数据
- float Soil_Moisture; //土壤水分
- float Soil_Temp; //土壤温度
- float Dielectric_Constant;// 介电常数
- int Soil_Volume_EC; //土壤体积
- int Soil_Pore_Water_EC;//土壤孔隙水
- ////////////////////////////////////////////
- void setup() { //里面的函数只执行一次
- Serial.begin(9600);
- TDRInit();
- ///////////////////////////////////////////////
- wifiMulti.addAP("CMCC-PbWq","9bf07201");//添加多个wifi网络
- wifiMulti.addAP("4G-UFI-19DE","seuseucyd");
- wifiMulti.addAP("esp8266","esp82661");
- wifiMulti.addAP("4G-UFI-5FD6","onenetesp8266");
- wifiMulti.addAP("MERCURY_6A1A","datalogging");
- wifiMulti.addAP("4G-CPE-6CAA","12345678");
- wifiMulti.addAP("YWGCJD-WIFI","12345678");
- /////////////////////////////////////////////////
- WiFi.mode(WIFI_STA); //设置wifi模式
- connectWifi(); //链接Wifi函数
- mqttClient.setServer(mqttServer, mqttPort); //mqttServer是onenet地址,mqtt接口端口mqttPort
- mqttClient.setCallback(receiveCallback); //设置MQTT订阅回调,函数
- connectMQTTServer(); //链接MQTT服务器函数
- ticker.attach(1, tickerCount);
- }
- void loop() { //里面的函数无限循环执行
- if (mqttClient.connected()) { //如果开发板成功连接服务器
- pubMQTTmsg(); //调用pubMQTTmsg()函数发送数据到云端
- mqttClient.loop(); //调用回调函数,接收平台下发的数据
- } else { //如果开发板未能成功连接服务器
- connectMQTTServer(); //则尝试连接服务器
- }
- }
- int tickerCount() {count++;}
- void connectMQTTServer() { //通过MQTT协议链接Onenet
- String clientId = DEVICE_ID;
- String productId = PRODUCT_ID;
- String apiKey = API_KEY;
- if (mqttClient.connect(clientId.c_str(), productId.c_str(), apiKey.c_str())) {//连接MQTT服务器
- subscribeTopic(); //订阅指定主题
- } else {
- delay(3000);
- }
- }
-
- void subscribeTopic() {//订阅指定主题,主题名称以Taichi-Maker-Sub为前缀,后面添加设备的MAC地址
- String topicString = "temperature" + WiFi.macAddress();
- char subTopic[topicString.length() + 1];
- strcpy(subTopic, topicString.c_str()); //
- if (mqttClient.subscribe(subTopic)){ //通过串口监视器输出是否成功订阅主题以及订阅的主题名称
- } else {
- }
- }
- ///////////////////////////////////
- void receiveCallback(char* topic, byte* payload, unsigned int length) {//反调函数用于接收平台下发的消息
- for (int i = 0; i < length; i++){ //按字符一个一个输出
- }
- payload[length] = '\0'; // 添加一个空值使其转化为字符数组
- String payload1=(char *)payload;
- m = atoi((char *)payload);//将字符数组转化为整型数组
- n=nn*m;
- //analogWrite(LED2, m);
- if (payload1 == "21"){//测试下发数据,如果收到的信息以“21”为开始
- //digitalWrite(LED1, HIGH); // 点亮LED。
- } else if (payload1 == "20"){
- //digitalWrite(LED1, LOW); //熄灭LED。
- }
- }
- ////////////////////////////////////////
-
- void pubMQTTmsg() { //onenet数据点上传系统主题
- String topicString = "$dp";
- char publishTopic[topicString.length() + 1];
- strcpy(publishTopic, topicString.c_str()); //c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同,json数据转换为数组
- /////////////////////////////////////////////////////////////
- for(ii=1;ii<=n;ii++){
- connectMQTTServer();mqttClient.loop(); //则尝试连接服务器
- //////////////////////////////////////////////////////////////
- ////---------------1
- readDataOntimer(); TDRListen();
- Soil_Moisture=soil_moisture; //土壤水分
- Soil_Temp=soil_temp; //土壤温度
- Dielectric_Constant=dielectric_constant;// 介电常数
- Soil_Volume_EC=soil_volume_EC; //土壤体积
- Soil_Pore_Water_EC=soil_pore_water_EC;//土壤孔隙水
- connectMQTTServer();mqttClient.loop();
- //////////////////////////////////////////////////////////////
- snprintf(msgJson, 800, dataTemplate0,Soil_Moisture,Soil_Temp,Dielectric_Constant,Soil_Volume_EC,Soil_Pore_Water_EC,m);//将模拟温湿度数据套入dataTemplate模板中, 生成的字符串传给msgJson
-
- int json_len = strlen(msgJson); //msgJson的长度
- msg_buf[0] = char(0x03); //要发送的数据必须按照ONENET的要求发送, 根据要求,数据第一位是3
- msg_buf[1] = char(json_len >> 8); //数据第二位是要发送的数据长度的高八位
- msg_buf[2] = char(json_len & 0xff); //数据第三位是要发送数据的长度的低八位
- memcpy(msg_buf + 3, msgJson, strlen(msgJson)); //从msg_buf的第四位开始,放入要传的数据msgJson
- msg_buf[3 + strlen(msgJson)] = 0; //添加一个0作为最后一位, 这样要发送的msg_buf准备好了
- mqttClient.publish("$dp", (uint8_t *)msg_buf, 3 + strlen(msgJson)); //发送数据到主题$dp
- }
- //////////////////////////////////////////////////
- snprintf(msgJson1, 800, dataTemplate1,Soil_Moisture,Soil_Temp,Dielectric_Constant,Soil_Volume_EC,Soil_Pore_Water_EC);
- int json_len1 = strlen(msgJson1); //msgJson的长度
- msg_buf1[0] = char(0x03); //要发送的数据必须按照ONENET的要求发送, 根据要求,数据第一位是3
- msg_buf1[1] = char(json_len1 >> 8); //数据第二位是要发送的数据长度的高八位
- msg_buf1[2] = char(json_len1 & 0xff); //数据第三位是要发送数据的长度的低八位
- memcpy(msg_buf1 + 3, msgJson1, strlen(msgJson1)); //从msg_buf的第四位开始,放入要传的数据msgJson
- msg_buf1[3 + strlen(msgJson1)] = 0; //添加一个0作为最后一位, 这样要发送的msg_buf准备好了
- mqttClient.publish("$dp", (uint8_t *)msg_buf1, 3 + strlen(msgJson1)); //发送数据到主题$dp
-
- }
- void connectWifi() { //wifi连接函数
- int ii=0;
- while (wifiMulti.run() != WL_CONNECTED) { //等待WiFi连接,成功连接后输出成功信息
- delay(1000);
- }
- Serial.println('\n'); // WiFi连接成功后
- Serial.print("Connected to "); // NodeMCU将通过串口监视器输出。
- Serial.println(WiFi.SSID()); // 连接的WiFI名称
- Serial.print("IP address:\t"); // 以及IP
- Serial.println(WiFi.localIP()); // NodeMCU的IP地址
- }
复制代码- #define TDRSerial Serial//完整的串口
- #define READCMD "0M0!"//其中0是485芯片地址
- #define READDATA "0D0!"//闻讯帧
- #define DebugSerial Serial1//用于调试的,对应esp8266的IO2
- #define RS485DIR 4 //RS485的方向引脚
- String TDRdata = "";//用于储存返回的数据
- bool TDRflag = false;
- int address;
- float soil_moisture; //土壤水分
- float soil_temp; //土壤温度
- float dielectric_constant;// 介电常数
- int soil_volume_EC; //土壤体积电导率
- int soil_pore_water_EC;//土壤孔隙水电导率
- void splitString(String s)
- {
- unsigned char indexA;
- unsigned char indexB;
- String sbuf = "";
- indexA = s.indexOf('+', 0);//从第1个位置开始数到第一次出现加号的索引,(索引)这个方法就是说从指定位置往后找返回字符在该字符串中第一次出现处的索引,比如“woaizhongguo”indexOf('o',2)那返回值就是6而不是1,也不是11;
- sbuf = s.substring(0, indexA);//(取值)str=str.substring(int beginIndex,int endIndex);截取str中从beginIndex开始至endIndex结束时的字符串,并将其赋值给str;
- address = sbuf.toInt();//字符转化为整型
- indexB = s.indexOf('+', indexA + 1);//从字符串的缩影位置的第indexA + 1开始后的第一次出现加号的索引值,此处的索引是基于在整个字符串中的索引
- sbuf = s.substring(indexA + 1, indexB);
- soil_moisture = sbuf.toFloat();//字符转化为浮点型
- indexA = s.indexOf("+", indexB + 1);
- sbuf = s.substring(indexB + 1, indexA);
- soil_temp = sbuf.toFloat();
- indexB = s.indexOf("+", indexA + 1);
- sbuf = s.substring(indexA + 1, indexB);
- dielectric_constant = sbuf.toFloat();
- indexA = s.indexOf("+", indexB + 1);
- sbuf = s.substring(indexB + 1, indexA);
- soil_volume_EC = sbuf.toInt();
- indexB = s.indexOf(0x0d, indexA + 1);
- sbuf = s.substring(indexA + 1, indexB);
- soil_pore_water_EC = sbuf.toInt();
- }
- void TDRInit()
- {
- TDRSerial.begin(9600);
- DebugSerial.begin(9600);
- DebugSerial.println("debug");
- pinMode(RS485DIR, OUTPUT);
- digitalWrite(RS485DIR, LOW);
- }
- void readDataOntimer() //定时读取,放在loop中
- {
- static unsigned char read_status;
- static unsigned long read_timer;
- switch (read_status)
- { case 0://问询状态为0时,发送问询传感器数据的信息,此处为00025
- digitalWrite(RS485DIR, HIGH);
- delay(2);
- TDRSerial.print(READCMD);
- delay(5);
- digitalWrite(RS485DIR, LOW);
- read_timer = millis();//将当前的时间赋值给read_timer
- read_status++;//问询状态+1
- break;//跳出case1,重新进行选择
- case 1://通常case0进行完以后进行case1
- if (millis() - read_timer >= 1000)//若当前的时间比前一次取的大于1s,执行下面语句
- { digitalWrite(RS485DIR, HIGH);
- delay(2);
- TDRSerial.print(READDATA);//发送问询数据的SDI-12码
- delay(5);
- digitalWrite(RS485DIR, LOW);
- read_status++;//问询状态再次+1变为2
- read_timer = millis();
- }
- break;
- case 2://等一段时间将问询状态
- if (millis() - read_timer >= 1000)
- {
- read_status = 0;
- }
- break;
- default:
- break;
- }
- }
- void TDRListen() //放在loop中
- {
- while (TDRSerial.available()) {
- char inChar = (char)TDRSerial.read();
- TDRdata += inChar;
- TDRflag = true;
- delay(2);//一定要加这个延时,否则串口最后一个字节可能收不到
- }
- if (TDRflag == true)
- {
- if (TDRdata.length() > 10)
- {
- splitString(TDRdata);
- }
- TDRdata = "";
- TDRflag = false;
- }
- }
- void splitStringDebug(String s)
- {
- unsigned char indexA;
- unsigned char indexB;
- String sbuf = "";
- indexA = s.indexOf('+', 0);
- DebugSerial.println(indexA);
- sbuf = s.substring(0, indexA);
- address = sbuf.toInt();
- DebugSerial.print("sbuf:");
- DebugSerial.println(sbuf);
- DebugSerial.println( address);
- indexB = s.indexOf('+', indexA + 1);
- sbuf = s.substring(indexA + 1, indexB);
- soil_moisture = sbuf.toFloat();
- DebugSerial.print("sbufB:");
- DebugSerial.println(sbuf);
- DebugSerial.println(soil_moisture);
- indexA = s.indexOf("+", indexB + 1);
- sbuf = s.substring(indexB + 1, indexA);
- soil_temp = sbuf.toFloat();
- DebugSerial.print(indexA);
- DebugSerial.print("sbufC:");
- DebugSerial.println(sbuf);
- DebugSerial.println(soil_temp);
- indexB = s.indexOf("+", indexA + 1);
- sbuf = s.substring(indexA + 1, indexB);
- dielectric_constant = sbuf.toFloat();
- DebugSerial.print(indexB);
- DebugSerial.print("sbufD:");
- DebugSerial.println(sbuf);
- DebugSerial.println(dielectric_constant);
- indexA = s.indexOf("+", indexB + 1);
- sbuf = s.substring(indexB + 1, indexA);
- soil_volume_EC = sbuf.toInt();
- DebugSerial.print(indexB);
- DebugSerial.print("sbufE:");
- DebugSerial.println(sbuf);
- DebugSerial.println(soil_volume_EC);
- indexB = s.indexOf(0x0d, indexA + 1);
- sbuf = s.substring(indexA + 1, indexB);
- soil_pore_water_EC = sbuf.toInt();
- DebugSerial.print(indexB);
- DebugSerial.print("sbufF:");
- DebugSerial.println(sbuf);
- DebugSerial.println(soil_pore_water_EC);
- delay(1000);
- }
复制代码
|