BLE Beacon
Broadcast a fake temperature reading in the advertising packet's manufacturer-data field. This is broadcaster-mode BLE: the device never accepts connections; it just transmits a short payload that any scanner in range can pick up. See the ble API reference for the full type surface.
Useful for presence beacons, simple sensor broadcasts, iBeacon-style advertisers, or anything where you want to avoid the power and complexity of GATT connections.
Hardware
- Any ESP32 family board with BLE (ESP32-C3, ESP32-C6, ESP32-S3, original ESP32). ESP32-S2 has no BLE radio and is not supported.
- USB cable for flashing and serial logging.
- A phone with nRF Connect for Mobile or LightBlue to observe the beacon.
Code
ts
import {ble, peripheral} from 'mikro/ble'
import {sleep} from 'mikro/sleep'
ble.name = 'mikrojs-beacon'
// Encode a fake temperature reading as manufacturer-specific data.
// First two bytes are the company ID (0xFFFF = reserved for testing).
// Remaining bytes are a little-endian int16 in units of 0.01 °C.
function encodeTemperature(tempCelsius: number): Uint8Array {
const centi = Math.round(tempCelsius * 100)
return new Uint8Array([0xff, 0xff, centi & 0xff, (centi >> 8) & 0xff])
}
async function startAdvertising(tempCelsius: number) {
return peripheral.advertise({
name: 'mikrojs-beacon',
connectable: false,
interval: {min: 1000, max: 1500},
includeTxPower: true,
manufacturerData: encodeTemperature(tempCelsius),
})
}
let temp = 21.5
let result = await startAdvertising(temp)
if (!result.ok) {
console.error('ble.advertise failed:', result.error)
} else {
console.log('BLE beacon advertising as %s at %s', ble.name, ble.address)
while (true) {
await sleep(5000)
temp += (Math.random() - 0.5) * 0.2
const stopResult = result.value.stop()
if (!stopResult.ok) {
console.warn('ble.stop (refresh) failed:', stopResult.error)
}
result = await startAdvertising(temp)
if (!result.ok) {
console.error('ble.advertise (refresh) failed:', result.error)
break
}
}
}What you'll see
In nRF Connect / LightBlue, scan for nearby devices and look for mikrojs-beacon. Tapping into it reveals the advertising payload:
- Complete Local Name:
mikrojs-beacon - Manufacturer Specific Data:
FFFF5308(or similar). Decoded as:- First two bytes
FFFF= company ID (reserved for testing). - Next two bytes
5308= little-endian int16 = 0x0853 = 2131 = 21.31 °C.
- First two bytes
- TX Power Level: typically 0 or +9 dBm on ESP32-C6's default setting.
- RSSI: updates continuously as you move.
Every ~5 seconds the temperature bytes drift slightly as the loop adds random noise.
Create project
sh
pnpm create mikro --template ble-beaconsh
npm create mikro -- --template ble-beaconsh
yarn create mikro --template ble-beaconsh
bun create mikro --template ble-beaconRun it
sh
pnpm install
pnpm mikro flash # only needed once per board
pnpm mikro devsh
npm install
npx mikro flash # only needed once per board
npx mikro devsh
yarn install
yarn mikro flash # only needed once per board
yarn mikro devsh
bun install
bunx mikro flash # only needed once per board
bunx mikro dev