前言
我国是农业大国,而非农业强国。近30年来农业高产量主要依靠农药化肥的大量投入,大部分化肥和水资源没有被有效利用而随地弃置,导致大量养分损失并造成环境污染。我国农业生产仍然以传统生产模式为主,传统耕种只能凭经验施肥灌溉,不仅浪费大量的人力物力,也对环境保护与水土保持构成严重威胁,对农业可持续性发展带来严峻挑战
基于I.MX6ULL的Linux C多线程物联网网关+STM32+Qt上位机+Linux C++多线程服务器(含web服务)的多种无线通信系统的智慧农场项目
技术栈+硬件选型
Linux c++应用编程(JS,WEB,HTML什么的我只知道一点点皮毛,只用了其一两个函数);
Linux socket编程,多线程编程,内核驱动编程,文件I/O
Qt/C++ 客户端开发;
Mysql 数据存储;
C语言下位机开发;
I.MX6ULL 挺贵,不建议买,我脑抽买了:
光敏模块 模拟天黑天亮;
水泵 抽水;
电机 散热
电机驱动模块 *2
土壤湿度检测传感器;
ZigBee DL_22 *2 45r;
HC-06 *2 蓝牙模块;
stm32c8t6 *4 下位机(便宜,够用,市面价格10r);
RC522(RFID模块 SPI协议) 与白卡通信 获取卡号;
DTH11 温湿度采集模块(单总线协议,市面价格 4r);
sg90 舵机模块(PWM协议 市面价9r );
8226 01-s WIFI模块*2 (uart协议 市面价格5r) 连接 C++ 服务器 和做热点;
蜂鸣器 RFID注册提示音
总设计流程
本系统一共有四个单片机,四个单片机上分别挂载了的不同传感器结点和一个通信模块,通过三种无线通信协议向物联网网关发送结点数据,物联网网关收到数据将数据上传至Linux C++云服务器,服务器监听了5个端口,分别是用于监听物联网网关的消息,web服务的80端口用与发送HTML给浏览器,与JavaScript通信更新HTML网页的端口,还有物联网网关终端Qt界面,用与对物联网网关的下结点的整体控制与显示,最后一个端口给一个Qt移动端的端口,用于智慧农场的一系列数据显示,整个物联网系统服务于智慧农场。两两单片机可以说毫无联系,但经过网关和服务器的连接,又显得联系紧密恰巧凸显一个完整的物联网控制系统。
STM32C8T6:
除了使用8266模块的单片机不需要编号因为8266会自动为其局域网下的用户编号,其他都需要编号,同一个无线传感器上编号必须不一样,还有就上传服务器的设备名字不能一样,Mysql将设备名字设为主键了,唯一。单片机需要每隔2s左右上传一次传感器结点数据,上传格式为 “ ID+传感器设备名字+#+value+操作符” 例如 ,002舵机#false0 , 001电机#true#LED3#false0 这样为一个数据包可一直延申,按格式写就好了,服务器按格式拆包。因为存在设备控制,所以需要预留控制接口,比如对收到的数据拆包,如果收到id与自身id相同,再判断传感器设备名字,如果传感器设备名字相同,在根据value去改变设备状态。操作符什么的在服务器介绍那边会讲诉清楚,知道有这个事情就可以了。RFID模块上传数据都很特殊,所以自己写了几个操作符专门服务于RFID设备的注册,与门禁比对。
I.MX6ULL Linux C 多线程物联网网关:
一共有四个通信模块,物联网网关通过三种无线通信模块接收数据MCU传来的数据,然后通过一个无线通信模块(8266 -01s Sation模式)连接Linux云服务器上传数据,所以可以通过三种协议向物联网网关发送数据,无线通信为块为ESP 8266-01s,HC-06,ZigBee,其中只有HC-06是一对一通信,其他都是可以一对多,三种通信模块都是串口(UART)驱动,其中一个ESP 8266-01s配置成AP 模式,AP 模式是指 ESP8266 模块自身作为一个热点,然后监听一个8888端口,单片机即可直接与其连接,从而实现物联网网关获取整个局域网的结点信息,另外一个ESP 8266-01s 配置成Sation 模式,Sation 模式是 ESP8266 模块通过路由器连接Linux云服务器,对设备的远程控制功就能通过互联网实现,用于将其他三个模块收到的消息上传Linux云服务器。ZigBee是硬件上按钮配置,配置成相同信道,广播模式就可以接收同信道的数据了。三种都是串口协议,所以我们需要编写I.MX6ULL的Linux驱动程序,这里I.MX6ULL的恩智浦官方已经写好了,但是我不会搞设备树那些,好不容易修改的设备树去编译驱动然后Uboot启动I.MX6ULL时,内核启动报错,我也不知道什么原因,用的是原子的的设备树和内核文件,当时搞不出来,我就中直接用配置寄存器的方式自己写4个串口驱动就行了,串口驱动还是简单的,照着裸机历程去配置寄存器就好了,有一些细微的区别就是定义寄存器地址需要映射出寄存器虚拟地址指针,读取寄存器修改寄存器的方式也不同。
驱动程序编写,最重要的就是文件操作结构体,用户与内核空间信息交互的桥梁,学过STM32都知道串口接收函数的编写吧,接收寄存读到的数据一直追加在一个BUF里面,直到当收到\r\n时,代表接收完毕,立一个标志位,用户空间一直通过read函数读取驱动文件,如果驱动文件中的标志位为一,则代表收到了一个完整的数据包,将数据包发给用户空间,然后清除标志位。这样用户空间获取到了该通信模块接收到的数据,这里不同的是8266的消息接收函数不一样! ! ! 当时因为接收函数都写一样的这个8266的一直收不到,这个8266收到的消息有前有多个\r\n所以需要对其过滤。
一共3模块,所以需要开启三个线程,三个线程打开各自的通信模块的UART驱动模块文件然后按照AT指令集用write函数将用户空间的数据发送到内核空间然后去发送AT指令配置模块,配置完成后就轮询获取接收标志位,如果收到数据,就通过一个 ESP 8266-01s 发送给Linux云服务器,所以思路就有了,我们创建三个线程,6个全局变量,其中三个为各自的接收数据BUF,另外三个为各自的标志位,子线程循环读取内核模块传给用户空间的消息,当read函数>0,说明内核模块中的标志位被置一,说明接收到了一个完整的数据包,然后将用户空间的对应标志位置一,主线程循环判断三个标志位,当其中一个标志位为一,就将对应的buf写入 上传Linux云服务器的那个8266的UART内核驱动文件,内核驱动文件就将其写入寄存器上传Linux云服务器,最后标志位赋值为0,一直轮询下去,然后需要将打开的文件的文件描述符置为全局变量方便主线程收到服务器的消息需要对特定的文件进行操作(写入寄存器给指定的无线通信模块发送数据)。
一共有三个对外接收消息的模块嘛,在网关我们可以知道该数据是哪个无线传感器来的数据,但是上传服务器器后,服务器不知道,所以我们将经过 8266-01s结点的消息都加1000 ,ZigBee加2000,HC-06加3000 ,这样其中单片机id为001经过ZigBee发过来就成了2001,同理蓝牙,但是8266不一样,他热点模式自动分配IP号,可以直接实现该局域网下点对点通信,所以8266的单片机不需要发送带id的数据包,这样就根据单片机的id值大小范围我们就知道了他所在结点区域,这样就可以实现服务器对其的控制,同样收到服务器传下来的控制数据包时,我们需要根据范围去判断该值的是哪个结点的数据包,然后减去该增加的数,最后传到指定单片机上。
主循环
while(1){if(WiFi8266Server_Flag==1){WiFi8266Server_handle();WiFi8266Server_Flag=0;}if(UARTServer_Flag==1){TCP_Server_handle();UARTServer_Flag=0;}if(ZigBee_Flag==1){ ZigBee_handle();ZigBee_Flag=0;}if(HC06_Flag==1){HC06_handle();HC06_Flag=0; }if(UART_Flag==1){ UART_handle(); UART_Flag=0; }}
串口3文件驱动编写,按照可以去写其他几个
#define NEWCHRIOBEE_CNT1 /* 设备号个数 */#define NEWCHRIOBEE_NAME"newchriobee"/* 名字 *//* 寄存器物理地址 */#define CCM_CGPR1_BASE (0x020C406C) //uart3 ,uart4#define CCM_CGPR0_BASE (0x020C4068) //uart2//3#define IOMUXC_UART3_TX_DATA_UART3_TX_BASE (0x020E00A4) #define IOMUXC_UART3_RX_DATA_UART3_RX_BASE (0x020E00A8)#define PAD_CTL_PAD_UART3_TX_DATA_BASE (0x020E0330)#define PAD_CTL_PAD_UART3_RX_DATA_BASE (0x020E0334)#define UART3_UFCR_BASE (0x021EC090)#define UART3_UBIR_BASE (0x021EC0A4) #define UART3_UBMR_BASE (0x021EC0A8)#define UART3_UCR1_BASE (0x021EC080)#define UART3_UCR2_BASE (0x021EC084)#define UART3_UCR3_BASE (0x021EC088)#define UART3_USR2_BASE (0x021EC098)#define UART3_URXD_BASE (0x021EC000)#define UART3_UTXD_BASE (0x021EC040)/* 映射后的寄存器虚拟地址指针 */static void __iomem * CCM_CGPR1;static void __iomem * CCM_CGPR0;static void __iomem *IOMUXC_UART3_TX_DATA_UART3_TX;static void __iomem *IOMUXC_UART3_RX_DATA_UART3_RX;static void __iomem *PAD_CTL_PAD_UART3_TX_DATA;static void __iomem *PAD_CTL_PAD_UART3_RX_DATA;static void __iomem *UART3_UFCR;static void __iomem *UART3_UBIR;static void __iomem *UART3_UBMR;static void __iomem *UART3_UCR1;static void __iomem *UART3_UCR2;static void __iomem *UART3_UCR3;static void __iomem *UART3_USR2;static void __iomem *UART3_URXD;static void __iomem *UART3_UTXD; void register_init(void); void uart_init(void); void uart_io_init(void); void uart_disable(void); void uart_enable(void); void uart_softreset(void); int getc(void); void myexit(void);unsigned char i; char RECS[100];void register_init(void){ printk("register_init\r\n"); CCM_CGPR1=ioremap(CCM_CGPR1_BASE,4); CCM_CGPR0=ioremap(CCM_CGPR0_BASE,4);IOMUXC_UART3_TX_DATA_UART3_TX=ioremap(IOMUXC_UART3_TX_DATA_UART3_TX_BASE, 4);IOMUXC_UART3_RX_DATA_UART3_RX=ioremap(IOMUXC_UART3_RX_DATA_UART3_RX_BASE,4);PAD_CTL_PAD_UART3_TX_DATA=ioremap(PAD_CTL_PAD_UART3_TX_DATA_BASE,4);PAD_CTL_PAD_UART3_RX_DATA=ioremap(PAD_CTL_PAD_UART3_RX_DATA_BASE,4);UART3_UFCR=ioremap(UART3_UFCR_BASE,4);UART3_UBIR=ioremap(UART3_UBIR_BASE,4);UART3_UBMR=ioremap(UART3_UBMR_BASE,4);UART3_UCR1=ioremap(UART3_UCR1_BASE,4);UART3_UCR2=ioremap(UART3_UCR2_BASE,4);UART3_UCR3=ioremap(UART3_UCR3_BASE,4);UART3_USR2=ioremap(UART3_USR2_BASE,4);UART3_URXD=ioremap(UART3_URXD_BASE,4);UART3_UTXD=ioremap(UART3_UTXD_BASE,4);}void uart_init(void){__u32 ret;uart_io_init(); writel(0XFFFFFFFF,CCM_CGPR1);writel(0XFFFFFFFF,CCM_CGPR0);uart_disable();uart_softreset();writel(0,UART3_UCR1); ret=readl(UART3_UCR2);ret|= (1<<14) | (1<<5) | (1<<2) | (1<<1);writel(ret,UART3_UCR2); ret=readl(UART3_UCR3);ret|= 1<<2; writel(ret,UART3_UCR3); ret=readl(UART3_UCR1);ret &= ~(1<<14); writel(ret,UART3_UCR1);writel(5<<7,UART3_UFCR);writel(71,UART3_UBIR);writel(3124,UART3_UBMR);uart_enable();}void uart_io_init(void){writel(0,IOMUXC_UART3_TX_DATA_UART3_TX);writel(0,IOMUXC_UART3_RX_DATA_UART3_RX);writel(0x10B0,PAD_CTL_PAD_UART3_TX_DATA);writel(0x10B0,PAD_CTL_PAD_UART3_TX_DATA);}void uart_disable(){__u32 ret;ret=readl(UART3_UCR1);ret &= ~(1<<0); writel(ret,UART3_UCR1);}void uart_enable(){__u32 ret;ret=readl(UART3_UCR1);ret |= (1<<0); writel(ret,UART3_UCR1);}void uart_softreset(){__u32 ret;ret=readl(UART3_UCR2);ret &= ~(1<<0); /* UCR2的bit0为0,复位UART */ writel(ret,UART3_UCR2);while((readl(UART3_UCR2) & 0x1) == 0); /* 等待复位完成 */}void send_c(unsigned char c){ while(((readl(UART3_USR2)>> 3) & 0X01) == 0); /*等待上一次发送完成*/ writel(c & 0XFF, UART3_UTXD); /*写入寄存器*/}void send_str(char *str){char *p = str;while(*p) send_c(*p++);}int read_c(void){ while((readl(UART3_USR2) & 0x1) == 0);/*等待接收完成*/RECS[i++]=readl(UART3_URXD);if((RECS[i-2]=='\r')|(RECS[i-1]=='\n')) {RECS[i-2]='\0';i = 0;return 1; } return 0;}void myexit(void){iounmap(CCM_CGPR1);iounmap(IOMUXC_UART3_TX_DATA_UART3_TX);iounmap(IOMUXC_UART3_RX_DATA_UART3_RX);iounmap(PAD_CTL_PAD_UART3_TX_DATA);iounmap(PAD_CTL_PAD_UART3_RX_DATA);iounmap(UART3_UFCR);iounmap(UART3_UBIR);iounmap(UART3_UBMR);iounmap(UART3_UCR1);iounmap(UART3_UCR2);iounmap(UART3_UCR3);iounmap(UART3_USR2);iounmap(UART3_URXD);iounmap(UART3_UTXD);}struct newchriobee_dev{dev_t devid;/* 设备号 */struct cdev cdev;/* cdev */struct class *class;/* 类 */struct device *device;/* 设备 */int major;/* 主设备号 */int minor;/* 次设备号 */};struct newchriobee_dev newchriobee;static int iobee_open(struct inode *inode, struct file *filp){register_init(); /*初始化寄存器*/ uart_init(); /*初始化uart*/printk("kernel open!\r\n");filp->private_data = &newchriobee; /* 设置私有数据 */return 0;}static ssize_t iobee_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){ int ret; if(!read_c()) /*判断是否接收到数据*/return 0; printk("kernel recvdata:%s@\r\n",RECS); /*调试用途,可删除*/ ret=copy_to_user(buf,RECS,cnt); /*内核到用户空间*/ if(ret<0){ printk("kernel read error!\r\n");}return 0;}static ssize_t iobee_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){ char sendbuf[1024]={0}; int ret; ret=copy_from_user(sendbuf,buf,cnt); if(ret< 0) { printk("write error !\r\n"); return 0; } printk("kernel sendstring:%s@\r\n",sendbuf); /*调试用途,可删除*/send_str(sendbuf);return 0;}static int iobee_release(struct inode *inode, struct file *filp){return 0;}/* 设备操作函数 */static struct file_operations newchriobee_fops = {.owner = THIS_MODULE,.open = iobee_open,.read = iobee_read,.write = iobee_write,.release = iobee_release,};static int __init iobee_init(void){/* 注册字符设备驱动 *//* 1、创建设备号 */if (newchriobee.major) {/* 定义了设备号 */newchriobee.devid = MKDEV(newchriobee.major, 0);register_chrdev_region(newchriobee.devid, NEWCHRIOBEE_CNT, NEWCHRIOBEE_NAME);} else {/* 没有定义设备号 */alloc_chrdev_region(&newchriobee.devid, 0, NEWCHRIOBEE_CNT, NEWCHRIOBEE_NAME);/* 申请设备号 */newchriobee.major = MAJOR(newchriobee.devid);/* 获取分配号的主设备号 */newchriobee.minor = MINOR(newchriobee.devid);/* 获取分配号的次设备号 */}printk("newcheiobee major=%d,minor=%d\r\n",newchriobee.major, newchriobee.minor);/* 2、初始化cdev */newchriobee.cdev.owner = THIS_MODULE;cdev_init(&newchriobee.cdev, &newchriobee_fops);/* 3、添加一个cdev */cdev_add(&newchriobee.cdev, newchriobee.devid,NEWCHRIOBEE_CNT);/* 4、创建类 */newchriobee.class = class_create(THIS_MODULE,NEWCHRIOBEE_NAME);if (IS_ERR(newchriobee.class)) {return PTR_ERR(newchriobee.class);}/* 5、创建设备 */newchriobee.device = device_create(newchriobee.class, NULL, newchriobee.devid, NULL,NEWCHRIOBEE_NAME);if (IS_ERR(newchriobee.device)) {return PTR_ERR(newchriobee.device);}return 0;}static void __exit iobee_exit(void){ myexit();/* 注销字符设备驱动 */cdev_del(&newchriobee.cdev);/* 删除cdev */unregister_chrdev_region(newchriobee.devid, NEWCHRIOBEE_CNT); /* 注销设备号 */device_destroy(newchriobee.class, newchriobee.devid);class_destroy(newchriobee.class);printk("iobee clear !\r\n");}module_init(iobee_init);module_exit(iobee_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("YZM very Good");
Linux C++服务器:
咱们物联网系统一共有五个设备需要接入云服务,分别是web,MCU,QT,QT APP,JS。所以创建五个套接字,绑定五个不同端口,实现不同的服务。访问80端口服务器向客户发送一个HTML文件,HTML文件里面有JS,JS连接服务器的另外一个端口,每2秒向服务器拿一次结点数据更新,响应JS数据需要自己发送响应头和json格式的数据模拟WEB服务器。同理其他都差不多,MCU的端口服务是工程量最大的,首先介绍服务器的自动判断单片机结点是否连接断开功能,我规定单片机每2秒上传一次传感器个结点数据并且给每个单片机都固定一个IP,当上传数据时包头都是ID,我们拆包拿出ID,将IP数字作为键放入一个map容器。初始化键的值为true。然后给这个ID开一个线程让一直while循环每sleep 5秒判断一次键的值是否为true,如果为true则置为false继续循环,如果为false则删除该数据退出线程清理线程。所以单片机每次上传数据都判断map容器是否存在ID如果存在则让它置为true,不存在则添加,就像STM32的看门狗一样没有及时喂狗就没有及时将false置为true,线程判断到时如果还为false就删除ID当作断开连接处理,所以需要单片机每一段时间更新一次数据去将map容器的值置true。接下来多线程操作数据库的问题,数据库的性能瓶颈往往出现在并发读写上。我对Mysql的查询优化性能什么的挺薄弱的,我想了一个办法,每次收到单片机的数据时我们对数据库库插入更改后,立马进行一次查询,然后将查询到的数据包保存起来,web网页呀Qt程序呀他们那边都是定时器定时拿数据,避免其他线程频繁对数据库查询,直接让他们定时去访问这个保存起来的数据包,因为每来一次单片机数据都会更新数据库和数据包所以数据包时刻都是最新状态。
接下来就是各个单片机之间的控制系统的熟实现,单片机上传过来的数据包最后一为代表他需要的服务器进行的操作,0代表更新数据,1代表需要控制其他单片机结点,2代表RFID注册,3代表RFID卡号与数据库比对,打个比如:我的数据包是 1000name#value0 这个数据包是id1000 设备名字name 值为value 0为操作符 所以该数据是更新服务器数据,服务器收到此数据就去数据库更新数据, 如果数据包为 1000 LED3#true1 这个1就是代表该id:1000 的单片机需要服务器将设备为 LED3的值修改为true 就是开灯的意思,然后服务器就去数据库或者那个数据包找到包涵LED3设备的ID 知道了ID 我们直接组包发给网关,网关根据ID范围就知道该ID属于哪个结点,然后就向该无线通信结点发送数据包控制该结点下的单片机的结点,对应广播形式的无线传感器结点我们单片机需要判断收到的数据包的ID是不是跟本设备ID匹配,不同则代表不属于该单片机消息,直接丢包。
通过内模板创建线程
int main(void){ //数据库初始化 SQLifconfig *sql_typ_MCU=new SQLifconfig; sql_typ_MCU->SQL_init(host,user,pwd,dbname); /*数据库连接*/ SQLifconfig *sql_typ_MID=new SQLifconfig; sql_typ_MID->SQL_init(host,user,pwd,dbname); /*数据库连接*/ SQLifconfig *sql_typ_APP=new SQLifconfig; sql_typ_APP->SQL_init(host,user,pwd,dbname); /*数据库连接*/ SQLifconfig *sql_typ_Web=new SQLifconfig; sql_typ_Web->SQL_init(host,user,pwd,dbname); /*数据库连接*/ SQLifconfig *sql_typ_JS=new SQLifconfig; sql_typ_JS->SQL_init(host,user,pwd,dbname); /*数据库连接*/ cout<<"sql select :"<<sql_typ_MCU->Dql_sql("SELECT * FROM node")<<endl; sql_typ_MCU->Dml_sql("DELETE FROM node"); //server初始化 Myserver<MCUServer> MCU_server(AF_INET,SOCK_STREAM,0,sql_typ_MCU); MCU_server.server_start(MY_IP,MY_MCUPORT,AF_INET); Myserver<APPServer> APP_server(AF_INET,SOCK_STREAM,0,sql_typ_APP); APP_server.server_start(MY_IP,MY_APPPORT,AF_INET); Myserver<WebServer> Web_server(AF_INET,SOCK_STREAM,0,sql_typ_Web); Web_server.server_start(MY_IP,MY_WEBPORT,AF_INET); Myserver<JSServer> js_server(AF_INET,SOCK_STREAM,0,sql_typ_JS); js_server.server_start(MY_IP,MY_JSPORT,AF_INET); Myserver<MEEServer> MEE_server(AF_INET,SOCK_STREAM,0,sql_typ_MID); MEE_server.server_start(MY_IP,MY_MMEPORT,AF_INET); while (1) { //std::cout<<"new connect !!"<<endl; } }
Qt物联网网关终端:
终端开一个定时器,定时去服务器拿数据显示,根据ID范围判断哪个无线模块结点下的数据,显示在特定的容器控件下,首先因为存在不同类型的传感器节点所以有不同的操作,比如温湿度只需要显示,RFID需要信息显示和清空注册,温度阈值需要设置阈值,电机需要开关,所以传过来的数据我对器其ID进行子串匹配,存在TH(threshold)代表设备关于阈值 (温度TH),然后给他创建一共自己封装好的类,然后在根据id把这个对象放入特定容器布局,同理设备名字存在RFID,value值等于true或者false的都格外判断为启动电机,LED等设备的标志,这些规则都需要单片机去遵循。
特殊功能:服务器那边以STM32看门狗的方式判断是否断开连接,我们客户端也同样需要。首先一共有四种容器,分别转四个自定义类,每一个设备类都有两个槽函数,一个为接收主窗口的信号,一个为数据比对函数,每次收到数据包都需要根据上面的分类将其id,name,value与早就存在特定的容器的相同对象进行比对(封装了一个槽函数),如果比对成功该类的标志位置一,全部比对完成后,发送一个所以对象都绑定了的信号,所有对象判断自身标志位,如果为一则将置0,如果为0,则代表没有及时喂狗,代表新的数据包没有上次存储的数据,代表该结点以不存在,需要delete清除该自己,同时发送信号到主窗口,删除容器中的数据,这样可以一直保持最新的数据显示。(这个思路真的复杂,想了好久)
挺乱这算法哈哈
void Widget::Data_Analysis(QStringList Data){ bool asd; if(Data.at(1).contains("TH")) { asd=true; for (int i=0;i<mlist_setth.size();i++) { if(mlist_setth[i]->Data_compare(Data)){ asd=false; return ; } } if(asd) { setWidget_layout(Data,1); } } else if(Data.at(2)=="true" || Data.at(2)=="false"){ asd=true; for (int i=0;i<mlist_isbtn.size();i++) { if(mlist_isbtn[i]->Data_compare(Data)) { asd=false; return ; } } if(asd) { setWidget_layout(Data,0); } } else if(Data.at(1).contains("RFID")){ asd=true; for (int i=0;i<mlist_rfid.size();i++) { if(mlist_rfid[i]->Data_compare(Data)) { asd=false; return ; } } if(asd) { setWidget_layout(Data,3); } } else { asd=true; for (int i=0;i<mlist_showdata.size();i++) { if(mlist_showdata[i]->Data_compare(Data)) { asd=false; return ; } } if(asd) { setWidget_layout(Data,2); } }}void Widget::setWidget_layout(QStringList Data,int x){ int id=Data.at(0).toInt(); if(id>=1000 && id<2000) { if(x==0) { MyIsbtn* isbtn=new MyIsbtn(this,Data,socket); connect(this,&Widget::Delete_SING,isbtn,&MyIsbtn::Delete_MyIsbtn); connect(isbtn,&MyIsbtn::sendindex,this,&Widget::Delete_index); layout8266->insertWidget(0,isbtn); mlist_isbtn.append(isbtn); }else if(x==1){ MySetTH* setTh=new MySetTH(this,Data,socket); connect(this,&Widget::Delete_SING,setTh,&MySetTH::Delete_MySetTH); connect(setTh,&MySetTH::sendindex,this,&Widget::Delete_index); layout8266->insertWidget(0,setTh); mlist_setth.append(setTh); } else if(x==2){ MyShowData* showdata=new MyShowData(this,Data); connect(this,&Widget::Delete_SING,showdata,&MyShowData::Delete_MyShowData); connect(showdata,&MyShowData::sendindex,this,&Widget::Delete_index); layout8266->insertWidget(0,showdata); mlist_showdata.append(showdata); } else if(x==3){ MyRFID* rfid=new MyRFID(this,Data,socket); connect(this,&Widget::Delete_SING,rfid,&MyRFID::Delete_MyRFID); connect(rfid,&MyRFID::sendindex,this,&Widget::Delete_index); layout8266->insertWidget(0,rfid); mlist_rfid.append(rfid); } } else if(id>=2000 && id<3000) { if(x==0) { MyIsbtn* isbtn=new MyIsbtn(this,Data,socket); connect(this,&Widget::Delete_SING,isbtn,&MyIsbtn::Delete_MyIsbtn); connect(isbtn,&MyIsbtn::sendindex,this,&Widget::Delete_index); layouthc06->insertWidget(0,isbtn); mlist_isbtn.append(isbtn); }else if(x==1){ MySetTH* setTh=new MySetTH(this,Data,socket); connect(this,&Widget::Delete_SING,setTh,&MySetTH::Delete_MySetTH); connect(setTh,&MySetTH::sendindex,this,&Widget::Delete_index); layouthc06->insertWidget(0,setTh); mlist_setth.append(setTh); } else if(x==2){ MyShowData* showdata=new MyShowData(this,Data); connect(this,&Widget::Delete_SING,showdata,&MyShowData::Delete_MyShowData); connect(showdata,&MyShowData::sendindex,this,&Widget::Delete_index); layouthc06->insertWidget(0,showdata); mlist_showdata.append(showdata); } else if(x==3){ MyRFID* rfid=new MyRFID(this,Data,socket); connect(this,&Widget::Delete_SING,rfid,&MyRFID::Delete_MyRFID); connect(rfid,&MyRFID::sendindex,this,&Widget::Delete_index); layouthc06->insertWidget(0,rfid); mlist_rfid.append(rfid); } } else if(id>=3000 && id<4000) { if(x==0) { MyIsbtn* isbtn=new MyIsbtn(this,Data,socket); connect(this,&Widget::Delete_SING,isbtn,&MyIsbtn::Delete_MyIsbtn); connect(isbtn,&MyIsbtn::sendindex,this,&Widget::Delete_index); layoutzibge->insertWidget(0,isbtn); mlist_isbtn.append(isbtn); }else if(x==1){ MySetTH* setTh=new MySetTH(this,Data,socket); connect(this,&Widget::Delete_SING,setTh,&MySetTH::Delete_MySetTH); connect(setTh,&MySetTH::sendindex,this,&Widget::Delete_index); layoutzibge->insertWidget(0,setTh); mlist_setth.append(setTh); } else if(x==2){ MyShowData* showdata=new MyShowData(this,Data); connect(this,&Widget::Delete_SING,showdata,&MyShowData::Delete_MyShowData); connect(showdata,&MyShowData::sendindex,this,&Widget::Delete_index); layoutzibge->insertWidget(0,showdata); mlist_showdata.append(showdata); } else if(x==3){ MyRFID* rfid=new MyRFID(this,Data,socket); connect(this,&Widget::Delete_SING,rfid,&MyRFID::Delete_MyRFID); connect(rfid,&MyRFID::sendindex,this,&Widget::Delete_index); layoutzibge->insertWidget(0,rfid); mlist_rfid.append(rfid); } }}void Widget::Delete_index(int i,QString name){ //ui->textEdit->append("销毁"+QString ::number(i)+":"+name); if(i==0) //isbtn { for (int i=0;i<mlist_isbtn.size();i++) { if(mlist_isbtn.at(i)->Isname(name)) { mlist_isbtn.removeAt(i); return ; } } } else if(i==1){ for (int i=0;i<mlist_setth.size();i++) { if(mlist_setth.at(i)->Isname(name)) { mlist_setth.removeAt(i); return ; } } } else if(i==2){ for (int i=0;i<mlist_showdata.size();i++) { if(mlist_showdata.at(i)->Isname(name)) { mlist_showdata.removeAt(i); return ; } } } else if(i==3){ for (int i=0;i<mlist_rfid.size();i++) { if(mlist_rfid.at(i)->Isname(name)) { mlist_rfid.removeAt(i); return ; } } }}
Qt APP:
本项目是服务于智慧农场,所以开发一个app 只显示一个农场的特定的数据和特定的操作,使其一些操作和数据固化。
JS,WEB,HTML:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>智慧农场</title> <style> /* 样式表 */ body { font-family: Arial, sans-serif; margin: 0; padding: 0; } #header { background-color: #333; color: white; text-align: center; padding: 10px; } #content { margin: 20px; padding: 20px; border: 1px solid #ccc; } #footer { background-color: #333; color: white; text-align: center; padding: 10px; } #DJ { margin-left: 600px; /* 你可以根据需要调整右移的距离 */ } #DJ2 { margin-left: 600px; /* 你可以根据需要调整右移的距离 */ } #SB { margin-left: 600px; /* 你可以根据需要调整右移的距离 */ } #deng { margin-left: 600px; /* 你可以根据需要调整右移的距离 */ } #WD { margin-left: 100px; /* 你可以根据需要调整右移的距离 */ } #WDHT { margin-left: 100px; /* 你可以根据需要调整右移的距离 */ } #SD { margin-left: 100px; /* 你可以根据需要调整右移的距离 */ } #TRSD { margin-left: 100px; /* 你可以根据需要调整右移的距离 */ } #TRSDTH { margin-left: 100px; /* 你可以根据需要调整右移的距离 */ } #GM { margin-left: 100px; /* 你可以根据需要调整右移的距离 */ } #GMTH { margin-left: 100px; /* 你可以根据需要调整右移的距离 */ } </style></head><body><div id="header"> <h1>智慧农场</h1></div><div id="content"> <h2 id="WD">温------------度: <label id="wendu">等待数据传输</label> <label id="DJ">电机:</label> <label id="dianji">等待数据传输</label> </h2> <h2 id="WDHT">温--度--阈--值: <label id="wendu_TH">等待数据传输</label> </h2> <br> <h2 id="SD">湿------------度: <label id="shidu">等待数据传输</label> <label id="DJ2">舵机:</label> <label id="duoji">等待数据传输</label> </h2> <br> <h2 id="TRSD">土--壤--湿--度: <label id="shidu_2">等待数据传输</label> </h2> <h2 id="TRSDTH">土壤湿度阈值: <label id="shidu_2_TH">等待数据传输</label> <label id="SB">水泵:</label> <label id="shuibeng">等待数据传输</label> </h2> <br> <h2 id="GM">光------------敏: <label id="light">等待数据传输</label> </h2> <h2 id="GMTH">光--敏--阈--值: <label id="light_TH">等待数据传输</label> <label id="deng">LED:</label> <label id="LED">等待数据传输</label> </h2> <br></div><div style="margin-top: 160px;"></div><div id="footer"> 版权所有 © 2023 YZM系统公司</div></body><script> setInterval(function () { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { var ctext = xhr.responseText; var obj = JSON.parse(ctext); for(key in obj) { if(key == "温度") { document.getElementById('wendu').innerHTML = obj[key]; } else if(key == "土壤湿度") { document.getElementById('shidu_2').innerHTML = obj[key]; } else if(key == "土壤湿度TH") { document.getElementById('shidu_2_TH').innerHTML = obj[key]; } else if(key == "光敏") { document.getElementById('light').innerHTML = obj[key]; } else if(key == "光敏TH") { document.getElementById('light_TH').innerHTML = obj[key]; } else if(key == "湿度") { document.getElementById('shidu').innerHTML = obj[key]; } else if(key == "温度TH") { document.getElementById('wendu_TH').innerHTML = obj[key]; } else if(key == "电机") { document.getElementById('dianji').innerHTML = obj[key]; } else if(key == "舵机") { document.getElementById('duoji').innerHTML = obj[key]; } else if(key == "水泵") { document.getElementById('shuibeng').innerHTML = obj[key]; } else if(key == "LED3") { document.getElementById('LED').innerHTML = obj[key]; } } } else { console.error("请求失败:" + xhr.status); } } }; var url = "http://110.42.228.65:65000" xhr.open("GET", url, true); xhr.send(); }, 2000);</script></html>
本人hello word的水平,不多说 (面向人工智障编程,cv工程师)
总结
本项目是我写过最麻烦的项目,调试花了一周,总时长25天,各种BUG,每个设备之间的数据传递问题巨多,先局部后则整体,代码看了百遍不止,调麻了,但也只有调试BUG才能成长和磨练自己,学习阶段还是好好自己学扎实。