local flash = {}
function flash.ListAP(t)
local res = ""
if t then
for k,v in pairs(t) do
res = res .. '"' .. k .. '"' .. ",\n"
end
if file.open("aplist.txt","w+") then
file.write(res..'null')
file.close()
print("accesspoint list : " .. res .. " has ben written to aplist.txt file")
end
end
collectgarbage("collect")
end
function flash.GetAPList()
local list = ""
if file.open("aplist.txt","r") then
list = file.read()
file.close()
end
return list
end
return loadfile("flashmodule.lc")("config", {}, {}, flash)
testBaseline.lua:
print("start", node.heap())
config = flashMod("config")
print("before", node.heap())
wifi.sta.getap(function() print("hi") end)
print("after", node.heap())
testConfig.lua:
print("start", node.heap())
config = flashMod("config")
print("before", node.heap())
wifi.sta.getap(function(t) config.ListAP(t) end)
print("after", node.heap())
Note that they are identical except for the test version calls the config.ListAP in the callback, and the baseline version calls print("hi"). This gave me a way to determine if it was the function taking up heap or other lua overhead that I don't have control over, and both methods had nearly identical memory utilization, even on the original approach.
However, I did notice I was leaking some memory on the persistent environment of private and public methods, that the 4-way chain of __index calls was slowing things down even more than loading from flash, and a few other issues that the original approach had, so I decided to distill the approach down to its simplest possible form, and go for the lightest implementation I could imagine. And here is what I came up with:
flash = {MOD_NAME = "flash"}
function flash.flashMod(tbl)
if type(tbl) == "string" then tbl = {MOD_NAME=tbl} end
for k,v in pairs(tbl) do
if type(v) == "function" then
file.open(string.format("%s_%s.lc", tbl.MOD_NAME, k), "w+")
file.write(string.dump(v))
file.close()
tbl[k] = nil
end
end
return setmetatable(tbl, {
__index = function(t, k)
return assert(loadfile(string.format("%s_%s.lc",t.MOD_NAME,k)))
end
})
end
flash.flashMod(flash)
flash = nil
module = nil
package = nil
newproxy = nil
require = nil
collectgarbage()
function flashMod(tbl) return loadfile("flash_flashMod.lc")(tbl) end
Notice a few things here - I've completely gotten rid of the concept public/private/flash. Everything is considered a flash function at the time flashMod is called. So, if you want some in-memory functions, add them to the table after you call flashMod... simple enough. Also, it is no longer setting the environment of the flash functions, so there is no concept of a private variable. You could easily pass an environment table as the second argument to flashMod, and use it inside the __index function to set the environment of the function, but this creates a closure around the __index function, bloating memory requirements for a feature that is a nice to have. I'd suggest marking private functions and data with an _, or only accessing them from in-memory functions, which do have access to the local namespace of the module.
Also, flashMod has become a global function which just bootstraps the flashMod function into existence using its own technique. The idea was to replace the module/package/require functionality with a single flash function, so I went ahead and deleted those other functions from the global namespace, freeing up almost another 1k of heap.
The special value MOD_NAME has been added to the table to hold the name of the module (the first half of the flash function file name). This was done to remove another closure, reducing memory requirements of the function that does have to stay in memory: __index. At this point, I could move the metatable into the global environment, and reference the same metatable from every flash module. I'll have to do some profiling to see if this helps once I have several flash modules to work with.
flashMod can be called in two ways:
flashMod(table)
flashMod("modName")
So, a full module with public, private, and flash methods will take this form now:
mod = { MOD_NAME = "my_mod"}
function mod.flashFunc1() end
function mod.flashFunc2() end
flashMod(mod) --everything before this is a flash function, everything after it is an in-memory public function
local function privateFunc() end --only visible to public and other private functions
local privateData --ditto
function mod.publicFunc() end
return mod
The zip attached to the first post has the new version of the config module mentioned in previous posts, as well as a flash version of LLbin (I was tired of waiting for a dofile() to load LLbin, so now I have it as a flash function with a global wrapper that is loaded during init. Faster binary transfer startups with hardly any heap usage). Feel free to play with this new version.
Let me know what you think.
David
but can you please give us the oled sample, in this new form?
I have been messing with the old form trying to add the i2c back (still no results), and I would love to use this new way, with the spi sample adjusted to it, as refrence.
Thanks
I am trying to use your code, to do the i2c oled again, but I am failing, so I thought I'd ask for some help.
Here are my files, when I run I get kernel panic, no memory.
I loaded the fnt file with lualoader, I would love to know how to do this in esplorer (my main tool)
oledilan.lua
mod = { MOD_NAME = "oledilan"}
mod.dc = 3
mod.oled_gpio = {[0]=3,[2]=4,[4]=2,[5]=1,[12]=6,[13]=7,[14]=5}
mod.oled_id = 0
mod.oled_sda = mod.oled_gpio[0]
mod.oled_scl = mod.oled_gpio[2]
mod.oled_addr = 0x3C
mod.spi = 'true'
function mod.initSPI(dc_n)
print("init SPI")
dc = dc_n
spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0)
gpio.mode(dc, gpio.OUTPUT)
command(0x8d,0x14,0xaf,0xd3,0x00,0x40,0xa1,0xc8,0xda,0x12,0x81,0xff,0x20,0x02)
end
function mod.initI2C(sda_n, scl_n)
print("init i2c")
spi= 'false'
oled_sda = mod.oled_gpio[sda_n]
oled_scl = mod.oled_gpio[scl_n]
print("scl="..oled_scl..",sda="..oled_sda)
i2c.setup(mod.oled_id, mod.oled_sda, mod.oled_scl, i2c.SLOW)
mod.command(0x8d,0x14,0xaf,0xd3,0x00,0x40,0xa1,0xc8,0xda,0x12,0x81,0xff,0x20,0x02)
end
function mod.write_reg(dev_addr, reg_addr, reg_val)
i2c.start(mod.oled_id)
print(reg_val)
i2c.address(mod.oled_id, dev_addr, i2c.TRANSMITTER)
i2c.write(mod.oled_id, reg_addr)
i2c.write(mod.oled_id, reg_val)
i2c.stop(mod.oled_id)
end
function mod.command(...)
print("command");
if (spi == 'true') then
gpio.write(dc, gpio.LOW)
spi.send(1, arg)
else
for i,v in ipairs(arg) do
mod.write_reg(mod.oled_addr, 0, v)
end
--mod.write_reg(mod.oled_addr, 0, arg)
end
end
function mod.on()
print("on")
mod.command(0xAF)
end
function mod.off()
print("off")
mod.command(0xAE)
end
function mod.invert(state)
print("invert")
mod.command(state == 1 and 0xA7 or 0xA6)
end
function mod.set_pos(x, y)
mod.command(0xB0+y, bit.band(x, 0xf0) / 16 + 16, bit.band(x, 0x0e) + 1)
end
function mod.data(...)
--print("data", unpack(arg))
if (spi == 'true') then
gpio.write(dc, gpio.HIGH)
spi.send(1, arg)
else
print('write i2c')
--for ic=0,4 do
-- write_reg(oled_addr, 0x40, ascii[char][ic])
-- tmr.wdclr()
-- end
end
end
flashMod(mod) --everything before this is a flash function, everything after it is an in-memory public function
file.open("font6x8.fnt")
local font6x8 = file.read()
file.close()
local function write(str, x, y)
for i=1,#str do
set_pos(x, y)
local start = (string.byte(str,i) - 0x20)*6
mod.data(string.byte(font6x8,start+1,start+6))
x = x + 6
if x > 122 then x = 0; y = y + 1 end
end
end
return flashMod(mod)
testoledilan.lua:
print("start", node.heap())
config = flashMod("oledilan")
config.initI2C(12,13)
config.on()
config.invert(1)
config.set_pos(10,10)
--config.initSPI()
print("after", node.heap())
init.lua:
tmr.alarm(0,1000,0,function()
dofile("flashmod.lc")
dofile("LLbin.lc")
dofile("oledilan.lua")
print("ready for action")
end)