From d308da9a008b925eb3177e9f777b97abbdd23b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=ED=98=84?= Date: Thu, 27 Aug 2026 00:19:50 +0900 Subject: [PATCH] feat: add XIAO nRF52840 Sense firmware source Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: sisyphus-dev-ai --- .github/firmware-toolchain.json | 3 +- .github/workflows/publish-firmware.yml | 1 + .github/workflows/validate.yml | 1 + boards/seeed-xiao-nrf52840-sense.ino | 98 +++++++++++++++++++ sources/boards/seeed-xiao-nrf52840-sense.json | 34 +++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 boards/seeed-xiao-nrf52840-sense.ino create mode 100644 sources/boards/seeed-xiao-nrf52840-sense.json diff --git a/.github/firmware-toolchain.json b/.github/firmware-toolchain.json index 33074b4..77cfe52 100644 --- a/.github/firmware-toolchain.json +++ b/.github/firmware-toolchain.json @@ -2,6 +2,7 @@ "schemaVersion": 1, "arduinoCli": "1.5.1", "platforms": { - "arduino:avr": "1.8.8" + "arduino:avr": "1.8.8", + "Seeeduino:nrf52": "1.1.13" } } diff --git a/.github/workflows/publish-firmware.yml b/.github/workflows/publish-firmware.yml index 73fa776..1041f53 100644 --- a/.github/workflows/publish-firmware.yml +++ b/.github/workflows/publish-firmware.yml @@ -53,6 +53,7 @@ jobs: run: | arduino-cli core update-index arduino-cli core install arduino:avr@1.8.8 + arduino-cli core install Seeeduino:nrf52@1.1.13 --additional-urls https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json - name: Test publisher run: python -m unittest -v tests.test_firmware diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 82b323d..6251f0b 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -37,6 +37,7 @@ jobs: run: | arduino-cli core update-index arduino-cli core install arduino:avr@1.8.8 + arduino-cli core install Seeeduino:nrf52@1.1.13 --additional-urls https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json - name: Run unit tests run: python -m unittest -v tests.test_firmware diff --git a/boards/seeed-xiao-nrf52840-sense.ino b/boards/seeed-xiao-nrf52840-sense.ino new file mode 100644 index 0000000..bec1646 --- /dev/null +++ b/boards/seeed-xiao-nrf52840-sense.ino @@ -0,0 +1,98 @@ +// Hanbeon Bluetooth switch firmware for Seeed XIAO nRF52840 Sense. +// +// Reports one P record per stable press and one R record per stable release +// over both USB CDC and Nordic UART Service. A USB bridge can send HELLO, +// FLASH, OFF, or BOOT; the latter enters the serial DFU bootloader. + +const byte buttonPin = 2; +const byte ledPin = 9; +const unsigned long debounceMs = 30; +const unsigned long flashMs = 120; + +int lastRawReading = HIGH; +int stableState = HIGH; +unsigned long lastChangeMs = 0; +unsigned long flashUntilMs = 0; +String command; +bool commandHadCarriageReturn = false; + +#ifdef USE_TINYUSB +#include +#include +#endif + +#include + +BLEUart bleuart; + +void sendLine(const char* line) { + Serial.print(line); + if (bleuart.notifyEnabled()) bleuart.print(line); +} + +void enterBootloader() { + sendLine("BOOT_ENTER\n"); + Serial.flush(); + delay(100); +#ifdef USE_TINYUSB + TinyUSB_Port_EnterDFU(); +#endif +} + +void setup() { + pinMode(buttonPin, INPUT_PULLUP); + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, LOW); + Serial.begin(115200); + + Bluefruit.autoConnLed(false); + Bluefruit.begin(); + Bluefruit.setName("HanBeon XIAO"); + bleuart.begin(); + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addService(bleuart); + Bluefruit.Advertising.start(); +} + +void loop() { + readBridgeCommands(Serial); + readBridgeCommands(bleuart); + + const int rawReading = digitalRead(buttonPin); + if (rawReading != lastRawReading) { + lastChangeMs = millis(); + lastRawReading = rawReading; + } + if (millis() - lastChangeMs >= debounceMs && rawReading != stableState) { + stableState = rawReading; + sendLine(stableState == LOW ? "P\n" : "R\n"); + } + + const bool buttonPressed = stableState == LOW; + const bool flashing = millis() < flashUntilMs; + digitalWrite(ledPin, buttonPressed || flashing ? HIGH : LOW); +} + +void readBridgeCommands(Stream& stream) { + while (stream.available() > 0) { + const char next = static_cast(stream.read()); + if (next == '\n') { + if (command == "HELLO" && !commandHadCarriageReturn) { + sendLine("HANBEON_UNO_V1\n"); + } else if (command == "FLASH") { + flashUntilMs = millis() + flashMs; + } else if (command == "OFF") { + flashUntilMs = 0; + } else if (command == "BOOT") { + enterBootloader(); + } + command = ""; + commandHadCarriageReturn = false; + } else if (next == '\r') { + commandHadCarriageReturn = true; + } else { + command += next; + } + } +} diff --git a/sources/boards/seeed-xiao-nrf52840-sense.json b/sources/boards/seeed-xiao-nrf52840-sense.json new file mode 100644 index 0000000..f5be554 --- /dev/null +++ b/sources/boards/seeed-xiao-nrf52840-sense.json @@ -0,0 +1,34 @@ +{ + "schemaVersion": 1, + "id": "seeed.xiao-nrf52840-sense", + "name": "Seeed XIAO nRF52840 Sense", + "sketch": { + "path": "boards/seeed-xiao-nrf52840-sense.ino", + "fqbn": "Seeeduino:nrf52:xiaonRF52840Sense" + }, + "detect": { + "usb": [ + { + "vid": "2886", + "pid": "8045", + "confidence": "exact", + "manufacturerAliases": ["Seeed Studio"], + "productAliases": ["XIAO nRF52840 Sense"] + }, + { + "vid": "2886", + "pid": "0045", + "confidence": "exact", + "manufacturerAliases": ["Seeed Studio"], + "productAliases": ["XIAO nRF52840 Sense"] + } + ] + }, + "wiring": [ + { + "from": "D2", + "to": "순간 누름 스위치 NO 단자", + "note": "스위치 COM 단자는 GND에 연결" + } + ] +}