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
| Function | Parameter | Description |
|---|---|---|
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 entries | Open or create database, return handle (non-zero for success, zero for failure) |
easydb_close(db) | Handle | Close database, release resources |
easydb_get_count(db) | Handle | Current number of valid records |
easydb_get(db, idx) | idx Starts from 0 | Read record at specified index, return string on success, nil on failure |
easydb_add(db, dataset) | Append a record at the end of string data | with 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) | idx | Delete the specified record. |
easydb_clear(db) | Handle | to 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
endControl 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
endRecord 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
endEach 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.
Compiled from Guangzhou Dacai Technology VisualHMI development documentation (hmi-doc.gz-dc.com) LUA tutorial "Simple Database", copyright belongs to Dacai Technology.
Leave a Reply