【问题标题】:Is there a way to increase SD Card Write speed in LUA on Nodemcu ESP8266?有没有办法在 Nodemcu ESP8266 上的 LUA 中提高 SD 卡写入速度?
【发布时间】:2021-07-28 04:00:46
【问题描述】:

我能达到的最大写入速度是 2.4 KB/s。有没有办法增加这个?

在 NodeMCU ESP8266 上使用 LUA 和 User_Modules.h 中的 SPI 模块。 #define BUILD_FATFS 也在 user_config.h 中启用。

我有一个数据记录器,它一次采样 920SPS 或 ~1.1ms/Sample 10 小时。 1.1 ms 应该是很多时间将两个字节写入 SD 卡或样本之间的 xxx 字节缓冲区,但是我看到的最大写入速度是 498 ms 写入 1200 字节或 7ms 写入 3 字节。这与 12.5MB/s 的 SD 0 类标准相去甚远。当我将 1200 B 转储到卡上时,记录器最终会丢失约 450 个样本。


local adc1 = nil
local t_tbl={}
local n=1

function adcReady(_,_,c)
    
    _,_, adctbl[n], _ = adc1:read()
    n=n+1
    if n>400 then
    
        t_tbl[1]=tmr.now()
        
        file.open("/SD0/sddata.txt","a")
        for k,v in ipairs(adctbl) do 
            file.write(v..",")
            adctbl[k]=nil
        end
        file.close()
        
        t_tbl[2]=tmr.now()
        
        print(t_tbl[2] - t_tbl[1])
        n=1
        
    end
end

do
    local adc = {
        ADC1_ID             =   0,
        ADC1_ADDRESS        =   ads1115.ADDR_GND, 
        GAIN                =   ads1115.GAIN_4_096V, 
        SAMPLES             =   ads1115.DR_920SPS, 
        CHANNEL             =   ads1115.SINGLE_0,       
        MODE                =   ads1115.CONTINUOUS, 
        CONV_READY          =   ads1115.CONV_RDY_1, 
    }
    i2c.setup(i2c0.id, i2c0.sda, i2c0.scl, i2c0.speed)
    ads1115.reset()
    adc1 = ads1115.ads1015(adc.ADC1_ID, adc.ADC1_ADDRESS)   
    adc1:setting(adc.GAIN, adc.SAMPLES, adc.CHANNEL, adc.MODE, adc.CONV_READY)  
    
    spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 2, spi.HALFDUPLEX)
    vol = file.mount("/SD0", 8)   -- 2nd parameter is optional for non-standard SS/CS pin
    file.open("/SD0/sddata.txt","w+")
    file.close()
    
    tmr.create():alarm(1000,tmr.ALARM_SINGLE,function()
        gpio.mode(i2c0.conv_rdy,gpio.INT) 
        gpio.trig(i2c0.conv_rdy,'up', adcReady) --enable interrupt, active low rising edge==conv ready  
    end)
end

【问题讨论】:

  • 选择最便宜的解决方案并尝试从中获得高端品质是很有趣的。将您的工具更改为为初学者编写的 C 语言。您使用的是儿童版,没有任何严肃的项目可以考虑在 ESP8266 上使用 Lua 来完成 RT 更接近的任务。
  • 哦,我不知道 LUA 是为孩子们准备的。我刚开始使用 esp8266,我不喜欢 arduino 的 c 和 c++ 混搭。认为 LUA 会给我一个新的挑战,让我在业余时间学习一门新语言。
  • Lua 是一种有趣的语言,问题是你在平台上运行它,其他任务超载。
  • @0andriy 我怀疑这会说服你,但 Lua 肯定不(只是)为孩子们准备的。 en.wikipedia.org/wiki/List_of_applications_using_Lua。如果 NodeMCU Lua 足以运行商业 ESP8266/ESP32 家庭安全系统(而不是家庭自动化),它可能足以满足许多其他用例。话虽如此,看到 NodeMCU Lua 和使用例如本地 C/C++ 解决方案之间的这个特定任务的速度/吞吐量比较会很有趣。 Arduino 核心。
  • 和@MarcelStör,你似乎完全错过了我的观点,我建议重新阅读我上面的cmets。

标签: lua esp8266 sd-card spi nodemcu


【解决方案1】:

您可以通过准备 2Kbyte 对齐的文本块来加快文件写入速度。
将您的 adcReady 替换为:

local log_text = ""
local chunk_size = 2*1024

function adcReady(_,_,c)
   _, _, adctbl[n], _ = adc1:read()
   n = n + 1
   if n > 400 then
   
      t_tbl[1] = tmr.now()
      
      log_text = log_text..table.concat(adctbl, ",", 1, n-1)..","
      local size = #log_text - #log_text % chunk_size
      local log_text_to_save = log_text:sub(1, size)
      log_text = log_text:sub(size + 1)
      
      t_tbl[2] = tmr.now()
      
      if size ~= 0 then 
         file.open("/SD0/sddata.txt","a")
         file.write(log_text_to_save)
         file.close()
      end
      
      t_tbl[3] = tmr.now()
      
      print(t_tbl[2] - t_tbl[1], t_tbl[3] - t_tbl[2])  -- for strings and GC, for File operations
      n = 1
      
   end
end

比 498 毫秒快吗?


更新:

带有缓存 tostring() 的新版本

local num2str = {}

function adcReady(_,_,c)
   _, _, adctbl[n], _ = adc1:read()
   n = n + 1
   if n > 400 then

      t_tbl[1] = tmr.now()

      for i = 1, n - 1 do
         local v = adctbl[i]
         local s = num2str[v]
         if not s then
            s = v..","
            num2str[v] = s
         end
         adctbl[i] = s
      end
      local log_text_to_save = table.concat(adctbl, "", 1, n-1)

      t_tbl[2] = tmr.now()

      file.open("/SD0/sddata.txt","a")
      file.write(log_text_to_save)
      file.close()

      t_tbl[3] = tmr.now()

      print(t_tbl[2] - t_tbl[1], t_tbl[3] - t_tbl[2])  -- for strings and GC, for File operations
      n = 1

   end
end

比以前的版本快吗?


更新 2:

local chr = string.char

function adcReady(_,_,c)
   _, _, adctbl[n], _ = adc1:read()
   n = n + 1
   if n > 400 then

      t_tbl[1] = tmr.now()

      for i = 1, n - 1 do
         local v = adctbl[i]
         -- 0<=v<=4095
         local s
         if v < 10 then
            s = chr(v + 48, 44)
         else
            local m10 = v % 10
            if v < 100 then
               s = chr((v - m10)/10 + 48, m10 + 48, 44)
            else
               local m100 = v % 100
               if v < 1000 then
                  s = chr((v - m100)/10 + 48, (m100 - m10)/10 + 48, m10 + 48, 44)
               else
                  local m1000 = v % 1000
                  s = chr((v - m1000)/1000 + 48, (m1000 - m100)/100 + 48, (m100 - m10)/10 + 48, m10 + 48, 44)
               end
            end
         end
         adctbl[i] = s
      end
      local log_text_to_save = table.concat(adctbl, "", 1, n-1)

      t_tbl[2] = tmr.now()

      file.open("/SD0/sddata.txt","a")
      file.write(log_text_to_save)
      file.close()

      t_tbl[3] = tmr.now()

      print(t_tbl[2] - t_tbl[1], t_tbl[3] - t_tbl[2])  -- for strings and GC, for File operations
      n = 1

   end
end

更新3:

对于 Lua 5.3 和日志中的十六进制数字:

-- log output is in hex
local high = {} -- [1] = "", [2] = "1", ..., [256] = "FF"
local low = {}  -- [1] = "0,", [2] = "1,", ..., [16] = "F,"
for x = 0, 255 do  -- replace 255 with 127 (to save memory) if ADC generates only positive values 0x0000-0x7FF0
   high[x+1] = string.format("%X", x*16):sub(1, -2)
   if x < 16 then
      low[x+1] = string.format("%X,", x)
   end
end

-- in case of out-of-memory error reduce measures count (400) to 256
local measures = 400   -- recommended values are powers of 2
local measures_2 = measures*2

-- adctbl[] is not used anymore, text_buffer[] is used instead
local text_buffer = {}  -- array of (2*measures) elements
for x = 1, measures_2 do
   text_buffer[x] = ""
end

function adcReady(_,_,c)
   local _, _, v = adc1:read()
   -- 0x0000<=v<=0xFFF0
   text_buffer[n] = high[(v>>8)+1]
   text_buffer[n+1] = low[((v>>4)&15)+1]
   n = n + 2
   if n > measures_2 then

      t_tbl[1] = tmr.now()

      local log_text_to_save = table.concat(text_buffer, "", 1, n-1)

      t_tbl[2] = tmr.now()

      file.open("/SD0/sddata.txt","a")
      file.write(log_text_to_save)
      file.close()

      t_tbl[3] = tmr.now()

      print(t_tbl[2] - t_tbl[1], t_tbl[3] - t_tbl[2])  -- for strings and GC, for File operations
      n = 1

   end
end

【讨论】:

  • 从测量中可以看出,脚本在字符串操作(将数字转换为字符串并连接)上花费了 250 毫秒,在保存到文件上花费了 6 毫秒。您的 SD 卡速度很快! 6 毫秒 = 2 KB,0.3 兆字节/秒。 Lua 字符串很慢。 250 毫秒是创建人类可读日志文件所必须付出的代价。
  • 检查新版本(请参阅答案中的更新)。它可能会更快。
  • 这很聪明,使用 num2str 作为动态创建的查找表。你是对的,当它实际上是LUA中的数据类型转换时,我错误地认为SD spi写入速度很慢。对于 400 个样本,更新后的代码要快得多(13194 3264, 13183 4359, 13192 3116, 13180 3228, 13172 4203, 13166 4214, 13155 3598, 13140 6142, 13203 3022, 13159 3366, 13197 3223
  • 如果值为 0..4095,则尝试 update2 中的代码。
  • 你有什么 Lua:5.1 还是 5.3?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多