M-Bus
M-Bus Functions
This category contains functions specific to M-Bus devices.
| API Name | Brief Description |
|---|---|
| api.mbusTransaction() | Sends and receives M-Bus frames. Multiple variants. See below. |
| api.mbusSetup(baudRate, parity, stopBits, dataBits) | Configures the M-Bus communication interface. |
| api.mbusState(state) | Controls the M-Bus circuitry. |
| api.mbusScan(filter, timeout, mode) | Scans for M-Bus devices. |
| api.mbusFilter() | Creates and manages internal table of secondary addresses. Multiple variants. See below. |
| api.mbusVifDifFilter() | Filters received M-Bus frames by given group of bytes (VIF/DIF). Multiple variants. See below. |
api.mbusTransaction()
Sends and receives M-Bus frames.
Tip: Ensure M-Bus is enabled by first calling
api.mbusState(1).
| API Name | Brief Description |
|---|---|
| api.mbusTransaction(msg, timeout, retry) | Sends a custom M-Bus message. |
| api.mbusTransaction(index, timeout, retry) | Sends a readout request with the specified secondary address. |
api.mbusTransaction(msg, timeout, retry)
api.mbusTransaction(pack.pack("<b4", 0x10, 0x50, 0x30, 0x16), 5000, 1)
Sends a custom M‑Bus message and waits for a response or nil to receive without sending.
Arguments
- msg (string or nil):
- string - raw bytes to send (use Lua strings like
"\x68..."orpack.pack(...)). - nil - do not send, wait only for a response.
- string - raw bytes to send (use Lua strings like
Optional
- timeout (integer, optional): Maximum time in milliseconds to wait for a response (default: device MBUS timeout setting).
- retry (integer, optional): Number of receive attempts if no data is returned.
0default.1no retry.
Return
- status (integer): Receive result:
> 1- data length (payload bytes received)1- single‑byte response (e.g., ACK0xE5)0- no data received after retries-1bad filter ID.-2device not responding to selection.
- c (integer): M‑Bus C field (Control).
- a (integer): M‑Bus A field (Address).
- ci (integer): M‑Bus CI field (Control Information).
- answer (string): Payload received from the bus (data portion). May be post‑processed by a VIF/DIF filter if enabled on the device.
- raw (string): Complete M‑Bus frame including header/trailer (e.g.,
68 LL LL 68 C A CI DATA CS 16).
Examples
-- Send a custom M-Bus message and wait for the response
api.mbusState(1) -- Turn on M-Bus
local msg = pack.pack("<b4", 0x10, 0x50, 0x30, 0x16)
local status, c, a, ci, answer, raw = api.mbusTransaction(msg, 5000, 2)
api.mbusState(0) -- Turn off M-Bus
-- Receive-only for 500 ms (no transmit)
local status, c, a, ci, answer, raw = api.mbusTransaction(nil, 500)
-- Also works (empty string => no transmit)
local status, c, a, ci, answer, raw = api.mbusTransaction("", 500)
api.mbusTransaction(index, timeout, retry)
api.mbusTransaction(0, 3000, 1)
Sends a readout request to the device whose secondary address is referenced by the given filter index.
Arguments
- index (integer): Index of the secondary address in the table created by
api.mbusFilter()(starting with0).
Optional
- timeout (integer, optional): Maximum time in milliseconds to wait for a response. Default: device M-Bus timeout setting.
- retry (integer, optional): Number of receive attempts if no data is returned (Default:
1- no retry).*
Return
- status (integer): Receive result:
> 1- payload length (bytes received)1- single-byte response (e.g., ACK0xE5)0- no data received after retries< 0- error:-1- bad filter index-2- device not responding during secondary-address selection- other negatives - transport-layer codes
- c (integer): M-Bus C field (Control).
- a (integer): M-Bus A field (Address).
- ci (integer): M-Bus CI field (Control Information).
- answer (string): Payload (data portion) of the response. May be post-processed by a VIF/DIF filter; length can differ from status.
- raw (string): Complete M-Bus frame including header/trailer (e.g.,
68 LL LL 68 C A CI DATA CS 16).
Example
-- Send a readout request using a secondary address (index 0)
api.mbusState(1) -- Turn on M-Bus
local status, c, a, ci, answer, raw = api.mbusTransaction(0, 3000, 1)
api.mbusState(0) -- Turn off M-Bus
print(status, raw)
api.mbusSetup(baudRate, parity, stopBits, dataBits)
api.mbusSetup(9600, 2, 2, 8)
Configures the M-Bus communication interface.
Tip: After configuring the M-Bus parameters with this function, activate the M-Bus by calling
api.mbusState(1).
Arguments
- baudrate (integer): Baudrate for communication (up to
921600baud). - parity (integer): Parity setting:
0for none.1for odd.2for even.
- stopBits (integer): Number of stop bits:
12
- dataBits (integer): Number of data bits:
78
Example
-- Configure M-Bus interface to 9600 baud, 8E2
api.mbusSetup(9600, 2, 2, 8)
--------------------------------
if state==1 then
api.mbusSetup(baudR, parity, stopB, dataBits)
print(mbh,"Baud rate: "..baudR,"Parity: ".. parity,"Stop byte: ".. stopB,"Data byte: ".. dataBits)
api.mbusState(state)
api.mbusState(1)
Controls the M-Bus circuitry.
Info: Use
api.mbusState(1)beforeapi.mbusTransaction()andapi.mbusState(0)after to reduce consumption. The consumption significantly increases if the circuitry is turned on for too long.Danger: Do not use
api.mbusState(1)during LoRaWAN or NB-IoT message transmission.
Controls the M-Bus circuitry.
Arguments
- state (integer): New state of M-Bus circuitry:
1for on.- Approximately 30 seconds is needed to turn on the M-Bus circuitry.
0for off.
Return
- UL (integer): Approximate UL value when turning on (valid only for new topology, otherwise no return value).
Example
-- Turn on M-Bus
api.mbusState(1)
--------------------------------
function onWake ()
if transmissionCounter % mbusRefreshEverNWake == 0 then
print("Refresh MBus frame by readout...")
-- set link parameters - 2400 baud, 8E1
api.mbusSetup(baudrate,parity,stopBits,dataBits)
api.mbusState(1)
api.delayms(initialDelay)
api.mbusScan(filter, timeout, mode)
api.mbusScan()
Scans for M-Bus devices.
Arguments
Optional
- filter (string, optional): Filter by:
identification,manufacturer,version,medium. If no option is selected, scans for everything. - timeout (integer, optional): Timeout in milliseconds, default is
3000. - mode (integer, optional): If
1, scan in range0-Einstead of default0-9(1for true,0for false).
Return
- var (table of variables): Table of variables (identification, manufacturer, version, medium).
- cnt (integer): Number of found devices.
Example
-- Scan for M-Bus devices
api.mbusSetup(2400, 2, 1, 8)
api.mbusState(1) -- Turn on M-Bus
e, cnt = api.mbusScan() -- Scan for everything with default timeout
for i = 1, cnt do
print(string.format("%08X", (e[i].identification)))
end
api.mbusState(0) -- Turn off M-Bus
api.mbusFilter()
Creates and manages internal table of secondary addresses.
| API Name | Brief Description |
|---|---|
| api.mbusFilter("purge") | Purges the table, removing all stored secondary addresses. |
| api.mbusFilter("populate", filter) | Populates the table with a set of secondary addresses. |
| api.mbusFilter("show") | Displays all secondary addresses currently in the table. |
| api.mbusFilter("fetch") | Fetches the table from EEPROM. |
| api.mbusFilter("scan", filter, save, timeout, scan_mode, useBatteryMonitor, initialDelay, batteryMonitorTimeout, targetVoltage, minimalVoltage, minimalRaise) | Performs a scan of devices using secondary addressing. |
api.mbusFilter("purge")
api.mbusFilter("purge")
Purges the table, removing all stored secondary addresses.
Arguments
-
"purge" (string, command)
❗command - the argument needs to have this exact form❗
Return
- status (integer):
0on success, negative values on failure.
Example
-- Purge M-Bus filter
api.mbusFilter("purge")
if cfg == nil or (as and #cfg < 2) or (not as and #cfg < 4) then
api.mbusFilter("purge")
FLen=0
api.setVar(1, FLen)
api.mbusFilter("populate", filter)
api.mbusFilter("populate", pack.pack("<I", 0x22003287))
Populates the table with a set of secondary addresses provided as input.
Arguments
-
"populate" (string, command)
❗command - the argument needs to have this exact form❗ -
filter (string): Array of secondary address IDs.
Return
- number (integer): Number of secondary addresses populated, negative values on failure.
Example
-- Populate M-Bus filter with one secondary address
x = pack.pack("<I", 0x22003287) -- Store little-endian unsigned integer value
api.mbusFilter("populate", x)
status, _, _, _, _, raw = api.mbusTransaction(0, 3000, 1)
api.dumpArray(raw)
api.mbusFilter("show")
api.mbusFilter("show")
Displays all secondary addresses currently in the table.
Arguments
-
"show" (string, command)
❗command - the argument needs to have this exact form❗
Return
- number (integer): Number of secondary addresses in the table,
0if empty. - addresses (string): Secondary addresses in the table, empty string if empty.
Example
-- Show M-Bus filter
number, addresses = api.mbusFilter("show")
api.dumpArray(addresses)
api.mbusFilter("fetch")
api.mbusFilter("fetch")
Fetches the table from EEPROM, retrieving the previously saved secondary addresses.
Arguments
-
"fetch" (string, command)
❗command - the argument needs to have this exact form❗
Return
- status (integer):
0for success.- negative values for failure.
- number (integer): Number of secondary addresses in the table.
Example
-- Fetch M-Bus filter
status, number = api.mbusFilter("fetch")
print("Number of secondary addresses: " .. number)
--------------------------------
_, fLen = api.mbusFilter("fetch")
api.setVar(1, fLen)
api.mbusFilter("scan", filter, save, timeout, scan_mode, useBatteryMonitor, initialDelay, batteryMonitorTimeout, targetVoltage, minimalVoltage, minimalRaise)
api.mbusFilter("scan", "")
Performs a scan of devices using secondary addressing, based on the filter provided.
Arguments
-
"scan" (string, command)
❗command - the argument needs to have this exact form❗ -
filter (string): Array of IDs to scan for.
Optional
- save (integer, optional):
1to save the found addresses in the filter. - timeout (integer, optional): Timeout in milliseconds.
- scan_mode (integer, optional): Mode for the scan operation.
- useBatteryMonitor (integer, optional):
1to enable battery monitoring. - initialDelay (integer, optional): Initial delay before scanning in milliseconds.
- batteryMonitorTimeout (integer, optional): Timeout for battery monitoring in milliseconds.
- targetVoltage (integer, optional): Target voltage for the battery monitor.
- minimalVoltage (integer, optional): Minimal voltage for the battery monitor.
- minimalRaise (integer, optional): Minimal raise for the battery monitor.
Return
- addresses (string): Found secondary addresses.
Example
-- Scan M-Bus filter
filter = ""
ids = {0x22003287, 0x22004567, 0x22005678}
for _, id in ipairs(ids) do
filter = filter .. pack.pack("<I", id)
end
save = 1
raw = api.mbusFilter("scan", filter, save)
api.dumpArray(raw)
api.mbusVifDifFilter()
Filters received M-Bus frames by given group of bytes (VIF/DIF).
| API Name | Brief Description |
|---|---|
| api.mbusVifDifFilter("purge") | Purges the filter. |
| api.mbusVifDifFilter("populate", filters) | Populates the filter with a set of VIF/DIF filters. |
| api.mbusVifDifFilter("show") | Displays all VIF/DIF filters currently in the filter. |
| api.mbusVifDifFilter("fetch") | Fetches the filter from EEPROM. |
| api.mbusVifDifFilter("activate", index) | Activates specified filter. |
| api.mbusVifDifFilter("deactivate") | Deactivates all filters. |
| api.mbusVifDifFilter("activated") | Returns the index of the activated filter. |
| api.mbusVifDifFilter("filter", mbusFrame, mbusFilter) | Filters the M-Bus frame by given VIF/DIF filter. |
| api.mbusVifDifFilter("filter", mbusFrame, skipIndexes, sizeLimit) | Filters the M-Bus frame by maximum size and skip indexes. |
api.mbusVifDifFilter("purge")
api.mbusVifDifFilter("purge")
Purges the filter.
Arguments
-
"purge" (string, command)
❗command - the argument needs to have this exact form❗
Return
- status (integer):
0on success.
Example
-- Purge M-Bus VIF/DIF filter
api.mbusVifDifFilter("purge")
api.mbusVifDifFilter("populate", filters)
api.mbusVifDifFilter("populate", pack.pack("b2", 0x02, 0x03))
Populates the filter with a set of VIF/DIF filters provided as input.
Arguments
-
"populate" (string, command)
❗command - the argument needs to have this exact form❗ -
filters (string): Array of VIF/DIF filters.
Return
- number (integer): Length of the filter, negative values on failure.
Example
-- Populate M-Bus VIF/DIF filter
api.mbusSetup(2400, 2, 1, 8)
api.mbusState(1)
api.delayms(2000)
filters = pack.pack("b18", 0x02, 0x03, 0xFD, 0xDC, 0xFF, 0x03, 0xFD, 0xC9, 0xFF,
0x02, 0x03, 0xFD, 0xDA, 0xFF, 0x03, 0xFD, 0xC8, 0xFF)
api.mbusVifDifFilter("populate", filters)
api.mbusVifDifFilter("activate", 0)
b = pack.pack('<b5', 0x10, 0x7B, 0xFE, (0x7B + 0xFE) % 256, 0x16)
status, _, _, _, _, raw = api.mbusTransaction(b, 3000, 1)
api.mbusState(0)
print("From M-Bus Calorimeter: ")
api.dumpArray(raw)
--------------------------------
function loadConfiguration()
vifDifFilterLength = api.getVar(20, -1)
if vifDifFilterLength == -1 then
api.setVar(22, "bytes", defaultVifDifFilter)
api.setVar(20, #defaultVifDifFilter, true)
vifDifFilterLength = #defaultVifDifFilter
end
if vifDifFilterLength ~= 0 then
api.mbusVifDifFilter("populate", api.getVar(22, "bytes", vifDifFilterLength))
end
api.mbusVifDifFilter("show")
api.mbusVifDifFilter("show")
Displays all VIF/DIF filters currently in the filter.
Arguments
-
"show" (string, command)
❗command - the argument needs to have this exact form❗
Return
- number (integer): Length of the filter,
0if empty. - filters (string): VIF/DIF filter entries, empty if none.
Example
-- Show M-Bus VIF/DIF filter
number, filters = api.mbusVifDifFilter("show")
api.dumpArray(filters)
api.mbusVifDifFilter("fetch")
api.mbusVifDifFilter("fetch")
Fetches the filter from EEPROM, retrieving the previously saved VIF/DIF filters.
Arguments
-
"fetch" (string, command)
❗command - the argument needs to have this exact form❗
Return
- status (integer):
0on success, negative values on failure. - number (integer): Length of the filter.
Example
-- Fetch M-Bus VIF/DIF filter
status, number = api.mbusVifDifFilter("fetch")
print("Number of VIF/DIF filters: " .. number)
api.mbusVifDifFilter("activate", index)
api.mbusVifDifFilter("activate", 0)
Activates specified filter.
Arguments
-
"activate" (string, command)
❗command - the argument needs to have this exact form❗
Optional
- index (integer, optional): Index of the filter to activate; if nothing or
-1is provided, all filters are deactivated.
Return
- status (integer):
0on success.-1on failure.
Example
-- Activate M-Bus VIF/DIF filter
status = api.mbusVifDifFilter("activate", 0)
print("Activated filter status: " .. status)
api.mbusVifDifFilter("deactivate")
api.mbusVifDifFilter("deactivate")
Deactivates all filters.
Arguments
-
"deactivate" (string, command)
❗command - the argument needs to have this exact form❗
Return
- status (integer):
0on success.
Example
-- Deactivate M-Bus VIF/DIF filter
api.mbusVifDifFilter("deactivate")
api.mbusVifDifFilter("activated")
api.mbusVifDifFilter("activated")
Returns the index of the activated filter.
Arguments
-
"activated" (string, command)
❗command - the argument needs to have this exact form❗
Return
- index (integer):
- Index of activated filter.
-1if none.
Example
-- Check activated M-Bus VIF/DIF filter
index = api.mbusVifDifFilter("activated")
print("Activated filter index: " .. index)
api.mbusVifDifFilter("filter", mbusFrame, mbusFilter)
api.mbusVifDifFilter("filter", "frame", "filter")
Filters the M-Bus frame by given VIF/DIF filter.
Arguments
-
"filter" (string, command)
❗command - the argument needs to have this exact form❗ -
mbusFrame (string): M-Bus frame to filter.
-
mbusFilter (string): VIF/DIF filter to apply.
Return
- filteredFrame (string): Filtered M-Bus frame.
Example
-- Filter M-Bus frame with VIF/DIF filter
f = pack.pack("b61",
0x68, 0x59, 0x59, 0x68, 0x08, 0x03, 0x72, 0x64, 0x05, 0x03, 0x41, 0xAE, 0x4C, 0x49, 0x07, 0x7C, 0x00, 0x00, 0x00, 0x0C, 0x14, 0x00, 0x96, 0x06, 0x00, 0x0C, 0x94, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x78, 0x64, 0x05, 0x03, 0x41, 0x0C, 0xFD, 0x10, 0x64, 0x05, 0x03, 0x41, 0x04, 0x6D, 0x32, 0x0D, 0x3F, 0x31, 0x04, 0xFD, 0x17, 0x10, 0x00, 0x00, 0x00, 0x84, 0x0F, 0x6D, 0x3B, 0x17, 0x1F, 0x3C, 0x8C, 0x0F, 0x14, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x0F, 0x6D, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x0F, 0x14, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xB1, 0x16
)
filter = pack.pack("b7", 0x02, 0x02, 0x0C, 0x14, 0x02, 0x0C, 0x78)
filteredFrame = api.mbusVifDifFilter("filter", f, filter)
api.dumpArray(filteredFrame, "raw")
api.mbusVifDifFilter("filter", mbusFrame, skipIndexes, sizeLimit)
api.mbusVifDifFilter("filter", "frame", 2, 51)
Filters the M-Bus frame by maximum size and skip indexes.
Arguments
-
"filter" (string, command)
❗command - the argument needs to have this exact form❗ -
mbusFrame (string): M-Bus frame to filter.
-
skipIndexes (integer): Number of items to skip.
-
sizeLimit (integer): Maximum size of the filtered frame.
Return
- filteredFrame (string): Filtered M-Bus frame with applied callback filter.
- lastAcceptedIndex (integer): Index of the last accepted item.
- highestIndex (integer): Highest index found during filtering.
- isDone (boolean):
trueif the whole frame is filtered.falseif the whole frame was not filtered.
Example
-- Filter M-Bus frame by maximum size
f = pack.pack("b61",
0x68, 0x59, 0x59, 0x68, 0x08, 0x03, 0x72, 0x64, 0x05, 0x03, 0x41, 0xAE, 0x4C, 0x49, 0x07, 0x7C, 0x00, 0x00, 0x00, 0x0C, 0x14, 0x00, 0x96, 0x06, 0x00, 0x0C, 0x94, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x78, 0x64, 0x05, 0x03, 0x41, 0x0C, 0xFD, 0x10, 0x64, 0x05, 0x03, 0x41, 0x04, 0x6D, 0x32, 0x0D, 0x3F, 0x31, 0x04, 0xFD, 0x17, 0x10, 0x00, 0x00, 0x00, 0x84, 0x0F, 0x6D, 0x3B, 0x17, 0x1F, 0x3C, 0x8C, 0x0F, 0x14, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x0F, 0x6D, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x0F, 0x14, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xB1, 0x16
)
filteredFrame, lastAcceptedIndex, highestIndex, isDone = api.mbusVifDifFilter("filter", f, 2, 51)
api.dumpArray(filteredFrame, "raw")
print("Last Accepted Index: " .. lastAcceptedIndex, "Highest Index: " .. highestIndex, "Is Done: " .. tostring(isDone))