AnyScan209 Live Scan – Bluetooth Protocol for External Scanners (ANY_SCAN_MS209)

Build your own single-scan or four-sensor scanner for AnyScan209 with Arduino, BASCOM, C or MicroPython. This page describes the exact data packet, command words and battery display used in Live Scan (professional scan) mode. The protocol is open and free to use under the MIT License.

1. What Live Scan Mode Does

In Live Scan mode a microcontroller with a Bluetooth serial module (HC-05, HC-06 or any SPP device) streams sensor readings to AnyScan209. The app can receive one sensor or four sensors at the same time, so a four-sensor scanner fills four rows at once and scans four times faster. The scanner also reports its battery level, which the app shows as a progress bar with a low-battery alarm.

Without the special name described below, the app stays in simple external mode: every incoming input moves the scan forward by one step. Both modes work, so scanners you already built keep working.

2. Enable Live Scan: Set the Bluetooth Name

Rename your Bluetooth module to exactly:

ANY_SCAN_MS209

Bluetooth module nameApp behavior
No name, or any other nameSimple mode: each input advances the scan one step
ANY_SCAN_MS209Live Scan options unlock: single scan, four scan, battery display, Next and Bal commands

Copy the name exactly, including the underscores and digits. Most modules are renamed with an AT command, for example AT+NAME=ANY_SCAN_MS209 on an HC-05 (the command differs between modules, so check your module's datasheet). After renaming, pair the phone with the module again so the new name appears.

3. Single Scan or Four-Sensor Scan

After the name is set, choose the scan type in the app options to match your hardware:

The option in the app must match what the scanner sends (see the next section). Scanning starts from the scan button in the app, or when the scanner sends the word Next.

4. Data Packet Format

Every scan packet is a numeric string of exactly 20 characters: five fields of four digits, each from 0000 to 1024 and always padded with leading zeros. Each packet ends with a normal line break (CR+LF), which Print in BASCOM and println in Arduino add automatically.

FieldDigitsMeaning
11 – 4Sensor 1
25 – 8Sensor 2
39 – 12Sensor 3
413 – 16Sensor 4
517 – 20Battery
Mandatory rule: always send all five fields, even the unused ones. The app accepts a scan packet only when its length is exactly 20 characters. Send 512 and the packet is rejected; send 0512.

Single-scan packet

Field 1 holds the real value, fields 2–4 are sent as 0000, field 5 is the battery.

0512 0000 0000 0000 0980   →   send as   05120000000000000980

Four-sensor packet

All four sensor fields hold real values.

0512 0300 0700 0090 0980   →   send as   05120300070000900980

Keep every value between 0 and 1024 (a 10-bit ADC range). The app clamps sensor values to a small safety margin beyond that range, but a well-behaved sender stays inside 0–1024.

5. Command Words: Next and Bal

Two optional words let the user control the app from the scanner. Send the word alone on its own line.

WordMeaningWhat the app does
NextGo to the next scanSame as pressing the scan button in the app
BalBalance requestShows the Balance Device message and starts the balance process

Both words are optional. They are meant for two hardware buttons on the scanner, so the user does not have to touch the phone each time. If you skip the buttons nothing breaks: the app already has an on-screen scan button and a balance button. Adding the two hardware buttons is recommended for comfort.

6. Battery Display and Alarm

The app knows nothing about voltages. Field 5 is just a number, and your scanner is responsible for mapping its real battery voltage to that number. The app turns the number into a percentage with a linear formula, averages 50 packets before updating the bar (so the icon does not flicker), and colors the bar from dark red (low) to yellow-green (full).

Scanner typeNumber for empty battery (0%)Number for full battery (100%)
Magnetometer600860
Metal detector8201024

Percentage formulas used by the app: magnetometer (value − 600) / 2.6, metal detector (value − 820) / 2.04; at or below the empty point the bar shows 3%.

Low-battery alarm: if the raw battery number is below 630, the app shows a LOW BATTERY message and plays a warning sound. For a magnetometer this is roughly 11% charge left. Because the metal detector range starts at 820, its alarm only triggers when the sender reports a value far below its normal range.

Mapping your battery to the number

Choose the empty and full voltages of your battery, then map linearly:

number = emptyNumber + (voltage − emptyVoltage) × (fullNumber − emptyNumber) / (fullVoltage − emptyVoltage)

Example, single lithium cell (3.3 V empty, 4.2 V full) on a magnetometer: number = 600 + (voltage − 3.3) × 260 / 0.9. The same formula works for any battery: 3.3–4.2 V, 9.6–12.4 V, 14–18 V and so on. Only the empty and full voltages change.

7. Voltage Divider

A microcontroller ADC can only read voltages up to its reference voltage, so a higher battery voltage must be reduced by a resistor voltage divider before it reaches the ADC pin. Designing the divider (resistor values, accuracy, protecting the ADC pin) is entirely the builder's responsibility.

The relationship between divided voltage and ADC value is nearly linear. If your divider cannot hit the ranges above exactly, correct the value in your microcontroller code with the mapping formula. Any AI assistant can calculate the resistors and the code for you (see section 9).

8. Sender Code: BASCOM, Arduino, Python

BASCOM (AVR)

This follows the reference scanner: one sensor on ADC0, battery on ADC1 with a fixed offset of 305 so the number lands in the range the app expects. Calibrate the offset for your own hardware.

Dim Volt1 As Word
Dim Sampl1 As Word
Dim Magno11 As String * 4
Dim Volt11 As String * 4
Dim Volt12 As String * 4

Sampl1 = Getadc(0)
Volt1 = Getadc(1) + 305            ' calibrate: empty battery = 600, full = 860

Magno11 = Str(sampl1) : Magno11 = Format(magno11 , "0000")
Volt11 = Str(volt1)   : Volt12 = Format(volt11 , "0000")

' Single scan: the three unused fields are mandatory
Print #1 , Magno11 ; "0000" ; "0000" ; "0000" ; Volt12

' Balance button
Print #1 , "Bal"

Arduino (four-sensor example)

const float V_EMPTY = 3.3, V_FULL = 4.2;   // your battery
const int   N_EMPTY = 600, N_FULL = 860;   // magnetometer (metal detector: 820 to 1024)
const float ADC_REF = 5.0;                 // ADC reference voltage
const float DIVIDER = 1.0;                 // (R1+R2)/R2 of your voltage divider

int batteryNumber() {
  float v = analogRead(A4) * ADC_REF / 1023.0 * DIVIDER;
  float n = N_EMPTY + (v - V_EMPTY) * (N_FULL - N_EMPTY) / (V_FULL - V_EMPTY);
  return constrain((int)n, 0, 1024);
}

void sendScan(int s1, int s2, int s3, int s4) {
  char buf[21];
  snprintf(buf, sizeof(buf), "%04d%04d%04d%04d%04d",
           constrain(s1, 0, 1024), constrain(s2, 0, 1024),
           constrain(s3, 0, 1024), constrain(s4, 0, 1024),
           batteryNumber());
  Serial.println(buf);          // or bluetooth.println(buf)
}

void loop() {
  // Four-sensor mode
  sendScan(analogRead(A0), analogRead(A1), analogRead(A2), analogRead(A3));
  // Single-scan mode: sendScan(analogRead(A0), 0, 0, 0);

  // Optional buttons
  // if (digitalRead(NEXT_PIN) == LOW) Serial.println("Next");
  // if (digitalRead(BAL_PIN)  == LOW) Serial.println("Bal");
}

Python (MicroPython or desktop test)

def make_packet(s1, s2=0, s3=0, s4=0, batt=0):
    vals = [max(0, min(1024, v)) for v in (s1, s2, s3, s4, batt)]
    return "".join(f"{v:04d}" for v in vals)

print(make_packet(512, 300, 700, 90, 980))  # 05120300070000900980
print(make_packet(512, batt=980))           # 05120000000000000980

Test with a serial terminal first: every line must be exactly 20 digits.

9. Let an AI Write Your Sender Code

You do not need to be an embedded expert. Copy this page into an AI assistant and ask for the code for your board. A good request looks like this:

"Using the AnyScan209 Live Scan protocol on this page, write sender firmware for an ESP32 with an HC-05 module, four sensors, a 3.3–4.2 V lithium battery and a Next button. Also calculate the voltage divider resistors."

The assistant should ask for your microcontroller, language, Bluetooth baud rate, scan type (single or four), device type (magnetometer or metal detector), battery empty and full voltages, and the ADC reference voltage. Then it designs the divider, writes the battery mapping, builds the 20-character packet and reminds you to rename the Bluetooth module to ANY_SCAN_MS209.

10. FAQ

How do I turn on Live Scan mode in AnyScan209?

Rename your Bluetooth module to exactly ANY_SCAN_MS209, pair it again, then choose single scan or four-sensor scan in the app options.

Why is my packet ignored?

The packet must be exactly 20 characters: five fields of four digits with leading zeros. Unused sensor fields must be sent as 0000.

What numbers should my battery send?

Magnetometer scanners send 600 for an empty battery and 860 for a full one. Metal detector scanners send 820 for empty and 1024 for full. Map your real voltage to this range linearly.

Do I need the Next and Bal buttons?

No. They are optional hardware conveniences. The app has its own scan and balance buttons.

Can I use any battery voltage?

Yes. The app only sees a number, so any battery works as long as you map its empty and full voltages to the expected range and scale the voltage down for the ADC with a divider.

← Back to the main AnyScan209 guide