目录
- [用Python控制硬件:Raspberry Pi项目初体验](#用Python控制硬件:Raspberry Pi项目初体验)
-
- [1. Raspberry Pi与Python硬件编程概述](#1. Raspberry Pi与Python硬件编程概述)
-
- [1.1 Raspberry Pi简介](#1.1 Raspberry Pi简介)
- [1.2 Python在硬件控制中的优势](#1.2 Python在硬件控制中的优势)
- [2. 环境搭建与基础配置](#2. 环境搭建与基础配置)
-
- [2.1 系统安装与初始设置](#2.1 系统安装与初始设置)
- [2.2 GPIO引脚布局与电气特性](#2.2 GPIO引脚布局与电气特性)
- [3. 基础硬件控制项目](#3. 基础硬件控制项目)
-
- [3.1 LED控制与PWM调光](#3.1 LED控制与PWM调光)
- [3.2 按钮输入与中断处理](#3.2 按钮输入与中断处理)
- [4. 传感器数据采集项目](#4. 传感器数据采集项目)
-
- [4.1 温湿度传感器 (DHT11/DHT22)](#4.1 温湿度传感器 (DHT11/DHT22))
- [4.2 光敏电阻与运动传感器](#4.2 光敏电阻与运动传感器)
- [5. 完整项目:智能家居控制系统](#5. 完整项目:智能家居控制系统)
-
- [5.1 系统架构与集成](#5.1 系统架构与集成)
- [6. 完整代码结构与部署](#6. 完整代码结构与部署)
-
- [6.1 项目配置文件](#6.1 项目配置文件)
- [6.2 系统服务与自启动](#6.2 系统服务与自启动)
- [6.3 Web控制界面](#6.3 Web控制界面)
- [7. 总结与进阶学习](#7. 总结与进阶学习)
-
- [7.1 Raspberry Pi Python硬件编程要点](#7.1 Raspberry Pi Python硬件编程要点)
- [7.2 电气安全与最佳实践](#7.2 电气安全与最佳实践)
- [7.3 进阶学习路径](#7.3 进阶学习路径)
- [7.4 故障排除指南](#7.4 故障排除指南)
- [7.5 项目扩展思路](#7.5 项目扩展思路)
- [7.6 社区资源推荐](#7.6 社区资源推荐)
- [7.7 持续学习建议](#7.7 持续学习建议)
『宝藏代码胶囊开张啦!』------ 我的 CodeCapsule 来咯!✨
写代码不再头疼!我的新站点 CodeCapsule 主打一个 "白菜价"+"量身定制 "!无论是卡脖子的毕设/课设/文献复现 ,需要灵光一现的算法改进 ,还是想给项目加个"外挂",这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网
用Python控制硬件:Raspberry Pi项目初体验
1. Raspberry Pi与Python硬件编程概述
1.1 Raspberry Pi简介
Raspberry Pi(树莓派)是一款信用卡大小的单板计算机,由英国树莓派基金会开发,旨在促进基础计算机科学教育。自2012年问世以来,它已成为创客、教育工作者和物联网开发者的首选平台。
树莓派的核心优势:
- 低成本:价格亲民,适合大规模部署
- 开源生态:基于Linux系统,软件资源丰富
- GPIO接口:40针通用输入输出引脚,可直接连接传感器和执行器
- 社区支持:全球活跃的开发者社区
- Python友好:原生支持Python,硬件控制库完善
1.2 Python在硬件控制中的优势
Python凭借其简洁的语法和丰富的库生态系统,成为树莓派硬件控制的首选语言:
python
# 传统C语言硬件控制 vs Python硬件控制
"""
C语言示例(复杂):
#include <wiringPi.h>
#include <stdio.h>
int main() {
wiringPiSetup();
pinMode(0, OUTPUT);
while(1) {
digitalWrite(0, HIGH);
delay(500);
digitalWrite(0, LOW);
delay(500);
}
return 0;
}
Python示例(简洁):
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)
while True:
GPIO.output(18, GPIO.HIGH)
time.sleep(0.5)
GPIO.output(18, GPIO.LOW)
time.sleep(0.5)
"""
Python硬件编程的优势公式:
开发效率 = 功能实现 代码复杂度 × 库生态丰富度 \text{开发效率} = \frac{\text{功能实现}}{\text{代码复杂度}} \times \text{库生态丰富度} 开发效率=代码复杂度功能实现×库生态丰富度
2. 环境搭建与基础配置
2.1 系统安装与初始设置
python
# scripts/setup_raspberry_pi.py
"""
树莓派环境设置脚本
"""
import subprocess
import sys
import os
from pathlib import Path
def run_command(cmd, check=True, capture_output=False):
"""运行shell命令并返回结果"""
print(f"执行命令: {cmd}")
result = subprocess.run(cmd, shell=True, capture_output=capture_output, text=True)
if check and result.returncode != 0:
print(f"错误: {result.stderr}")
return False
return True
def check_system_requirements():
"""检查系统要求"""
try:
# 检查是否在树莓派上运行
with open('/proc/device-tree/model', 'r') as f:
model = f.read()
if 'Raspberry Pi' not in model:
print("⚠️ 警告: 可能未在树莓派上运行")
else:
print(f"✅ 检测到: {model.strip()}")
# 检查Python版本
version = sys.version_info
print(f"Python版本: {version.major}.{version.minor}.{version.micro}")
if version.major < 3 or (version.major == 3 and version.minor < 7):
print("❌ 需要Python 3.7或更高版本")
return False
return True
except Exception as e:
print(f"❌ 系统检查失败: {e}")
return False
def install_required_packages():
"""安装必要的软件包"""
packages = [
"python3-pip",
"python3-venv",
"git",
"build-essential",
"python3-dev",
"i2c-tools",
"spi-tools"
]
print("更新软件包列表...")
if not run_command("sudo apt update"):
return False
for package in packages:
print(f"安装 {package}...")
if not run_command(f"sudo apt install -y {package}"):
print(f"❌ 安装 {package} 失败")
return False
return True
def install_python_libraries():
"""安装Python库"""
libraries = [
"RPi.GPIO==0.7.1",
"gpiozero==1.6.2",
"picamera==1.13",
"adafruit-blinka==8.23.0",
"adafruit-circuitpython-bme280==2.5.10",
"adafruit-circuitpython-dht==3.7.4",
"smbus2==0.4.3",
"spidev==3.6",
"pillow==10.0.1",
"numpy==1.24.3",
"opencv-python==4.8.1.78",
"pyserial==3.5"
]
for lib in libraries:
print(f"安装 {lib}...")
if not run_command(f"pip3 install {lib}"):
print(f"❌ 安装 {lib} 失败")
return False
return True
def enable_interfaces():
"""启用硬件接口"""
interfaces = [
("Interface Options", "I2C", "yes"),
("Interface Options", "SPI", "yes"),
("Interface Options", "Serial Port", "no"), # 禁用登录shell,启用串口
("Interface Options", "Serial Console", "no"),
("Interface Options", "Camera", "yes"),
("Interface Options", "SSH", "yes"),
("Interface Options", "VNC", "yes")
]
print("启用硬件接口...")
for category, interface, enabled in interfaces:
cmd = f"sudo raspi-config nonint {interface.lower().replace(' ', '_')} {'1' if enabled == 'yes' else '0'}"
if not run_command(cmd):
print(f"❌ 启用 {interface} 失败")
return True
def setup_project_structure():
"""设置项目目录结构"""
directories = [
"projects/led_control",
"projects/sensor_monitoring",
"projects/camera_projects",
"projects/iot_applications",
"libraries",
"docs",
"config"
]
for directory in directories:
Path(directory).mkdir(parents=True, exist_ok=True)
print(f"创建目录: {directory}")
# 创建基础配置文件
config_files = {
"config/gpio_setup.py": """
# GPIO引脚配置
LED_PINS = {
'red': 17,
'green': 27,
'blue': 22
}
BUTTON_PINS = {
'btn1': 2,
'btn2': 3
}
SENSOR_PINS = {
'dht11': 4,
'motion': 18
}
""",
"config/i2c_devices.py": """
# I2C设备地址配置
I2C_DEVICES = {
'bme280': 0x76,
'ssd1306': 0x3C,
'pcf8574': 0x20
}
"""
}
for file_path, content in config_files.items():
with open(file_path, 'w') as f:
f.write(content)
print(f"创建文件: {file_path}")
return True
def test_gpio_access():
"""测试GPIO访问权限"""
try:
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)
GPIO.output(18, GPIO.HIGH)
GPIO.cleanup()
print("✅ GPIO访问测试成功")
return True
except Exception as e:
print(f"❌ GPIO访问测试失败: {e}")
return False
def main():
"""主安装流程"""
print("🎮 Raspberry Pi Python环境设置")
print("=" * 50)
if not check_system_requirements():
return
if not install_required_packages():
print("❌ 软件包安装失败")
return
if not install_python_libraries():
print("❌ Python库安装失败")
return
if not enable_interfaces():
print("❌ 硬件接口启用失败")
return
if not setup_project_structure():
print("❌ 项目结构设置失败")
return
if not test_gpio_access():
print("❌ GPIO测试失败")
return
print("\n" + "=" * 50)
print("🎉 环境设置完成!")
print("接下来可以开始Raspberry Pi项目开发了!")
if __name__ == "__main__":
main()
2.2 GPIO引脚布局与电气特性
树莓派的40针GPIO接口提供了多种通信协议和电源引脚:
python
# libraries/gpio_map.py
"""
GPIO引脚映射和电气特性
"""
import RPi.GPIO as GPIO
from typing import Dict, List, Tuple
class GPIOMapper:
"""GPIO引脚映射器"""
# BCM编号引脚映射
BCM_MAPPING = {
# 电源引脚
2: {"type": "5V", "description": "5V电源"},
4: {"type": "5V", "description": "5V电源"},
17: {"type": "3.3V", "description": "3.3V电源"},
34: {"type": "3.3V", "description": "3.3V电源"},
# 接地引脚
6: {"type": "GND", "description": "接地"},
9: {"type": "GND", "description": "接地"},
14: {"type": "GND", "description": "接地"},
20: {"type": "GND", "description": "接地"},
25: {"type": "GND", "description": "接地"},
30: {"type": "GND", "description": "接地"},
34: {"type": "GND", "description": "接地"},
39: {"type": "GND", "description": "接地"},
# GPIO引脚
3: {"type": "GPIO", "description": "GPIO 2 (SDA1)", "pwm": False, "pull": True},
5: {"type": "GPIO", "description": "GPIO 3 (SCL1)", "pwm": False, "pull": True},
7: {"type": "GPIO", "description": "GPIO 4 (GPCLK0)", "pwm": False, "pull": True},
8: {"type": "GPIO", "description": "GPIO 14 (TXD0)", "pwm": False, "pull": False},
10: {"type": "GPIO", "description": "GPIO 15 (RXD0)", "pwm": False, "pull": False},
11: {"type": "GPIO", "description": "GPIO 17", "pwm": False, "pull": True},
12: {"type": "GPIO", "description": "GPIO 18 (PWM0)", "pwm": True, "pull": True},
13: {"type": "GPIO", "description": "GPIO 27", "pwm": False, "pull": True},
15: {"type": "GPIO", "description": "GPIO 22", "pwm": False, "pull": True},
16: {"type": "GPIO", "description": "GPIO 23", "pwm": False, "pull": True},
18: {"type": "GPIO", "description": "GPIO 24", "pwm": False, "pull": True},
19: {"type": "GPIO", "description": "GPIO 10 (MOSI)", "pwm": False, "pull": True},
21: {"type": "GPIO", "description": "GPIO 9 (MISO)", "pwm": False, "pull": True},
22: {"type": "GPIO", "description": "GPIO 25", "pwm": False, "pull": True},
23: {"type": "GPIO", "description": "GPIO 11 (SCLK)", "pwm": False, "pull": True},
24: {"type": "GPIO", "description": "GPIO 8 (CE0)", "pwm": False, "pull": True},
26: {"type": "GPIO", "description": "GPIO 7 (CE1)", "pwm": False, "pull": True},
27: {"type": "GPIO", "description": "GPIO 0 (ID_SD)", "pwm": False, "pull": True},
28: {"type": "GPIO", "description": "GPIO 1 (ID_SC)", "pwm": False, "pull": True},
29: {"type": "GPIO", "description": "GPIO 5", "pwm": False, "pull": True},
31: {"type": "GPIO", "description": "GPIO 6", "pwm": False, "pull": True},
32: {"type": "GPIO", "description": "GPIO 12 (PWM0)", "pwm": True, "pull": True},
33: {"type": "GPIO", "description": "GPIO 13 (PWM1)", "pwm": True, "pull": True},
35: {"type": "GPIO", "description": "GPIO 19 (PCM_FS)", "pwm": True, "pull": True},
36: {"type": "GPIO", "description": "GPIO 16", "pwm": False, "pull": True},
37: {"type": "GPIO", "description": "GPIO 26", "pwm": False, "pull": True},
38: {"type": "GPIO", "description": "GPIO 20 (PCM_DIN)", "pwm": False, "pull": True},
40: {"type": "GPIO", "description": "GPIO 21 (PCM_DOUT)", "pwm": False, "pull": True},
}
# 特殊功能引脚
SPECIAL_PINS = {
"I2C": {
"SDA": [3], # BCM 2
"SCL": [5] # BCM 3
},
"SPI": {
"MOSI": [19], # BCM 10
"MISO": [21], # BCM 9
"SCLK": [23], # BCM 11
"CE0": [24], # BCM 8
"CE1": [26] # BCM 7
},
"UART": {
"TXD": [8], # BCM 14
"RXD": [10] # BCM 15
},
"PWM": {
"PWM0": [12, 32], # BCM 18, 12
"PWM1": [33, 35] # BCM 13, 19
}
}
@classmethod
def get_pin_info(cls, pin: int) -> Dict:
"""获取引脚信息"""
return cls.BCM_MAPPING.get(pin, {"type": "UNKNOWN", "description": "未知引脚"})
@classmethod
def list_all_pins(cls):
"""列出所有引脚信息"""
print("Raspberry Pi GPIO引脚映射 (BCM编号):")
print("=" * 60)
for pin_num in sorted(cls.BCM_MAPPING.keys()):
info = cls.BCM_MAPPING[pin_num]
pin_type = info["type"]
description = info["description"]
if pin_type == "GPIO":
pwm_info = " (PWM)" if info.get("pwm", False) else ""
pull_info = " (上拉)" if info.get("pull", False) else ""
print(f"GPIO {pin_num:2d}: {description}{pwm_info}{pull_info}")
else:
print(f"{pin_type:5} {pin_num:2d}: {description}")
@classmethod
def validate_pin_usage(cls, pin: int, purpose: str) -> bool:
"""验证引脚用途是否合适"""
info = cls.get_pin_info(pin)
if info["type"] != "GPIO":
print(f"❌ 引脚 {pin} 不是GPIO引脚")
return False
if purpose == "PWM" and not info.get("pwm", False):
print(f"❌ 引脚 {pin} 不支持PWM")
return False
if purpose == "INPUT" and not info.get("pull", False):
print(f"⚠️ 引脚 {pin} 没有内部上拉电阻")
return True
@classmethod
def calculate_current_requirements(cls, components: List[Dict]) -> Tuple[float, float]:
"""
计算电流需求
Args:
components: 组件列表,每个组件包含 'current_ma' 和 'voltage' 字段
Returns:
(总电流mA, 总功率W)
"""
total_current_3v3 = 0.0
total_current_5v = 0.0
for comp in components:
if comp['voltage'] == 3.3:
total_current_3v3 += comp['current_ma']
elif comp['voltage'] == 5:
total_current_5v += comp['current_ma']
total_power = (total_current_3v3 * 3.3 + total_current_5v * 5.0) / 1000 # 转换为瓦特
print(f"电源需求分析:")
print(f" 3.3V总线: {total_current_3v3:.1f} mA")
print(f" 5V总线: {total_current_5v:.1f} mA")
print(f" 总功率: {total_power:.2f} W")
# 树莓派4B的电源限制
if total_current_3v3 > 500:
print("⚠️ 警告: 3.3V总线电流接近极限 (500mA)")
if total_current_5v > 2500:
print("⚠️ 警告: 5V总线电流接近极限 (2.5A)")
return total_current_3v3 + total_current_5v, total_power
def demonstrate_gpio_mapping():
"""演示GPIO映射功能"""
mapper = GPIOMapper()
# 显示所有引脚
mapper.list_all_pins()
print("\n特殊功能引脚:")
for interface, pins in mapper.SPECIAL_PINS.items():
print(f"{interface}:")
for name, pin_list in pins.items():
print(f" {name}: {pin_list}")
# 验证一些引脚用途
test_cases = [
(18, "PWM"), # 有效的PWM引脚
(17, "OUTPUT"), # 普通输出引脚
(3, "I2C"), # I2C引脚
(2, "OUTPUT") # 电源引脚(应该失败)
]
print("\n引脚验证测试:")
for pin, purpose in test_cases:
result = mapper.validate_pin_usage(pin, purpose)
print(f"引脚 {pin} 用于 {purpose}: {'✅ 通过' if result else '❌ 失败'}")
# 电流需求计算示例
components = [
{"name": "LED", "voltage": 3.3, "current_ma": 20},
{"name": "传感器", "voltage": 3.3, "current_ma": 5},
{"name": "舵机", "voltage": 5, "current_ma": 500}
]
print("\n电源需求计算:")
total_current, total_power = mapper.calculate_current_requirements(components)
print(f"总需求: {total_current:.1f} mA, {total_power:.2f} W")
if __name__ == "__main__":
demonstrate_gpio_mapping()
3. 基础硬件控制项目
3.1 LED控制与PWM调光
python
# projects/led_control/led_manager.py
"""
LED控制管理器 - 基础GPIO控制
"""
import RPi.GPIO as GPIO
import time
import threading
from typing import Dict, List, Optional
from enum import Enum
class LEDState(Enum):
"""LED状态枚举"""
OFF = 0
ON = 1
BLINKING = 2
BREATHING = 3
class LEDController:
"""LED控制器"""
def __init__(self, pin: int, frequency: int = 1000):
"""
初始化LED控制器
Args:
pin: GPIO引脚 (BCM编号)
frequency: PWM频率 (Hz)
"""
self.pin = pin
self.frequency = frequency
self.state = LEDState.OFF
self.brightness = 0 # 0-100
self.blink_thread = None
self.breath_thread = None
self.running = False
# GPIO设置
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.pin, GPIO.OUT)
# PWM初始化
self.pwm = GPIO.PWM(self.pin, self.frequency)
self.pwm.start(0) # 初始亮度为0
print(f"✅ LED控制器初始化完成 - 引脚: {self.pin}")
def set_brightness(self, brightness: int):
"""
设置LED亮度
Args:
brightness: 亮度值 (0-100)
"""
self.brightness = max(0, min(100, brightness))
duty_cycle = self.brightness
if self.state not in [LEDState.BLINKING, LEDState.BREATHING]:
self.pwm.ChangeDutyCycle(duty_cycle)
self.state = LEDState.ON if duty_cycle > 0 else LEDState.OFF
print(f"💡 LED亮度设置为: {self.brightness}%")
def on(self):
"""打开LED (100%亮度)"""
self.set_brightness(100)
self.state = LEDState.ON
def off(self):
"""关闭LED"""
self.set_brightness(0)
self.state = LEDState.OFF
def toggle(self):
"""切换LED状态"""
if self.state == LEDState.OFF:
self.on()
else:
self.off()
def blink(self, interval: float = 0.5, duration: Optional[float] = None):
"""
LED闪烁
Args:
interval: 闪烁间隔 (秒)
duration: 持续时间 (秒),None表示无限
"""
self._stop_threads()
self.state = LEDState.BLINKING
self.running = True
def blink_task():
start_time = time.time()
while self.running:
self.pwm.ChangeDutyCycle(100) # 亮
time.sleep(interval)
self.pwm.ChangeDutyCycle(0) # 灭
time.sleep(interval)
# 检查持续时间
if duration and (time.time() - start_time) >= duration:
break
self.off()
self.state = LEDState.OFF
self.blink_thread = threading.Thread(target=blink_task)
self.blink_thread.daemon = True
self.blink_thread.start()
print(f"✨ LED开始闪烁 - 间隔: {interval}s")
def breath(self, cycle_time: float = 2.0, duration: Optional[float] = None):
"""
LED呼吸效果
Args:
cycle_time: 完整呼吸周期时间 (秒)
duration: 持续时间 (秒)
"""
self._stop_threads()
self.state = LEDState.BREATHING
self.running = True
def breath_task():
start_time = time.time()
steps = 50 # 每个周期的步数
while self.running:
# 渐亮
for i in range(steps):
if not self.running:
break
brightness = int((i / steps) ** 2 * 100) # 非线性变化,更自然
self.pwm.ChangeDutyCycle(brightness)
time.sleep(cycle_time / (2 * steps))
# 渐暗
for i in range(steps, 0, -1):
if not self.running:
break
brightness = int((i / steps) ** 2 * 100)
self.pwm.ChangeDutyCycle(brightness)
time.sleep(cycle_time / (2 * steps))
# 检查持续时间
if duration and (time.time() - start_time) >= duration:
break
self.off()
self.state = LEDState.OFF
self.breath_thread = threading.Thread(target=breath_task)
self.breath_thread.daemon = True
self.breath_thread.start()
print(f"🌬️ LED开始呼吸效果 - 周期: {cycle_time}s")
def _stop_threads(self):
"""停止所有线程"""
self.running = False
if self.blink_thread and self.blink_thread.is_alive():
self.blink_thread.join(timeout=1.0)
if self.breath_thread and self.breath_thread.is_alive():
self.breath_thread.join(timeout=1.0)
def get_status(self) -> Dict:
"""获取LED状态"""
return {
"pin": self.pin,
"state": self.state.name,
"brightness": self.brightness,
"frequency": self.frequency
}
def cleanup(self):
"""清理资源"""
self._stop_threads()
self.pwm.stop()
print(f"🧹 LED控制器清理完成 - 引脚: {self.pin}")
class RGBLEDController:
"""RGB LED控制器"""
def __init__(self, red_pin: int, green_pin: int, blue_pin: int):
"""
初始化RGB LED控制器
Args:
red_pin: 红色引脚
green_pin: 绿色引脚
blue_pin: 蓝色引脚
"""
self.leds = {
'red': LEDController(red_pin),
'green': LEDController(green_pin),
'blue': LEDController(blue_pin)
}
self.current_color = (0, 0, 0)
print("🌈 RGB LED控制器初始化完成")
def set_color(self, red: int, green: int, blue: int):
"""
设置RGB颜色
Args:
red: 红色亮度 (0-100)
green: 绿色亮度 (0-100)
blue: 蓝色亮度 (0-100)
"""
self.leds['red'].set_brightness(red)
self.leds['green'].set_brightness(green)
self.leds['blue'].set_brightness(blue)
self.current_color = (red, green, blue)
print(f"🎨 RGB颜色设置为: R{red} G{green} B{blue}")
def set_color_hex(self, hex_color: str):
"""
使用十六进制颜色代码设置颜色
Args:
hex_color: 十六进制颜色代码 (如 "#FF5733")
"""
hex_color = hex_color.lstrip('#')
if len(hex_color) != 6:
raise ValueError("颜色代码必须是6位十六进制数")
red = int(hex_color[0:2], 16) * 100 // 255
green = int(hex_color[2:4], 16) * 100 // 255
blue = int(hex_color[4:6], 16) * 100 // 255
self.set_color(red, green, blue)
def color_cycle(self, colors: List[tuple], interval: float = 1.0):
"""
RGB颜色循环
Args:
colors: 颜色列表,每个颜色为 (r, g, b) 元组
interval: 颜色切换间隔 (秒)
"""
def cycle_task():
while getattr(threading.current_thread(), "do_run", True):
for color in colors:
if not getattr(threading.current_thread(), "do_run", True):
return
self.set_color(*color)
time.sleep(interval)
thread = threading.Thread(target=cycle_task)
thread.do_run = True
thread.daemon = True
thread.start()
return thread # 返回线程以便控制
def rainbow_effect(self, duration: float = 10.0):
"""
彩虹效果
Args:
duration: 效果持续时间 (秒)
"""
def rainbow_task():
start_time = time.time()
steps = 360 # 色相步数
while (time.time() - start_time) < duration:
elapsed = time.time() - start_time
progress = (elapsed / duration) % 1.0
# HSV到RGB转换
hue = progress * 360
r, g, b = self.hsv_to_rgb(hue, 1.0, 1.0)
self.set_color(int(r * 100), int(g * 100), int(b * 100))
time.sleep(0.05)
thread = threading.Thread(target=rainbow_task)
thread.daemon = True
thread.start()
return thread
@staticmethod
def hsv_to_rgb(h: float, s: float, v: float) -> tuple:
"""HSV到RGB颜色转换"""
h = h % 360
c = v * s
x = c * (1 - abs((h / 60) % 2 - 1))
m = v - c
if 0 <= h < 60:
r, g, b = c, x, 0
elif 60 <= h < 120:
r, g, b = x, c, 0
elif 120 <= h < 180:
r, g, b = 0, c, x
elif 180 <= h < 240:
r, g, b = 0, x, c
elif 240 <= h < 300:
r, g, b = x, 0, c
else:
r, g, b = c, 0, x
return (r + m, g + m, b + m)
def get_status(self) -> Dict:
"""获取RGB LED状态"""
return {
"pins": {color: led.pin for color, led in self.leds.items()},
"current_color": self.current_color,
"states": {color: led.get_status() for color, led in self.leds.items()}
}
def cleanup(self):
"""清理资源"""
for led in self.leds.values():
led.cleanup()
print("🧹 RGB LED控制器清理完成")
def demonstrate_led_control():
"""演示LED控制功能"""
try:
# 单色LED演示
print("单色LED演示:")
led = LEDController(18) # 使用GPIO18
led.on()
time.sleep(1)
led.set_brightness(50)
time.sleep(1)
led.blink(0.3, 2) # 闪烁2秒
time.sleep(2.5)
led.breath(3, 5) # 呼吸效果5秒
time.sleep(6)
led.off()
# RGB LED演示
print("\nRGB LED演示:")
rgb_led = RGBLEDController(17, 27, 22) # R:17, G:27, B:22
# 基本颜色
colors = [
(100, 0, 0), # 红
(0, 100, 0), # 绿
(0, 0, 100), # 蓝
(100, 100, 0), # 黄
(100, 0, 100), # 紫
(0, 100, 100) # 青
]
for color in colors:
rgb_led.set_color(*color)
time.sleep(1)
# 十六进制颜色
rgb_led.set_color_hex("#FF5733") # 橙色
time.sleep(1)
# 彩虹效果
print("开始彩虹效果...")
rainbow_thread = rgb_led.rainbow_effect(5)
rainbow_thread.join()
rgb_led.set_color(0, 0, 0) # 关闭
print("✅ LED控制演示完成")
except KeyboardInterrupt:
print("\n🛑 演示被用户中断")
except Exception as e:
print(f"❌ 演示错误: {e}")
finally:
# 清理GPIO
GPIO.cleanup()
print("🧹 GPIO资源已清理")
if __name__ == "__main__":
demonstrate_led_control()
3.2 按钮输入与中断处理
python
# projects/led_control/button_manager.py
"""
按钮输入管理器 - GPIO输入和中断处理
"""
import RPi.GPIO as GPIO
import time
import threading
from typing import Dict, List, Callable, Optional
from enum import Enum
class ButtonEvent(Enum):
"""按钮事件类型"""
PRESS = 1
RELEASE = 2
CLICK = 3
LONG_PRESS = 4
DOUBLE_CLICK = 5
class ButtonController:
"""按钮控制器"""
def __init__(self, pin: int, pull_up: bool = True,
bounce_time: int = 300, long_press_duration: float = 2.0):
"""
初始化按钮控制器
Args:
pin: GPIO引脚 (BCM编号)
pull_up: 是否使用上拉电阻
bounce_time: 防抖时间 (毫秒)
long_press_duration: 长按判定时间 (秒)
"""
self.pin = pin
self.pull_up = pull_up
self.bounce_time = bounce_time
self.long_press_duration = long_press_duration
# 状态变量
self.last_state = GPIO.HIGH if pull_up else GPIO.LOW
self.press_start_time = 0
self.click_count = 0
self.last_click_time = 0
# 事件回调函数
self.event_handlers = {event: [] for event in ButtonEvent}
# GPIO设置
GPIO.setmode(GPIO.BCM)
pull_resistor = GPIO.PUD_UP if pull_up else GPIO.PUD_DOWN
GPIO.setup(self.pin, GPIO.IN, pull_up_down=pull_resistor)
# 添加边缘检测
edge = GPIO.FALLING if pull_up else GPIO.RISING
GPIO.add_event_detect(self.pin, edge, callback=self._edge_callback,
bouncetime=self.bounce_time)
# 状态监测线程
self.monitoring = False
self.monitor_thread = None
print(f"✅ 按钮控制器初始化完成 - 引脚: {pin} (上拉: {pull_up})")
def add_event_handler(self, event: ButtonEvent, handler: Callable):
"""
添加事件处理器
Args:
event: 按钮事件类型
handler: 处理函数
"""
self.event_handlers[event].append(handler)
def remove_event_handler(self, event: ButtonEvent, handler: Callable):
"""移除事件处理器"""
if handler in self.event_handlers[event]:
self.event_handlers[event].remove(handler)
def _edge_callback(self, channel):
"""边缘检测回调函数"""
if channel != self.pin:
return
current_state = GPIO.input(self.pin)
# 检测按钮按下(上拉模式下低电平表示按下)
if self.pull_up and current_state == GPIO.LOW and self.last_state == GPIO.HIGH:
self._handle_press()
# 检测按钮释放
elif self.pull_up and current_state == GPIO.HIGH and self.last_state == GPIO.LOW:
self._handle_release()
self.last_state = current_state
def _handle_press(self):
"""处理按钮按下事件"""
self.press_start_time = time.time()
self._trigger_event(ButtonEvent.PRESS)
print(f"🔘 按钮按下 - 引脚: {self.pin}")
def _handle_release(self):
"""处理按钮释放事件"""
press_duration = time.time() - self.press_start_time
self._trigger_event(ButtonEvent.RELEASE)
# 判断点击类型
current_time = time.time()
if press_duration >= self.long_press_duration:
# 长按
self._trigger_event(ButtonEvent.LONG_PRESS)
self.click_count = 0
print(f"⏱️ 长按检测 - 持续时间: {press_duration:.2f}s")
else:
# 短按,检查是否双击
if current_time - self.last_click_time < 0.5: # 双击时间窗口
self.click_count += 1
if self.click_count >= 2:
self._trigger_event(ButtonEvent.DOUBLE_CLICK)
self.click_count = 0
print("👆 双击检测")
else:
self.click_count = 1
self._trigger_event(ButtonEvent.CLICK)
print("👉 单击检测")
self.last_click_time = current_time
def _trigger_event(self, event: ButtonEvent):
"""触发事件"""
for handler in self.event_handlers[event]:
try:
handler(self.pin)
except Exception as e:
print(f"❌ 事件处理器错误: {e}")
def start_monitoring(self):
"""开始状态监测"""
if self.monitoring:
return
self.monitoring = True
def monitor_task():
while self.monitoring:
current_state = GPIO.input(self.pin)
time.sleep(0.1) # 降低CPU使用率
self.monitor_thread = threading.Thread(target=monitor_task)
self.monitor_thread.daemon = True
self.monitor_thread.start()
print(f"📊 开始按钮状态监测 - 引脚: {self.pin}")
def stop_monitoring(self):
"""停止状态监测"""
self.monitoring = False
if self.monitor_thread:
self.monitor_thread.join(timeout=1.0)
print(f"🛑 停止按钮状态监测 - 引脚: {self.pin}")
def is_pressed(self) -> bool:
"""检查按钮是否被按下"""
current_state = GPIO.input(self.pin)
return current_state == (GPIO.LOW if self.pull_up else GPIO.HIGH)
def wait_for_press(self, timeout: Optional[float] = None) -> bool:
"""
等待按钮按下
Args:
timeout: 超时时间 (秒)
Returns:
是否成功检测到按下
"""
start_time = time.time()
while True:
if self.is_pressed():
return True
if timeout and (time.time() - start_time) >= timeout:
return False
time.sleep(0.01) # 小延迟避免过高CPU使用
def get_status(self) -> Dict:
"""获取按钮状态"""
return {
"pin": self.pin,
"pulled_up": self.pull_up,
"is_pressed": self.is_pressed(),
"bounce_time": self.bounce_time,
"event_handlers": {event.name: len(handlers)
for event, handlers in self.event_handlers.items()}
}
def cleanup(self):
"""清理资源"""
self.stop_monitoring()
GPIO.remove_event_detect(self.pin)
print(f"🧹 按钮控制器清理完成 - 引脚: {self.pin}")
class ButtonManager:
"""多按钮管理器"""
def __init__(self):
self.buttons = {}
self.sequences = {} # 按钮序列检测
print("🎮 按钮管理器初始化完成")
def add_button(self, name: str, pin: int, **kwargs):
"""
添加按钮
Args:
name: 按钮名称
pin: GPIO引脚
**kwargs: 其他ButtonController参数
"""
if name in self.buttons:
print(f"⚠️ 按钮 '{name}' 已存在,正在替换")
self.buttons[name].cleanup()
self.buttons[name] = ButtonController(pin, **kwargs)
print(f"✅ 添加按钮: {name} (引脚: {pin})")
def remove_button(self, name: str):
"""移除按钮"""
if name in self.buttons:
self.buttons[name].cleanup()
del self.buttons[name]
print(f"🗑️ 移除按钮: {name}")
def add_button_sequence(self, sequence_name: str, button_sequence: List[str],
timeout: float = 3.0, handler: Optional[Callable] = None):
"""
添加按钮序列检测
Args:
sequence_name: 序列名称
button_sequence: 按钮名称序列
timeout: 序列超时时间
handler: 序列完成时的处理函数
"""
self.sequences[sequence_name] = {
'sequence': button_sequence,
'timeout': timeout,
'handler': handler,
'current_index': 0,
'last_press_time': 0
}
# 为序列中的按钮添加点击处理器
for button_name in button_sequence:
if button_name in self.buttons:
self.buttons[button_name].add_event_handler(
ButtonEvent.CLICK,
lambda pin, btn=button_name, seq=sequence_name:
self._handle_sequence_press(btn, seq)
)
print(f"🔢 添加按钮序列: {sequence_name} -> {button_sequence}")
def _handle_sequence_press(self, button_name: str, sequence_name: str):
"""处理序列按钮按下"""
if sequence_name not in self.sequences:
return
seq_info = self.sequences[sequence_name]
current_time = time.time()
# 检查超时
if (seq_info['last_press_time'] > 0 and
current_time - seq_info['last_press_time'] > seq_info['timeout']):
seq_info['current_index'] = 0 # 超时重置
# 检查序列匹配
expected_button = seq_info['sequence'][seq_info['current_index']]
if button_name == expected_button:
seq_info['current_index'] += 1
seq_info['last_press_time'] = current_time
print(f"🎯 序列 '{sequence_name}' 进度: {seq_info['current_index']}/{len(seq_info['sequence'])}")
# 序列完成
if seq_info['current_index'] >= len(seq_info['sequence']):
seq_info['current_index'] = 0
if seq_info['handler']:
try:
seq_info['handler'](sequence_name)
except Exception as e:
print(f"❌ 序列处理器错误: {e}")
print(f"🎉 按钮序列 '{sequence_name}' 完成!")
else:
# 序列错误,重置
seq_info['current_index'] = 0
print(f"🔄 序列 '{sequence_name}' 重置")
def get_button(self, name: str) -> Optional[ButtonController]:
"""获取按钮控制器"""
return self.buttons.get(name)
def get_all_status(self) -> Dict:
"""获取所有按钮状态"""
return {
name: button.get_status()
for name, button in self.buttons.items()
}
def cleanup(self):
"""清理所有资源"""
for button in self.buttons.values():
button.cleanup()
self.buttons.clear()
self.sequences.clear()
print("🧹 按钮管理器清理完成")
def demonstrate_button_control():
"""演示按钮控制功能"""
try:
from led_control.led_manager import LEDController, RGBLEDController
# 初始化LED
led = LEDController(18)
rgb_led = RGBLEDController(17, 27, 22)
# 初始化按钮管理器
button_mgr = ButtonManager()
# 添加按钮
button_mgr.add_button("red_button", 2, pull_up=True) # GPIO2
button_mgr.add_button("green_button", 3, pull_up=True) # GPIO3
button_mgr.add_button("blue_button", 4, pull_up=True) # GPIO4
# 单色LED控制
def handle_red_click(pin):
led.toggle()
print(f"🔴 红色按钮点击 - LED状态: {'开' if led.state.name == 'ON' else '关'}")
def handle_green_click(pin):
if led.state.name == 'ON':
led.blink(0.2, 3)
print("🟢 绿色按钮点击 - LED开始闪烁")
def handle_blue_click(pin):
if led.state.name == 'ON':
led.breath(2, 5)
print("🔵 蓝色按钮点击 - LED开始呼吸")
# 绑定事件处理器
button_mgr.get_button("red_button").add_event_handler(ButtonEvent.CLICK, handle_red_click)
button_mgr.get_button("green_button").add_event_handler(ButtonEvent.CLICK, handle_green_click)
button_mgr.get_button("blue_button").add_event_handler(ButtonEvent.CLICK, handle_blue_click)
# RGB LED控制
def handle_red_long_press(pin):
rgb_led.set_color(100, 0, 0)
print("🔴 红色按钮长按 - RGB设为红色")
def handle_green_long_press(pin):
rgb_led.set_color(0, 100, 0)
print("🟢 绿色按钮长按 - RGB设为绿色")
def handle_blue_long_press(pin):
rgb_led.set_color(0, 0, 100)
print("🔵 蓝色按钮长按 - RGB设为蓝色")
button_mgr.get_button("red_button").add_event_handler(ButtonEvent.LONG_PRESS, handle_red_long_press)
button_mgr.get_button("green_button").add_event_handler(ButtonEvent.LONG_PRESS, handle_green_long_press)
button_mgr.get_button("blue_button").add_event_handler(ButtonEvent.LONG_PRESS, handle_blue_long_press)
# 按钮序列示例
def handle_konami_code(sequence_name):
rgb_led.rainbow_effect(5)
print("🎮 科乐美代码激活! 彩虹效果启动")
# 上上下下左右左右BA (使用现有按钮模拟)
button_mgr.add_button_sequence(
"konami_code",
["red_button", "red_button", "green_button", "green_button",
"blue_button", "blue_button", "red_button", "green_button"],
timeout=5.0,
handler=handle_konami_code
)
# 开始监测
for button in button_mgr.buttons.values():
button.start_monitoring()
print("\n🎯 按钮控制演示开始")
print("单击红色按钮: 切换LED开关")
print("单击绿色按钮: LED闪烁")
print("单击蓝色按钮: LED呼吸")
print("长按红色按钮: RGB设为红色")
print("长按绿色按钮: RGB设为绿色")
print("长按蓝色按钮: RGB设为蓝色")
print("序列: 红红绿绿蓝蓝红绿 -> 彩虹效果")
print("按Ctrl+C退出演示")
# 主循环
try:
while True:
# 显示当前状态
status = button_mgr.get_all_status()
pressed_buttons = [name for name, info in status.items() if info['is_pressed']]
if pressed_buttons:
print(f"当前按下的按钮: {pressed_buttons}")
time.sleep(0.1)
except KeyboardInterrupt:
print("\n🛑 演示被用户中断")
except Exception as e:
print(f"❌ 演示错误: {e}")
finally:
# 清理资源
if 'button_mgr' in locals():
button_mgr.cleanup()
if 'led' in locals():
led.cleanup()
if 'rgb_led' in locals():
rgb_led.cleanup()
GPIO.cleanup()
print("🧹 所有资源已清理")
if __name__ == "__main__":
demonstrate_button_control()
4. 传感器数据采集项目
4.1 温湿度传感器 (DHT11/DHT22)
python
# projects/sensor_monitoring/dht_sensor.py
"""
DHT11/DHT22温湿度传感器读取
"""
import time
import threading
from typing import Dict, Optional, Callable
import RPi.GPIO as GPIO
class DHTSensor:
"""DHT系列温湿度传感器"""
def __init__(self, pin: int, sensor_type: str = "DHT11"):
"""
初始化DHT传感器
Args:
pin: 数据引脚 (BCM编号)
sensor_type: 传感器类型 ("DHT11" 或 "DHT22")
"""
self.pin = pin
self.sensor_type = sensor_type.upper()
if self.sensor_type not in ["DHT11", "DHT22"]:
raise ValueError("传感器类型必须是 'DHT11' 或 'DHT22'")
# 传感器特性
self.sensor_specs = {
"DHT11": {
"temperature_range": (0, 50),
"humidity_range": (20, 90),
"accuracy": {"temp": "±2°C", "humi": "±5%"},
"reading_interval": 1 # 最小读取间隔(秒)
},
"DHT22": {
"temperature_range": (-40, 80),
"humidity_range": (0, 100),
"accuracy": {"temp": "±0.5°C", "humi": "±2%"},
"reading_interval": 2
}
}
# 状态变量
self.last_reading = None
self.last_read_time = 0
self.reading_interval = self.sensor_specs[self.sensor_type]["reading_interval"]
self.monitoring = False
self.monitor_thread = None
self.callbacks = []
# GPIO设置
GPIO.setmode(GPIO.BCM)
print(f"✅ {self.sensor_type}传感器初始化完成 - 引脚: {pin}")
def _read_sensor(self) -> Optional[Dict]:
"""
读取传感器数据
Returns:
包含温度和湿度的字典,读取失败返回None
"""
# 检查读取间隔
current_time = time.time()
if current_time - self.last_read_time < self.reading_interval:
time.sleep(0.1)
return self.last_reading
# 发送开始信号
GPIO.setup(self.pin, GPIO.OUT)
GPIO.output(self.pin, GPIO.LOW)
time.sleep(0.018) # 18ms低电平
# 准备读取数据
GPIO.setup(self.pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# 等待传感器响应
if not self._wait_for_state(GPIO.LOW, 100): # 等待80μs低电平
print("❌ 传感器响应超时")
return None
if not self._wait_for_state(GPIO.HIGH, 100): # 等待80μs高电平
print("❌ 传感器准备超时")
return None
# 读取40位数据
data = []
for i in range(40):
# 每个位以50μs低电平开始
if not self._wait_for_state(GPIO.LOW, 60):
print(f"❌ 数据位 {i} 开始超时")
return None
# 高电平持续时间决定位值
high_duration = self._measure_pulse(GPIO.HIGH, 100)
if high_duration is None:
print(f"❌ 数据位 {i} 读取超时")
return None
data.append(1 if high_duration > 40 else 0)
# 解析数据
humidity = self._parse_data(data[0:8])
humidity_decimal = self._parse_data(data[8:16])
temperature = self._parse_data(data[16:24])
temperature_decimal = self._parse_data(data[24:32])
checksum = self._parse_data(data[32:40])
# 验证校验和
calculated_checksum = humidity + humidity_decimal + temperature + temperature_decimal
if checksum != calculated_checksum & 0xFF:
print("❌ 校验和错误")
return None
# 计算最终值
if self.sensor_type == "DHT11":
final_humidity = humidity
final_temperature = temperature
else: # DHT22
final_humidity = (humidity * 256 + humidity_decimal) / 10.0
# 处理负数温度
if temperature & 0x80:
temperature = -((temperature & 0x7F) * 256 + temperature_decimal) / 10.0
else:
final_temperature = (temperature * 256 + temperature_decimal) / 10.0
# 数据验证
specs = self.sensor_specs[self.sensor_type]
if not (specs["temperature_range"][0] <= final_temperature <= specs["temperature_range"][1]):
print(f"❌ 温度值超出范围: {final_temperature}")
return None
if not (specs["humidity_range"][0] <= final_humidity <= specs["humidity_range"][1]):
print(f"❌ 湿度值超出范围: {final_humidity}")
return None
reading = {
"temperature": final_temperature,
"humidity": final_humidity,
"timestamp": time.time(),
"sensor_type": self.sensor_type
}
self.last_reading = reading
self.last_read_time = current_time
return reading
def _wait_for_state(self, state: int, timeout_ms: int) -> bool:
"""等待引脚达到指定状态"""
start_time = time.time()
while GPIO.input(self.pin) != state:
if (time.time() - start_time) * 1000 > timeout_ms:
return False
return True
def _measure_pulse(self, state: int, timeout_ms: int) -> Optional[float]:
"""测量脉冲持续时间(微秒)"""
if not self._wait_for_state(state, timeout_ms):
return None
start_time = time.time()
while GPIO.input(self.pin) == state:
if (time.time() - start_time) * 1000 > timeout_ms:
return None
return (time.time() - start_time) * 1000000 # 转换为微秒
def _parse_data(self, bits: list) -> int:
"""解析8位数据"""
value = 0
for bit in bits:
value = (value << 1) | bit
return value
def read(self, max_retries: int = 3) -> Optional[Dict]:
"""
读取传感器数据(带重试)
Args:
max_retries: 最大重试次数
Returns:
传感器数据字典
"""
for attempt in range(max_retries):
try:
reading = self._read_sensor()
if reading:
return reading
time.sleep(0.1)
except Exception as e:
print(f"❌ 读取尝试 {attempt + 1} 失败: {e}")
print(f"❌ 传感器读取失败,已重试 {max_retries} 次")
return None
def add_callback(self, callback: Callable):
"""添加数据回调函数"""
self.callbacks.append(callback)
def start_monitoring(self, interval: float = 5.0):
"""
开始持续监测
Args:
interval: 读取间隔(秒)
"""
if self.monitoring:
return
self.monitoring = True
def monitor_task():
while self.monitoring:
reading = self.read()
if reading and self.callbacks:
for callback in self.callbacks:
try:
callback(reading)
except Exception as e:
print(f"❌ 回调函数错误: {e}")
time.sleep(interval)
self.monitor_thread = threading.Thread(target=monitor_task)
self.monitor_thread.daemon = True
self.monitor_thread.start()
print(f"📊 开始传感器监测 - 间隔: {interval}s")
def stop_monitoring(self):
"""停止监测"""
self.monitoring = False
if self.monitor_thread:
self.monitor_thread.join(timeout=1.0)
print("🛑 停止传感器监测")
def get_sensor_info(self) -> Dict:
"""获取传感器信息"""
specs = self.sensor_specs[self.sensor_type]
return {
"sensor_type": self.sensor_type,
"gpio_pin": self.pin,
"temperature_range": specs["temperature_range"],
"humidity_range": specs["humidity_range"],
"accuracy": specs["accuracy"],
"min_reading_interval": specs["reading_interval"]
}
def cleanup(self):
"""清理资源"""
self.stop_monitoring()
print(f"🧹 {self.sensor_type}传感器清理完成")
class EnvironmentMonitor:
"""环境监测系统"""
def __init__(self):
self.sensors = {}
self.data_log = []
self.max_log_size = 1000
print("🌡️ 环境监测系统初始化完成")
def add_sensor(self, name: str, pin: int, sensor_type: str = "DHT11"):
"""添加传感器"""
if name in self.sensors:
print(f"⚠️ 传感器 '{name}' 已存在,正在替换")
self.sensors[name].cleanup()
self.sensors[name] = DHTSensor(pin, sensor_type)
# 添加数据回调
def sensor_callback(data):
self._handle_sensor_data(name, data)
self.sensors[name].add_callback(sensor_callback)
print(f"✅ 添加传感器: {name} ({sensor_type} - 引脚: {pin})")
def _handle_sensor_data(self, sensor_name: str, data: Dict):
"""处理传感器数据"""
log_entry = {
"sensor": sensor_name,
"timestamp": data["timestamp"],
"temperature": data["temperature"],
"humidity": data["humidity"]
}
self.data_log.append(log_entry)
# 限制日志大小
if len(self.data_log) > self.max_log_size:
self.data_log.pop(0)
# 打印数据
print(f"📈 {sensor_name}: {data['temperature']:.1f}°C, {data['humidity']:.1f}%")
def start_monitoring(self, interval: float = 5.0):
"""开始监测所有传感器"""
for name, sensor in self.sensors.items():
sensor.start_monitoring(interval)
print(f"🚀 开始环境监测 - 间隔: {interval}s")
def stop_monitoring(self):
"""停止监测所有传感器"""
for sensor in self.sensors.values():
sensor.stop_monitoring()
print("🛑 停止环境监测")
def get_current_readings(self) -> Dict:
"""获取当前所有传感器读数"""
readings = {}
for name, sensor in self.sensors.items():
readings[name] = sensor.read()
return readings
def get_statistics(self, sensor_name: str, data_points: int = 100) -> Dict:
"""获取传感器统计信息"""
sensor_data = [entry for entry in self.data_log if entry["sensor"] == sensor_name]
sensor_data = sensor_data[-data_points:] # 最近的数据点
if not sensor_data:
return {}
temperatures = [d["temperature"] for d in sensor_data]
humidities = [d["humidity"] for d in sensor_data]
return {
"sensor": sensor_name,
"data_points": len(sensor_data),
"temperature": {
"current": temperatures[-1],
"average": sum(temperatures) / len(temperatures),
"min": min(temperatures),
"max": max(temperatures),
"trend": self._calculate_trend(temperatures)
},
"humidity": {
"current": humidities[-1],
"average": sum(humidities) / len(humidities),
"min": min(humidities),
"max": max(humidities),
"trend": self._calculate_trend(humidities)
}
}
def _calculate_trend(self, data: list) -> str:
"""计算数据趋势"""
if len(data) < 2:
return "stable"
# 使用线性回归计算趋势
x = list(range(len(data)))
y = data
n = len(x)
sum_x = sum(x)
sum_y = sum(y)
sum_xy = sum(x[i] * y[i] for i in range(n))
sum_x2 = sum(xi * xi for xi in x)
slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x)
if slope > 0.1:
return "rising"
elif slope < -0.1:
return "falling"
else:
return "stable"
def generate_report(self) -> Dict:
"""生成监测报告"""
report = {
"timestamp": time.time(),
"sensors": {},
"summary": {
"total_sensors": len(self.sensors),
"total_data_points": len(self.data_log),
"monitoring_duration": self.data_log[-1]["timestamp"] - self.data_log[0]["timestamp"] if self.data_log else 0
}
}
for sensor_name in self.sensors:
stats = self.get_statistics(sensor_name)
if stats:
report["sensors"][sensor_name] = stats
return report
def cleanup(self):
"""清理所有资源"""
self.stop_monitoring()
for sensor in self.sensors.values():
sensor.cleanup()
self.sensors.clear()
print("🧹 环境监测系统清理完成")
def demonstrate_dht_sensor():
"""演示DHT传感器功能"""
try:
# 创建环境监测系统
monitor = EnvironmentMonitor()
# 添加传感器(假设DHT11连接在GPIO4,DHT22连接在GPIO17)
monitor.add_sensor("living_room", 4, "DHT11")
monitor.add_sensor("outside", 17, "DHT22")
# 显示传感器信息
print("\n传感器信息:")
for name, sensor in monitor.sensors.items():
info = sensor.get_sensor_info()
print(f"{name}: {info['sensor_type']} - 温度范围: {info['temperature_range']}, 精度: {info['accuracy']}")
# 单次读取测试
print("\n单次读取测试:")
readings = monitor.get_current_readings()
for name, reading in readings.items():
if reading:
print(f"{name}: {reading['temperature']:.1f}°C, {reading['humidity']:.1f}%")
else:
print(f"{name}: 读取失败")
# 开始持续监测
print("\n开始持续监测...")
monitor.start_monitoring(interval=3.0)
# 监测10秒后生成报告
time.sleep(10)
print("\n生成监测报告:")
report = monitor.generate_report()
print(f"监测时长: {report['summary']['monitoring_duration']:.1f}秒")
print(f"数据点数: {report['summary']['total_data_points']}")
for sensor_name, stats in report['sensors'].items():
print(f"\n{sensor_name}统计:")
print(f" 温度: {stats['temperature']['current']:.1f}°C "
f"(平均: {stats['temperature']['average']:.1f}°C, "
f"趋势: {stats['temperature']['trend']})")
print(f" 湿度: {stats['humidity']['current']:.1f}% "
f"(平均: {stats['humidity']['average']:.1f}%, "
f"趋势: {stats['humidity']['trend']})")
# 继续监测一段时间
print("\n继续监测5秒...")
time.sleep(5)
except KeyboardInterrupt:
print("\n🛑 演示被用户中断")
except Exception as e:
print(f"❌ 演示错误: {e}")
finally:
# 清理资源
if 'monitor' in locals():
monitor.cleanup()
GPIO.cleanup()
print("🧹 所有资源已清理")
if __name__ == "__main__":
demonstrate_dht_sensor()
4.2 光敏电阻与运动传感器
python
# projects/sensor_monitoring/light_motion_sensors.py
"""
光敏电阻和运动传感器监测
"""
import RPi.GPIO as GPIO
import time
import threading
import math
from typing import Dict, List, Callable, Optional
from enum import Enum
class SensorType(Enum):
"""传感器类型枚举"""
LIGHT = 1
MOTION = 2
class AnalogSensor:
"""模拟传感器基类(使用RC电路测量)"""
def __init__(self, pin: int, sensor_type: SensorType,
charge_time: float = 0.01, max_measure_time: float = 0.1):
"""
初始化模拟传感器
Args:
pin: GPIO引脚
sensor_type: 传感器类型
charge_time: 充电时间(秒)
max_measure_time: 最大测量时间(秒)
"""
self.pin = pin
self.sensor_type = sensor_type
self.charge_time = charge_time
self.max_measure_time = max_measure_time
# 状态变量
self.last_value = 0
self.calibration = {"min": 0, "max": 1}
self.callbacks = []
# GPIO设置
GPIO.setmode(GPIO.BCM)
print(f"✅ {sensor_type.name}传感器初始化完成 - 引脚: {pin}")
def _measure_rc_time(self) -> float:
"""
测量RC电路放电时间
Returns:
放电时间(秒)
"""
# 充电阶段
GPIO.setup(self.pin, GPIO.OUT)
GPIO.output(self.pin, GPIO.HIGH)
time.sleep(self.charge_time)
# 放电阶段
GPIO.setup(self.pin, GPIO.IN)
start_time = time.time()
# 等待引脚变为低电平
while GPIO.input(self.pin) == GPIO.HIGH:
if time.time() - start_time > self.max_measure_time:
return self.max_measure_time
return time.time() - start_time
def read_raw(self) -> float:
"""读取原始测量值"""
return self._measure_rc_time()
def read_normalized(self) -> float:
"""读取归一化值 (0-1)"""
raw_value = self.read_raw()
normalized = (raw_value - self.calibration["min"]) / (self.calibration["max"] - self.calibration["min"])
return max(0.0, min(1.0, normalized))
def calibrate(self, min_time: float = None, max_time: float = None):
"""
校准传感器
Args:
min_time: 最小测量时间(暗环境)
max_time: 最大测量时间(亮环境)
"""
if min_time is None or max_time is None:
print("开始自动校准...")
print("请将传感器置于最暗环境,5秒后开始测量...")
time.sleep(5)
min_time = self.read_raw()
print(f"最小值测量: {min_time:.4f}s")
print("请将传感器置于最亮环境,5秒后开始测量...")
time.sleep(5)
max_time = self.read_raw()
print(f"最大值测量: {max_time:.4f}s")
self.calibration = {"min": min_time, "max": max_time}
print(f"✅ 传感器校准完成: {min_time:.4f}s - {max_time:.4f}s")
def add_callback(self, callback: Callable):
"""添加数据回调"""
self.callbacks.append(callback)
def cleanup(self):
"""清理资源"""
print(f"🧹 {self.sensor_type.name}传感器清理完成")
class LightSensor(AnalogSensor):
"""光敏电阻传感器"""
def __init__(self, pin: int):
super().__init__(pin, SensorType.LIGHT)
def read_lux(self) -> float:
"""
读取光照强度(估算值)
Returns:
估算的光照强度(lux)
"""
# 这是一个近似转换,实际值需要根据具体传感器校准
normalized = self.read_normalized()
# 使用对数关系近似光照强度(光敏电阻特性)
if normalized < 0.01:
return 0
else:
# 近似公式: lux ≈ 10^(4 * normalized - 2)
return 10 ** (4 * normalized - 2)
def get_light_level(self) -> str:
"""获取光照等级描述"""
lux = self.read_lux()
if lux < 10:
return "黑暗"
elif lux < 50:
return "昏暗"
elif lux < 200:
return "一般"
elif lux < 1000:
return "明亮"
else:
return "非常明亮"
class MotionSensor:
"""PIR运动传感器"""
def __init__(self, pin: int, warmup_time: int = 30):
"""
初始化运动传感器
Args:
pin: 数字输出引脚
warmup_time: 启动预热时间(秒)
"""
self.pin = pin
self.warmup_time = warmup_time
self.last_motion_time = 0
self.motion_detected = False
self.callbacks = []
self.monitoring = False
self.monitor_thread = None
# GPIO设置
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
# 添加边缘检测
GPIO.add_event_detect(self.pin, GPIO.RISING, callback=self._motion_callback)
print(f"✅ PIR运动传感器初始化完成 - 引脚: {pin}")
print(f"⏳ 传感器预热中... ({warmup_time}秒)")
time.sleep(warmup_time)
print("🔍 运动传感器就绪")
def _motion_callback(self, channel):
"""运动检测回调"""
if channel == self.pin:
self.motion_detected = True
self.last_motion_time = time.time()
# 触发回调
for callback in self.callbacks:
try:
callback(self.last_motion_time)
except Exception as e:
print(f"❌ 运动回调错误: {e}")
print("🚶 检测到运动!")
def add_callback(self, callback: Callable):
"""添加运动检测回调"""
self.callbacks.append(callback)
def start_monitoring(self):
"""开始监测"""
if self.monitoring:
return
self.monitoring = True
def monitor_task():
while self.monitoring:
# 检查运动状态超时(通常PIR传感器会在运动停止后自动复位)
current_state = GPIO.input(self.pin)
if not current_state and self.motion_detected:
self.motion_detected = False
print("💤 运动停止")
time.sleep(0.1)
self.monitor_thread = threading.Thread(target=monitor_task)
self.monitor_thread.daemon = True
self.monitor_thread.start()
print("📊 开始运动监测")
def stop_monitoring(self):
"""停止监测"""
self.monitoring = False
if self.monitor_thread:
self.monitor_thread.join(timeout=1.0)
print("🛑 停止运动监测")
def get_status(self) -> Dict:
"""获取传感器状态"""
current_state = GPIO.input(self.pin)
time_since_motion = time.time() - self.last_motion_time if self.last_motion_time > 0 else float('inf')
return {
"pin": self.pin,
"current_state": "运动" if current_state else "静止",
"motion_detected": self.motion_detected,
"last_motion_time": self.last_motion_time,
"time_since_motion": time_since_motion,
"warmup_complete": time.time() > self.warmup_time
}
def cleanup(self):
"""清理资源"""
self.stop_monitoring()
GPIO.remove_event_detect(self.pin)
print("🧹 运动传感器清理完成")
class SmartLightController:
"""智能灯光控制器"""
def __init__(self, light_sensor_pin: int, motion_sensor_pin: int, led_pins: List[int]):
"""
初始化智能灯光控制器
Args:
light_sensor_pin: 光敏传感器引脚
motion_sensor_pin: 运动传感器引脚
led_pins: LED控制引脚列表
"""
from led_control.led_manager import LEDController
# 初始化传感器
self.light_sensor = LightSensor(light_sensor_pin)
self.motion_sensor = MotionSensor(motion_sensor_pin)
# 初始化LED控制器
self.leds = [LEDController(pin) for pin in led_pins]
# 控制参数
self.auto_mode = True
self.light_threshold = 0.3 # 光照阈值 (0-1)
self.motion_timeout = 60 # 运动超时时间(秒)
self.last_activity = 0
# 状态变量
self.lights_on = False
print("💡 智能灯光控制器初始化完成")
def calibrate_sensors(self):
"""校准传感器"""
print("开始传感器校准...")
self.light_sensor.calibrate()
print("✅ 传感器校准完成")
def enable_auto_mode(self):
"""启用自动模式"""
self.auto_mode = True
# 设置传感器回调
self.light_sensor.add_callback(self._light_sensor_callback)
self.motion_sensor.add_callback(self._motion_sensor_callback)
# 开始监测
self.motion_sensor.start_monitoring()
print("🤖 启用自动灯光模式")
def disable_auto_mode(self):
"""禁用自动模式"""
self.auto_mode = False
self.motion_sensor.stop_monitoring()
print("👤 禁用自动灯光模式")
def _light_sensor_callback(self, data):
"""光敏传感器回调"""
if not self.auto_mode:
return
light_level = self.light_sensor.read_normalized()
# 根据环境光照调整LED亮度
if self.lights_on:
target_brightness = int((1 - light_level) * 100)
for led in self.leds:
led.set_brightness(target_brightness)
def _motion_sensor_callback(self, timestamp):
"""运动传感器回调"""
if not self.auto_mode:
return
self.last_activity = timestamp
# 检查环境光照
light_level = self.light_sensor.read_normalized()
if light_level < self.light_threshold:
# 环境较暗且检测到运动,打开灯光
if not self.lights_on:
self._turn_on_lights()
self.lights_on = True
else:
# 环境明亮,保持灯光关闭
if self.lights_on:
self._turn_off_lights()
self.lights_on = False
def _check_motion_timeout(self):
"""检查运动超时"""
if not self.auto_mode or not self.lights_on:
return
time_since_activity = time.time() - self.last_activity
if time_since_activity > self.motion_timeout:
self._turn_off_lights()
self.lights_on = False
print("💤 运动超时,关闭灯光")
def _turn_on_lights(self):
"""打开灯光"""
light_level = self.light_sensor.read_normalized()
brightness = int((1 - light_level) * 100)
for led in self.leds:
led.set_brightness(brightness)
print(f"💡 打开灯光 - 亮度: {brightness}%")
def _turn_off_lights(self):
"""关闭灯光"""
for led in self.leds:
led.off()
print("🌙 关闭灯光")
def set_light_threshold(self, threshold: float):
"""设置光照阈值"""
self.light_threshold = max(0.0, min(1.0, threshold))
print(f"📏 光照阈值设置为: {self.light_threshold:.2f}")
def set_motion_timeout(self, timeout: int):
"""设置运动超时时间"""
self.motion_timeout = timeout
print(f"⏰ 运动超时设置为: {timeout}秒")
def manual_control(self, state: bool):
"""手动控制灯光"""
self.disable_auto_mode()
if state:
self._turn_on_lights()
self.lights_on = True
else:
self._turn_off_lights()
self.lights_on = False
def get_status(self) -> Dict:
"""获取控制器状态"""
light_level = self.light_sensor.read_normalized()
motion_status = self.motion_sensor.get_status()
return {
"auto_mode": self.auto_mode,
"lights_on": self.lights_on,
"light_level": light_level,
"light_threshold": self.light_threshold,
"motion_detected": motion_status["motion_detected"],
"time_since_motion": motion_status["time_since_motion"],
"motion_timeout": self.motion_timeout
}
def start(self):
"""启动控制器"""
self.enable_auto_mode()
# 启动超时检查线程
def timeout_checker():
while True:
self._check_motion_timeout()
time.sleep(5) # 每5秒检查一次
thread = threading.Thread(target=timeout_checker)
thread.daemon = True
thread.start()
print("🚀 智能灯光控制器启动")
def cleanup(self):
"""清理资源"""
self.disable_auto_mode()
self._turn_off_lights()
self.light_sensor.cleanup()
self.motion_sensor.cleanup()
for led in self.leds:
led.cleanup()
print("🧹 智能灯光控制器清理完成")
def demonstrate_smart_light():
"""演示智能灯光控制系统"""
try:
# 创建智能灯光控制器
# 假设: 光敏传感器-GPIO5, 运动传感器-GPIO6, LED-GPIO18
controller = SmartLightController(
light_sensor_pin=5,
motion_sensor_pin=6,
led_pins=[18]
)
# 校准传感器(在实际使用中需要根据环境校准)
print("跳过自动校准(演示模式)...")
# controller.calibrate_sensors()
# 设置参数
controller.set_light_threshold(0.4) # 40%光照阈值
controller.set_motion_timeout(30) # 30秒超时
# 启动控制器
controller.start()
print("\n🎯 智能灯光演示开始")
print("自动模式已启用 - 系统将根据环境和运动自动控制灯光")
print("按Ctrl+C退出演示")
# 显示状态信息
try:
while True:
status = controller.get_status()
print(f"\r环境光照: {status['light_level']:.2f} | "
f"灯光: {'开' if status['lights_on'] else '关'} | "
f"运动: {'是' if status['motion_detected'] else '否'} | "
f"超时: {status['time_since_motion']:.0f}s", end="")
time.sleep(1)
except KeyboardInterrupt:
print("\n🛑 演示被用户中断")
except Exception as e:
print(f"❌ 演示错误: {e}")
finally:
# 清理资源
if 'controller' in locals():
controller.cleanup()
GPIO.cleanup()
print("🧹 所有资源已清理")
if __name__ == "__main__":
demonstrate_smart_light()
5. 完整项目:智能家居控制系统
5.1 系统架构与集成
python
# projects/iot_applications/smart_home.py
"""
智能家居控制系统 - 完整项目
"""
import RPi.GPIO as GPIO
import time
import threading
import json
import logging
from typing import Dict, List, Optional
from datetime import datetime, timedelta
from pathlib import Path
# 导入自定义模块
from sensor_monitoring.dht_sensor import EnvironmentMonitor
from sensor_monitoring.light_motion_sensors import SmartLightController
from led_control.led_manager import RGBLEDController
from led_control.button_manager import ButtonManager
class SmartHomeSystem:
"""智能家居主控制系统"""
def __init__(self, config_file: str = "config/smart_home.json"):
"""
初始化智能家居系统
Args:
config_file: 配置文件路径
"""
self.config_file = Path(config_file)
self.config = self._load_config()
# 系统状态
self.running = False
self.components = {}
self.automations = {}
# 数据记录
self.data_log = []
self.max_log_size = self.config.get("max_log_size", 10000)
# 设置日志
self._setup_logging()
# 初始化组件
self._initialize_components()
print("🏠 智能家居系统初始化完成")
def _load_config(self) -> Dict:
"""加载配置文件"""
default_config = {
"gpio_pins": {
"living_room_led": 18,
"bedroom_led": 23,
"rgb_led_red": 17,
"rgb_led_green": 27,
"rgb_led_blue": 22,
"light_sensor": 5,
"motion_sensor": 6,
"dht_sensor": 4,
"button_1": 2,
"button_2": 3
},
"settings": {
"auto_light_threshold": 0.3,
"motion_timeout": 60,
"temperature_alert": 30,
"humidity_alert": 80,
"check_interval": 5
},
"max_log_size": 10000,
"log_file": "logs/smart_home.log"
}
if self.config_file.exists():
try:
with open(self.config_file, 'r') as f:
user_config = json.load(f)
# 合并配置
default_config.update(user_config)
print("✅ 配置文件加载成功")
except Exception as e:
print(f"❌ 配置文件加载失败: {e}, 使用默认配置")
else:
print("⚠️ 配置文件不存在,使用默认配置")
# 创建默认配置文件
self.config_file.parent.mkdir(parents=True, exist_ok=True)
with open(self.config_file, 'w') as f:
json.dump(default_config, f, indent=2)
print("📁 已创建默认配置文件")
return default_config
def _setup_logging(self):
"""设置日志系统"""
log_file = Path(self.config["settings"].get("log_file", "logs/smart_home.log"))
log_file.parent.mkdir(parents=True, exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler()
]
)
self.logger = logging.getLogger("SmartHome")
self.logger.info("智能家居系统启动")
def _initialize_components(self):
"""初始化所有组件"""
pins = self.config["gpio_pins"]
try:
# 环境监测
self.components["environment"] = EnvironmentMonitor()
self.components["environment"].add_sensor("living_room", pins["dht_sensor"], "DHT11")
# 智能灯光
self.components["lights"] = SmartLightController(
light_sensor_pin=pins["light_sensor"],
motion_sensor_pin=pins["motion_sensor"],
led_pins=[pins["living_room_led"], pins["bedroom_led"]]
)
# RGB情景灯
self.components["mood_light"] = RGBLEDController(
pins["rgb_led_red"],
pins["rgb_led_green"],
pins["rgb_led_blue"]
)
# 按钮控制
self.components["buttons"] = ButtonManager()
self.components["buttons"].add_button("mode_button", pins["button_1"])
self.components["buttons"].add_button("scene_button", pins["button_2"])
self.logger.info("所有硬件组件初始化完成")
except Exception as e:
self.logger.error(f"组件初始化失败: {e}")
raise
def _setup_automations(self):
"""设置自动化规则"""
settings = self.config["settings"]
# 环境监测自动化
def environment_alert(data):
if data["temperature"] > settings["temperature_alert"]:
self._trigger_alert("高温警报", f"温度过高: {data['temperature']}°C")
if data["humidity"] > settings["humidity_alert"]:
self._trigger_alert("高湿警报", f"湿度过高: {data['humidity']}%")
self.automations["environment_alert"] = environment_alert
# 按钮控制自动化
def toggle_auto_mode(pin):
lights = self.components["lights"]
if lights.auto_mode:
lights.disable_auto_mode()
self.components["mood_light"].set_color(100, 50, 0) # 橙色表示手动模式
self.logger.info("切换到手动模式")
else:
lights.enable_auto_mode()
self.components["mood_light"].set_color(0, 100, 0) # 绿色表示自动模式
self.logger.info("切换到自动模式")
def cycle_scenes(pin):
scenes = [
(100, 100, 100), # 白光
(100, 50, 0), # 暖黄
(50, 0, 100), # 紫调
(0, 100, 100), # 青蓝
(100, 0, 50) # 粉红
]
current_color = self.components["mood_light"].current_color
try:
current_index = scenes.index(current_color)
next_index = (current_index + 1) % len(scenes)
except ValueError:
next_index = 0
new_color = scenes[next_index]
self.components["mood_light"].set_color(*new_color)
self.logger.info(f"切换到情景模式 {next_index + 1}")
# 绑定按钮事件
self.components["buttons"].get_button("mode_button").add_event_handler(
lambda pin: toggle_auto_mode(pin)
)
self.components["buttons"].get_button("scene_button").add_event_handler(
lambda pin: cycle_scenes(pin)
)
# 注册环境监测回调
self.components["environment"].sensors["living_room"].add_callback(
environment_alert
)
self.logger.info("自动化规则设置完成")
def _trigger_alert(self, alert_type: str, message: str):
"""触发警报"""
self.logger.warning(f"警报: {alert_type} - {message}")
# 视觉警报:RGB灯闪烁红色
def alert_blink():
mood_light = self.components["mood_light"]
original_color = mood_light.current_color
for _ in range(6):
mood_light.set_color(100, 0, 0) # 红色
time.sleep(0.5)
mood_light.set_color(0, 0, 0) # 关闭
time.sleep(0.5)
mood_light.set_color(*original_color) # 恢复原颜色
threading.Thread(target=alert_blink, daemon=True).start()
def _log_system_data(self):
"""记录系统数据"""
log_entry = {
"timestamp": datetime.now().isoformat(),
"environment": self.components["environment"].get_current_readings(),
"lights": self.components["lights"].get_status(),
"system": {
"running_time": time.time() - self.start_time,
"active_automations": len(self.automations)
}
}
self.data_log.append(log_entry)
# 限制日志大小
if len(self.data_log) > self.max_log_size:
self.data_log.pop(0)
def _save_data_log(self):
"""保存数据日志到文件"""
data_file = Path("data/system_log.json")
data_file.parent.mkdir(parents=True, exist_ok=True)
try:
with open(data_file, 'w') as f:
json.dump(self.data_log, f, indent=2, default=str)
except Exception as e:
self.logger.error(f"数据保存失败: {e}")
def get_system_status(self) -> Dict:
"""获取系统状态"""
status = {
"timestamp": datetime.now().isoformat(),
"running": self.running,
"components": {},
"statistics": {
"total_data_points": len(self.data_log),
"running_time": time.time() - self.start_time if hasattr(self, 'start_time') else 0
}
}
# 收集各组件状态
for name, component in self.components.items():
if hasattr(component, 'get_status'):
status["components"][name] = component.get_status()
elif hasattr(component, 'get_current_readings'):
status["components"][name] = component.get_current_readings()
return status
def start(self):
"""启动智能家居系统"""
if self.running:
self.logger.warning("系统已经在运行中")
return
self.running = True
self.start_time = time.time()
# 设置自动化
self._setup_automations()
# 启动各组件
self.components["environment"].start_monitoring(
self.config["settings"]["check_interval"]
)
self.components["lights"].start()
self.components["buttons"].get_button("mode_button").start_monitoring()
self.components["buttons"].get_button("scene_button").start_monitoring()
# 设置初始情景
self.components["mood_light"].set_color(0, 100, 0) # 绿色 - 自动模式
# 启动数据记录线程
def data_logging_task():
while self.running:
self._log_system_data()
time.sleep(self.config["settings"]["check_interval"])
self.logging_thread = threading.Thread(target=data_logging_task)
self.logging_thread.daemon = True
self.logging_thread.start()
# 启动自动保存线程
def auto_save_task():
while self.running:
time.sleep(300) # 每5分钟保存一次
self._save_data_log()
self.save_thread = threading.Thread(target=auto_save_task)
self.save_thread.daemon = True
self.save_thread.start()
self.logger.info("智能家居系统启动完成")
print("🚀 智能家居系统正在运行...")
print("按Ctrl+C停止系统")
def stop(self):
"""停止智能家居系统"""
if not self.running:
return
self.running = False
self.logger.info("正在停止智能家居系统...")
# 停止各组件
for name, component in self.components.items():
if hasattr(component, 'cleanup'):
try:
component.cleanup()
self.logger.info(f"组件 {name} 已停止")
except Exception as e:
self.logger.error(f"停止组件 {name} 时出错: {e}")
# 保存最终数据
self._save_data_log()
# 等待线程结束
if hasattr(self, 'logging_thread'):
self.logging_thread.join(timeout=5.0)
if hasattr(self, 'save_thread'):
self.save_thread.join(timeout=5.0)
GPIO.cleanup()
self.logger.info("智能家居系统已停止")
print("🛑 智能家居系统已停止")
def run_interactive(self):
"""运行交互式控制界面"""
self.start()
try:
while self.running:
print("\n" + "="*50)
print("🏠 智能家居控制系统")
print("="*50)
status = self.get_system_status()
# 显示环境信息
env_data = status["components"]["environment"]["living_room"]
if env_data:
print(f"🌡️ 温度: {env_data['temperature']:.1f}°C")
print(f"💧 湿度: {env_data['humidity']:.1f}%")
# 显示灯光状态
light_status = status["components"]["lights"]
print(f"💡 灯光: {'开' if light_status['lights_on'] else '关'}")
print(f"🤖 模式: {'自动' if light_status['auto_mode'] else '手动'}")
print(f"📏 光照: {light_status['light_level']:.2f}")
print(f"🚶 运动: {'检测到' if light_status['motion_detected'] else '无'}")
print("\n选项:")
print("1. 切换自动/手动模式")
print("2. 切换情景灯光")
print("3. 打开所有灯光")
print("4. 关闭所有灯光")
print("5. 系统状态报告")
print("6. 退出系统")
choice = input("\n请选择操作 (1-6): ").strip()
if choice == "1":
# 模拟模式按钮按下
self.components["buttons"].get_button("mode_button")._handle_click()
elif choice == "2":
# 模拟情景按钮按下
self.components["buttons"].get_button("scene_button")._handle_click()
elif choice == "3":
self.components["lights"].manual_control(True)
elif choice == "4":
self.components["lights"].manual_control(False)
elif choice == "5":
self.generate_report()
elif choice == "6":
break
else:
print("❌ 无效选择")
time.sleep(1)
except KeyboardInterrupt:
print("\n🛑 用户请求退出")
finally:
self.stop()
def generate_report(self):
"""生成系统报告"""
print("\n" + "="*50)
print("📊 智能家居系统报告")
print("="*50)
status = self.get_system_status()
print(f"系统运行时间: {status['statistics']['running_time']:.1f} 秒")
print(f"数据记录数: {status['statistics']['total_data_points']}")
# 环境统计
if self.data_log:
env_data = [entry["environment"]["living_room"] for entry in self.data_log if entry["environment"]["living_room"]]
if env_data:
temps = [d["temperature"] for d in env_data]
humids = [d["humidity"] for d in env_data]
print(f"\n环境统计 (最近{len(env_data)}次记录):")
print(f" 温度: {sum(temps)/len(temps):.1f}°C (范围: {min(temps):.1f}-{max(temps):.1f}°C)")
print(f" 湿度: {sum(humids)/len(humids):.1f}% (范围: {min(humids):.1f}-{max(humids):.1f}%)")
# 组件状态
print(f"\n组件状态:")
for name, comp_status in status["components"].items():
if name == "lights":
print(f" 💡 灯光: {'运行中' if comp_status['auto_mode'] else '手动模式'}")
elif name == "environment":
print(f" 🌡️ 环境监测: 运行中")
elif name == "mood_light":
print(f" 🎨 情景灯: 运行中")
elif name == "buttons":
print(f" 🔘 按钮控制: 运行中")
input("\n按回车键继续...")
def main():
"""主函数"""
print("🏠 Raspberry Pi 智能家居系统")
print("="*50)
try:
# 创建智能家居系统
smart_home = SmartHomeSystem()
# 运行交互式界面
smart_home.run_interactive()
except Exception as e:
print(f"❌ 系统错误: {e}")
logging.error(f"系统错误: {e}", exc_info=True)
finally:
# 确保资源被清理
GPIO.cleanup()
print("🧹 所有资源已清理")
if __name__ == "__main__":
main()
6. 完整代码结构与部署
6.1 项目配置文件
python
# config/smart_home.json
{
"gpio_pins": {
"living_room_led": 18,
"bedroom_led": 23,
"rgb_led_red": 17,
"rgb_led_green": 27,
"rgb_led_blue": 22,
"light_sensor": 5,
"motion_sensor": 6,
"dht_sensor": 4,
"button_1": 2,
"button_2": 3,
"buzzer": 24
},
"settings": {
"auto_light_threshold": 0.3,
"motion_timeout": 60,
"temperature_alert": 30,
"humidity_alert": 80,
"check_interval": 5,
"data_save_interval": 300
},
"system": {
"max_log_size": 10000,
"log_file": "logs/smart_home.log",
"data_file": "data/system_data.json"
},
"automations": {
"enable_environment_alerts": true,
"enable_auto_lighting": true,
"enable_motion_detection": true
}
}
6.2 系统服务与自启动
python
# scripts/install_service.py
"""
智能家居系统服务安装脚本
"""
import os
import sys
import subprocess
from pathlib import Path
def check_root_privileges():
"""检查root权限"""
if os.geteuid() != 0:
print("❌ 需要root权限运行此脚本")
print("请使用: sudo python install_service.py")
sys.exit(1)
def create_systemd_service():
"""创建systemd服务文件"""
service_content = """[Unit]
Description=Smart Home System
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/smart_home
ExecStart=/usr/bin/python3 /home/pi/smart_home/main.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
"""
service_path = "/etc/systemd/system/smart-home.service"
try:
with open(service_path, 'w') as f:
f.write(service_content)
print(f"✅ 创建服务文件: {service_path}")
return True
except Exception as e:
print(f"❌ 创建服务文件失败: {e}")
return False
def enable_service():
"""启用系统服务"""
commands = [
"sudo systemctl daemon-reload",
"sudo systemctl enable smart-home.service",
"sudo systemctl start smart-home.service"
]
for cmd in commands:
try:
subprocess.run(cmd, shell=True, check=True)
print(f"✅ 执行: {cmd}")
except subprocess.CalledProcessError as e:
print(f"❌ 执行失败: {cmd} - {e}")
return False
return True
def setup_crontab():
"""设置定时任务"""
# 添加每日数据备份任务
crontab_line = "0 2 * * * /usr/bin/python3 /home/pi/smart_home/scripts/backup_data.py"
try:
# 获取当前crontab
result = subprocess.run(["crontab", "-l"], capture_output=True, text=True)
current_crontab = result.stdout
# 检查是否已存在
if "backup_data.py" not in current_crontab:
new_crontab = current_crontab + f"\n{crontab_line}\n"
subprocess.run(["crontab", "-"], input=new_crontab, text=True, check=True)
print("✅ 添加定时备份任务")
else:
print("✅ 定时备份任务已存在")
return True
except Exception as e:
print(f"❌ 设置定时任务失败: {e}")
return False
def create_backup_script():
"""创建数据备份脚本"""
backup_script = """#!/usr/bin/env python3
\"\"\"
智能家居数据备份脚本
\"\"\"
import json
import shutil
from datetime import datetime
from pathlib import Path
def backup_data():
\"\"\"备份系统数据\"\"\"
data_dir = Path("data")
backup_dir = Path("backups")
backup_dir.mkdir(exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = backup_dir / f"smart_home_backup_{timestamp}.json"
try:
# 备份数据文件
if (data_dir / "system_data.json").exists():
shutil.copy2(data_dir / "system_data.json", backup_file)
print(f"✅ 数据备份完成: {backup_file}")
# 清理旧备份(保留最近7天)
for old_file in backup_dir.glob("smart_home_backup_*.json"):
file_time = datetime.fromtimestamp(old_file.stat().st_mtime)
if (datetime.now() - file_time).days > 7:
old_file.unlink()
print(f"🗑️ 删除旧备份: {old_file}")
except Exception as e:
print(f"❌ 备份失败: {e}")
if __name__ == "__main__":
backup_data()
"""
script_path = Path("scripts/backup_data.py")
script_path.parent.mkdir(exist_ok=True)
with open(script_path, 'w') as f:
f.write(backup_script)
# 设置执行权限
script_path.chmod(0o755)
print(f"✅ 创建备份脚本: {script_path}")
def main():
"""主安装流程"""
print("🔧 智能家居系统服务安装")
print("=" * 50)
# 检查权限
check_root_privileges()
# 创建备份脚本
create_backup_script()
# 创建systemd服务
if not create_systemd_service():
sys.exit(1)
# 启用服务
if not enable_service():
sys.exit(1)
# 设置定时任务
if not setup_crontab():
print("⚠️ 定时任务设置失败,但服务安装完成")
print("\n" + "=" * 50)
print("🎉 智能家居系统服务安装完成!")
print("\n服务管理命令:")
print(" sudo systemctl start smart-home.service # 启动服务")
print(" sudo systemctl stop smart-home.service # 停止服务")
print(" sudo systemctl restart smart-home.service # 重启服务")
print(" sudo systemctl status smart-home.service # 查看状态")
print(" journalctl -u smart-home.service -f # 查看日志")
if __name__ == "__main__":
main()
6.3 Web控制界面
python
# projects/iot_applications/web_interface.py
"""
智能家居Web控制界面
"""
from flask import Flask, render_template, jsonify, request
import threading
import time
from pathlib import Path
# 假设这是我们的智能家居系统
from smart_home import SmartHomeSystem
app = Flask(__name__)
smart_home = None
def initialize_smart_home():
"""初始化智能家居系统"""
global smart_home
try:
smart_home = SmartHomeSystem()
smart_home.start()
return True
except Exception as e:
print(f"❌ 智能家居系统初始化失败: {e}")
return False
@app.route('/')
def index():
"""主页"""
return render_template('index.html')
@app.route('/api/status')
def get_status():
"""获取系统状态API"""
if not smart_home or not smart_home.running:
return jsonify({"error": "系统未运行"}), 503
try:
status = smart_home.get_system_status()
return jsonify(status)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/lights', methods=['POST'])
def control_lights():
"""控制灯光API"""
if not smart_home:
return jsonify({"error": "系统未初始化"}), 503
data = request.get_json()
action = data.get('action')
try:
if action == 'auto':
smart_home.components["lights"].enable_auto_mode()
elif action == 'manual_on':
smart_home.components["lights"].manual_control(True)
elif action == 'manual_off':
smart_home.components["lights"].manual_control(False)
else:
return jsonify({"error": "无效操作"}), 400
return jsonify({"success": True})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/moodlight', methods=['POST'])
def control_moodlight():
"""控制情景灯API"""
if not smart_home:
return jsonify({"error": "系统未初始化"}), 503
data = request.get_json()
color = data.get('color') # hex颜色代码
try:
if color:
smart_home.components["mood_light"].set_color_hex(color)
return jsonify({"success": True})
else:
return jsonify({"error": "需要颜色参数"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/scenes', methods=['POST'])
def activate_scene():
"""激活情景模式API"""
if not smart_home:
return jsonify({"error": "系统未初始化"}), 503
data = request.get_json()
scene = data.get('scene')
scenes = {
'reading': (100, 100, 100), # 白光
'relax': (100, 50, 0), # 暖黄
'party': (50, 0, 100), # 紫调
'night': (0, 0, 20) # 夜灯
}
try:
if scene in scenes:
smart_home.components["mood_light"].set_color(*scenes[scene])
return jsonify({"success": True})
else:
return jsonify({"error": "无效情景"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/report')
def get_report():
"""获取系统报告API"""
if not smart_home:
return jsonify({"error": "系统未初始化"}), 503
try:
smart_home.generate_report()
return jsonify({"success": True})
except Exception as e:
return jsonify({"error": str(e)}), 500
def run_web_server():
"""运行Web服务器"""
print("🌐 启动Web控制界面...")
app.run(host='0.0.0.0', port=5000, debug=False)
def main():
"""主函数"""
print("🏠 智能家居Web控制系统")
print("=" * 50)
# 初始化智能家居系统
if not initialize_smart_home():
return
# 启动Web服务器
try:
run_web_server()
except KeyboardInterrupt:
print("\n🛑 停止Web服务器")
except Exception as e:
print(f"❌ Web服务器错误: {e}")
finally:
# 清理资源
if smart_home:
smart_home.stop()
if __name__ == "__main__":
main()
7. 总结与进阶学习
7.1 Raspberry Pi Python硬件编程要点
通过本文的完整学习,您已经掌握了树莓派Python硬件开发的核心技能:
- GPIO基础:数字输入输出、PWM控制、中断处理
- 传感器集成:模拟/数字传感器、环境监测、运动检测
- 执行器控制:LED控制、电机驱动、显示设备
- 系统集成:多线程编程、事件驱动架构、数据持久化
- 项目部署:系统服务、Web界面、生产环境配置
7.2 电气安全与最佳实践
重要安全公式:
- 电流限制 : I m a x = P s u p p l y V × 0.8 I_{max} = \frac{P_{supply}}{V} \times 0.8 Imax=VPsupply×0.8(80%安全余量)
- 电压分压 : V o u t = V i n × R 2 R 1 + R 2 V_{out} = V_{in} \times \frac{R_2}{R_1 + R_2} Vout=Vin×R1+R2R2
- 功率计算 : P = V × I P = V \times I P=V×I
安全实践:
- 使用逻辑电平转换器处理5V设备
- 为电机等感性负载添加续流二极管
- 使用保险丝和限流电阻保护GPIO引脚
- 遵循正确的接地和屏蔽原则
7.3 进阶学习路径
基础GPIO控制 传感器集成 通信协议 物联网平台 电机控制 机器人技术 自主系统 计算机视觉 机器学习 AI边缘计算 云服务集成 大数据分析 预测性维护
7.4 故障排除指南
常见问题及解决方案:
-
GPIO无响应
- 检查引脚编号模式(BCM vs BOARD)
- 验证引脚是否被其他进程占用
- 检查物理连接和电源
-
传感器读数不稳定
- 添加软件去抖动
- 使用硬件滤波电路
- 检查电源噪声
-
系统性能问题
- 优化线程管理和资源清理
- 使用硬件PWM代替软件PWM
- 实现数据缓存和批量处理
7.5 项目扩展思路
- 环境监测站:集成更多传感器(空气质量、噪音、辐射)
- 安防系统:添加摄像头、门磁传感器、报警器
- 农业自动化:土壤湿度、自动灌溉、生长监控
- 能源管理:电能监测、太阳能优化、智能开关
- 健康监测:心率、血氧、体温监测系统
7.6 社区资源推荐
- 官方文档:raspberrypi.org/documentation
- Python库:gpiozero、RPi.GPIO、picamera
- 项目灵感:Hackster.io、Instructables
- 论坛支持:Raspberry Pi Forums、Stack Overflow
7.7 持续学习建议
- 掌握电子基础:电路理论、数字逻辑、信号处理
- 学习通信协议:I2C、SPI、UART、MQTT
- 探索嵌入式Linux:设备树、内核模块、系统优化
- 实践项目管理:版本控制、文档编写、团队协作
通过持续学习和实践,您将能够开发出更加复杂和实用的物联网项目。记住,硬件编程是一个结合软件开发和电子工程的综合性领域,享受创造的过程!
重要安全提示:
- 本文提供的代码和电路仅供参考
- 实际操作前请确保理解电路原理和安全规范
- 高电压项目请在专业人士指导下进行
- 始终遵循电气安全标准和当地法规