“智慧教室”项目的目标是实现实时采集环境中的温度、湿度及光照信息,并将这些数据可视化地呈现在Qt图形界面上。用户既可以选择手动控制风扇与窗帘的开关,也可以设置系统根据所采集的环境参数自动调节,以达到舒适的室内环境。接下来,将和各位小伙伴深入探讨这一项目的具体实现方式。
一、 功能特性
1、数据监测与显示:实时监测室内温度、湿度和光照强度,并直观地显示在Qt界面上。
2、手动/感应控制:用户可以通过切换界面按钮选择手动控制风扇和窗帘的状态,或者根据实时监测到的环境数据,自动的调节风扇和窗帘的状态,以提供最佳的舒适度和能源利用效率。
3、节能优化:自动调节功能可实时调整设备状态,以减少能源消耗并提高设备寿命。
二、 环境说明
1、开发环境操作系统:Ubuntu18.04 64位版
2、交叉编译工具链:arm-poky-linux-gnueabi-gcc 5.3.0
3、开发板使用Bootloader版本:u-boot-2016.03
4、开发板内核版本:linux-4.1.15
5、开发板移植QT版本:qt5.6.2
三、 硬件连接
1、风扇与板卡的连接
2、步进电机与板卡的连接
四、 内核适配
ELF 1开发板已集成温湿度传感器与光线传感器,实现环境温度、湿度和光照强度的采集。为支持智慧教室系统的其它功能,需要对内核源码进行以下适配步骤。
1、实现风扇转动的功能
风扇采用pwm的方式来控制,需要在设备树中进行pwm7的复用。
(1)拷贝ELF1开发板资料包\02-Linux 源代码\02-0 出厂内核和uboot源码\内核源码\linux-4.1.15-elf1.tar.bz2内核源码到开发环境/home/elf/work/目录下解压。
elf@ubuntu:~/work$ tar -xvf linux-4.1.15-elf1.tar.bz2
(2)修改顶层设备树文件arch/arm/boot/dts/imx6ull.dtsi
elf@ubuntu:~/work$ cd linux-4.1.15-elf1/
elf@ubuntu:~/work/linux-4.1.15-elf1$ vi arch/arm/boot/dts/imx6ull.dtsi
(3)修改设备树文件arch/arm/boot/dts/imx6ull-elf1-emmc.dts
elf@ubuntu:~/work/linux-4.1.15-elf1$ vi arch/arm/boot/dts/imx6ull-elf1-emmc.dts
添加pwm7设备节点
&pwm7 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_pwm7>;status = "okay";
};
在iomux节点下面添加pwm7引脚复用
pinctrl_pwm7: pwm7grp {fsl,pins = <MX6UL_PAD_CSI_VSYNC__PWM7_OUT 0x110b0>;
};
取消其它用到csi功能的地方
至此pwm7已经复用完成。
2、实现控制窗帘开合的功能
该项目选用28BYJ-48步进电机实现窗帘的开合操作,28BYJ-48电机需要4个GPIO来控制,需要在设备树中进行GPIO的复用并在内核中添加电机驱动。
(1)修改设备树文件arch/arm/boot/dts/imx6ull-elf1-emmc.dts。
elf@ubuntu:~/work$ cd linux-4.1.15-elf1/
elf@ubuntu:~/work/linux-4.1.15-elf1$ vi arch/arm/boot/dts/imx6ull-elf1-emmc.dts
添加设备节点
mymotor {compatible = "motor";pinctrl-names = "default";pinctrl-0 = <&mymotor>;status = "okay";motorA-gpios = <&gpio4 21 GPIO_ACTIVE_HIGH>;motorB-gpios = <&gpio4 22 GPIO_ACTIVE_HIGH>;motorC-gpios = <&gpio4 23 GPIO_ACTIVE_HIGH>;motorD-gpios = <&gpio4 24 GPIO_ACTIVE_HIGH>;};
在iomux节点下面添加引脚复用
mymotor: mymotorgrp {fsl,pins = <MX6UL_PAD_CSI_DATA00__GPIO4_IO21 0x10b0MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x10b0MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x10b0MX6UL_PAD_CSI_DATA03__GPIO4_IO24 0x10b0>;
};
(2)添加电机驱动。
拷贝motor.c到内核源码的drivers/gpio目录下,修改drivers/gpio目录下的Makefile文件,添加如下内容:
obj-y += motor.o
3、编译并替换设备树和内核
(1)执行环境变量
elf@ubuntu:~/work/linux-4.1.15-elf1$ . /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
(2)编译设备树和内核
elf@ubuntu:~/work/linux-4.1.15-elf1$ make imx6ull_elf1_defconfig
elf@ubuntu:~/work/linux-4.1.15-elf1$ make dtbs
elf@ubuntu:~/work/linux-4.1.15-elf1$ make zImage
(3)将arch/arm/boot/路径下的zImage和arch/arm/boot/dts/路径下的imx6ull-elf1-emmc.dtb放到U盘,通过U盘拷贝到开发板。
root@ELF1:~# cp /run/media/sda1/imx6ull-elf1-emmc.dtb /run/media/mmcblk1p1/
root@ELF1:~# cp /run/media/sda1/zImage /run/media/mmcblk1p1/
(4)保存并重启开发板
root@ELF1:~# sync
root@ELF1:~# reboot
五、基于Qt界面的数据采集和控制
1、程序设计
主函数的实现main.cpp
int main(int argc, char* argv[])
{QApplication a(argc, argv);if(!ControllerHandler::instance()->startController()){return 0;}a.setStyle("Fusion");QFont f = a.font();f.setPointSize(20);f.setPixelSize(16);a.setFont(f);MWainWindow w;w.setWindowFlags(Qt::FramelessWindowHint);if(a.primaryScreen()){w.resize(a.primaryScreen()->size());}w.show();int ret = a.exec();return ret;
}
设置显示的时间的样式和位置
void MWainWindow::showtime()
{/* 实例化与设置显示的位置大小*/lcdNumber = new QLCDNumber(this);QScreen *screen = QGuiApplication::primaryScreen();// 获取屏幕分辨率的大小QSize screenSize = screen->size();int screenWidth = screenSize.width();int screenHeight = screenSize.height();// 根据屏幕分辨率的大小执行不同的操作if (screenWidth == 1024 && screenHeight == 600) {lcdNumber->setGeometry( 850, 9, 100, 40);} else if (screenWidth == 800 && screenHeight == 480) {lcdNumber->setGeometry( 650, 9, 100, 40);} else {qDebug() << "Performing other operation";}/* 设置显示的位数 8 位 */lcdNumber->setDigitCount(8);/* 设置样式 */lcdNumber->setFrameStyle(QFrame::NoFrame);/* 设置 lcd 显示为当前系统时间 */QTime time = QTime::currentTime();/* 设置显示的样式 */lcdNumber->display(time.toString("hh:mm:ss"));lcdNumber->setStyleSheet("color: black");}
设置显示的图片的大小和位置
void MWainWindow::showphoto()
{ /* 设置大小与位置 */this->setGeometry(0, 0, 1024, 600);/* 使用资源里的文件时格式是 :+前缀+文件路径 */QPixmap pixmap(":images/res.png");labelImage = new QLabel(this);QScreen *screen = QGuiApplication::primaryScreen();// 获取屏幕分辨率的大小QSize screenSize = screen->size();int screenWidth = screenSize.width();int screenHeight = screenSize.height();// 根据屏幕分辨率的大小执行不同的操作if (screenWidth == 1024 && screenHeight == 600) {labelImage->setGeometry(14, 61, 343, 524);} else if (screenWidth == 800 && screenHeight == 480) {labelImage->setGeometry(14, 61, 343, 403);} else {qDebug() << "Performing other operation";}/* 设置图像 */labelImage->setPixmap(pixmap);/* 开启允许缩放填充 */labelImage->setScaledContents(true);}
获取设备的状态
void MWainWindow::readData()
{/* 当定时器计时 1000 毫秒后,刷新 lcd 显示当前系统时间 */QTime time = QTime::currentTime();/* 设置显示的样式 */lcdNumber->display(time.toString("hh:mm:ss"));ControllerHandler* pInstance =ControllerHandler::instance();float value=-100;int curtainstate = targetCurtainState;pInstance->getValue(TEMPERATURE_SENSOR,value);ui->widget_temp->setData("温度", QString::number(value) +"°");if(bOpen==1){if(value > 31) {ControllerHandler::instance()->setValue(FAN, 10, ' ', ' ');} else {ControllerHandler::instance()->setValue(FAN, 0, ' ', ' ');}}pInstance->getValue(HUMIDITY_SENSOR,value);ui->widget_hum->setData("湿度", QString::number(value) +"%");pInstance->getValue(LIGHT_SENSOR,value);ui->widget_light->setData("亮度", QString::number(value) +" lt");if(bOpen==1){if (value >= 0 && value < 100) {if (curtainstate == 0) {printf("Automatic curtain opening!!!\n");//ControllerHandler::instance()->setValue(MYMOTOR, ' ', 'R', 2048);targetCurtainState = 1;}} else if (value >= 100 && value < 500) {if (curtainstate == 1) {printf("Automatic curtain closing!!!\n");//ControllerHandler::instance()->setValue(MYMOTOR, ' ', 'L', 2048);targetCurtainState = 0;}}}if(bOpen==0){if(bOpen2==1){//printf("Fan open!!!\n");ControllerHandler::instance()->setValue(FAN, 10, ' ', ' ');}if(bOpen2==0){//printf("Fan close!!!\n");ControllerHandler::instance()->setValue(FAN, 0, ' ', ' ');}}if(bOpen == 0){if (isFirstCheck) {lastbOpen3 = bOpen3;isFirstCheck = false;} else if (bOpen3 != lastbOpen3) {if(bOpen3 == 1){printf("Curtain open!!!\n");//ControllerHandler::instance()->setValue(MYMOTOR, ' ', 'R', 2048);} else {printf("Curtain close!!!\n");//ControllerHandler::instance()->setValue(MYMOTOR, ' ', 'L', 2048);}lastbOpen3 = bOpen3;}}
}
设置界面样式
void MWainWindow::Style()
{QString qssFileName;qssFileName = ":/green.qss";QFile file(qssFileName);if(file.open(QFile::ReadOnly| QFile::Text)){QString qss =file.readAll();QApplication *app = dynamic_cast<QApplication*>(qApp);if(!qss.isEmpty()){app->setStyleSheet(qss);}file.close();}
}
2、应用编译
(1)拷贝smartclassroom.tar.bz2到开发环境/home/elf/work目录下解压
elf@ubuntu:~/work$ tar xvf smartclassroom.tar.bz2
(2)执行环境变量
elf@ubuntu:~/work/adc$ . /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
(3)编译
elf@ubuntu:~/work$ cd smartclassroom/
elf@ubuntu:~/work/smartclassroom$ qmake
elf@ubuntu:~/work/smartclassroom$ make
(4)压缩
elf@ubuntu:~/work/smartclassroom$ cd ../smartclassroom_output/release/
elf@ubuntu:~/work/smartclassroom_output/release$ tar -cjvf smartclassroom.tar.bz2 *
(5)拷贝smartclassroom.tar.bz2压缩包到开发板/home/root路径下解压
root@ELF1:~# cp /run/media/sda1/smartclassroom.tar.bz2 ./
root@ELF1:~# tar xvf smartclassroom.tar.bz2
root@ELF1:~# chmod 777 *
root@ELF1:~# cp lib* /usr/lib
root@ELF1:~# sync
六、项目测试
1、确保开发板已正确连接风扇、步进电机和屏幕
2、执行应用
root@ELF1:~# export DISPLAY=:0.0
root@ELF1:~# ./app
应用执行成功后,Qt界面显示如下,此时可以通过切换按钮选择手动控制或者感应控制风扇开关以及窗帘开合。