Post your best Lua script examples here

User avatar
By Patriko
#11722 No, it's not - I used this function based on this optimization method to provide network configuration:

Code: Select alllocal 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)
User avatar
By dpwhittaker
#11765 First, I've been profiling the memory performance using this technique:

testBaseline.lua:

Code: Select allprint("start", node.heap())
config = flashMod("config")
print("before", node.heap())
wifi.sta.getap(function() print("hi") end)
print("after", node.heap())


testConfig.lua:

Code: Select allprint("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:

Code: Select allflash = {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:

Code: Select allflashMod(table)
serializes all the functions on the table to flash, removes them, and augments the table with a metatable that loads the functions on demand.
Code: Select allflashMod("modName")
creates an empty table with the metatable populated. Use this if you want to create the module in one file with the first version, but don't need to attach any additional in-memory methods or data.

So, a full module with public, private, and flash methods will take this form now:

Code: Select allmod = { 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
User avatar
By alon24
#11770 Great changes, I will look into them soon (i hope)

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
User avatar
By alon24
#11830 I need some help, I think I have a lack of understanding on what can access what, and it translates to not enough memory and general code not working.
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
Code: Select allmod = { 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:
Code: Select allprint("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:
Code: Select alltmr.alarm(0,1000,0,function()
   dofile("flashmod.lc")
   dofile("LLbin.lc")
     dofile("oledilan.lua")
     print("ready for action")
end)