ESP8266 NodeMCU中Lua旋转编码器回调的Upvalue为nil问题
问题分析与解决
你的核心误区在于对Lua闭包(Upvalue)的作用范围理解错误:只有当函数定义在包含目标局部变量的作用域内时,才能捕获该变量作为Upvalue。
代码问题拆解
你当前的turn函数是在setupRotary外部定义的局部函数,它和setupRotary参数里的sv没有任何闭包关联。当rot模块触发回调调用turn时,Lua会在turn的作用域里找sv——找不到局部变量就会去全局表中查找,而你并没有定义全局的sv,自然会出现nil错误。
修正方案
方案1:将turn定义在setupRotary内部
让turn直接处于sv的作用域内,自然捕获该变量作为Upvalue:
local setupRotary = function(sv) local ch, a, b, sw = sv.rotary_ch, sv.rotary_a, sv.rotary_b, sv.rotary_sw print(ch, a, b, sw) rot.init(ch, a, b, 1, sw) sv.last = 0 -- 将turn定义在setupRotary内部,直接捕获当前的sv local turn = function(type, pos, when) print(type, pos, when, sv.value) if type == rotary.TURN then local move = sv.last - pos sv.last = pos sv.x = sv.value + move local tm if tm == nil then tm = tmr.create() tm:register(500, tmr.ALARM_SINGLE, function() setTo(sv) end) end tm:start() end end rot.on(ch, rot.TURN, turn) end
方案2:用工厂函数生成绑定sv的回调
如果需要复用turn的逻辑,可以通过工厂函数返回闭包,显式绑定目标sv:
-- 工厂函数:传入sv,返回绑定了该sv的回调函数 local createTurnHandler = function(sv) return function(type, pos, when) print(type, pos, when, sv.value) if type == rotary.TURN then local move = sv.last - pos sv.last = pos sv.x = sv.value + move local tm if tm == nil then tm = tmr.create() tm:register(500, tmr.ALARM_SINGLE, function() setTo(sv) end) end tm:start() end end end local setupRotary = function(sv) local ch, a, b, sw = sv.rotary_ch, sv.rotary_a, sv.rotary_b, sv.rotary_sw print(ch, a, b, sw) rot.init(ch, a, b, 1, sw) sv.last = 0 -- 生成绑定当前sv的回调 local turn = createTurnHandler(sv) rot.on(ch, rot.TURN, turn) end
关键知识点回顾
- Upvalue是内部函数对外部函数局部变量的引用,必须满足"函数嵌套定义"的结构才能形成闭包。
- 若函数不在目标变量的作用域内定义,Lua会优先查找局部变量,找不到则去全局表中查找,不会自动关联其他作用域的变量。
内容的提问来源于stack exchange,提问作者fanThomas
相关产品推荐
相关产品推荐

