Для того, щоб краще зрозуміти, як влаштоване системне оточення змінних і констант NodeMCU, варто хоча б раз на них поглянути.
Кожен C-модуль, доданий до складу прошивки може утворювати свої структури з посилань на функції та їх методи, константи та дані у тимчасових змінних. А отже, з цієї точки зору, також корисно подивитися, що є під капотом. Ми впевнені, що кожен знайде для себе щось нове і корисне.
Модулів у прошивці багато, а тому ми не будемо наводити повний перелік даних, а візьмемо лише деякі “системні” модулі.
Взявшись за вивід всього того, що є у пам’яті прошивки NodeMCU, ми не очікували, що там зберігається так багато різноманітних даних.
У нашому випадку, маємо наступну версію прошивки:
1 2 3 4 5 6 7 8 9 10 |
NodeMCU 3.0.0.0 built on nodemcu-build.com provided by frightanic.com branch: release commit: 4f6792773f93f36a7255cfd28dca7aa6c4aa9552 release: 3.0-release_20201107 release DTS: 202011071523 SSL: true build type: float LFS: 0x0 bytes total capacity modules: adc, bme280, cron, file, gpio, http, i2c, mdns, net, node, rtctime, sjson, sntp, softuart, tmr, uart, wifi, wps, ws2812, tls build 2020-12-21 13:25 powered by Lua 5.1.4 on SDK 3.0.1-dev(fce080e) |
Цикл виводу даних
Найпростіший варіант, як вивести деякі з наявних даних у консоль, це запустити цикл, що роздруковує вміст глобальної змінної оточення _G:
Команда у консолі: for k, v in pairs(_G) do print(k, v) end
У відповідь отримаємо приблизно такі дані:
1 2 3 4 5 6 7 8 9 |
module function: 0x40242288 pairs function: 0x3fff0690 __index table: 0x3ffef77c package table: 0x3fff1138 _G table: 0x3fff0660 _VERSION Lua 5.1 ipairs function: 0x3fff0c90 newproxy function: 0x3fff0e88 require function: 0x402420d8 |
Але, якщо вивести на екран дані, що розташовано на рівень глибше:
Команда у консолі: for k, v in pairs(_G.__index) do print(k, v) end
вже отримаємо величезну купу цікавої інформації:
string | table | 0x4029eab4 |
table | table | 0x4029deb4 |
debug | table | 0x4029f380 |
coroutine | table | 0x4029f068 |
math | table | 0x4029e6d8 |
ROM | table | 0x3ffef77c |
assert | function | 0x40246bbc |
collectgarbage | function | 0x40246920 |
dofile | function | 0x402468c0 |
error | function | 0x40246870 |
gcinfo | function | 0x40246848 |
getfenv | function | 0x4024680c |
getmetatable | function | 0x40246c88 |
loadfile | function | 0x40246f80 |
load | function | 0x40246b5c |
loadstring | function | 0x40246fb8 |
next | function | 0x402467d4 |
pcall | function | 0x402461b4 |
function | 0x4024672c | |
rawequal | function | 0x402466e8 |
rawget | function | 0x402466b4 |
rawset | function | 0x4024667c |
select | function | 0x40246594 |
setfenv | function | 0x40246508 |
setmetatable | function | 0x402463d4 |
tonumber | function | 0x402462d0 |
tostring | function | 0x40246a94 |
type | function | 0x4024629c |
unpack | function | 0x402461fc |
xpcall | function | 0x40246164 |
tls | table | 0x4029fff4 |
rtctime | table | 0x402a0258 |
uart | table | 0x402a047c |
i2c | table | 0x402a0624 |
sjson | table | 0x402a0804 |
wps | table | 0x402a09a0 |
node | table | 0x402a1298 |
bme280 | table | 0x402a158c |
pipe | table | 0x402a162c |
file | table | 0x402a1a54 |
wifi | table | 0x402a274c |
net | table | 0x402a2e20 |
sntp | table | 0x402a31ec |
adc | table | 0x402a32e8 |
gpio | table | 0x402a3528 |
tmr | table | 0x402a36c4 |
ws2812 | table | 0x402a3a2c |
http | table | 0x402a3be4 |
mdns | table | 0x402a3c74 |
softuart | table | 0x402a3e48 |
cron | table | 0x402a3fa4 |
Зверніть увагу, що більшість назв збігається з назвами C-модулів у прошивці.
Та й це ще не все. Якщо рекурсивно вивести на екран абсолютно весь вміст змінної _G, то ми отримаємо 420 рядків за 21 ітерацію.
Але звісно, як ми вже казали, у складі цієї прошивки, багато додаткових C-модулів, як от adc, bme280, cron, file, gpio, http, i2c, mdns, net, node, rtctime, sjson, sntp, softuart, tmr, uart, wifi, wps, ws2812, tls
Все, що ми самостійно підключили генеруючи прошивку на сайті nodemcu-build.com, вважаємо додатковими модулями.
Крім того, що додаткових модулів дуже багато, призначення та їх методи відносно добре описано у документації на прошивку ось тут:
https://nodemcu.readthedocs.io/en/release/
І тому ми не будемо на них зупинятися.
Якщо дані про згадані додаткові компоненти відкинути, то отримаємо ось такий набір змінних і констант системного оточення _G, які описують інтерфейси доступу до вбудованих (які включено до прошивки за замовчуванням) функцій та їх даних:
_G | table |
_G.module | function |
_G.pairs | function |
_G.__index | table |
_G.__index.string | table |
_G.__index.string.__index | table |
_G.__index.string.__mod | function |
_G.__index.string.byte | function |
_G.__index.string.char | function |
_G.__index.string.dump | function |
_G.__index.string.find | function |
_G.__index.string.format | function |
_G.__index.string.gfind | function |
_G.__index.string.gmatch | function |
_G.__index.string.gsub | function |
_G.__index.string.len | function |
_G.__index.string.lower | function |
_G.__index.string.match | function |
_G.__index.string.rep | function |
_G.__index.string.reverse | function |
_G.__index.string.sub | function |
_G.__index.string.upper | function |
_G.__index.table | table |
_G.__index.table.concat | function |
_G.__index.table.foreach | function |
_G.__index.table.foreachi | function |
_G.__index.table.getn | function |
_G.__index.table.maxn | function |
_G.__index.table.insert | function |
_G.__index.table.remove | function |
_G.__index.table.setn | function |
_G.__index.table.sort | function |
_G.__index.debug | table |
_G.__index.debug.getregistry | function |
_G.__index.debug.getstrings | function |
_G.__index.debug.traceback | function |
_G.__index.coroutine | table |
_G.__index.coroutine.create | function |
_G.__index.coroutine.resume | function |
_G.__index.coroutine.running | function |
_G.__index.coroutine.status | function |
_G.__index.coroutine.wrap | function |
_G.__index.coroutine.yield | function |
_G.__index.math | table |
_G.__index.math.abs | function |
_G.__index.math.ceil | function |
_G.__index.math.floor | function |
_G.__index.math.max | function |
_G.__index.math.min | function |
_G.__index.math.pow | function |
_G.__index.math.random | function |
_G.__index.math.randomseed | function |
_G.__index.math.sqrt | function |
_G.__index.math.pi | 3.1415926535898 |
_G.__index.math.huge | inf |
_G.__index.ROM | table |
_G.__index.assert | function |
_G.__index.collectgarbage | function |
_G.__index.dofile | function |
_G.__index.error | function |
_G.__index.gcinfo | function |
_G.__index.getfenv | function |
_G.__index.getmetatable | function |
_G.__index.loadfile | function |
_G.__index.load | function |
_G.__index.loadstring | function |
_G.__index.next | function |
_G.__index.pcall | function |
_G.__index.print | function |
_G.__index.rawequal | function |
_G.__index.rawget | function |
_G.__index.rawset | function |
_G.__index.select | function |
_G.__index.setfenv | function |
_G.__index.setmetatable | function |
_G.__index.tonumber | function |
_G.__index.tostring | function |
_G.__index.type | function |
_G.__index.unpack | function |
_G.__index.xpcall | function |
_G.__index.pipe | table |
_G.__index.pipe.create | function |
_G.package | table |
_G.package.preload | table |
_G.package.loadlib | function |
_G.package.path | ?.lc;?.lua |
_G.package.loaders | table |
_G.package.loaders.1 | function |
_G.package.loaders.2 | function |
_G.package.loaders.3 | function |
_G.package.loaders.4 | function |
_G.package.cpath | |
_G.package.loaded | table |
_G.package.loaded.package | table |
_G.package.config | /;?!- |
_G.package.seeall | function |
_G._G | table |
_G._VERSION | Lua 5.1 |
_G.ipairs | function |
_G.newproxy | function |
_G.require | function |
Які ми можемо зробити відкриття, виходячи з цієї інформації? – Щонайменше, ми можемо бачити перелік вбудованих у прошивку C-модулів та їх методів, про які ми могли навіть не підозрювати і які ми можемо використовувати під час розробки на рівні Lua (а деякі з них, взагалі, являють собою частину мови Lua):
Примітка: звернуть увагу, що на ці модулі і їх методи немає документації прошивки NodeMCU, як на зовнішні C-модулі, але вони добре описані у NodeMCU Reference Manual, тобто у документації на мову Lua безпосередньо: https://nodemcu.readthedocs.io/en/release/nodemcu-lrm/
Модуль string | |
_G.__index.string | table |
_G.__index.string.__index | table |
_G.__index.string.__mod | function |
_G.__index.string.byte | function |
_G.__index.string.char | function |
_G.__index.string.dump | function |
_G.__index.string.find | function |
_G.__index.string.format | function |
_G.__index.string.gfind | function |
_G.__index.string.gmatch | function |
_G.__index.string.gsub | function |
_G.__index.string.len | function |
_G.__index.string.lower | function |
_G.__index.string.match | function |
_G.__index.string.rep | function |
_G.__index.string.reverse | function |
_G.__index.string.sub | function |
_G.__index.string.upper | function |
Модуль table | |
_G.__index.table | table |
_G.__index.table.concat | function |
_G.__index.table.foreach | function |
_G.__index.table.foreachi | function |
_G.__index.table.getn | function |
_G.__index.table.maxn | function |
_G.__index.table.insert | function |
_G.__index.table.remove | function |
_G.__index.table.setn | function |
_G.__index.table.sort | function |
Модуль gebug | |
_G.__index.debug | table |
_G.__index.debug.getregistry | function |
_G.__index.debug.getstrings | function |
_G.__index.debug.traceback | function |
Модуль coroutine | |
_G.__index.coroutine | table |
_G.__index.coroutine.create | function |
_G.__index.coroutine.resume | function |
_G.__index.coroutine.running | function |
_G.__index.coroutine.status | function |
_G.__index.coroutine.wrap | function |
_G.__index.coroutine.yield | function |
Модуль math | |
_G.__index.math | table |
_G.__index.math.abs | function |
_G.__index.math.ceil | function |
_G.__index.math.floor | function |
_G.__index.math.max | function |
_G.__index.math.min | function |
_G.__index.math.pow | function |
_G.__index.math.random | function |
_G.__index.math.randomseed | function |
_G.__index.math.sqrt | function |
_G.__index.math.pi | 3.1415926535898 |
_G.__index.math.huge | inf |
Модуль pipe | |
_G.__index.pipe | table |
_G.__index.pipe.create | function |
Інші, важливі методи, що ми щоденно застосовуємо | |
_G.module | function |
_G.pairs | function |
_G.__index.assert | function |
_G.__index.collectgarbage | function |
_G.__index.dofile | function |
_G.__index.error | function |
_G.__index.gcinfo | function |
_G.__index.getfenv | function |
_G.__index.getmetatable | function |
_G.__index.loadfile | function |
_G.__index.load | function |
_G.__index.loadstring | function |
_G.__index.next | function |
_G.__index.pcall | function |
_G.__index.print | function |
_G.__index.rawequal | function |
_G.__index.rawget | function |
_G.__index.rawset | function |
_G.__index.select | function |
_G.__index.setfenv | function |
_G.__index.setmetatable | function |
_G.__index.tonumber | function |
_G.__index.tostring | function |
_G.__index.type | function |
_G.__index.unpack | function |
_G.__index.xpcall | function |
_G.ipairs | function |
_G.newproxy | function |
_G.require | function |
Модуль package | |
_G.package | table |
_G.package.preload | table |
_G.package.loadlib | function |
_G.package.path | ?.lc;?.lua |
_G.package.loaders | table |
_G.package.loaders.1 | function |
_G.package.loaders.2 | function |
_G.package.loaders.3 | function |
_G.package.loaders.4 | function |
_G.package.cpath | |
_G.package.loaded | table |
_G.package.loaded.package | table |
_G.package.config | /;?!- |
_G.package.seeall | function |
Наприклад, візьмемо вбудований модуль математичних функцій math
. Знайдене у системному оточенні, означає, що в даному модулі є методи і можемо їх застосовувати
math.abs(), |
math.ceil(), |
math.floor(), |
math.max(), |
math.min(), |
math.pow(), |
math.random(), |
math.randomseed(), |
math.sqrt(). |
Також маємо вектори посилання на значення для констант:
math.pi == 3.1415926535898
math.huge == inf -- тобто infinity, нескінченність
По аналогії, з системного оточення можливо виокремити й інші функції та використовувати у програмуванні мікроконтролера ESP8266 на рівні викликів у Lua.
Рекурсивний вивід даних
Щоб отримати більш повний перелік системного оточення радимо скористатися прикладом з офіційного набору файлів прошивки, що через обгортку в coroutine, дозволяє запустити рекурсивний вивід даних у консоль розробника.
Для цього знадобиться скачати і завантажити у контролер файл lua-модуля cohelper.lua:
https://github.com/nodemcu/nodemcu-firmware/blob/release/lua_modules/cohelper/cohelper.lua
Вміст файлу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
--[[ A coroutine Helper T. Ellison, June 2019 This version of couroutine helper demonstrates the use of corouting within NodeMCU execution to split structured Lua code into smaller tasks ]] --luacheck: read globals node local modname = ... local function taskYieldFactory(co) local post = node.task.post return function(nCBs) -- upval: co,post post(function () -- upval: co, nCBs coroutine.resume(co, nCBs or 0) end) return coroutine.yield() + 1 end end return { exec = function(func, ...) -- upval: modname package.loaded[modname] = nil local co = coroutine.create(func) return coroutine.resume(co, taskYieldFactory(co), ... ) end } |
Повний приклад запуску рекурсії тут:
https://nodemcu.readthedocs.io/en/release/lua-modules/cohelper/#full-example
Вміст файлу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
require "cohelper".exec( function(taskYield, list) local s, n, nCBs = {}, 0, 0 local function list_entry (name, v) -- upval: taskYield, nCBs print(name, v) n = n + 1 if n % 20 == 0 then nCBs = taskYield(nCBs) end if type(v):sub(-5) ~= 'table' or s[v] or name == 'Reg.stdout' then return end s[v]=true for k,tv in pairs(v) do list_entry(name..'.'..k, tv) end s[v] = nil end for k,v in pairs(list) do list_entry(k, v) end print ('Total lines, print batches = ', n, nCBs) end, {_G = _G, Reg = debug.getregistry(), ROM = ROM} ) |
Все що потрібно, щоб все запустити рекурсивний вивід даних системного оточення у консоль, у вас має бути два файли у контролері, на які вказано посилання.
Наприклад так:
cohelper.lua
cohelper_full_example.lua
, який сам викликаєcohelper.lua
через метод require.- запустити
cohelper_full_example.lua
черезdofile()
.
Що ми знайшли для себе
Серед більшості відомих нам функцій Lua, таких як:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
collectgarbage(); dofile(); loadfile(); load(); loadstring(); pcall(); print(); tonumber(); tostring(); type(); unpack(); ipairs(); require(); |
ми для себе знайшли кілька, з якими ми зовсім/мало (не)працювали, або про існування яких забули з якихось причин:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
assert(); error(); gcinfo(); getfenv(); getmetatable(); next(); rawequal(); rawget(); rawset(); select(); setfenv(); setmetatable(); xpcall(); newproxy(); модулі debug, pipe та coroutine. |
І тому, маємо в планах надолужити: розібратися що це за методи і спробувати включити їх можливості у щоденне застосування, там, де це буде доцільним.
Ось і все, що ми хотіли розповісти за цією темою на сьогодні.
Бажаємо успіхів!