【毕设】智慧园区(家居)系统调试
0、前言
- 电脑、树莓派、手机 三者要处于同一网络
- 树莓派使用时记得散热,直接用风扇吹,不用后及时断电,注意保养和珍惜
- 提前了解Linu系统基础指令,了解vi nano编辑器
- 最好提前学习Linux系统编程,主要有:文件、线程与网络(不然main函数无法自由修改)
- 终端下删除的文件不放在回收站,删了直接没了,也没有撤回这一说法,Linux系统下没有后悔药
- 全套课程视频在实验室我看课的电脑上,b站也已有人将课录成了视频:
- 同门博客首页
青木
莘莘
殷华阳
풍殇
小肥猫
巫传珺
小高同学
浩子哥
一、☆树莓派网络及系统配置
树莓派开机等启动不熟悉或出现问题的可以先参考:
1.快速上手树莓派
2.树莓派4B从0开始配置到使用vim编辑器输出helloworld!
0. 利用官方提供的软件烧录镜像(推荐)
若卡非空,请先利用 SDFormatter(内存卡修复工具).exe 修复工具初格式化SD数据。
选择树莓派型号:RASPBERRY PI 4
选择烧写的系统:Raspberry Pi OS (64-bit)
选择存储卡
点击 NETX 编辑设置
配置树莓派名称、密码,连接的 wifi 热点名称及其密码,开启SSH登录权限
![](https://i-blog.csdnimg.cn/direct/bd853c47a1904ac2b5fc54c2a4c9e134.png#pic_center)
1. 查看树莓派IP地址与登录
方式一:直接在手机热点处看接入设备的名称和IP地址(即树莓派的IP地址)
PS:有些手机支持看,有些手机不支持看
方式二: 通过串口登陆的方式查看树莓派IP 地址
串口登陆
1.修改系统配置,启用串口登录树莓派,默认是蓝牙
1.打开SD卡根目录的"config.txt"文件,将以下内容添加在最后并且保存
dtoverlay=pi3-miniuart-bt
dtoverlay=uart2
dtoverlay=uart3
dtoverlay=uart4
dtoverlay=uart5
这样就停止了蓝牙,解除了对串口的占用,同时添加了其他串口,用于后续多串口通信,方式后续忘记添加。
2.然后再修改根目录的"cmdline.txt",将里面的内容全部替换成以下内容,以防万一,请先备份好这个文件的原内容。
dwc_otg.lpm_enable=0 console=tty1 console=serial0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
配置修改完成。
注意:后续在语音识别通信的地方要对cmdline.txt文件修改,这里用于串口登录,后续串口通信,波特率不一样
(1)查看IP指令:
通过 ctrl+alt+t 打开linu终端界面,通过 ifconfig
命令查看此时树莓派的ip地址,并做记录,后期这个ip地址很重要
后期说的树莓派ip或者没有特定说哪个ip默认是树莓派ip
ifconfig
![](https://i-blog.csdnimg.cn/direct/aa8a582f8bfc417497e91839e5bfb7dd.png#pic_center)
若Wifi连接失败
通过显示屏+键盘+鼠标+HDMI,使树莓派上电,在显示屏上显示界面,在右上角选择你的热点并登陆 (推荐)
失败原因可能有:
- wifi 账号密码输入错误
- wifi 频率问题,2.4G较佳,5G似乎也可以
- wifi名称不要有中文等特殊符号!!!
- 等等
请参考博文:
1.快速上手树莓派
2. 树莓派4B从0开始配置到使用vim编辑器输出helloworld!
(2)SSH 登陆
在个人PC上(需要与树莓派连接同一个wifi),用mobaXterm 或 securtCRT 等终端软件采用ssh登陆树莓派, 登陆信息包括:
1.树莓派ip: xxx.xxx.xxx.xxx
2.用户名:烧镜像时候的账号 偏好 pi
3.password(密码):烧镜像时候的密码 偏好 pi
securtCRT SSH 界面设置:
选项 -> 编辑默认会话 -> 终端 -> 仿真 -> Linux
设置外观:
(3)获取工程
利用FileZilla工具传输 GraProject 工程到树莓派
远程传输指令:
sftp://192.168.43.117
Ps:可以用其他登陆软件,方便传输文件
二、树莓派主程序分析与环境配置
2.1 Linux环境相关
0. linux常用指令
1.若实际有需求的,这里没有的自行csdn查找
2.这里为了方便与演示,文件夹名用 smarthome 代替,.c 文件用 main.c 代替,可执行文件用 a.out 代替,实际以真实情况为准
3.指令注意空格和大小写
功能 | 指令 |
---|---|
查看当前目录下有哪些文件 | ls |
查看当前位置路径 | pwd |
打开文件夹 smarthome | cd smarthome |
返回上一级目录 | cd … |
打开程序 main.c | vi main.c |
运行程序 a.out | ./a.out |
编译 main.c | gcc mian.c |
vi 编辑器(插入模式写代码,命令模式下执行命令)
PS:未指定说明,指定都属命令模式下操作
功能 | 命令 |
---|---|
打开文件 main.c | vi main.c |
进入插入模式 | o |
退出插入模式,进入命令模式 | Esc(插入模式下按) |
保存退出 | :wq |
撤销 | u |
复制当前行 | yy |
复制当前起的n行 | n yy |
剪切当前行 | dd |
复制当前起n行 | n dd |
粘贴复制行 | p |
复制选中部分 | ctrl+shift+c |
粘贴选中部分 | ctrl+shift+v(只限插入模式) |
nano 编辑器(直接编写即可)
功能 | 按键 |
---|---|
保存 | Ctrl + o |
确认保存? | Enter |
退出 | Ctrl + x |
1. 必要环境的安装
参考博文:快速上手树莓派
(1)安装GCC编译器
sudo apt install build-essential
(2)下载安装新版VIM
sudo apt-get install vim
(3)下载树莓派WiringPi库:【可用】树莓派安装WiringPi库
2. 配置串口通信
(1)修改ttyAMA0串口(之前用于串口登陆的串口,配置为与语音模块串口通信),这个修改经常忘记
修改 cmdline.txt文件
sudo vim /boot/cmdline.txt
删除 【console=serial0,115200】 部分
sudo reboot 重启
参考博文:树莓派wiringPi库详解
(2)配置开启串口 UART2-5,执行编辑 config.txt 命令:
sudo nano /boot/config.txt
在文件结尾添加如下:
dtoverlay=uart2
dtoverlay=uart3
dtoverlay=uart4
dtoverlay=uart5
保存(Ctrl + o)并退出(Ctrl + x)
重启(sudo reboot
)后查看是否生效:
ls /dev/ttyAMA*
结果显示如下:
pi@raspberrypi:~ $ ls /dev/ttyAMA*
/dev/ttyAMA0 /dev/ttyAMA1 /dev/ttyAMA2 /dev/ttyAMA3 /dev/ttyAMA4
3. 摄像头视频监控
注意这里使用的是:720P USB高清摄像头,端口号:8081,在start.sh中修改即可
详细操作见:树莓派实时监控-mjpg-streame库
2.2 程序分析与运行
1.修改socketContrl.c内的ip改为树莓派ip,并修改记录端口号(一般为8081-8089比较好)
2.修改链表节点IO接口(如果用我那块板子就不用改)
包括文件有:livingroomLight.c restaurantLight.c bedroomLight.c bathroomLight.c
3.main函数硬件接口(如果用我那块板子就不用改)
PS:这里程序算法是有点突兀的,开灯用的是工厂模式下的节点控制,关灯是在main函数内比对接收的字符串关灯,没有涉及到节点,故需改2处接口,后续对程序进行优化。
4.修改火灾报警的电话(模块记得插电话卡)
5.编译指令
gcc *.c -lwiringPi -l pthread //默认生产可执行文件 a.out
gcc *.c -lwiringPi -l pthread -o mainend //生成 mainend可执行文件
6.异常退出正在编辑的程序,第二次进入会出现以下情况:
解决方案:
1.按 q 退出程序,不打开
2.ls -a 查看全部文件
3. 删除.开头且后续文件名和异常文件同名的.swp文件即可
8. 脚本与自启动
(1)用vi在pi 目录下创建并打开两个脚本 main.sh 以及 camera.sh。
vi main.sh
vi camera.sh
(2)分别编辑两个脚本内容
main.sh:启动即可运行主程序
cd /home/pi/smarthome
./a.out
camera.sh:运行即可启动摄像头视频监控
cd /home/pi/mjpg-streamer/mjpg-streamer-experimental/
./start.sh
(3)修改两个脚本的权限,变成可执行
chmod +x main.sh
chmod +x camera.sh
(4)将两个脚本放到开启自启动配置中
打开配置文件
sudo nano /etc/rc.local
配置文件中启动脚本
location="/home/pi" #脚本路径
cd $location
$location/main.sh & $location/camera.sh
main.c源程序见文末 !!!
main.c源程序见文末!!!
main.c源程序见文末!!!
核心代码有注释
三、java与安卓工程(Eclipse)
3.1 导入与下载到手机
1.导入工程:file->Import->Android->Existing Android Code Into Workspace
2.下载工程:
记得在手机上开启开发者模式,并开启USB调试
Ps:
- 通常情况下,在电脑的Android模拟器上调试查看界面
- 相关视频:安卓环境搭建
- 如果下载不了APP,试试其他电脑,换成华为或者荣耀的手机比较好,小米手机似乎不好好调试。当有一个手机能下载时,可以先下载好APP,然后通过QQ发送应用给其他人用。
3.2 页面布局的设计
1.背景图片
2.控制界面和数据界面的文字
first是主界面,second是控制,third是场景与音乐,fourth是数据接收
3.3 指令程序的设计
1.网络配置
2.发送数据与图像实时监控
四、STM32与云平台
4.1 STM32程序设计
STM32(你们比我懂)初始化与硬件模块
内容包括:IO口,中断、定时器、串口、IIC、SPI
4.2 云平台的连接与控制
云平台的设置
1.云平台:中国移动OneNET云平台
以上为现有的产品和设备和产品内部设备,可直接用(SmartPark),若后续需继续添加如下操作
添加产品
添加设备
4.3 云平台的对接(模块记得插物联网卡)与数据传输
修改程序参数,对接云平台
M5310_Init(); //这是NB的初始化,配置AT使对接云平台
while(OneNet_DevLink()) //接入OneNET
delay_ms(300);
数据上传
数据接收
接收buf:req_payload
五、语音模块
5.1 LCD3322语音模块
(绿色的,好用)
参考配套视频:LD3322 高品质语音识别模块系列教程
功能配置网站:智能公园
通过导入json文件进行后续修改开发
主要功能:语音识别设置、串口发送数据、模块自学习
程序烧写软件:Hummingbird-M-Update-Tool下的UniOneUpdateTool
5.2 LD3320语音模块
方案二、LD3320(蓝色的,不好用),可研究程序等
参考配套视频:LD3320语音识别模块的使用
软件:keilC51+STC-ISP
六、openMV人脸识别的实现(CNN)
前期数据采集,模型训练、及识别参考:OpenMV4 Plus训练神经网络进行口罩识别
相关配置:
发送数据:
附1:树莓派的mainpro.c
#include<stdio.h>
#include"contrlDevices.h"
#include<string.h>
#include<unistd.h>
#include"inputCommand.h"
#include "pthread.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<wiringSerial.h>
#include<wiringPi.h>
#define led1 24
#define led2 25
#define led3 23
#define led4 22
//设备结构体、指令结构体、网络结构体,不动就行
struct Devices *pdeviceHead=NULL;
struct InputCommand *pCommandHead=NULL;
struct InputCommand *socketHandler=NULL;
/*
* 标识符就看成是某个设备/文件的 ID,Linux系统下一切皆文件思想
* c_fd:客户端网络标识符
* fd0: 串口0标识符 TX 用于mp3 music player 播放音乐 Rx用于接收LD3322语音识别模块指令
* fd2:串口2标识符 Tx 用于 NB 打电话串口 Rx用于接收openMV人脸数据
* flag:网络接收数据标志位,接收到了然后发,发线程还没有做好
* Send_bbuf:网络发送buf
*/
int c_fd,fd0,fd2,flag=0,Send_buf[10];
//需拨打的电话、NB模组记得要插打正常的电话卡,模组要按一下闪绿灯才算启动,末尾 分号和\n 不要改
char number[]={"ATD15928084196;\n"};
char msg_32[128]={'\0'};
//硬件端口初始化
void HARDWARE_Init()
{
pinMode(led1,OUTPUT);
pinMode(led2,OUTPUT);
pinMode(led3,OUTPUT);
pinMode(led4,OUTPUT);
digitalWrite(led1,HIGH);
digitalWrite(led2,HIGH);
digitalWrite(led3,HIGH);
digitalWrite(led4,HIGH);
//打开串口2 Tx 用于 NB 打电话串口 Rx用于接收openMV人脸数据
fd2 = serialOpen("/dev/ttyAMA2",9600);
//打开川口0 TX 用于 music player 播放音乐 Rx用于接收LD3322语音识别模块指令
fd0 = serialOpen("/dev/ttyAMA0",9600);
//串口1是用于和STm32的全双工通信接口,在STM32线程中进行配置与初始化
}
//关灯
void closeAllLight()
{
digitalWrite(led1,HIGH);
digitalWrite(led2,HIGH);
digitalWrite(led3,HIGH);
digitalWrite(led4,HIGH);
}
//开灯
void openAllLight()
{
digitalWrite(led1,LOW);
digitalWrite(led2,LOW);
digitalWrite(led3,LOW);
digitalWrite(led4,LOW);
}
//music player驱动程序,不改
void SendCmd(int len)
{
int a=0;
serialPutchar(fd0,0x7E);
for(a=0;a<len;a++)
{
serialPutchar(fd0,Send_buf[a]);
}
serialPutchar(fd0,0xEF);
}
//music player驱动程序,不改
void DoSum(int *str,int len)
{
int xorsum=0;
int a;
for(a=0;a<len;a++)
{
xorsum= xorsum+str[a];
}
xorsum=0-xorsum;
*(str+a)=(int)(xorsum>>8);
*(str+a+1)=(int)(xorsum&0x00ff);
}
/*
* music player发送指令程序,用于播放不同序号的音乐
* CMD : 0x03 (不变)
* feedback : 0 (不变)
* dati : 歌曲序号(十六进制) 例如:0x01 0x03 0x1e 等
*/
void Uart_SendCMD(int CMD,int feedback,int dati)
{
Send_buf[0]=0xff;
Send_buf[1]=0x06;
Send_buf[2]=CMD;
Send_buf[3]=feedback;
Send_buf[4]=(int) (dati>>8);
Send_buf[5]=(int) (dati);
DoSum(&Send_buf[0],6);
SendCmd(8);
}
//工厂模式:工厂加入设备节点(不变)
struct Devices *findDeviceByName(char *name,struct Devices *phead)
{
struct Devices *tmp=phead;
if(phead==NULL)
{
return NULL;
}
else
{
while(tmp!=NULL)
{
if(strcmp(tmp->deviceName,name)==0)
{
return tmp;
}
tmp=tmp->next;
}
return tmp;
}
}
//工厂模式:查找指令(不变)
struct InputCommand *findCommandByName(char *name,struct InputCommand *phead)
{
struct InputCommand *tmp=phead;
if(phead==NULL)
{
return NULL;
}
else
{
while(tmp!=NULL)
{
if(strcmp(tmp->commandName,name)==0)
{
return tmp;
}
tmp=tmp->next;
}
return NULL;
}
}
//通过翔云人工智能云平台(有次数限制,还要钱)人脸识别,主要涉及拍照、对接与接收
现在不用了,现在用openMV了,想用结合get.c自行研究
void face(void)
{
char ch[1];
FILE *p;
int count=3; //一定每次启动最多识别3次即可,1分钟就没次数了
int i;
while(count>0)
{
memset(ch,'0',1);
printf("photeing.......\n");
//以下接的树莓派摄像头拍照
//system("raspistill -t 2000 -o img2.jpg -q 5");
//以下是uSB拍照程序,记得修改ip+端口号,这个端口不是网络端口,是在摄像头那边设备的端口,默认是8080
system("wget http://192.168.43.216:8081/?action=snapshot -O ./img2.jpg");
ch[0]='0';
printf("photo successed! please waiting...\n");
p=popen("./facerx","r"); //启动get.c编译出来的可执行程序
fread(ch,1,1,p);
if(ch[0]=='1')
{
printf("The same person\n");
openAllLight();
sleep(5);
closeAllLight();
count=0; //识别成功就提前结束本次识别,不然在浪费次数
}
else
printf("The different person\n");
count--;
}
}
//语音线程,用于接收LD3322语音模块数据
//线程可当做中断服务函数理解,为核心控制代码区
void *voice_thread(void *datas)
{
int nread; //接收串口接收的字符指令,LD3322发的是字符
char msg[128]={'\0'}; //接收数据msg
// Uart_SendCMD(0x03,0,0x01); //园区初始化语音播报
while(1)
{
while(serialDataAvail(fd0)!=-1 ) //有数据来了
{
nread=read(fd0,msg,128); //接收数据,接收的数据msg,返回接收到的字节数存入nread
if(nread==0) //没数据
continue;
//打印接收来自LD3322的n字节数据 msg
printf("From LD3320 get :%d Byte context:%s \n",nread,msg);
//判断接收到的数据,然后开灯+播放音乐
if(strstr(msg,"A")!=NULL){
Uart_SendCMD(0x03,0,0x03);
}
if(strstr(msg,"B")!=NULL){
Uart_SendCMD(0x03,0,0x04);
digitalWrite(led3,LOW);
}
if(strstr(msg,"C")!=NULL){
Uart_SendCMD(0x03,0,0x05);
digitalWrite(led2,LOW);
}
if(strstr(msg,"D")!=NULL){
Uart_SendCMD(0x03,0,0x04);
digitalWrite(led1,LOW);
}
if(strstr(msg,"E")!=NULL){
Uart_SendCMD(0x03,0,0x05);
digitalWrite(led4,LOW);
}
if(strstr(msg,"F")!=NULL){
closeAllLight();
Uart_SendCMD(0x03,0,0x04);
}
if(strstr(msg,"G")!=NULL){
openAllLight();
Uart_SendCMD(0x03,0,0x04);
}
if(strstr(msg,"P")!=NULL)
Uart_SendCMD(0x03,0,0x0b);
if(strstr(msg,"Q")!=NULL)
Uart_SendCMD(0x03,0,0x0b);
if(strstr(msg,"R")!=NULL)
Uart_SendCMD(0x03,0,0x15);
if(strstr(msg,"S")!=NULL)
Uart_SendCMD(0x03,0,0x0d);
if(strstr(msg,"T")!=NULL)
Uart_SendCMD(0x03,0,0x0e);
if(strstr(msg,"U")!=NULL)
Uart_SendCMD(0x03,0,0x21);
//清空接收区
memset(msg,'\0',128);
}
}
}
//网络接收线程,用于读取来自客户端的指令并做控制
void *read_thread(void *datas)
{
int n_read; //读取到的字节数
struct Devices *tmp=NULL; //暂存设备节点
memset(socketHandler->command,'\0',sizeof(socketHandler->command)); //清空接收区
//接收区为 socketHandler->command 结构体成员,看成buf字符串就行
while(1)
{
//网络控制硬件
//接收,返回接收到的字节数放入n_read,指令在socketHandler->command内
n_read=read(c_fd,socketHandler->command,sizeof(socketHandler->command));
//打印接收到的数据
printf("\n ********get %d Byte:%s*************\n",n_read,socketHandler->command);
//通过名称找设备,设备名称存入tmp
tmp=findDeviceByName(socketHandler->command,pdeviceHead);
//控制方式1:这里就是工厂模式的开灯指令
if(tmp!=NULL)
{
tmp->deviceInit(tmp->pinNum);
tmp->open(tmp->pinNum);
}
//给客户端发送指令
// write(c_fd,socketHandler->command,sizeof(socketHandler->command));
flag=1; //说明收到指令,在发送线程调用
//控制方式2:直接判断接收到的字符串,接收到关灯指令,关灯
if(strstr(socketHandler->command,"closeLED1")!=NULL)
digitalWrite(led3,HIGH);
if(strstr(socketHandler->command,"closeLED2")!=NULL)
digitalWrite(led4,HIGH);
if(strstr(socketHandler->command,"closeLED3")!=NULL)
digitalWrite(led1,HIGH);
if(strstr(socketHandler->command,"closeLED4")!=NULL)
digitalWrite(led2,HIGH);
//手机发送火灾指令,路演时偷偷强行打电话
if(strstr(socketHandler->command,"fire")!=NULL)
{
write(fd2,number,sizeof(number));
printf("Called\n");
}
//清空接收区
memset(socketHandler->command,'\0',sizeof(socketHandler->command));
}
}
//openMV串口线程,用于接收openMV的数据
void *openMV_thread(void *datas)
{
int nread2; //接收openMV数据字节数
char msg_mv[128]={'\0'}; //接收openMV
while(1)
{
memset(msg_mv,'\0',128); //清空接收区
while(serialDataAvail(fd2)!=-1)
{
nread2=read(fd2,msg_mv,128); //读取,数据存入msg_mv,返回字节存入nread2
if(nread2==0)
continue;
printf("get open_MV:%d Byte context:%s \n",nread2,msg_mv); //打印接收到哦数据和字节
if(strstr(msg_mv,"face")!=NULL) //接收到face指令
openAllLight(); //开灯并延时
delayMicroseconds(1500000);
closeAllLight();
memset(msg_mv,'\0',128); //情况字符串
}
}
}
//与STM32通信进行
void *STM32_thread(void *datas)
{
int nread1,fd1;
fd1=serialOpen("/dev/ttyAMA1",9600);
while(1)
{
while(serialDataAvail(fd1)!=-1)
{
nread1=read(fd1,msg_32,128);
if(nread1==0)
continue;
// printf("get data from stm32:%d Byte context:%s\n",nread1,msg_32);
printf("get:%s\r\n",msg_32);
delayMicroseconds(3000000);
//STM32接收来自云平台数据后,将数据发送给树莓派
//server open
if(strstr(msg_32,"enLED1")!=NULL)
digitalWrite(led1,LOW);
if(strstr(msg_32,"seLED1")!=NULL)
digitalWrite(led1,HIGH);
if(strstr(msg_32,"enLED2")!=NULL)
digitalWrite(led2,LOW);
if(strstr(msg_32,"seLED2")!=NULL)
digitalWrite(led2,HIGH);
//server close
if(strstr(msg_32,"enLED3")!=NULL)
digitalWrite(led3,LOW);
if(strstr(msg_32,"seLED3")!=NULL)
digitalWrite(led3,HIGH);
if(strstr(msg_32,"enLED4")!=NULL)
digitalWrite(led4,LOW);
if(strstr(msg_32,"seLED4")!=NULL)
digitalWrite(led4,HIGH);
//火灾信号,这个上云的
if(strstr(msg_32,"fire")!=NULL)
{
write(fd2,number,sizeof(number));
printf("get STM32 fire\n");
}
memset(msg_32,'\0',128);
}
}
}
void *write_thread(void *datas) //通知线程,向客服端发送消息
{
while (1)
{
if(flag==1)
{
//printf("%s\n",msg_32);
// delayMicroseconds(3000000);
// write(c_fd,msg_32,128);
}
}
}
//网络线程:创建网络TCP服务器,并等待客户端接入
void *socket_thread(void *datas)
{
int n_read=0;
pthread_t readThread;
int clen=sizeof(struct sockaddr_in);
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
socketHandler=findCommandByName("socketServer",pCommandHead);
if(socketHandler==NULL)
{
printf("find socketHandler error\n");
pthread_exit(NULL);
}
else
{
printf("%s init success\n",socketHandler->commandName);
}
socketHandler->Init(socketHandler,NULL,NULL);
while(1)
{
c_fd=accept(socketHandler->sfd,(struct sockaddr *)&c_addr,&clen);
pthread_create(&readThread,NULL,read_thread,NULL);
}
}
int main()
{
//进程变量
pthread_t voiceThread;
pthread_t socketThread;
pthread_t STM32Thread;
pthread_t openMVThread;
pthread_t writeThread;
//树莓派库初始化
if(-1==wiringPiSetup())
{
printf("setup error\n");
exit(-1);
}
//硬件初始化
HARDWARE_Init();
//添加指令池、设备池到链表
pCommandHead=addvoiceContrlToInputCommandLink(pCommandHead);
pCommandHead=addSocketContrlToInputCommandLink(pCommandHead);
pdeviceHead=addBathroomLightToDeviceLink(pdeviceHead);
pdeviceHead=addBedroomLightToDeviceLink(pdeviceHead);
pdeviceHead=addRestaurantLightToDeviceLink(pdeviceHead);
pdeviceHead=addLivingroomLightToDeviceLink(pdeviceHead);
//创建线程
pthread_create(&voiceThread,NULL,voice_thread,NULL);
pthread_create(&socketThread,NULL,socket_thread,NULL);
pthread_create(&STM32Thread,NULL,STM32_thread,NULL);
pthread_create(&openMVThread,NULL,openMV_thread,NULL);
pthread_create(&writeThread,NULL,write_thread,NULL);
//加入线程
pthread_join(voiceThread,NULL);
pthread_join(socketThread,NULL);
pthread_join(STM32Thread,NULL);
pthread_join(openMVThread,NULL);
pthread_join(writeThread,NULL);
return 0;
}