“智慧教室”项目的目标是实现实时采集环境中的温度、湿度及光照信息,并将这些数据可视化地呈现在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
2、步进电机与板卡的连接
ELF 1开发板已集成温湿度传感器与光线传感器,实现环境温度、湿度和光照强度的采集。为支持智慧教室系统的其它功能,需要对内核源码进行以下适配步骤。
1、实现风扇转动的功能
风扇采用pwm的方式来控制,需要在设备树中进行pwm7的复用。
(1)拷贝ELF1开发板资料包2-Linux 源代码2-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
elf@ubuntu:~/work/linux-4.1.15-elf1$ vi arch/arm/boot/dts/imx6ull-elf1-emmc.dts
&pwm7 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm7>;
status = "okay";
};
pinctrl_pwm7: pwm7grp {
fsl,pins = <
MX6UL_PAD_CSI_VSYNC__PWM7_OUT 0x110b0
>;
};
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>;
};
mymotor: mymotorgrp {
fsl,pins = <
MX6UL_PAD_CSI_DATA00__GPIO4_IO21 0x10b0
MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x10b0
MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x10b0
MX6UL_PAD_CSI_DATA03__GPIO4_IO24 0x10b0
>;
};
(2)添加电机驱动。
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/
root@ELF1:~# sync
root@ELF1:~# reboot
主函数的实现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、应用编译
elf@ubuntu:~/work$ tar xvf smartclassroom.tar.bz2
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 *
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
2、执行应用
root@ELF1:~# export DISPLAY=:0.0
root@ELF1:~# ./app
应用执行成功后,Qt界面显示如下,此时可以通过切换按钮选择手动控制或者感应控制风扇开关以及窗帘开合。