2025-05-20 13:26:40 +12:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <Arduino.h>
|
|
|
|
|
|
Merge upstream/main (v1.16.0) into wio-unified
Merge 254 upstream commits. Highlights relevant to the Wio fork: native
NRF52 companion power-saving (sleep when !hasPendingWork), preamble 16->32
for SF<9, generic sensor-registration model, CMD_SEND_RAW_PACKET, isEink()
display abstraction, KEEP_DISPLAY_ON_USB, contacts-sync/transient fixes.
Conflicts resolved (11 files):
- buzzer.h/.cpp: keep NRF52 include guard + our begin()->startup(); fold in
upstream doc comment.
- platformio (wio + eink): keep our slim env layout, add upstream kiss_modem
env (+ debug_tool=stlink on eink ble).
- GxEPDDisplay.h: take upstream isEink() override.
- EnvironmentSensorManager.h: take upstream's wider #if guard.
- DataStore.cpp: take upstream saveContacts(filter) signature, keep our
::openWrite (member openWrite would shadow the global 2-arg overload).
- main.cpp: keep our watchdog init + add board.onBootComplete(); adopt
upstream's `if(!hasPendingWork()) sleep` loop.
- MyMesh.h: version -> v1.16-solo.0, build date 6 Jun 2026.
- MyMesh.cpp: keep both CMD codes (SCREENSHOT 66 + RAW_PACKET 65), both field
inits, both handlers, our screenshot fns + upstream saveContacts/save_filter,
upstream hasPendingWork() + our MyMeshBot include.
- UITask.cpp: keep our solo splash/clock page/snprintf/buzzer init; merge
auto-off (+ opt-in KEEP_DISPLAY_ON_USB); merge low-batt shutdown (our EMA +
prefs threshold + upstream's !isExternalPowered() guard and eink-aware delay).
Post-merge drift fixes (upstream API/architecture changes, not conflicts):
- applyTxPower(): radio_set_tx_power() removed upstream -> radio_driver.setTxPower().
- Sensor refactor dropped the per-sensor *_initialized flags our
getAvailableLPPTypes() relied on; removed that override and the SENSORS page
now derives available LPP types from the populated sensors_lpp buffer.
README kept ours via .gitattributes merge=ours. Builds verified on all 5 Wio
envs (dual, eink dual, radio_ble, dual_dev, eink_dual_dev). Not yet
hardware-verified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 17:21:41 +02:00
|
|
|
// NRF52 uses a custom non-blocking RTTTL player (see buzzer.cpp); only the
|
|
|
|
|
// other platforms pull in the NonBlockingRtttl library here.
|
2026-05-15 13:09:38 +02:00
|
|
|
#if !defined(NRF52_PLATFORM)
|
|
|
|
|
#include <NonBlockingRtttl.h>
|
|
|
|
|
#endif
|
2025-05-20 19:09:49 +12:00
|
|
|
|
Merge upstream/main (v1.16.0) into wio-unified
Merge 254 upstream commits. Highlights relevant to the Wio fork: native
NRF52 companion power-saving (sleep when !hasPendingWork), preamble 16->32
for SF<9, generic sensor-registration model, CMD_SEND_RAW_PACKET, isEink()
display abstraction, KEEP_DISPLAY_ON_USB, contacts-sync/transient fixes.
Conflicts resolved (11 files):
- buzzer.h/.cpp: keep NRF52 include guard + our begin()->startup(); fold in
upstream doc comment.
- platformio (wio + eink): keep our slim env layout, add upstream kiss_modem
env (+ debug_tool=stlink on eink ble).
- GxEPDDisplay.h: take upstream isEink() override.
- EnvironmentSensorManager.h: take upstream's wider #if guard.
- DataStore.cpp: take upstream saveContacts(filter) signature, keep our
::openWrite (member openWrite would shadow the global 2-arg overload).
- main.cpp: keep our watchdog init + add board.onBootComplete(); adopt
upstream's `if(!hasPendingWork()) sleep` loop.
- MyMesh.h: version -> v1.16-solo.0, build date 6 Jun 2026.
- MyMesh.cpp: keep both CMD codes (SCREENSHOT 66 + RAW_PACKET 65), both field
inits, both handlers, our screenshot fns + upstream saveContacts/save_filter,
upstream hasPendingWork() + our MyMeshBot include.
- UITask.cpp: keep our solo splash/clock page/snprintf/buzzer init; merge
auto-off (+ opt-in KEEP_DISPLAY_ON_USB); merge low-batt shutdown (our EMA +
prefs threshold + upstream's !isExternalPowered() guard and eink-aware delay).
Post-merge drift fixes (upstream API/architecture changes, not conflicts):
- applyTxPower(): radio_set_tx_power() removed upstream -> radio_driver.setTxPower().
- Sensor refactor dropped the per-sensor *_initialized flags our
getAvailableLPPTypes() relied on; removed that override and the SENSORS page
now derives available LPP types from the populated sensors_lpp buffer.
README kept ours via .gitattributes merge=ours. Builds verified on all 5 Wio
envs (dual, eink dual, radio_ble, dual_dev, eink_dual_dev). Not yet
hardware-verified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 17:21:41 +02:00
|
|
|
/* class abstracts underlying RTTTL library
|
2025-05-20 13:26:40 +12:00
|
|
|
|
2026-06-02 22:12:18 +02:00
|
|
|
Just a simple implementation to start. At the moment use same
|
2025-05-20 13:26:40 +12:00
|
|
|
melody for message and discovery
|
|
|
|
|
Suggest enum type for different sounds
|
|
|
|
|
- on message
|
|
|
|
|
- on discovery
|
|
|
|
|
|
|
|
|
|
TODO
|
|
|
|
|
- make message ring tone configurable
|
|
|
|
|
|
|
|
|
|
*/
|
2025-05-20 19:09:49 +12:00
|
|
|
|
2025-05-20 13:26:40 +12:00
|
|
|
class genericBuzzer
|
|
|
|
|
{
|
|
|
|
|
public:
|
2026-05-15 13:09:38 +02:00
|
|
|
void begin();
|
2026-05-11 21:17:31 +02:00
|
|
|
void play(const char *melody);
|
2026-05-15 13:09:38 +02:00
|
|
|
void playForced(const char *melody);
|
|
|
|
|
void loop();
|
|
|
|
|
void startup();
|
|
|
|
|
void shutdown();
|
|
|
|
|
bool isPlaying();
|
|
|
|
|
void quiet(bool buzzer_state);
|
|
|
|
|
bool isQuiet();
|
|
|
|
|
void setVolume(uint8_t level);
|
2026-05-12 09:17:37 +02:00
|
|
|
uint8_t getVolume() const { return _volume_level; }
|
2026-05-15 13:09:38 +02:00
|
|
|
void stop();
|
2025-05-20 13:26:40 +12:00
|
|
|
|
|
|
|
|
private:
|
2026-05-12 09:17:37 +02:00
|
|
|
uint8_t _volume_level = 4;
|
|
|
|
|
void applyVolume();
|
2026-05-15 13:09:38 +02:00
|
|
|
const char *startup_song = "Startup:d=4,o=5,b=160:16c6,16e6,8g6";
|
2025-05-20 13:26:40 +12:00
|
|
|
const char *shutdown_song = "Shutdown:d=4,o=5,b=100:8g5,16e5,16c5";
|
|
|
|
|
bool _is_quiet = true;
|
2026-05-15 13:09:38 +02:00
|
|
|
|
|
|
|
|
#if defined(NRF52_PLATFORM)
|
|
|
|
|
// Own RTTTL player — bypasses tone() to allow volume control from note start.
|
|
|
|
|
// tone() pre-computes seq_refresh so the DMA repeats 50% duty for ~30ms before
|
|
|
|
|
// re-reading its buffer; we cannot beat that timing. By owning NRF_PWM2 directly
|
|
|
|
|
// and setting REFRESH=0, DMA re-reads _duty_buf every period so duty takes effect
|
|
|
|
|
// immediately at SEQSTART.
|
|
|
|
|
volatile uint16_t _duty_buf = 0; // DMA source — must stay in RAM
|
|
|
|
|
const char* _rtttl_pos = nullptr;
|
|
|
|
|
bool _rtttl_done = true;
|
|
|
|
|
bool _pwm_on = false;
|
|
|
|
|
uint8_t _def_dur = 4;
|
|
|
|
|
uint8_t _def_oct = 5;
|
|
|
|
|
uint16_t _def_bpm = 120;
|
|
|
|
|
|
|
|
|
|
void _nrfBegin(const char* melody);
|
|
|
|
|
void _nrfAdvance();
|
|
|
|
|
void _nrfStartPwm(uint16_t freq);
|
|
|
|
|
void _nrfStopPwm();
|
|
|
|
|
uint8_t _dutyPct() const;
|
|
|
|
|
static uint16_t _noteFreq(char letter, bool sharp, uint8_t octave);
|
|
|
|
|
static bool _parseNext(const char*& pos, uint8_t def_dur, uint8_t def_oct,
|
|
|
|
|
uint16_t bpm, uint16_t& freq_hz, uint32_t& dur_ms);
|
|
|
|
|
static void _parseHeader(const char* melody, uint8_t& def_dur, uint8_t& def_oct,
|
|
|
|
|
uint16_t& bpm, const char*& notes_start);
|
2026-06-28 11:37:48 +02:00
|
|
|
|
|
|
|
|
// TIMER1-driven note advance: a hardware compare interrupt calls
|
|
|
|
|
// _nrfAdvance() at the exact moment the current note's duration ends,
|
|
|
|
|
// so playback timing survives a blocking display refresh (e-ink)
|
|
|
|
|
// instead of waiting for the next loop() poll. Same reasoning as
|
|
|
|
|
// MomentaryButton's GPIO-IRQ edge capture, just timer- rather than
|
|
|
|
|
// pin-driven. TIMER0 is reserved by the SoftDevice; TIMER1 is free.
|
|
|
|
|
void _armNoteTimer(uint32_t dur_ms);
|
|
|
|
|
void _disarmNoteTimer();
|
|
|
|
|
static genericBuzzer* _isr_instance;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Must be public: the extern "C" TIMER1_IRQHandler (see buzzer.cpp) is
|
|
|
|
|
// a free function — the vector table requires that exact symbol — so
|
|
|
|
|
// it needs access from outside the class to dispatch into it.
|
|
|
|
|
static void _timer1ISR();
|
2026-05-15 13:09:38 +02:00
|
|
|
#endif
|
2025-05-20 13:26:40 +12:00
|
|
|
};
|