本帖最后由 王大富 于 2022-1-7 08:13 编辑
- //这个为esp32的主机程序,硬件选择DOIT ESP32 DEVKIT V1
- #include <WiFi.h>
- #include <PubSubClient.h> //MQTT协议中的库
- #include <ArduinoJson.h> //调用Json数据库
- #include <Wire.h>
- #include "FastLED.h" //FastLED库用于调用随机函数库
- #include <stdlib.h> //接收数据的转化
- #include <stdio.h>
- #include "TD4027.h"
- #include "SDCARD.h"
- #include "HMI.h"
- ////////////////////////////////////////////////////////
- #define PRODUCT_ID "447665" //Onenote产品名(需修改)
- #define API_KEY "KOPgb=Mqy3OgfZf8SE2eGjpSMoY="//"CDglbagwPMllUURDk9mO=qKTozU="//产品密钥(需修改)
- #define DEVICE_ID "852782109" //设备名(需修改),与开发板没有关系,由平台生成的
- const char* mqttServer = "183.230.40.39"; //onenet地址(不修改)
- const uint16_t mqttPort = 6002; //mqtt接口端口(不修改)
- #define TOPIC "ceshitopic1" //订阅主题
- ////////////////////////////////////////////////////////////
- #define RUNLED 22
- unsigned char nowpage = 0;
- ///////////////////////////////////////////////////////////
- WiFiClient wifiClient; //
- PubSubClient mqttClient(wifiClient); //
- //////////////////////////////////////////////
- int count=0,aa =0,bt=0,i=0,ii=0,j=0,jj=0,ij=0,nn=5,n=20,m=1,k=0,kk=0,N=0,NN=100;//一分钟发nn=20 //定义参数变量
- float kvalue[8] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0},tt;
- float rawEC[8];
- float value[8] ;
- float ecValue[8] ;
- float advalue1[8];
- float ecValue_aver[8];
- float ecValue_sum[8];
- float ecValue_t[100][8];
- ///////////////////////////////////
- char msgJson[1000];//msgJson数据长度
- char msgJson1[1000];//msgJson数据长度
- char msg_buf[1000];//
- char msg_buf1[1000];//
- char dataTemplate0[] = "{"rtCH1":%.3f,"rtCH2":%.3f,"rtCH3":%.3f,"rtCH4":%.3f,"rtCH5":%.3f,"rtCH6":%.3f,"rtCH7":%.3f,"rtCH8":%.3f,"m":%d}"; //rt代表real time 实时数据
- char dataTemplate1[] = "{"CH1":%.3f, "CH2":%.3f, "CH3":%.3f, "CH4":%.3f, "CH5":%.3f, "CH6":%.3f, "CH7":%.3f, "CH8":%.3f}"; //间隔指定时间的数据
- //////////////////////////////////////////////
- const char* ssid = "4G-UFI-5FD6";
- const char* password = "onenetesp8266";
- /////////////////////////////////////////////////
- void setup() {
- Serial.begin(9600);
- TDInit();
- pinMode(RUNLED, OUTPUT);
- hmiInit(9600);
- WiFi.mode(WIFI_STA); //设置wifi模式
- WiFi.begin(ssid, password);
- connectWiFi(); //链接Wifi函数
- mqttClient.setServer(mqttServer, mqttPort); //mqttServer是onenet地址,mqtt接口端口mqttPort
- mqttClient.setCallback(receiveCallback); //设置MQTT订阅回调,函数
- connectMQTTServer(); //链接MQTT服务器函数
- }
- void loop() {
- if (mqttClient.connected()) { //如果开发板成功连接服务器
- pubMQTTmsg(); //调用pubMQTTmsg()函数发送数据到云端
- mqttClient.loop(); //调用回调函数,接收平台下发的数据
- } else { //如果开发板未能成功连接服务器
- connectMQTTServer(); //则尝试连接服务器
- }
-
- }
- 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;
- if (payload1 == "21")//测试下发数据,如果收到的信息以“21”为开始
- {}
- else if (payload1 == "20")
- {}
- }
- ////////////////////////////////////////
- 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(); //则尝试连接服务器
- ledFlash();
- ////////采集电导率数据/////////////////
- readDataOntimer();
- TDRListen();
- //ECdata();
-
- for (k = 0; k < NN; k++)
- {readDataOntimer();//发送问询帧
- TDRListen();//接收问询帧
- ECdata();//根据测试电压计算电导率
-
-
- for (i = 0; i < 8; i++)
- {ecValue_t[k][i]=ecValue[i];
-
- }//行是读取次数,列是通道数
- delay(10);//间隔10ms采集一个数据
- }
-
- sort();//数据排序
-
- for (j = 0; j < 8; j++)
- {ecValue_sum[j]=0;N=0;
- for (k = 20; k < 80; k++)
- {ecValue_sum[j]+=ecValue_t[k][j];N++;
- }//去掉前后各20个数据
- ecValue[j]=ecValue_sum[j]/N;//取剩下数据平均值
- }
-
- hmiListen();
- showToHMI();
-
- //////////////////////////////////////////////////////////////
- snprintf(msgJson, 800, dataTemplate0,ecValue[0],ecValue[1],ecValue[2],ecValue[3],ecValue[4],ecValue[5],ecValue[6],ecValue[7],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,ecValue[0],ecValue[1],ecValue[2],ecValue[3],ecValue[4],ecValue[5],ecValue[6],ecValue[7]);//将模拟温湿度数据套入dataTemplate模板中, 生成的字符串传给msgJson
- 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 (WiFi.status()!= 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地址
- }
- /////////////////////////////////////////////////////////////////////////
- void ECdata()
- { for (kk = 0; kk < 8; kk++)
- {rawEC[kk] = (double)advalue[kk]*1000/820/196;//需要数据类型转换
- value[kk] = rawEC[kk]*kvalue[kk];
- ecValue[kk]=value[kk]/(1.0+0.0185*(25-25.0));
- }
- }
- float sort()
- {
- float temp1;
- for(ij=0;ij<8;ij++)
- {for(ii=0;ii<NN-1;ii++)
- {for(jj=0;jj<NN-1-ii;jj++)
- {if(ecValue_t[jj][ij]>ecValue_t[jj+1][ij])
- {temp1=ecValue_t[jj][ij];
- ecValue_t[jj][ij]=ecValue_t[jj+1][ij];
- ecValue_t[jj+1][ij]=temp1;
- }
- }
- }
- }
- }
- ///////////////////////////////////////////////////////////////
- void ledFlash()
- {
- static unsigned long ledtimer;
- static bool output;
- if (millis() - ledtimer >= 1000)
- {
- ledtimer = millis();
- digitalWrite(RUNLED, output);
- output = !output;
- }
- }
- /* HMI按键回调函数
- 使用方法:如果在HMI端的按键勾选了“发送键值”,则可以在case 后添加 0x页面+按键+事件
- 如果自定义 printh 65 03 12 01 则case 后面是 0x031201
- 所有按键的动作都在这个函数里写
- */
- void keyEventCallBack(unsigned long keyvalue)
- {
- switch (keyvalue)
- {
- case 0x000100: //进入主菜单
- nowpage = 1;
- break;
- case 0x010100: //进入通道电压监视
- nowpage = 2;
- delay(500); //页面滑动,最好阻塞一下
- break;
- }
- keyvalue = 0;
- }
- void showToHMI()
- {
- if ( nowpage == 2)
- {
- String texttemp = "";
- texttemp = advalue[0];
- writeTextbox("t0", texttemp);
- delay(2); //指令间加点延时,不至于两条指令混了
- texttemp = advalue[1];
- writeTextbox("t1", texttemp);
- delay(2);
- texttemp = advalue[2];
- writeTextbox("t2", texttemp);
- delay(2);
- texttemp = advalue[3];
- writeTextbox("t3", texttemp);
- delay(2);
- texttemp = advalue[4];
- writeTextbox("t4", texttemp);
- delay(2);
- texttemp = advalue[5];
- writeTextbox("t5", texttemp);
- delay(2);
- texttemp = advalue[6];
- writeTextbox("t6", texttemp);
- delay(2);
- texttemp = advalue[7];
- writeTextbox("t7", texttemp);
- delay(2);
- }
- }
复制代码
HMI.h
SDCARD.h
- /*
- 依赖外部库:esp32-micro-sdcard-master.zip
- */
- #include <SPI.h>
- #include <mySD.h>
- File root;
- bool SD_init()
- {
- bool initflag = false;
- // Serial.begin(115200);
- // Serial.print("Initializing SD card...");
- /*初始化SD库SPI引脚*/
- if (!SD.begin(5, 23, 19, 18)) {
- initflag = false;
- } else
- {
- initflag = true;
- }
- return initflag;
- Serial.println("initialization done.");
- }
- /*从根目录root“/”*/
- bool SD_openFileToWriteOnce(char *filepath,String text)
- {
- root = SD.open(filepath, FILE_WRITE);
- if(root)
- {
- root.println(text);
- root.flush();
- /*关闭文件 */
- root.close();
- return 1;
- }else
- return 0;
- }
复制代码 TD4027.h
- HardwareSerial mySerial(1);
- #define TDSerial Serial
- #define DebugSerial mySerial
- #define RS485DIR 21 //RS485的方向引脚
- bool TDflag = false;
- #define SLAVE_ARRDESS 0x01
- const unsigned char readcmd[8] = {SLAVE_ARRDESS, 0x03, 0x00, 0x08, 0x00, 0x08, 0xC5, 0xCE};
- unsigned char combuf[32];
- int advalue[8] = {0, 0, 0, 0, 0, 0, 0, 0};
- /*********
- //将两个字节转为16位的有符号int,因为esp32是32位机,单纯转为会出错。
- 输入(高字节,低字节)
- 输出,16位有符号整数
- */
- int byteToInt16(unsigned char highbyte, unsigned char lowbyte)
- {
- int a;
- a = highbyte * 0x100 + lowbyte;
- if (a >= 32768)
- {
- a = -1 * (65536 - a);
- }
- return a;
- }
- void DebugOutput()
- {
- DebugSerial.print("AD0:");
- DebugSerial.println(advalue[0], DEC);
- DebugSerial.print("AD1:");
- DebugSerial.println(advalue[1], DEC);
- DebugSerial.print("AD2:");
- DebugSerial.println(advalue[2], DEC);
- DebugSerial.print("AD3:");
- DebugSerial.println(advalue[3], DEC);
- DebugSerial.print("AD4:");
- DebugSerial.println(advalue[4], DEC);
- DebugSerial.print("AD5:");
- DebugSerial.println(advalue[5], DEC);
- DebugSerial.print("AD6:");
- DebugSerial.println(advalue[6], DEC);
- DebugSerial.print("AD7:");
- DebugSerial.println(advalue[7], DEC);
- }
- void TDInit()
- {
- TDSerial.begin(9600);
- DebugSerial.begin(9600);
- //33脚:RXD
- //25脚:TXD
- mySerial.begin(9600,SERIAL_8N1,33,25);
- 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:
- digitalWrite(RS485DIR, HIGH);
- delay(2);
- TDSerial.write(readcmd, 8);
- delay(8); //延时20ms,等待数据发送完毕,波特率是9600,使用10位异步通信,则每秒可传960个字节,每个字节(字符)1.04毫秒。
- digitalWrite(RS485DIR, LOW);
- read_timer = millis();
- read_status++;
- break;
- case 1:
- if (millis() - read_timer >= 1000)
- {
- read_status = 0;
- }
- break;
- default:
- break;
- }
- }
- void TDRListen() //放在loop中
- {
- unsigned char num = 0;
- while (TDSerial.available()) {
- char inChar = (char)TDSerial.read();
- combuf[num] = inChar;
- num++;
- TDflag = true;
- delay(2);//一定要加这个延时,否则串口最后一个字节可能收不到
- }
- if (TDflag == true)
- {
- if (combuf[0] == SLAVE_ARRDESS and combuf[1] == 0x03) //并没有验证CRC,所以这方式不是最可靠的
- {
- advalue[0] = byteToInt16(combuf[3], combuf[4]);
- advalue[1] = byteToInt16(combuf[5], combuf[6]);
- advalue[2] = byteToInt16(combuf[7], combuf[8]);
- advalue[3] = byteToInt16(combuf[9], combuf[10]);
- advalue[4] = byteToInt16(combuf[11], combuf[12]);
- advalue[5] = byteToInt16(combuf[13], combuf[14]);
- advalue[6] = byteToInt16(combuf[15], combuf[16]);
- advalue[7] = byteToInt16(combuf[17], combuf[18]);
- combuf[0] = 0;
- combuf[1] = 0;
- //DebugOutput(); ///调试输出,可以去掉此句
- }
- TDflag = false;
- }
- }
复制代码
|