|
// ----------------------------------------------------------------------------
// Program: BLE Key Fob Data Receiver Demo (LX)
//
// Description:
// This educational program demonstrates how to read BLE packets
// sent from the BLE Key Fob.
//
// Key Concepts Demonstrated:
// BLE communication:
// - Initializing the BLE interface.
// - Listening BLE packages.
// - Receiving and Interpreting the data
//
// Hardware Setup:
// - RTCU LX-family Device with BLE interface.
// - RTCU BLE Key Fob.
// ----------------------------------------------------------------------------
INCLUDE rtcu.inc
#DEFINE KeyFobMAC "p2C:6A:6F:90:AE:10" // MAC address of the key fob to listen
// Data Object IDs
// The object IDs are used by the BLE key fob
#DEFINE PACKET_ID 16#00
#DEFINE BATTERY_ID 16#01
#DEFINE VOLTAGE_ID 16#0C
#DEFINE BUTTON_ID 16#3A
#DEFINE TEMPERATURE_ID 16#57
// ===========================================================================
// GLOBAL DATA STORAGE
// ===========================================================================
VAR
// Global buffer to hold the raw packet for inspection
ble_raw_data : ARRAY[1.. 100] OF USINT;
temp_packetID : USINT; // temporary sequence number buffer
END_VAR;
// ===========================================================================
// BLE KEY FOB PACKET PARSER (CALLBACK FUNCTION)
// ===========================================================================
FUNCTION CALLBACK cbBLEKeyFob;
VAR_INPUT
mac : STRING; // MAC address of the emitting sensor
ev_type : UINT; // Type of event triggering this call
adv_type : USINT; // Advertisement type (Expecting 0x16 for Service Data)
data : PTR; // Memory pointer to the raw sensor payload
len : INT; // Length of the payload
rssi : SINT; // Signal strength in dBm
arg : DINT; // Custom user argument
END_VAR;
VAR
// Local variables for extracted values
v_packetID : USINT;
v_btn : ARRAY[1..4] OF SINT;
v_batt_pct : SINT;
v_batt_vol : INT;
v_temp : SINT;
pos : USINT;
btn : USINT := 1;
exp_len : SINT := 20; // expected data length
END_VAR;
// STEP 1: Filter by Packet Length and Type
// We only care about Service Data (0x16) and your specific 20-byte length.
IF adv_type = 16#16 AND len = exp_len THEN
// STEP 2: Copy raw pointer data to a readable array
memcpy(dst := ADDR(ble_raw_data), src := data, len := exp_len);
// STEP 3: Verify UUID (Bytes 1 & 2 must be D2 FC)
IF ble_raw_data[1] = 16#D2 AND ble_raw_data[2] = 16#FC THEN
// STEP 4: Decode the data
pos := 4; // Decode after the device information
WHILE pos < len DO
IF pos >= len THEN
EXIT;
END_IF;
CASE ble_raw_data[pos] OF
PACKET_ID:
IF pos + 2 <= len THEN
v_packetID := ble_raw_data[pos+1];
pos := pos + 2; // packet ID is 1 byte
END_IF;
BUTTON_ID:
IF pos + 1 <= len THEN
v_btn[btn] := ble_raw_data[pos+1];
btn := btn + 1;
pos := pos + 2; // button event is 1 byte
END_IF;
BATTERY_ID:
IF pos + 1 <= len THEN
v_batt_pct := ble_raw_data[pos+1];
pos := pos + 2; // battery level is 1 byte
END_IF;
VOLTAGE_ID:
IF pos + 2 <= len THEN
v_batt_vol := shl16(in := INT(ble_raw_data[pos+2]), n := 8) OR (ble_raw_data[pos+1] AND 16#FF);
pos := pos + 3; // battery voltage is 2 bytes
END_IF;
TEMPERATURE_ID:
IF pos + 1 <= len THEN
v_temp := ble_raw_data[pos+1];
pos := pos + 2; // temperature is 1 bytes
END_IF;
ELSE
EXIT; // Unknown object ID, data is not from BLE key fob
END_CASE;
END_WHILE;
btn := 1; // reset the button count
// STEP 5: Debug reporting
// Show only once per button press
IF temp_packetID <> v_packetID THEN
temp_packetID := v_packetID;
IF ((v_btn[1] = 0) AND (v_btn[2] = 0) AND (v_btn[3] = 0) AND (v_btn[4] = 0)) THEN
DebugMsg(message := "--- Status Packet Received ---");
DebugFmt(message := "Sensor MAC : " + mac + ", RSSI: \1, Advertisement Type: \2", v1:=INT(rssi), v2:=INT(adv_type));
DebugFmt(message := "Packet ID : \1", v1 := v_packetID);
DebugFmt(message := "Battery : \1% (\2 mV)", v1 := INT(v_batt_pct), v2 := v_batt_vol);
DebugFmt(message := "Temperature: \1 C", v1 := INT(v_temp));
ELSE
DebugMsg(message := "--- Button Event Packet Received ---");
DebugFmt(message := "Sensor MAC : " + mac + ", RSSI: \1, Advertisement Type: \2", v1:=INT(rssi), v2:=INT(adv_type));
DebugFmt(message := "Packet ID : \1", v1 := v_packetID);
DebugFmt(message := "Buttons : B1=\1, B2=\2, B3=\3, B4=\4",
v1 := INT(v_btn[1]), v2 := INT(v_btn[2]), v3 := INT(v_btn[3]), v4 := DINT(v_btn[4]));
IF ((v_btn[1] = 4) OR (v_btn[2] = 4) OR (v_btn[3] = 4) OR (v_btn[4] = 4)) THEN
DebugFmt(message := "Long-press : YES");
ELSE
DebugFmt(message := "Long-press : NO");
END_IF;
DebugFmt(message := "Battery : \1% (\2 mV)", v1 := INT(v_batt_pct), v2 := v_batt_vol);
DebugFmt(message := "Temperature: \1 C", v1 := INT(v_temp));
END_IF;
END_IF;
END_IF;
END_IF;
END_FUNCTION;
// ===========================================================================
// MAIN PROGRAM BLOCK
// ===========================================================================
PROGRAM BLE_KEY_FOB;
// PHASE 1: INITIALIZATION (Executes Once)
DebugMsg(message := "Initializing BLE Platform...");
// 1. Turn on the BLE chip power
IF blePower(power := TRUE) = 1 THEN
DebugMsg(message := "Success: BLE Powered ON.");
ELSE
DebugMsg(message := "Error: Failed to power BLE.");
END_IF;
// 2. Register the Callback for Service Data (0x16)
// This tells the system: "If you see a 0x16 packet, run cbBLEKeyFob"
bleRegisterAdvData(adv_type := 16#16, cb_adv := @cbBLEKeyFob);
DebugMsg(message := "Registration: Listening for Service Data (0x16).");
// 3. Filter the other BLE devices
// Remove all MAC addresses from accept filter
bleAcceptFilterClear();
// Listen only on the BLE key fob
bleAcceptFilterAdd(mac:=KeyFobMAC);
// 4. Start the Bluetooth Observer (Scanning)
// scan_interval/window at 10s = Continuous scanning
bleObserverStart(
scan_interval := bleTimeFromMs(v:=10000),
scan_window := bleTimeFromMs(v:=10000),
filter_policy := 1
);
DebugMsg(message := "Scanning: Started.");
BEGIN
// PHASE 2: CYCLIC SCAN (Executes Infinitely)
// Logic is handled by the callback
END;
END_PROGRAM;
|