Skip to content

IoT: 实践 - ESP32 课程(Thonny 软件)

1. 准备步骤

1.1 安装 Thonny

1.2 安装 ESP32 驱动(串口驱动安装)

1.3 固件烧录与程序运行

B 站视频地址

2. 程序编写

常见概念与设备标记

  1. 在电子电路和开发板中,G、V、S 通常代表

    • G - V - S 排列通常表示:
      • [地线] - [电源] - [信号]
    • G - Ground (地线/接地)
      • 电路的参考零电位点
      • 通常接电源负极
      • 颜色:黑色
    • V - Voltage (电源)
      • 电路的电源正极
      • 通常接电源正极
      • 颜色:红色
    • S - Signal (信号)
      • 电路中的信号线
      • 颜色:黄色、绿色、蓝色、白色等
  2. 在开发板中,3V3、5V、GND 通常代表

    • 3V3 - 3.3V 电源
    • 5V - 5V 电源
    • GND - 地线
  3. 引脚的 PWM 模式:

    • PWM(Pulse Width Modulation,脉冲宽度调制)是一种用于控制电路中电流或电压的方法,通过改变脉冲的宽度来控制输出信号的幅度。在许多微控制器和开发板中,某些引脚支持 PWM 模式,可以用于控制 LED 灯的亮度、电机转速等。
  4. Pin.PULL_UP 和 Pin.PULL_DOWN 是两种不同的上拉/下拉电阻配置,它们决定了引脚在悬空时的默认电平。

    • Pin.PULL_UP(上拉电阻)

      • 默认状态:引脚被拉高到 3.3V,读取值为 1
      • 当传感器触发时:引脚被拉低到 GND,读取值为 0
      • 逻辑:触发 = 0,未触发 = 1
    • Pin.PULL_DOWN(下拉电阻)

      • 默认状态:引脚被拉低到 GND,读取值为 0
      • 当传感器触发时:引脚被拉高到 3.3V,读取值为 1
      • 逻辑:触发 = 1,未触发 = 0
  5. xxxx

2.1 LED 闪烁

python
from machine import Pin  # 从machine模块中导入Pin类,用于控制硬件引脚
import time  # 导入time模块,用于实现延时功能

led = Pin(5, Pin.OUT)  # 创建一个Pin对象,将第5号引脚配置为输出模式,用于控制LED灯

while True:  # 无限循环,使得以下代码块会不断重复执行
    led.value(1)  # 将led引脚的值设置为1(通常表示高电平),从而打开LED灯
    time.sleep(1)  # 程序暂停执行1秒
    led.value(0)  # 将led引脚的值设置为0(通常表示低电平),从而关闭LED灯
    time.sleep(1)  # 程序再次暂停执行1秒

An image

2.2 离线烧录

2.2.1 将 boot.py 程序烧录到开发板中,这样即使没有连接电脑,程序也会自动运行(即离线运行)。

python
#!/opt/bin/lv_micropython
import uos as os
import uerrno as errno
iter = os.ilistdir()
IS_DIR = 0x4000
IS_REGULAR = 0x8000

while True:
    try:
        entry = next(iter)
        filename = entry[0]
        file_type = entry[1]
        if filename == 'boot.py':
            continue
        else:
            print("===============================")
            print(filename,end="")
            if file_type == IS_DIR:
                print(", File is a directory")
                print("===============================")
            else:
                print("\n===============================")
                #print("Contents:")
                #with open(filename) as f:
                #   for line in enumerate(f):
                #       print("{}".format(line[1]),end="")
                #print("")
                exec(open(filename).read(),globals())
    except StopIteration:
        break

2.2.2 将 LED 闪烁 的代码另存为 blink.py(存到 MicroPython 设备的文件系统里),然后点击主板上的复位键,即可看到 LED 闪烁。

2.2.3 截图如下

An image

2.3 呼吸灯

python
import time  #导入时间模块,用于在代码中引入延时。
from machine import Pin,PWM

#ESP32 PWM引脚输出的方式与传统控制器不同
#在初始化阶段通过配置PWM的参数,可以改变频率和占空比
#定义GPIO 5的输出频率为10000Hz,占空比为0,分配给PWM
pwm =PWM(Pin(5,Pin.OUT),10000)

try:
    while True:
#占空比范围为0-1023,因此我们使用第一个for环来控制PWM以改变占空比
#周期值,使PWM输出0% -100%;使用第二个for环路使PWM输出100%-0%
        for i in range(0,1023):
            pwm.duty(i)
            time.sleep_ms(2)

        for i in range(0,1023):
            pwm.duty(1023-i)
            time.sleep_ms(2)
except:
#每次使用PWM时,硬件定时器将打开以配合它。因此,每次使用PWM后
#需要调用deinit()来关闭计时器。否则会导致下次PWM工作失败
    pwm.deinit()

An image

2.4 低电平按键测试

python
from machine import Pin
import time

button = Pin(5, Pin.IN,Pin.PULL_UP)

while True:
    print("button:",button.value())   #按下打印相应信息
    time.sleep(0.1) #延时0.1秒

An image

2.4.1 拓展

以上代码是一段【按钮模块】(按下低电平,输出 0),正常输出 1。 我在在 12 号引脚上加入一个【LED 模块】,然后点击【按钮模块】时(低电平时 LED 亮一下),如果没有按下的时候,LED 是不亮的。

python
from machine import Pin
import time

# 初始化按钮(引脚5,上拉输入模式)
button = Pin(5, Pin.IN, Pin.PULL_UP)
# 初始化LED(引脚12,输出模式)
led = Pin(12, Pin.OUT)

while True:
    button_state = button.value()  # 读取按钮状态

    if button_state == 0:  # 按钮按下(低电平)
        led.value(1)       # 点亮LED
        print("按钮按下 - LED亮")
    else:                  # 按钮未按下(高电平)
        led.value(0)       # 熄灭LED
        print("按钮释放 - LED灭")

    time.sleep(0.1)  # 延时0.1秒

2.5 电位器 PWM 输入

电位器(可调电阻)是一种常见的模拟输入设备,通常用于调节输出电压或电流。在许多应用中,电位器可以用来调节 LED 的亮度,或者作为传感器来检测环境变化。

电位器按照调节方式分类,可以分为 有级调节和无级调节。有级调节电位器通常有多个固定阻值点,通过旋转旋钮可以切换不同的阻值(一级一级间断变化);而无级调节电位器则可以在整个阻值范围内连续变化(从低到高、调速非常平滑)。

python
# 导入引脚、ADC模块
# ADC模块用于读取模拟信号
# Pin类,引脚模块用于控制引脚
from machine import ADC,Pin
import time

# 开启并配置ADC,量程为0-3.3V
adc=ADC(Pin(13))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)

# 每0.1秒读取一次ADC值,将ADC值转换为DAC值输出;
#并将这些数据打印到“Shell”
try:
    while True:
        adcVal=adc.read()
        dacVal=adcVal // 16
        voltage = adcVal / 4095.0 * 3.3
        print("ADC Val:",adcVal,"DACVal:",dacVal,"Voltage:",voltage,"V")
        time.sleep_ms(100)
except:
    pass

An image

2.6 光敏传感器测试

光敏传感器是一种常见的模拟传感器,用于检测环境光线的变化。通过读取光敏传感器的模拟输出值,可以判断当前环境的光线强度。

python
# 光敏传感器测试
# 导入引脚、ADC模块&时间模块
from machine import ADC,Pin
import time

# 开启并配置ADC
adc=ADC(Pin(13))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_10BIT)

# 每100毫秒读取一次ADC值
# 并将这些数据打印到“Shell”
try:
    while True:
        adcVal=adc.read()
        print("Light:",adcVal)
        time.sleep_ms(100)
except:
    pass

以下代码如果是光敏传感器被遮住,则打印 0,否则打印 1

python
from machine import Pin
import time

light = Pin(13, Pin.IN,Pin.PULL_DOWN)

while True:
    print("Light:",light.value())   #按下打印相应信息
    time.sleep(0.1) #延时0.1秒

2.6.1 拓展:光敏传感器控制 LED 灯

冰箱灯的开启关闭:通过读取光敏传感器的模拟输出值,可以判断当前环境的光线强度。根据光线强度,可以控制 LED 灯的亮灭。当光线强度较强时,LED 灯亮起;当光线强度较暗时,LED 灯熄灭。

python
# 注意:需要使用DO接口(左侧)连接光敏传感器
from machine import Pin
import time

# 初始化光敏传感器(引脚13,下拉输入模式)
light_sensor = Pin(13, Pin.IN, Pin.PULL_UP)
# 初始化LED(假设LED连接在引脚12)
led = Pin(12, Pin.OUT)

while True:
    light_value = light_sensor.value()  # 读取光敏传感器值
    print(light_value)

    if light_value == 0:  # 传感器未被遮住(光线较强)
        led.value(1)      # 点亮LED
        print("光线较强 - LED亮起")
    else:                 # 传感器被遮住(光线较暗)
        led.value(0)      # 熄灭LED
        print("光线较暗 - LED熄灭")

    time.sleep(0.1)  # 延时0.1秒

备注

python
# 对于光敏传感器
# 使用 PULL_UP + 使用DO接口(左侧)连接光敏传感器
light_sensor = Pin(13, Pin.IN, Pin.PULL_UP)
while True:
    if light_sensor.value() == 0:  # 未遮住
        print("光线亮")
    else:                          # 被遮住
        print("光线暗")

# 使用 PULL_DOWN + 使用AO接口(右侧)连接光敏传感器
light_sensor = Pin(13, Pin.IN, Pin.PULL_DOWN)
while True:
    if light_sensor.value() == 1:  # 未遮住
        print("光线亮")
    else:                          # 被遮住
        print("光线暗")

An image

2.7 声音传感器测试

场景有:工地噪音检测、核磁共振成像通信、声控开关、声控灯等。

根据工作原理可以分为:电容式、压电式、电磁式、电感式、超声波式等。

2.7.1 声音传感器测试

python
#导入引脚、ADC模块
from machine import ADC,Pin
import time

# 开启并配置ADC,量程为0-4095
adc=ADC(Pin(13))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)

# 每0.1秒读取一次ADC值
# 并将这些数据打印到“Shell”
try:
    while True:
        adcVal=adc.read()
        print("Sound:",adcVal)
        time.sleep(0.1)
except:
    pass

2.7.2 声音传感器应用(通过 ADC 控制 LED 灯)

python
#导入引脚、ADC模块
from machine import ADC,Pin
import time

# 开启并配置ADC,量程为0-4095
adc=ADC(Pin(13))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)

# 初始化LED(假设LED连接在引脚12)
led = Pin(12, Pin.OUT)

# 每0.1秒读取一次ADC值
# 并将这些数据打印到“Shell”
try:
    while True:
        adcVal=adc.read()
        if adcVal > 2000:  # 声音大于2000时,点亮LED
            led.value(1)
            print("Sound:",adcVal, 'LED ON')
        else:              # 否则,关闭LED
            led.value(0)
            print("Sound:",adcVal, 'LED OFF')
        time.sleep(0.1)
except:
    pass

An image

2.8 有源蜂鸣器

有源蜂鸣器自带振荡电路,只要提供电源,就可以发声。适用场景:闹钟、计时器、电子琴等。

有源蜂鸣器和无源蜂鸣器的主要区别在于:有源蜂鸣器自带振荡电路,只要提供电源,就可以发声;而无源蜂鸣器需要提供一定频率的方波信号才能发声。

2.8.1 有源蜂鸣器测试

python
from machine import Pin
import time

buzzer = Pin(5, Pin.OUT)

while True:
    buzzer.value(1)
    time.sleep(1)
    buzzer.value(0)
    time.sleep(1)

2.8.2 无源蜂鸣器测试

它会发出“哒哒哒”的声音,并且可以发出不同频率的声音。

An image

2.9 温度传感器

适用场景:水银温度计、红外体温枪、温度传感器等。

2.9.1 DS18B20 温度传感器

python
from machine import Pin, ADC
import time
import math

adc=ADC(Pin(13))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)

try:
    while True:
        adcValue = adc.read()
        voltage = adcValue / 4096 * 3.3
        print("ADC value:",adcValue,"  Voltage:",voltage,"V");
        time.sleep(0.5)
except:
    pass

2.9.2 DHT11 温湿度传感器(加显示屏显示)

python
# 待办: 1. 加显示屏显示

An image

2.10 交通灯顺序切换

线的颜色不是必须的,但是引脚的连接顺序是必须的。

2.10.1 红绿灯顺序切换

python
import machine
import time

led_red = machine.Pin(5, machine.Pin.OUT)
led_yellow = machine.Pin(23, machine.Pin.OUT)
led_green = machine.Pin(27, machine.Pin.OUT)

while True:
    led_green.value(1) # 绿灯亮
    time.sleep(2) # 延迟2 s
    led_green.value(0) # 绿灯关闭
    for i in range(3): #黄灯闪烁3次
        led_yellow.value(1)
        time.sleep(0.5)
        led_yellow.value(0)
        time.sleep(0.5)
    led_red.value(1) # 红灯亮
    time.sleep(4) # 延迟4 s
    led_red.value(0) #红灯关闭

2.10.2 模拟香港红绿灯:绿灯时“滴滴滴”效果

当绿灯亮的时候,加上有源蜂鸣器(让它发出声音),其他灯时不发出声音,有源蜂鸣器的引脚是 13。

在绿灯亮时让有源蜂鸣器发出声音,其他灯时不发声

python
import machine
import time

# 初始化LED引脚
led_red = machine.Pin(5, machine.Pin.OUT)
led_yellow = machine.Pin(23, machine.Pin.OUT)
led_green = machine.Pin(27, machine.Pin.OUT)

# 初始化有源蜂鸣器引脚(引脚13)
buzzer = machine.Pin(13, machine.Pin.OUT)

while True:
    # 绿灯亮 + 蜂鸣器响
    led_green.value(1)    # 绿灯亮
    buzzer.value(1)       # 蜂鸣器响
    time.sleep(2)         # 延迟2秒
    led_green.value(0)    # 绿灯关闭
    buzzer.value(0)       # 蜂鸣器关闭

    # 黄灯闪烁3次(蜂鸣器不响)
    for i in range(3):
        led_yellow.value(1)
        time.sleep(0.5)
        led_yellow.value(0)
        time.sleep(0.5)

    # 红灯亮(蜂鸣器不响)
    led_red.value(1)      # 红灯亮
    time.sleep(4)         # 延迟4秒
    led_red.value(0)      # 红灯关闭

An image

2.11 RGB 随机颜色

适用场景:LED 彩屏、RGB 补光灯、装饰灯带、RGB 氛围灯等。

python
#导入Pin, PWM和Random功能模块
from machine import Pin, PWM
from random import randint
import time

#配置GPIO32、GPIO4和GPIO2的输出模式为PWM输出,PWM频率为10000Hz
pins = [5, 23, 27]

pwm0 = PWM(Pin(pins[0]),10000)
pwm1 = PWM(Pin(pins[1]),10000)
pwm2 = PWM(Pin(pins[2]),10000)

#定义一个函数来设置RGBLED的颜色
def setColor(r, g, b):
    pwm0.duty(r)
    pwm1.duty(g)
    pwm2.duty(b)

try:
    while True:
        red   = randint(0, 1023)
        green = randint(0, 1023)
        blue  = randint(0, 1023)
        setColor(red, green, blue)
        time.sleep_ms(500)
except:
    pwm0.deinit()
    pwm1.deinit()
    pwm2.deinit()

An image

2.12 红外避障测试

python
from machine import Pin
import time

distance = Pin(13, Pin.IN)

while True:
    print(distance.value())
    time.sleep(0.1) #延时0.1秒

An image

2.13 倾斜传感器

适用场景:倾斜电路、倾斜开关、倾斜报警器等。

python
from machine import Pin
import time

TiltSensor = Pin(12, Pin.IN)

while True:
    value = TiltSensor.value()
    print(value, end = " ")
    if  value== 0:
        print("The switch is turned on")
    else:
        print("The switch is turned off")
    time.sleep(0.1)

一定要注意,倾斜传感器引脚的连接顺序,否则可能会造成传感器无法正常工作。

An image

2.14 干簧管测试

适用场景:门磁开关、门窗感应器等。

python
from machine import Pin
import time

ReedSensor = Pin(5, Pin.IN)
while True:
    value = ReedSensor.value()
    print(value, end = " ")
    if value == 0:
        print("A magnetic field")
    else:
        print("There is no magnetic field")
    time.sleep(0.1)

An image

2.15 PCF8563 时钟模块

python
import micropython
from machine import SoftI2C, Pin

PCF8563_I2C_ADDR = 0x51
SEC_REG = 0x02  # 秒寄存器,低位在前
MIN_REG = 0x03  # 分寄存器,低位在前
HOUR_REG = 0x04  # 时寄存器,低位在前(24小时制)
DAY_REG = 0x05  # 日期寄存器,低位在前
MONTH_REG = 0x07  # 月份寄存器,低位在前
YEAR_REG = 0x08  # 年份寄存器(后两位),低位在前

i2c = SoftI2C(scl=Pin(22), sda=Pin(21), freq=100000)
# 辅助函数:将BCD格式转换为十进制
def bcd_to_dec(bcd):
    return ((bcd >> 4) * 10) + (bcd & 0x0F)

    # 辅助函数:将十进制转换为BCD格式
def dec_to_bcd(dec):
    return ((dec // 10) << 4) | (dec % 10)

    # 读取单个字节的函数
def read_byte(addr, reg):
    return i2c.readfrom_mem(addr, reg, 1)[0]

class pcf8563(object):

    # 设置时间的函数
    def set_time(year, month, day, hour, minute, second):
        # 构建时间数据(BCD格式)
        sec_bcd = dec_to_bcd(second)
        min_bcd = dec_to_bcd(minute)
        hour_bcd = dec_to_bcd(hour)
        day_bcd = dec_to_bcd(day)
        month_bcd = dec_to_bcd(month)
        year_bcd = dec_to_bcd(year % 100)  # 假设只存储后两位年份

    # 写入时间到PCF8563
        i2c.writeto_mem(PCF8563_I2C_ADDR, SEC_REG, bytearray([sec_bcd]))
        i2c.writeto_mem(PCF8563_I2C_ADDR, MIN_REG, bytearray([min_bcd]))
        i2c.writeto_mem(PCF8563_I2C_ADDR, HOUR_REG, bytearray([hour_bcd]))
        i2c.writeto_mem(PCF8563_I2C_ADDR, DAY_REG, bytearray([day_bcd]))
        i2c.writeto_mem(PCF8563_I2C_ADDR, MONTH_REG, bytearray([month_bcd]))
        i2c.writeto_mem(PCF8563_I2C_ADDR, YEAR_REG, bytearray([year_bcd]))

    # 读取并解析时间的函数
    def read_time():
        # 读取秒、分、时、日、月、年(注意:年份通常只存储后两位)
        seconds_bcd = read_byte(PCF8563_I2C_ADDR, SEC_REG)
        minutes_bcd = read_byte(PCF8563_I2C_ADDR, MIN_REG)
        hours_bcd = read_byte(PCF8563_I2C_ADDR, HOUR_REG)
        day_bcd = read_byte(PCF8563_I2C_ADDR, DAY_REG)
        month_bcd = read_byte(PCF8563_I2C_ADDR, MONTH_REG)
        year_bcd = read_byte(PCF8563_I2C_ADDR, YEAR_REG)

    # 将BCD格式转换为十进制
        seconds = bcd_to_dec(seconds_bcd)
        minutes = bcd_to_dec(minutes_bcd)
        hours = bcd_to_dec(hours_bcd & 0x3F)  # 清除最高两位(如果用作控制位)
        day = bcd_to_dec(day_bcd)
        month = bcd_to_dec(month_bcd)
        year = 2000 + bcd_to_dec(year_bcd)  # 假设年份是2000-2099
        if 20 < month < 33:
            month = month - 20
        if month > 40:
            month = month - 40
        if day > 31:
           day = day - 40
        return year, month, day, hours, minutes, seconds

TODO

python
from machine import SoftI2C, Pin
import time
from PCF8563 import pcf8563

# 设置当前时间(仅为示例)
pcf8563.set_time(2024, 12, 31, 23, 59, 10)

#示例:读取并打印时间
while True:
    year, month, day, hours, minutes, seconds = pcf8563.read_time()
    print("Current time from PCF8563:", year, month, day, hours, minutes, seconds)
    time.sleep(1)

An image

2.16 四位数码管显示

适用场景:显示时间、温度、电压等,冰箱、空调、电饭煲等家电设备中常见。

python
from machine import Pin
import time

# TM1650的定义
ADDR_DIS = 0x48  #加密模式命令
ADDR_KEY = 0x49  #读键值命令

# 亮度的定义
BRIGHT_DARKEST = 0
BRIGHT_TYPICAL = 2
BRIGHTEST      = 7

on  = 1
off = 0

# number:0~9
NUM = [0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f]
DIG = [0x6e,0x6c,0x6a,0x68]
DOT = [0,0,0,0]

clkPin = 22
dioPin = 21
clk = Pin(clkPin, Pin.OUT)
dio = Pin(dioPin, Pin.OUT)

DisplayCommand = 0

def writeByte(wr_data):
    global clk,dio
    for i in range(8):
        if(wr_data & 0x80 == 0x80):
            dio.value(1)
        else:
            dio.value(0)
        clk.value(0)
        time.sleep_us(100)
        clk.value(1)
        time.sleep_us(100)
        clk.value(0)
        wr_data <<= 1
    return

def start():
    global clk,dio
    dio.value(1)
    clk.value(1)
    time.sleep_us(100)
    dio.value(0)
    return

def ack():
    global clk,dio
    dy = 0
    clk.value(0)
    time.sleep_us(100)
    dio = Pin(dioPin, Pin.IN)
    while(dio.value() == 1):
        time.sleep_us(100)
        dy += 1
        if(dy>5000):
            break
    clk.value(1)
    time.sleep_us(100)
    clk.value(0)
    dio = Pin(dioPin, Pin.OUT)
    return

def stop():
    global clk,dio
    dio.value(0)
    clk.value(1)
    time.sleep_us(100)
    dio.value(1)
    return

def displayBit(bit, num):
    global ADDR_DIS
    if(num > 9 and bit > 4):
        return
    start()
    writeByte(ADDR_DIS)
    ack()
    writeByte(DisplayCommand)
    ack()
    stop()
    start()
    writeByte(DIG[bit-1])
    ack()
    if(DOT[bit-1] == 1):
        writeByte(NUM[num] | 0x80)
    else:
        writeByte(NUM[num])
    ack()
    stop()
    return

def clearBit(bit):
    if(bit > 4):
        return
    start()
    writeByte(ADDR_DIS)
    ack()
    writeByte(DisplayCommand)
    ack()
    stop()
    start()
    writeByte(DIG[bit-1])
    ack()
    writeByte(0x00)
    ack()
    stop()
    return


def setBrightness(b = BRIGHT_TYPICAL):
    global DisplayCommand,brightness
    DisplayCommand = (DisplayCommand & 0x0f)+(b<<4)
    return

def setMode(segment = 0):
    global DisplayCommand
    DisplayCommand = (DisplayCommand & 0xf7)+(segment<<3)
    return

def displayOnOFF(OnOff = 1):
    global DisplayCommand
    DisplayCommand = (DisplayCommand & 0xfe)+OnOff
    return

def displayDot(bit, OnOff):
    if(bit > 4):
        return
    if(OnOff == 1):
        DOT[bit-1] = 1;
    else:
        DOT[bit-1] = 0;
    return

def InitDigitalTube():
    setBrightness(2)
    setMode(0)
    displayOnOFF(1)
    for _ in range(4):
        clearBit(_)
    return

def ShowNum(num): #0~9999
    displayBit(1,num%10)
    if(num < 10):
        clearBit(2)
        clearBit(3)
        clearBit(4)
    if(num > 9 and num < 100):
        displayBit(2,num//10%10)
        clearBit(3)
        clearBit(4)
    if(num > 99 and num < 1000):
        displayBit(2,num//10%10)
        displayBit(3,num//100%10)
        clearBit(4)
    if(num > 999 and num < 10000):
        displayBit(2,num//10%10)
        displayBit(3,num//100%10)
        displayBit(4,num//1000)

InitDigitalTube()

while True:
    #displayDot(1,on)     # 开或关, DigitalTube.Display(bit,number); bit=1---4  number=0---9
    for i in range(0,9999):
        ShowNum(i)
        time.sleep_ms(10)

An image

2.16.1 使用模拟温度传感器并显示温度数到数码管上

python
from machine import Pin, ADC
import time
import math

# TM1650的定义
ADDR_DIS = 0x48  #加密模式命令
ADDR_KEY = 0x49  #读键值命令

# 亮度的定义
BRIGHT_DARKEST = 0
BRIGHT_TYPICAL = 2
BRIGHTEST      = 7

on  = 1
off = 0

# number:0~9
NUM = [0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f]
DIG = [0x6e,0x6c,0x6a,0x68]
DOT = [0,0,0,0]

clkPin = 22
dioPin = 21
clk = Pin(clkPin, Pin.OUT)
dio = Pin(dioPin, Pin.OUT)

DisplayCommand = 0

# 初始化温度传感器(引脚13)
adc = ADC(Pin(13))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)

def writeByte(wr_data):
    global clk,dio
    for i in range(8):
        if(wr_data & 0x80 == 0x80):
            dio.value(1)
        else:
            dio.value(0)
        clk.value(0)
        time.sleep_us(100)
        clk.value(1)
        time.sleep_us(100)
        clk.value(0)
        wr_data <<= 1
    return

def start():
    global clk,dio
    dio.value(1)
    clk.value(1)
    time.sleep_us(100)
    dio.value(0)
    return

def ack():
    global clk,dio
    dy = 0
    clk.value(0)
    time.sleep_us(100)
    dio = Pin(dioPin, Pin.IN)
    while(dio.value() == 1):
        time.sleep_us(100)
        dy += 1
        if(dy>5000):
            break
    clk.value(1)
    time.sleep_us(100)
    clk.value(0)
    dio = Pin(dioPin, Pin.OUT)
    return

def stop():
    global clk,dio
    dio.value(0)
    clk.value(1)
    time.sleep_us(100)
    dio.value(1)
    return

def displayBit(bit, num):
    global ADDR_DIS
    if(num > 9 and bit > 4):
        return
    start()
    writeByte(ADDR_DIS)
    ack()
    writeByte(DisplayCommand)
    ack()
    stop()
    start()
    writeByte(DIG[bit-1])
    ack()
    if(DOT[bit-1] == 1):
        writeByte(NUM[num] | 0x80)
    else:
        writeByte(NUM[num])
    ack()
    stop()
    return

def clearBit(bit):
    if(bit > 4):
        return
    start()
    writeByte(ADDR_DIS)
    ack()
    writeByte(DisplayCommand)
    ack()
    stop()
    start()
    writeByte(DIG[bit-1])
    ack()
    writeByte(0x00)
    ack()
    stop()
    return

def setBrightness(b = BRIGHT_TYPICAL):
    global DisplayCommand,brightness
    DisplayCommand = (DisplayCommand & 0x0f)+(b<<4)
    return

def setMode(segment = 0):
    global DisplayCommand
    DisplayCommand = (DisplayCommand & 0xf7)+(segment<<3)
    return

def displayOnOFF(OnOff = 1):
    global DisplayCommand
    DisplayCommand = (DisplayCommand & 0xfe)+OnOff
    return

def displayDot(bit, OnOff):
    if(bit > 4):
        return
    if(OnOff == 1):
        DOT[bit-1] = 1;
    else:
        DOT[bit-1] = 0;
    return

def InitDigitalTube():
    setBrightness(2)
    setMode(0)
    displayOnOFF(1)
    for i in range(1, 5):
        clearBit(i)
    return

def ShowNum(num): #0~9999
    displayBit(1, num % 10)
    if(num < 10):
        clearBit(2)
        clearBit(3)
        clearBit(4)
    elif(num < 100):
        displayBit(2, num // 10 % 10)
        clearBit(3)
        clearBit(4)
    elif(num < 1000):
        displayBit(2, num // 10 % 10)
        displayBit(3, num // 100 % 10)
        clearBit(4)
    else:
        displayBit(2, num // 10 % 10)
        displayBit(3, num // 100 % 10)
        displayBit(4, num // 1000)

def read_temperature():
    """读取ADC值并转换为温度"""
    try:
        adcValue = adc.read()
        voltage = adcValue / 4096 * 3.3

        # 根据你的温度传感器类型选择转换公式:

        # 方案1:如果是LM35温度传感器 (10mV/°C)
        temperature = voltage * 100

        # 方案2:如果是NTC热敏电阻(需要根据具体型号调整)
        # 这里是一个示例公式,请根据你的热敏电阻调整
        # R = 10000 / (4095 / adcValue - 1)  # 10kΩ NTC
        # temperature = 1 / (1/298.15 + 1/3950 * math.log(R/10000)) - 273.15

        # 方案3:如果是TMP36温度传感器
        # temperature = (voltage - 0.5) * 100

        return temperature, adcValue, voltage
    except Exception as e:
        print("读取温度错误:", e)
        return None, 0, 0

def display_temperature(temp):
    """在数码管上显示温度(格式:XX.X)"""
    if temp is None:
        # 显示错误信息
        ShowNum(8888)
        return

    # 限制温度范围
    temp = max(-99, min(999, temp))

    if temp < 0:
        # 负温度处理(如果需要)
        temp_int = int(abs(temp) * 10)
        # 这里可以添加负号显示逻辑
    else:
        # 正温度,转换为整数(乘以10保留一位小数)
        temp_int = int(temp * 10)

    # 显示温度,格式为 XX.X(如 23.5°C)
    if temp_int < 1000:  # 温度在0-99.9度之间
        displayBit(1, temp_int % 10)        # 小数位
        displayDot(2, on)                   # 在第二位显示小数点
        displayBit(2, (temp_int // 10) % 10) # 个位
        displayBit(3, (temp_int // 100) % 10) # 十位
        clearBit(4)
    else:  # 温度超过100度
        displayBit(1, temp_int % 10)        # 小数位
        displayDot(2, on)                   # 在第二位显示小数点
        displayBit(2, (temp_int // 10) % 10) # 个位
        displayBit(3, (temp_int // 100) % 10) # 十位
        displayBit(4, (temp_int // 1000) % 10) # 百位

# 初始化数码管
InitDigitalTube()

print("开始显示温度...")

try:
    while True:
        # 读取温度
        temperature, adc_value, voltage = read_temperature()

        if temperature is not None:
            # 打印调试信息
            print("ADC值:{:4d}  电压:{:.2f}V  温度:{:.1f}°C".format(
                adc_value, voltage, temperature))

            # 在数码管上显示温度
            display_temperature(temperature)
        else:
            print("读取温度失败")
            ShowNum(8888)  # 显示错误代码

        # 每1秒更新一次
        time.sleep(1)

except KeyboardInterrupt:
    print("程序停止")
    # 清理显示
    InitDigitalTube()

3. 程序编写(提高版)

3.1 人体红外感应

适用场景:当有人靠近时,自动打开灯泡,无人时关闭灯泡。比如:客厅、卧室、洗手间等。走廊感应灯、智能安防等。

人体红外传感器(PIR 传感器)是通过检测人体发出的红外辐射来工作的。

PIR 传感器工作原理:

  1. 红外辐射检测
    • 所有物体都会发出红外辐射,辐射强度与温度相关
    • 人体体温约 37°C,会发出特定波长的红外线(8-14μm)
    • PIR 传感器内部有热释电红外传感器,对这种人體红外辐射特别敏感
  2. 菲涅尔透镜
    • 人体红外辐射通过菲涅尔透镜,聚焦到热释电红外传感器上
    • 传感器检测到人体红外辐射时,输出高电平
  3. 输出信号
    • PIR 传感器输出高电平时,表示有人靠近
    • 输出低电平时,表示无人靠近
python
from machine import Pin
import time

PIR = Pin(5, Pin.IN)

while True:
    value = PIR.value()
    print(value, end = " ")
    if value == 0:
        print("Some body is in this area!")
    else:
        print("No one!")
    time.sleep_ms(100)

An image

与 “热成像搜救技术”的对比

荒野搜救用的热成像技术是基于红外辐射绝对强度检测,能够生成温度分布图像,探测距离远,能发现静止人体,是比普通 PIR 传感器先进得多的技术!

技术原理对比表:

特征PIR 传感器热成像技术
检测原理红外辐射的变化差异红外辐射的绝对强度
输出信息有人/无人(1 比特信息)温度分布图(数百万比特信息)
探测能力只能检测移动人体能检测静止和移动人体
空间信息无位置信息精确的方位和距离信息
温度信息无温度数据每个点的具体温度值

3.2 旋转编码器

适用场景:测速、角度、位置,比如:电梯、电机转速、舵机角度、机械臂位置、机器人行走距离等。

3.2.1 rotary.py 模块

python
# The MIT License (MIT)
# Copyright (c) 2020 Mike Teachman
# https://opensource.org/licenses/MIT

# Platform-independent MicroPython code for the rotary encoder module

# Documentation:
#   https://github.com/MikeTeachman/micropython-rotary

import micropython

_DIR_CW = const(0x10)  # Clockwise step
_DIR_CCW = const(0x20)  # Counter-clockwise step

# Rotary Encoder States
_R_START = const(0x0)
_R_CW_1 = const(0x1)
_R_CW_2 = const(0x2)
_R_CW_3 = const(0x3)
_R_CCW_1 = const(0x4)
_R_CCW_2 = const(0x5)
_R_CCW_3 = const(0x6)
_R_ILLEGAL = const(0x7)

_transition_table = [

    # |------------- NEXT STATE -------------|            |CURRENT STATE|
    # CLK/DT    CLK/DT     CLK/DT    CLK/DT
    #   00        01         10        11
    [_R_START, _R_CCW_1, _R_CW_1,  _R_START],             # _R_START
    [_R_CW_2,  _R_START, _R_CW_1,  _R_START],             # _R_CW_1
    [_R_CW_2,  _R_CW_3,  _R_CW_1,  _R_START],             # _R_CW_2
    [_R_CW_2,  _R_CW_3,  _R_START, _R_START | _DIR_CW],   # _R_CW_3
    [_R_CCW_2, _R_CCW_1, _R_START, _R_START],             # _R_CCW_1
    [_R_CCW_2, _R_CCW_1, _R_CCW_3, _R_START],             # _R_CCW_2
    [_R_CCW_2, _R_START, _R_CCW_3, _R_START | _DIR_CCW],  # _R_CCW_3
    [_R_START, _R_START, _R_START, _R_START]]             # _R_ILLEGAL

_transition_table_half_step = [
    [_R_CW_3,            _R_CW_2,  _R_CW_1,  _R_START],
    [_R_CW_3 | _DIR_CCW, _R_START, _R_CW_1,  _R_START],
    [_R_CW_3 | _DIR_CW,  _R_CW_2,  _R_START, _R_START],
    [_R_CW_3,            _R_CCW_2, _R_CCW_1, _R_START],
    [_R_CW_3,            _R_CW_2,  _R_CCW_1, _R_START | _DIR_CW],
    [_R_CW_3,            _R_CCW_2, _R_CW_3,  _R_START | _DIR_CCW]]

_STATE_MASK = const(0x07)
_DIR_MASK = const(0x30)


def _wrap(value, incr, lower_bound, upper_bound):
    range = upper_bound - lower_bound + 1
    value = value + incr

    if value < lower_bound:
        value += range * ((lower_bound - value) // range + 1)

    return lower_bound + (value - lower_bound) % range


def _bound(value, incr, lower_bound, upper_bound):
    return min(upper_bound, max(lower_bound, value + incr))


def _trigger(rotary_instance):
    for listener in rotary_instance._listener:
        listener()


class Rotary(object):

    RANGE_UNBOUNDED = const(1)
    RANGE_WRAP = const(2)
    RANGE_BOUNDED = const(3)

    def __init__(self, min_val, max_val, reverse, range_mode, half_step):
        self._min_val = min_val
        self._max_val = max_val
        self._reverse = -1 if reverse else 1
        self._range_mode = range_mode
        self._value = min_val
        self._state = _R_START
        self._half_step = half_step
        self._listener = []

    def set(self, value=None, min_val=None,
            max_val=None, reverse=None, range_mode=None):
        # disable DT and CLK pin interrupts
        self._hal_disable_irq()

        if value is not None:
            self._value = value
        if min_val is not None:
            self._min_val = min_val
        if max_val is not None:
            self._max_val = max_val
        if reverse is not None:
            self._reverse = -1 if reverse else 1
        if range_mode is not None:
            self._range_mode = range_mode
        self._state = _R_START

        # enable DT and CLK pin interrupts
        self._hal_enable_irq()

    def value(self):
        return self._value

    def reset(self):
        self._value = 0

    def close(self):
        self._hal_close()

    def add_listener(self, l):
        self._listener.append(l)

    def remove_listener(self, l):
        if l not in self._listener:
            raise ValueError('{} is not an installed listener'.format(l))
        self._listener.remove(l)

    def _process_rotary_pins(self, pin):
        old_value = self._value
        clk_dt_pins = (self._hal_get_clk_value() <<
                       1) | self._hal_get_dt_value()
        # Determine next state
        if self._half_step:
            self._state = _transition_table_half_step[self._state &
                                                      _STATE_MASK][clk_dt_pins]
        else:
            self._state = _transition_table[self._state &
                                            _STATE_MASK][clk_dt_pins]
        direction = self._state & _DIR_MASK

        incr = 0
        if direction == _DIR_CW:
            incr = 1
        elif direction == _DIR_CCW:
            incr = -1

        incr *= self._reverse

        if self._range_mode == self.RANGE_WRAP:
            self._value = _wrap(
                self._value,
                incr,
                self._min_val,
                self._max_val)
        elif self._range_mode == self.RANGE_BOUNDED:
            self._value = _bound(
                self._value,
                incr,
                self._min_val,
                self._max_val)
        else:
            self._value = self._value + incr

        try:
            if old_value != self._value and len(self._listener) != 0:
                micropython.schedule(_trigger, self)
        except:
            pass

3.2.2 rotary_irq_rp2.py 模块

python
# The MIT License (MIT)
# Copyright (c) 2020 Mike Teachman
# Copyright (c) 2021 Eric Moyer
# https://opensource.org/licenses/MIT

# Platform-specific MicroPython code for the rotary encoder module
# Raspberry Pi Pico implementation

# Documentation:
#   https://github.com/MikeTeachman/micropython-rotary

from machine import Pin
from rotary import Rotary

IRQ_RISING_FALLING = Pin.IRQ_RISING | Pin.IRQ_FALLING


class RotaryIRQ(Rotary):
    def __init__(
        self,
        pin_num_clk,
        pin_num_dt,
        min_val=0,
        max_val=10,
        reverse=False,
        range_mode=Rotary.RANGE_UNBOUNDED,
        pull_up=False,
        half_step=False,
    ):
        super().__init__(min_val, max_val, reverse, range_mode, half_step)

        if pull_up:
            self._pin_clk = Pin(pin_num_clk, Pin.IN, Pin.PULL_UP)
            self._pin_dt = Pin(pin_num_dt, Pin.IN, Pin.PULL_UP)
        else:
            self._pin_clk = Pin(pin_num_clk, Pin.IN)
            self._pin_dt = Pin(pin_num_dt, Pin.IN)

        self._hal_enable_irq()

    def _enable_clk_irq(self):
        self._pin_clk.irq(self._process_rotary_pins, IRQ_RISING_FALLING)

    def _enable_dt_irq(self):
        self._pin_dt.irq(self._process_rotary_pins, IRQ_RISING_FALLING)

    def _disable_clk_irq(self):
        self._pin_clk.irq(None, 0)

    def _disable_dt_irq(self):
        self._pin_dt.irq(None, 0)

    def _hal_get_clk_value(self):
        return self._pin_clk.value()

    def _hal_get_dt_value(self):
        return self._pin_dt.value()

    def _hal_enable_irq(self):
        self._enable_clk_irq()
        self._enable_dt_irq()

    def _hal_disable_irq(self):
        self._disable_clk_irq()
        self._disable_dt_irq()

    def _hal_close(self):
        self._hal_disable_irq()

3.2.3 Rotary_encoder_counting.py 模块

python
import time  # 导入Python的time模块,用于控制代码的执行时间,比如休眠
from rotary_irq_rp2 import RotaryIRQ  # 从rotary_irq_rp2模块中导入RotaryIRQ类,这个类通常用于处理旋转编码器的输入
from machine import Pin  # 从machine模块中导入Pin类,这个类用于操作微控制器(如Raspberry Pi Pico)上的GPIO引脚
SW=Pin(14, Pin.IN, Pin.PULL_UP)  # 初始化一个名为SW的Pin对象,连接到GPIO引脚14,设置为输入模式(IN),并启用内部上拉电阻(PULL_UP)
# 初始化一个RotaryIRQ对象r,用于处理旋转编码器的输入
r = RotaryIRQ(pin_num_clk=13,# pin_num_clk和pin_num_dt分别指定旋转编码器所需的时钟和数据引脚
               pin_num_dt=12,
               min_val=0,  # min_val设置旋转编码器的最小值为0
               reverse=False,  # reverse决定是否反转旋转编码器的方向。这里设置为False,表示不反转
               range_mode=RotaryIRQ.RANGE_UNBOUNDED)# range_mode设置旋转编码器的范围模式为无界,可以无限旋转而不会到达最大值或最小值
val_old = r.value()  # 读取旋转编码器的当前值,并存储在变量val_old中

while True:  # 无限循环,程序将持续运行直到被外部中断(如用户按下Ctrl+C)
    try:  # 尝试执行下面的代码块,如果出现异常则跳转到except块
        val_new = r.value()   # 读取旋转编码器的当前值,并存储在变量val_new中
        if SW.value()==0 and n==0:  # 检查按钮是否被按下(SW.value()==0表示按钮被按下)且变量n的值为0
            print("Button Pressed")   # 如果上述条件满足,打印“Button Pressed”
            print("Selected Number is : ", val_new)  # 打印当前旋转编码器的值,表示用户选择的数字
            n=1  # 将变量n的值设置为1,用于防止在按钮持续按下时重复触发
            while SW.value()==0:
                continue   # 如果按钮仍然被按下,则持续等待,直到按钮释放
        n=0   # 将变量n的值重置为0,为下一次按钮按下做准备
        if val_old != val_new:  # 检查旋转编码器的值是否发生了变化
            val_old = val_new  # 如果值发生了变化,更新val_old为新的值
            print('result =', val_new)   # 打印新的旋转编码器的值
        time.sleep_ms(50)  # 程序暂停执行50毫秒,以减少CPU使用率
    except KeyboardInterrupt:  # 如果在上面的try块中发生KeyboardInterrupt异常(通常由用户按下Ctrl+C引起)
        break   # 跳出while无限循环

An image

3.3 舵机测试

python
from machine import Pin, PWM
import time
pwm = PWM(Pin(5))
pwm.freq(50)

'''
Duty cycle corresponding to the Angle
0°----2.5%----25
45°----5%----51.2
90°----7.5%----77
135°----10%----102.4
180°----12.5%----128
'''

angle_0 = 25
angle_90 = 77
angle_180 = 128

while True:
    pwm.duty(angle_0)
    time.sleep(1)
    pwm.duty(angle_90)
    time.sleep(1)
    pwm.duty(angle_180)
    time.sleep(1)
python
from utime import sleep
from machine import Pin
from machine import PWM
import time

pwm = PWM(Pin(5))#舵机销连接GP5
pwm.freq(50)#20ms周期,所以频率为50Hz

'''
Duty cycle corresponding to the Angle
0°----2.5%----25
45°----5%----51.2
90°----7.5%----77
135°----10%----102.4
180°----12.5%----128
'''

def setServoCycle (position):# 设置伺服旋转角度
    pos = convert(position, 0, 180, 25, 128)
    pwm.duty(pos)
    sleep(0.01)

def convert(x, i_m, i_M, o_m, o_M):# 将旋转角度转换为占空比
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

while True:
    for degree in range(0, 180, 1):#伺服电机从0到180
        setServoCycle(degree)
    time.sleep(1)

    for degree in range(180, 0, -1):#伺服电机从180到0
        setServoCycle(degree)
    time.sleep(1)

An image

3.4 温湿度传感器

适用场景:家用加湿器、农业温室、仓库存储、医疗监护等。

python
# 导入机器、时间和dht模块
import machine
import time
import dht

#将DHT11与引脚(13)关联
DHT = dht.DHT11(machine.Pin(13))

# 每秒获取1次温湿度数据并打印
while True:
    DHT.measure() # 启动DHT11测量一次数据
   # 调用DHT的内置函数来获取温度和湿度数据,并打印在“Shell”中
    print('temperature:',DHT.temperature(),'℃','humidity:',DHT.humidity(),'%')
    time.sleep_ms(1000)

An image

3.4.1 温湿度传感器测试(将数据显示在四位数码管上)

python
from machine import Pin
import time
import dht

# 初始化DHT11温湿度传感器(引脚13)
DHT = dht.DHT11(Pin(5))

# TM1650的定义
ADDR_DIS = 0x48  # 加密模式命令
ADDR_KEY = 0x49  # 读键值命令

# 亮度的定义
BRIGHT_DARKEST = 0
BRIGHT_TYPICAL = 2
BRIGHTEST = 7

on = 1
off = 0

# number:0~9
NUM = [0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f]
DIG = [0x6e, 0x6c, 0x6a, 0x68]
DOT = [0, 0, 0, 0]

clkPin = 22
dioPin = 21
clk = Pin(clkPin, Pin.OUT)
dio = Pin(dioPin, Pin.OUT)

DisplayCommand = 0

def writeByte(wr_data):
    global clk, dio
    for i in range(8):
        if wr_data & 0x80:
            dio.value(1)
        else:
            dio.value(0)
        clk.value(0)
        time.sleep_us(100)
        clk.value(1)
        time.sleep_us(100)
        clk.value(0)
        wr_data <<= 1
    return

def start():
    global clk, dio
    dio.value(1)
    clk.value(1)
    time.sleep_us(100)
    dio.value(0)
    return

def ack():
    global clk, dio
    dy = 0
    clk.value(0)
    time.sleep_us(100)
    dio = Pin(dioPin, Pin.IN)
    while dio.value() == 1:
        time.sleep_us(100)
        dy += 1
        if dy > 5000:
            break
    clk.value(1)
    time.sleep_us(100)
    clk.value(0)
    dio = Pin(dioPin, Pin.OUT)
    return

def stop():
    global clk, dio
    dio.value(0)
    clk.value(1)
    time.sleep_us(100)
    dio.value(1)
    return

def displayBit(bit, num):
    global ADDR_DIS
    if num > 9 and bit > 4:
        return
    start()
    writeByte(ADDR_DIS)
    ack()
    writeByte(DisplayCommand)
    ack()
    stop()
    start()
    writeByte(DIG[bit-1])
    ack()
    if DOT[bit-1] == 1:
        writeByte(NUM[num] | 0x80)
    else:
        writeByte(NUM[num])
    ack()
    stop()
    return

def clearBit(bit):
    if bit > 4:
        return
    start()
    writeByte(ADDR_DIS)
    ack()
    writeByte(DisplayCommand)
    ack()
    stop()
    start()
    writeByte(DIG[bit-1])
    ack()
    writeByte(0x00)
    ack()
    stop()
    return

def setBrightness(b=BRIGHT_TYPICAL):
    global DisplayCommand
    DisplayCommand = (DisplayCommand & 0x0f) + (b << 4)
    return

def setMode(segment=0):
    global DisplayCommand
    DisplayCommand = (DisplayCommand & 0xf7) + (segment << 3)
    return

def displayOnOFF(OnOff=1):
    global DisplayCommand
    DisplayCommand = (DisplayCommand & 0xfe) + OnOff
    return

def displayDot(bit, OnOff):
    if bit > 4:
        return
    if OnOff == 1:
        DOT[bit-1] = 1
    else:
        DOT[bit-1] = 0
    return

def InitDigitalTube():
    setBrightness(2)
    setMode(0)
    displayOnOFF(1)
    for i in range(1, 5):
        clearBit(i)
    return

def ShowNum(num):
    """显示4位数字"""
    # 确保数字在合理范围内
    num = max(0, min(9999, num))

    # 分离各位数字
    digits = []
    temp_num = num
    for i in range(4):
        digits.append(temp_num % 10)
        temp_num //= 10
    digits.reverse()

    # 显示各位数字
    for i in range(4):
        if digits[i] != 0 or i == 3:  # 避免前导零,但保留个位
            displayBit(i+1, digits[i])
        else:
            clearBit(i+1)

def clearDisplay():
    """清空显示"""
    for i in range(1, 5):
        clearBit(i)

def displayTemperature(temp):
    """显示温度(格式:tXX.X)"""
    clearDisplay()

    # 显示温度标识 't'
    displayBit(1, 5)  # 用数字5显示为't'的形状(近似)
    print(111111, temp)

    # 显示温度值(如23.5显示为23.5)
    temp_int = int(temp * 10)  # 转换为整数(235表示23.5)

    # 个位和小数位
    displayBit(3, (temp_int // 10) % 10)  # 个位
    displayDot(4, on)  # 在第四位显示小数点
    displayBit(4, temp_int % 10)  # 小数位

    # 十位(如果有)
    if temp_int >= 100:
        displayBit(2, (temp_int // 100) % 10)  # 十位
    else:
        clearBit(2)

def displayHumidity(hum):
    """显示湿度(格式:HXX.X)"""
    clearDisplay()

    # 显示湿度标识 'H'
    displayBit(1, 4)  # 用数字4显示为'H'的形状(近似)

    # 显示湿度值
    hum_int = int(hum * 10)  # 转换为整数(655表示65.5)

    # 十位和个位
    displayBit(2, (hum_int // 100) % 10)  # 十位
    displayBit(3, (hum_int // 10) % 10)   # 个位
    displayDot(4, on)  # 在第四位显示小数点
    displayBit(4, hum_int % 10)  # 小数位

def read_dht_sensor():
    """读取DHT11传感器数据"""
    try:
        DHT.measure()
        temperature = DHT.temperature()
        humidity = DHT.humidity()
        return temperature, humidity
    except Exception as e:
        print("读取传感器失败:", e)
        return None, None

# 初始化数码管
InitDigitalTube()

print("开始显示温湿度数据...")

# 显示模式控制
display_mode = 0  # 0:温度, 1:湿度
last_change_time = time.time()

try:
    while True:
        # 读取温湿度数据
        temp, hum = read_dht_sensor()

        if temp is not None and hum is not None:
            # 打印到串口
            print(f"温度: {temp}℃, 湿度: {hum}%")

            # 轮流显示温度和湿度(每3秒切换一次)
            current_time = time.time()
            if current_time - last_change_time >= 3:
                display_mode = 1 - display_mode  # 切换模式
                last_change_time = current_time

            if display_mode == 0:
                displayTemperature(temp)
            else:
                displayHumidity(hum)
        else:
            # 显示错误信息
            ShowNum(8888)

        time.sleep(1)  # 每秒更新一次传感器数据

except KeyboardInterrupt:
    print("程序停止")
    clearDisplay()

3.5 无源蜂鸣器

python
from machine import Pin, PWM
from time import sleep

buzzer = PWM(Pin(13))

buzzer.duty(512) #PWM的占空比通常是一个介于0和1023之间的值,大小控制音量

buzzer.freq(523)#设置PWM信号的频率为523Hz,这是音乐简谱中“DO”音符的频率
sleep(0.5)
buzzer.freq(586)#更改PWM信号的频率为586Hz,这是音乐简谱中“RE”音符的频率
sleep(0.5)      #以此类推,以下代码依次播放“MI”、“FA”、“SO”、“LA”和“SI”音符
buzzer.freq(658)#MI
sleep(0.5)
buzzer.freq(697)#FA
sleep(0.5)
buzzer.freq(783)#SO
sleep(0.5)
buzzer.freq(879)#LA
sleep(0.5)
buzzer.freq(987)#SI
sleep(0.5)
buzzer.duty(0)

An image

3.6 摇杆模块测试

适用场景主要有:摇杆控制机器人移动、摇杆控制游戏角色移动、摇杆控制无人机飞行等。

python
from machine import Pin, ADC
import time

#初始化摇杆模块(ADC功能)
# 在代码中,将Z_Pin配置为上拉输入模式
rocker_x=ADC(Pin(12))
rocker_y=ADC(Pin(13))
button_z=Pin(14,Pin.IN,Pin.PULL_UP)

# 设置两个ADC通道的电压采集范围为0-3.3V,
# 并且采集的数据宽度为0-4095
rocker_x.atten(ADC.ATTN_11DB)
rocker_y.atten(ADC.ATTN_11DB)
rocker_x.width(ADC.WIDTH_12BIT)
rocker_y.width(ADC.WIDTH_12BIT)

# 在循环中,使用Read()读取X轴和Y轴的值
# 并使用value()读取Z轴的值,然后显示它们
while True:
    print("X,Y,Z:",rocker_x.read(),",",rocker_y.read(),",",button_z.value())
    time.sleep(0.1)

An image

3.7 超声波测距

超声波测距模块主要用于测量距离,其工作原理是发射超声波,然后测量超声波从发射到接收的时间,根据声速计算距离。比如:倒车雷达、B 超、测距仪、洗牙等。

python
from machine import Pin
import time

# 定义超声波测距模块的控制引脚
Trig = Pin(13, Pin.OUT, 0)
Echo = Pin(12, Pin.IN, 0)
distance = 0 # 将初始距离定义为0
soundVelocity = 340 #设定超声波在空气中的速度340m/s

def getDistance():# getDistance()函数用于驱动超声波模块测量距离
    Trig.value(1)# 将Trig引脚电平设为高(发送超声波信号)
    time.sleep_us(10)# 三角脚保持高电平10us以启动超声波模块
    Trig.value(0)# 将Trig引脚电平设为低,结束超声波信号发送
    while not Echo.value():# 等待Echo引脚变为高电平,表示超声波已经返回
        pass
    pingStart = time.ticks_us()# 记录Echo引脚变为高电平的时间点(开始计时)
    while Echo.value():        #等待Echo引脚变为低电平,表示超声波接收完成
        pass                  #然后使用时间模块的时间戳函数计算Echo的持续时间
    pingStop = time.ticks_us()
    pingTime = time.ticks_diff(pingStop, pingStart) // 2
    distance = round(soundVelocity * pingTime / 10000,2)
    #round 是 Python 中的一个内置函数,用于将数字四舍五入到指定的小数位数
    #根据时间计算测量距离并返回值。
    return distance  # 返回计算得到的距离值

time.sleep(2)# 延时2秒,等待超声波模块稳定
while True:
    time.sleep_ms(500)
    distance = getDistance()
    print("Distance: ", distance, "cm")

3.8 红外遥控与接收

适用场景:红外遥控器、红外接收模块、红外医疗灯、红外夜视仪、红外体温计等。

python
import utime   # 导入utime模块,用于获取时间戳和延时
from machine import Pin   # 从machine模块导入Pin类,用于控制硬件引脚

ird = Pin(5,Pin.IN)# 创建一个名为ird的Pin对象,连接到硬件的引脚5,并设置为输入模式

# 定义了一个字典act,用于存储各种IR控制命令对应的脉冲序列——键值
act = {"R": "00000000111111111010001001011101",  # R十六进制键值: 0xFFA25D
       "G": "00000000111111110110001010011101",  # G十六进制键值: 0xFF629D
       "B": "00000000111111111110001000011101",  # B十六进制键值: 0xFFE20D
       "ON": "00000000111111110010001011011101",  # ON十六进制键值: 0xFF22BD
       "BACK": "00000000111111111100001000111101",  # BACK十六进制键值: 0xFFC20D
       "OK": "00000000111111111010100001010111",  # OK十六进制键值: 0xFFA857
       "UP": "00000000111111110000001011111101",  # UP十六进制键值: 0xFF02FD
       "DOWN": "00000000111111111001100001100111",  # DOWN十六进制键值: 0xFF9867
       "LEFT": "00000000111111111110000000011111",  # LEFT十六进制键值: 0xFFE00F
       "RIGHT": "00000000111111111001000001101111",  # RIGHT十六进制键值: 0xFF906F
       "TEST": "00000000111111111011000001001111",  # TEST十六进制键值: 0xFFB04F
       "0": "00000000111111110110100010010111",  # 0十六进制键值: 0xFF6897
       "1": "00000000111111110011000011001111",  # 1十六进制键值: 0xFF30CF
       "2": "00000000111111110001100011100111",  # 2十六进制键值: 0xFF18E7
       "3": "00000000111111110111101010000101",  # 3十六进制键值: 0xFF7AAD
       "4": "00000000111111110001000011101111",  # 4十六进制键值: 0xFF10EF
       "5": "00000000111111110011100011000111",  # 5十六进制键值: 0xFF3C87
       "6": "00000000111111110101101010100101",  # 6十六进制键值: 0xFF5ABD
       "7": "00000000111111110100001010111101",  # 7十六进制键值: 0xFF42BD
       "8": "00000000111111110100101010110101",  # 8十六进制键值: 0xFF4AB5
       "9": "00000000111111110101001010101101"}  # 9十六进制键值: 0xFF52AD

def read_ircode(ird):# 定义一个函数read_ircode,接收ird参数(引脚对象)
    wait = 1         # 初始化等待状态为1
    complete = 0     # 初始化完成状态为0
    seq0 = []        # 初始化一个空列表seq0,用于存储低电平持续时间
    seq1 = []        # 初始化一个空列表seq1,用于存储高电平持续时间

    while wait == 1:# 循环等待IR信号的开始(引脚值变为0)
        if ird.value() == 0:
            wait = 0# 一旦检测到IR信号的开始,设置等待状态为0
    while wait == 0 and complete == 0:# 当等待状态为0且未完成解析时,循环读取脉冲序列
        start = utime.ticks_us()# 记录当前时间(微秒级)
        while ird.value() == 0:# 循环等待低电平结束
            ms1 = utime.ticks_us()# 记录低电平结束的时间
        diff = utime.ticks_diff(ms1,start)# 计算低电平的持续时间
        seq0.append(diff)# 将低电平持续时间添加到seq0列表
        # 循环等待高电平,并在等待期间检查是否接收完整
        while ird.value() == 1 and complete == 0:
            ms2 = utime.ticks_us()# 记录当前时间(用于计算高电平持续时间)
            diff = utime.ticks_diff(ms2,ms1)# 计算高电平持续时间
            if diff > 10000:#高电平持续时间超过10000微秒
                complete = 1
        seq1.append(diff)# 将高电平持续时间添加到seq1列表

# 将高电平序列seq1转换成字符串code,0代表低电平,1代表高电平
    code = ""
    for val in seq1:
        if val < 2000:
            if val < 700:
                code += "0"
            else:
                code += "1"
    command = ""
    for k,v in act.items():# 根据code在act字典中查找对应的控制命令
        if code == v:
            command = k
            print(hex(int(code,2))[2:].upper(),end=" | ")
            #将二进制的键值转为十六进制并切除前两位00后输出

    if command == "": # 如果没有找到对应的命令,则将输出字符串“press”
        command = "press"
    return command     # 返回解析得到的控制命令

while True:
    command = read_ircode(ird)  # 调用read_ircode函数读取IR信号
    print(command)        # 打印解析得到的控制命令
    utime.sleep_ms(50)     # 延时50毫秒,减少CPU占用率

An image

3.9 震动传感器

适用场景:地震预警、家具防跌落、汽车安全系统等。

python
from machine import Pin
import time

TiltSensor = Pin(5, Pin.IN)

while True:
    value = TiltSensor.value()
    print(value, end = " ")
    if  value== 0:
        print("No vibration")
    else:
        print("The  is vibration")
    time.sleep(0.1)

An image

3.10 8X8 点阵显示

python
from machine import SPI, Pin
from _init_ import Matrix8x8
import time

# 初始化SPI总线
spi = SPI(1, baudrate=1000000, sck=18, mosi=23)

# 片选引脚
cs = Pin(14, Pin.OUT, value=1)  # 初始状态为高电平

# 假设我们只有一个LED矩阵
num_matrices = 1

# 创建Matrix8x8实例
matrix = Matrix8x8(spi, cs, num_matrices)

glyphs = {
    'B': [
        [0, 1, 1, 0, 0, 1, 1, 0],
        [1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1],
        [0, 1, 1, 1, 1, 1, 1, 0],
        [0, 0, 1, 1, 1, 1, 0, 0],
        [0, 0, 0, 1, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0]
    ]
}

glyphs1 = {
    'S': [
        [0, 0, 0, 0, 0, 0, 0, 0],
        [0, 1, 1, 0, 0, 1, 1, 0],
        [0, 1, 1, 1, 1, 1, 1, 0],
        [0, 1, 1, 1, 1, 1, 1, 0],
        [0, 0, 1, 1, 1, 1, 0, 0],
        [0, 0, 0, 1, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0]
    ]
}

while True:
    matrix.text_from_glyph("B", glyphs)
    matrix.show()
    time.sleep(0.5)
    matrix.text_from_glyph("S", glyphs1)
    matrix.show()
    time.sleep(0.5)
# 现在可以添加更多的显示逻辑,比如循环显示不同的消息或图形
python
# _init_.py
"""
MicroPython max7219 cascadable 8x8 LED matrix driver

Licensed under MIT, found in LICENSE.txt
    Copyright (c) 2017 Mike Causer
    Copyright (c) 2022 Leo Spratt
"""
from micropython import const
from framebuf import FrameBuffer, MONO_HLSB

_NOOP = const(0)
_DIGIT0 = const(1)
_DECODEMODE = const(9)
_INTENSITY = const(10)
_SCANLIMIT = const(11)
_SHUTDOWN = const(12)
_DISPLAYTEST = const(15)


class Matrix8x8(FrameBuffer):
    def __init__(self, spi, cs, num):
        """
        Driver for cascading MAX7219 8x8 LED matrices.

        >>> from machine import Pin, SPI
        >>> from max7219 import Matrix8x8
        >>> spi = SPI(1)
        >>> display = Matrix8x8(spi, Pin('X5'), 4)
        >>> display.text('1234')
        >>> display.show()

        """
        self._spi = spi
        self._cs = cs
        self._cs.init(self._cs.OUT, True)
        self._num = num
        self._buffer = bytearray(8 * self._num)

        super().__init__(self._buffer, 8 * self._num, 8, MONO_HLSB)

        self._write_init()

    def _write(self, command, data):
        self._cs(0)
        for _ in range(self._num):
            self._spi.write(bytearray([command, data]))
        self._cs(1)

    def _write_init(self):
        for command, data in (
            (_SHUTDOWN, 0),
            (_DISPLAYTEST, 0),
            (_SCANLIMIT, 7),
            (_DECODEMODE, 0),
            (_SHUTDOWN, 1),
        ):
            self._write(command, data)

    def brightness(self, value):
        if not 0 <= value <= 15:
            raise ValueError("Brightness out of range")
        self._write(_INTENSITY, value)

    def text(self, s, x=0, y=0, c=1):
        super().text(s, x, y, c)

    def text_from_glyph(self, s, glyphs, x_offset=0, y_offset=0):
        col = 0
        for char in s:
            glyph = glyphs.get(char)

            if glyph:
                for y in range(8):
                    for x in range(8):
                        self.pixel(x+col+x_offset, y+y_offset, glyph[y][x])
            else:
                self.text(char, col+x_offset, y_offset)

            col += 8

    def show(self):
        for y in range(8):
            self._cs(0)
            for m in range(self._num):
                self._spi.write(bytearray([_DIGIT0 + y, self._buffer[(y * self._num) + m]]))
            self._cs(1)

    def zero(self):
        self.fill(0)

    def shutdown(self):
        self._write(_SHUTDOWN, 0)

    def wake(self):
        self._write(_SHUTDOWN, 1)

    def test(self, enable=True):
        self._write(_DISPLAYTEST, int(enable))

An image

4. 程序编写(进阶版)

4.1 电容触摸传感器

适用场景:触摸开关、触摸按键、触摸笔等。比如:油烟机、汽车中控屏、电子书等。

python
from machine import Pin
import time

touch = Pin(5, Pin.IN, Pin.PULL_UP)

while True:
    if touch.value() == 1:
        print("Your finger touch the sensor !")
    else:
        print("No touching !")
    time.sleep(0.1) #延迟0.1s

An image

4.2 霍尔传感器

适用场景:门磁、车锁、限位开关等。比如:智能门锁、智能车锁、智能家居、汽车转速表、出租车计价表等。

python
from machine import Pin
import time

hall = Pin(5, Pin.IN)

while True:
    value = hall.value()
    print(value, end = " ")
    if value == 0:
        print("A magnetic field")
    else:
        print("There is no magnetic field")
    time.sleep(0.1)

An image

4.3 火焰传感器

python
# 导入引脚、ADC和DAC模块
from machine import ADC,Pin
import time

flame_D = Pin(13, Pin.IN,Pin.PULL_UP)
# 开启并配置ADC,量程为0-3.3V
adc=ADC(Pin(12))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)

# 每0.1秒读取一次数字值和ADC值,将ADC值转换为DAC值和电压值输出
# 并将这些数据打印到“Shell”
try:
    while True:
        digitalVal = flame_D.value()
        adcVal=adc.read()
        dacVal=adcVal//16
        voltage = adcVal / 4095.0 * 3.3
        print("digitalVal:",digitalVal,"ADC Val:",adcVal,"DACVal:",dacVal,"Voltage:",voltage,"V")
        time.sleep(0.1)
except:
    pass

An image

4.3.1 火焰传感器测试(增加蜂鸣器)

TODO

4.4 光折断计数测试

python
from machine import Pin

sensor = Pin(5, Pin.IN, Pin.PULL_UP)
lastState = 1  # 初始状态应为高电平,即1
PushCounter = 0

while True:
    State = sensor.value()
    if State != lastState:
        if State == 0:  # 如果检测到低电平,即光线被阻断
            PushCounter += 1
            # 可以选择将计数存储到文件、发送到服务器或其他存储方式,更多灵活使用
            print(PushCounter)
        lastState = State

An image

4.5 一路循迹测试

python
from machine import Pin
import time

sensor = Pin(5, Pin.IN, Pin.PULL_UP)

while True:
    if sensor.value() == 1:
        print("1   White")   #按下打印相应信息
    else:
        print("0   Black")
    time.sleep(0.1) #延时 0.1s

An image

4.6 MQ-2 气体传感器

烟雾传感器的适用场景:高铁、厨房、工业、家庭、汽车、养殖场、仓库、实验室、办公室等。

python
#导入引脚、ADC和DAC模块
from machine import ADC,Pin
import time

mq2_D = Pin(13, Pin.IN,Pin.PULL_UP )
# 开启并配置ADC,量程为0-3.3V
adc=ADC(Pin(12))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)

# 每0.1秒读取一次数字值和ADC值,将ADC值转换为DAC值和电压值输出
# 并将这些数据打印到“Shell”

while True:
    digitalVal = mq2_D.value()
    adcVal=adc.read()
    voltage = adcVal / 4095.0 * 3.3
    print("digitalVal:",digitalVal,"ADC Val:",adcVal,"Voltage:",voltage,"V", end = "  ")
    if digitalVal == 0:
        print("Exceeding")
    else:
        print("Normal")
    time.sleep(0.1)

An image

4.7 DS18B20 数字温度传感器

python

An image

4.8 激光发射器测试

python
from machine import Pin
import time

#建立一个激光对象,将激光器连接到5号引脚,将5号引脚设置为输出模式
laser = Pin(5, Pin.OUT)

while True:
    laser.value(1) # 打开激光器
    time.sleep(2) # 延时2s
    laser.value(0) # 关掉激光
    time.sleep(2) # 延时2s

An image

4.9 一路继电器

python

An image

4.10 雨滴水滴检测

python

An image

4.11 控制电机风扇转动

python

An image

4.12 水泵抽水实验

python

An image

xxxxx

xxxxx

xxxxx

xxxxx

xxxxx

xxxxx

xxxxx

xxxxx