feat(pybot): bot4cj 的 Python 迁移版(.mt 解析 + 硬件驱动 + 插件系统)

- parser: FSM 解析器与仓颉版逐语义对齐,修 S01/S02/S04/Q05
- robot/hardvars: 六类硬件变量工厂,修 r.cj:911 重名 FIXME、Q07 倒序释放
- plugin: Python 插件替代 Lua 桥接,修 S07 三级路径查找
- hd: i2c(每fd锁 Q03/S06)/spi/servo/kalman/mpu6050(S05/Q06)/speaker(S03 实声)
- modules: PCA9685 与 Lua 版逐字节对齐;ADS1115 重写(experimental);ADS1256 空桩
- tests: 42 项 pytest 全绿(Mock 总线 journal 断言)
- CLI: --dry-run / --test roundtrip / --verbose;运行时零第三方依赖
This commit is contained in:
qing committed 2026-08-09 18:57:03 +00:00
1 parent 85eaa2114e
commit bba7ee3eb4
31 files changed
+2155

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
"""ADS1115 16 位 I2C ADC 插件(experimental)
⚠️ bot4cj/modules/ADS1115.lua 是 PCA9685 的复制残片(引用未定义的
prescale、写的是 PCA9685 寄存器,运行必错),无忠实移植价值。
本文件按 ADS1115 数据手册写干净的最小单次转换实现,未经实机验证。
ctx 元数据:name / fd(I2C 引用展开)/ address(如 "0x48")
call 实时参数:channel(0~3,单端输入 AINx)
call 返回:{"raw": 有符号 16 位原始值, "voltage": 电压(±4.096V 量程)}
"""
import logging
import struct
import time
from pybot.hd import i2c
log = logging.getLogger("pybot.modules.ADS1115")
REG_CONVERSION = 0x00
REG_CONFIG = 0x01
# 单次转换配置:OS=1(开始) | MUX=100+ch(单端) | PGA=001(±4.096V) | MODE=1(单次)
# 低字节:DR=100(128SPS) | 比较器禁用(11)
_CONFIG_HI_BASE = 0b1_100_001_1 # channel 0,其余通道 MUX +ch
_CONFIG_LO = 0b100_0_0_0_11
FSR = 4.096 # 满量程 ±4.096V
def build(ctx):
"""无需初始化(单次转换模式按需触发),仅校验设备可达"""
fd = int(ctx["fd"])
addr = int(str(ctx["address"]), 0)
log.info("ADS1115 build (experimental): fd=%d addr=0x%02X", fd, addr)
return {"result": "OK"}
def call(ctx):
"""触发一次单次转换并读回结果"""
fd = int(ctx["fd"])
addr = int(str(ctx["address"]), 0)
channel = int(ctx.get("channel", 0)) & 0x03
config_hi = _CONFIG_HI_BASE + (channel << 4)
with i2c.transaction(fd):
i2c.set_addr(fd, addr)
i2c.write_block(fd, REG_CONFIG, bytes([config_hi, _CONFIG_LO]))
time.sleep(0.009) # 128SPS 一次转换约 8ms
data = i2c.read_block(fd, REG_CONVERSION, 2)
raw = struct.unpack(">h", data)[0] # 大端有符号 16 位
return {"result": "OK", "raw": raw, "voltage": raw * FSR / 32768.0}
def destroy(ctx):
"""单次转换模式空闲即低功耗,无需复位"""
log.info("ADS1115 destroy")
return {"result": "OK"}