VisualHMI Simple Database: easydb_* and Custom Mode for Data Record Controls

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

The simple database (exclusive for customized firmware) is alightweight, file-system-free embedded database: it allocates a continuous area in Flash to store structured data (parameter configuration, event logs, operation records) in fixed-length records, which are retained even in the event of a power loss, and support on-demand initialization during runtime. The data record control component is interfaced using a "custom" data source mode to implement the full process of adding, deleting, modifying, and querying data.

1. ⚠️ Flash address planning (failure to consider may result in bricking)

The simple database directly operates on Flash,without a file system isolation mechanism, and the starting address of flashaddr must strictly avoid four high-risk areas:

  • engineering resource storage area
  • data sampling block storage area
  • operation record block storage area
  • OTA upgrade backup area

Total occupied space = dataRowSize × maxCount. When planning addresses, include this space in the calculation and avoid conflicts with other functions.

2. API Summary Table

FunctionParameterDescription
easydb_open(flashaddr, createIfNotExist, dataRowSize, maxCount)flashaddr Flash starting address; createIfNotExist 0=Open only/1=Create if not exist; dataRowSize ≥1; maxCount Maximum number of entriesOpen or create database, return handle (non-zero for success, zero for failure)
easydb_close(db)HandleClose database, release resources
easydb_get_count(db)HandleCurrent number of valid records
easydb_get(db, idx)idx Starts from 0Read record at specified index, return string on success, nil on failure
easydb_add(db, dataset)Append a record at the end of string datawith idx + new content
easydb_update(db, idx, dataset)and update the specified record.Update the specified record,The length of the dataset must be strictly equal to dataRowSize.
easydb_del(db, idx)idxDelete the specified record.
easydb_clear(db)Handleto clear all records.

3. Data record control "Custom" mode

When the data source is set toCustom, the system does not know how many rows of data there are, and it must be provided by two callbacks:

  • Record count callback on_get_record_count(screen_id, control_id): Returns the total row count. Internally, it calls easydb_get_count(g_db_handle)
  • Record content callback on_get_record_row(screen_id, control_id, row): Returns the string content of row 'row'.
function on_get_record_count(screen_id, control_id)
  if screen_id == 0 and control_id == 1 then
    return db.getCnt(g_db_handle)
  end
end

function on_get_record_row(screen_id, control_id, row)
  local ret, msg = db.get(g_db_handle, row)
  if ret == 0 then
    return msg
  end
end

Control configuration: Data source = custom, character encoding = UTF8, page turning control √, control address LW4000, selected row notification √ (LW4004, INT32) -- When a row is selected, the index is written into LW4004, which is used for positioning during add, delete, and update operations.

4. Application: db.lua handle encapsulation + add, delete, update, and query

4.1 Initialization (on_init)

function on_init()
  dofile("db.lua")
  _EN_ON_UPDATA_API_ = 0
  -- 10MB 起始、不存在则创建、单条1000字节、最多1000条
  g_db_handle = db.open(10*1024*1024, 1, 1000, 1000)
  if g_db_handle == 0 then
    print("ERROR: 无法打开数据库!")
    return
  end
  db.clear(g_db_handle)               -- 清空旧数据(可选)
  db.add(g_db_handle, "Alice;Engineer;30")
  db.add(g_db_handle, "Bob;Technician;25")
  db.add(g_db_handle, "Charlie;Manager;40")
  db.add(g_db_handle, "Harden;Basketball players;36")
  _EN_ON_UPDATA_API_ = 1
end

Record format 'Name;Occupation;Age', separated by semicolons in English, consistent with the parsing rules of the table control, directly mapping multiple columns.

4.2 Add, delete, and update (LW5000 button dispatch)

function on_update(slave, vtype, addr)
  if addr == 0x4004 then              -- 选中行通知
    local idx = get_uint32(VT_LW, addr)
    local ret, msg = db.get(g_db_handle, idx)
    if ret == 0 then
      local parts = my_split(msg, ";")
      if parts ~= nil then
        set_uint16(VT_LW, 0x4530, idx)   -- 记录当前选中索引
        set_string(VT_LW, 0x4500, (parts[1] == nil) and "--" or parts[1])
        set_string(VT_LW, 0x4510, (parts[2] == nil) and "--" or parts[2])
        set_string(VT_LW, 0x4520, (parts[3] == nil) and "--" or parts[3])
      end
      show_dialog(1, 232, 33, 50)        -- 弹窗显示详情
    end
  elseif addr == 0x5000 then
    local key = get_uint16(VT_LW, addr)
    if key == 0 then                    -- 添加
      local name = get_string(VT_LW, 0x5010)
      local position = get_string(VT_LW, 0x5020)
      local age = get_string(VT_LW, 0x5030)
      db.add(g_db_handle, name..";"..position..";"..age..";")
    elseif key == 1 then                -- 修改(改选中行)
      local name = get_string(VT_LW, 0x4500)
      local position = get_string(VT_LW, 0x4510)
      local age = get_string(VT_LW, 0x4520)
      local idx = get_uint16(VT_LW, 0x4530)
      db.modify(g_db_handle, idx, name..";"..position..";"..age..";")
    elseif key == 2 then                -- 删除(删选中行)
      local idx = get_uint16(VT_LW, 0x4530)
      db.del(g_db_handle, idx)
    end
  end
end

Each function in the db.lua encapsulation layer performshandle verification + index out-of-bounds check(idx = count returns -2). This is a standard practice in handle-driven design: if it's invalid, it directly returns an error code and never passes it down.

Four pitfalls:① flashaddr must avoid the four areas of engineering resources/data sampling/operation records/OTA backups, and directly operate Flash without isolation. Choosing the wrong address data can overwrite each other or even turn into a brick;② the length of the dataset for easydb_update must strictly equal the dataRowSize when created, and it will fail if it is too long or too short;③ records are separated by semicolons (;) in English, aligning with the parsing rules of the table control, and do not use Chinese semicolons;④ the handle is a global lifecycle resource, and it is held throughout the entire process in on_init. Before operation, verify that the handle is not 0 and the index is within the range. This set of APIs is only available in customized firmware, not in regular firmware.

Compiled from Guangzhou Dacai Technology VisualHMI development documentation (hmi-doc.gz-dc.com) LUA tutorial "Simple Database", copyright belongs to Dacai Technology.

Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email address will not be published. Required fields are marked *.