Developer Resources

Device API Documentation

Build your own applications for Blue Maestro environmental logging devices. Complete BLE protocol reference for iOS and Android development.

Version 2.0|Last Updated: 5th December 2025

1. Overview

This documentation provides comprehensive technical details for developing applications that communicate with Blue Maestro environmental logging devices (versions 13, 23, 27, 41, 42, and 43).

1.1 Device Versions Summary

VersionSensorsPrecisionMax LogsFeatures
13Temperature0.1C20,000Basic temperature logging
23Temp, Humidity, Dew Point0.16,000Multi-sensor logging
27Temp, Humidity, Pressure, Dew Point0.16,000Environmental monitoring
41Temperature0.01C100,000High precision, extended features
42Temp, Humidity, Dew Point0.0150,000High precision, alerts
43Temp, Humidity, Pressure, Dew Point0.0125,000Full environmental suite

1.2 Device Generations

Legacy Devices (v13, v23, v27)

  • Settings stored locally on phone only
  • Commands prefixed with * (asterisk)
  • 0.1 precision (divide raw values by 10)
  • No device-side calibration storage

Modern Devices (v41, v42, v43)

  • Settings stored on device via BLE commands
  • Direct commands (no asterisk prefix)
  • 0.01 precision (divide raw values by 100)
  • Device-side storage for calibration, alerts, and settings
  • Enhanced flag byte in advertisement

2. Device Identification

2.1 Manufacturer Identification

Blue Maestro devices are identified by their manufacturer data header:

Manufacturer Header
Byte 0: 0x33 (51 decimal)
Byte 1: 0x01 (1 decimal)

2.2 Version Detection

The device version is located at byte offset +2 from the manufacturer data start:

Version Byte
Byte 2: Device Version (13, 23, 27, 41, 42, or 43)

2.3 Platform-Specific Advertisement Position

PlatformAdvertisement Data Location
iOSadvertisement.manufacturerData - Start at position 0
AndroidrawManufacturerData - Search for 0xFF byte, start after it
Android Detection Algorithm
Find position where byte == 0xFF (255)
Start parsing at position + 1

4. BLE Communication Protocol

4.1 Service UUIDs

Nordic UART Service (NUS) - Primary Communication
Service UUID:        6E400001-B5A3-F393-E0A9-E50E24DCCA9E
RX Characteristic:   6E400002-B5A3-F393-E0A9-E50E24DCCA9E  (Write - App to Device)
TX Characteristic:   6E400003-B5A3-F393-E0A9-E50E24DCCA9E  (Notify - Device to App)
Alerts Service - Real-time Notifications
Service UUID:        6E400001-9489-44E4-9E39-60BA18464A30
Indicator:           6E400002-9489-44E4-9E39-60BA18464A30  (Notify)

4.2 Connection Sequence

  1. Scan for devices with manufacturer data starting with 0x33 0x01
  2. Connect to device using BLE address
  3. Discover Services and characteristics
  4. Subscribe to TX characteristic for notifications
  5. Write commands to RX characteristic
  6. Receive responses via TX notifications

4.3 Command Transmission

Commands are transmitted as ASCII strings via the NUS RX characteristic.

Chunking Rules

  • Maximum chunk size: 20 bytes
  • Delay between chunks: 100ms
  • Commands ≤20 bytes: Single write
  • Commands >20 bytes: Split into 20-byte chunks

5. Device Commands

5.1 Command Format

Commands are sent over Bluetooth Nordic UART Service (NUS) after connecting to the device.

Command Structure
<command>~<value>
  • Commands are case-insensitive
  • Use ~ as delimiter between command and value
  • Some commands don't require a value

Response Format:

  • OK or OK: <message> - Success
  • ERR or custom error message - Failure
  • Device typically disconnects after sending response

5.2 Command Summary Table

CommandParametersRequires UnlockDescription
lognumber/allNoDownload logs
logallnoneNoDownload all logs
setlogsecondsYesSet logging interval (1-86400s)
pin4-digitNoSet/clear device PIN lock
airplanenoneYesToggle airplane mode
buttonnoneYesToggle button enable/disable
lednoneYesToggle LED enable/disable
infononeNoGet device information
rcalnoneNoRead calibration values
wcalversion-specificYesWrite calibration offsets
rstrngnoneNoRead user string
wstrnglen~stringYesWrite user string (max 500 chars)
clrnoneYesClear all logs
dellogsnoneYesDelete logs
resetnoneYesFactory reset device
offnoneYesPower off device
namestringYesSet device name (max 20 chars)
txpwrdBmYesSet TX power (4, 0, -4, -8)
blinksecondsNoBlink LED (1-30s)
delaysecondsYesDelayed logging start

5.3 Legacy Command Reference (v13, v23, v27)

Legacy devices use asterisk (*) prefixed commands with values appended directly (no separator).

Legacy Command Summary

CommandFormatDescription
*logall*logallDownload all logs
*logtemp*logtemp<pos>Download temperature log from position
*loghumi*loghumi<pos>Download humidity log from position
*logdewp*logdewp<pos>Download dewpoint log from position
*logntemp*logntemp<pos>Download temp log (stay connected)
*lognhumi*lognhumi<pos>Download humidity log (stay connected)
*lint*lint<seconds>Set logging interval (2-86400s)
*sint*sint<seconds>Set sensor/advertising interval
*clr*clrClear all stored logs
*pwd*pwd<4-digit>Set or enter password
*nam*nam<name>Set device name (max 8 chars)
*txp*txp<0-2>TX power (0=+4dBm, 1=0dBm, 2=-4dBm)
*led*ledToggle LED on/off
*airon*aironEnable airplane mode
*airoff*airoffDisable airplane mode
*rboot*rbootReboot device
*dfu*dfuEnter DFU/bootloader mode
*shp*shpEnter shipping/deep sleep
*info*infoGet device settings info
*tell*tellStream telemetric data
*batt*battGet battery level
*bur*burEnable burst/streaming mode
*qq*qqForce disconnect
*unitsc*unitscSet units to Celsius
*unitsf*unitsfSet units to Fahrenheit
*alrm1t*alrm1t<op><val>Set alarm 1 temperature threshold
*alrm2t*alrm2t<op><val>Set alarm 2 temperature threshold
*alrm1h*alrm1h<op><val>Set alarm 1 humidity threshold
*alrm2h*alrm2h<op><val>Set alarm 2 humidity threshold
*alrmi*alrmiGet alarm information
*alrmclr*alrmclrClear all alarms

Legacy Command Examples

Log Downloads
*logall              // Download all logs (disconnects after)
*logtemp100          // Download temp logs from record 100
*loghumi0            // Download all humidity logs
*logntemp50          // Download temp logs, stay connected
Configuration
*lint60              // Log every 60 seconds
*lint3600            // Log every hour
*namMyDisc           // Set device name to "MyDisc"
*txp0                // Set to +4 dBm (max range)
*pwd1234             // Set/enter password
Alarm Thresholds
// Format: *alrm<1|2><t|h|d><operator><value>
// Operators: > (above), < (below), = (equals)

*alrm1t>25           // Alarm 1: temp above 25°C
*alrm2t<10           // Alarm 2: temp below 10°C
*alrm1h>80           // Alarm 1: humidity above 80%

Legacy Data Format

Legacy devices store values with 0.1 precision (divide raw value by 10):

VersionSensorsBytes/RecordData Format
v13Temperature2int16_t temp
v23Temp + Humidity4int16_t temp, int16_t humidity
v27Temp + Hum + Dewpoint6int16_t temp, hum, dewpoint

5.4 Detailed Command Reference (v41, v42, v43)

Airplane Mode

Toggles airplane mode on/off. Designed for battery conservation and RF-restricted environments.

Command
airplane         // No value needed
Response: OK: Airplane mode toggled

Requirements: Device must be unlocked (no PIN set, or correct PIN previously entered)

Airplane Mode Behavior

When Enabling (OFF → ON):

  1. FLAG_AIRPLANE (bit 6) is set immediately
  2. Device continues advertising for 5 minutes (grace period)
  3. After 5 minutes, BLE advertising stops completely
  4. All existing connections are disconnected
  5. Setting persists across reboots

Recovery: Press device button → advertising enabled for 5 minutes → connect and send airplane command

PIN Security

PIN Commands
pin~1234    // Set PIN to 1234 (locks device)
pin~5678    // If locked with 1234, this fails
pin~1234    // If already locked with 1234, unlocks and clears PIN

Response:
- OK: New PIN set and device locked
- OK: Device unlocked successfully and PIN cleared
- ERR: Incorrect PIN

Logging Interval

Set Logging Interval
setlog~60        // Log every 60 seconds (1 minute)
setlog~3600      // Log every hour
Response: OK: Logging interval updated successfully

// Side effects: Resets log counters, clears buffer memory

Calibration

Write Calibration (version-specific)
// Version 41 (temperature only)
wcal~-50         // Temp offset -0.50°C (-0.90°F)

// Version 42 (temperature + humidity)
wcal~-50,25      // Temp -0.50°C, Humidity +0.25%

// Version 43 (temperature + humidity + pressure)
wcal~-50,25,100  // Temp -0.50°C, Humidity +0.25%, Pressure +1.00 hPa

// Values multiplied by 100 (e.g., -50 = -0.50°C)
Response: OK: Calibrations updated

Download Logs

Download Commands
// Modern devices (v41-43)
logall      // Download all logs
log~100     // Download last 100 logs

// Legacy devices (v13-27)
*logall     // Download all logs

// Response: Binary data stream, terminated with '..'
// Note: Only one download per connection

TX Power

Set Bluetooth TX Power
txpwr~4      // +4 dBm (maximum range ~246ft / 75m)
txpwr~0      // 0 dBm
txpwr~-4     // -4 dBm
txpwr~-8     // -8 dBm (minimum power)
Response: OK: TX Power updated successfully

5.3 Response Patterns

PatternMeaning
OKCommand succeeded
updatedCalibration write succeeded
User string updatedNotes write succeeded
..End of log download
~~Incomplete transmission (ignore)

6. Data Download Protocol

6.1 Download Initiation

JavaScript
// Modern devices (v41, v42, v43)
command = "logall";  // Full download
command = "log~100"; // Last 100 logs

// Legacy devices (v13, v23, v27)
command = "*logall"; // Full download

6.2 Byte-Level Data Formats

Version 41 Log Data (2 bytes per reading)

JavaScript Parsing
function parseV41Reading(data, offset) {
    const temp = (data[offset + 1] << 8) | data[offset];
    // Convert to signed int16
    const temperature = temp > 32767 ? temp - 65536 : temp;
    // temperature is in 0.01C (divide by 100 for display)
    return { temperature };
}

Version 42 Log Data (4 bytes per reading)

JavaScript Parsing
function parseV42Reading(data, offset) {
    const temp = toInt16(data[offset], data[offset + 1]);
    const hum = toInt16(data[offset + 2], data[offset + 3]);
    const dew = calculateDewpoint(temp / 100, hum / 100) * 100;
    return { temperature: temp, humidity: hum, dewpoint: Math.round(dew) };
}

function toInt16(low, high) {
    let value = (high << 8) | low;
    return value > 32767 ? value - 65536 : value;
}

Version 43 Log Data (8 bytes per reading)

JavaScript Parsing
function parseV43Reading(data, offset) {
    const temp = toInt16(data[offset], data[offset + 1]);
    const hum = toInt16(data[offset + 2], data[offset + 3]);
    const press = toInt32(data[offset + 4], data[offset + 5],
                          data[offset + 6], data[offset + 7]);
    const dew = calculateDewpoint(temp / 100, hum / 100) * 100;
    return {
        temperature: temp,
        humidity: hum,
        pressure: press,
        dewpoint: Math.round(dew)
    };
}

6.3 Timestamp Calculation

Logs do not contain timestamps. Calculate timestamps from device reference time:

JavaScript
function calculateTimestamps(readings, referenceUnix, intervalSeconds) {
    return readings.map((reading, index) => ({
        ...reading,
        unix: referenceUnix + (index * intervalSeconds * 1000)
    }));
}

7. Alert Service (v41, v42, v43)

The modern devices support BLE notifications for real-time sensor alerts.

7.1 Alert Service UUIDs

Service UUIDs
Service UUID:        6E400001-9489-44E4-9E39-60BA18464A30
Characteristic UUID: 6E400002-9489-44E4-9E39-60BA18464A30

7.2 Subscribing to Alerts

  1. Connect to the device
  2. Discover the Alert Service
  3. Write 0x0001 to the CCCD (Client Characteristic Configuration Descriptor)
  4. Receive ATT Write Response (success) confirming subscription
  5. Receive notifications every 60 seconds

Subscription Restrictions

Subscriptions are automatically rejected if:

  • Device is in airplane mode (FLAG_AIRPLANE set)
  • Device is locked (FLAG_LOCKED set)

7.3 Alert Data Format by Version

C Struct Definitions
// Version 41: 6 bytes
typedef struct __packed {
    int16_t temperature;    // 2 bytes (temp × 100)
    uint8_t mac_address[4]; // 4 bytes (last 4 of MAC)
} alert_data_v41_t;

// Version 42: 8 bytes
typedef struct __packed {
    int16_t temperature;    // 2 bytes
    int16_t humidity;       // 2 bytes
    uint8_t mac_address[4]; // 4 bytes
} alert_data_v42_t;

// Version 43: 12 bytes
typedef struct __packed {
    int16_t temperature;    // 2 bytes
    int16_t humidity;       // 2 bytes
    int32_t pressure;       // 4 bytes
    uint8_t mac_address[4]; // 4 bytes
} alert_data_v43_t;

8. Data Models

8.1 Reading Interfaces

TypeScript Interfaces
// Version 41 Reading
interface Device41Reading {
    index: number;       // Reading sequence number
    unix: number;        // Timestamp (milliseconds)
    temperature: number; // Raw value (/100 for C)
}

// Version 42 Reading
interface Device42Reading {
    index: number;
    unix: number;
    temperature: number; // Raw value (/100 for C)
    humidity: number;    // Raw value (/100 for %RH)
    dewpoint: number;    // Calculated (/100 for C)
}

// Version 43 Reading
interface Device43Reading {
    index: number;
    unix: number;
    temperature: number; // Raw value (/100 for C)
    humidity: number;    // Raw value (/100 for %RH)
    pressure: number;    // Raw value (Pa)
    dewpoint: number;    // Calculated (/100 for C)
}

8.2 Device Capabilities Matrix

Propertyv13v23v27v41v42v43
TemperatureYYYYYY
Humidity-YY-YY
Pressure--Y--Y
Dew Point-YY-YY
Device PIN---YYY
Alerts---YYY
LED Control---YYY
BLE Calibration---YYY

9. Unit Conversions

9.1 Temperature

JavaScript
// Celsius to Fahrenheit
function celsiusToFahrenheit(celsius) {
    return (celsius * 9/5) + 32;
}

// Fahrenheit to Celsius
function fahrenheitToCelsius(fahrenheit) {
    return (fahrenheit - 32) * 5/9;
}

9.2 Pressure

JavaScript
// Pascal to hPa (hectopascal/millibar)
function paToHpa(pa) {
    return pa / 100;
}

// hPa to inHg (inches of mercury)
function hpaToInhg(hpa) {
    return hpa * 0.02953;
}

9.3 Dew Point Calculation

Magnus-Tetens Approximation
function calculateDewpoint(temperatureCelsius, humidityPercent) {
    const a = 17.27;
    const b = 237.7;
    const alpha = ((a * temperatureCelsius) / (b + temperatureCelsius)) +
                  Math.log(humidityPercent / 100);
    return (b * alpha) / (a - alpha);
}

10. Developing Your Own App

This section provides guidance for developers building applications to communicate with Blue Maestro devices.

10.1 Basic Connection Flow

  1. Scan for BLE devices with manufacturer data starting with 0x33 0x01
  2. Connect to device with desired name/MAC
  3. Discover services (find Nordic UART Service)
  4. Subscribe to RX characteristic notifications
  5. Send commands via TX characteristic
  6. Receive responses via RX notifications
  7. Disconnect when done

10.2 Example Workflows

Workflow 1: Basic Info Retrieval

Steps
1. Connect to device
2. Subscribe to RX characteristic
3. Write "info" to TX characteristic
4. Read response from RX notification
5. Parse text response
6. Disconnect

Workflow 2: Download All Logs

Steps
1. Connect to device
2. Subscribe to RX characteristic
3. Write "log~all" to TX characteristic
4. Receive binary stream of sensor data
5. Parse based on device version (check advertisement data)
6. Convert: value / 100.0 for actual reading
7. Device disconnects when complete

Workflow 3: Configure Device

Steps
1. Connect to device
2. Check if locked (send "info", check response)
3. If locked, send "pin~<PIN>" to unlock
4. Send "setlog~300" (5 minute logging)
5. Send calibration command (version-specific)
6. Send "name~MyDevice"
7. Disconnect

10.3 Sample Code

Python Example (using bleak library)

Python
# Connect to device (using bleak library)
device = ble.connect("Device_MAC_ADDRESS")
service = device.get_service(NUS_SERVICE_UUID)
tx_char = service.get_characteristic(NUS_TX_UUID)
rx_char = service.get_characteristic(NUS_RX_UUID)

# Enable notifications
rx_char.enable_notifications(on_data_received)

# Send command
def send_command(command):
    tx_char.write(command.encode())

# Receive response
def on_data_received(data):
    print(f"Response: {data.decode()}")

# Example: Get device info
send_command("info")
time.sleep(1)  # Wait for response

# Disconnect
device.disconnect()

Binary Log Parsing (Python)

Python
import struct

# Parse log data - VERSION SPECIFIC
def parse_logs_v41(data):
    logs = []
    for i in range(0, len(data), 2):
        temp = struct.unpack('<h', data[i:i+2])[0]
        temp_c = temp / 100.0
        temp_f = temp_c * 9/5 + 32
        logs.append({'temperature_c': temp_c, 'temperature_f': temp_f})
    return logs

def parse_logs_v42(data):
    logs = []
    for i in range(0, len(data), 4):
        temp, humi = struct.unpack('<hh', data[i:i+4])
        logs.append({
            'temperature_c': temp / 100.0,
            'temperature_f': (temp / 100.0) * 9/5 + 32,
            'humidity': humi / 100.0
        })
    return logs

def parse_logs_v43(data):
    logs = []
    for i in range(0, len(data), 8):
        temp, humi, press = struct.unpack('<hhi', data[i:i+8])
        logs.append({
            'temperature_c': temp / 100.0,
            'temperature_f': (temp / 100.0) * 9/5 + 32,
            'humidity': humi / 100.0,
            'pressure_hpa': press / 100.0,
            'pressure_inhg': (press / 100.0) * 0.02953
        })
    return logs

Platform-Specific Libraries

PlatformRecommended Library
iOSCoreBluetooth framework
AndroidAndroid BLE API (BluetoothGatt)
Pythonbleak library
JavaScript/WebWeb Bluetooth API
React Nativereact-native-ble-plx
Flutterflutter_blue_plus

Testing Tips

  • Use nRF Connect mobile app to test commands manually
  • Monitor responses to understand timing
  • Always wait for response before sending next command
  • Device auto-disconnects after most commands
  • Test with device unlocked first (no PIN set)
  • Check device version in advertisement data before parsing binary responses

11. Default Settings

11.1 Factory Default Values (v41, v42, v43)

SettingDefault ValueUnitNotes
Temperature Calibration01/100 °CNo offset
Humidity Calibration (v42/43)01/100 %RHNo offset
Pressure Calibration (v43)01/100 hPaNo offset
Logging Interval900seconds15 minutes
Advertising Update30seconds
High Temp Threshold30°C (86°F)
Low Temp Threshold10°C (50°F)
High Humidity Threshold (v42/43)75%RH
Low Humidity Threshold (v42/43)25%RH
LEDOnEnabled
ButtonOnEnabled
Airplane ModeOffNormal operation
TX Power+4dBmMaximum range (~75m / 246ft)

11.2 Firmware Information

PropertyVersion 41Version 42Version 43
Firmware Version2.0.12.0.12.0.1
PlatformnRF Connect SDK / Zephyr RTOS
StorageNVS (Non-Volatile Storage)
Sensor ICSTS4XSHT4XBME280
Max Logs100,00050,00025,000
Record Size2 bytes4 bytes8 bytes

12. Appendices

12.1 Error Handling

Error ConditionRecommended Action
Device lockedPrompt user to unlock device
Connection timeoutRetry connection (max 3 attempts)
Command timeout (45s)Display timeout error, allow retry
Invalid data rangeDiscard reading, log error
Incomplete downloadRe-request from last valid position

12.2 Best Practices

  • Scanning: Limit scan duration to 10-30 seconds
  • Connection: Disconnect when not actively communicating
  • Downloads: Use incremental downloads for large datasets
  • Calibration: Store calibration locally for v13/v23/v27
  • Timeouts: Allow 45 seconds for commands, 15 seconds for quick operations
  • Chunking: Split commands >20 bytes with 100ms delays

12.3 Glossary

TermDefinition
BEBig-Endian byte order
LELittle-Endian byte order
NUSNordic UART Service
RXReceive (from device perspective - app writes here)
TXTransmit (from device perspective - app reads here)
hPaHectopascal (pressure unit, same as mbar)
%RHPercent relative humidity

Need Help?

For technical support or API questions, contact our development team.

Contact Support