8通道电导率采集器-Arduino中文社区 - Powered by Discuz! Archiver

王大富 发表于 2022-1-7 03:17

8通道电导率采集器

本帖最后由 王大富 于 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 = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0},tt;
float rawEC;
float value ;
float ecValue ;
float advalue1;
float ecValue_aver;
float ecValue_sum;

float ecValue_t;
///////////////////////////////////
char msgJson;//msgJson数据长度
char msgJson1;//msgJson数据长度
char msg_buf;//
char msg_buf1;//
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;
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 = '\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;
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=ecValue;
      
      }//行是读取次数,列是通道数      
       delay(10);//间隔10ms采集一个数据
}

sort();//数据排序
   
for (j = 0; j < 8; j++)
      {ecValue_sum=0;N=0;
      for (k = 20; k < 80; k++)
          {ecValue_sum+=ecValue_t;N++;         
          }//去掉前后各20个数据
      ecValue=ecValue_sum/N;//取剩下数据平均值
      }

hmiListen();
showToHMI();

//////////////////////////////////////////////////////////////
snprintf(msgJson, 800, dataTemplate0,ecValue,ecValue,ecValue,ecValue,ecValue,ecValue,ecValue,ecValue,m);//将模拟温湿度数据套入dataTemplate模板中, 生成的字符串传给msgJson   
int json_len = strlen(msgJson);                //msgJson的长度
msg_buf = char(0x03);                     //要发送的数据必须按照ONENET的要求发送, 根据要求,数据第一位是3
msg_buf = char(json_len >> 8);            //数据第二位是要发送的数据长度的高八位
msg_buf = char(json_len & 0xff);            //数据第三位是要发送数据的长度的低八位
memcpy(msg_buf + 3, msgJson, strlen(msgJson)); //从msg_buf的第四位开始,放入要传的数据msgJson
msg_buf = 0;            //添加一个0作为最后一位, 这样要发送的msg_buf准备好了
mqttClient.publish("$dp", (uint8_t *)msg_buf, 3 + strlen(msgJson)); //发送数据到主题$dp
}
//////////////////////////////////////////////////
snprintf(msgJson1, 800, dataTemplate1,ecValue,ecValue,ecValue,ecValue,ecValue,ecValue,ecValue,ecValue);//将模拟温湿度数据套入dataTemplate模板中, 生成的字符串传给msgJson   
int json_len1 = strlen(msgJson1);                //msgJson的长度
msg_buf1 = char(0x03);                     //要发送的数据必须按照ONENET的要求发送, 根据要求,数据第一位是3
msg_buf1 = char(json_len1 >> 8);            //数据第二位是要发送的数据长度的高八位
msg_buf1 = char(json_len1 & 0xff);            //数据第三位是要发送数据的长度的低八位
memcpy(msg_buf1 + 3, msgJson1, strlen(msgJson1)); //从msg_buf的第四位开始,放入要传的数据msgJson
msg_buf1 = 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 = (double)advalue*1000/820/196;//需要数据类型转换
value = rawEC*kvalue;
ecValue=value/(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>ecValue_t)
            {temp1=ecValue_t;
             ecValue_t=ecValue_t;
             ecValue_t=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;
    writeTextbox("t0", texttemp);
    delay(2);//指令间加点延时,不至于两条指令混了
    texttemp = advalue;
    writeTextbox("t1", texttemp);
    delay(2);
    texttemp = advalue;
    writeTextbox("t2", texttemp);
    delay(2);
    texttemp = advalue;
    writeTextbox("t3", texttemp);
    delay(2);
    texttemp = advalue;
    writeTextbox("t4", texttemp);
    delay(2);
    texttemp = advalue;
    writeTextbox("t5", texttemp);
    delay(2);
    texttemp = advalue;
    writeTextbox("t6", texttemp);
    delay(2);
    texttemp = advalue;
    writeTextbox("t7", texttemp);
    delay(2);
}
}

HMI.h

/*
* 这是HMI的头文件,有些功能可能并没有用到
*/
#definehmiSerial Serial2

unsigned char hmi_done_flag = false;
//unsigned charkey_page = 0;
//unsigned charkey_id = 0;   
//unsigned charkey_event = 0; //按键事件,00
unsigned charstring_return_flag=false;
unsigned charpage_switch_flag = false;
unsigned char page_id=0;
unsigned char hmiBuf;
String textbox_text="";

extern void keyEventCallBack(unsigned long keyvalue);
void sendEnd(void) {
hmiSerial.write(0xff);
hmiSerial.write(0xff);
hmiSerial.write(0xff);
}
voidhmiInit(unsigned int bandrate) {

hmi_done_flag = false;
//key_page = 0;
//key_id = 0;
//key_event = 0; //按键事件,00
string_return_flag=false;
page_switch_flag = false;
hmiSerial.begin(bandrate);
}

void writeTextbox(String textbox_name, String textbox_text) {
hmiSerial.print(textbox_name);
hmiSerial.print(".txt=");
hmiSerial.print("\"");
hmiSerial.print(textbox_text);
hmiSerial.print("\"");
sendEnd();
}

void selectPage(unsigned char page_id) {
String page = "";
page = page_id;
hmiSerial.print("page");
hmiSerial.write(0x20);//空格
hmiSerial.print(page);
sendEnd();
}

void writePicturebox(String picturebox_name, unsigned char picture_id) {
String pic = "";
pic = picture_id;
hmiSerial.print(picturebox_name);
hmiSerial.print(".pic=");
hmiSerial.print(pic);
sendEnd();
}

void writeNumbox(String numbox_name, int num) {
   hmiSerial.print(numbox_name);

if(num<0)
{hmiSerial.print(".val=-");
hmiSerial.print(abs(num));
}else
{hmiSerial.print(".val=");
hmiSerial.print(num);}
sendEnd();
}

void setCombox(String Combox_name,unsigned char cbval) //设置下拉框
{
   hmiSerial.print(Combox_name);
hmiSerial.print(".val=");
hmiSerial.print(cbval);
sendEnd();
}


void writefloat(String floatname, unsigned int floatval) //写入虚拟浮点数,小数点在HMI端设定
{
hmiSerial.print(floatname);
hmiSerial.print(".val=");
hmiSerial.print(floatval, DEC);
sendEnd();
}

int key_data_return1=0;//数值返回后合成的int
int key_data_return2=0;//数值返回后合成的int

void hmiListen(void) {
unsigned char hmi_num = 0;
while (hmiSerial.available()) {
    char inChar = (char)hmiSerial.read();
    hmiBuf = inChar;
    hmi_num++;
    hmi_done_flag = true;
    delay(2);//一定要加这个延时,否则串口最后一个字节可能收不到
}

if (hmi_done_flag == true)
{
    if (hmiBuf == 0x65) //按键事件
    {
      unsigned long keyvalue=hmiBuf*0x10000+hmiBuf*0x100+hmiBuf;   
   //   key_page = hmiBuf;
   //   key_id = hmiBuf;
   //   key_event = hmiBuf;
      key_data_return1=hmiBuf*0x100+hmiBuf;
      key_data_return2=hmiBuf*0x100+hmiBuf;
   if(key_data_return2>=32768)
    {
      key_data_return2=-1*(65536-key_data_return2);
      }
      keyEventCallBack(keyvalue);

    }
    if (hmiBuf == 0x66) //页面切换事件,在HMI端前初始化事件中加入 printh 66 02 FF FF FF,02代表页面ID
    {
      page_id = hmiBuf;
    }
    if(hmiBuf==0x70)//get返回
    {
      unsigned char i=1;
      textbox_text="";
      while(hmiBuf!=0xff)
      {
      textbox_text+=char(hmiBuf);
      i=i+1;
      }
   string_return_flag=true;
   }
    hmi_done_flag = false;
}
}

void clearBuf(void) {
hmiSerial.print("code_c");
sendEnd();
}

void resetHmi()//重启
{
   hmiSerial.print("rest");
   sendEnd();
}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 = {SLAVE_ARRDESS, 0x03, 0x00, 0x08, 0x00, 0x08, 0xC5, 0xCE};
unsigned char combuf;
int advalue = {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, DEC);
DebugSerial.print("AD1:");
DebugSerial.println(advalue, DEC);
DebugSerial.print("AD2:");
DebugSerial.println(advalue, DEC);
DebugSerial.print("AD3:");
DebugSerial.println(advalue, DEC);
DebugSerial.print("AD4:");
DebugSerial.println(advalue, DEC);
DebugSerial.print("AD5:");
DebugSerial.println(advalue, DEC);
DebugSerial.print("AD6:");
DebugSerial.println(advalue, DEC);
DebugSerial.print("AD7:");
DebugSerial.println(advalue, 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 = inChar;
    num++;
    TDflag = true;
    delay(2);//一定要加这个延时,否则串口最后一个字节可能收不到
}

if (TDflag == true)
{
    if (combuf == SLAVE_ARRDESS and combuf == 0x03) //并没有验证CRC,所以这方式不是最可靠的
    {
      advalue = byteToInt16(combuf, combuf);
      advalue = byteToInt16(combuf, combuf);
      advalue = byteToInt16(combuf, combuf);
      advalue = byteToInt16(combuf, combuf);
      advalue = byteToInt16(combuf, combuf);
      advalue = byteToInt16(combuf, combuf);
      advalue = byteToInt16(combuf, combuf);
      advalue = byteToInt16(combuf, combuf);
      combuf = 0;
      combuf = 0;
      //DebugOutput();///调试输出,可以去掉此句
    }
    TDflag = false;
}


}

页: [1]
查看完整版本: 8通道电导率采集器