[博创智联]蔬菜大棚实验

  • 项目功能
  • 整体设计
    • 硬件端
      • 硬件连接
      • 烧录系统
    • 软件端
      • 代码整合
      • 开发板UDP通信流程
    • 其他
      • ESP8266模块
      • 安卓端开发
      • 服务器配置
  • 成品

项目功能

利用嵌入式设备组成蔬菜大棚监测系统,实现实时温湿度监测,光照强度监测,在温湿度过高的程度时控制风扇的开关和排风速率,根据光照数据实时调整补光LED的亮度,同时在安卓端实时查看设备情况,查看历史传感器数据,展示温湿度变化图表,同时支持手动接管风扇和LED的开关。

整体设计

硬件端

硬件连接

使用博创的i.MX6和配套的Cortex-A底板、自行购买的高亮度LED模块、自行购买的ESP-01模块以及配套的5V供电串口转接板和USB烧录器、博创的温湿度传感器、博创的光照强度传感器、博创的直流电机桥模块。
接线如下,其中博创的温湿度传感器连接 底板的P8接口,博创的光照强度传感器的J1接口连接到 底板的P1接口,J2接口连接到 底板的P5接口,博创的直流电机桥模块连接 底板的P6接口,高亮度LED模块可以随便在底板P系列接口处找到空余的5V和GND接入,同时将Signal引脚接入 底板J5的20Pin,ESP-01转接板可以随便在底板P系列接口处找到空余的5V和GND接入即可。



烧录系统

进入虚拟机内核,修改设备树文档,启用PWM3引脚(SD1_DAT0),并注释掉引脚复用的冲突部分,用于高亮度LED使用。注意,此处不能启用PWM4引脚(SD1_CMD),因为温湿度传感器默认配置下依赖此引脚作为GPIO。详情请见番外篇——直流电机桥源码分析&LED驱动例程开发

修改后重新编译内核和设备树,并烧录系统,完成后初始化网络,挂载虚拟机NFS共享目录。

软件端

代码整合

由于技术有限,选择使用额外的ESP-01作为中转,而不是直接让开发板操作数据库,使用UDP协议和开发板双向通信,ESP-01需要从数据库获取控制数据,并发送给开发板,同时从开发板接收传感器数据,发送至服务器。

整合时需要特别注意变量名称可能重复,例如int fd经常用作open操作,在整合时需要对不同的传感器分别命名。

整合后需要对每个传感器进行功能测试,互相兼容即可。通过测试后可以开发上层应用,控制每一轮循环内检测传感器数值,同时接收UDP提供的数据库控制数据,两者结合对电机、LED硬件进行相应的控制。

最终开发后代码如下,驱动文件请自行加载,代码仅供参考,请以实际情况为准。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netdb.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<errno.h>#define DCM_IOCTRL_SETPWM    (0x10)
#define DCM_IOCTRL_STATUS   (0x20)
#define DCM_IOCTRL_STOP     (0x30)
#define DCM_IOCTRL_START    (0x40)#define I2C_DEV       "/dev/i2c-0"
#define I2C_SLAVE       0x0703
#define I2C_TENBIT      0x0704#define CHIP_ADDR 0x23#define HBLED_IOCTRL_SETPWM (0x10)
#define HBLED_IOCTRL_OFF    (0x20)
#define HBLED_IOCTRL_ON     (0x30)//local listening port
#define getPort 12300
//remote listening port
#define sendPort 10000
//remote ip
#define sendIP "192.168.43.86"
//cycle count
#define delayTimes 15//limit
#define TempTrigger 23
#define HumiTrigger 60
#define IlluTrigger 400char *DCM_DEV= "/dev/DCMotor";
int cmd_group1[] = { DCM_IOCTRL_SETPWM, DCM_IOCTRL_START, DCM_IOCTRL_STATUS, DCM_IOCTRL_STOP };char* HBLED_DEV = "/dev/HBLED";
int cmd_group2[] = { HBLED_IOCTRL_SETPWM, HBLED_IOCTRL_ON, HBLED_IOCTRL_OFF };//R&W BH1750
static int read_BH1750(int fd, void *buff, int count)
{int bh1750_res;    bh1750_res = read(fd,buff,count);return bh1750_res;
}static int write_BH1750(int fd, unsigned char addr, size_t count)
{int bh1750_res;char sendbuffer[count+1];sendbuffer[0] = addr;bh1750_res = write(fd,sendbuffer,count);return bh1750_res;
}int main(void)
{//UDP_init_paraint sockListen;int manualValue;int mrpmValue;int lswitch;char msg[128] = "";int recvbytes;char recvbuf[128];char tempManul[10];char tempRpm[10];char tempLignt[10];double temperature;double humidity;int rpm;double light;int count = 0;//DCMotor pre-configint dcm_fd;int dcm_arg = 0;//SHT11 pre-configint sht_fd, sht_ret;//0:temp_value;  1:humi_value   int sht_data[2];float temp_value = 0.0;float humi_value = 0.0;//BH1750 pre-configint bh1750_fd,bh1750_res;float flux;unsigned char bh1750_buf[2];//HBLED pre-configint hbled_fd;int hbled_arg = 0;//open DCMotordcm_fd = open(DCM_DEV, O_WRONLY);if(dcm_fd < 0){      printf("open /dev/DCMotor error!\n");return -1;}//open SHT11sht_fd = open("/dev/SHT11",0);if(sht_fd < 0){printf("open /dev/SHT11 error!\n");return -1;} //open BH1750bh1750_fd = open(I2C_DEV, O_RDWR);if(bh1750_fd < 0){printf("open /dev/BH1750 error!\n");return -1;}//config BH1750if(-1 == ioctl(bh1750_fd,I2C_TENBIT,0)){printf("ioctl error on line %d\n",__LINE__);return -1;}if(-1 == ioctl(bh1750_fd,I2C_SLAVE,CHIP_ADDR)){printf("ioctl error on line %d\n",__LINE__);return -1;}bh1750_res = write_BH1750(bh1750_fd, 0x01, 1);if(bh1750_res == -1){printf("write error on line %d\n", __LINE__);}bh1750_res = write_BH1750(bh1750_fd, 0x10, 1);if(bh1750_res == -1){printf("write error on line %d\n", __LINE__);}//open HBLEDhbled_fd = open(HBLED_DEV, O_WRONLY);          if (hbled_fd < 0) {printf("open /dev/HBLED error!\n");return -1;}while(1){if ((count+1) % (delayTimes+1) != 0) {if((sockListen = socket(AF_INET, SOCK_DGRAM, 0)) == -1){printf("socket fail\n");return -1;}int set = 1;setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(int));struct sockaddr_in recvAddr;memset(&recvAddr, 0, sizeof(struct sockaddr_in));recvAddr.sin_family = AF_INET;recvAddr.sin_port = htons(getPort);recvAddr.sin_addr.s_addr = INADDR_ANY;// bind is critial before listeningif(bind(sockListen, (struct sockaddr *)&recvAddr, sizeof(struct sockaddr)) == -1){printf("bind fail\n");return -1;}char* temp;int i = 0;int addrLen = sizeof(struct sockaddr_in);if((recvbytes = recvfrom(sockListen, recvbuf, 128, 0,(struct sockaddr *)&recvAddr, &addrLen)) != -1 && strlen(recvbuf) != 0){recvbuf[recvbytes] = '\0';temp = recvbuf;memset(tempManul, 0, strlen(tempManul));memset(tempRpm, 0, strlen(tempRpm));memset(tempLignt, 0, strlen(tempLignt));while (*temp >= '0' && *temp <= '9'){tempManul[i] = *temp;i++;temp++;}temp++;i = 0;while (*temp >= '0' && *temp <= '9'){tempRpm[i] = *temp;i++;temp++;}temp++;i = 0;while (*temp >= '0' && *temp <= '9'){tempLignt[i] = *temp;i++;temp++;}manualValue = atoi(tempManul);mrpmValue = atoi(tempRpm);lswitch = atoi(tempLignt);// --------此处设置是否手动,转速值,光照值从数据库中获得---------sht_ret=read(sht_fd,sht_data,sizeof(sht_data));if(sht_ret<0){printf("SHT11 read err!\n");continue;}temp_value = (float)sht_data[0]/1000;humi_value = (float)sht_data[1]/1000;               printf("temp:%.2f  humi:%4.2f%\n",temp_value,humi_value); memset(bh1750_buf,0,sizeof(bh1750_buf));read_BH1750(bh1750_fd,bh1750_buf,2);flux = (float)(bh1750_buf[0] << 8 | bh1750_buf[1])/1.2;printf("BH1750: %6.2f lux\n", flux);fflush(stdout);dcm_arg = 0;if(manualValue){if(mrpmValue){dcm_arg = mrpmValue;ioctl(dcm_fd, DCM_IOCTRL_START, dcm_arg);ioctl(dcm_fd, DCM_IOCTRL_SETPWM, dcm_arg);}else{ioctl(dcm_fd, DCM_IOCTRL_STOP, dcm_arg);}}else{if(temp_value >= TempTrigger){dcm_arg = (int)(temp_value - TempTrigger) / 2 + 1;if(dcm_arg > 9){dcm_arg = 9;}if(dcm_arg < 1){dcm_arg = 1;}}if(humi_value >= HumiTrigger){dcm_arg = (int)(humi_value - HumiTrigger) / 5 + 1 + dcm_arg;if(dcm_arg > 9){dcm_arg = 9;}if(dcm_arg < 1){dcm_arg = 1;}}if(dcm_arg){ioctl(dcm_fd, DCM_IOCTRL_START, dcm_arg);ioctl(dcm_fd, DCM_IOCTRL_SETPWM, dcm_arg);}else{ioctl(dcm_fd, DCM_IOCTRL_STOP, dcm_arg);}}if(lswitch){ioctl(hbled_fd, HBLED_IOCTRL_SETPWM, hbled_arg);ioctl(hbled_fd, HBLED_IOCTRL_ON, hbled_arg);if(flux <= IlluTrigger){hbled_arg++;if(hbled_arg > 100){hbled_arg = 100;}}else{hbled_arg--;if(hbled_arg < 0){hbled_arg = 0;}}ioctl(hbled_fd, HBLED_IOCTRL_SETPWM, hbled_arg);}else{ioctl(hbled_fd, HBLED_IOCTRL_OFF, hbled_arg);}// ---------------------------------------------------------------printf("receive a broadCast messgse:%d %d %d\n", manualValue, mrpmValue, lswitch);memset(recvbuf, 0, strlen(recvbuf));count++;}else{printf("recvfrom fail\n");}}//close(sockListen);//------------------------------------------------------------------------else {// get all four data from sensor// ----------------------------------------temperature = (float)((int)(temp_value * 100) / 100);humidity = (float)((int)(humi_value * 100) / 100);rpm = dcm_arg;light = flux;// ----------------------------------------sprintf(msg, "%f %f %d %f", temperature, humidity, rpm, light);int brdcFd;if((brdcFd = socket(PF_INET, SOCK_DGRAM, 0)) == -1){printf("socket fail\n");return -1;}int optval = 1;//这个值一定要设置,否则可能导致sendto()失败setsockopt(brdcFd, SOL_SOCKET, SO_BROADCAST | SO_REUSEADDR, &optval, sizeof(int));struct sockaddr_in theirAddr;memset(&theirAddr, 0, sizeof(struct sockaddr_in));theirAddr.sin_family = AF_INET;theirAddr.sin_addr.s_addr = inet_addr(sendIP);theirAddr.sin_port = htons(sendPort);int sendBytes;if((sendBytes = sendto(brdcFd, msg, strlen(msg), 0,(struct sockaddr *)&theirAddr, sizeof(struct sockaddr))) == -1){printf("sendto fail, errno=%d\n", errno);return -1;}printf("msg=%s, msgLen=%d\n", msg, (int)strlen(msg));count = 0;}}ioctl(dcm_fd, DCM_IOCTRL_STOP, dcm_arg);ioctl(hbled_fd, HBLED_IOCTRL_OFF, hbled_arg);close(dcm_fd);close(sht_fd);close(bh1750_fd);close(hbled_fd);return 0;
}

开发板UDP通信流程

UDP通信在未接收到信息时会阻塞线程,利用此特点,可以设置计数器,每接收15个数据再向ESP8266发送一次。

最终可以将配网、加载驱动、执行程序写在同一个脚本中,然后修改rc.local,使其开机自启。

其他

ESP8266模块

使用Arduino IDE对ESP-01进行开发,实现配网、SQL语句操作数据库、UDP协议和开发板通信。

首先从数据库接收最新的控制数据发送到开发板,发送频率控制在一秒一次,然后判断UDP接收,解析后有内容则按规则拆解变量,再发送至数据库。

若有需要,请自行深入学习如何在Arduino上实现ESP8266开发,此处仅提供部分代码以供参考。使用Arduino IDE进行编译烧录,服务器ip、UDP发送和监听端口、数据库账号密码、wifi账号密码需要根据需要进行修改。

#include <ESP8266WiFi.h>
#include <WiFiUDP.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
#include<stdlib.h>IPAddress server_addr(***,***,***,***);
IPAddress local_IP(192, 168, 43, 86);
IPAddress gateway(192, 168, 43, 1);
IPAddress subnet(255, 255, 255, 0);char user[30] = "MYSQL_ACCOUNT";
char password[30] = "MYSQL_PASSWD";
char ssid[30] = "SSID";
char pass[30] = "PASSWD";
unsigned int rmUDPPort = 12300;
unsigned int lcUDPPort = 10000;
char ReceiveBuffer[255] = "";
//buffer to hold incoming packet
char SendBuffer[255] = "";
// a string to send back
int manualValue;
int mrpmValue;
int lswitchValue;
WiFiClient client;
WiFiUDP Udp;
char SQL[500];void Send(char sendbuff[])
{Udp.beginPacket("192.168.43.3", rmUDPPort);Udp.write(sendbuff);Udp.endPacket();
}void SQL_GEN_I(double temperature, double humidity, int rpm, double light){sprintf(SQL, "INSERT INTO SCDP.Data (temperature, humidity, rpm, light) VALUES ('%f','%f','%d','%f')", temperature, humidity, rpm, light);
}void SQL_GEN_S(){sprintf(SQL, "SELECT manual, mrpm, lswitch FROM SCDP.Control order by id desc limit 1");
}void SQL_SELECT(){MySQL_Connection conn(&client);MySQL_Cursor* cur;Serial.print("Connecting to SQL...  ");if (conn.connect(server_addr, 3306, user, password))Serial.println("OK.");elseSerial.println("FAILED.");cur = new MySQL_Cursor(&conn);if (conn.connected())cur->execute(SQL);row_values *row = NULL;column_names *cols = cur->get_columns();do {row = cur->get_next_row();if (row != NULL) {manualValue = atoi(row->values[0]);mrpmValue = atoi(row->values[1]);lswitchValue = atoi(row->values[2]);} while (row != NULL);cur->close();conn.close();delete cur;
}void SQL_INSERT(){MySQL_Connection conn(&client);MySQL_Cursor* cur;Serial.print("Connecting to SQL...  ");if (conn.connect(server_addr, 3306, user, password))Serial.println("OK.");elseSerial.println("FAILED.");cur = new MySQL_Cursor(&conn);if (conn.connected()){cur->execute(SQL);}cur->close();conn.close();delete cur;
}void setup() {// put your setup code here, to run once:Serial.begin(115200);Serial.printf("\nConnecting to %s", ssid);//设置静态IPWiFi.config(local_IP, gateway, subnet);WiFi.mode(WIFI_STA);WiFi.begin(ssid, pass);while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");}Serial.println("\nConnected to network");Serial.print("My IP address is: ");Serial.println(WiFi.localIP());// 开始UDP端口侦听Udp.begin(lcUDPPort);
}void loop() {// put your main code here, to run repeatedly:delay(1000);ReceiveBuffer[0] = '\0';SendBuffer[0] = '\0';int packetSize = Udp.parsePacket();//解析包不为空if (packetSize){String tempString = Udp.readString(); for(int i = 0; i < tempString.length(); i++){ReceiveBuffer[i] = tempString[i];}int countSpace = 0;int i = 0;char tempTemperature[255] = "";char tempHumidity[255] = "";char tempRPM[255] = "";char tempLight[255] = "";char tempChar[255] = "";for(i = 0; i < strlen(ReceiveBuffer); i++){switch(countSpace){case 0: if(ReceiveBuffer[i] != ' '){if(ReceiveBuffer[i] == '.' || ReceiveBuffer[i] <= '9' || ReceiveBuffer[i] >= '0'){tempChar[0] = ReceiveBuffer[i];strcat(tempTemperature, tempChar);}}else{countSpace++;tempChar[0] = '\0';strcat(tempTemperature, tempChar);}break;case 1:if(ReceiveBuffer[i] != ' '){if(ReceiveBuffer[i] == '.' || ReceiveBuffer[i] <= '9' || ReceiveBuffer[i] >= '0'){tempChar[0] = ReceiveBuffer[i];strcat(tempHumidity, tempChar);}                        }else{countSpace++;tempChar[0] = '\0';strcat(tempHumidity, tempChar);}break;case 2:if(ReceiveBuffer[i] != ' '){if(ReceiveBuffer[i] <= '9' || ReceiveBuffer[i] >= '0'){tempChar[0] = ReceiveBuffer[i];strcat(tempRPM, tempChar);}                         }else{countSpace++;tempChar[0] = '\0';strcat(tempRPM, tempChar);}break;case 3:if(ReceiveBuffer[i] != ' '){if(ReceiveBuffer[i] == '.' || ReceiveBuffer[i] <= '9' || ReceiveBuffer[i] >= '0'){tempChar[0] = ReceiveBuffer[i];strcat(tempLight, tempChar);}                           }else{countSpace++;tempChar[0] = '\0';strcat(tempLight, tempChar);}break;}           }double temperature = atof(tempTemperature);double humidity = atof(tempHumidity);int rpm = atoi(tempRPM);double light = atof(tempLight);SQL_GEN_I(temperature, humidity, rpm, light);SQL_INSERT();}SQL_GEN_S();SQL_SELECT();sprintf(SendBuffer, "%d %d %d", manualValue, mrpmValue, lswitchValue);Send(SendBuffer);
}

安卓端开发

使用Android Studio进行开发,通过定时从数据库获取数据实现实时查看设备情况以及查看历史传感器数据,展示温湿度变化图表。手动接管风扇和LED的开关有变化时,向数据库上传操作数据。

安卓端代码不予提供。


服务器配置

使用华为云CentOS7云耀云服务器,借助宝塔面板完成LNMP环境配置,数据库结构如下。

成品

[博创智联]蔬菜大棚实验相关推荐

  1. [博创智联]创新创客智能硬件平台入门教程目录

    [博创智联]创新创客智能硬件平台入门教程目录 前言 文章链接 前期准备篇 常用传感器实验篇 进阶修改篇 综合实战篇 前言 笔者为江苏科技大学18级物联网工程专业本科生,有幸使用本套设备完成了智能宠物笼 ...

  2. [博创智联]创新创客智能硬件平台——温湿度传感器

    [博创智联]创新创客智能硬件平台--温湿度传感器 前言 准备环节 修正错误代码 测试环节 编译驱动和测试代码 加载驱动并执行代码 疑难杂症解决方案----网络设备未定义 前言 从本篇起,将正式进入实践 ...

  3. [博创智联]创新创客智能硬件平台——认识实验箱

    [博创智联]创新创客智能硬件平台--认识实验箱 硬件部分 软件部分 硬件部分 图片来自博创智联官网 箱子分为三层,第一层主要放置各类 传感器.模块,第二层放置三块 核心板 和 对应的底板.屏幕.以及部 ...

  4. [博创智联]创新创客智能硬件平台——烧录系统

    [博创智联]创新创客智能硬件平台--烧录系统 前言 准备工作 前言 本部分可以参考文件资料中/01 创新创客智能硬件平台光盘资料/创新创客智能硬件平台光盘V1.0/03_系统/0301_Linux/0 ...

  5. [博创智联]创新创客智能硬件平台——超声波测距传感器

    [博创智联]创新创客智能硬件平台--超声波测距传感器 准备工作 测试环节 编译驱动和测试代码 加载驱动并执行代码 准备工作 烧录原版系统,供电.连接串口线,配置网络,挂载NFS共享,拷贝超声波测距传感 ...

  6. [博创智联]创新创客智能硬件平台——配置交叉编译环境

    [博创智联]创新创客智能硬件平台--配置交叉编译环境 安装合适的串口软件 配置虚拟机 虚拟机的启动与配网 虚拟机的环境配置 安装串口芯片驱动 前言 驱动文件位置 安装合适的串口软件 在开发板首次烧录系 ...

  7. 18款、19款、20款博越智联、智雅、智尊、智慧,安装第三方软件教程

    2022年11月25日更新测试可用! 图为老款博越车机安装效果! 适用于2018款.2019款和2020款的博越智联.智雅.智尊.智慧车型,目前已知20款博越PRO部分车型可通过此方法来升级自带的高德 ...

  8. python爬取控制台信息_python爬虫实战之爬取智联职位信息和博客文章信息

    1.python爬取招聘信息 简单爬取智联招聘职位信息 # !/usr/bin/env python # -*-coding:utf-8-*- """ @Author  ...

  9. 华为云联合HarmonyOS发布智联生活行业加速器

    摘要:华为云DevRun智联生活行业加速器,为产业链上下游企业提供技术支持.生态建设.商业变现等多方面的资源扶持,共同打造智联生活新未来. 据IDC的报告,2021年上半年中国智能家居设备市场出货量约 ...

最新文章

  1. change python是什么意思_回溯是什么意思?
  2. git报错:Pull is not possible because you have unmerged files解决方法
  3. 文本编辑器中查找对话框及功能实现
  4. led显字风扇原理?
  5. 到底要不要考研?读完研究生就能找到好工作了吗?
  6. [转载] Python高级变量(列表、元组、字典、字符串、公共方法)
  7. 对韩java_清华大学毕业的韩老师图解Java数据结构和算法教程
  8. unity脚本生命流程
  9. 无向图是欧拉图的充要条件_500页开放书搞定概率图建模,图灵奖得主Judea Pearl推荐...
  10. MySQL中针对大数据量常用技术
  11. flutter怎么手动刷新_Flutter 怎样更新?怎样升级? - Flutter - Angular 教程网
  12. Oracle中的索引类型
  13. opencv3学习笔记(九)--------直方图与匹配
  14. 怎么在图片上编辑文字?超简单的两种编辑方法都教给你。
  15. 快速入门Unity机器学习:三:
  16. 秘宝 联想正式进入元宇宙 互联网巨头的数字藏品用的哪种技术?
  17. Mobile(3)-攻防世界-APK逆向
  18. Cohort Analysis组群分析(1)
  19. android 的悬浮窗口,Android 之 悬浮窗口
  20. Dubbo注解方式与spring的整合原理即@DubboService的机制(2)

热门文章

  1. 华硕B85 PRO Game 添加M.2 NVME 硬盘驱动(刷BIOS)
  2. echarts折线图横轴标签间隔
  3. excel工作表标签颜色设置方法
  4. 用php向新年问候,2021新年贺词寄语简短励志 2021给自己的新年祝福语一句话
  5. 游戏中常见的洗牌算法
  6. python判断x是否为奇数表达式_若希望当x的值为奇数时,表达式的值为“真”,x的值为偶数时,表达式的值为“假”。则以下不能满足要求的表达式是( )。...
  7. 做好产品运营,从运营工具开始
  8. 数据库实验 图书管理系统(使用SQLsever,附C#窗体程序源码)
  9. android 2.6.32 SOUND 移植 OMAP3630
  10. C# 第七章『I/O数据流』◆第4节:数据流—FileStream 类