Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for bambu tags #8

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d90b989
Working KDF
spuder Nov 27, 2024
ca846f6
add pn532 component
spuder Nov 27, 2024
69a860d
Add working reading of bambu tag
spuder Nov 29, 2024
1bcc5a5
Read Mifare tags
spuder Nov 30, 2024
6ecdc63
add components
spuder Nov 30, 2024
6418478
Auth not quite workign
spuder Nov 30, 2024
6faeab6
Add test file
spuder Nov 30, 2024
08a35ff
Merge branch 'main' into bambu-rfid
spuder Nov 30, 2024
941fe73
Revert
spuder Nov 30, 2024
09b9440
not yet working
spuder Dec 1, 2024
0e3a07b
Not yet working
spuder Dec 1, 2024
5e1484c
Merge branch 'main' into bambu-rfid
spuder Dec 4, 2024
af535c3
Add host option
spuder Dec 4, 2024
f82e95e
Add comments
spuder Dec 4, 2024
f36fe60
Fix logging output
spuder Dec 4, 2024
9316837
Change parameter order
spuder Dec 4, 2024
a411b42
Fix kdf
spuder Dec 4, 2024
907997c
Add images
spuder Dec 5, 2024
b22afe9
Add todos
spuder Dec 5, 2024
4c2df0e
Merge branch 'main' into bambu-rfid
spuder Dec 5, 2024
8d00f17
Merge branch 'main' into bambu-rfid
spuder Dec 6, 2024
89d6cca
Parameterize generate_keys()
spuder Dec 6, 2024
17b5537
Clean up logs
spuder Dec 7, 2024
7e9348a
Add external component
spuder Dec 7, 2024
ed556a6
Simplified reading data for mifare tags
spuder Dec 7, 2024
3a53f48
Add logging for raw data
spuder Dec 7, 2024
b680056
Add logging for tag raw data
spuder Dec 7, 2024
a9abefc
Add data
spuder Dec 7, 2024
a98b658
Parse bambu data
spuder Dec 7, 2024
4639b30
Working color
spuder Dec 7, 2024
07c14b8
Cleanup
spuder Dec 8, 2024
6abfb4f
Move KDF to nfc_helpers
spuder Dec 8, 2024
4188c32
Read all tags
spuder Dec 8, 2024
c681b80
Cleanup bambu.h
spuder Dec 8, 2024
e1acf1f
Merge branch 'main' into bambu-rfid
spuder Dec 8, 2024
2594a16
Cleanup
spuder Dec 8, 2024
678695e
Switch vector to array, still crashing
spuder Dec 8, 2024
c61f78a
Merge branch 'main' into bambu-rfid
spuder Dec 8, 2024
9a5ee1e
Break faster if reading fails, use const for safety
spuder Dec 8, 2024
b352413
Speed up parsing by auth only to sectors
spuder Dec 8, 2024
211b463
Fix type (PLA) due to skipping sector 0
spuder Dec 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion firmware/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,6 @@ lolin_s3_mini:
run lolin_s3_mini.yaml --device $(USB_ADDRESS)
devkit:
esphome \
run esp32-s3-devkitc-1.yaml --device $(USB_ADDRESS)
run esp32-s3-devkitc-1.yaml --device $(USB_ADDRESS)
host:
esphome run host.yaml
97 changes: 96 additions & 1 deletion firmware/bambu.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,22 @@
#include <unordered_map>
#include <string>

#ifdef ESP_PLATFORM
#include "esp_idf_version.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/md.h"
#else
#include <mbedtls/hkdf.h>
#include <mbedtls/md.h>
#include <ArduinoJson.h>
#endif

namespace bambulabs
{
struct FilamentInfo {
std::string color_code;
std::string type;
};
const std::unordered_map<std::string, std::string> filament_mappings = {
{"TPU", "GFU99"},
{"PLA", "GFL99"},
Expand Down Expand Up @@ -132,4 +146,85 @@ namespace bambulabs
return result;
}

}
//TODO: move this to a utils file or find a more native esphome way to convert to ascii
// inline std::string hex_to_ascii(const std::string& hex) {
// std::string ascii;
// for (size_t i = 0; i < hex.length(); i += 2) {
// std::string byte = hex.substr(i, 2);
// char chr = static_cast<char>(std::stoi(byte, nullptr, 16));
// ascii.push_back(chr);
// }
// return ascii;
// }

inline FilamentInfo parse_tag_data(const std::vector<uint8_t>& tag_data) {
FilamentInfo info;
const int block_size = 16;
ESP_LOGD("bambu", "Parsing tag data");

for (int block = 0; block < 16; ++block) {
if (block * block_size >= tag_data.size()) {
break;
}

const uint8_t* block_data = tag_data.data() + (block * block_size);

switch (block) {
case 0:
ESP_LOGV("bambu", "UID: %s", format_hex(block_data, 4).c_str());
break;
case 1:
ESP_LOGV("bambu", "Material Variant: %s", format_hex(block_data, 8).c_str());
ESP_LOGV("bambu", "Unique Material Type: %s", format_hex(block_data + 8, 8).c_str());
break;
case 2:
ESP_LOGV("bambu", "Filament Type: %s", format_hex(block_data, 16).c_str());
// info.type = hex_to_ascii(format_hex(block_data, 16));
info.type = std::string(reinterpret_cast<const char*>(block_data), 16);
ESP_LOGV("bambu", "Filament Type Ascii: %s", info.type.c_str());
break;
case 4:
ESP_LOGV("bambu", "Detailed Filament Type: %s", format_hex(block_data, 16).c_str());
break;
case 5:
ESP_LOGV("bambu", "Color Code: %s", format_hex(block_data, 4).c_str());
info.color_code = format_hex(block_data, 4);
ESP_LOGV("bambu", "Spool Weight: %s", format_hex(block_data + 4, 2).c_str());
ESP_LOGV("bambu", "Filament Diameter: %s", format_hex(block_data + 8, 4).c_str());
break;
case 6:
ESP_LOGV("bambu", "Temperatures: %s", format_hex(block_data, 8).c_str());
ESP_LOGV("bambu", "Drying Info: %s", format_hex(block_data + 8, 8).c_str());
break;
case 8:
ESP_LOGV("bambu", "X Cam Info: %s", format_hex(block_data, 8).c_str());
ESP_LOGV("bambu", "Nozzle Diameter: %s", format_hex(block_data + 8, 8).c_str());
break;
case 9:
ESP_LOGV("bambu", "Tray UID: %s", format_hex(block_data, 16).c_str());
break;
case 10:
ESP_LOGV("bambu", "Spool Width: %s", format_hex(block_data, 16).c_str());
break;
case 12:
ESP_LOGV("bambu", "Production Date/Time: %s", format_hex(block_data, 16).c_str());
break;
case 13:
ESP_LOGV("bambu", "Short Production Date/Time: %s", format_hex(block_data, 16).c_str());
break;
case 14:
ESP_LOGV("bambu", "Filament Length: %s", format_hex(block_data, 16).c_str());
break;
case 16:
ESP_LOGV("bambu", "Extra Color Info: %s", format_hex(block_data, 16).c_str());
break;
}

if (block != 0 && block != 1 && block !=2 && block != 3 && block != 7 && block != 11 && block != 15) {
ESP_LOGV("bambu", "Block %d Data: %s", block, format_hex(block_data, block_size).c_str());
}
}
return info;
}

}
30 changes: 30 additions & 0 deletions firmware/components/nfc/nfc_helpers.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "nfc_helpers.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/md.h"


namespace esphome {
namespace nfc {
Expand Down Expand Up @@ -43,5 +46,32 @@ std::string get_random_ha_tag_ndef() {
return uri;
}

// TODO: should this use unique_ptr for better memory management?
std::array<std::array<uint8_t, 6>, 16> generate_keys(const std::vector<uint8_t>& uid) {

// Output buffer
static uint8_t output[96];

// Context
const unsigned char context[] = {'R', 'F', 'I', 'D', '-', 'A', '\0'};

// Perform HKDF
mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
nfc::BAMBU_SALT.data(), nfc::BAMBU_SALT.size(),
uid.data(), uid.size(),
context, sizeof(context),
output, sizeof(output));

//TODO: should this use std::unique_ptr for better memory management?
std::array<std::array<uint8_t, 6>, 16> result;
for (int i = 0; i < 16; i++) {
std::copy_n(output + i*6, 6, result[i].begin());
ESP_LOGD("bambu", "Key %d: %02x%02x%02x%02x%02x%02x", i,
result[i][0], result[i][1], result[i][2],
result[i][3], result[i][4], result[i][5]);
}
return result;
}

} // namespace nfc
} // namespace esphome
5 changes: 5 additions & 0 deletions firmware/components/nfc/nfc_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ namespace nfc {
static const char HA_TAG_ID_EXT_RECORD_TYPE[] = "android.com:pkg";
static const char HA_TAG_ID_EXT_RECORD_PAYLOAD[] = "io.homeassistant.companion.android";
static const char HA_TAG_ID_PREFIX[] = "https://www.home-assistant.io/tag/";
static const std::vector<uint8_t> BAMBU_SALT = {
0x9a, 0x75, 0x9c, 0xf2, 0xc4, 0xf7, 0xca, 0xff,
0x22, 0x2c, 0xb9, 0x76, 0x9b, 0x41, 0xbc, 0x96
};

std::string get_ha_tag_ndef(NfcTag &tag);
std::string get_random_ha_tag_ndef();
bool has_ha_tag_ndef(NfcTag &tag);
std::array<std::array<uint8_t, 6>, 16> generate_keys(const std::vector<uint8_t> &uid);

} // namespace nfc
} // namespace esphome
16 changes: 16 additions & 0 deletions firmware/components/nfc/nfc_tag.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,39 @@ class NfcTag {
this->tag_type_ = tag_type;
this->ndef_message_ = make_unique<NdefMessage>(ndef_data);
};

NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type, std::vector<uint8_t> &raw_data, bool is_raw_data) {
//TODO: this probably could be merged with `std::vector<uint8_t> &uid, const std::string &tag_type, std::vector<uint8_t> &ndef_data`
// but instead of making an ndef_message we just store the raw data
this->uid_ = uid;
this->tag_type_ = tag_type;
this->raw_data_ = raw_data;
this->is_raw_data_ = is_raw_data;
}
NfcTag(const NfcTag &rhs) {
uid_ = rhs.uid_;
tag_type_ = rhs.tag_type_;
if (rhs.ndef_message_ != nullptr)
ndef_message_ = make_unique<NdefMessage>(*rhs.ndef_message_);
if (!rhs.raw_data_.empty())
raw_data_ = rhs.raw_data_;
is_raw_data_ = rhs.is_raw_data_;
}

std::vector<uint8_t> &get_uid() { return this->uid_; };
const std::string &get_tag_type() { return this->tag_type_; };
bool has_ndef_message() { return this->ndef_message_ != nullptr; };
const std::shared_ptr<NdefMessage> &get_ndef_message() { return this->ndef_message_; };
void set_ndef_message(std::unique_ptr<NdefMessage> ndef_message) { this->ndef_message_ = std::move(ndef_message); };
bool has_raw_data() { return this->is_raw_data_; };
const std::vector<uint8_t> &get_raw_data() { return this->raw_data_; };

protected:
std::vector<uint8_t> uid_;
std::string tag_type_;
std::shared_ptr<NdefMessage> ndef_message_;
std::vector<uint8_t> raw_data_;
bool is_raw_data_ = false;
};

} // namespace nfc
Expand Down
4 changes: 3 additions & 1 deletion firmware/components/pn532/pn532.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ void PN532::loop() {
for (const auto &record : records) {
ESP_LOGD(TAG, " %s - %s", record->get_type().c_str(), record->get_payload().c_str());
}
}
}//TODO: elseif not ndef, but has raw data
}
} else if (next_task_ == CLEAN) {
ESP_LOGD(TAG, " Tag cleaning...");
Expand Down Expand Up @@ -363,6 +363,8 @@ std::unique_ptr<nfc::NfcTag> PN532::read_tag_(std::vector<uint8_t> &uid) {
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
ESP_LOGD(TAG, "Mifare classic");
return this->read_mifare_classic_tag_(uid);
//TODO: figure out how to preserve backwards compatiblilyt with ndef tags without keys
// return this->read_mifare_classic_tag_(uid, nfc::KEYS); //TODO: should this be pointer?
} else if (type == nfc::TAG_TYPE_2) {
ESP_LOGD(TAG, "Mifare ultralight");
return this->read_mifare_ultralight_tag_(uid);
Expand Down
2 changes: 2 additions & 0 deletions firmware/components/pn532/pn532.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "esphome/components/nfc/nfc_tag.h"
#include "esphome/components/nfc/nfc.h"
#include "esphome/components/nfc/automation.h"
#include "esphome/components/nfc/nfc_helpers.h" //TODO: is this the best way to add generaet_keys() dep?

#include <cinttypes>
#include <vector>
Expand Down Expand Up @@ -76,6 +77,7 @@ class PN532 : public PollingComponent {
bool write_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);

std::unique_ptr<nfc::NfcTag> read_mifare_classic_tag_(std::vector<uint8_t> &uid);
std::unique_ptr<nfc::NfcTag> read_mifare_classic_tag_(std::vector<uint8_t> &uid, std::array<const uint8_t*, 16> keys); //todo should this be *keys
bool read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
bool write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
bool auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key);
Expand Down
Loading