如何列出当前运行的 NodeMCU 固件中内置的所有“romtable”模块?

问题描述

这个问题就像标题一样,差不多就是这样。

固件当然知道里面装的是什么。这是 NodeMCU 的“欢迎”信息:

NodeMCU ESP32 built on nodemcu-build.com provided by frightanic.com
        branch: dev-esp32
        commit: fb12af06e7e01f699d68496b80dae481e355adb7
        SSL: false
        modules: adc,can,dac,file,gpio,i2c,ledc,net,node,ow,sigma_delta,spi,time,tmr,touch,uart,wifi
 build 2021-02-01-23-53-37 powered by Lua 5.1.4 on ESP-IDF v3.3-beta1-1391-g9e70825d1 on SDK IDF
lua: cannot open init.lua
>

我很高兴能得到逗号分隔的“模块”字符串。

由于内置模块通常以它们的名字提供,作为全局变量,它有点类似于 Lua 中的“如何列出所有全局变量”,正如我所期望的,每个人都在普通 Lua 上使用它来检查所有内容包括math 这样的内置函数。然而,这里就不一样了。我查过了。

列出来自 _G 的全局变量并没有揭示 tmruart 等变量的存在。

> =tmr
romtable: 0x3f406e30

> =uart
romtable: 0x3f40acc8

> for k,v in pairs(_G) do print(k.." : "..tostring(v)) end
module : function: 0x3ffc2fa0
require : function: 0x3ffc2fd4
pairs : function: 0x3ffba950
newproxy : function: 0x3ffc90b4
package : table: 0x3ffba8fc
_G : table: 0x3ffc29c0
_VERSION : Lua 5.1
ipairs : function: 0x3ffba888

> for k,v in pairs(package.preload) do print(k.." : "..tostring(v)) end
((nothing!))

> for k,v in pairs(package.loaded) do print(k.." : "..tostring(v)) end
package
_G

如您所见,tmruart 名称已被识别,但它们并未列为 _G内容

有趣的是,即使 math 也没有在 _G 中列出,尽管它显然可用:

> =math
romtable: 0x3f420ef0
> =_G.math
romtable: 0x3f420ef0
> =math.PI
nil
> =math.pi
3.1415926535898

是什么让我想到,好吧,那些 tmruart 怎么样?是的,一样:

> =_G.tmr
romtable: 0x3f406e30
> =_G.uart
romtable: 0x3f40acc8

我们有了。它们未列在 _G 的内容中,但仍可用。

我不是 Lua 或 NodeMCU 的专家,我可能遗漏了一些东西,但是通过挖掘文档,尝试各种 Lua 运行时检查方法,我只是不知道是否/如何从 _G 中列出它们。

我有一种感觉,_G 有两个单独的元方法一个用于列表,一个用于访问/获取,并且只有后一个由固件修补以实际按名称提供模块,而前者没有被修补以列出它们,但这只是一个猜测。

顺便说一下,我在 ESP32 上的 NodeMCU 上遇到了这个问题,dev-esp32 版本(在 https://nodemcu-build.com/ 上可用),但是当我写这篇文章时,我在 ESP8288 上尝试了同样的方法,结果是一样的:math,uart 直接可用,通过_G可用,但在查看_G的内容时没有列出。

作为旁注#2,经过数小时的搜索,我找到了 this video 'ESP8266 NodeMCU - How to know NodeMCU firmware Module info? ',它显示一个“NodeMCU_firmware_info.lua”,也称为“AEW_NodeMCU_info.lua”(可能是品牌问题),它实际上只包含名称变量对的列表,并迭代对各种模块的所有硬编码引用,尝试每个模块的 nil 值..

是的,我知道这会奏效。而这正是我想要避免的。我想读出内置信息,而无需硬编码或暴力破解名称


编辑:按照 koyaanisqatsi 的建议,我检查了 _G 的元表和 __index,结果是 romtable。这是一个转储:

function printtable(t) for k,v in pairs(t) do print(k.." : "..tostring(v)) end end

> printtable(getMetatable(_G))
__index : romtable: 0x3f4218b8

> printtable(getMetatable(_G)['__index'])
assert : lightfunction: 0x40156de8
collectgarbage : lightfunction: 0x40156be8
dofile : lightfunction: 0x40156bac
error : lightfunction: 0x40156b60
gcinfo : lightfunction: 0x40156b44
getfenv : lightfunction: 0x40156b14
getMetatable : lightfunction: 0x40156e8c
loadfile : lightfunction: 0x401570f4
load : lightfunction: 0x40156da4
loadstring : lightfunction: 0x40157120
next : lightfunction: 0x40156aec
pcall : lightfunction: 0x40156600
print : lightfunction: 0x40156a44
rawequal : lightfunction: 0x40156a1c
rawget : lightfunction: 0x401569f8
rawset : lightfunction: 0x4015694c
select : lightfunction: 0x401568f4
setfenv : lightfunction: 0x40156874
setMetatable : lightfunction: 0x4015677c
tonumber : lightfunction: 0x401566e0
tostring : lightfunction: 0x40156cfc
type : lightfunction: 0x401566bc
unpack : lightfunction: 0x40156638
xpcall : lightfunction: 0x401565bc
__Metatable : romtable: 0x3f421b28

所以,至少有一些内置函数最终出现了,但遗憾的是,这两个模块都没有,甚至标准的 mathdebug 都没有。


编辑:我还检查了我的 ESP8266 上的情况。

启动消息:

        branch: release
        commit: 64bbf006898109b936fcc09478cbae9d099885a8
        release: 3.0-master_20200910
        release DTS: 202009090323
        SSL: false
        build type: float
        LFS: 0x40000 bytes total capacity
        modules: adc,bit,cron,encoder,gpio_pulse,pwm2,rtctime,sntp,softuart,wifi
 build 2020-10-10 21:38 powered by Lua 5.1.4 on SDK 3.0.1-dev(fce080e)

和_G的元表:

printtable(getMetatable(_G)['__index'])
string : table: 0x402710e0
table : table: 0x402704cc
debug : table: 0x402719b0
coroutine : table: 0x40271694
math : table: 0x40270d04
ROM : table: 0x3ffef580
assert : function: 0x402424cc
......
......
net : table: 0x40274e9c
sntp : table: 0x40275268
bit : table: 0x40275398
adc : table: 0x40275494
gpio : table: 0x402756e4
tmr : table: 0x40275880
ow : table: 0x40275b04
softuart : table: 0x40275cd8
cron : table: 0x40275e2c
gpiopulse : table: 0x40276120

所以实际上,内置模块确实出现在这里。 现在,这是一个难题,为什么他们不在 ESP32 上?

解决方法

我找到了!耶!

谢谢@koyaanisqatsi。
您关于 _G 元表中的 __index 的建议是正确的,并且对于 ESP8266 的 NodeMCU 3.0.0 固件来说是 100% 完美的,并且几乎是带有 dev-esp32 fb12af06e7e01f699d68496b80dae481e355adb7 固件的 ESP32 解决方案的一半。

在 ESP8266 上,这会检索我想查看的所有键(与其他条目混在一起,但这是一个小问题):

function printtable(t) for k,v in pairs(t) do print(k.." : "..tostring(v)) end end

printtable(getmetatable(_G)['__index'])

string : table: 0x402710e0
table : table: 0x402704cc
debug : table: 0x402719b0
coroutine : table: 0x40271694
math : table: 0x40270d04
ROM : table: 0x3ffef580
assert : function: 0x402424cc
collectgarbage : function: 0x40242230
dofile : function: 0x402421d0
error : function: 0x40242180
gcinfo : function: 0x40242158
getfenv : function: 0x4024211c
getmetatable : function: 0x40242598
loadfile : function: 0x40242890
load : function: 0x4024246c
loadstring : function: 0x402428c8
next : function: 0x402420e4
pcall : function: 0x40241ac4
print : function: 0x4024203c
rawequal : function: 0x40241ff8
rawget : function: 0x40241fc4
rawset : function: 0x40241f8c
select : function: 0x40241ea4
setfenv : function: 0x40241e18
setmetatable : function: 0x40241ce4
tonumber : function: 0x40241be0
tostring : function: 0x402423a4
type : function: 0x40241bac
unpack : function: 0x40241b0c
xpcall : function: 0x40241a74
pwm2 : table: 0x402723b4
encoder : table: 0x402724b4
rtctime : table: 0x40272660
i2c : table: 0x4027282c
spi : table: 0x40272a30
sigma_delta : table: 0x40272af8
node : table: 0x40273398
pipe : table: 0x402735f8
file : table: 0x40273a20
wifi : table: 0x4027472c
net : table: 0x40274e9c
sntp : table: 0x40275268
bit : table: 0x40275398
adc : table: 0x40275494
gpio : table: 0x402756e4
tmr : table: 0x40275880
ow : table: 0x40275b04
softuart : table: 0x40275cd8
cron : table: 0x40275e2c
gpiopulse : table: 0x40276120

如您所见,stringdebug 等内置模块排在最前面,然后所有可选模块(如 owtmr)排在最后。当然,订单不能由pairs()保证。

然后,在 ESP32 的固件上,这段代码只显示了一些内置函数,如 getmetatabletonumber,但没有显示任何模块。但是,事实证明 __index 是一个 romtable 而不是函数,它本身可以有另一个元表!

这次我得到了我想要检查的东西,它甚至清除了所有其他噪音,都只是模块!

function printtable(t) for k,v in pairs(t) do print(k.." : "..tostring(v)) end end

> printtable( getmetatable(getmetatable(_G)['__index'])['__index'] )
time : romtable: 0x3f403458
ow : romtable: 0x3f403600
net : romtable: 0x3f403c60
touch : romtable: 0x3f404a08
node : romtable: 0x3f405218
spi : romtable: 0x3f405570
sigma_delta : romtable: 0x3f405638
ledc : romtable: 0x3f405978
file : romtable: 0x3f405eb0
gpio : romtable: 0x3f406290
can : romtable: 0x3f406578
i2c : romtable: 0x3f406820
wifi : romtable: 0x3f406b78
tmr : romtable: 0x3f406e30
dac : romtable: 0x3f406fc8
adc : romtable: 0x3f407118
uart : romtable: 0x3f40acc8
string : romtable: 0x3f4212a8
table : romtable: 0x3f421ef0
debug : romtable: 0x3f421cc8
coroutine : romtable: 0x3f4217d0
math : romtable: 0x3f420ef0
ROM : romtable: 0x3f422ac8

当然,如果我们有第一个元表,然后是第二个元表,我必须检查我们是否可以更深入 - 而不是,它为零。

> =getmetatable(getmetatable(getmetatable(_G)['__index'])['__index'])
nil

无论如何,非常感谢koyaanisqatsiPiglet


编辑:正如 Marcel 在评论中指出的那样,在 ESP8266 上它实际上就像

> = node.info('build_config')['modules']
adc,can,dac,file,gpio,i2c,ledc,net,node,ow,sigma_delta,spi,time,tmr,touch,uart,wifi

但是,目前这在 ESP32 上是不可能的,因为 node.info() 尚未实现。在那之前,我的 double-getmetatable 解决方法非常有用。