Skip to content

Commit bed3113

Browse files
committed
Adding support for Ikoka Stick with Seeed Xiao nRF54 baseboard.
Adds a new variant 'ikoka_stick_nrf' and associated support files. This is based on the Xiao nRF54 code with pin numbers and functions changed to suit the differences in hardware between the WIO SX1262 board and the Ikoka Stick. Sets the default LoRa transmit power to 9dBm to avoid burning up the frontend in Ikoka Sticks equipped with the Ebyte 33dBm S22 module on first boot. Adds support for an SSD1306 display connected to the display header. Note the display pinout is the same as the RAK4631 display header so make sure to use a display wired accordingly (aliexpress etc. SSD1306s typically have Vcc & GND reversed from what this board expects). Adds support for display rotation to SSD1306Display via a platformIO define. This support was added following the same paradigms found elsewhere in the code for rotating a display.
1 parent 72d13ca commit bed3113

File tree

8 files changed

+605
-0
lines changed

8 files changed

+605
-0
lines changed

src/helpers/ui/SSD1306Display.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ bool SSD1306Display::i2c_probe(TwoWire& wire, uint8_t addr) {
77
}
88

99
bool SSD1306Display::begin() {
10+
#ifdef DISPLAY_ROTATION
11+
display.setRotation(DISPLAY_ROTATION);
12+
#endif
1013
return display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_ADDRESS, true, false) && i2c_probe(Wire, DISPLAY_ADDRESS);
1114
}
1215

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#ifdef XIAO_NRF52
2+
3+
#include <Arduino.h>
4+
#include "ikoka_stick_nrf_board.h"
5+
6+
#include <bluefruit.h>
7+
#include <Wire.h>
8+
9+
static BLEDfu bledfu;
10+
11+
static void connect_callback(uint16_t conn_handle)
12+
{
13+
(void)conn_handle;
14+
MESH_DEBUG_PRINTLN("BLE client connected");
15+
}
16+
17+
static void disconnect_callback(uint16_t conn_handle, uint8_t reason)
18+
{
19+
(void)conn_handle;
20+
(void)reason;
21+
22+
MESH_DEBUG_PRINTLN("BLE client disconnected");
23+
}
24+
25+
void ikoka_stick_nrf_board::begin() {
26+
// for future use, sub-classes SHOULD call this from their begin()
27+
startup_reason = BD_STARTUP_NORMAL;
28+
29+
pinMode(PIN_VBAT, INPUT);
30+
pinMode(VBAT_ENABLE, OUTPUT);
31+
digitalWrite(VBAT_ENABLE, HIGH);
32+
33+
#ifdef PIN_USER_BTN
34+
pinMode(PIN_USER_BTN, INPUT_PULLUP);
35+
#endif
36+
37+
#if defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL)
38+
Wire.setPins(PIN_WIRE_SDA, PIN_WIRE_SCL);
39+
#endif
40+
41+
Wire.begin();
42+
43+
#ifdef P_LORA_TX_LED
44+
pinMode(P_LORA_TX_LED, OUTPUT);
45+
digitalWrite(P_LORA_TX_LED, HIGH);
46+
#endif
47+
48+
// pinMode(SX126X_POWER_EN, OUTPUT);
49+
// digitalWrite(SX126X_POWER_EN, HIGH);
50+
delay(10); // give sx1262 some time to power up
51+
}
52+
53+
bool ikoka_stick_nrf_board::startOTAUpdate(const char* id, char reply[]) {
54+
// Config the peripheral connection with maximum bandwidth
55+
// more SRAM required by SoftDevice
56+
// Note: All config***() function must be called before begin()
57+
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
58+
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
59+
60+
Bluefruit.begin(1, 0);
61+
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
62+
Bluefruit.setTxPower(4);
63+
// Set the BLE device name
64+
Bluefruit.setName("XIAO_NRF52_OTA");
65+
66+
Bluefruit.Periph.setConnectCallback(connect_callback);
67+
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
68+
69+
// To be consistent OTA DFU should be added first if it exists
70+
bledfu.begin();
71+
72+
// Set up and start advertising
73+
// Advertising packet
74+
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
75+
Bluefruit.Advertising.addTxPower();
76+
Bluefruit.Advertising.addName();
77+
78+
/* Start Advertising
79+
- Enable auto advertising if disconnected
80+
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
81+
- Timeout for fast mode is 30 seconds
82+
- Start(timeout) with timeout = 0 will advertise forever (until connected)
83+
84+
For recommended advertising interval
85+
https://developer.apple.com/library/content/qa/qa1931/_index.html
86+
*/
87+
Bluefruit.Advertising.restartOnDisconnect(true);
88+
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
89+
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
90+
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
91+
92+
strcpy(reply, "OK - started");
93+
return true;
94+
95+
96+
return false;
97+
}
98+
99+
#endif
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#pragma once
2+
3+
#include <MeshCore.h>
4+
#include <Arduino.h>
5+
6+
#ifdef XIAO_NRF52
7+
8+
// redefine lora pins if using the S3 variant of SX1262 board
9+
#ifdef SX1262_XIAO_S3_VARIANT
10+
#undef P_LORA_DIO_1
11+
#undef P_LORA_BUSY
12+
#undef P_LORA_RESET
13+
#undef P_LORA_NSS
14+
#undef SX126X_RXEN
15+
#define P_LORA_DIO_1 D0
16+
#define P_LORA_BUSY D1
17+
#define P_LORA_RESET D2
18+
#define P_LORA_NSS D3
19+
#define SX126X_RXEN D4
20+
#endif
21+
22+
class ikoka_stick_nrf_board : public mesh::MainBoard {
23+
protected:
24+
uint8_t startup_reason;
25+
26+
public:
27+
void begin();
28+
uint8_t getStartupReason() const override { return startup_reason; }
29+
30+
#if defined(P_LORA_TX_LED)
31+
void onBeforeTransmit() override {
32+
digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on
33+
}
34+
void onAfterTransmit() override {
35+
digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off
36+
}
37+
#endif
38+
39+
uint16_t getBattMilliVolts() override {
40+
// Please read befor going further ;)
41+
// https://wiki.seeedstudio.com/XIAO_BLE#q3-what-are-the-considerations-when-using-xiao-nrf52840-sense-for-battery-charging
42+
43+
// We can't drive VBAT_ENABLE to HIGH as long
44+
// as we don't know wether we are charging or not ...
45+
// this is a 3mA loss (4/1500)
46+
digitalWrite(VBAT_ENABLE, LOW);
47+
int adcvalue = 0;
48+
analogReadResolution(12);
49+
analogReference(AR_INTERNAL_3_0);
50+
delay(10);
51+
adcvalue = analogRead(PIN_VBAT);
52+
return (adcvalue * ADC_MULTIPLIER * AREF_VOLTAGE) / 4.096;
53+
}
54+
55+
const char* getManufacturerName() const override {
56+
return "Ikoka Stick (Xiao-nrf52)";
57+
}
58+
59+
void reboot() override {
60+
NVIC_SystemReset();
61+
}
62+
63+
bool startOTAUpdate(const char* id, char reply[]) override;
64+
};
65+
66+
#endif
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
[nrf52840_xiao]
2+
extends = nrf52_base
3+
platform_packages =
4+
toolchain-gccarmnoneeabi@~1.100301.0
5+
framework-arduinoadafruitnrf52
6+
board = seeed-xiao-afruitnrf52-nrf52840
7+
board_build.ldscript = boards/nrf52840_s140_v7.ld
8+
build_flags = ${nrf52_base.build_flags}
9+
-D NRF52_PLATFORM -D XIAO_NRF52
10+
-I lib/nrf52/s140_nrf52_7.3.0_API/include
11+
-I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52
12+
lib_ignore =
13+
BluetoothOTA
14+
lvgl
15+
lib5b4
16+
lib_deps =
17+
${nrf52_base.lib_deps}
18+
rweather/Crypto @ ^0.4.0
19+
adafruit/Adafruit INA3221 Library @ ^1.0.1
20+
adafruit/Adafruit INA219 @ ^1.2.3
21+
adafruit/Adafruit AHTX0 @ ^2.0.5
22+
adafruit/Adafruit BME280 Library @ ^2.3.0
23+
adafruit/Adafruit SSD1306 @ ^2.5.13
24+
25+
26+
[ikoka_stick_nrf]
27+
extends = nrf52840_xiao
28+
;board_build.ldscript = boards/nrf52840_s140_v7.ld
29+
build_flags = ${nrf52840_xiao.build_flags}
30+
-D P_LORA_TX_LED=11
31+
-I variants/ikoka_stick_nrf
32+
-I src/helpers/nrf52
33+
-D DISPLAY_CLASS=SSD1306Display
34+
-D DISPLAY_ROTATION=2
35+
-D RADIO_CLASS=CustomSX1262
36+
-D WRAPPER_CLASS=CustomSX1262Wrapper
37+
-D LORA_TX_POWER=9
38+
-D P_LORA_DIO_1=D1
39+
-D P_LORA_RESET=D2
40+
-D P_LORA_BUSY=D3
41+
-D P_LORA_NSS=D4
42+
-D SX126X_RXEN=D5
43+
-D SX126X_TXEN=RADIOLIB_NC
44+
-D SX126X_DIO2_AS_RF_SWITCH=1
45+
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
46+
-D SX126X_CURRENT_LIMIT=140
47+
-D SX126X_RX_BOOSTED_GAIN=1
48+
-D PIN_USER_BTN=0
49+
-D PIN_WIRE_SCL=7
50+
-D PIN_WIRE_SDA=6
51+
-D ENV_INCLUDE_AHTX0=1
52+
-D ENV_INCLUDE_BME280=1
53+
-D ENV_INCLUDE_INA3221=1
54+
-D ENV_INCLUDE_INA219=1
55+
build_src_filter = ${nrf52840_xiao.build_src_filter}
56+
+<helpers/*.cpp>
57+
+<helpers/sensors>
58+
+<helpers/ui/SSD1306Display.cpp>
59+
+<../variants/ikoka_stick_nrf>
60+
debug_tool = jlink
61+
upload_protocol = nrfutil
62+
63+
[env:ikoka_stick_nrf_companion_radio_ble]
64+
extends = ikoka_stick_nrf
65+
build_flags =
66+
${ikoka_stick_nrf.build_flags}
67+
-D MAX_CONTACTS=100
68+
-D MAX_GROUP_CHANNELS=8
69+
-D BLE_PIN_CODE=123456
70+
-D OFFLINE_QUEUE_SIZE=256
71+
; -D BLE_DEBUG_LOGGING=1
72+
; -D MESH_PACKET_LOGGING=1
73+
; -D MESH_DEBUG=1
74+
build_src_filter = ${ikoka_stick_nrf.build_src_filter}
75+
+<helpers/nrf52/SerialBLEInterface.cpp>
76+
+<../examples/companion_radio>
77+
lib_deps =
78+
${ikoka_stick_nrf.lib_deps}
79+
densaugeo/base64 @ ~1.4.0
80+
81+
[env:ikoka_stick_nrf_companion_radio_usb]
82+
extends = ikoka_stick_nrf
83+
build_flags =
84+
${ikoka_stick_nrf.build_flags}
85+
-D MAX_CONTACTS=100
86+
-D MAX_GROUP_CHANNELS=8
87+
; -D MESH_PACKET_LOGGING=1
88+
; -D MESH_DEBUG=1
89+
build_src_filter = ${ikoka_stick_nrf.build_src_filter}
90+
+<helpers/nrf52/SerialBLEInterface.cpp>
91+
+<../examples/companion_radio>
92+
lib_deps =
93+
${ikoka_stick_nrf.lib_deps}
94+
densaugeo/base64 @ ~1.4.0
95+
96+
[env:ikoka_stick_nrf_alt_pinout_companion_radio_ble]
97+
extends = env:ikoka_stick_nrf_companion_radio_ble
98+
build_flags =
99+
${env:ikoka_stick_nrf_companion_radio_ble.build_flags}
100+
-D SX1262_XIAO_S3_VARIANT
101+
102+
[env:ikoka_stick_nrf_repeater]
103+
extends = ikoka_stick_nrf
104+
build_flags =
105+
${ikoka_stick_nrf.build_flags}
106+
-D ADVERT_NAME='"Ikoka Stick Repeater"'
107+
-D ADVERT_LAT=0.0
108+
-D ADVERT_LON=0.0
109+
-D ADMIN_PASSWORD='"password"'
110+
-D MAX_NEIGHBOURS=8
111+
; -D MESH_PACKET_LOGGING=1
112+
; -D MESH_DEBUG=1
113+
build_src_filter = ${ikoka_stick_nrf.build_src_filter}
114+
+<../examples/simple_repeater/main.cpp>
115+
116+
[env:ikoka_stick_nrf_alt_pinout_repeater]
117+
extends = env:ikoka_stick_nrf_repeater
118+
build_flags =
119+
${env:ikoka_stick_nrf_repeater.build_flags}
120+
-D SX1262_XIAO_S3_VARIANT
121+
122+
[env:ikoka_stick_nrf_room_server]
123+
extends = ikoka_stick_nrf
124+
build_flags =
125+
${ikoka_stick_nrf.build_flags}
126+
-D ADVERT_NAME='"Ikoka Stick Room"'
127+
-D ADVERT_LAT=0.0
128+
-D ADVERT_LON=0.0
129+
-D ADMIN_PASSWORD='"password"'
130+
; -D MESH_PACKET_LOGGING=1
131+
; -D MESH_DEBUG=1
132+
build_src_filter = ${ikoka_stick_nrf.build_src_filter}
133+
+<../examples/simple_room_server/main.cpp>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <Arduino.h>
2+
#include "target.h"
3+
#include <helpers/ArduinoHelpers.h>
4+
5+
ikoka_stick_nrf_board board;
6+
7+
#ifdef DISPLAY_CLASS
8+
DISPLAY_CLASS display;
9+
#endif
10+
11+
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
12+
13+
WRAPPER_CLASS radio_driver(radio, board);
14+
15+
VolatileRTCClock fallback_clock;
16+
AutoDiscoverRTCClock rtc_clock(fallback_clock);
17+
EnvironmentSensorManager sensors;
18+
19+
bool radio_init() {
20+
rtc_clock.begin(Wire);
21+
22+
return radio.std_init(&SPI);
23+
}
24+
25+
uint32_t radio_get_rng_seed() {
26+
return radio.random(0x7FFFFFFF);
27+
}
28+
29+
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
30+
radio.setFrequency(freq);
31+
radio.setSpreadingFactor(sf);
32+
radio.setBandwidth(bw);
33+
radio.setCodingRate(cr);
34+
}
35+
36+
void radio_set_tx_power(uint8_t dbm) {
37+
radio.setOutputPower(dbm);
38+
}
39+
40+
mesh::LocalIdentity radio_new_identity() {
41+
RadioNoiseListener rng(radio);
42+
return mesh::LocalIdentity(&rng); // create new random identity
43+
}

variants/ikoka_stick_nrf/target.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#define RADIOLIB_STATIC_ONLY 1
4+
#include <RadioLib.h>
5+
#include <helpers/radiolib/RadioLibWrappers.h>
6+
#include <ikoka_stick_nrf_board.h>
7+
#include <helpers/radiolib/CustomSX1262Wrapper.h>
8+
#include <helpers/AutoDiscoverRTCClock.h>
9+
#include <helpers/ArduinoHelpers.h>
10+
#include <helpers/sensors/EnvironmentSensorManager.h>
11+
12+
#ifdef DISPLAY_CLASS
13+
#include <helpers/ui/SSD1306Display.h>
14+
extern DISPLAY_CLASS display;
15+
#endif
16+
17+
extern ikoka_stick_nrf_board board;
18+
extern WRAPPER_CLASS radio_driver;
19+
extern AutoDiscoverRTCClock rtc_clock;
20+
extern EnvironmentSensorManager sensors;
21+
22+
bool radio_init();
23+
uint32_t radio_get_rng_seed();
24+
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
25+
void radio_set_tx_power(uint8_t dbm);
26+
mesh::LocalIdentity radio_new_identity();

0 commit comments

Comments
 (0)