BLE RX/TX Changes (#2752)

* Work on BLE Rx Tx improvements.
* Working on compile size.
* cleanup
* Formatting
* Fixes
* More Improvements + Custom Parsing for Tags
* Moving ERT to external apps.
* Fix Icon.
This commit is contained in:
Netro 2025-08-11 01:42:58 -04:00 committed by GitHub
parent 3983749f11
commit 6b05878532
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 1167 additions and 873 deletions

View file

@ -247,7 +247,7 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
};
button_send.on_select = [this, &nav](const ui::Button&) {
auto packetToSend = build_packet();
auto packetToSend = build_packet(entry_);
nav.set_on_pop([packetToSend, &nav]() {
nav.replace<BLETxView>(packetToSend);
});
@ -255,7 +255,7 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
};
button_save.on_select = [this, &nav](const ui::Button&) {
auto packetToSave = build_packet();
auto packetToSave = build_packet(entry_);
packetFileBuffer = "";
text_prompt(
@ -436,7 +436,7 @@ void BleRecentEntryDetailView::set_entry(const BleRecentEntry& entry) {
set_dirty();
}
BLETxPacket BleRecentEntryDetailView::build_packet() {
BLETxPacket BleRecentEntryDetailView::build_packet(BleRecentEntry entry_) {
BLETxPacket bleTxPacket;
memset(&bleTxPacket, 0, sizeof(BLETxPacket));
@ -444,8 +444,8 @@ BLETxPacket BleRecentEntryDetailView::build_packet() {
strncpy(bleTxPacket.macAddress, macAddressStr.c_str(), 12);
strncpy(bleTxPacket.advertisementData, entry_.dataString.c_str(), entry_.packetData.dataLen * 2);
strncpy(bleTxPacket.packetCount, "50", 3);
bleTxPacket.packet_count = 50;
strncpy(bleTxPacket.packetCount, "10", 3);
bleTxPacket.packet_count = 10;
return bleTxPacket;
}
@ -495,16 +495,18 @@ BLERxView::BLERxView(NavigationView& nav)
&field_vga,
&options_channel,
&field_frequency,
&check_log,
&button_find,
&check_name,
&label_sort,
&options_sort,
&label_found,
&text_found_count,
&check_serial_log,
&button_filter,
&options_filter,
&check_name,
&check_log,
&check_serial_log,
&check_unique,
&check_duplicate_packets,
&button_find,
&label_found,
&text_found_count,
&button_save_list,
&button_clear_list,
&button_switch,
@ -516,6 +518,29 @@ BLERxView::BLERxView(NavigationView& nav)
nav_.push<BleRecentEntryDetailView>(entry);
};
ensure_directory(find_packet_path);
ensure_directory(log_packets_path);
ensure_directory(packet_save_path);
// ------------------------------------------------------------------------------
// Handle Check Boxes
// ------------------------------------------------------------------------------
logger = std::make_unique<BLELogger>();
check_name.on_select = [this](Checkbox&, bool v) {
name_enable = v;
// update the include_name instance variable value of each entry in recent entries
setAllMembersToValue(recent, &BleRecentEntry::include_name, v);
recent_entries_view.set_dirty();
};
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
if (logger && logging)
logger->append(bletx_dir.string() + "/Packets/BLETx_" + to_string_timestamp(rtc_time::now()) + ".TXT");
};
check_serial_log.on_select = [this](Checkbox&, bool v) {
serial_logging = v;
if (v) {
@ -524,12 +549,28 @@ BLERxView::BLERxView(NavigationView& nav)
portapack::async_tx_enabled = false;
}
};
check_unique.on_select = [this](Checkbox&, bool v) {
uniqueParsing = v;
recent.clear();
recent_entries_view.set_dirty();
};
check_duplicate_packets.on_select = [this](Checkbox&, bool v) {
duplicatePackets = v;
recent.clear();
recent_entries_view.set_dirty();
};
check_name.set_value(name_enable);
check_log.set_value(logging);
check_serial_log.set_value(serial_logging);
check_unique.set_value(uniqueParsing);
check_duplicate_packets.set_value(duplicatePackets);
ensure_directory(find_packet_path);
ensure_directory(log_packets_path);
ensure_directory(packet_save_path);
// ------------------------------------------------------------------------------
// Handle Buttons
// ------------------------------------------------------------------------------
filterBuffer = filter;
button_filter.on_select = [this](Button&) {
@ -543,16 +584,6 @@ BLERxView::BLERxView(NavigationView& nav)
});
};
logger = std::make_unique<BLELogger>();
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
if (logger && logging)
logger->append(blerx_dir.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
};
check_log.set_value(logging);
button_save_list.on_select = [this, &nav](const ui::Button&) {
listFileBuffer = "";
text_prompt(
@ -574,17 +605,18 @@ BLERxView::BLERxView(NavigationView& nav)
nav.replace<BLETxView>();
};
field_frequency.set_step(0);
check_name.set_value(name_enable);
check_name.on_select = [this](Checkbox&, bool v) {
name_enable = v;
// update the include_name instance variable value of each entry in recent entries
setAllMembersToValue(recent, &BleRecentEntry::include_name, v);
recent_entries_view.set_dirty();
button_find.on_select = [this](Button&) {
auto open_view = nav_.push<FileLoadView>(".TXT");
open_view->on_changed = [this](std::filesystem::path new_file_path) {
on_file_changed(new_file_path);
};
};
// ------------------------------------------------------------------------------
// Handle Options
// ------------------------------------------------------------------------------
field_frequency.set_step(0);
options_channel.on_change = [this](size_t index, int32_t v) {
channel_index = (uint8_t)index;
@ -611,7 +643,6 @@ BLERxView::BLERxView(NavigationView& nav)
filter_index = (uint8_t)index;
recent.clear();
handle_filter_options(v);
uniqueParsing = filter_index == 2 ? true : false;
recent_entries_view.set_dirty();
};
@ -619,15 +650,6 @@ BLERxView::BLERxView(NavigationView& nav)
options_sort.set_selected_index(sort_index, true);
options_filter.set_selected_index(filter_index, true);
button_find.on_select = [this](Button&) {
auto open_view = nav_.push<FileLoadView>(".TXT");
open_view->on_changed = [this](std::filesystem::path new_file_path) {
on_file_changed(new_file_path);
// nav_.set_on_pop([this]() { button_play.focus(); });
};
};
// Auto-configure modem for LCR RX (will be removed later)
baseband::set_btlerx(channel_number);
@ -785,12 +807,17 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
}
void BLERxView::on_data(BlePacketData* packet) {
uint64_t macAddressEncoded = copy_mac_address_to_uint64(packet->macAddress);
uint64_t uniqueKeyEncoded = copy_mac_address_to_uint64(packet->macAddress);
// Start of Packet stuffing.
// Masking off the top 2 bytes to avoid invalid keys.
uint64_t key = macAddressEncoded & 0xFFFFFFFFFFFF;
uint64_t key = (uniqueKeyEncoded & 0xFFFFFFFFFFFF);
if (duplicatePackets) {
key |= ((uint64_t)packet->type) << 48;
}
bool packetExists = false;
// If found store into tempEntry to modify.
@ -866,10 +893,6 @@ void BLERxView::log_ble_packet(BlePacketData* packet) {
void BLERxView::on_filter_change(std::string value) {
// New filter? Reset list from recent entries.
if (filter != value) {
// resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
// // return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
// return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos) && (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos);
// });
filter = value;
handle_filter_options(options_filter.selected_index());
}
@ -947,7 +970,7 @@ void BLERxView::handle_entries_sort(uint8_t index) {
switch (index) {
case 0:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
recent, [](const BleRecentEntry& entry) { return entry.uniqueKey & 0xFFFFFFFFFFFF; }, true);
break;
case 1:
sortEntriesBy(
@ -965,6 +988,10 @@ void BLERxView::handle_entries_sort(uint8_t index) {
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.nameString; }, true);
break;
case 5:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.informationString; }, true);
break;
default:
break;
}
@ -975,16 +1002,43 @@ void BLERxView::handle_entries_sort(uint8_t index) {
void BLERxView::handle_filter_options(uint8_t index) {
auto value = filter;
switch (index) {
case 0: // filter by Data
// Data
case 0:
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
break;
case 1: // filter by MAC address (All caps: e.g. AA:BB:CC:DD:EE:FF)
// MAC address (All caps: e.g. AA:BB:CC:DD:EE:FF)
case 1:
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos);
});
break;
// Name
case 2:
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.nameString.find(value) == std::string::npos);
});
break;
// Info
case 3:
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.informationString.find(value) == std::string::npos);
});
break;
// Vendor
case 4:
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
std::string vendor_name = lookup_mac_vendor(entry.packetData.macAddress);
return (vendor_name.find(value) == std::string::npos);
});
break;
// Channel
case 5:
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (to_string_dec_int(entry.channelNumber).find(value) == std::string::npos);
});
break;
default:
break;
}
@ -1007,62 +1061,178 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
bool success = false;
int i;
for (i = 0; i < packet->dataLen; i++) {
data_string += to_string_hex(packet->data[i], 2);
}
entry.dbValue = packet->max_dB - (receiver_model.lna() + receiver_model.vga() + (receiver_model.rf_amp() ? 14 : 0));
entry.timestamp = to_string_timestamp(rtc_time::now());
entry.dataString = data_string;
entry.packetData.type = packet->type;
entry.packetData.size = packet->size;
entry.packetData.dataLen = packet->dataLen;
// Mac Address of sender.
entry.packetData.macAddress[0] = packet->macAddress[0];
entry.packetData.macAddress[1] = packet->macAddress[1];
entry.packetData.macAddress[2] = packet->macAddress[2];
entry.packetData.macAddress[3] = packet->macAddress[3];
entry.packetData.macAddress[4] = packet->macAddress[4];
entry.packetData.macAddress[5] = packet->macAddress[5];
entry.pduType = pdu_type;
entry.channelNumber = channel_number;
entry.numHits++;
if (entry.vendor_status == MAC_VENDOR_UNKNOWN) {
std::string vendor_name;
entry.vendor_status = lookup_mac_vendor_status(entry.packetData.macAddress, vendor_name);
}
// Parse Data Section into buffer to be interpretted later.
for (int i = 0; i < packet->dataLen; i++) {
entry.packetData.data[i] = packet->data[i];
}
entry.include_name = check_name.value();
// Only parse name for advertisment packets and empty name entries
if (pdu_type == ADV_IND || pdu_type == ADV_NONCONN_IND || pdu_type == SCAN_RSP || pdu_type == ADV_SCAN_IND) {
if (uniqueParsing) {
// Add your unique beacon parsing function here.
success = parse_tracking_beacon_data(packet->data, packet->dataLen, entry.nameString, entry.informationString);
}
if (!success && !uniqueParsing) {
success = parse_beacon_data(packet->data, packet->dataLen, entry.nameString, entry.informationString);
}
} else if (pdu_type == ADV_DIRECT_IND || pdu_type == SCAN_REQ) {
ADV_PDU_PAYLOAD_TYPE_1_3* directed_mac_data = (ADV_PDU_PAYLOAD_TYPE_1_3*)entry.packetData.data;
reverse_byte_array(directed_mac_data->A1, 6);
if (!uniqueParsing) {
ADV_PDU_PAYLOAD_TYPE_1_3* directed_mac_data = (ADV_PDU_PAYLOAD_TYPE_1_3*)packet->data;
reverse_byte_array(directed_mac_data->A1, 6);
success = true;
}
}
if (success) {
int i;
for (i = 0; i < packet->dataLen; i++) {
data_string += to_string_hex(packet->data[i], 2);
}
entry.dbValue = packet->max_dB - (receiver_model.lna() + receiver_model.vga() + (receiver_model.rf_amp() ? 14 : 0));
entry.timestamp = to_string_timestamp(rtc_time::now());
entry.dataString = data_string;
entry.packetData.type = packet->type;
entry.packetData.size = packet->size;
entry.packetData.dataLen = packet->dataLen;
// Mac Address of sender.
entry.packetData.macAddress[0] = packet->macAddress[0];
entry.packetData.macAddress[1] = packet->macAddress[1];
entry.packetData.macAddress[2] = packet->macAddress[2];
entry.packetData.macAddress[3] = packet->macAddress[3];
entry.packetData.macAddress[4] = packet->macAddress[4];
entry.packetData.macAddress[5] = packet->macAddress[5];
entry.pduType = pdu_type;
entry.channelNumber = channel_number;
entry.numHits++;
if (entry.vendor_status == MAC_VENDOR_UNKNOWN) {
std::string vendor_name;
entry.vendor_status = lookup_mac_vendor_status(entry.packetData.macAddress, vendor_name);
}
// Parse Data Section into buffer to be interpretted later.
for (int i = 0; i < packet->dataLen; i++) {
entry.packetData.data[i] = packet->data[i];
}
entry.include_name = check_name.value();
}
return success;
}
bool BLERxView::parse_tracking_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString) {
uint8_t currentByte, currentLength, currentType = 0;
for (currentByte = 0; currentByte < length;) {
currentLength = data[currentByte++];
currentType = data[currentByte++];
// Manufacturer Specific Data (0xFF)
if (currentType == 0xFF && currentLength >= 4) {
uint16_t companyID = data[currentByte] | (data[currentByte + 1] << 8);
// Apple AirTag / Find My
if (companyID == 0x004C && currentLength >= 6) {
uint8_t appleType = data[currentByte + 2];
uint8_t appleLen = data[currentByte + 3];
if (appleType == 0x12 && appleLen == 0x19 && currentLength >= 4 + appleLen) {
nameString.assign("Apple AirTag");
informationString.assign("Find My");
return true;
} else if (appleType == 0x02 && appleLen == 0x15) {
uint16_t major = (data[currentByte + 20] << 8) | data[currentByte + 21];
uint16_t minor = (data[currentByte + 22] << 8) | data[currentByte + 23];
nameString.assign("iBeacon");
informationString.assign(to_string_hex(major) + to_string_hex(minor));
return true;
}
}
}
// Services
else if ((currentType == 0x02 || currentType == 0x03) && currentLength >= 3) {
for (int u = 0; u < currentLength - 1; u += 2) {
uint16_t uuid16 = data[currentByte + u] | (data[currentByte + u + 1] << 8);
if (uuid16 == 0x1802) { // Immediate Alert Service = Find Me Profile
nameString.assign("FindMe");
informationString.assign("IAS");
return true;
}
}
}
// Service Data
else if (currentType == 0x16 && currentLength >= 3) {
uint16_t uuid16 = data[currentByte] | (data[currentByte + 1] << 8);
switch (uuid16) {
case 0xFD59: { // Samsung SmartTag - Unregistered
nameString.assign("Samsung SmartTag");
informationString.assign("Unreg");
return true;
}
case 0xFD5A: { // Samsung SmartTag - Registered
nameString.assign("Samsung SmartTag");
informationString.assign("Reg");
return true;
}
case 0xFD84: {
if (currentByte + 2 < length) {
uint8_t model = data[currentByte + 2];
switch (model) {
case 0x01:
informationString.assign("Mate");
break;
case 0x02:
informationString.assign("Pro");
break;
case 0x03:
informationString.assign("Slim");
break;
case 0x04:
informationString.assign("Sticker");
break;
default:
informationString.assign("Unknown");
break;
}
}
nameString.assign("Tile");
return true;
}
case 0xFEAA: {
if (currentByte + 2 < length) {
uint8_t frameType = data[currentByte + 2];
switch (frameType) {
case 0x00:
informationString.assign("UID");
break;
case 0x10:
informationString.assign("URL");
break;
case 0x20:
informationString.assign("TLM");
break;
case 0x30:
informationString.assign("EID");
break;
default:
informationString.assign("Unknown");
break;
}
}
nameString.assign("Eddystone");
return true;
}
}
}
currentByte += (currentLength - 1);
}
return false;
}
bool BLERxView::parse_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString) {
uint8_t currentByte, currentLength, currentType = 0;
std::string tempName = "";