This document provides detailed documentation for the RAK3112 LoRa Basic Modem C++ API interface.
- Initialization
- Network Management
- Data Transmission
- ADR Configuration
- Channel Access Control
- Network Utilities
- Hardware Configuration
- Timers
- Event Handling
- Complete Usage Example
- Return Codes
- Important Notes
- Version Information
Initialize the LoRa Basic Modem.
Returns: smtc_modem_return_code_t
SMTC_MODEM_RC_OK: Success
Example:
void setup() {
Serial.begin(115200);
lbm.init();
}Run the modem engine, must be called periodically in the main loop.
Example:
void loop() {
lbm.runEngine();
delay(10);
}Register an event callback function.
Parameters:
callback: Callback function of typevoid (*)(void)
Example:
void onModemEvent() {
// Handle modem events
}
void setup() {
lbm.setEventCallback(onModemEvent);
}Set the device unique identifier (DevEUI).
Parameters:
dev_eui: Pointer to 8-byte DevEUI array
Returns: smtc_modem_return_code_t
Note:
- Must be called before join
- Cannot be modified when already joined
Example:
uint8_t dev_eui[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
lbm.lorawan.setDevEUI(dev_eui);Set Join EUI (also known as App EUI).
Parameters:
join_eui: Pointer to 8-byte JoinEUI array
Returns: smtc_modem_return_code_t
Example:
uint8_t join_eui[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
lbm.lorawan.setJoinEUI(join_eui);Set the application key (AppKey).
Parameters:
app_key: Pointer to 16-byte AppKey array
Returns: smtc_modem_return_code_t
Example:
uint8_t app_key[16] = {0x00, 0x01, 0x02, ..., 0x0F};
lbm.lorawan.setAppKey(app_key);Set the network key (NwkKey).
Parameters:
nwk_key: Pointer to 16-byte NwkKey array
Returns: smtc_modem_return_code_t
Set the LoRaWAN region.
Parameters:
region: Region code, predefined macros:REGION_EU868: Europe 868MHzREGION_US915: USA 915MHzREGION_AS923_1: Asia 923MHz Group 1REGION_CN470: China 470MHzREGION_AU915: Australia 915MHzREGION_KR920: Korea 920MHz- etc.
Returns: smtc_modem_return_code_t
Example:
lbm.lorawan.setRegion(REGION_EU868);Set the LoRaWAN device class.
Parameters:
modem_class: Device classSMTC_MODEM_CLASS_A: Default classSMTC_MODEM_CLASS_B: Requires network supportSMTC_MODEM_CLASS_C: Requires network support
Returns: smtc_modem_return_code_t
Example:
lbm.lorawan.setClass(SMTC_MODEM_CLASS_A);Start the OTAA join procedure.
Returns: smtc_modem_return_code_t
Note:
- Must set DevEUI, JoinEUI, AppKey, NwkKey and Region first
- Generates
SMTC_MODEM_EVENT_JOINEDevent on success - Generates
SMTC_MODEM_EVENT_JOINFAILevent on failure
Example:
lbm.lorawan.join();Check if the device is joined to the network.
Parameters:
joined: Output parameter,trueif joined,falseotherwise
Returns: smtc_modem_return_code_t
Example:
bool joined;
lbm.lorawan.isJoined(&joined);
if (joined) {
Serial.println("Device is joined");
}Leave the network or cancel an ongoing join.
Returns: smtc_modem_return_code_t
Example:
lbm.lorawan.leaveNetwork();Get the network type configuration.
Parameters:
network_type: Output parameter,truefor public network,falsefor private network
Returns: smtc_modem_return_code_t
Set the network type.
Parameters:
network_type:truefor public network,falsefor private network
Returns: smtc_modem_return_code_t
Example:
lbm.lorawan.setNetworkType(true); // Public networkSend uplink data.
Parameters:
data: Pointer to payload datalen: Payload length (maximum depends on region and data rate)port: LoRaWAN FPort (1-223, default 2)confirmed:truefor confirmed uplink,falsefor unconfirmed (default)
Returns: smtc_modem_return_code_t
Note:
- Device must be joined
- Generates
SMTC_MODEM_EVENT_TXDONEevent after transmission
Example:
uint8_t payload[] = {0x01, 0x02, 0x03};
lbm.lorawan.send(payload, 3, 2, false);Send an empty uplink (keepalive packet).
Parameters:
send_fport:trueto include FPort,falseotherwisefport: FPort value (if send_fport is true)confirmed:truefor confirmed uplink,falsefor unconfirmed
Returns: smtc_modem_return_code_t
Purpose: Create a downlink opportunity or keepalive without sending data to the application server
Example:
// Send keepalive packet
lbm.lorawan.sendEmptyUplink(false, 0, false);Get received downlink data.
Parameters:
payload: Output buffer (maximum 242 bytes)payload_size: Output actual received payload lengthmetadata: Output downlink metadata (RSSI, SNR, window, etc.)remaining: Output number of remaining downlinks to read
Returns: smtc_modem_return_code_t
Note:
- Call after receiving
SMTC_MODEM_EVENT_DOWNDATAevent - If remaining > 0, call again to get next downlink
Example:
void onModemEvent() {
uint8_t payload[242];
uint8_t payload_size;
smtc_modem_dl_metadata_t metadata;
uint8_t remaining;
if (lbm.lorawan.getDownlinkData(payload, &payload_size, &metadata, &remaining) == SMTC_MODEM_RC_OK) {
Serial.printf("Received %d bytes on port %d\n", payload_size, metadata.fport);
Serial.printf("RSSI: %d dBm, SNR: %d dB\n", metadata.rssi - 64, metadata.snr / 4);
}
}Get the maximum payload size for the next uplink.
Parameters:
tx_max_payload_size: Output maximum payload size in bytes
Returns: smtc_modem_return_code_t
Note: Value depends on current data rate and regional parameters
Example:
uint8_t max_size;
lbm.lorawan.getNextTxMaxPayload(&max_size);
Serial.printf("Max payload: %d bytes\n", max_size);Get the current duty cycle status.
Parameters:
duty_cycle_status_ms: Output duty cycle status (milliseconds)- Positive value: Available time remaining
- Negative value: Wait time required
Returns: smtc_modem_return_code_t
Example:
int32_t duty_cycle_ms;
lbm.lorawan.getDutyCycleStatus(&duty_cycle_ms);
if (duty_cycle_ms >= 0) {
Serial.printf("Can transmit, %dms available\n", duty_cycle_ms);
} else {
Serial.printf("Must wait %dms\n", -duty_cycle_ms);
}Set custom data rate distribution during join phase.
Parameters:
dr_distribution: 16-byte array, each byte represents the probability weight for DR0-DR15 (sum should be 100)
Returns: smtc_modem_return_code_t
Note:
- EU868 typically uses DR0-DR5, others set to 0
- Invalid for US915/AU915 regions
Example:
// EU868: DR0=20%, DR1=20%, DR2=20%, DR3=20%, DR4=10%, DR5=10%
uint8_t dr_dist[16] = {20, 20, 20, 20, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
lbm.lorawan.setJoinDataRateDistribution(dr_dist);Set the ADR profile after joining the network.
Parameters:
adr_profile: ADR configuration typeSMTC_MODEM_ADR_PROFILE_NETWORK_CONTROLLED: Network controlled (static device)SMTC_MODEM_ADR_PROFILE_MOBILE_LONG_RANGE: Mobile device long rangeSMTC_MODEM_ADR_PROFILE_MOBILE_LOW_POWER: Mobile device low powerSMTC_MODEM_ADR_PROFILE_CUSTOM: Custom distribution
dr_distribution: Custom distribution array (only needed for CUSTOM mode)
Returns: smtc_modem_return_code_t
Example:
// Use mobile device long range mode
lbm.lorawan.setADRProfile(SMTC_MODEM_ADR_PROFILE_MOBILE_LONG_RANGE, nullptr);
// Use custom distribution
uint8_t custom_dr[16] = {10, 20, 30, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
lbm.lorawan.setADRProfile(SMTC_MODEM_ADR_PROFILE_CUSTOM, custom_dr);Set the number of transmissions for unconfirmed uplinks.
Parameters:
nb_trans: Number of transmissions (1-15)
Returns: smtc_modem_return_code_t
Note: Only effective in MOBILE or CUSTOM ADR modes
Example:
lbm.lorawan.setNbTrans(3); // Retransmit 3 timesGet the currently enabled data rates mask.
Parameters:
enabled_datarates_mask: Output bitmask
Returns: smtc_modem_return_code_t
Note: Depends on uplink channel mask and dwell time settings
Example:
uint16_t dr_mask;
lbm.lorawan.getEnabledDatarates(&dr_mask);
Serial.printf("Enabled DRs: 0x%04X\n", dr_mask);Set ADR ACK limit and delay.
Parameters:
adr_ack_limit: ADR ACK limit (2-127)adr_ack_delay: ADR ACK delay (2-127)
Returns: smtc_modem_return_code_t
Purpose: Control ADR fallback behavior when no downlinks are received
Example:
lbm.lorawan.setADRAckLimitDelay(64, 32);Get ADR ACK limit and delay configuration.
Parameters:
adr_ack_limit: Output ADR ACK limitadr_ack_delay: Output ADR ACK delay
Returns: smtc_modem_return_code_t
Used for regulatory requirements in regions like Japan (AS923) and Korea (KR920).
Set LBT parameters.
Parameters:
listening_duration_ms: Listening duration (typically 5ms for Japan)threshold_dbm: RSSI threshold (typically -80dBm)bw_hz: Listening bandwidth (typically 200000 for 200kHz)
Returns: smtc_modem_return_code_t
Example:
// Typical configuration for Japan AS923
lbm.lorawan.setLBTParameters(5, -80, 200000);Get current LBT parameters.
Enable or disable LBT.
Parameters:
enable:trueto enable,falseto disable
Note: Must call setLBTParameters first
Get LBT state.
Channel access mechanism automatically enabled in regions where LBT is not mandatory.
Enable or disable CSMA.
Get CSMA state.
Set CSMA parameters.
Parameters:
max_ch_change: Number of channel changes when channel is busy (default 4)bo_enabled: Enable backoff (multiple CAD detections on same channel, default false)nb_bo_max: Number of CAD detections (default 6)
Note: CAD = Channel Activity Detection
Example:
lbm.lorawan.setCSMAParameters(4, false, 6);Get current CSMA parameters.
Suspend or resume radio communications.
Parameters:
suspend:trueto suspend,falseto resume
Returns: smtc_modem_return_code_t
Get radio suspension status.
Get join duty cycle backoff bypass status.
Purpose: LoRaWAN certification testing
Set join duty cycle backoff bypass.
Parameters:
enable:trueto bypass duty cycle limits,falsefor normal mode
Note: Only for certification testing, should be disabled in production
Example:
// Only for testing
lbm.lorawan.setJoinDutyCycleBackoffBypass(true);Set crystal error (ppm).
Parameters:
crystal_error_ppm: Crystal error value
Returns: smtc_modem_return_code_t
Example:
lbm.lorawan.setCrystalError(10); // 10ppm errorGet crystal error configuration.
Start an alarm timer.
Parameters:
alarm_timer_in_s: Timer duration (seconds, maximum 864000 seconds = 10 days)
Returns: smtc_modem_return_code_t
Note: Generates SMTC_MODEM_EVENT_ALARM event when expired
Example:
// Set 60-second timer
lbm.lorawan.startAlarmTimer(60);Stop and clear the alarm timer.
Returns: smtc_modem_return_code_t
Get alarm remaining time.
Parameters:
remaining_time_in_s: Output remaining seconds
Returns: smtc_modem_return_code_t
The modem notifies the application of state changes through an event system. Main events include:
SMTC_MODEM_EVENT_RESET: Modem has resetSMTC_MODEM_EVENT_ALARM: Alarm timer expiredSMTC_MODEM_EVENT_JOINED: Successfully joined networkSMTC_MODEM_EVENT_TXDONE: Transmission completedSMTC_MODEM_EVENT_DOWNDATA: Downlink data receivedSMTC_MODEM_EVENT_JOINFAIL: Join failed
void onModemEvent() {
smtc_modem_event_t event;
uint8_t event_pending_count;
while (smtc_modem_get_event(&event, &event_pending_count) == SMTC_MODEM_RC_OK) {
switch (event.event_type) {
case SMTC_MODEM_EVENT_RESET:
Serial.println("Modem reset");
break;
case SMTC_MODEM_EVENT_JOINED:
Serial.println("Network joined!");
break;
case SMTC_MODEM_EVENT_TXDONE:
Serial.printf("TX done, status: %d\n", event.event_data.txdone.status);
break;
case SMTC_MODEM_EVENT_DOWNDATA:
Serial.println("Downlink received");
// Call getDownlinkData() to retrieve data
break;
case SMTC_MODEM_EVENT_JOINFAIL:
Serial.println("Join failed");
break;
case SMTC_MODEM_EVENT_ALARM:
Serial.println("Alarm triggered");
break;
}
}
}
void setup() {
lbm.setEventCallback(onModemEvent);
}#include <Arduino.h>
#include "lbm_api.h"
// LoRaWAN credentials
uint8_t dev_eui[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
uint8_t join_eui[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
uint8_t app_key[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
uint8_t nwk_key[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
bool joined = false;
void onModemEvent() {
smtc_modem_event_t event;
uint8_t event_pending_count;
while (smtc_modem_get_event(&event, &event_pending_count) == SMTC_MODEM_RC_OK) {
switch (event.event_type) {
case SMTC_MODEM_EVENT_JOINED:
Serial.println("✓ Network joined successfully!");
joined = true;
break;
case SMTC_MODEM_EVENT_TXDONE:
Serial.println("✓ Transmission completed");
break;
case SMTC_MODEM_EVENT_DOWNDATA:
handleDownlink();
break;
case SMTC_MODEM_EVENT_JOINFAIL:
Serial.println("✗ Join failed, retrying...");
break;
}
}
}
void handleDownlink() {
uint8_t payload[242];
uint8_t payload_size;
smtc_modem_dl_metadata_t metadata;
uint8_t remaining;
if (lbm.lorawan.getDownlinkData(payload, &payload_size, &metadata, &remaining) == SMTC_MODEM_RC_OK) {
Serial.printf("Downlink: port=%d, size=%d, RSSI=%ddBm, SNR=%ddB\n",
metadata.fport, payload_size,
metadata.rssi - 64, metadata.snr / 4);
// Process payload data
for (int i = 0; i < payload_size; i++) {
Serial.printf("%02X ", payload[i]);
}
Serial.println();
}
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("RAK3112 LoRaWAN Starting...");
// Initialize modem
lbm.init();
lbm.setEventCallback(onModemEvent);
// Configure LoRaWAN parameters
lbm.lorawan.setDevEUI(dev_eui);
lbm.lorawan.setJoinEUI(join_eui);
lbm.lorawan.setAppKey(app_key);
lbm.lorawan.setNwkKey(nwk_key);
lbm.lorawan.setRegion(REGION_EU868);
lbm.lorawan.setClass(SMTC_MODEM_CLASS_A);
// Configure ADR (optional)
uint8_t dr_dist[16] = {20, 20, 20, 20, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
lbm.lorawan.setJoinDataRateDistribution(dr_dist);
// Start join
lbm.lorawan.join();
Serial.println("Join request sent...");
}
void loop() {
// Must be called periodically
lbm.runEngine();
// Send data every 30 seconds after joining
static uint32_t last_send = 0;
if (joined && (millis() - last_send > 30000)) {
last_send = millis();
// Prepare payload
uint8_t payload[] = {0x01, 0x02, 0x03, 0x04};
// Check maximum payload
uint8_t max_size;
if (lbm.lorawan.getNextTxMaxPayload(&max_size) == SMTC_MODEM_RC_OK) {
if (sizeof(payload) <= max_size) {
// Send data
lbm.lorawan.send(payload, sizeof(payload), 2, false);
Serial.println("Uplink sent");
}
}
}
delay(10);
}All API functions return smtc_modem_return_code_t type:
SMTC_MODEM_RC_OK(0x00): Command executed successfullySMTC_MODEM_RC_NOT_INIT: Command not initializedSMTC_MODEM_RC_INVALID: Invalid parameterSMTC_MODEM_RC_BUSY: Command cannot be executed at this timeSMTC_MODEM_RC_FAIL: Command execution failedSMTC_MODEM_RC_NO_TIME: No time availableSMTC_MODEM_RC_INVALID_STACK_ID: Invalid stack_idSMTC_MODEM_RC_NO_EVENT: No event available
- Initialization Order: Must call
lbm.init()before configuring other parameters - Periodic Calling:
lbm.runEngine()must be called periodically in the main loop - Event Handling: Register an event callback to handle asynchronous events
- Pre-Join Configuration: DevEUI, JoinEUI, AppKey, NwkKey, Region must be set before join
- Duty Cycle: Regions like Europe have strict duty cycle limits, use
getDutyCycleStatus()to check - Maximum Payload: Use
getNextTxMaxPayload()to check maximum payload at current DR - Debug Output: Enable
BASIC_MODEM_DEBUGmacro to view detailed debug information
- API Version: 1.0
- Based on: Semtech LoRa Basic Modem SWL2001
- Hardware Platform: RAK3112 (ESP32-S3 + SX1262)
- Last Updated: 2025-11-22