Wireless M-Bus Gateway V4 with 4-20 mA Add-On
Description
Gateway for remote reading of wireless M-Bus meters and external sensors (4–20 mA and 1-Wire temperature sensors) via NB-IoT, LTE-M mobile radio or LoRaWAN to the Internet. Suitable for indoor and weather-protected outdoor installation.
In addition to all the features of the Wireless M-Bus Gateway V4, this variant provides:
-
2 analogue 4–20 mA input channels for industrial sensors. Each channel is supplied with an extra 12V (1W) supply voltage for sensor supply. Connection with each 4-pin channel (I+, I-, 12V, GND) can be established over a 22 cm cable connection.
-
A 1-Wire interface for up to 20 temperature sensors (DS18B20). Connection with each 3-pin channel (3V, GND, DQ) can be established over a 22 cm cable connection.
Product Components
This product is manufactured by Lobaro using the components listed below. Detailed information about the firmware and other components can be found in their respective descriptions. Please use the links provided to access comprehensive product details.
| Component | Manual / Description | Additional note |
|---|---|---|
| Firmware | app-nrf91-origin | See here for available firmware updates |
| Housing | LoCube | 151 mm (l) x 125 mm (w) x 62 mm (h) |
| Battery | 38 Ah, included | Li-SOCl2 (3.6 V) |
Firmware Updates
The latest firmware can be found here:
-
For further Information on how to update the device, refer to: Firmware Update.
A remote firmware update over LoRaWAN is currently not possible.
Product Identification
- Name: Gateway V4 + 4-20 mA
- Type: LOB-GW4-420-1W
- MPN / Ordering code: 1200588
Datasheet & Quickstart
Please contact our sales team.
Product specific details
LED signal patterns
The device has 3 three LEDs (B, G, R = Blue, Green, Red), labeled Status on the PCB: 🔵🟢🔴.
Patterns during booting/restart
- 🔵⚪🔴 B and R on: Device is in Bootloader Mode (not actively running, remove config adapter press reset to leave Bootloader Mode).
- 🔵->🟢->🔴 Quick cycle B, G, R for ~0.5s: Device just booted, either after power on, reset b button or software, or after a hard failure.
- ⚪⚪🔴 R flashes repeatedly on and off in 1s interval: critical failure during boot (failed to start application).
Patterns during normal operation
- 🔵⚪⚪ B flashes 1s on and 1s off in loop: building LTE connection to mobile provider. Followed by:
- ⚪🟢⚪ Short G flash on success.
- ⚪⚪🔴 Short R flash after connection failed.
- ⚪🟢⚪ G flash during installation mode while collecting wireless M-Bus telegrams.
Exceptional patterns
- 🔵⚪🔴/🔵🟢⚪ Quickly changing between R & B and G & B every 5s in a loop: Modem is in connection restriction mode - keep device on power, will fix itself after 30min.
Configuration
The device is shipped with default configuration parameters. The configuration can be changed via the 6-pin config port using the Lobaro USB Configuration Adapter. More information about the usage of the configuration tools can be found in our documentation.
Remote Configuration is also supported after initial network connection.
Default Values are the firmware defaults. Depending on the application the device may be delivered with different settings.
General Parameters
| Description | Key | Type | Possible Values | Default |
|---|---|---|---|---|
| Uplink channel selection | WAN | String | lte or lorawan | lte |
| Days without connectivity until device reset | LostReboot | Number | Any, e.g., 3 | 5 |
Detailed Description
WAN Parameter
LPWAN technology is used for connection and data communication with the backend server. This can be either cellular LTE (NB-IoT, LTE-M) or LoRaWAN.
lte: Utilizes cellular technologies, either NB-IoT or LTE-M.lorawan: Employs LoRaWAN with OTAA (Over-The-Air Activation).
- LoRaWAN uplinks and downlinks are limited to 52–222 bytes, depending on the DataRate (connection quality):
- Uplinks containing longer wMBUS telegrams are split across multiple LoRaWAN messages.
- Uplinks with numerous wMBUS telegrams may take significant time due to LoRaWAN's duty-cycle limitations.
- Downlinks with large configuration values (e.g., long whitelists) must be split into multiple messages, which can be challenging to implement.
- Limited metadata:
- Status telegrams include less information due to reduced uplink channel capacity.
- No remote firmware update capabilities.
Wireless M-Bus Meter Reading
| Description | Key | Type | Possible Values | Default |
|---|---|---|---|---|
| WMBUS Listen Cron [UTC+0] | listenCron | String | Any CRON String | 0 0 12 * * * |
| WMBUS C1/T1 Listen Duration [s] | cmodeDurSec | Number | Number of seconds0 = Do not collect C1/T1Max Value= 36000 | 300 |
| WMBUS S1 Listen Duration [s] | smodeDurSec | Number | Number of seconds0 = Do not collect S1Max Value= 36000 | 0 |
| Sensus RF Listen Duration [s] | xmodeDurSec | Number | Number of seconds0 = Do not collect X-ModeMax Value= 36000 | 0 |
| Müller-Funk Listen Duration [s] | umodeDurSec | Number | Number of seconds0 = Do not collect U-ModeMax Value= 36000 | 0 |
| WMBUS ID Filter List | devFilter | String | List, e.g.88009035,13456035 | [not set] |
| WMBUS Type Filter List | typFilter | String | List, e.g.08,07 | [not set] |
| WMBUS M-Field Filter List | mFilter | String | List, e.g.DME,ITW,SEN,QDS | [not set] |
| WMBUS CI-Field Filter List | ciFilter | String | List, e.g.8a,72 | [not set] |
| WMBUS Telegram Upload Limit | maxTelegrams | Number | Any number of max. Telegrams0 = no limit. | 0 |
Detailed Description
listenCron Parameter
The listen cron specifies when the device wakes up to receive data via enabled wireless M-Bus and other radio protocols. Each listening period is followed by data upload using the configured WAN technology. The ideal interval largely depends on the device's power supply (battery-powered vs. externally powered) and the application's need for new metering data. Typical intervals range from every 15 minutes to 14 days between consecutive readouts.
Learn more about CRON configuration parameters.
*modeDurSec Parameters
Parameters: cmodeDurSec smodeDurSec xmodeDurSec umodeDurSec
The duration, specified in seconds (e.g., 300 for 5 minutes), defines how long the device collects metering data for
each corresponding wireless protocol. Each listening period is executed sequentially based on the configured durations.
Once all listening periods are complete, the collected meter telegrams are transmitted using the configured WAN
technology.
The maximum duration that can be specified is 10 hours, i.e. 36,000 seconds. Please note that such long listening periods will drain the battery very quickly. If in doubt, please consult Lobaro.
*Filter Parameters
Parameters: devFilter ciFilter mFilter typFilter
Filters are applied to meters based on wireless M-Bus-related fields to determine which telegrams are collected and uploaded. These filters include:
mFilter: Manufacturer filter – filters telegrams by the 3-letter manufacturer code included in every telegram ( e.g.,LOBfor Lobaro GmbH).typFilter: Device Type filter – filters telegrams by the 2-hex-digit code that defines the type of sending device (e.g.,07for water meters).devFilter: Device filter – filters telegrams by the 8-digit ID that is mandatory for each sending device (e.g.,87654321).ciFilter: CI-Field filter – filters telegrams by the 2-hex-digit CI-Field included in every telegram. This field is a technical code that describes the purpose of the telegram (e.g.,8a).
These filters help refine data collection to target specific devices or device types.
For a detailed explanation, read more about telegram filter parameterization.
Blacklist
Each filter parameter can be preceded with an exclamation mark (!) to convert the whitelist into a blacklist.
This applies to the entire filter parameter, not to individual entries within the list. For example:
mFilter=LOBwill collect only telegrams from Lobaro GmbH (whitelist).mFilter=!LOBwill exclude telegrams from Lobaro GmbH (blacklist).
maxTelegrams Parameter
Set a hard limit on the number of telegrams to be collected and uploaded. The firmware will stop collecting once this limit is reached, regardless of the elapsed time. This can help conserve battery life and data volume, especially if the device is located in an area with a high density of meters.
When filtering for a single telegram, the parameter can be set to 1, causing the device to stop listening immediately
after the desired telegram is received.
LTE Connection
| Description | Key | Type | Possible Values | Default |
|---|---|---|---|---|
| LTE Lobaro Platform Host | Host | IP / URL | List of various Endpoints | coaps://platform.lobaro.com |
| LTE MCC+MNC Code | Operator | Number | e.g. 26201 (Dt. Telekom) | [not set] |
| LTE Band | Band | Number | 3 or 8,20 or 3,8,20 | 3,8,20 |
| LTE APN | APN | String | any APN | * |
| LTE SIM Pin | PIN | Number | 4 digits pin, e.g. 1234 | [not set] |
| LTE NB-IoT on/off | UseNbiot | Bool | true or false | true |
| LTE M1 on/off | UseLtem | Bool | true or false | true |
| LTE DNS Servers used | DNS | IP | List of DNS server IPs | 9.9.9.9,1.1.1.1 |
| Plain UDP Host | UdpHost | IP | any, e.g 94.130.20.37 | [not set] |
| Plain UDP Port | UdpPort | Number | any, e.g 3333 | [not set] |
Detailed Description
Host Parameter
- Lobaro Platform CoAP
- MQTT
Hostname or IP of the Lobaro Platform instance CoAP endpoint to which the gateway communicates using UDP.
- Using DTLS:
coaps://platform.lobaro.com - No DTLS:
coap://platform.lobaro.com - Plain IP:
94.130.20.37(platform.lobaro.com)
It's also possible to configure a list of URLs to implement a fallback mechanism. This is particulary helpful
for
combining DTLS and non DTLS connection attempts: coaps://platform.lobaro.com,coap://platform.lobaro.com
The device can communicate directly via MQTT. See MQTT → Host Parameter.
LTE Parameters
Parameters: APN Operator Band, PIN
Basic parameters are required to configure the NB-IoT or LTE-M connection. These settings must align with the SIM card
and network provider used. Typically, the default parameters work well, as they enable the modem to automatically select
and join the network using SIM card information. However, if the APN (Access Point Name) is known, it is recommended
to configure it for optimal performance.
Read more about LTE network configuration parameters.
UseNbiot UseLtem
The modem supports both NB-IoT and LTE-M technologies. By default, both are enabled, allowing the cellular modem to
automatically select the most suitable network type based on the location. However, you can force the modem to use a
specific technology by setting one parameter to false, ensuring the other remains enabled (set to true). At least
one technology must always be active.
UdpHost UdpPort
This feature is deprecated and not recommended for production use. Lobaro provides limited support for errors caused by using raw UDP upload.
Instead of sending metering data to the Lobaro IoT Platform, the data can alternatively be sent to an external UDP socket. This option is useful if you prefer to keep metering data off external servers while still using the Lobaro Platform to control your gateways.
UdpHost: IP address to upload plain telegrams via UDP.[not set]: Defaults to uploading data to the Lobaro IoT Platform using the configured Host parameter.UdpPort: Port number to upload plain telegrams via UDP.- Only used if
UdpHostis set.
Even when metering data is sent to an external server, the firmware typically still requires a connection to a Lobaro Platform instance to send status information, perform remote configurations, or handle firmware updates.
For solutions that allow all communication without the Lobaro Platform, such as direct MQTT to an external broker, contact Lobaro for potential options.
LoRaWAN Connection
| Description | Key | Type | Possible Values | Default |
|---|---|---|---|---|
| DevEUI | DevEUI | byte[8] | any | Device EUI64 |
| AppEUI / JoinEUI (1.1) | AppEUI/JoinEUI | byte[8] | any | random |
| AppKey | AppKey | byte[16] | any | random |
| NwkKey (1.1) | NwkKey | byte[16] | any | 00000000000000000000000000000000 |
| Days between Timesync | TimeSync | Number | any | 3 |
| Payload Format | PayloadFormat | Number | 0, 1, 2 | 0 |
| Use OTAA | OTAA | Bool | true or false | true |
| Random TX Delay [s] | RndDelay | Number | any | 10 |
| Spreading Factor | SF | Number | 7-12 | 12 |
| Transmission Power | TxPower | Number | 2-14 | 14 |
| Adaptiv Data Rate | ADR | Bool | true or false | true |
| LoRaWAN max. Payload Length | loraPLMax | Number | 10 to 241 | 100 |
Detailed Description
LoRaWAN 1.1 is experimental and not certified. For production environments, we recommend using LoRaWAN 1.0.2.
Depending on configuration some parameters are not available.
NwkKey Parameter
The Network Key (NwkKey) is used for LoRaWAN 1.1. If set to zeros (00000000000000000000000000000000) or to the same value as the AppKey, the device will stick to LoRaWAN 1.0.2 which is the recommended mode of operation.
PayloadFormat Parameter
Used encoding of the wmBus LoRaWAN uplink payload packets.
0= Encoding in ports with static message length1= prefix bytes and time2= prefix bytes, time, and rssi
For a detailed specification of the payload formats, please refer to the LoRaWAN Communication page.
Special
| Description | Key | Type | Possible Values | Default |
|---|---|---|---|---|
| Verbose UART Log | verbose | Bool | true or false | false |
| Addon RAM configuration | extRam | String | Lobaro Internal | [not set] |
| Live Mode | liveMode | String | [not set] | |
| Operation Mode | opMode | Number | Lobaro Internal | 1 |
Detailed Description
liveMode Parameter
An empty string disables the live mode.
suppression=<seconds> enable live mode with suppression of duplicate telegrams for seconds
Analog Channel Parameters
| Description | Key | Type | Possible Values | Default |
|---|---|---|---|---|
| Enable measurement on 4–20 mA input channel 1 (CH1) | ch1Enable | Bool | true / false false = channel disabled | true |
| Number of seconds to wait for the CH1 sensor to get ready after power on | ch1PreReadSec | Number | Seconds (integer) 0… | 1 |
| Number samples to take for a single CH1 measurement | ch1Samples | Number | 1-1000 | 200 |
| Scaling factor applied to the raw CH1 value | ch1Factor | Number | Numeric (float) e.g. 1.0, 0.1 | 1 |
| Offset added to the CH1 value after scaling | ch1Offset | Number | Numeric (float) e.g. 0, -5.0 | 0 |
| High alarm threshold for CH1 measurement value | ch1AlarmHigh | Number | Numeric (float) empty = disabled | [not set] |
| Low alarm threshold for CH1 measurement value | ch1AlarmLow | Number | Numeric (float) empty = disabled | [not set] |
| Enable measurement on 4–20 mA input channel 2 (CH2) | ch2Enable | Bool | true / false false = channel disabled | true |
| Number of seconds to wait for the CH2 sensor to get ready after power-on | ch2PreReadSec | Number | Seconds (integer) 0… | 1 |
| Number of samples taken for a single CH2 measurement | ch2Samples | Number | 1-1000 | 200 |
| Scaling factor applied to the raw CH2 value | ch2Factor | Number | Numeric (float) | 1 |
| Offset added to the CH2 value after scaling | ch2Offset | Number | Numeric (float) | 0 |
| High alarm threshold for CH2 measurement value | ch2AlarmHigh | Number | Numeric (float) empty = disabled | [not set] |
| Low alarm threshold for CH2 measurement value | ch2AlarmLow | Number | Numeric (float) empty = disabled | [not set] |
| Cron schedule used while an alarm is active, overriding the normal device cron | alarmCron | String | Cron expression (string) empty = no override | [not set] |
| Number of seconds CH1 stays powered after first power-on or brownout | ch1PwrAfterBrownoutDur | Integer | Seconds (integer) 0 = disabled | 0 |
Detailed Description
chXFactor and chXOffset Parameters
The measured raw value is multiplied by chXFactor and then chXOffset is added. All further calculations and alarm checks are done with this derived value.
chXAlarmLow and chXAlarmHigh Parameters
If the calculated measurement value is above chXAlarmHigh or below chXAlarmLow, an alarm is triggered. The number of triggered alarms and the time in the alarm state is measured and transmitted.
alarmCron Parameter
In case of an active alarm, this cron is used instead of the regular cron. If empty, the regular sensor cron will be used.
Upload Formats LoRaWAN
Communication with this gateway version via LoRaWAN is currently not possible.
Upload Formats LTE
CoAP Protocol
The protocol details of the CoAP implementation are not publicly disclosed and are intended for use exclusively with the Lobaro IoT Platform. For integration with third-party systems, MQTT is the preferred protocol.
If you require access to this and MQTT is not an option, please contact Lobaro directly.
MQTT Protocol
Please contact our sales team.
Example JS Parser
Lobaro Platform / TheThingsNetwork (TTN) / ChirpStack
// Version 0.0.2
// Source: github.com/lobaro-parsers-private/lobaro/lob-gw4-420-1w.js
//
// Changelog
//
// v0.0.2 - 2025-04-28
// - Add gwImei field to 4-20 mA parsed data
////////////////////////////
// Helper
////////////////////////////
function now() {
return typeof (nowMs) == 'function' ? nowMs() : new Date().getTime();
}
/**
* Trim leading zeros from a string
* @param {string} str Input string, e.g. '00096117836alt'
* @returns {string} Input tring with leading zeros removed, e.g. '96117836alt'
*/
function trimLeadingZeros(str) {
for (var i = 0; i < str.length; i += 1) {
if (str[i] !== '0') {
return str.slice(i);
}
}
return str;
}
////////////////////////////
// START - Status Functions
////////////////////////////
var receivedMetersCached = null;
function Status_GetReceivedMeters() {
if (receivedMetersCached !== null) {
return receivedMetersCached;
}
var received = Device.getProperty("platform.wmbus.receivedMeters") || [];
if (received) {
return received;
}
return [];
}
// Add a single id to the list if not exists
function Status_AddReceivedMeterIdIfNotExists(mbusId) {
var received = Status_GetReceivedMeters();
if (received.indexOf(mbusId) >= 0) {
// Already in list
return;
}
received = [mbusId].concat(received)
// Update cache and actual properties
receivedMetersCached = received;
Device.setProperty("platform.wmbus.receivedMeters", received);
Device.setProperty("platform.wmbus.receivedMetersCount", received.length);
}
// Add a single meter with multiple IDs
// idLL = LinkLayer
// idAL = ApplicationLayer
function Status_AddReceivedMeter(idLL, idAL) {
idLL = trimLeadingZeros(idLL);
if (!idLL) return;
if (idAL !== undefined) {
idAL = trimLeadingZeros(idAL);
if (!idAL) return;
}
// Only add expected meters
var expected = Status_GetExpectedMeters();
if (expected.length === 0) {
// If we expect nothing, just add the Link Layer ID to the list to avoid double count of one meter
Status_AddReceivedMeterIdIfNotExists(idLL);
}
var idLLExpected = expected.some(function (x) {
return x === idLL;
});
if (idLLExpected) {
Status_AddReceivedMeterIdIfNotExists(idLL);
}
var idALExpected = expected.some(function (x) {
return x === idAL;
});
if (idALExpected) {
Status_AddReceivedMeterIdIfNotExists(idAL);
}
}
function Status_ClearReceivedMeters() {
var received = [];
receivedMetersCached = received;
Device.setProperty("platform.wmbus.receivedMeters", received);
Device.setProperty("platform.wmbus.receivedMetersCount", received.length);
}
function Status_GetWhitelist(input) {
var whiteList = "";
if (input && input.q === "config") {
whiteList = input.d.devFilter
} else {
whiteList = Device.getConfig("devFilter");
}
return whiteList;
}
function Status_GetExpectedMeters(input) {
var whiteList = Status_GetWhitelist(input);
if (whiteList) {
return whiteList.split(",").map(trimLeadingZeros);
}
return [];
}
function Status_UpdateExpectedMeters(input) {
var expectedMeters = Status_GetExpectedMeters(input);
Device.setProperty("platform.wmbus.expectedMeters", expectedMeters);
Device.setProperty("platform.wmbus.expectedMetersCount", expectedMeters.length);
}
function Status_UpdateMissingMeters(input) {
var expected = Status_GetExpectedMeters(input);
var received = Status_GetReceivedMeters();
var missingMeters = expected
.map(trimLeadingZeros)
.filter(function (x) {
// Remove all that are not in received meters
return received.indexOf(x) === -1
});
Device.setProperty("platform.wmbus.missingMeters", missingMeters);
Device.setProperty("platform.wmbus.missingMetersCount", missingMeters.length);
}
function Status_UpdateStautusDate(date) {
Device.setProperty("platform.wmbus.statusDate", date.toISOString());
}
// Handle a single meter with multiple IDs
// idLL = LinkLayer
// idAL = ApplicationLayer
function Status_HandleReceivedMeterById(idLL, idAL) {
Status_AddReceivedMeter(idLL, idAL);
Status_UpdateMissingMeters(null);
}
function Status_ResetWmbusMetersStatistics(input) {
Status_ClearReceivedMeters();
Status_UpdateMissingMeters(input);
var statusDate = new Date(now());
if (input && input.d && input.d.time) {
statusDate = new Date(input.d.time * 1000);
}
Status_UpdateStautusDate(statusDate);
}
////////////////////////////
// END - Status Functions
////////////////////////////
function NB_SetBatteryStatus(vbat) {
Device.setProperty("device.vbat", vbat);
Device.setProperty("platform.powerType", "battery");
//powerStatus Score:
if (vbat > 3.4) {
Device.setProperty("platform.powerStatus", 9);
} else if (vbat > 3.2) {
Device.setProperty("platform.powerStatus", 5);
} else {
Device.setProperty("platform.powerStatus", 2);
}
}
function NB_ESM_reason(esm) {
switch (esm) {
case 0:
return "-";
case 255:
return "-";
case parseInt("00001000", 2):
return "Operator Determined Barring" + " (" + esm + ")";
case parseInt("00011010", 2):
return "Insufficient resources" + " (" + esm + ")";
case parseInt("00011011", 2):
return "Missing or unknown APN" + " (" + esm + ")";
case parseInt("00011100", 2):
return "Unknown PDN type" + " (" + esm + ")";
case parseInt("00011101", 2):
return "User authentication failed" + " (" + esm + ")";
case parseInt("00011110", 2):
return "Request rejected by Serving GW or PDN GW" + " (" + esm + ")";
case parseInt("00011111", 2):
return "Request rejected, unspecified" + " (" + esm + ")";
case parseInt("00100000", 2):
return "Service option not supported" + " (" + esm + ")";
case parseInt("00100001", 2):
return "Requested service option not subscribed" + " (" + esm + ")";
case parseInt("00100010", 2):
return "Service option temporally out of order" + " (" + esm + ")";
case parseInt("00100011", 2):
return "PTI already in use" + " (" + esm + ")";
case parseInt("00100100", 2):
return "Regular deactivation" + " (" + esm + ")";
case parseInt("00100101", 2):
return "EPS QoS not accepted" + " (" + esm + ")";
case parseInt("00100110", 2):
return "Network failure" + " (" + esm + ")";
case parseInt("00100111", 2):
return "Reactivation requested" + " (" + esm + ")";
case parseInt("00101001", 2):
return "Semantic error in the TFT operation" + " (" + esm + ")";
case parseInt("00101010", 2):
return "Syntactical error in the TFT operation" + " (" + esm + ")";
case parseInt("00101011", 2):
return "Invalid EPS bearer identity" + " (" + esm + ")";
case parseInt("00101100", 2):
return "Semantic errors in packet filters" + " (" + esm + ")";
case parseInt("00101101", 2):
return "Syntactical errors in packet filters" + " (" + esm + ")";
case parseInt("00101110", 2):
return "Unused, protocol error, unspecified" + " (" + esm + ")";
case parseInt("00101111", 2):
return "PTI mismatch" + " (" + esm + ")";
case parseInt("00110001", 2):
return "Last PDN disconnection not allowed" + " (" + esm + ")";
case parseInt("00110010", 2):
return "PDN type IPv4 only allowed" + " (" + esm + ")";
case parseInt("00110011", 2):
return "PDN type IPv6 only allowed" + " (" + esm + ")";
case parseInt("00111001", 2):
return "PDN type IPv4v6 only allowed" + " (" + esm + ")";
case parseInt("00111010", 2):
return "PDN type non IP only allowed" + " (" + esm + ")";
case parseInt("00110100", 2):
return "Single address bearers only allowed" + " (" + esm + ")";
case parseInt("00110101", 2):
return "ESM information not received" + " (" + esm + ")";
case parseInt("00110110", 2):
return "PDN connection does not exist" + " (" + esm + ")";
case parseInt("00110111", 2):
return "Multiple PDN connections for a given APN not allowed" + " (" + esm + ")";
case parseInt("00111000", 2):
return "Collision with network initiated request" + " (" + esm + ")";
case parseInt("00111011", 2):
return "Unsupported QCI value" + " (" + esm + ")";
case parseInt("00111100", 2):
return "Bearer handling not supported" + " (" + esm + ")";
case parseInt("01000001", 2):
return "Maximum number of EPS bearers reached" + " (" + esm + ")";
case parseInt("01000010", 2):
return "Requested APN not supported in current RAT and PLMN combination" + " (" + esm + ")";
case parseInt("01010001", 2):
return "Invalid PTI value" + " (" + esm + ")";
case parseInt("01011111", 2):
return "Semantically incorrect message" + " (" + esm + ")";
case parseInt("01100000", 2):
return "Invalid mandatory information" + " (" + esm + ")";
case parseInt("01100001", 2):
return "Message type non-existent or not implemented" + " (" + esm + ")";
case parseInt("01100010", 2):
return "Message type not compatible with the protocol state" + " (" + esm + ")";
case parseInt("01100011", 2):
return "Information element non-existent or not implemented" + " (" + esm + ")";
case parseInt("01100100", 2):
return "Conditional IE error" + " (" + esm + ")";
case parseInt("01100101", 2):
return "Message not compatible with the protocol state" + " (" + esm + ")";
case parseInt("01101111", 2):
return "Protocol error, unspecified" + " (" + esm + ")";
case parseInt("01110000", 2):
return "APN restriction value incompatible with active EPS bearer context" + " (" + esm + ")";
case parseInt("01110001", 2):
return "Multiple accesses to a PDN connection not allowed" + " (" + esm + ")";
default:
return "esm unknown value" + " (" + esm + ")";
}
}
function NB_EMM_reason(emm) {
switch (emm) {
case 0:
return "-";
case 255:
return "-";
case parseInt("00000010", 2):
return "IMSI unknown in HSS" + " (" + emm + ")";
case parseInt("00000011", 2):
return "Illegal UE" + " (" + emm + ")";
case parseInt("00000101", 2):
return "IMEI not accepted" + " (" + emm + ")";
case parseInt("00000110", 2):
return "Illegal ME" + " (" + emm + ")";
case parseInt("00000111", 2):
return "EPS services not allowed" + " (" + emm + ")";
case parseInt("00001000", 2):
return "EPS services and non-EPS services not allowed" + " (" + emm + ")";
case parseInt("00001001", 2):
return "UE identity cannot be derived by the network" + " (" + emm + ")";
case parseInt("00001010", 2):
return "Implicitly detached" + " (" + emm + ")";
case parseInt("00001011", 2):
return "PLMN not allowed" + " (" + emm + ")";
case parseInt("00001100", 2):
return "Tracking Area not allowed" + " (" + emm + ")";
case parseInt("00001101", 2):
return "Romaing not allowed in this Tracking Area" + " (" + emm + ")";
case parseInt("00001110", 2):
return "EPS services not allowed in this tracking area" + " (" + emm + ")";
case parseInt("00001111", 2):
return "No Suitable Cells in tracking area" + " (" + emm + ")";
case parseInt("00010000", 2):
return "MSC temporarily not reachable" + " (" + emm + ")";
case parseInt("00010001", 2):
return "Network failure" + " (" + emm + ")";
case parseInt("00010010", 2):
return "CS domain not available" + " (" + emm + ")";
case parseInt("00010011", 2):
return "ESM failure" + " (" + emm + ")";
case parseInt("00010100", 2):
return "MAC failure" + " (" + emm + ")";
case parseInt("00010101", 2):
return "Synch failure" + " (" + emm + ")";
case parseInt("00010110", 2):
return "Congestion" + " (" + emm + ")";
case parseInt("00010111", 2):
return "UE security capabilities mismatch" + " (" + emm + ")";
case parseInt("00011000", 2):
return "Security mode rejected, unspecified" + " (" + emm + ")";
case parseInt("00011001", 2):
return "Not authorized for this CSG" + " (" + emm + ")";
case parseInt("00011010", 2):
return "Non-EPS authentication unacceptable" + " (" + emm + ")";
case parseInt("00100011", 2):
return "Requested service option not authorized in this PLMN" + " (" + emm + ")";
case parseInt("00100111", 2):
return "CS service temporarily not available" + " (" + emm + ")";
case parseInt("00101000", 2):
return "No EPS bearer context activated" + " (" + emm + ")";
case parseInt("00101010", 2):
return "Severe network failure" + " (" + emm + ")";
case parseInt("01011111", 2):
return "Semantically incorrect message" + " (" + emm + ")";
case parseInt("01100000", 2):
return "Invalid mandatory information" + " (" + emm + ")";
case parseInt("01100001", 2):
return "Message type non-existent or not implemented" + " (" + emm + ")";
case parseInt("01100010", 2):
return "Message type not compatible with the protocol state" + " (" + emm + ")";
case parseInt("01100011", 2):
return "Information element non-existent or not implemented" + " (" + emm + ")";
case parseInt("01100100", 2):
return "Conditional IE error" + " (" + emm + ")";
case parseInt("01100101", 2):
return "Message not compatible with the protocol state" + " (" + emm + ")";
case parseInt("01101111", 2):
return "Protocol error, unspecified" + " (" + emm + ")";
default:
return "EMM unknown" + " (" + emm + ")";
}
}
// TODO: The statistics are accessing "old" data via getProperty. The prser must use the current values from input.d
function NB_UpdateDerivedStatistics(input) {
var count = Device.getProperty("device.txCount");
if (count) {
var rsrp = Device.getProperty("device.txRsrp") || 0;
Device.setProperty("platform.avgRsrp", (-1 * rsrp / count).toFixed(2));
var rsrq = Device.getProperty("device.txRsrq") || 0;
Device.setProperty("platform.avgRsrq", (-1 * rsrq / count).toFixed(2));
}
var rsrpRaw = Device.getProperty("device.rsrp");
if (rsrpRaw !== undefined) {
Device.setProperty("platform.rsrp", rsrpRaw > 97 ? -100 : rsrpRaw - 140);
}
var rsrqRaw = Device.getProperty("device.rsrq");
if (rsrqRaw !== undefined) {
Device.setProperty("platform.rsrq", rsrqRaw > 34 ? -10 : (rsrqRaw - 39) / 2.0);
}
var awakeSecs = Device.getProperty("device.awakeTime");
var sleepSecs = Device.getProperty("device.sleepTime");
if (awakeSecs && sleepSecs) {
Device.setProperty("platform.sleepRatioPercent", parseFloat((1 - (sleepSecs / (awakeSecs + sleepSecs))) * 100).toFixed(2));
}
// extract sector and tower id from cell-id
// maybe used for lookup with online lte cell map services like www.cellmapper.net
var cellID = Device.getProperty("device.ci");
if (cellID) {
var ci = parseInt(cellID, 16);
var towerID = parseInt(ci / 256, 10);
Device.setProperty("platform.eNodeB.towerID", towerID);
var sector = ci & 255
Device.setProperty("platform.eNodeB.sectorID", sector);
}
}
function NB_ParseDeviceQuery(input) {
for (var key in input.d) {
var v = input.d[key];
switch (key) {
case "temperature":
v = v / 10.0;
break;
case "vbat":
v = v / 1000.0;
NB_SetBatteryStatus(v)
continue;
case "EMM":
Device.setProperty("device.EMM", NB_EMM_reason(v));
continue;
case "EMM_r":
Device.setProperty("device.EMM_r", NB_EMM_reason(v));
continue;
case "ESM":
Device.setProperty("device.ESM", NB_ESM_reason(v));
continue;
case "ESM_r":
Device.setProperty("device.ESM_r", NB_ESM_reason(v));
continue;
case "hFree":
Device.setProperty("platform.device.heapAllocs", input.d.hAlloc - input.d.hFree);
Device.setProperty("device." + key, v);
continue;
}
Device.setProperty("device." + key, v);
}
NB_UpdateDerivedStatistics(input);
return null;
}
function NB_ExtractMainWmbusValue(wmbus) {
// Generic
if (wmbus.Body && wmbus.Body.DataRecords) {
var len = wmbus.Body.DataRecords.length;
for (var i = 0; i < len; i++) {
if (wmbus.Body.DataRecords[i].StorageNo === 0
&& wmbus.Body.DataRecords[i].VifQuantity !== "Time & Date"
&& wmbus.Body.DataRecords[i].VifQuantity !== "Date"
&& wmbus.Body.DataRecords[i].VifQuantity !== "Averaging Duration"
&& wmbus.Body.DataRecords[i].DifFunctionString === "Current Value"
&& wmbus.Body.DataRecords[i].VifUnit
&& wmbus.Body.DataRecords[i].VifUnit !== "s") {
return wmbus.Body.DataRecords[i].ValueString + " " + wmbus.Body.DataRecords[i].VifUnit;
}
}
}
return undefined;
}
function NB_ParseConfigQuery(input) {
for (var key in input.d) {
Device.setConfig(key, input.d[key]);
}
return null;
}
function NB_ExtractStatus(input) {
if (input.d) {
var d = input.d;
if (d.vbat) {
var vbat = d.vbat / 1000.0
NB_SetBatteryStatus(vbat)
}
if (d.monitor) {
Device.setProperty("debug.monitor", d.monitor);
}
if (d.temperature) {
Device.setProperty("device.temperature", d.temperature / 10.0);
}
}
}
// NB_UpdateDeviceStatus takes any upload and updates the device status for the customer
// The device status shows:
// "Empfangs Status" = (platform.wmbus.receivedMetersCount / platform.wmbus.expectedMetersCount) (e.g. = "1/20")
// Sort Property for the column: "platform.wmbus.missingMetersCount"
// "Status Datum" = platform.wmbus.receivedMetersCount.updatedAt // Date of last wMbus Telegram (or empty upload)
function NB_UpdateDeviceStatus(input) {
// TODO: Write test for install telegram
if (input && input.q === "install") {
if (input.d.telegrams === input.d.uploading) {
Status_UpdateExpectedMeters(input);
Status_ResetWmbusMetersStatistics(input);
}
}
if (input && input.q === "status") {
// Pre Upload Status (collected = true && telegrams == uploading) - also with uploading == 0
if (input.d && input.d.collected && input.d.telegrams === input.d.uploading) {
Status_UpdateExpectedMeters(input);
Status_ResetWmbusMetersStatistics(input);
}
// Nightly Upload (collected = false && telegrams == uploading && uploading != 0)
// Nightly Upload with uploading == 0 is the normal one,
// Reset stats only if no telegrams are uploaded already (telegrams == uploading && uploading != 0)
else if (input.d && input.d.telegrams === input.d.uploading && input.d.uploading !== 0) {
Status_UpdateExpectedMeters(input);
Status_ResetWmbusMetersStatistics(input);
}
}
Status_UpdateMissingMeters(input);
}
function NB_ParseTelegram(input, item) {
var wmbus = Parser.parseWmbus(parseBase64(item.telegram));
var parsed = {
"mainValue": NB_ExtractMainWmbusValue(wmbus),
"mbus": wmbus,
"time": (item.timestamp || 0) * 1000,
"rssi": item.rssi,
"gwImei": input.i,
};
var oldLastReadout = Device.getProperty("platform.wmbus.lastReadout")
var newLastReadout = new Date(item.timestamp * 1000 || now())
if (!oldLastReadout || new Date(oldLastReadout) < newLastReadout) {
Device.setProperty("platform.wmbus.lastReadout", newLastReadout.toISOString());
}
var idLL = undefined;
var idAL = undefined;
if (wmbus && wmbus.IdString) {
idLL = wmbus.IdString;
if (wmbus.Header && wmbus.Header.IdString) {
idAL = wmbus.Header.IdString;
}
Status_UpdateStautusDate(newLastReadout);
Status_HandleReceivedMeterById(idLL, idAL);
}
return parsed;
}
function NB_ParseDataQuery(input) {
NB_ExtractStatus(input);
Record.setTimeField("time", "UNIXMS");
// batch upload
if (input.d.batch) {
var d = input["d"] || {};
var batch = d["batch"] || [];
var out = [];
batch.forEach(function (item, index) {
var t = NB_ParseTelegram(input, item);
out.push(t);
});
return out;
} else if (input.d.telegram) {
return NB_ParseTelegram(input, input.d);
}
return null;
}
function NB_Parse420Entry(input, item) {
item.gwImei = input.i;
return item;
}
function NB_Parse420Query(input) {
Record.setTimeField("time", "UNIXS");
// batch upload
if (input.d.batch) {
var d = input["d"] || {};
var batch = d["batch"] || [];
var out = [];
batch.forEach(function (item, index) {
var t = NB_Parse420Entry(input, item);
out.push(t);
});
return out;
} else if (input.d.telegram) {
return NB_Parse420Entry(input, input.d);
}
return null;
}
function NB_ParseInstallQuery(input) {
NB_UpdateDeviceStatus(input)
}
function NB_ParseStatusQuery(input) {
// Device Query and Status Query are handled the same here
NB_ParseDeviceQuery(input);
// Update the device status regarding the device table config values
NB_UpdateDeviceStatus(input);
}
function NB_Parse(input) {
var query = input.q || "data";
switch (query) {
case "device":
return NB_ParseDeviceQuery(input);
case "install":
return NB_ParseInstallQuery(input);
case "config":
return NB_ParseConfigQuery(input);
case "data":
return NB_ParseDataQuery(input);
case "420":
return NB_Parse420Query(input);
case "status":
return NB_ParseStatusQuery(input);
default:
return null;
// Führt nur zu Verwirrung
//throw new Error("Unknown message type: '" + query + "'");
}
}
function LW_Parse(input) {
// Decode an incoming message to an object of fields.
var bytes = parseBase64(input.data);
var port = input.fPort;
var decoded = null;
// TODO: LoRaWAN Parser not implemented
return decoded;
}
function Parse(input) {
if (input.i) {
//IMEI = nb iot input
return NB_Parse(input)
} else if (input.devEUI) {
//devEUI = lorawan input
return LW_Parse(input)
} else {
throw new Error("Neither NB IoT nor LORWAN Msg detected.");
}
}
Declaration of Conformity
| Download CE declaration of conformity |