实战课题

第 27 课:红外感应灯

手一凑近就亮,拿开等几秒才灭——一个装在纸皮底座上的近场感应灯

我是吴老师。这节课的第一个字,是"泼冷水"。

上一课结尾我打过预防针,现在正式说:

这个红外感应灯,做不到"人走过来就自动亮"。

我知道你脑子里的画面是什么——你走进房间,灯"啪"地亮了;你一走,灯灭了。 商场、酒店、楼道,都是这个效果。

但你手上这个模块做不到。 不是代码写得不好,不是接线不对,是它的物理原理就不支持。

第 14 课我把这件事掰开讲过:它是红外反射式的——发一束红外光出去,看有没有光弹回来。它的有效距离只有几厘米。

红外传感器发射红外光,光打到物体上反射回来被接收头收到,就输出一个电平

几厘米是什么概念? 你把手掌摊开,从远处慢慢往模块跟前凑——凑到大概一拳的距离,它才"看见"你。

对比图:红外反射模块只能感知几厘米内有没有东西,而商场自动门用的人体红外能感知几米外的人,两者不是一回事

看这张图,两行对比一下:

红外反射(你手上这个) 人体红外 PIR(商场那个)
靠什么工作 发一束光,看反不反射 感受人体发出的热量
能探多远 几厘米 几米
分得清人吗 分不清,纸箱、书本、你的手都一样 分得出(人体温度和墙不一样)

看第二行那个差距。 差了一百倍。

所以这一课,我们先认清这个模块的"能力边界",再决定拿它做什么。

第 14 课结尾我说过:新手最容易犯的错,不是写错代码,是选错了零件,然后花一整天去调试一个物理上就不可能实现的东西。

把你想做的效果,跟你手里的零件对一遍——这是这个课题教你的第一件事。

一、那它能做什么

它能做"手一凑近就亮"的灯。

想想这个场景你熟不熟:

  • 晚上回家,摸黑找钥匙孔 / 玄关柜上的开关
  • 半夜起来,想看一下床头柜上的药盒,又不想开大灯
  • 书桌下面太暗,手伸过去就有一盏灯亮
  • 衣柜门一开,里面的灯亮

共同点是什么? 都是**"手边几十厘米以内"**的事。这正是红外模块的地盘。

我们做的东西长这样:一个纸皮底座上立着红外模块,旁边一颗 LED。你把手往模块前面一伸(大概 5 厘米),灯亮;手拿开,过 3 秒它自己灭。

为什么要"过 3 秒"才灭? 如果手一拿开马上灭,你找东西的时候得一直举着手——那是折磨,不是产品。

"保持 3 秒"是这个课题里最像"产品"的地方,第五节专门讲。

二、这个课题的三个环节

环节 这个课题里是什么 具体到引脚
传感器(看) 红外模块,一直在问"前面有没有东西" GPIO4数字量
判断(想) 板子比一下:看见了吗?上次看见是多久以前? if 加一个时间判断
执行器(做) LED 模块,亮或者灭 GPIO3

注意第一行那三个字:数字量。

第 14 课的重点就是这个——红外是"门卫",不是"体温计"。它只回答"有 / 没有",读它用 digitalRead,得到的是 HIGHLOW

所以它接的是 OUT

这里我得拦你一下,因为上一个课题留下的习惯会害你。

你刚做完小夜灯,手上那个光敏模块是四个针AO DO GND VCC。所以你可能下意识地在红外模块上找 AO

红外模块只有三个针:OUT GND VCC 没有 AO,也没有 DO它只有一个输出,而且那个输出是数字的。

别照搬上一个模块的接法。 拿到一个新模块,第一件事是看它板上印的是哪几个字——这是第 4 课那句"丝印是厂家随便印的,功能是芯片定死的"的实战版。

三、材料清单

套件里拿这些:

元件 数量 干什么用
ESP32-C3 主控 + 拓展板 1 大脑
红外传感器模块 1 感知前面有没有东西
LED 灯模块 1 发光
母对母杜邦线 6 根 接线

身边自己准备:

东西 干什么用
快递纸皮 做底座(三块)
剪刀 / 美工刀 裁纸皮
热熔胶枪 + 胶棒
尺子 + 铅笔 量尺寸

四、接线

先断电。 这句话我每节课都说,因为这节课第一个坑就跟"电"有关。

红外模块 接到
VCC 3.3V(看清是 3.3V 那一排,不是别处)
GND GND
OUT GPIO4(数字引脚)
LED 灯模块 接到
VCC 3.3V
GND GND
Signal GPIO3

为什么 OUTGPIO4 因为第 14 课用的就是这个脚,跟你按钮那节课是同一个脚。这不是巧合——红外和按钮是同一类东西,都是"数字输入",都是门卫。

拓展板上那几排针,插之前拿眼睛对一下丝印。 那几排挨得很近,3.3V5VGND 挤在一起,母对母的线头又都是塑料壳。这是下一个坑的伏笔,先记住这句话。

五、分步实现

第 1 步:先看它"看见"没有

最丑的版本,只有读取和打印:

C++
void setup() {
  Serial.begin(115200);
  pinMode(4, INPUT);
}

void loop() {
  Serial.println(digitalRead(4));
  delay(100);
}

烧进去,打开串口。先看模块板上那个小指示灯(第 14 课说过,检测到东西的时候它会变)。

然后把手掌从远处慢慢往模块跟前凑。

凑到某个距离,你会看到两件事同时发生

  1. 模块板上的小指示灯变了
  2. 串口里的数字也跳了

记下两件事:

  • 大概几厘米的时候它才认?(多半是 3~8 厘米)
  • "有东西"的时候数字是 1 还是 0?

第二件事必须你自己测,别背。

第 14 课我就强调过:方向以你实测为准。 我手上这块是"有东西 = 0",你那块可能一样,也可能不一样。唯一可靠的办法是拿手挡一下看数字怎么变。

下面的代码我按"有东西 = 低电平"写。如果你量出来是反的,就把 LOW 改成 HIGH 这是第 5 课那句"先测,再猜"。

第 2 步:看见了就亮

C++
void setup() {
  Serial.begin(115200);
  pinMode(4, INPUT);
  pinMode(3, OUTPUT);
}

void loop() {
  int seen = digitalRead(4);      // 1. 读

  if (seen == LOW) {              // 2. 判断(方向以你实测为准)
    digitalWrite(3, HIGH);        // 3. 亮
  } else {
    digitalWrite(3, LOW);         // 手拿开就灭
  }
}

烧进去。手凑近——灯亮;手拿开——灯立刻灭。

好玩吧? 但你先别急着做底座,试一下你就知道它有多难用了

手拿开的那一瞬间,灯就灭了。也就是说,你想照一下抽屉,得一直把手举在模块前面

这不是产品,这是个玩具。 下一步我们把它变成产品。

第 3 步:让它"记住刚才看见过"

我们要的效果是:手拿开之后,灯再亮 3 秒。

怎么做到?板子得知道"现在几点了"。

这里要用一个新东西:millis()

millis() 是"板子从开机到现在,过了多少毫秒"。

它就是一个一直在往前走的数:开机时是 0,过一秒钟变成 1000,过一分钟变成 60000。它只会变大,不会回头,也不会自己归零。

你拿它当秒表用就行。 不用 delay 是因为——delay 会让板子卡在那儿干等,等的这段时间它什么都看不见。我们要的是"一边等一边继续看"。

C++
unsigned long lastSeen = 0;      // 最后一次"看见东西"是几点

void setup() {
  Serial.begin(115200);
  pinMode(4, INPUT);
  pinMode(3, OUTPUT);
}

void loop() {
  if (digitalRead(4) == LOW) {   // 现在看见了
    lastSeen = millis();         // 那就把"此刻"记下来
  }

  // 距离上次看见,还没过 3000 毫秒(3 秒)?
  if (millis() - lastSeen < 3000) {
    digitalWrite(3, HIGH);       // 那就还亮着
  } else {
    digitalWrite(3, LOW);        // 超过 3 秒了,灭
  }
}

烧进去。手凑近——亮。手拿开——它还亮着。数三下,它灭了。

现在它像个东西了。

两个小提醒:

  1. 开机后头 3 秒它会亮一下。 因为 lastSeen 一开始是 0,板子以为"刚看见过东西"。这是正常的,别慌。
  2. 3000 这个数你自己改。 改成 1000 就是"亮 1 秒",改成 10000 就是"亮 10 秒"。找东西的话,3~5 秒比较舒服。

第 4 步:拧那个蓝色小电位器

这一步是动手,不是读代码。

模块板上有一个蓝色的小十字电位器——第 14 课讲过,它调的是接收头的灵敏度,也就是"能看多远"。

现在拿一张纸片,固定在离模块大概 5 厘米的地方。然后慢慢拧。

你会看到:

  • 往一个方向拧 → 更灵敏,5 厘米的纸片它认了,甚至 10 厘米也认
  • 往另一个方向拧 → 更迟钝,纸片得贴到 2 厘米它才认

你拧的不是一个抽象参数,你拧的是这个模块的"视力"。

拧到一个你满意的位置: 手伸过来大概 5 厘米就亮,但旁边桌上放个杯子它不会乱触发。

第 14 课结尾我提醒过一句:拧完记得拧回去,别拧到极端。

为什么?因为拧到极端(要么太灵要么太钝),后面的实验会莫名其妙地不工作,而你已经忘了是你自己拧的。

今天这盏灯,你拧完就别再动了。 拿铅笔在电位器旁边画一个小记号,记下你定在哪——这样下次它"失灵"了,你能一眼看出来是不是被碰过了。

这跟第 11 课的标定、第 14 课的拧电位器,是同一件事:参数得在你的实际环境里调出来,抄不来。

第 5 步:调成你自己的

到这一步,这个作品就基本成型了。你有两个参数可以调:

参数 在哪 效果
灵敏度 板上那个蓝色电位器(手动拧) 手要凑多近才触发
保持时间 代码里的 3000 手拿开之后还亮几秒

两个都试一遍,找到你最舒服的组合。 这个"调到自己满意"的过程,就是这个作品真正变成"你的"的过程。

六、组装:纸皮底座

纸皮底座的做法:裁底板、立板、三角撑三块纸皮,用热熔胶粘成 L 形支架,红外模块粘在立板前面,两个眼睛朝外

目标:让模块"脸朝前"立起来,前面还不能有东西挡着。

第 1 步,裁三块纸皮:

编号 尺寸 干什么
10cm × 8cm 底板(放桌上)
8cm × 6cm 立板(模块粘它前面)
4cm × 6cm 三角撑(防止往后倒)

第 2 步,先把三块摆一摆。 别急着上胶——摆一下确认尺寸对得上,纸皮剪短了可补不回来。

第 3 步,粘立板。 把②竖着放在①靠后的位置,底下挤一条热熔胶,按住十几秒。

按住的时候别松手。 热熔胶十几秒就凝,但没凝之前它会滑。你一松手,立板就歪了,干了之后掰不回来(一掰纸皮就毛)。

第 4 步,粘三角撑。 把③竖着贴在立板后面,两条直角边各挤一点胶——一条粘在立板背面,一条粘在底板上。

这一块是整个底座最关键的零件。 没有它,你手一伸过去,模块往后一倒。三角形是最稳的形状(下一课搭冰棒棍支架还要用这个道理)。

第 5 步,粘模块。 在立板朝前那一面挤一小坨胶,把红外模块按上去,两个"眼睛"朝外

胶点在模块板子的边缘,尤其别糊到那个透明的发射管——它被胶盖住,光就发不出去了。

模块也别粘得太靠下。 粘在立板上半部分,让它的"眼睛"离桌面高一点,免得桌面反光把它晃到(第 14 课讲过,环境光会干扰它)。

第 6 步,等胶干,再通电。 线从底座后面绕出去,插到板子上。做完放到桌上,手一伸——亮了。

这时候你再回头看那句"它做不到人走过来自动亮",是不是就不难受了?

它做到的是另一件事:手边几十厘米的"我看得见你"。 这件事,商场那个 PIR 反而做不到——PIR 站远了才灵,凑近了它反而瞎。

七、排查:如果它一动不动

这一段是这节课最值钱的部分。

你做完了,通电了,结果模块完全没反应:板上的小指示灯不动,串口里的数字一直是同一个值,手凑上去它也不理你。

先别改代码。 代码你刚才跑通过,它大概率没问题。

按下面这个顺序来。

第一步:先看那个小指示灯

凑近看模块板上那个小指示灯。

注意:第 14 课提醒过,不同批次的板子,灯的数量和亮灭规律可能不一样。 如果你那块板上压根没有小灯,直接跳到第二步。

但"完全不亮、完全不动"这件事本身是线索——这个小灯是模块自己驱动的,它不亮,多半说明模块压根没吃到电。

第二步:量模块的电源

万用表拨直流电压档V⎓)。先短接两支表笔确认表是好的。

  • 黑表笔搭拓展板上的 GND
  • 红表笔红外模块上的 VCC

读数是多少?

  • 3.3V 左右 → 模块有电,问题不在供电,去查信号线(跳到后面"如果不是电源"那段)
  • 0V模块确实没吃到电。往下走。

第三步:顺着这根线往上找

红表笔挪到 VCC 这根线的另一头——插在拓展板上的那一端。注意扎在金属上,不是塑料壳上(母对母的线头是个塑料方块,你得把表笔尖从侧面伸进去顶到里面的金属)。

读数是多少?

  • 还是 0V → 问题不在线上,在它插的那个位置。再量一下拓展板上标着 3.3V 的那一排针:如果是 3.3V,那板子的电是好的,问题就锁定在"这根线插在哪儿"。
  • 如果是 3.3V → 那这根线是好的,问题在模块这头的接触(拔下来重插一次)

第四步:用通断档,把"线坏了"和"插错了"分开

万用表拨通断档)))),先短接表笔听一声"嘀"确认表是好的

一支表笔搭这根线在模块那一头,另一支搭它在拓展板上那一头。

听到"嘀"了。 说明这根线本身是好的——内部没断。

好,那就只剩一个可能:它插的那个位置不对。

第五步:看一眼它到底插在哪

眼睛顺着这根线看,它插进了拓展板的哪一排。

你会看到——它插在标着 GND 的那一排里。

找到了。

根因

VCC 那根线的另一头,插到了 GND 那一排。

模块的电源和地都接到了"地"上——它等于没接电源。 而所有线看上去都插得好好的,没有一根是悬空的,这就是它骗过你的地方。

拔出来,看清丝印,插回 3.3V。通电——小灯亮了,手一凑,串口数字就跳了。

这个坑,是我在这个课题里故意留的。

为什么选它?因为第 4 课那张表你可能扫过一眼就过了——拓展板上 3.3V5VGND 三排针挤在一起,母对母的线头又都是塑料方块。 那节课我说过"接线前先找 GND 认准地"。

今天你就是那个没认准的人。 不丢人——丢人的是量都不量,直接换个模块试。

再捋一遍这套排查顺序,它比这个具体的坑重要一百倍:

顺序 干什么 想确认什么
1 看模块上的指示灯 模块自己觉得它有电吗
2 量模块 VCCGND 模块到底有没有电
3 量这根线的另一头 电有没有送到线的这一端
4 量板子上真正的 3.3V 电源本身是好的吗
5 通断档量这根线的两头 是线坏了,还是插错了
6 看它插在哪个孔里 定位到根因

注意第 5 步。

"线内部断了"和"线插错位置了",症状一模一样,都是"没反应"。 但这两个的修法完全不同:一个是换线,一个是拔出来重插。

通断档那一"嘀",就是把它俩分开的那一刀。 这也是为什么第 4 课第一件事就是教你们验线——手上有一根坏线,会让后面每一次排查都多绕一圈。

自测

单选题为什么这个红外模块做不了"人走到门口自动开灯"?
单选题手拿开之后灯马上就灭,用起来很别扭。该怎么让它多亮几秒?
单选题模块板上的小指示灯不亮,串口读数也一直不变。用万用表查,下面哪个顺序最合理?
问答题这个红外感应灯的闭环是哪三个环节?和一个"手一直放着才亮"的版本比,加了 millis() 之后的版本好在哪?
拓展手机接电话的时候,你一贴到耳朵边,屏幕就自己黑了。是哪个传感器在干活?它跟这个红外模块是什么关系?

这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。

提示:搜"手机 距离传感器 proximity sensor 原理"。想一想:手机屏幕那么大,为什么这个传感器只要管"几厘米"就够了?它和我们的红外模块,是不是同一类东西?

拓展那如果想做真正的"人走过来自动亮",该怎么办?

这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。

提示:搜"HC-SR501 人体红外 PIR 模块 接线"。想一想:PIR 能探几米,但它有个脾气——人站着不动它就不认了,为什么?(提示:它感受的是"热量变化"。)那商场里的自动门,是怎么做到人站着不动门也不关的?

本节你要动手做的事

  • [ ] 亲手验证它的边界:手从远处慢慢凑近,记下它几厘米才认你——这个数你要记一辈子
  • [ ] 接好线:红外 VCC3.3V看清丝印)、GNDGNDOUTGPIO4,LED SignalGPIO3
  • [ ] 跑通"看见就亮",顺便确认方向:是有东西为 LOW 还是 HIGH
  • [ ] 加上 millis() 保持 3 秒,自己改数值,找到最舒服的那个
  • [ ] 拧电位器:固定一张纸片在 5 厘米处,左右拧,亲眼看探测距离被拉长、缩短,然后用铅笔做个小记号
  • [ ] 做出底座:裁三块纸皮(10×8 / 8×6 / 4×6),粘成 L 形
  • [ ] 跑一遍第五步那个排查表,就算没坏也走一遍——手熟比什么都强
  • [ ] 做完自测题

这节课你做的是一个"只有几厘米视野"的灯。

但我希望你带走的东西,比这个灯大得多。

你先知道了它做不到什么,然后才决定拿它做什么。 这个顺序,跟大多数人是反的。多数人是先想好一个效果,然后硬往上套零件,套不上就怀疑自己——"是不是我代码写错了?"

不是你写错了。是那个方案本来就不成立。

以后每拿到一个新模块,先问自己两句话:"它到底在测什么?它的物理极限在哪?" 想清楚这两句,能省你一整天。

还有一件事——你今天第一次用了 millis() 它看起来只是"记个时间",但它背后是一个特别重要的思路:板子不用停下来等,它可以一边干活一边等。 这个思路,在你后面做联网、做多任务的时候,会反复出现。

下一课我们做倒车雷达——"嘀……嘀……嘀嘀嘀嘀嘀!" 那个声音越急、离墙越近的东西。

那一课你要同时指挥三个模块:超声波测距、蜂鸣器发声、LED 闪。而且你会遇到一个新问题——超声波读出来的数会跳,而蜂鸣器可不会替你忍着。