本帖最后由 eew_7QI3Yq 于 2022-10-17 15:02 编辑
作品名称
基于树莓派教育套件的物联网开发
作者:eew_7QI3Yq
本次大赛申请了树莓派作为我们的物联网开发套件,其主要是借助了树莓派的强大生态,以及用户群体众多的好处,树莓派可以实现我们的图像开发,可以直接添加好我们的QT,然后做界面处理,还可以使用QT调用OPENCV来运行,本身也可以直接做相关开发,极大的简化了我们的开发流程,同时网上的教程也很多,所以非常适合我们的开发工作。我作为申请了物联网开发,我在其中通过QT 的MQTT模块,进行了阿里云连接,上传数据,同时通过阿里云平台进行下发控制数据,可以控制我们底层的LED灯状态。
二、系统框图
整体框图如上,硬件部分全部通过树莓派400的拓展接口连接,软件通过树莓派系统中安装QT,进行桌面端的开发工作。后续自己也想通过学习OPENCV和QT之间进行一些图像识别功能处理。
三、各部分功能说明
主要的三个传感器,一个PM2.5激光传感器,一个SHT20温湿度传感器,一个SGP30 TVOC和CO2传感器。
给树莓派400的一个拓展接口板子,还有一个LED灯设备。
整体图片,主题是树莓派400,一个键盘形式的开发板,省去了我们外接键盘非常好用,就是hdmi接口需要购买一个单独的线,进行转接输出。
显示屏幕上我,做了QT开发的方案,里面可以直接运行程序然后读取底层三个传感器的数值,同时控制一个按键作为底层灯亮灭的功能。还有一部分是做了摄像头采集图像功能,可以实时显示画面的。
QString host("iot-06z00ac7lu605si.mqtt.iothub.aliyuncs.com"); // 代理服务器 IP
QByteArray password = xxxx; // 设备密码
quint16 port = 1883; // 代理服务器端口
QString ClientId = xxxx; // 设备 ID
QString productId = "qt_test&h14lBOv58fr"; // 产品 ID
client->setKeepAlive(120); // 心跳
client->setHostName(host); // 设置 EMQ 代理服务器
client->setPort(port); // 设置 EMQ 代理服务器端口
client->setClientId(ClientId); // 设备 ID
client->setUsername(productId); // 产品 ID
client->setPassword(password);
client->cleanSession();
client->setVersion(QMQTT::MQTTVersion::V3_1_1); // 设置mqtt版本
client->connectToHost(); // 连接 EMQ 代理服务器
整体连接阿里云服务器就是上面的代码,我们只需要设置好服务器的网址,这个是阿里云统一的华东服务器的网址。后面的设备密码,代理服务器端口,ID都是需要在阿里云端做好之后,他会生成的。
#include "sht20.h"
#define SHT2X_ADDR 0x40
#define SHT2X_SOFTRESET 0xFE
#define SHT2X_MEAS_TEMPER 0xF3
#define SHT2X_MEAS_HUMIDITY 0xF5
int MainWindow::sht20_Init()
{
fd=open(DEV_FILE,O_RDWR);
if(fd<0)
{
printf("App:Open dev failed.\n");
return -1;
// goto END;
}
ioctl(fd, I2C_TENBIT, 0);
ioctl(fd, I2C_SLAVE, SHT2X_ADDR);
return fd;
}
void MainWindow::read_Sht20_data()
{
char buf[50];
static int now = 0;
unsigned char tmp[5];
unsigned short tempori,humiori;//温湿度原始数据
static int temp_max=0,humi_max=0;
int ret = -1;
ioctl(fd, I2C_SLAVE, SHT2X_ADDR);
ret = sht2x_softreset(fd);
if( ret < 0 )
{
printf("sht2x_softreset failure.\n");
// return -2;
}
ret = sht2x_get_temper_rh(fd, &temp, &humi);
if( ret < 0 )
{
printf("sht2x_get_temper_rh failure.\n");
// return -3;
}
printf("SHT2x-T:%f RH:%f%%\n",temp,humi);
ui->label_8->setText(QString::number(temp,'f',2));
ui->label_9->setText(QString::number(humi,'f',2));
if(show_temp_humi)
{
now_time++;
dataCustomPlot->graph(0)->addData(now_time, temp); //addData(double key, double value);原型
dataCustomPlot->graph(1)->addData(now_time, humi);
dataCustomPlot->replot();
}
}
void MainWindow::msleep(unsigned int time)
{
struct timespec sleeper, temp;
sleeper.tv_sec = (time_t)(time/1000);
sleeper.tv_nsec = (long)(time%1000)*1000000;
nanosleep(&sleeper, &temp);
return ;
}
int MainWindow::sht2x_softreset(int fd)
{
int ret = -1;
uint8_t buf[2] = {0};
if( fd < 0 )
{
printf("input the invalid argument.\n");
return -1;
}
buf[0] = SHT2X_SOFTRESET;
ret = write(fd, buf, 1);
if( ret < 0 )
{
printf("write softrest cmd to sht2x failure.\n");
return -2;
}
msleep(50);
return 0;
}
int MainWindow::sht2x_send_cmd_wr(int fd, char *which)
{
int ret = -1;
uint8_t buf[2] = {0};
if( fd < 0 )
{
printf("input the invalid argument.\n");
return -1;
}
if( strcmp(which, "temper") == 0 )
{
buf[0] = SHT2X_MEAS_TEMPER;
ret = write(fd, buf, 1);
if( ret < 0 )
{
printf("write temper cmd to sht2x failure.\n");
return -2;
}
msleep(85); //datasheet typ=66, max=85
}
else if( strcmp(which, "rh") == 0 )
{
buf[0] = SHT2X_MEAS_HUMIDITY;
ret = write(fd, buf, 1);
if( ret < 0 )
{
printf("write humidity cmd to sht2x failure.\n");
return -3;
}
msleep(29); //datasheet typ=22, max=29
}
return 0;
}
int MainWindow::sht2x_get_temper_rh(int fd, float *temper, float *rh)
{
uint8_t buf[4] = {0};
int ret = -1;
if( fd<0 || !temper || !rh )
{
printf("input the invalid arguments.\n");
return -1;
}
ret = sht2x_send_cmd_wr(fd, "temper");
if( ret < 0 )
{
printf("sht2x_send_cmd_wr temper failure.\n");
return -2;
}
else
{
ret = read(fd, buf, 3);
if( ret < 0 )
{
printf("get the temper failure.\n");
return -3;
}
*temper = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
}
ret = sht2x_send_cmd_wr(fd, "rh");
if( ret < 0 )
{
printf("sht2x_send_cmd_wr rh failure.\n");
return -2;
}
else
{
read(fd, buf, 3);
if( ret < 0 )
{
printf("get the temper failure.\n");
return -3;
}
*rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
}
return 0;
}
整体的sht20的温湿度代码,其实就是还是使用linux那套,操作linux底层,通过ioctl来操作,QT正好对这些都是可以兼容的。同理我们的SGP30也是可以这样的,直接读取。在树莓派上更多的一种是通过python进行编写测试,后期我们也可以自己尝试。
QSerialPort *myplotcom = new QSerialPort;
void MainWindow::usartDataInit()
{
// UsartData::myplotcom = new QSerialPort;
myplotcom->setBaudRate(QSerialPort::Baud9600,QSerialPort::AllDirections);//设置波特率和读写方向
myplotcom->setDataBits(QSerialPort::Data8); //数据位为8位
myplotcom->setFlowControl(QSerialPort::NoFlowControl);//无流控制
myplotcom->setParity(QSerialPort::NoParity); //无校验位
myplotcom->setStopBits(QSerialPort::OneStop); //一位停止位
#ifdef Q_OS_WIN//如果是windows系统
myplotcom->setPortName("COM1");//ttymxc2
#else//如果是unix或者其他系统
myplotcom->setPortName("/dev/ttyAMA1");//ttymxc2
#endif
myplotcom ->open(QIODevice::ReadWrite);
if(myplotcom->isOpen())
{
qDebug()<<"myplotcom is open"<<endl;
}
else
{
qDebug()<<"myplotcom is error"<<endl;
}
// connect(myplotcom,SIGNAL(readyRead()),this,SLOT(receiveInfo()));
myReadTimer=new QTimer(this);
myReadTimer->setInterval(500);
connect(myReadTimer,&QTimer::timeout,this,&MainWindow::receiveInfo);//这个定时器是一进此界面就开始启动,定时读取,主要读取探测器参数。
myReadTimer->start();
}
//接收到单片机发送的数据进行解析
void MainWindow::receiveInfo()
{
if (myplotcom->bytesAvailable()<=0){return;}
myHelper::Sleep(100);//延时100毫秒保证接收到的是一条完整的数据,而不是脱节的,
QByteArray info = myplotcom->readAll();
QString plotDataHex = myHelper::ByteArrayToHexStr(info);//分出16进制HEX文件打印输出,后期可以关闭
qDebug() << plotDataHex << endl;
if(info.length()<24)return;
if((uchar)info.at(0)==0x32&&(uchar)info.at(1==0x3d))
{
PM_2_5_data = (uchar)info.at(6)<<8|(uchar)info.at(7);
PM10_data = (uchar)info.at(8)<<8|(uchar)info.at(9);
ui->label_12->setText(QString::number(PM_2_5_data));
ui->label_13->setText(QString::number(PM10_data));
if(show_pm2_5)
{
now_time++;
dataCustomPlot->graph(0)->addData(now_time, PM_2_5_data); //addData(double key, double value);原型
dataCustomPlot->graph(1)->addData(now_time, PM10_data);
dataCustomPlot->replot();
}
}
}
对于PM2.5来说我们的是通过一个串口,目前树莓派4可以做到了多串口,我使用了增加的一个串口,ttyAMA1这样就不用愁串口太少了。
void MainWindow::led_init()
{
system("echo 26 > /sys/class/gpio/export");
system("echo out > /sys/class/gpio/gpio26/direction");
system("echo 0 > /sys/class/gpio/gpio26/value");
}
void MainWindow::on_pushButton_2_clicked()
{
if(ui->pushButton_2->text() == "开灯")
{
on_off = true;
system("echo 1 > /sys/class/gpio/gpio26/value");
ui->pushButton_2->setText("关灯");
}
else
{
on_off = false;
system("echo 0 > /sys/class/gpio/gpio26/value");
ui->pushButton_2->setText("开灯");
}
}
开关灯,同样的道理,使用linux底层操作就可以完成。
五、作品功能演示视频
【基于树莓派教育套件的物联网开发】
六、项目总结
首先还是感谢得捷电子提供此次物料费用,同时在得捷上也能购买相应的电子物料,到货也还是非常快的,其次本次大赛给的时间够长,完成了此次作品也学习了很多东西。最后树莓派用起来真的是生态丰富了,后续我再分享开发案例吧。
七、其他
|