add MML tones

This commit is contained in:
my 2026-09-15 15:20:29 +02:00
parent ac544c1790
commit 8b03bc5e84
8 changed files with 72 additions and 28 deletions

View file

@ -1,10 +1,8 @@
#nick=your nick, picked randomly on first run #nick=your nick, picked randomly on first run
brightness=200 brightness=200
msg_freq=1800 tones_msg=T300 O4 l8 D l4 A+
msg_ms=20 tones_sys=T300 O4 l8 A l4 D+
sys_freq=440
sys_ms=10
touch_threshold=300 touch_threshold=300

View file

@ -18,16 +18,21 @@ board_build.filesystem = littlefs
monitor_speed = 115200 monitor_speed = 115200
upload_speed = 921600 upload_speed = 921600
;monitor_filters = send_on_enter ;monitor_filters = send_on_enter
build_src_filter = +<meshberry/> -<chat-relay/> build_src_filter =
+<meshberry/>
-<chat-relay/>
lib_deps = lib_deps =
lovyan03/LovyanGFX @ ^1.2 lovyan03/LovyanGFX @ ^1.2
lvgl/lvgl @ ^9.0 lvgl/lvgl @ ^9.0
; t-vk/ESP32 BLE Keyboard@^0.3.2 ; t-vk/ESP32 BLE Keyboard@^0.3.2
https://github.com/sakul-the-one/sakuls-ESP32-BLE-Keyboard https://github.com/sakul-the-one/sakuls-ESP32-BLE-Keyboard
h2zero/NimBLE-Arduino h2zero/NimBLE-Arduino
https://github.com/maxint-rd/MmlMusic
build_flags = build_flags =
-DLV_CONF_INCLUDE_SIMPLE -DLV_CONF_INCLUDE_SIMPLE
-I include -I include
; use strchr instead of strchr_P
-Dstrchr_P=strchr
; We only need HID peripheral from NimBLE ; We only need HID peripheral from NimBLE
; saving some memory here. ; saving some memory here.
-DCONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED -DCONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
@ -39,7 +44,9 @@ board = esp32dev
framework = arduino framework = arduino
monitor_speed = 115200 monitor_speed = 115200
upload_speed = 921600 upload_speed = 921600
build_src_filter = -<meshberry/> +<chat-relay/> ; ignores root build_src_filter =
-<meshberry/>
+<chat-relay/> ; ignores meshberry
lib_deps = lib_deps =
build_flags = build_flags =
-I include -I include

View file

@ -403,8 +403,14 @@ void handleCommand(const char* in) {
ui_push_message("", "Usage: /set color_bg ff0000", false, true); ui_push_message("", "Usage: /set color_bg ff0000", false, true);
} else { } else {
*sp = '\0'; *sp = '\0';
if (settings_set_color(args, sp + 1)) size_t klen = strlen(args);
bool is_tones = klen > 6 && !strcmp(args + klen - 6, "_tones");
bool ok = is_tones ? settings_set_tones(args, sp + 1)
: settings_set_color(args, sp + 1);
if (ok)
ui_push_message("", "Saved. Restart to apply.", false, true); ui_push_message("", "Saved. Restart to apply.", false, true);
else if (is_tones)
ui_push_message("", "Unknown tones key.", false, true);
else else
ui_push_message("", "Unknown color or bad hex value.", false, true); ui_push_message("", "Unknown color or bad hex value.", false, true);
} }
@ -435,7 +441,7 @@ void handleCommand(const char* in) {
ui_push_message("", "/bt : toggle BT send mode", false, true); ui_push_message("", "/bt : toggle BT send mode", false, true);
ui_push_message("", "/set color_bg ff0000 : set a color (restart to apply)", false, true); ui_push_message("", "/set color_bg ff0000 : set a color (restart to apply)", false, true);
ui_push_message("", "/people : list connected", false, true); ui_push_message("", "/people : list connected", false, true);
ui_push_message("", "/sleep : sleep device", false, true); ui_push_message("", "/sleep or /s : sleep device", false, true);
ui_push_message("", "TAB : notes mode", false, true); ui_push_message("", "TAB : notes mode", false, true);
//drawChatWindow(); //drawChatWindow();

View file

@ -45,5 +45,6 @@ void loop() {
char key = Serial.available() ? (char)Serial.read() : cardkb_read(); char key = Serial.available() ? (char)Serial.read() : cardkb_read();
chat_tick(key); // TODO: debug why a serial key does not show in the UI? chat_tick(key); // TODO: debug why a serial key does not show in the UI?
ui_tick(); ui_tick();
sound_tick();
delay(5); delay(5);
} }

View file

@ -1,16 +1,15 @@
#include "settings.h" #include "settings.h"
#include <Arduino.h> #include <Arduino.h>
#include <LittleFS.h> #include <LittleFS.h>
#include <string>
settings_t g_settings; settings_t g_settings;
static const settings_t DEFAULTS = { static const settings_t DEFAULTS = {
.nick = NICK_DEFAULT_PREFIX, .nick = NICK_DEFAULT_PREFIX,
.brightness = 200, .brightness = 200,
.msg_freq = 1800, .tones_msg = "T140 G",
.msg_ms = 20, .tones_sys = "T140 A",
.sys_freq = 440,
.sys_ms = 10,
.touch_threshold = TOUCH_THRESHOLD, .touch_threshold = TOUCH_THRESHOLD,
.color_bg = {.blue = 0x11, .green = 0x11, .red = 0x11}, .color_bg = {.blue = 0x11, .green = 0x11, .red = 0x11},
.color_header_bg = {.blue = 0x1e, .green = 0x1e, .red = 0x1e}, .color_header_bg = {.blue = 0x1e, .green = 0x1e, .red = 0x1e},
@ -43,10 +42,8 @@ static bool is_hex6(const char* s) {
static bool apply_kv(const char* key, const char* val) { static bool apply_kv(const char* key, const char* val) {
if (!strcmp(key, "nick")) { strncpy(g_settings.nick, val, CHAT_MAX_NICK_LEN - 1); } if (!strcmp(key, "nick")) { strncpy(g_settings.nick, val, CHAT_MAX_NICK_LEN - 1); }
else if (!strcmp(key, "brightness")) { g_settings.brightness = (uint8_t)constrain(atoi(val), 0, 255); } else if (!strcmp(key, "brightness")) { g_settings.brightness = (uint8_t)constrain(atoi(val), 0, 255); }
else if (!strcmp(key, "msg_freq")) { g_settings.msg_freq = (uint16_t)atoi(val); } else if (!strcmp(key, "tones_msg")) { g_settings.tones_msg = String(val); }
else if (!strcmp(key, "msg_ms")) { g_settings.msg_ms = (uint16_t)atoi(val); } else if (!strcmp(key, "tones_sys")) { g_settings.tones_sys = String(val); }
else if (!strcmp(key, "sys_freq")) { g_settings.sys_freq = (uint16_t)atoi(val); }
else if (!strcmp(key, "sys_ms")) { g_settings.sys_ms = (uint16_t)atoi(val); }
else if (!strcmp(key, "touch_threshold")) { g_settings.touch_threshold = (uint16_t)atoi(val); } else if (!strcmp(key, "touch_threshold")) { g_settings.touch_threshold = (uint16_t)atoi(val); }
else if (!strcmp(key, "color_bg")) { if (!is_hex6(val)) return false; g_settings.color_bg = parse_color(val); } else if (!strcmp(key, "color_bg")) { if (!is_hex6(val)) return false; g_settings.color_bg = parse_color(val); }
else if (!strcmp(key, "color_header_bg")) { if (!is_hex6(val)) return false; g_settings.color_header_bg = parse_color(val); } else if (!strcmp(key, "color_header_bg")) { if (!is_hex6(val)) return false; g_settings.color_header_bg = parse_color(val); }
@ -60,12 +57,12 @@ static bool apply_kv(const char* key, const char* val) {
} }
static void dump_settings() { static void dump_settings() {
Serial.println("[settings] ---- effective config ----"); Serial.println("[settings]");
Serial.printf ("[settings] nick=%s brightness=%d\n", Serial.printf ("[settings] nick=%s brightness=%d\n",
g_settings.nick, g_settings.brightness); g_settings.nick, g_settings.brightness);
Serial.printf ("[settings] msg_freq=%u msg_ms=%u sys_freq=%u sys_ms=%u touch_threshold=%u\n", Serial.printf ("[settings] tones_msg=%s tones_sys=%s\n",
g_settings.msg_freq, g_settings.msg_ms, g_settings.sys_freq, g_settings.tones_msg.c_str(), g_settings.tones_sys.c_str());
g_settings.sys_ms, g_settings.touch_threshold); Serial.printf ("[settings] touch_threshold=%u\n", g_settings.touch_threshold);
Serial.printf ("[settings] color_bg=%02x%02x%02x color_header_bg=%02x%02x%02x\n", Serial.printf ("[settings] color_bg=%02x%02x%02x color_header_bg=%02x%02x%02x\n",
g_settings.color_bg.red, g_settings.color_bg.green, g_settings.color_bg.blue, g_settings.color_bg.red, g_settings.color_bg.green, g_settings.color_bg.blue,
g_settings.color_header_bg.red, g_settings.color_header_bg.green, g_settings.color_header_bg.blue); g_settings.color_header_bg.red, g_settings.color_header_bg.green, g_settings.color_header_bg.blue);
@ -76,7 +73,7 @@ static void dump_settings() {
Serial.printf ("[settings] color_system=%02x%02x%02x color_input_prompt=%02x%02x%02x\n", Serial.printf ("[settings] color_system=%02x%02x%02x color_input_prompt=%02x%02x%02x\n",
g_settings.color_system.red, g_settings.color_system.green, g_settings.color_system.blue, g_settings.color_system.red, g_settings.color_system.green, g_settings.color_system.blue,
g_settings.color_input_prompt.red, g_settings.color_input_prompt.green, g_settings.color_input_prompt.blue); g_settings.color_input_prompt.red, g_settings.color_input_prompt.green, g_settings.color_input_prompt.blue);
Serial.println("[settings] ----------------------------"); Serial.println("[settings]");
} }
// Public // Public
@ -170,3 +167,13 @@ bool settings_set_color(const char* key, const char* hex) {
rewrite_config_line(key, hex); rewrite_config_line(key, hex);
return true; return true;
} }
bool settings_set_tones(const char* key, const char* tones) {
if (strlen(key) <= 6 || strcmp(key + strlen(key) - 6, "_tones") != 0) {
return false;
}
if (!apply_kv(key, tones)) return false; // unknown key or bad tones
rewrite_config_line(key, tones);
return true;
}

View file

@ -1,16 +1,15 @@
#pragma once #pragma once
#include <lvgl.h> #include <lvgl.h>
#include "config.h" #include "config.h"
#include <Arduino.h>
// All values read from /config.txt on LittleFS. // All values read from /config.txt on LittleFS.
struct settings_t { struct settings_t {
char nick[CHAT_MAX_NICK_LEN]; char nick[CHAT_MAX_NICK_LEN];
uint8_t brightness; uint8_t brightness;
uint16_t msg_freq; // Hz, notification beep for incoming messages String tones_msg; // MML string for incoming message
uint16_t msg_ms; // duration in ms String tones_sys; // MML string for system message
uint16_t sys_freq; // Hz, beep for join / leave events uint16_t touch_threshold; // touch7 (GPIO27) wake-from-sleep sensitivity
uint16_t sys_ms; // duration in ms
uint16_t touch_threshold; // touch7 (GPIO27) wake-from-sleep sensitivity
lv_color_t color_bg; lv_color_t color_bg;
lv_color_t color_header_bg; lv_color_t color_header_bg;
lv_color_t color_msg; lv_color_t color_msg;
@ -26,3 +25,4 @@ extern settings_t g_settings;
void settings_load(); void settings_load();
void settings_set_nick(const char* nick); void settings_set_nick(const char* nick);
bool settings_set_color(const char* key, const char* hex); bool settings_set_color(const char* key, const char* hex);
bool settings_set_tones(const char* key, const char* tones);

View file

@ -2,11 +2,35 @@
#include "config.h" #include "config.h"
#include "settings.h" #include "settings.h"
#include <Arduino.h> #include <Arduino.h>
#include <MmlMusic.h>
MmlMusic music;
static bool sound_tone_callback(unsigned int frequency, unsigned long length, uint8_t nTrack, uint8_t nVolume) {
if (frequency > 0) {
tone(PIN_SPEAKER, frequency);
} else if (length == 0) {
noTone(PIN_SPEAKER);
}
return true;
}
void sound_init() { void sound_init() {
pinMode(PIN_SPEAKER_EN, OUTPUT); pinMode(PIN_SPEAKER_EN, OUTPUT);
digitalWrite(PIN_SPEAKER_EN, LOW); // LOW = amp enabled digitalWrite(PIN_SPEAKER_EN, LOW); // LOW = amp enabled
music.setToneCallback(sound_tone_callback);
} }
void sound_msg_in() { tone(PIN_SPEAKER, g_settings.msg_freq, g_settings.msg_ms); } void sound_tick() {
void sound_system() { tone(PIN_SPEAKER, g_settings.sys_freq, g_settings.sys_ms); } music.continuePlaying();
}
void sound_msg_in() {
//tone(PIN_SPEAKER, g_settings.snd_msg_freq, g_settings.snd_msg_ms);
//music.play("T140 O5 O5 D L4 G L8 D G L4 B. G8 G. F#8 G. G#8 A2");
music.play(g_settings.tones_msg.c_str());
}
void sound_system() {
// tone(PIN_SPEAKER, g_settings.snd_sys_freq, g_settings.snd_sys_ms);
music.play(g_settings.tones_sys.c_str());
}

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
void sound_init(); void sound_init();
void sound_tick(); // call regularly from loop() to advance MML playback
void sound_msg_in(); // short high beep, new chat message void sound_msg_in(); // short high beep, new chat message
void sound_system(); // short low beep , join / leave void sound_system(); // short low beep , join / leave