重要的内容写在前面:
- 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。
- 个人把这个教程学完之后,整体感觉是很好的,如果有条件的可以先学习一些相关课程,学起来会更加轻松,相关课程有数字电路(强烈推荐先学数电,不然可能会有一些地方理解起来很困难)、模拟电路等,然后就是C++(注意C++是必学的)。
- 文章中的代码都是跟着老师边学边敲的,不过比起老师的版本我还把注释写得详细了些,并且个人认为重要的地方都有详细的分析。
- 一些函数的介绍有参考太极创客官网给出的中文翻译,为了便于现查现用,把个人认为重要的部分粘贴了过来并做了一些修改。
- 如有错漏欢迎指正。
视频链接:3-0 本章介绍_哔哩哔哩_bilibili
太极创客官网:太极创客 – Arduino, ESP8266物联网的应用、开发和学习资料
一、智能小夜灯
1、光敏电阻
(1)光敏电阻的阻值会随环境亮度而产生变化,光照强度越高,光敏电阻的阻值越低,反之,光照强度越低,光敏电阻的阻值就越高。
(2)按上图所示将电路连接好(光敏电阻的型号为5528),把硬件电路置于光照良好(亮度适中即可,不宜过亮)的地方,并将下面的程序下载到开发板中,然后进行人工调试。
void setup()
{
Serial.begin(9600); //启动串口通讯
}
void loop()
{
Serial.print("LDR Reading: ");
Serial.println(analogRead(A0)); //通过串口将A0引脚的电压情况发生给计算机
delay(1000); //为了便于观察光照改变前后A0的电压,这里增加一下延时
}
①遮挡光敏电阻接受的光照,那么光敏电阻的阻值将会变大,根据基尔霍夫定律可知A0引脚处的电压也会升高。
②用光照较强的光源照射光敏电阻,那么光敏电阻的阻值将会变小,根据基尔霍夫定律可知A0引脚处的电压也会降低。
2、红外人体感应模块
(1)人的身体是有一定的温度的,而且只要是活人,其身体每时每刻都会向外释放热量,这些热量很多是以红外线的形式向外释放的,而红外人体感应传感器恰恰可以感应这种红外线,并将感应到的信息通过数字信号的形式发送给单片机。
(2)红外人体感应传感器引脚说明如下,其中OUT引脚的作用是将传感器感应到的信息通过数字信号的形式发送给单片机,当感应到周围有人体活动时,OUT引脚将输出高电平,反之则输出低电平。
(3)红外人体感应传感器有一个扇形的感应区域,如下图所示,当人或动物进入红外感应区域时,该区域的红外状态将发生改变,传感器感应到有人体在周围活动,而如果人或动物在红外感应区域内静止不动时,该区域的红外状态将不会发生改变,传感器感应不到有人体在周围活动。
(4)人体感应传感器模块基本参数:
(5)按下图所示将电路连接好,并将下面的程序下载到开发板中,然后进行人工调试。
int irSensorPin = 7 ; //引脚7连接红外传感器
bool irSensorOutput; //红外传感器输出信号
void setup()
{
pinMode(irSensorPin, INPUT);
Serial.begin(9600);
Serial.println("Welcome to Taichi-Maker's IR Motion Sensor tutorial.");
}
void loop()
{
irSensorOutput = digitalRead(irSensorPin); //读取红外传感器输出
if (irSensorOutput == HIGH)
{ //如果红外传感器输出高电平,说明有人体活动
Serial.println("IR Motion Sensor OUTPUT: HIGH.");
}
else
{ //如果红外传感器输出低电平,说明没人体活动
Serial.println("IR Motion Sensor OUTPUT: LOW.");
}
delay(100);
}
①当前感应区域内没有任何人体,红外传感器感应不到红外状态发生变化,遂输出低电平。
②将人体(要活的)放进感应区域进行活动,红外传感器感应到红外状态发生变化,遂输出高电平。(如果人体进入感应区域后不进行大幅度活动,红外传感器会认为区域内没有人体活动,遂输出低电平)
③将感应区域内的人体撤离,红外传感器感应到红外状态发生变化,遂输出高电平。
④人体完全撤离感应区域后,红外传感器虽然感应不到红外状态发生变化,但还是会输出一段时间的高电平,再输出低电平,这是因为如果有人体在感应区域内活动,但他的活动幅度可能不会很大,不能保证无时无刻都在活动,但他的活动间隔时长又非常短,理论上也应该将其视作正在进行持续活动,所以红外传感器有了这种延时机制。
(6)人体感应传感器模块的调节控制可通过转动下图所示的两个旋钮来实现,它们的本质是电位器,一个用于调节感应距离(也就是感应区域的大小),另一个用于调节感应延时,也就是传感器从感应到人体活动转变为感应不到人体活动后OUT引脚输出高电平的时长(8-200秒)。
(7)人体感应传感器模块的触发方式有两种,分别为不可重复触发方式(选择引脚接低电平)和可重复触发方式(选择引脚接高电平),可通过跳线进行选择,一般默认选择可重复触发方式。这两种方式最大区别在于,模块在输出高电平的延迟时间内,是否再次检查监测区域内的红外状态有无变化。
①不可重复触发方式:即感应输出高电平后,延时时间内不再检查监测区域有无红外状态改变,当延时一结束,输出将自动从高电平变成低电平。
②可重复触发方式:即感应输出高电平后,延时时间内仍持续不断地检查监测区域有无红外状态改变,如果在延时结束前再次检查到区域内有红外状态改变,模块将重新开始计算延时。
3、REG-LED
(1)RGB-LED中包含三种颜色的LED灯,有共阳极RGB-LED和共阴极RGB-LED两种,下面使用的是共阴极RGB-LED,顾名思义就是将三个LED灯的阴极与GND连接在一起,通过给各LED的阳极配置不同的电压,可以使RGB-LED发出不同颜色的灯光。
(2)按下图所示将电路连接好(电阻可选220Ω),并将下面的程序下载到开发板中,然后进行人工调试。
const int rLedPin = 6; //引脚R
const int gLedPin = 5; //引脚G
const int bLedPin = 3; //引脚B
int ledR = 0; //R LED 亮度
int ledG = 0; //G LED 亮度
int ledB = 0; //B LED 亮度
void setup()
{
pinMode(rLedPin, OUTPUT); pinMode(gLedPin, OUTPUT); pinMode(bLedPin, OUTPUT);
Serial.begin(9600);
Serial.println("Welcome to Taichi-Maker RGB LED Tutorial.");
Serial.println("Please Input RGB value(eg. r128g100b20)."); //发送命令的格式说明
}
void loop()
{
if (Serial.available() > 0) //判断串口接收缓存是否有信息需要处理
{
char serialCmdChar = Serial.read(); //获取信息中的第一个字节,这个字节对应的字符是'r'、'g'、'b'、'c'中的一个
serialCmd(serialCmdChar);
}
delay(50);
}
void serialCmd(char serialCmdChar)
{
switch (serialCmdChar) //根据之前获取的字符,设置相应LED灯的亮度
{
case 'r': ledR = Serial.parseInt();break; //从缓存中的内容获取整数数据作为亮度参数
case 'g': ledG = Serial.parseInt();break; //从缓存中的内容获取整数数据作为亮度参数
case 'b': ledB = Serial.parseInt();break; //从缓存中的内容获取整数数据作为亮度参数
case 'c': ledR = 0; ledG = 0; ledB = 0; //熄灯操作对应的亮度参数
}
analogWrite(rLedPin, ledR); delay(100);
analogWrite(gLedPin, ledG); delay(100);
analogWrite(bLedPin, ledB); delay(100);
Serial.print ("Red Value = "); Serial.println (ledR);
Serial.print ("Green Value = "); Serial.println (ledG);
Serial.print ("Blue Value = "); Serial.println (ledB);
Serial.println ("-------------");
}
①通过串口助手向Arduino发送内容“r255g0b0”,Arduino会将红色LED的亮度点满,另外两个LED的亮度为0,结果RGB-LED将发出红光。
②通过串口助手向Arduino发送内容“r0g255b0”,Arduino会将绿色LED的亮度点满,另外两个LED的亮度为0,结果RGB-LED将发出绿光。
③通过串口助手向Arduino发送内容“r0g0b255”,Arduino会将蓝色LED的亮度点满,另外两个LED的亮度为0,结果RGB-LED将发出蓝光。
④通过串口助手向Arduino发送内容“c”,Arduino会将三个LED的亮度均设置为0,RGB-LED将不发出任何光亮。
⑤R、G、B的亮度参数不局限于0或255,其取值范围在0-255之间,三个参数的不同取值可以有非常多的组合,相应地,每一个组合都对应一种独特的颜色,如下图所示,其原理就是三原色相加可得出更多的颜色,比如通过串口助手向Arduino发送内容“r75g0b130”,那么红色LED的亮度参数为75,绿色LED的亮度参数为0,蓝色LED的亮度参数为130,RGB-LED将发出紫光。
4、智能小夜灯的实现
(1)电路搭建如下图所示。
(2)各模块所承担的任务:
①光敏电阻:“帮助”Arduino感应环境亮度,如果当前环境的亮度较高,RGB-LED不需要发光。
②人体感应传感器模块:“帮助”Arduino感应附近有没有人在活动,如果附近没有人,或者没有人在活动(比如人在睡觉),那么RGB-LED是不需要发光的。
③RGB-LED:负责发光。
(3)基础版控制程序(能实现基本需求,在该发光时发光,在没必要发光时不发光):
①全局变量及宏定义:
#define rLedPin 6 //RGB-LED引脚R连接Arduino的引脚6
#define gLedPin 5 //RGB-LED引脚G连接Arduino的引脚5
#define bLedPin 3 //RGB-LED引脚B连接Arduino的引脚3
#define irSensorPin 8 //红外人体感应模块信号输出连接Arduino的引脚8
#define lightSensorPin A0 //光敏电阻分压电路信号输出连接Arduino的引脚A0
// 用户可以通过变量 ledR、ledG、ledB 自定义RGB-LED颜色
int ledR = 99; //R LED 亮度
int ledG = 185; //G LED 亮度
int ledB = 33; //B LED 亮度
bool irReading; //存储红外人体感应模块的输出
int lightReading; //存储光敏电阻分压电路信号(反映当前环境亮度)
②初始化工作部分:
void setup()
{
pinMode(rLedPin, OUTPUT); pinMode(gLedPin, OUTPUT); pinMode(bLedPin, OUTPUT);
pinMode(irSensorPin, INPUT);
Serial.begin(9600); Serial.println("Welcome to Taichi-Maker RGB Led Night-Light.");
}
③循环工作部分:
void loop()
{
irReading = digitalRead(irSensorPin); //读取红外人体感应模块
lightReading = analogRead(lightSensorPin); //读取光敏电阻分压电路信号输出
if(irReading == HIGH && lightReading >= 500)
{ //如感应到有人活动且亮度达到需照明程度
lightOn(1); //点亮小夜灯照明
}
else
{ //如未感应到有人活动或者亮度未达到需照明程度
lightOn(0); //保持小夜灯熄灭
}
//通过串口输出一些用于调试的信息
Serial.println("");
Serial.println("===========================");
Serial.print("irReading = "); Serial.println(irReading);
Serial.print("lightReading = "); Serial.println(lightReading);
Serial.println("===========================");
delay(50);
}
void lightOn(bool on)
{
if (on == 1)
{ //如参数on的数值为1则点亮小夜灯
analogWrite(rLedPin, ledR); delay(10);
analogWrite(gLedPin, ledG); delay(10);
analogWrite(bLedPin, ledB); delay(10);
}
else
{ //否则保持小夜灯熄灭
analogWrite(rLedPin, 0); delay(10);
analogWrite(gLedPin, 0); delay(10);
analogWrite(bLedPin, 0); delay(10);
}
}
[1]loop函数将不断读取红外人体感应模块的输出以及光敏电阻分压电路的信号,以此分别判断当前是否有人在附近活动以及当前环境的亮度。
[2]如果红外人体感应模块输出高电平,说明此时周围有人活动,这时需要继续判断当前环境亮度是否达到需照明程度,若光敏电阻此时的分压高于某一数值(这个数值在调试光敏电阻的过程中可以得出,用来作为判断环境亮度是否过低的依据),说明环境亮度不够,则程序调用lightOn函数点亮小夜灯。
[3]如果红外人体感应模块输出低电平,说明此时周围无人活动,程序调用lightOn函数熄灭小夜灯。
[4]如果红外人体感应模块输出高电平,说明此时周围有人活动,这时需要继续判断当前环境亮度是否达到需照明程度,若光敏电阻此时的分压低于某一数值(这个数值在调试光敏电阻的过程中可以得出,用来作为判断环境亮度是否过低的依据),说明环境亮度足够,则程序调用lightOn函数熄灭小夜灯。
(4)改进版控制程序(在基础版程序的基础上,给小夜灯增加一个渐变和呼吸的效果,并且可对小夜灯的照明方式进行选择):
①全局变量及宏定义(全局变量debugOn提供的功能可以用C++中的“条件编译”实现):
#define rLedPin 6 //RGB-LED引脚R连接Arduino的引脚6
#define gLedPin 5 //RGB-LED引脚G连接Arduino的引脚5
#define bLedPin 3 //RGB-LED引脚B连接Arduino的引脚3
#define irSensorPin 8 //红外人体感应模块信号输出连接Arduino的引脚8
#define lightSensorPin A0 //光敏电阻分压电路信号输出连接Arduino的引脚A0
int ledR = 0; //R LED 亮度 (请勿改变数值)
int ledG = 0; //G LED 亮度 (请勿改变数值)
int ledB = 0; //B LED 亮度 (请勿改变数值)
bool irReading; //存储红外人体感应模块的输出
int lightReading; //存储光敏电阻分压电路信号(反映当前环境亮度)
//用户可以通过改变变量 maxRVal、maxGVal、maxBVal 自定义RGB-LED颜色
int maxRVal = 125; //夜灯R LED最大亮度 (允许数值:0 - 255)
int maxGVal = 200; //夜灯G LED最大亮度 (允许数值:0 - 255)
int maxBVal = 33; //夜灯B LED最大亮度 (允许数值:0 - 255)
int minRVal = 39; //夜灯呼吸时R LED最小亮度 (允许数值:0 - 255)
int minGVal = 95; //夜灯呼吸时G LED最小亮度 (允许数值:0 - 255)
int minBVal = 11; //夜灯呼吸时B LED最小亮度 (允许数值:0 - 255)
int breathStep = 1; //呼吸起伏速度——数值越大,一次亮度变化越大
int breathStepDelay = 18; //呼吸起伏延迟——数值越大,亮度变化速度越慢
bool breatheOnOff = 0; //记录小夜灯是否开启呼吸模式(1表示开启呼吸模式,0则反之)
bool onOffState; //记录小夜灯的开关状态
bool debugOn = 0; //调试小夜灯时debugOn初始化为0,表示需要调试,流入市场前将其初始化为1,表示不需要调试,省去向串口发送测试数据等调试步骤,从而确保Arduino处于最佳工作状态
②初始化工作部分:
void setup()
{
//设置引脚为相应工作模式
pinMode(rLedPin, OUTPUT);
pinMode(gLedPin, OUTPUT);
pinMode(bLedPin, OUTPUT);
pinMode(irSensorPin, INPUT);
if(debugOn) //在调试模式下需要通过串口发送数据
{
Serial.begin(9600);
Serial.println("Welcome to Taichi-Maker RGB Led Night-Light.");
}
//检测是否LED亮度参数符合程序要求(min数值小于等于max数值)
if (minRVal > maxRVal || minGVal > maxGVal || minBVal > maxBVal)
{
while(true) //如果出现数值设置问题,持续(死循环)闪烁红灯,并通过串口发送报错信息
{
analogWrite(rLedPin, 255);
delay(500);
analogWrite(rLedPin, 0);
delay(500);
Serial.begin(9600);
Serial.println("Bad LED Settings!!!");
}
}
}
③循环工作部分:
void loop()
{
irReading = digitalRead(irSensorPin);
lightReading = analogRead(lightSensorPin);
if(irReading == HIGH && lightReading >= 880)
{ //如果感应到有人活动且亮度达到需照明程度
if (onOffState == 0) fadeUp(maxRVal, maxGVal, maxBVal);//点亮小夜灯照明
onOffState = 1; //小夜灯处于被点亮状态
if (breatheOnOff == 1) ledBreathe(); //如果选择了呼吸模式,小夜灯需持续呼吸
}
else
{ //如果未感应到有人活动或者亮度未达到需照明程度
if (onOffState == 1) fadeDown(0, 0, 0); //熄灭小夜灯
onOffState = 0; //小夜灯处于熄灭状态
}
//在调试模式下,通过串口监视器实时输出各个传感器检测的数据结果
if(debugOn)
{
Serial.println("");
Serial.println("===========================");
Serial.print("irReading = "); Serial.println(irReading);
Serial.print("lightReading = "); Serial.println(lightReading);
Serial.println("===========================");
delay(50);
}
}
[1]loop函数将不断读取红外人体感应模块的输出以及光敏电阻分压电路的信号,以此分别判断当前是否有人在附近活动以及当前环境的亮度。
[2]如果红外人体感应模块输出高电平,说明此时周围有人活动,这时需要继续判断当前环境亮度是否达到需照明程度,若光敏电阻此时的分压高于某一数值(这个数值在调试光敏电阻的过程中可以得出,用来作为判断环境亮度是否过低的依据),说明环境亮度不够,则程序调用fadeUp函数点亮小夜灯,这个点灯过程是渐变的,即RGB-LED有一个由暗逐渐变亮的过程。
[3]如果红外人体感应模块输出低电平,说明此时周围无人活动,程序调用fadeDown函数熄灭小夜灯,若先前RGB-LED被点亮过,则会有一个由亮逐渐变暗的过程。
[4]如果红外人体感应模块输出高电平,说明此时周围有人活动,这时需要继续判断当前环境亮度是否达到需照明程度,若光敏电阻此时的分压低于某一数值(这个数值在调试光敏电阻的过程中可以得出,用来作为判断环境亮度是否过低的依据),说明环境亮度足够,则程序调用fadeDown函数熄灭小夜灯,若先前RGB-LED被点亮过,则会有一个由亮逐渐变暗的过程。
④增亮函数和减亮函数的实现:
[1]增亮函数:让小夜灯的亮度参数逐渐增加到预设的值,这个值由函数参数决定。
void fadeUp(int rTopVal, int gTopVal, int bTopVal)
{
while(ledR < rTopVal || ledG < gTopVal || ledB < bTopVal)
{ //如果当前RGB-LED的亮度未达预设的最大值
if (ledR+breathStep < rTopVal )
{ //若当前红色LED的亮度+“一次亮度变化”<红色LED最大亮度
ledR+=breathStep; analogWrite(rLedPin, ledR); //红色LED亮度增加“一次亮度变化”
}
else
{
ledR = rTopVal; analogWrite(rLedPin, ledR); //红色LED亮度保持预设最大值
}
if (ledG + breathStep < gTopVal)
{ //若当前绿色LED的亮度+“一次亮度变化”<绿色LED最大亮度
ledG+=breathStep; analogWrite(gLedPin, ledG); //绿色LED亮度增加“一次亮度变化”
}
else
{
ledG = gTopVal; analogWrite(gLedPin, ledG); //绿色LED亮度保持预设最大值
}
if (ledB + breathStep < bTopVal)
{ //若当前蓝色LED的亮度+“一次亮度变化”<蓝色LED最大亮度
ledB+=breathStep; analogWrite(bLedPin, ledB); //蓝色LED亮度增加“一次亮度变化”
}
else
{
ledB = bTopVal; analogWrite(bLedPin, ledB); //蓝色LED亮度保持预设最大值
}
delay(breathStepDelay); //等待一个呼吸起伏延迟
if (debugOn) //调试模式下通过串口发送调试信息
{
Serial.println("================");
Serial.print("rVal = "); Serial.println(ledR);
Serial.print("gVal = "); Serial.println(ledG);
Serial.print("bVal = "); Serial.println(ledB);
Serial.println("================");
}
}
}
[2]减亮函数:让小夜灯的亮度参数逐渐减小到预设的值,这个值由函数参数决定,当三个亮度参数全为0时,该函数执行让小夜灯逐渐熄灭的操作。
void fadeDown(int rBottomVal, int gBottomVal, int bBottomVal)
{
while(ledR > rBottomVal || ledG > gBottomVal || ledB > bBottomVal)
{ //如果当前RGB-LED的亮度未达预设的呼吸亮度最小值
if (ledR-breathStep > rBottomVal)
{ //若当前红色LED的亮度-“一次亮度变化”>红色LED呼吸最小亮度
ledR-=breathStep; analogWrite(rLedPin, ledR); //红色LED亮度减小“一次亮度变化”
}
else
{
ledR = rBottomVal; analogWrite(rLedPin, ledR); //保持预设的呼吸亮度最小值
}
if (ledG-breathStep > gBottomVal)
{ //若当前绿色LED的亮度-“一次亮度变化”>绿色LED呼吸最小亮度
ledG-=breathStep; analogWrite(gLedPin, ledG); //绿色LED亮度减小“一次亮度变化”
}
else
{
ledG = gBottomVal; analogWrite(gLedPin, ledG); //保持预设的呼吸亮度最小值
}
if (ledB-breathStep > bBottomVal)
{ //若当前蓝色LED的亮度-“一次亮度变化”>蓝色LED呼吸最小亮度
ledB-=breathStep; analogWrite(bLedPin, ledB); //蓝色LED亮度减小“一次亮度变化”
}
else
{
ledB = bBottomVal; analogWrite(bLedPin, ledB); //保持预设的呼吸亮度最小值
}
delay(breathStepDelay); //等待一个呼吸起伏延迟
if (debugOn) //调试模式下通过串口发送调试信息
{
Serial.println("================");
Serial.print("rVal = "); Serial.println(ledR);
Serial.print("gVal = "); Serial.println(ledG);
Serial.print("bVal = "); Serial.println(ledB);
Serial.println("================");
}
}
}
⑤小夜灯被点亮后,会有一个呼吸的效果(即灯的亮度在高与低之间徘徊),亮度的高值和低值可由程序员通过全局变量进行设定。在呼吸函数中调用一次增亮函数和一次减亮函数,其作用分别是让小夜灯到达一次最大亮度,再到达一次最小亮度,通过重复调用呼吸函数即可实现呼吸灯效果。
void ledBreathe()
{
//亮度先降低至呼吸亮度最小值
fadeDown(minRVal, minGVal, minBVal);
//亮度再提高置亮度最大值
fadeUp(maxRVal, maxGVal, maxBVal);
}
(5)变色版控制程序(在基础版程序的基础上,小夜灯被点亮时其颜色可不断发生变化):
①全局变量及宏定义:
#define rLedPin 6 //RGB-LED引脚R
#define gLedPin 5 //RGB-LED引脚G
#define bLedPin 3 //RGB-LED引脚B
#define irSensorPin 8 //红外人体感应模块信号输出
#define lightSensorPin A0 //光敏电阻分压电路信号输出
int ledR = 0; //R LED 亮度
int ledG = 0; //G LED 亮度
int ledB = 0; //B LED 亮度
bool irReading;
int lightReading;
bool onOffState; //记录小夜灯的开关状态
unsigned long previousIRMillis; //记录上一次检查红外传感器的时间
unsigned long previousLightMillis; //记录上一次检查光敏传感器的时间
int irCheckInterval = 500; //红外传感器检查时间间隔
int lightCheckInterval = 1000; //光敏传感器检查时间间隔
int colorIndex; //存储颜色序列号
int colorChangeDelay = 1; //颜色改变速度控制变量(数值越大,颜色改变速度越慢)
②初始化工作部分:
void setup()
{
//设置引脚为相应工作模式
pinMode(rLedPin, OUTPUT);
pinMode(gLedPin, OUTPUT);
pinMode(bLedPin, OUTPUT);
pinMode(irSensorPin, INPUT);
Serial.begin(9600);
Serial.println("Welcome to Taichi-Maker RGB Led Night-Light.");
Serial.println("System Start Sensor Check.");
irReading = digitalRead(irSensorPin);
lightReading = analogRead(lightSensorPin);
//通过串口监视器实时输出各个传感器检测的数据结果
Serial.println("");
Serial.println("======Checking Sensor.=====");
Serial.println("===========================");
Serial.print("irReading = "); Serial.println(irReading);
Serial.print("lightReading = "); Serial.println(lightReading);
Serial.println("===========================");
Serial.println("");
}
③循环工作部分:
void loop()
{
unsigned long currentMillis = millis(); //获取Arduino开机后运行的时间
irCheck(currentMillis);
lightCheck(currentMillis);
if (irReading == HIGH)
irCheckInterval = 30000; //感应到人时人体传感器检查时间间隔较长
else
irCheckInterval = 500; //未感应到人时人体传感器检查时间间隔较短
if(irReading == HIGH && lightReading >= 880)
{ //如感应到人体活动且亮度达到需照明程度
if (onOffState == 0) fadeOn(); //点亮小夜灯
onOffState = 1;
//检查颜色序号是否达到上限
if (colorIndex <= 1535)
colorIndex++; //如颜色序号没达到上限,令颜色序号递增
else if (colorIndex > 1535)
colorIndex = 0; //如颜色序号达到上限,令颜色序号归零
ledShowColor(colorIndex); //按照颜色序号改变灯光颜色
}
else
{ //如未感应到人体活动或者亮度未达到需照明程度
if (onOffState == 1) fadeOff(); //熄灭小夜灯
onOffState = 0;
}
}
[1]在这个控制程序中,Arduino不再频繁检测周围的人际以及环境亮度,而是设定一个时间间隔,以这个时间间隔为周期进行检测,对于环境亮度的检测,时间间隔是固定的,而对于人际的检测,当感应到人体活动时,检测间隔会适当延长,当感应不到人体活动后,检测间隔将缩短。
[2]如果当前满足点灯条件,那么Arduino将会调用点灯函数点亮小夜灯,由于loop函数重复执行,那么ledShowColor函数在满足点灯条件的前提下也会被重复调用,而每次调用都会传入不同的颜色序号作为参数,ledShowColor函数会根据传入的颜色序号进行点灯,可发出不同颜色的光。
[3]millis函数:millis函数可以用来获取Arduino开机后运行的时间长度,返回值为无符号长整型,单位是毫秒,最长可记录接近50天左右的时间,如果超出记录时间上限,记录将从0重新开始。
④检测函数:
[1]定时读取红外人体感应模块的输出:函数参数为当前Arduino的开机时间,全局变量previousIRMillis维护上一次的人际检测时间,根据这两个时间可判断Arduino是否需要读取红外人体感应模块的输出,如果需要检测,则读取模块输出后需将本次检测时间记录在previousIRMillis中。
void irCheck(unsigned long thisIRMillis)
{
if ((unsigned long)(thisIRMillis - previousIRMillis >= irCheckInterval))
{ //如果时间间隔到达了
irReading = digitalRead(irSensorPin); //读取红外人体感应模块
//通过串口监视器实时输出传感器检测的数据结果
Serial.println("");
Serial.println("=== Checking IR Sensor ====");
Serial.println("===========================");
Serial.print("irReading = "); Serial.println(irReading);
Serial.print("thisIRMillis = "); Serial.println(thisIRMillis);
Serial.print("previousIRMillis = "); Serial.println(previousIRMillis);
Serial.println("===========================");
Serial.println("");
previousIRMillis = thisIRMillis;
}
}
[2]定时读取光敏电阻分压模块的输出:函数参数为当前Arduino的开机时间,全局变量previousLightMillis维护上一次的环境亮度检测时间,根据这两个时间可判断Arduino是否需要读取光敏电阻分压模块的输出,如果需要检测,则读取模块输出后需将本次检测时间记录在previousLightMillis中。
void lightCheck(unsigned long thisLightMillis)
{
if ((unsigned long)(thisLightMillis - previousLightMillis >= lightCheckInterval))
{ //如果时间间隔到达了
lightReading = analogRead(lightSensorPin); //读取光敏电阻分压电路信号输出
//通过串口监视器实时输出各个传感器检测的数据结果
Serial.println("");
Serial.println("== Checking Light Sensor ==");
Serial.println("===========================");
Serial.print("lightReading = "); Serial.println(lightReading);
Serial.print("thisLightMillis = "); Serial.println(thisLightMillis);
Serial.print("previousLightMillis = "); Serial.println(previousLightMillis);
Serial.println("===========================");
Serial.println("");
previousLightMillis = thisLightMillis;
}
}
⑤颜色灯函数:函数参数为颜色序列号,该函数将根据参数ledColorIndex让RGB-LED显示相应颜色,从而产生小夜灯色彩渐变的效果。(函数ledShowColor中共定义了约1536种颜色)
void ledShowColor(int ledColorIndex)
{
if (ledColorIndex >= 0 && ledColorIndex <= 255){
ledR = 255 - ledColorIndex;
analogWrite (rLedPin, ledR);
} else if(ledColorIndex >= 256 && ledColorIndex <= 511){
ledR = ledColorIndex -256;
analogWrite (rLedPin, ledR);
} else if(ledColorIndex >= 512 && ledColorIndex <= 767){
ledG = 767 - ledColorIndex;
analogWrite (gLedPin, ledG);
} else if(ledColorIndex >= 768 && ledColorIndex <= 1023){
ledG = ledColorIndex - 768;
analogWrite (gLedPin, ledG);
} else if(ledColorIndex >= 1024 && ledColorIndex <= 1279){
ledB = 1279 - ledColorIndex;
analogWrite (bLedPin, ledB);
} else if(ledColorIndex >= 1280 && ledColorIndex <= 1535){
ledB = ledColorIndex - 1280;
analogWrite (bLedPin, ledB);
}
Serial.println("");
Serial.print("ledR = ");Serial.println(ledR);
Serial.print("ledG = ");Serial.println(ledG);
Serial.print("ledB = ");Serial.println(ledB);
delay(colorChangeDelay); //颜色改变延时
}
⑥点灯函数与关灯函数:
[1]点灯函数:
void fadeOn()
{
Serial.println("");
Serial.println("Fade ON");
int i;
while(i < 255) //3个LED的亮度参数逐渐增加到255
{
i++;
ledR++;ledG++;ledB++;
analogWrite(rLedPin, ledR);
analogWrite(gLedPin, ledG);
analogWrite(bLedPin, ledB);
Serial.println("");
Serial.print("ledR = ");Serial.println(ledR);
Serial.print("ledG = ");Serial.println(ledG);
Serial.print("ledB = ");Serial.println(ledB);
}
}
[2]关灯函数:
void fadeOff()
{
Serial.println("");
Serial.println("Fade OFF");
while(ledR > 0) //红色LED的亮度参数降至0
{
ledR--;analogWrite (rLedPin, ledR);
Serial.print("ledR = ");Serial.println(ledR);
}
while(ledB > 0) //蓝色LED的亮度参数降至0
{
ledB--;analogWrite (bLedPin, ledB);
Serial.print("ledB = ");Serial.println(ledB);
}
while(ledG > 0) //绿色LED的亮度参数降至0
{
ledG--;analogWrite (gLedPin, ledG);
Serial.print("ledG = ");Serial.println(ledG);
}
colorIndex = 0; //颜色序列号重置为0
}