BLE

Regarding Bluetooth Low Energy (BLE) functionality, this system supports scanning only. Pairing with other devices is not supported.


ble.on()

Starts the BLE module.

ble.on()

ble.off()

Stops the BLE module.
Once stopped, the system resources occupied by the BLE module for Lua are released.


ble.scanStart(callback, options)

Starts a BLE scan indefinitely.

Options

Key Type Default Description
active boolean true Set to true for Active Scanning.
interval number 0x10 Specifies the scan interval (2-byte numeric value).
window number 0x10 Specifies the scan window (2-byte numeric value).
phy string "all" PHY to scan: "1m" (standard BLE packets), "coded" (BLE5 Coded PHY packets), or "all" (both). (OS7.1.0 and later)
duplicate boolean true Set to false to filter out duplicate signals from the same MAC address.

Note: behavior of duplicate=false

When you set duplicate=false, signals from a MAC address that has already been received are not notified again. However, this filter is only effective until the scan is stopped or until the internal buffer becomes full.

If you keep scanning without ever stopping, the internal buffer will eventually overflow. When that happens the filter is reset, and signals from devices that were already received will start being received (notified) again.

For this reason, when using duplicate=false, design your code on the assumption that it must be paired with logic that periodically stops and restarts the scan. Restarting the scan resets the filter state and avoids unexpected re-notifications caused by buffer overflow.

Note: changes in OS7.1.0

In OS7.0.x, the PHY was selected with the boolean options phy1m and phyCoded. From OS7.1.0, these keys are ignored. Use phy instead.

Coded PHY is not available on every chip (for example, the original ESP32 does not support it). On such chips, the default "all" scans 1M only, and "coded" returns an error. Use ble.supportsCoded() to check.

Return value

Value Meaning
0 Scan started.
1 Invalid option (for example, an unknown phy value).
2 "coded" was specified on a chip without Coded PHY support. (OS7.1.0 and later)
Other The scan could not be started.

callback(peripheral)

The callback function receives a table (object) containing the data of the detected advertisement.

Peripheral Object Properties:

Key Type Description
address string The MAC address of the detected device.
rssi number Received Signal Strength Indicator (signal strength).
advData string The raw advertisement packet data.
isScanResp boolean true when the packet is a scan response, false when it is an advertisement. (OS7.1.0 and later)

ble.scanStop()

Stops the BLE scan.

ble.scanStop();

ble.getScanMode()

Returns the PHY of the current scan as a string: "1m", "coded", or "all". Returns nil when not scanning.

Available for OS7.1.0 and later

On chips without Coded PHY support, a scan started with "all" returns "1m".


ble.getScanOptions()

Returns the options of the current scan as a table with the keys phy, active, duplicate, interval, and window. Returns nil when not scanning.

Available for OS7.1.0 and later


ble.supportsCoded()

Returns true if the chip supports BLE5 Coded PHY, and false otherwise.

Available for OS7.1.0 and later

local phy = ble.supportsCoded() and "all" or "1m";
ble.scanStart(onFind, { phy = phy });

Example

The following example demonstrates how to start a passive scan immediately upon startup and receive all available signals.

The received signal details will be output to the serial console.

-- Helper function to convert string to hex format
local function tohex(data)
  local hex = {}
  for i = 1, #data do
    hex[#hex+1] = string.format("%02x", data:byte(i))
  end
  return table.concat(hex, "")
end

-- Callback function triggered when a device is found
function onFind(peripheral)
  os.log("found\n addr: " .. tohex(peripheral.address) .. 
         "\n rssi:" .. tostring(peripheral.rssi) .. 
         "\n data:" .. tohex(peripheral.advData));
end

-- Initialize BLE
local ret = ble.on();
os.log("ble on ret=" .. tostring(ret));

-- Start scanning with specific options
ret = ble.scanStart(onFind, {
  active=false,    -- Passive scan
  interval=16,
  window=16,
  phy="all",       -- 1M and Coded PHY
  duplicate=true   -- Do not filter duplicates
});

os.log("scan started ret=" .. tostring(ret));