Linux树莓派项目实战:外网访问、PWM呼吸灯、超声波测距与驱动开发
1. 通过SSH反向代理实现树莓派外网访问
1.1 原理说明
SSH反向代理通过在树莓派上建立到云服务器的隧道连接,使外网用户可以通过访问云服务器的特定端口连接到内网的树莓派。
1.2 实现步骤
1.2.1 树莓派端配置
ssh -p 22 -qngfNTR [云服务器端口号]:localhost:22 阿里云用户名@114.55.126.125
- 参数说明:
-p 22
:连接云服务器的SSH端口-q
:安静模式(不显示警告信息)-N
:不执行远程命令-g
:允许远程主机连接到本地转发端口-f
:后台运行-R
:反向代理(云服务器端口:本地主机:本地端口)
1.2.2 阿里云服务器端验证
ss -ntl
检查指定端口是否处于LISTEN状态:
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:你的端口号 0.0.0.0:*
1.2.3 外网访问树莓派
ssh -p [云服务器端口号] 树莓派用户名@114.55.126.125
1.3 自动化脚本
在树莓派上创建autossh.sh
脚本:
#!/bin/bash
REMOTE_PORT=2222 # 云服务器端口
CLOUD_USER="your_username"
CLOUD_IP="114.55.126.125"autossh -M 0 -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" \-N -R $REMOTE_PORT:localhost:22 $CLOUD_USER@$CLOUD_IP
设置开机启动:
sudo nano /etc/rc.local
# 在exit 0前添加:
su pi -c '/home/pi/autossh.sh' &
2. PWM呼吸灯控制
2.1 硬件连接
LED颜色 | 树莓派GPIO引脚 (BCM编号) | 物理引脚 |
---|---|---|
黄色 | GPIO17 | 引脚11 |
绿色 | GPIO23 | 引脚16 |
2.2 单灯顺序呼吸代码
import RPi.GPIO as GPIO
import time# 设置编码方式
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)# 设置引脚
YELLOW_LED = 11 # GPIO17
GREEN_LED = 16 # GPIO23GPIO.setup(YELLOW_LED, GPIO.OUT)
GPIO.setup(GREEN_LED, GPIO.OUT)# 创建PWM对象
yellow_pwm = GPIO.PWM(YELLOW_LED, 60) # 60Hz频率
green_pwm = GPIO.PWM(GREEN_LED, 60)# 启动PWM
yellow_pwm.start(0)
green_pwm.start(0)try:for _ in range(3):# 黄灯呼吸for dc in range(0, 101, 5):yellow_pwm.ChangeDutyCycle(dc)time.sleep(0.1)for dc in range(100, -1, -5):yellow_pwm.ChangeDutyCycle(dc)time.sleep(0.1)# 绿灯呼吸for dc in range(0, 101, 5):green_pwm.ChangeDutyCycle(dc)time.sleep(0.1)for dc in range(100, -1, -5):green_pwm.ChangeDutyCycle(dc)time.sleep(0.1)finally:yellow_pwm.stop()green_pwm.stop()GPIO.cleanup()
2.3 双灯同时呼吸(多线程实现)
import RPi.GPIO as GPIO
import time
import threadingGPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)YELLOW_LED = 11
GREEN_LED = 16GPIO.setup(YELLOW_LED, GPIO.OUT)
GPIO.setup(GREEN_LED, GPIO.OUT)yellow_pwm = GPIO.PWM(YELLOW_LED, 60)
green_pwm = GPIO.PWM(GREEN_LED, 60)yellow_pwm.start(0)
green_pwm.start(0)def breathe_led(pwm, color_name, delay=0.1):try:while True:for dc in range(0, 101, 5):pwm.ChangeDutyCycle(dc)time.sleep(delay)for dc in range(100, -1, -5):pwm.ChangeDutyCycle(dc)time.sleep(delay)except KeyboardInterrupt:passtry:# 创建线程yellow_thread = threading.Thread(target=breathe_led, args=(yellow_pwm, "Yellow"))green_thread = threading.Thread(target=breathe_led, args=(green_pwm, "Green"))# 启动线程yellow_thread.start()green_thread.start()# 等待线程结束yellow_thread.join()green_thread.join()finally:yellow_pwm.stop()green_pwm.stop()GPIO.cleanup()
3. 超声波测距模块
3.1 硬件连接
超声波模块 | 树莓派引脚 (BCM编号) | 物理引脚 |
---|---|---|
VCC | 5V电源 | 引脚2 |
Trig | GPIO23 | 引脚16 |
Echo | GPIO24 | 引脚18 |
GND | GND | 引脚20 |
3.2 测距代码实现
import RPi.GPIO as GPIO
import time# 设置GPIO模式为BCM
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)# 定义GPIO引脚
TRIG = 23
ECHO = 24# 设置引脚模式
GPIO.setup(TRIG, GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)def get_distance():# 确保TRIG引脚为低电平GPIO.output(TRIG, False)time.sleep(0.02) # 稳定时间# 发送10μs的脉冲触发GPIO.output(TRIG, True)time.sleep(0.00001) # 10微秒GPIO.output(TRIG, False)# 记录回波开始时间pulse_start = time.time()timeout = pulse_start + 0.04 # 40ms超时(最大测距约7米)while GPIO.input(ECHO) == 0 and pulse_start < timeout:pulse_start = time.time()# 记录回波结束时间pulse_end = time.time()while GPIO.input(ECHO) == 1 and pulse_end < timeout:pulse_end = time.time()# 计算时间差pulse_duration = pulse_end - pulse_start# 计算距离(声速343m/s = 34300cm/s)distance = pulse_duration * 17150 # 34300/2(往返距离)distance = round(distance, 2)# 有效距离范围(2cm-400cm)if distance < 2 or distance > 400:return Nonereturn distancetry:while True:dist = get_distance()if dist is not None:print(f"测量距离: {dist} cm")else:print("超出有效范围")time.sleep(1)except KeyboardInterrupt:print("测量已停止")GPIO.cleanup()
3.3 提高测量精度技巧
-
多次测量取平均值:
def get_avg_distance(samples=5):distances = []for i in range(samples):dist = get_distance()if dist is not None:distances.append(dist)time.sleep(0.05)if distances:return sum(distances) / len(distances)return None
-
温度补偿(声速修正):
# 温度传感器获取当前温度(需额外硬件) def get_speed(temp_celsius):return 331.3 * (1 + temp_celsius / 273.15)**0.5# 在距离计算中使用: speed = get_speed(current_temp) * 100 # 转换为cm/s distance = pulse_duration * speed / 2
4. PWM驱动开发
4.1 开发环境准备
# 安装内核头文件
sudo apt update
sudo apt install raspberrypi-kernel-headers# 验证内核版本
uname -r
# 示例输出:5.15.32-v7+# 创建驱动目录
mkdir pwm_driver
cd pwm_driver
4.2 驱动源代码
pwmled.h
#ifndef __PWMLED_H__
#define __PWMLED_H__#define PWMLED_MAX_BRIGHTNESS 1000typedef enum {PWMLED_CMD_SET_BRIGHTNESS = 0x1,PWMLED_CMD_GET_BRIGHTNESS,
} pwmled_cmd_t;#endif // __PWMLED_H__
pwmled.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/pwm.h>
#include "pwmled.h"MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("PWM LED Controller Driver");#define PWMLED_PERIOD 1000000 // 固定周期1ms (1KHz)static struct {struct pwm_device* pwm;unsigned int brightness;
} pwmled;long pwmled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{switch (cmd) {case PWMLED_CMD_SET_BRIGHTNESS:// 限制亮度值在0-MAX之间pwmled.brightness = (arg > PWMLED_MAX_BRIGHTNESS) ? PWMLED_MAX_BRIGHTNESS : arg;// 配置PWM:占空比 = 亮度值 * 1000nspwm_config(pwmled.pwm, pwmled.brightness * 1000, PWMLED_PERIOD);// 根据亮度值启用/禁用PWMif (pwmled.brightness > 0) {pwm_enable(pwmled.pwm);} else {pwm_disable(pwmled.pwm);}break;case PWMLED_CMD_GET_BRIGHTNESS:return pwmled.brightness;default:return -EINVAL;}return 0;
}static const struct file_operations pwmled_fops = {.owner = THIS_MODULE,.unlocked_ioctl = pwmled_ioctl,
};static struct miscdevice pwmled_misc = {.minor = MISC_DYNAMIC_MINOR,.name = "pwmled",.fops = &pwmled_fops,.mode = 0666,
};static int __init pwmled_init(void)
{int ret;// 申请PWM0通道pwmled.pwm = pwm_request(0, "pwm0");if (IS_ERR(pwmled.pwm)) {printk(KERN_ERR "Failed to request PWM0\n");return PTR_ERR(pwmled.pwm);}// 初始化亮度为0(关闭LED)pwmled.brightness = 0;pwm_config(pwmled.pwm, 0, PWMLED_PERIOD);pwm_disable(pwmled.pwm);// 注册设备ret = misc_register(&pwmled_misc);if (ret) {printk(KERN_ERR "Failed to register misc device\n");pwm_free(pwmled.pwm);return ret;}printk(KERN_INFO "PWM LED driver loaded\n");return 0;
}static void __exit pwmled_exit(void)
{misc_deregister(&pwmled_misc);pwm_disable(pwmled.pwm);pwm_free(pwmled.pwm);printk(KERN_INFO "PWM LED driver unloaded\n");
}module_init(pwmled_init);
module_exit(pwmled_exit);
4.3 Makefile
obj-m := pwmled.oKERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)all:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
4.4 编译与安装驱动
# 编译驱动
make# 加载驱动
sudo insmod pwmled.ko# 验证设备节点
ls /dev/pwmled# 设置权限
sudo chmod 666 /dev/pwmled# 查看内核日志
dmesg | tail
# 预期输出: PWM LED driver loaded# 测试驱动
sudo apt install python3
python3
>>> import fcntl
>>> fd = open('/dev/pwmled', 'r')
>>> fcntl.ioctl(fd, 0x1, 500) # 设置亮度为50%
>>> fcntl.ioctl(fd, 0x2) # 获取当前亮度
500# 卸载驱动
sudo rmmod pwmled
4.5 应用层控制示例
import fcntl
import time# 命令定义(与驱动头文件一致)
SET_BRIGHTNESS = 0x1
GET_BRIGHTNESS = 0x2def control_led(brightness):with open('/dev/pwmled', 'r') as f:# 设置亮度fcntl.ioctl(f, SET_BRIGHTNESS, brightness)# 读取当前亮度current = fcntl.ioctl(f, GET_BRIGHTNESS)print(f"LED亮度设置为: {current}/1000")if __name__ == "__main__":# 呼吸灯效果try:while True:for level in range(0, 1001, 10):control_led(level)time.sleep(0.02)for level in range(1000, -1, -10):control_led(level)time.sleep(0.02)except KeyboardInterrupt:control_led(0) # 关闭LED
5. 项目集成与优化建议
5.1 系统架构
+-----------------+ +----------------+ +------------------+
| 阿里云服务器 | SSH | 树莓派 | GPIO | 外围设备 |
| (SSH反向代理) |<---->| (运行驱动程序) |---->| - PWM LED呼吸灯 |
| 端口映射:114.55.x.x:2222 | 超声波测距模块 |<---->| - HC-SR04超声波模块|
+-----------------+ +----------------+ +------------------+
5.2 安全增强建议
-
SSH安全加固:
- 使用密钥认证替代密码
- 修改默认SSH端口
- 启用Fail2ban防护
-
驱动权限管理:
- 创建专门的用户组访问设备
- 使用udev规则设置设备权限:
# /etc/udev/rules.d/99-pwmled.rules SUBSYSTEM=="misc", KERNEL=="pwmled", GROUP="ledusers", MODE="0660"
5.3 性能优化技巧
-
PWM优化:
- 使用DMA控制PWM减少CPU占用
- 硬件PWM引脚优先(GPIO12/13/18/19)
-
超声波采样优化:
- 中断驱动代替轮询
- 内核模块实现精确计时
-
多进程通信:
# 使用共享内存存储距离数据 from multiprocessing import shared_memory# 创建共享内存 shm = shared_memory.SharedMemory(name='DistanceData', create=True, size=4)
5.4 扩展应用场景
-
智能小车避障系统:
- 超声波实时测距
- PWM控制电机速度
- 距离阈值自动转向
-
智能家居控制面板:
- 远程SSH控制
- PWM调光台灯
- 超声波感应自动开关
-
安防监控系统:
- 超声波入侵检测
- 驱动LED警示灯
- 云平台报警通知
项目总结:本文完整实现了树莓派外网访问、PWM呼吸灯控制、超声波测距及Linux驱动开发全流程,提供了可直接复制到Word的详细技术文档,涵盖了从硬件连接到软件开发的完整解决方案,适用于物联网、嵌入式系统开发教学与实践应用。