Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ docs/tigertag-architecture.md
docs/deep-thoughts.md
CODE-CLEANUP.md
docs/writer-ui-plan.md
.gstack/
34 changes: 34 additions & 0 deletions include/BoardPins.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,30 @@
#define PIN_PN5180_GPIO 10 // General purpose I/O (card detection)
#define PIN_PN5180_IRQ 11 // Interrupt request (active HIGH)
#define PIN_PN5180_AUX 12 // Auxiliary monitoring (future use)
// PN532 SPI (shares physical pins with PN5180; only one active at runtime)
#define PIN_PN532_SCK PIN_PN5180_SCK
#define PIN_PN532_MOSI PIN_PN5180_MOSI
#define PIN_PN532_MISO PIN_PN5180_MISO
#define PIN_PN532_SS PIN_PN5180_NSS
#define PIN_PN532_IRQ PIN_PN5180_IRQ
#define PIN_PN532_RST PIN_PN5180_RST
// LCD I2C
#define PIN_LCD_SDA 1
#define PIN_LCD_SCL 2
// Status LED — onboard WS2812 RGB (always available, no external wiring)
#define PIN_STATUS_LED 21
// 3x4 Matrix Keypad (ENABLE_KEYPAD)
// NOTE: GPIO 19/20 are USB D-/D+ on S3 and unavailable as GPIO.
// NOTE: If using LCD + keypad simultaneously on S3, pin conflicts may occur.
// For LCD + keypad builds, the ESP32-WROOM is strongly recommended
// due to its larger number of freely available GPIO pins.
#define PIN_KEYPAD_ROW1 38
#define PIN_KEYPAD_ROW2 39
#define PIN_KEYPAD_ROW3 40
#define PIN_KEYPAD_ROW4 41
#define PIN_KEYPAD_COL1 17
#define PIN_KEYPAD_COL2 18
#define PIN_KEYPAD_COL3 42
#else
// --- ESP32-WROOM-32 pin mapping (default) ---
// PN5180 SPI
Expand All @@ -31,9 +50,24 @@
#define PIN_PN5180_GPIO 32 // General purpose I/O (card detection)
#define PIN_PN5180_IRQ 35 // Interrupt request (active HIGH, input-only pin)
#define PIN_PN5180_AUX 34 // Auxiliary monitoring (input-only pin)
// PN532 SPI (shares physical pins with PN5180; only one active at runtime)
#define PIN_PN532_SCK PIN_PN5180_SCK
#define PIN_PN532_MOSI PIN_PN5180_MOSI
#define PIN_PN532_MISO PIN_PN5180_MISO
#define PIN_PN532_SS PIN_PN5180_NSS
#define PIN_PN532_IRQ PIN_PN5180_IRQ
#define PIN_PN532_RST PIN_PN5180_RST
// LCD I2C
#define PIN_LCD_SDA 23
#define PIN_LCD_SCL 22
// Status LED — external SK6812 RGBW (optional, requires wiring)
#define PIN_STATUS_LED 4
// 3x4 Matrix Keypad (ENABLE_KEYPAD)
#define PIN_KEYPAD_ROW1 15
#define PIN_KEYPAD_ROW2 16
#define PIN_KEYPAD_ROW3 17
#define PIN_KEYPAD_ROW4 18
#define PIN_KEYPAD_COL1 19
#define PIN_KEYPAD_COL2 21
#define PIN_KEYPAD_COL3 5
Comment on lines +65 to +72

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Document potential GPIO conflicts on PSRAM-equipped ESP32-WROOM modules.

GPIO 16 and 17 (PIN_KEYPAD_ROW2, PIN_KEYPAD_ROW3) are used for PSRAM interface on ESP32-WROVER and some ESP32-WROOM-32D modules. Users with PSRAM-equipped boards may encounter conflicts.

Consider adding a note similar to the S3 section:

   // 3x4 Matrix Keypad (ENABLE_KEYPAD)
+  // NOTE: GPIO 16/17 conflict with PSRAM on ESP32-WROVER modules.
+  //       For PSRAM builds, remap ROW2/ROW3 to different pins.
   `#define` PIN_KEYPAD_ROW1  15
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 3x4 Matrix Keypad (ENABLE_KEYPAD)
#define PIN_KEYPAD_ROW1 15
#define PIN_KEYPAD_ROW2 16
#define PIN_KEYPAD_ROW3 17
#define PIN_KEYPAD_ROW4 18
#define PIN_KEYPAD_COL1 19
#define PIN_KEYPAD_COL2 21
#define PIN_KEYPAD_COL3 5
// 3x4 Matrix Keypad (ENABLE_KEYPAD)
// NOTE: GPIO 16/17 conflict with PSRAM on ESP32-Wrover modules.
// For PSRAM builds, remap ROW2/ROW3 to different pins.
`#define` PIN_KEYPAD_ROW1 15
`#define` PIN_KEYPAD_ROW2 16
`#define` PIN_KEYPAD_ROW3 17
`#define` PIN_KEYPAD_ROW4 18
`#define` PIN_KEYPAD_COL1 19
`#define` PIN_KEYPAD_COL2 21
`#define` PIN_KEYPAD_COL3 5
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/BoardPins.h` around lines 65 - 72, Document that PIN_KEYPAD_ROW2 and
PIN_KEYPAD_ROW3 (GPIO 16 and 17) can conflict with PSRAM on some
ESP32-WROOM/WROVER modules and add a short note next to the keypad pin
definitions explaining this; instruct users to avoid 16/17 on PSRAM-equipped
boards and suggest alternative GPIOs (for example use non-PSRAM pins such as
GPIOs 2, 4, 12, 13, 14, 25-27, etc.), and optionally recommend providing a
comment or preprocessor/README hint to select alternate PIN_KEYPAD_ROW* values
when PSRAM/ESP32-WROVER is present.

#endif
3 changes: 3 additions & 0 deletions include/UserConfig.example.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
/* Optional hardware features */
#define ENABLE_LCD 0
#define ENABLE_STATUS_LED 1 // Always available on S3-Zero (onboard LED), optional on WROOM (external wiring)
#define ENABLE_KEYPAD 0 // 3x4 matrix keypad — wiring per BoardPins.h
// Note: Using LCD + keypad together on S3 is not recommended due to limited GPIO.
// For LCD + keypad builds, use the WROOM board.

/* Board selection: uncomment ONE of the following.
Pin mapping is automatic via BoardPins.h — no need to configure pins manually. */
Expand Down
78 changes: 78 additions & 0 deletions lib/openprinttag/openprinttag_adafruit_pn532.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* OpenPrintTag Adafruit PN532 HAL Adapter
*
* Provides NFC HAL implementation for Adafruit_PN532 Arduino library.
* Wraps mifareultralight_ReadPage/WritePage into opt_nfc_hal_t callbacks.
*/

#ifndef OPENPRINTTAG_ADAFRUIT_PN532_H
#define OPENPRINTTAG_ADAFRUIT_PN532_H

#include "openprinttag_lib.h"
#include <Adafruit_PN532.h>

#ifdef __cplusplus
extern "C" {
#endif
Comment on lines +14 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

extern "C" linkage is unnecessary for static inline functions.

These are static inline functions that won't have external linkage. The extern "C" block has no effect on them and could be misleading since the function bodies use C++ constructs (Adafruit_PN532* cast). Consider removing the extern "C" guards or, if kept for documentation purposes, add a comment clarifying their purpose.

Also applies to: 74-76

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/openprinttag/openprinttag_adafruit_pn532.h` around lines 14 - 16, The
extern "C" linkage block around the header is unnecessary and misleading because
the header only declares static inline functions that use C++ constructs (casts
to Adafruit_PN532*) and therefore have no external linkage; either remove the
extern "C" { ... } / `#endif` pair (the block beginning at the top of the file and
the matching closing at lines near 74-76) or keep it but add a clear comment
above it stating it is only for documentation and does not affect the static
inline functions; update references to the static inline helpers (the inline
functions that cast to Adafruit_PN532*) accordingly so there’s no confusion
about C vs C++ linkage.


/**
* Read a page from NTAG using Adafruit_PN532.
*
* @param ctx Adafruit_PN532 pointer
* @param page Page number to read
* @param buf Output buffer (4 bytes)
* @return OPT_OK on success, OPT_ERR_NFC_READ on failure
*/
static inline opt_error_t opt_adafruit_pn532_read_page(void *ctx, uint8_t page, uint8_t *buf) {
Adafruit_PN532 *pn532 = (Adafruit_PN532 *)ctx;
/* mifareultralight_ReadPage reads 4 bytes, returns count (non-zero = success) */
return pn532->mifareultralight_ReadPage(page, buf) ? OPT_OK : OPT_ERR_NFC_READ;
}

/**
* Write a page to NTAG using Adafruit_PN532.
*
* @param ctx Adafruit_PN532 pointer
* @param page Page number to write
* @param data Data to write (4 bytes)
* @return OPT_OK on success, OPT_ERR_NFC_WRITE on failure
*/
static inline opt_error_t opt_adafruit_pn532_write_page(void *ctx, uint8_t page, const uint8_t *data) {
Adafruit_PN532 *pn532 = (Adafruit_PN532 *)ctx;
/* mifareultralight_WritePage returns true on success */
return pn532->mifareultralight_WritePage(page, (uint8_t *)data) ? OPT_OK : OPT_ERR_NFC_WRITE;
}

/**
* Create a HAL structure for Adafruit_PN532.
*
* @param pn532 Initialized Adafruit_PN532 instance
* @return HAL structure ready for use with opt_read_from_nfc/opt_write_to_nfc
*
* Example usage:
* Adafruit_PN532 pn532(SS_PIN, &SPI);
* pn532.begin();
* pn532.SAMConfig();
*
* opt_nfc_hal_t hal = opt_create_adafruit_pn532_hal(&pn532);
*
* opt_tag_t tag;
* opt_init(&tag);
* opt_read_from_nfc(&tag, &hal, 4, 50);
* opt_parse_ndef(&tag);
*/
static inline opt_nfc_hal_t opt_create_adafruit_pn532_hal(Adafruit_PN532 *pn532) {
opt_nfc_hal_t hal = {
.read_page = opt_adafruit_pn532_read_page,
.write_page = opt_adafruit_pn532_write_page,
.is_present = NULL,
.user_ctx = pn532
};
return hal;
}

#ifdef __cplusplus
}
#endif

#endif /* OPENPRINTTAG_ADAFRUIT_PN532_H */
1 change: 1 addition & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ lib_deps =
knolleary/PubSubClient@^2.8
codewitch-honey-crisis/htcw_json@^0.2.5
adafruit/Adafruit NeoPixel
adafruit/Adafruit PN532@1.3.4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🌐 Web query:

What is the latest version of Adafruit PN532 Arduino library?

💡 Result:

The latest version of the Adafruit PN532 Arduino library is 1.3.4, released on 2025-02-18.

Citations:


🏁 Script executed:

# Read the platformio.ini file to see all dependencies and their version pinning styles
cat platformio.ini

Repository: SpoolSense/spoolsense_scanner

Length of output: 1187


Version pinning is inconsistent with other dependencies.

Most dependencies use ^ prefix for semver-compatible updates (e.g., ^1.1.4, ^7.0.0), but Adafruit PN532@1.3.4 is pinned to an exact version. While 1.3.4 is the current latest version, consider using @^1.3.4 for consistency with the rest of the dependency list.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platformio.ini` at line 27, The dependency "adafruit/Adafruit PN532@1.3.4" is
pinned to an exact version while other deps use semver-compatible ranges; update
the entry (the adafruit/Adafruit PN532 dependency line) to use a caret-prefixed
range, e.g., change "@1.3.4" to "@^1.3.4", so it matches the project's
versioning convention and allows non-breaking updates.

chris--a/Keypad@^3.1.1

[env:esp32dev]
Expand Down
14 changes: 11 additions & 3 deletions src/ApplicationManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,14 @@ void ApplicationManager::handleKeypadCancel() {

bool ApplicationManager::sendAssignSpool(const char* toolNumber) {
#ifndef NATIVE_TEST
// Validate tool number is digits-only (prevent GCode injection)
for (const char* p = toolNumber; *p; p++) {
if (*p < '0' || *p > '9') {
Serial.printf("ApplicationManager: Invalid tool number '%s'\n", toolNumber);
return false;
}
}

const char* moonrakerUrl = ConfigurationManager::getInstance().getMoonrakerURL();
if (!moonrakerUrl || moonrakerUrl[0] == '\0') {
Serial.println("ApplicationManager: Moonraker URL not configured — cannot assign spool");
Expand All @@ -989,7 +997,7 @@ bool ApplicationManager::sendAssignSpool(const char* toolNumber) {
}

extern SemaphoreHandle_t g_httpMutex;
if (g_httpMutex && xSemaphoreTake(g_httpMutex, pdMS_TO_TICKS(5000)) != pdTRUE) {
if (g_httpMutex && xSemaphoreTake(g_httpMutex, pdMS_TO_TICKS(3000)) != pdTRUE) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

HTTP timeout values are significantly shorter than other HTTP operations.

The mutex timeout (3000ms) and HTTP timeouts (1000ms connect, 2000ms operation) are much shorter than SpoolmanManager::HTTP_MUTEX_TIMEOUT which is 10000ms. This inconsistency could cause premature failures if the network or Moonraker is slow. Consider aligning these values or documenting why shorter timeouts are appropriate for this specific use case.

Proposed timeout alignment
-    if (g_httpMutex && xSemaphoreTake(g_httpMutex, pdMS_TO_TICKS(3000)) != pdTRUE) {
+    if (g_httpMutex && xSemaphoreTake(g_httpMutex, pdMS_TO_TICKS(5000)) != pdTRUE) {
-    http.setConnectTimeout(1000);
-    http.setTimeout(2000);
+    http.setConnectTimeout(3000);
+    http.setTimeout(5000);

Also applies to: 1017-1018

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ApplicationManager.cpp` at line 1000, The mutex and HTTP timeouts in
ApplicationManager.cpp are much shorter than the reference constant
SpoolmanManager::HTTP_MUTEX_TIMEOUT; update the call that uses g_httpMutex and
xSemaphoreTake(pdMS_TO_TICKS(3000)) to use SpoolmanManager::HTTP_MUTEX_TIMEOUT
(or another shared constant) and similarly align the HTTP connect/operation
timeouts (1000ms/2000ms) to the same shared timeout or document why shorter
values are required for this code path (also apply the same change where similar
calls occur around the xSemaphoreTake/HTTP setup later in the file).

Serial.println("ApplicationManager: Could not acquire HTTP mutex for ASSIGN_SPOOL");
if (lcdManager) lcdManager->updateScreen("Assign failed", "HTTP busy");
return false;
Expand All @@ -1006,8 +1014,8 @@ bool ApplicationManager::sendAssignSpool(const char* toolNumber) {

WiFiClient client;
HTTPClient http;
http.setConnectTimeout(3000);
http.setTimeout(5000);
http.setConnectTimeout(1000);
http.setTimeout(2000);
http.begin(client, url);
http.addHeader("Content-Type", "application/json");
int code = http.POST(postBody);
Expand Down
9 changes: 9 additions & 0 deletions src/ConfigHTML.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ const char CONFIG_HTML[] PROGMEM = R"rawliteral(
<span class="toggle-track"></span>
</label>
</div>
<div class="toggle-row">
<span class="toggle-label">NFC Reader</span>
<select id="nfc_reader" style="padding:6px 10px;border-radius:6px;border:1px solid var(--border);background:var(--card);color:var(--text);font-size:0.95em">
<option value="pn5180">PN5180 (ISO15693 + ISO14443A)</option>
<option value="pn532">PN532 (ISO14443A only)</option>
</select>
</div>
</div>
</section>

Expand Down Expand Up @@ -226,6 +233,7 @@ const char CONFIG_HTML[] PROGMEM = R"rawliteral(
document.getElementById('lcd_enabled').checked = !!cfg.lcd_enabled;
document.getElementById('led_enabled').checked = !!cfg.led_enabled;
document.getElementById('keypad_enabled').checked = !!cfg.keypad_enabled;
if (cfg.nfc_reader) document.getElementById('nfc_reader').value = cfg.nfc_reader;
maybeSetValue('moonraker_url', cfg.moonraker_url);
// Password placeholders
if (cfg.wifi_pass_set) document.getElementById('wifi_pass').placeholder = '(set) Leave blank to keep';
Expand Down Expand Up @@ -262,6 +270,7 @@ const char CONFIG_HTML[] PROGMEM = R"rawliteral(
lcd_enabled: document.getElementById('lcd_enabled').checked ? 1 : 0,
led_enabled: document.getElementById('led_enabled').checked ? 1 : 0,
keypad_enabled: document.getElementById('keypad_enabled').checked ? 1 : 0,
nfc_reader: document.getElementById('nfc_reader').value,
moonraker_url: document.getElementById('moonraker_url').value.trim(),
prusalink_on: document.getElementById('prusalink_on').checked ? 1 : 0,
prusalink_url: document.getElementById('prusalink_url').value.trim(),
Expand Down
14 changes: 14 additions & 0 deletions src/ConfigurationManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ static const char* NVS_KEY_MOONRAKER_URL = "moonraker_url";
static const char* NVS_KEY_PRUSALINK_ON = "prusalink_on";
static const char* NVS_KEY_PRUSALINK_URL = "prusalink_url";
static const char* NVS_KEY_PRUSALINK_KEY = "prusalink_key";
static const char* NVS_KEY_NFC_READER = "nfc_reader";

ConfigurationManager& ConfigurationManager::getInstance() {
static ConfigurationManager instance;
Expand Down Expand Up @@ -101,6 +102,9 @@ void ConfigurationManager::loadFromDeviceConfig() {
_prusaLinkUrl[0] = '\0';
_prusaLinkApiKey[0] = '\0';

// NFC reader default
strncpy(_nfcReader, "pn5180", sizeof(_nfcReader) - 1);

// Optional hardware feature defaults from compile-time flags
_lcdEnabled = cfg.peripherals.lcd_enabled;
_ledEnabled = cfg.peripherals.status_led_enabled;
Expand Down Expand Up @@ -188,6 +192,10 @@ bool ConfigurationManager::loadFromNVS() {
_keypadEnabled = prefs.getUChar(NVS_KEY_KEYPAD_ON, _keypadEnabled ? 1 : 0) != 0;
anyOverride = true;
}
if (prefs.isKey(NVS_KEY_NFC_READER)) {
prefs.getString(NVS_KEY_NFC_READER, _nfcReader, sizeof(_nfcReader));
anyOverride = true;
}

prefs.end();
return anyOverride;
Expand Down Expand Up @@ -270,6 +278,10 @@ const char* ConfigurationManager::getMoonrakerURL() const {
return _moonrakerUrl;
}

const char* ConfigurationManager::getNfcReader() const {
return _nfcReader;
}

void ConfigurationManager::getCurrentConfig(ConfigUpdate& out) const {
memset(&out, 0, sizeof(out));
strncpy(out.wifi_ssid, _ssid, sizeof(out.wifi_ssid) - 1);
Expand All @@ -288,6 +300,7 @@ void ConfigurationManager::getCurrentConfig(ConfigUpdate& out) const {
out.led_enabled = _ledEnabled ? 1 : 0;
out.keypad_enabled = _keypadEnabled ? 1 : 0;
strncpy(out.moonraker_url, _moonrakerUrl, sizeof(out.moonraker_url) - 1);
strncpy(out.nfc_reader, _nfcReader, sizeof(out.nfc_reader) - 1);
}

#ifndef NATIVE_TEST
Expand Down Expand Up @@ -321,6 +334,7 @@ bool ConfigurationManager::saveToNVS(const ConfigUpdate& update) {
if (update.prusalink_api_key[0] != '\0') {
prefs.putString(NVS_KEY_PRUSALINK_KEY, update.prusalink_api_key);
}
prefs.putString(NVS_KEY_NFC_READER, update.nfc_reader);

prefs.end();
Serial.println("ConfigurationManager: Config saved to NVS");
Expand Down
8 changes: 8 additions & 0 deletions src/ConfigurationManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ struct ConfigUpdate {
uint8_t prusalink_on;
char prusalink_url[128];
char prusalink_api_key[64];
// NFC reader selection
char nfc_reader[8]; // "pn5180" or "pn532"
};

class ConfigurationManager {
Expand Down Expand Up @@ -58,6 +60,9 @@ class ConfigurationManager {
const char* getPrusaLinkURL() const;
const char* getPrusaLinkAPIKey() const;

// NFC reader selection (NVS, default "pn5180")
const char* getNfcReader() const;

// Optional hardware features (compile-time default, overridable via NVS)
bool isLcdEnabled() const;
bool isLedEnabled() const;
Expand Down Expand Up @@ -99,6 +104,9 @@ class ConfigurationManager {
char _prusaLinkUrl[128] = {0};
char _prusaLinkApiKey[64] = {0};

// NFC reader selection
char _nfcReader[8] = "pn5180";

// Optional hardware features
bool _lcdEnabled = false;
bool _ledEnabled = false;
Expand Down
6 changes: 6 additions & 0 deletions src/HardwareNFCConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ opt_error_t HardwareNFCConnection::halWritePage(void* ctx, uint8_t page, const u
return OPT_OK;
}

void HardwareNFCConnection::getReaderInfo(char* buf, size_t len) const {
if (buf && len > 0) {
snprintf(buf, len, "PN5180 v%d.%d", fw_[1], fw_[0]);
}
}

bool HardwareNFCConnection::begin() {
// Configure additional input pins for future use
pinMode(PIN_PN5180_IRQ, INPUT); // Interrupt (active HIGH)
Expand Down
3 changes: 2 additions & 1 deletion src/HardwareNFCConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ class HardwareNFCConnection : public NFCConnectionI {
bool writeISO14443Pages(uint8_t startPage, uint8_t pageCount, const uint8_t* data, uint16_t dataLen) override;
uint8_t getLastSAK() const override { return lastSAK_; }
uint16_t getLastATQA() const override { return lastATQA_; }
void getReaderInfo(char* buf, size_t len) const override;
// Diagnostics: log RF_STATUS, IRQ_STATUS, SYSTEM_STATUS registers
void logDiagnostics();
void logDiagnostics() override;
// Returns PN5180 firmware version bytes (set during begin()). fw[0]=minor, fw[1]=major.
void getPN5180FirmwareVersion(uint8_t fw[2]) const { fw[0] = fw_[0]; fw[1] = fw_[1]; }
bool isPN5180Ready() const { return pn5180Ready_; }
Expand Down
Loading