IoT-For-Beginners 第 3 课:用传感器与执行器构建一个会感知光线的智能夜灯
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
本篇技术指南以IoT-For-Beginners课程第 3 课(1-getting-started/lessons/3-sensors-and-actuators/README.md)为骨架,系统讲解 IoT 设备的两类核心外设——传感器(Sensors)与执行器(Actuators):从概念原理(模拟/数字信号、ADC/DAC、PWM 脉宽调制),到 Raspberry Pi、Wio Terminal、虚拟设备三种硬件平台的完整接线与编程实战,最终亲手搭建一个"光线不足自动点亮 LED"的智能夜灯。读完你将掌握传感器/执行器的分类判断、GPIO 引脚读写方法,以及同一套逻辑在 Python 与 C++(PlatformIO/Arduino)两套技术栈中的落地写法。
传感器:感知物理世界的输入设备
传感器是硬件设备,用来"感知"物理世界——即测量周围的一个或多个属性,并把信息发送给 IoT 设备。从空气温度这样的自然属性,到运动这样的物理交互,可测量的对象极其庞杂,因此传感器种类繁多。课程中列举了几类常见传感器:
- 温度传感器:测量空气温度或其所浸入介质的温度。面向爱好者和开发者的产品常把温度、气压、湿度集成到同一个传感器中;
- 按钮:感知何时被按下;
- 光传感器:检测光照强度,可针对特定颜色、紫外线、红外线或普通可见光;
- 摄像头:通过拍照或视频流感知世界的视觉表征;
- 加速度计:在多个方向上感知运动;
- 麦克风:感知声音,可以是总体声级,也可以是定向声音。
所有传感器有一个共同点:它们把感知到的物理量转换为 IoT 设备可以解释的电信号。至于这个电信号如何被解释,则取决于传感器本身,以及它与 IoT 设备通信所用的协议。
传感器类型:模拟与数字
传感器分为**模拟(Analog)与数字(Digital)**两大类,区分关键在于输出信号的形态。
模拟传感器:电压即数据
最基础的一类传感器是模拟传感器。IoT 设备向传感器提供电压,传感器内部元件调整该电压,然后设备测量返回的电压值作为传感值。电压是衡量"推动电从一处流向另一处"有多大的物理量——例如一节标准 AA 电池是 1.5V(V 是伏特的符号),它能把 1.5V 的推力从正极送到负极;不同硬件需要不同电压,比如 LED 在 2~3V 即可点亮,而 100W 白炽灯泡需要 240V。
一个典型例子是电位器(Potentiometer)——一个可在两个位置之间旋转的旋钮,传感器测量旋转量。假设设备向电位器发送 5V:当旋钮在完全关闭位置(0)时,输出 0V;当旋钮在完全打开位置(11)时,输出 5V;中间位置则输出介于两者之间的电压(如下图中点输出约 3.8V)。
传感器输出的电压被 IoT 设备读取后,设备即可据此响应。这个电压可能是任意值,也可能映射到标准单位——例如基于热敏电阻(thermistor)的模拟温度传感器,其电阻随温度变化,输出电先后可在代码中换算成开尔文(K),进而转为 °C 或 °F。需要注意:课程明确提示不要测试"传感器返回电压高于发送电压"(例如来自外部电源)的情况,这属于硬件安全边界。
模数转换(ADC):数字世界如何读懂模拟值
IoT 设备是数字设备——只能处理 0 和 1,无法直接处理模拟值。因此模拟传感器的值需要先转换为数字信号。许多 IoT 设备自带模数转换器(ADC,Analog-to-Digital Converter),传感器也可以通过连接板上的 ADC 工作。例如在 Seeed Grove 生态中,Raspberry Pi 上的模拟传感器接入一个"帽子(hat)"的特定端口(hat 通过 GPIO 引脚架在 Pi 上),该 hat 内置 ADC,把电压转换为数字信号后通过 GPIO 引脚送出。
课程给出了一个非常具体的换算例子:假设一个模拟光传感器接在 3.3V 的 IoT 设备上,返回 1V。这个 1V 在数字世界没有意义,需要按设备和传感器的量程换算——Seeed Grove 光传感器输出 0~1023 的数值;对于运行在 3.3V 下的该传感器,1V 输出对应数值300。IoT 设备无法直接处理"模拟值 300",因此 Grove hat 会把它转换为 300 的二进制表示0000000100101100,再交给设备处理。
从编程视角看,这一切通常由传感器附带的库完成,开发者无需亲自处理换算:Grove 光传感器在 Python 里调用light属性、在 Arduino 里调用analogRead,都能直接得到 300 这样的整数值。
数字传感器:直接输出 0 和 1
数字传感器同样通过电压变化感知世界,区别在于它们输出数字信号——要么只测量两种状态,要么内置 ADC 完成转换。数字传感器正变得越来越普遍,正是为了免去在连接板或设备上额外使用 ADC 的麻烦。
最简单的数字传感器是按钮/开关:只有开、关两种状态的传感器。IoT 设备上的 GPIO 引脚可以直接把信号读成 0 或 1:如果返回电压与发送电压相同则读为 1,否则读为 0,无需转换。电压永远不会绝对精确(传感器元件总有电阻),所以存在容差——例如 Raspberry Pi 的 GPIO 工作在 3.3V,把高于 1.8V 的返回信号读为 1,低于 1.8V 读为 0。按钮的两种情形一目了然:
- 3.3V 进入按钮,按钮断开,输出 0V → 读为 0;
- 3.3V 进入按钮,按钮闭合,输出 3.3V → 读为 1。
更高级的数字传感器会先读取模拟值,再用板载 ADC转换为数字信号。例如数字温度传感器与模拟传感器一样使用热电偶、一样测量当前温度下热电偶电阻导致的电压变化,但不同的是:它不再返回模拟值依赖设备或连接板转换,而是由传感器内置的 ADC 完成转换,再把一串 0 和 1 发送给 IoT 设备——发送方式与按钮的数字信号一致,1 代表满电压、0 代表 0V。
输出数字数据让传感器可以承载更复杂、更详细的数据,甚至是加密数据以保障安全。典型例子是摄像头:它捕获图像并以 JPEG 等压缩格式的数字数据发送给 IoT 设备,还能逐帧发送完整图像或压缩视频流来实现视频直播。
执行器:把电信号变成物理动作
执行器与传感器恰好相反——把 IoT 设备的电信号转换为与物理世界的交互,例如发光、发声、驱动电机。常见执行器包括:
- LED:通电时发光;
- 扬声器:根据收到的信号发声,从基础蜂鸣器到能播放音乐的音频扬声器;
- 步进电机:把信号转换为精确的旋转量,例如把旋钮转过 90°;
- 继电器:可用电信号通断的开关,允许 IoT 设备的小电压去控制更大的电压;
- 屏幕:更复杂的执行器,在多段显示器上展示信息,从简单 LED 显示屏到高分辨率视频监视器。
执行器类型:模拟与数字
与传感器类似,执行器也分模拟、数字两类。
模拟执行器:电压决定交互强度
模拟执行器接收模拟信号并转换为某种交互,交互强度随供电电压变化。最直观的例子是可调光灯具:供给灯的电压越高,灯越亮。
与传感器侧同理,IoT 设备本身只输出数字信号,因此要发送模拟信号,设备需要数模转换器(DAC,Digital to Analog Converter)——可以内置于设备,也可以在连接板上。DAC 把设备的 0 和 1 转换为执行器可用的模拟电压。课程同样提示:不要测试"设备发送的电压高于执行器承受能力"的情形。
脉宽调制(PWM):用数字脉冲模拟模拟信号
除 DAC 外,把数字信号转成模拟信号的另一个方案是脉宽调制(PWM,Pulse-Width Modulation)——发送大量极短的数字脉冲,让整体效果表现得像模拟信号。课程用电机调速完整演示了 PWM 的数学原理:
150 RPM 场景:用一个 5V 电源控制电机。发送 0.02 秒的 5V 高电平脉冲,电机可转 1/10 圈(36°);再发送 0.02 秒的 0V 低电平,一个"开-关"周期 0.04 秒,循环往复。这样每秒有 25 个 5V 脉冲(各转 1/10 圈),电机每秒转 2.5 圈,即 150 转/分钟(rpm):
25 pulses per second x 0.1 rotations per pulse = 2.5 rotations per second 2.5 rotations per second x 60 seconds in a minute = 150rpm当 PWM 信号一半时间开、一半时间关时,称为50% 占空比(duty cycle)——占空比即信号处于"开"状态的时间百分比。
75 RPM 场景:保持 0.04 秒的周期不变,把高电平脉冲减半到 0.01 秒、低电平增加到 0.03 秒。每秒仍是 25 个脉冲,但每个脉冲只转 1/20 圈,于是电机每秒转 1.25 圈,即 75rpm——只靠改变数字脉冲的宽度,就把模拟电机速度减半了:
25 pulses per second x 0.05 rotations per pulse = 1.25 rotations per second 1.25 rotations per second x 60 seconds in a minute = 75rpm可以思考:如何让电机在低速时保持旋转平滑?是用少量长脉冲加长停顿,还是大量极短脉冲加极短停顿?另外值得一提的是,某些传感器也利用 PWM 把模拟信号转换为数字信号。
数字执行器:两态控制与内置 DAC
数字执行器要么像数字传感器一样由高低电压控制两种状态,要么内置 DAC 可以把数字信号转换为模拟信号。
最简单的数字执行器是LED:设备发送数字 1 时输出高电压点亮 LED,发送数字 0 时电压降至 0V、LED 熄灭。还能想到哪些简单的两态执行器?例如螺线管(solenoid)——一种电磁铁,通电后可以拨动门闩实现门锁的开/关。
更高级的数字执行器(如屏幕)要求按特定格式发送数字数据,通常自带库,让开发者更容易发送正确的控制数据。
实战:构建一个智能夜灯
本课的实战目标是把理论与实践结合:读取光传感器采集的光线强度,当光线过低时驱动 LED 执行器点亮——一个典型的"传感器 → 决策 → 执行器"闭环。课程为三种硬件平台提供了完整的分步指南与配套源码:
| 硬件平台 | 传感器接入指南 | 执行器接入指南 | 配套源码目录 |
|---|---|---|---|
| Arduino — Wio Terminal | wio-terminal-sensor.md | wio-terminal-actuator.md | code-sensor/wio-terminal、code-actuator/wio-terminal |
| 单板计算机 — Raspberry Pi | pi-sensor.md | pi-actuator.md | code-sensor/pi、code-actuator/pi |
| 单板计算机 — 虚拟设备(CounterFit) | virtual-device-sensor.md | virtual-device-actuator.md | code-sensor/virtual-device、code-actuator/virtual-device |
夜灯的整体工作流程可概括为下图:循环读取光照值 → 判断是否低于阈值 → 控制 LED 开/关。
方案一:Raspberry Pi + Grove 光传感器与 LED
硬件接线(传感器侧):Grove 光传感器基于光电二极管(photodiode),把光转换为电信号,输出 0~1023 的整数表示相对光照量(不对应 lux 等标准单位)。将 Grove 线缆一端插入传感器模块(只有一种方向),另一端在 Pi断电状态下接到 Grove Base hat 上的模拟插座A0(GPIO 引脚旁边那一排,从右数第二个)。
传感器编程(完整代码见 code-sensor/pi/nightlight/app.py):
import time from grove.grove_light_sensor_v1_2 import GroveLightSensor light_sensor = GroveLightSensor(0) while True: light = light_sensor.light print('Light level:', light) time.sleep(1)GroveLightSensor(0)创建连接A0模拟引脚的实例;light属性读取 0~1023 的模拟值并打印;- 末尾
time.sleep(1)降低轮询频率、减少设备功耗。
在 Pi 上执行python3 app.py后,遮挡/移开传感器即可看到数值变化:
pi@raspberrypi:~/nightlight $ python3 app.py Light level: 634 Light level: 634 Light level: 230 Light level: 104 Light level: 290硬件接线(执行器侧):Grove LED 模块带有多色 LED 可选。注意 LED 是二极管,只能单向导通,需把较圆一侧的引脚接入模块外侧标+的插孔、较扁一侧(负极)接入靠近模块中心的插孔;用十字螺丝刀逆时针把亮度旋钮拧到最大;然后同样在断电状态下把 Grove 线缆另一端接到 Base hat 的数字插座D5(GPIO 引脚旁那一排,从左数第二个)。
夜灯逻辑(伪代码):
Check the light level. If the light is less than 300 Turn the LED on Otherwise Turn the LED off夜灯编程(完整代码见 code-actuator/pi/nightlight/app.py):
import time from grove.grove_light_sensor_v1_2 import GroveLightSensor from grove.grove_led import GroveLed light_sensor = GroveLightSensor(0) led = GroveLed(5) while True: light = light_sensor.light print('Light level:', light) if light < 300: led.on() else: led.off() time.sleep(1)关键点:
GroveLed(5)创建连接D5数字引脚的实例;- 阈值300:
light < 300时调用led.on()发送数字 1 点亮 LED,否则led.off()发送数字 0 熄灭; - 知识卡片:Grove Base hat 上所有插座引脚号唯一——引脚 0、2、4、6 是模拟引脚;5、16、18、22、24、26 是数字引脚;向执行器发送数字值时,0 对应 0V,1 对应设备最大电压(Pi + Grove 场景下为 3.3V);
if/else必须与print('Light level:', light)同级缩进,确保位于while循环内。
运行后遮挡光传感器,LED 会在光照 ≤300 时点亮、>300 时熄灭。若 LED 不亮,检查是否接反方向、亮度旋钮是否开到最大。
方案二:Wio Terminal(Arduino + PlatformIO)
Wio Terminal 的光传感器内置于机身,透过背面的透明塑料窗口可见(见 wio-terminal-sensor.md 中的images/wio-light-sensor.png)。由于课程此前用 PlatformIO 创建了 nightlight 工程,传感器编程只需两步:
在setup函数末尾配置引脚(传感器接入):
pinMode(WIO_LIGHT, INPUT);WIO_LIGHT是连接板载光传感器的 GPIO 引脚号,设为INPUT表示从该引脚读取数据。随后清空loop函数并写入:
int light = analogRead(WIO_LIGHT); Serial.print("Light value: "); Serial.println(light); delay(1000);analogRead从板载光传感器读取 0~1023 的模拟值,通过串口打印;delay(1000)与 Python 版的time.sleep(1)作用相同。重新连接 Wio Terminal 上传代码后,打开 Serial Monitor 即可看到数值随遮挡变化。
执行器侧:Grove LED 模块接入 Wio Terminal 右侧 Grove 插座(面对屏幕时远离电源键的那个;该插座支持模拟/数字,左侧插座仅支持 I²C 与数字设备)。在setup末尾配置:
pinMode(D0, OUTPUT);D0是右侧 Grove 插座的数字引脚,设为OUTPUT表示向引脚写入数据以驱动执行器。在loop的delay之前加入控制逻辑(完整实现见 code-actuator/wio-terminal/nightlight/src/main.cpp):
if (light < 300) { digitalWrite(D0, HIGH); } else { digitalWrite(D0, LOW); }digitalWrite(D0, HIGH)发送 1 点亮 LED,LOW发送 0 熄灭。知识卡片:发送数字值时 LOW 对应 0V、HIGH 对应设备最大电压(Wio Terminal 为 3.3V)。完整工程的setup会先以Serial.begin(9600)初始化串口并等待就绪,PlatformIO 工程配置见 platformio.ini(env:seeed_wio_terminal,platform = atmelsam、board = seeed_wio_terminal、framework = arduino)。
方案三:虚拟设备(CounterFit)
没有实体硬件也没关系——课程提供基于CounterFit的虚拟 IoT 硬件方案。在 CounterFit Web 应用中创建虚拟光传感器:Sensors面板的Create sensor中选择Light类型,Units保持NoUnits,Pin设为0,点击Add(见 virtual-device-sensor.md 中的images/counterfit-create-light-sensor.png与images/counterfit-light-sensor.png)。
Python 代码与 Pi 版几乎一致,只是导入来自 CounterFit 的 shim 库:
import time from counterfit_connection import CounterFitConnection from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor CounterFitConnection.init('127.0.0.1', 5000) light_sensor = GroveLightSensor(0) while True: light = light_sensor.light print('Light level:', light) time.sleep(1)CounterFitConnection.init('127.0.0.1', 5000)把虚拟设备连接到本机 5000 端口的 CounterFit 服务。运行后初始数值为 0,可在 CounterFit 中通过两种方式改变读取值:
- 在Value框输入数字并点Set,该数字即传感器返回值;
- 勾选Random复选框并设置Min/Max后点Set,每次读取都会得到区间内的随机数。
虚拟 LED:在Actuators面板的Create actuator中选择LED、Pin设为5、点Add;创建后还可用Color取色器修改 LED 颜色(见 virtual-device-actuator.md 中的images/counterfit-create-led.png与images/counterfit-led.png)。完整夜灯代码见 code-actuator/virtual-device/nightlight/app.py,逻辑与 Pi 版完全一致:GroveLed(5)绑定引脚 5,光照 < 300 时led.on()、否则led.off()。运行后通过调节 CounterFit 中光传感器的Value/Random设置跨越 300 阈值,即可看到虚拟 LED 亮灭切换。
小结:三种平台、同一套心智模型
把三个方案放在一起对比,可以发现底层逻辑完全同构:
| 环节 | Raspberry Pi (Python) | Wio Terminal (C++) | 虚拟设备 (Python) |
|---|---|---|---|
| 传感器初始化 | GroveLightSensor(0) | pinMode(WIO_LIGHT, INPUT)+analogRead(WIO_LIGHT) | GroveLightSensor(0)(CounterFit shim) |
| 读取光照 | light_sensor.light | analogRead(WIO_LIGHT) | light_sensor.light |
| 执行器初始化 | GroveLed(5) | pinMode(D0, OUTPUT) | GroveLed(5)(CounterFit shim) |
| 控制 LED | led.on()/led.off() | digitalWrite(D0, HIGH/LOW) | led.on()/led.off() |
| 节流 | time.sleep(1) | delay(1000) | time.sleep(1) |
延伸挑战与作业
前两课的挑战是盘点家里、学校或工作场所的 IoT 设备并判断其基于微控制器还是单板计算机;本课的挑战在此基础上更进一步:为每台设备列出它们连接的传感器和执行器,并说明每个传感器/执行器在该设备上的用途。
课程还配有正式作业 assignment.md(研究传感器与执行器)以及课前后测验,适合作为复习自检。如需更深入地补充电学与电路、各类温度传感器、LED 工作原理的背景知识,可参考课程文末的 Review & Self Study 建议。下一课将在此基础上,把夜灯的数据接入互联网,进入"连接互联网"主题。
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考