1. 项目概述
如果你正在用CircuitPython捣鼓一块开发板,比如Adafruit的Feather M4 Express或者RP2040,你可能已经写过一些点亮LED、读取传感器的小脚本。一开始,把所有代码都堆在main.py里,感觉还挺直接。但随着项目越来越复杂——比如要同时处理多个传感器输入、控制几个舵机、还要响应按钮事件——你就会发现代码很快变成了一团乱麻,改一处而动全身,调试起来更是噩梦。这时候,函数(Functions)就该登场了。它不是什么高深莫测的黑魔法,而是每个程序员工具箱里最基础、也最强大的“代码打包机”。简单说,函数就是把一段能完成特定任务的代码封装起来,给它起个名字,以后想用的时候,喊一声这个名字就行,不用再把那段代码抄一遍。
在CircuitPython这样的嵌入式环境里,用好函数不仅仅是让代码看起来整洁,它直接关系到项目的可靠性和你的开发效率。想象一下,你的机器人需要检查五个不同的限位开关,每个开关的读取和去抖动逻辑都是一样的。如果没有函数,你就得把同样的代码写五遍;而有了函数,你只需要写一次,然后调用五次。这不仅减少了代码量,更重要的是,当你想改进去抖动算法时,只需要修改函数里的一个地方,五处调用就全部生效了,极大降低了出错概率。本文将带你从最基础的函数定义和调用开始,一直深入到如何利用Python的高级特性,如闭包和lambda表达式,来构建更优雅、更模块化的硬件交互代码,让你在有限的微控制器资源上,写出既强大又易于维护的程序。
2. 函数基础与核心机制解析
2.1 函数的定义、调用与作用域
在CircuitPython中,定义函数和标准Python完全一致,使用def关键字。其核心形式如下:
def function_name(parameter1, parameter2): # 函数体:执行具体任务的代码块 result = parameter1 + parameter2 return result这里,function_name是你为这个代码块起的名字,parameter1和parameter2是形参(Parameters),它们是函数内部的变量名,用于接收外部传入的数据。当你想使用这个函数时,需要进行调用(Call),并提供具体的实参(Arguments):
sum_result = function_name(5, 3) # 5和3是实参,分别传递给parameter1和parameter2 print(sum_result) # 输出:8调用函数时,实参会按顺序赋值给对应的形参。函数执行完毕后,可以通过return语句将一个值返回给调用者。如果没有return语句,函数会默认返回None。
一个关键概念是作用域(Scope)。在函数内部定义的变量(包括形参),其作用域仅限于该函数内部。这意味着在函数外部无法直接访问它们。这种隔离性是好事情,它避免了不同部分的代码因为变量名冲突而相互干扰。例如:
def calculate_power(voltage, current): power = voltage * current # `power` 是局部变量,只在函数内有效 return power # print(power) # 这里如果取消注释,会引发 NameError,因为`power`在函数外部未定义 result = calculate_power(5, 2) # 正确做法:通过返回值获取结果注意:为函数和参数选择清晰、描述性的名称至关重要。好的命名本身就是文档。避免使用
foo、bar、x、y这类无意义的名称,而是使用像read_temperature、debounce_button、calculate_pwm_duty这样的名字,让人一眼就能明白函数的用途。
2.2 参数传递的进阶技巧:默认参数与关键字参数
当函数参数较多,或者某些参数在大部分情况下都有一个常用值时,默认参数(Default Arguments)就非常有用。它允许你在定义函数时为参数指定一个默认值,调用时如果省略该参数,则使用默认值。
def blink_led(pin, times=3, delay=0.5): """控制指定引脚上的LED闪烁。 Args: pin (digitalio.DigitalInOut): LED所在的引脚对象。 times (int): 闪烁次数,默认为3。 delay (float): 每次亮/灭的间隔时间(秒),默认为0.5。 """ for _ in range(times): pin.value = True time.sleep(delay) pin.value = False time.sleep(delay) # 调用方式1:只提供必要参数,使用默认的闪烁3次,每次0.5秒 blink_led(led_pin) # 调用方式2:覆盖默认的闪烁次数 blink_led(led_pin, times=5) # 调用方式3:覆盖所有默认参数 blink_led(led_pin, times=3, delay=0.2)默认参数必须定义在非默认参数之后。这个特性在硬件初始化函数中特别常见,比如初始化一个传感器,其I2C地址、采样率等通常有默认值。
关键字参数(Keyword Arguments)则允许你在调用函数时,通过参数名=值的形式来指定实参。这样做有两个巨大好处:第一,你可以忽略参数的顺序;第二,它极大地提高了代码的可读性。
def configure_sensor(i2c_bus, address=0x68, rate=100, range=2): # ... 配置传感器的代码 ... pass # 使用关键字参数调用,顺序无关,意图清晰 configure_sensor(i2c, range=4, rate=50) # 明确指定量程和采样率对比一下只使用位置参数的调用configure_sensor(i2c, 0x68, 50, 4),哪个更一目了然?在团队协作或几个月后回顾自己的代码时,关键字参数的价值就会凸显出来。
2.3 返回值处理与程序的早期退出
函数通过return语句返回值。一个函数可以返回多个值,实际上这些值是以元组(Tuple)的形式打包返回的。你可以利用元组解包来优雅地接收它们。
def read_sensor_data(): """模拟读取传感器,返回温度和湿度。""" temperature = 25.3 # 模拟读取值 humidity = 60.1 return temperature, humidity # 返回一个元组 (25.3, 60.1) # 通过元组解包接收两个返回值 temp, humi = read_sensor_data() print(f"温度: {temp}°C, 湿度: {humi}%")有时,函数需要在某些条件不满足时提前退出,而不是执行完所有代码。这可以通过在函数体中间使用return来实现,这种模式常被称为守卫子句(Guard Clause)。
def safe_divide(dividend, divisor): """安全除法,避免除零错误。""" if divisor == 0: print("错误:除数不能为零!") return None # 或返回一个特定的错误码,如 -1.0 # 只有当除数不为零时,才执行下面的计算 return dividend / divisor守卫子句将错误检查前置,使得函数的主体逻辑更加清晰,避免了深层嵌套的if-else语句。在硬件编程中,这常用于检查引脚是否已初始化、传感器是否连接正常等前提条件。
3. 函数作为一等公民与高阶应用
3.1 理解“函数即数据”与嵌套函数
在Python(以及CircuitPython)中,函数是一等公民(First-class Citizen)。这意味着函数可以像整数、字符串、列表一样,被赋值给变量、存储在数据结构中、作为参数传递给另一个函数,或者作为另一个函数的返回值。def关键字本质上就是在创建一个函数对象,并将其绑定到你给的名字上。
def greet(name): return f"Hello, {name}!" # 将函数对象赋值给另一个变量 say_hello = greet print(say_hello("World")) # 输出: Hello, World! print(greet) # 输出: <function greet at ...>,这是一个函数对象既然函数可以赋值,那么自然也可以在一个函数内部定义另一个函数,即嵌套函数(Nested Function)。内部函数的作用域仅限于外部函数之内,这对于创建特定用途的辅助函数非常有用,可以避免污染全局命名空间。
def process_sensor_array(sensor_pins): """处理一组传感器引脚,计算平均值并过滤异常值。""" def read_sensor(pin): """内部函数:读取单个模拟传感器引脚的值。""" # 假设使用 analogio 读取 with analogio.AnalogIn(pin) as adc: return adc.value def is_valid(value, lower_bound, upper_bound): """内部函数:判断读数是否在有效范围内。""" return lower_bound <= value <= upper_bound valid_readings = [] for pin in sensor_pins: val = read_sensor(pin) if is_valid(val, 100, 65500): # 假设的有效范围 valid_readings.append(val) if valid_readings: return sum(valid_readings) / len(valid_readings) else: return None # 所有读数均无效在这个例子中,read_sensor和is_valid这两个辅助函数只在process_sensor_array内部有意义。将它们定义为内部函数,使得外部代码无法意外调用它们,代码结构更清晰、封装性更好。
3.2 闭包:携带状态的函数
当一个内部函数引用了其外部函数(enclosing function)的局部变量或参数时,就形成了一个闭包(Closure)。即使外部函数已经执行完毕并返回,内部函数仍然“记住”并可以访问那些被引用的变量。
def make_counter(initial_value=0): """创建一个计数器函数。""" count = initial_value # 外部函数的局部变量 def counter(): nonlocal count # 声明count不是局部变量,而是来自外部作用域 count += 1 return count return counter # 返回内部函数对象 # 创建两个独立的计数器 counter_a = make_counter(10) # 从10开始计数 counter_b = make_counter() # 从0开始计数 print(counter_a()) # 输出: 11 print(counter_a()) # 输出: 12 print(counter_b()) # 输出: 1 print(counter_a()) # 输出: 13 (counter_a的状态是独立的)make_counter每次被调用时,都会创建一个新的局部变量count和一个新的内部函数counter。这个内部函数counter和它“记住”的那个特定的count变量,一起构成了一个闭包。counter_a和counter_b是两个不同的闭包,它们拥有各自独立的count状态。在嵌入式开发中,闭包非常适合用来创建具有特定配置或状态的硬件控制函数。
3.3 高阶函数:将函数作为参数与返回值
高阶函数(Higher-order Function)是指那些以函数作为参数,和/或返回一个函数作为结果的函数。这极大地提升了代码的抽象能力和灵活性。 让我们看一个硬件相关的实用例子:一个通用的信号滤波器生成器。假设我们有来自传感器的原始数据流(比如ADC读数),数据有噪声,我们想应用不同的滤波算法。
def make_low_pass_filter(alpha): """创建一个一阶低通滤波器函数。 Args: alpha (float): 平滑系数 (0 < alpha <= 1)。值越小,滤波越强,响应越慢。 Returns: function: 一个滤波器函数,每次调用传入新值,返回滤波后的值。 """ filtered_value = None # 闭包保存的滤波状态 def filter_function(new_value): nonlocal filtered_value if filtered_value is None: filtered_value = new_value # 第一次调用,直接使用原始值 else: # 一阶低通滤波公式: y[n] = alpha * x[n] + (1-alpha) * y[n-1] filtered_value = alpha * new_value + (1 - alpha) * filtered_value return filtered_value return filter_function def make_moving_average(window_size): """创建一个移动平均滤波器函数。""" readings = [] # 闭包保存的窗口数据 def filter_function(new_value): nonlocal readings readings.append(new_value) if len(readings) > window_size: readings.pop(0) # 移除最旧的数据 return sum(readings) / len(readings) return filter_function # 使用高阶函数创建特定配置的滤波器 # 创建一个强低通滤波器(平滑系数0.1) smooth_sensor = make_low_pass_filter(alpha=0.1) # 创建一个5点移动平均滤波器 stable_sensor = make_moving_average(window_size=5) # 模拟数据流 raw_data = [10, 12, 11, 15, 9, 10, 13, 8, 12, 11] print("原始数据 | 低通滤波 | 移动平均") for raw in raw_data: low_pass_val = smooth_sensor(raw) moving_avg_val = stable_sensor(raw) print(f"{raw:^10} | {low_pass_val:^10.2f} | {moving_avg_val:^10.2f}")make_low_pass_filter和make_moving_average都是高阶函数,它们根据传入的参数(alpha,window_size)返回一个定制好的滤波函数。这样,我们就能轻松地为不同的传感器或不同的应用场景生成不同的滤波器,而无需重复编写滤波逻辑。
4. Lambda表达式与匿名函数的实战应用
4.1 Lambda表达式的基本语法与使用场景
Lambda表达式是一种创建匿名函数(即没有名字的函数)的简洁语法。它的基本形式是:lambda 参数列表: 表达式。这个表达式的结果会自动成为lambda函数的返回值。
# 一个普通的求平方函数 def square_def(x): return x * x # 用lambda表达式实现同样的功能 square_lambda = lambda x: x * x print(square_def(5)) # 输出: 25 print(square_lambda(5)) # 输出: 25lambda函数的特点是“小而美”:它仅限于单个表达式,不能包含复杂的语句块(如循环、if-elif-else多分支,但可以使用条件表达式a if condition else b)。因此,它最适合用于那些逻辑简单、无需复用的场景,尤其是作为参数传递给高阶函数。
4.2 在硬件交互中活用Lambda表达式
在嵌入式编程中,lambda表达式最常见的用途是与高阶函数配合,快速定义回调函数或条件判断函数,使代码更加紧凑和声明式。
场景一:事件触发与条件检查假设我们有一个系统,需要根据多个传感器的读数组合来触发一个动作(比如报警)。我们可以用一个高阶函数来封装这个“条件检查-触发动作”的逻辑。
class EventManager: def __init__(self): self.conditions_actions = [] # 存储(条件函数,动作函数)对 def add_trigger(self, condition_func, action_func): """添加一个触发规则:当condition_func返回True时,执行action_func。""" self.conditions_actions.append((condition_func, action_func)) def update(self, sensor_data): """检查所有触发规则。""" for condition, action in self.conditions_actions: if condition(sensor_data): # 调用条件函数 action() # 调用动作函数 # 初始化事件管理器 manager = EventManager() # 假设我们有传感器数据字典 sensor_data_template = {'temp': 0, 'humidity': 0, 'light': 0} # 使用lambda快速添加触发规则 # 规则1:温度超过30度且光照低于100时,打开风扇 manager.add_trigger( condition_func=lambda data: data['temp'] > 30 and data['light'] < 100, action_func=lambda: print("【动作】开启冷却风扇") ) # 规则2:湿度高于80%时,启动除湿器并亮起警告灯 manager.add_trigger( condition_func=lambda data: data['humidity'] > 80, action_func=lambda: (print("【动作】启动除湿器"), print("【动作】点亮黄色警告灯"))[0] ) # 模拟更新 sensor_data_template['temp'] = 35 sensor_data_template['light'] = 50 manager.update(sensor_data_template) # 应触发规则1这里,condition_func和action_func预期都是函数。我们用lambda表达式在现场即时创建了这些简单的函数,而无需先去别处用def定义它们,使得添加新规则的代码非常集中和易读。
场景二:简化硬件抽象接口回顾之前提到的去抖动器(Debouncer)的例子。去抖动器需要一个能返回布尔值的无参数函数。lambda非常适合用来包装硬件读取操作。
import board import digitalio from adafruit_debouncer import Debouncer # 假设使用这个库 # 初始化一个硬件按钮引脚 button_pin = digitalio.DigitalInOut(board.D12) button_pin.direction = digitalio.Direction.INPUT button_pin.pull = digitalio.Pull.UP # 启用内部上拉电阻 # 创建一个去抖动器对象。核心是传入一个lambda,它返回按钮的当前电平值。 # 注意:按钮按下时,引脚被拉低,所以value为False。 button = Debouncer(lambda: not button_pin.value) # 按下时返回True,更符合直觉 while True: button.update() # 更新去抖动器状态 if button.rose: # 检测到上升沿(按钮释放) print("按钮被按下了一次!") time.sleep(0.01)lambda: not button_pin.value创建了一个匿名函数,每次button.update()内部调用它时,它都会读取button_pin.value并取反。这样,去抖动器就与具体的硬件引脚解耦了。它不关心你是如何获取这个布尔值的,是从GPIO引脚、ADC比较器,还是I2C扩展器,它只要求一个无参数的、返回布尔值的函数。这种设计极大地提高了代码的模块化和可测试性。
4.3 Lambda与闭包结合的强大模式
Lambda表达式经常与闭包结合,创建出非常简洁而强大的行为封装。
def make_comparator(threshold, mode='greater'): """创建一个比较器函数,用于与阈值比较。""" if mode == 'greater': return lambda value: value > threshold elif mode == 'less': return lambda value: value < threshold elif mode == 'equal': return lambda value: value == threshold else: return lambda value: False # 默认返回一个总是False的函数 # 创建不同的比较器 overheat_check = make_comparator(85.0, 'greater') # 检查是否超过85度 undervoltage_check = make_comparator(3.0, 'less') # 检查电压是否低于3.0V target_check = make_comparator(1500, 'equal') # 检查是否等于1500 # 使用这些比较器 temperature = 90.5 voltage = 2.8 encoder_pos = 1500 print(f"温度过高: {overheat_check(temperature)}") # True print(f"电压过低: {undervoltage_check(voltage)}") # True print(f"到达目标位置: {target_check(encoder_pos)}") # Truemake_comparator是一个工厂函数,它根据传入的threshold和mode参数,返回一个特定的lambda函数。这个lambda函数就是一个闭包,它“记住”了创建时的threshold和mode逻辑。这样,我们就用很少的代码,动态生成了多种用途的判断函数。
实操心得:虽然lambda很强大,但切忌滥用。如果函数逻辑超过一行表达式,或者需要复用,老老实实用
def定义具名函数是更好的选择。代码的可读性永远比炫技更重要。在团队项目中,清晰的具名函数能让其他成员(包括未来的你)更快理解代码意图。
5. 项目实战:构建一个模块化的传感器数据采集系统
5.1 系统架构设计与模块划分
让我们综合运用以上所有概念,设计一个用于温室监控的小型数据采集系统。这个系统需要从多个不同类型的传感器(温度/湿度、光照强度、土壤湿度)读取数据,进行初步处理(滤波、校准),然后在满足特定条件时(如温度过高)触发执行器(风扇、补光灯)。
我们将系统划分为以下几个模块,每个模块都围绕函数构建:
- 传感器驱动层:为每种传感器提供统一的读取接口。使用函数封装具体的通信协议(如I2C、ADC读取)。
- 数据处理层:提供滤波、校准等函数。利用高阶函数和闭包创建可配置的滤波器。
- 逻辑控制层:定义事件规则(条件-动作对)。使用lambda表达式和高阶函数来灵活组合条件。
- 主循环:协调以上所有模块,周期性地执行数据采集、处理和逻辑判断。
5.2 核心代码实现与解析
首先,我们定义传感器驱动函数。为了统一接口,我们约定每个驱动函数返回一个字典。
import time import board import busio import analogio import adafruit_dht # 假设使用DHT22温湿度传感器 import adafruit_bh1750 # 假设使用BH1750光照传感器 # --- 传感器驱动层 (统一返回字典) --- def read_dht22(pin): """读取DHT22温湿度传感器。""" dht_device = adafruit_dht.DHT22(pin) try: temperature = dht_device.temperature humidity = dht_device.humidity # 模拟校准偏移,实际中可能需要更复杂的公式 temperature_calibrated = temperature - 0.5 return {'temp': temperature_calibrated, 'humidity': humidity, 'sensor': 'DHT22'} except RuntimeError as e: print(f"DHT22读取失败: {e}") return {'temp': None, 'humidity': None, 'sensor': 'DHT22', 'error': True} finally: dht_device.exit() def read_bh1750(i2c_bus): """读取BH1750光照传感器。""" sensor = adafruit_bh1750.BH1750(i2c_bus) lux = sensor.lux return {'light': lux, 'sensor': 'BH1750'} def read_soil_moisture(adc_pin): """通过ADC读取土壤湿度传感器(模拟量)。""" with analogio.AnalogIn(adc_pin) as adc: # 将16位ADC值转换为百分比(假设干燥时值最大,湿润时值最小) raw_value = adc.value # 假设校准值:干燥=50000,湿润=15000 moisture_percent = max(0, min(100, (50000 - raw_value) / (50000 - 15000) * 100)) return {'soil_moisture': moisture_percent, 'sensor': 'Soil Moisture'}接下来,实现数据处理层。我们创建一个高阶函数工厂,用于生成数据过滤器。
# --- 数据处理层 --- def make_threshold_filter(sensor_key, min_val, max_val): """创建一个阈值过滤器,丢弃超出范围的数据。 返回一个函数,该函数接收传感器数据字典,如果指定键的值在[min_val, max_val]内, 则返回该值,否则返回None。 """ def filter_func(data): value = data.get(sensor_key) if value is not None and min_val <= value <= max_val: return value else: print(f"过滤器告警: {sensor_key}={value} 超出范围[{min_val}, {max_val}]") return None return filter_func def make_exponential_smoother(alpha, initial_value=None): """创建一个指数平滑滤波器(一阶低通滤波)。""" filtered = initial_value def smoother(new_value): nonlocal filtered if filtered is None: filtered = new_value else: filtered = alpha * new_value + (1 - alpha) * filtered return filtered return smoother # 初始化具体的滤波器 temp_filter = make_threshold_filter('temp', -10, 60) # 温度合理范围 light_smoother = make_exponential_smoother(alpha=0.3) # 光照平滑滤波然后,构建逻辑控制层。我们设计一个简单的事件管理器。
# --- 逻辑控制层 --- class GreenhouseController: def __init__(self): self.rules = [] # 存储(条件函数, 动作描述, 动作函数)三元组 def add_rule(self, condition_func, action_desc, action_func): """添加一条控制规则。""" self.rules.append((condition_func, action_desc, action_func)) def evaluate(self, sensor_data): """根据当前传感器数据评估所有规则,并执行触发的动作。""" triggered_actions = [] for condition, desc, action in self.rules: if condition(sensor_data): print(f"【规则触发】{desc}") action() # 执行动作,例如控制GPIO triggered_actions.append(desc) return triggered_actions # 初始化控制器 controller = GreenhouseController() # 定义动作函数(在实际项目中,这里会控制具体的GPIO引脚) def turn_on_fan(): print(" -> 执行:开启通风风扇") # digitalio.DigitalInOut(fan_pin).value = True def turn_on_light(): print(" -> 执行:开启补光灯") # digitalio.DigitalInOut(light_pin).value = True def water_plants(): print(" -> 执行:启动浇水泵(持续2秒)") # digitalio.DigitalInOut(pump_pin).value = True # time.sleep(2) # digitalio.DigitalInOut(pump_pin).value = False # 使用lambda表达式添加规则,条件判断一目了然 controller.add_rule( condition_func=lambda data: data.get('temp', 0) > 28.0, action_desc="温度高于28°C,需要降温", action_func=turn_on_fan ) controller.add_rule( condition_func=lambda data: data.get('light', 10000) < 2000 and 8 <= time.localtime().tm_hour < 18, action_desc="白天光照不足(<2000 lux),需要补光", action_func=turn_on_light ) controller.add_rule( condition_func=lambda data: data.get('soil_moisture', 50) < 30.0, action_desc="土壤湿度低于30%,需要浇水", action_func=water_plants )最后,编写主循环,将所有模块串联起来。
# --- 主程序 --- def main(): # 硬件初始化 (模拟环境) print("温室监控系统启动...") # 假设的硬件初始化 # i2c = busio.I2C(board.SCL, board.SDA) # dht_pin = board.D5 # soil_adc_pin = board.A1 # 数据存储 sensor_history = {'temp': [], 'light': [], 'soil_moisture': []} try: while True: current_data = {} # 1. 采集数据 (模拟读取) # 实际项目中,这里会调用 read_dht22(dht_pin) 等函数 current_data.update({'temp': 25.5, 'humidity': 65}) # 模拟DHT22 current_data.update({'light': 4500}) # 模拟BH1750 current_data.update({'soil_moisture': 45.0}) # 模拟土壤湿度 # 2. 数据处理:应用滤波 filtered_temp = temp_filter(current_data) if filtered_temp is not None: current_data['temp'] = filtered_temp smoothed_light = light_smoother(current_data['light']) current_data['light_smoothed'] = smoothed_light # 存入新键 # 3. 记录历史(简单示例,只记录最近5次) for key in sensor_history: if key in current_data: sensor_history[key].append(current_data[key]) if len(sensor_history[key]) > 5: sensor_history[key].pop(0) # 4. 逻辑控制:评估并执行规则 print(f"\n[{time.monotonic():.1f}s] 传感器数据: {current_data}") triggered = controller.evaluate(current_data) if triggered: print(f" 本次循环触发的动作: {triggered}") else: print(" 状态正常,无需动作。") # 5. 简单显示(可替换为OLED显示) print(f" 温度历史: {sensor_history['temp']}") print(f" 平滑光照: {smoothed_light:.1f} lux") time.sleep(5) # 每5秒采集一次 except KeyboardInterrupt: print("\n程序被用户中断。") if __name__ == "__main__": main()5.3 系统优势与扩展思路
这个实战项目展示了如何运用函数式编程思想来构建一个清晰、模块化、易于扩展的嵌入式系统:
- 高内聚低耦合:每个函数职责单一(如
read_dht22只负责读取DHT22)。修改一个传感器驱动不会影响数据处理或逻辑控制。 - 可配置性:通过高阶函数(如
make_threshold_filter,make_exponential_smoother)可以动态创建不同参数的数据处理器,无需编写多个类似函数。 - 声明式逻辑:在
GreenhouseController中添加规则时,使用lambda表达式使得“在什么条件下执行什么动作”这一逻辑非常直观,接近于自然语言描述。 - 易于测试:由于函数之间依赖明确,你可以很容易地为每个函数编写单元测试。例如,可以模拟传感器数据来测试过滤器和控制规则,而无需连接真实硬件。
扩展思路:
- 增加传感器:只需编写一个新的驱动函数(如
read_co2_sensor),并在主循环中调用它、将其数据加入current_data字典即可。控制规则可以立即使用新数据。 - 复杂条件:如果条件逻辑变得复杂,可以将lambda表达式替换为用
def定义的具名函数,提高可读性。 - 状态持久化:可以使用闭包来创建带有状态的数据记录器或报警计数器。
- 网络通信:可以将
current_data字典通过JSON格式发送到服务器,发送逻辑也可以封装成一个独立的函数或模块。
通过这个项目,你应该能深刻体会到,在CircuitPython项目中,函数不仅仅是代码复用的工具,更是构建复杂、可维护嵌入式应用的基石。从简单的封装到高阶函数和闭包,这些特性共同为你提供了强大的抽象能力,让你能更专注于解决实际问题,而不是纠缠于混乱的代码细节。