Python脚本移植到ESP8266的MicroPython遇错误求排查
问题排查与移植修复方案
核心问题定位
你遇到的OSError 2在MicroPython环境中通常对应DNS解析失败、目标主机不可达或网络连接超时,同时移植代码存在几处逻辑错误,导致异常无法准确定位且功能失效:
1. 异常捕获过于宽泛,无法定位具体问题
当前代码仅用except:捕获所有异常,只打印固定字符串,完全无法获取异常类型和具体信息。需要修改捕获逻辑,打印异常详情:
except Exception as e: print(f"异常类型: {type(e).__name__}, 详情: {e}")
2. 函数返回值不匹配导致数据处理错误
login()函数返回(response.json(), response)元组,但check_status()中仅用json_data = login()接收,导致json_data是包含JSON数据和响应对象的元组而非纯JSON字典。后续调用get_relay_status(json_data)时,会因元组无法通过键取值触发错误。
3. get_relay_status无返回值,状态对比逻辑失效
原Python代码中该函数返回继电器状态,但移植后仅控制LED却未返回状态值,导致current_status始终为None,状态变化的对比逻辑完全无法执行。
4. mDNS域名解析问题
apex.local是本地mDNS域名,多数ESP8266的MicroPython固件默认不支持mDNS解析,会导致无法解析主机地址,触发OSError 2。建议直接使用Apex控制器的静态IP地址替代域名。
5. urequests资源未释放
urequests的响应对象如果不手动关闭,会导致ESP8266内存泄漏,长期运行可能引发其他异常。
修正后的完整代码
import machine import time import urequests import ubinascii import network # Wi-Fi credentials WIFI_SSID = "CREDS" WIFI_PASSWORD = "CREDS" # Apex controller credentials - 替换为静态IP url = "http://192.168.x.x/cgi-bin/status.json" # 替换成你的Apex实际IP username = "admin" password = "1234" previous_status = None # LED pin LED_PIN = 2 # Connect to Wi-Fi network station = network.WLAN(network.STA_IF) station.active(True) station.connect(WIFI_SSID, WIFI_PASSWORD) while not station.isconnected(): time.sleep(1) print(f'Wi-Fi连接成功,网络信息: {station.ifconfig()}') # Define LED pin as output led = machine.Pin(LED_PIN, machine.Pin.OUT) def login(): credentials = '{}:{}'.format(username, password) encoded_credentials = ubinascii.b2a_base64(credentials.encode())[:-1].decode() headers = {'Authorization': 'Basic {}'.format(encoded_credentials)} try: response = urequests.get(url, headers=headers, timeout=5) # 添加超时 if response.status_code != 200: raise Exception(f"请求失败,状态码: {response.status_code}") json_data = response.json() response.close() # 释放资源 return json_data except Exception as e: if 'response' in locals(): response.close() raise e # 抛出异常让上层处理 # Fetch relay status and toggle LED accordingly def get_relay_status(json_data): for output in json_data['istat']['outputs']: if output['name'] == 'Relay_1': status = output['status'][0] # 控制LED led.value(1 if status == 'ON' else 0) return status # 返回状态值用于对比 return None # 未找到Relay_1时返回None def check_status(): global previous_status try: json_data = login() current_status = get_relay_status(json_data) if current_status is None: print("未找到Relay_1设备") return if previous_status is not None and previous_status != current_status: print(f"继电器状态变更为: {current_status}") previous_status = current_status except Exception as e: print(f"异常类型: {type(e).__name__}, 详情: {e}") finally: time.sleep(5) # 添加延迟,避免频繁请求占用资源 while True: check_status()
额外排查建议
- 验证网络连通性:在ESP8266的REPL中执行
import socket; socket.getaddrinfo("192.168.x.x", 80)(替换为Apex的IP),如果报错则说明网络不通,检查Wi-Fi是否在同一网段、Apex控制器是否在线。 - 检查Apex接口权限:确保使用的账号密码有权限访问
/cgi-bin/status.json接口,可先用浏览器访问该地址验证。 - 固件兼容性:如果仍有问题,尝试更新ESP8266的MicroPython固件到最新稳定版,部分旧固件存在网络模块bug。
内容的提问来源于stack exchange,提问作者DATTO MOTORSPORTS
相关产品推荐
相关产品推荐

