Added support for the T-Deck internal speaker! Boot sounds added, notification sounds incomming.
This commit is contained in:
parent
e991577a77
commit
86b4488d92
@ -51,7 +51,7 @@ The device will scan for WiFi networks on boot. Once the list is displayed, you
|
|||||||
- [X] Screen timeout on inactivity *(default 30 seconds)*
|
- [X] Screen timeout on inactivity *(default 30 seconds)*
|
||||||
- [ ] Keyboard backlight timeout with screen timeout
|
- [ ] Keyboard backlight timeout with screen timeout
|
||||||
- [ ] Trackball support
|
- [ ] Trackball support
|
||||||
- [ ] Speaker support
|
- [X] Speaker support
|
||||||
- [ ] GPS support
|
- [ ] GPS support
|
||||||
- [ ] Lora support
|
- [ ] Lora support
|
||||||
- [ ] BLE support
|
- [ ] BLE support
|
||||||
|
@ -24,6 +24,4 @@ build_flags =
|
|||||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
-DDISABLE_ALL_LIBRARY_WARNINGS
|
-DDISABLE_ALL_LIBRARY_WARNINGS
|
||||||
lib_deps =
|
lib_deps =
|
||||||
;mikalhart/TinyGPSPlus@^1.0.2
|
lib_extra_dirs = lib
|
||||||
marian-craciunescu/ESP32Ping@^1.7.0
|
|
||||||
;sandeepmistry/LoRa
|
|
151
src/Speaker.h
Normal file
151
src/Speaker.h
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <driver/i2s.h>
|
||||||
|
|
||||||
|
#include "pins.h"
|
||||||
|
|
||||||
|
#define BOARD_I2S_PORT I2S_NUM_0
|
||||||
|
#define SAMPLE_RATE 44100
|
||||||
|
|
||||||
|
|
||||||
|
const float NOTE_FREQS[] = {
|
||||||
|
261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88, // C4 to B4
|
||||||
|
523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77, // C5 to B5
|
||||||
|
1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1480.00, 1568.00, 1661.22, 1760.00, 1864.66, 1975.53, // C6 to B6
|
||||||
|
2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2960.00, 3136.00, 3322.44, 3520.00, 3729.31, 3951.07 // C7 to B7
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
float getNoteFrequency(char note, int octave) {
|
||||||
|
if (note == 'p') return 0; // Pause
|
||||||
|
int noteIndex = 0;
|
||||||
|
switch (note) {
|
||||||
|
case 'c': noteIndex = 0; break;
|
||||||
|
case 'd': noteIndex = 2; break;
|
||||||
|
case 'e': noteIndex = 4; break;
|
||||||
|
case 'f': noteIndex = 5; break;
|
||||||
|
case 'g': noteIndex = 7; break;
|
||||||
|
case 'a': noteIndex = 9; break;
|
||||||
|
case 'b': noteIndex = 11; break;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
return NOTE_FREQS[noteIndex + ((octave - 4) * 12)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setupI2S() {
|
||||||
|
i2s_config_t i2s_config = {
|
||||||
|
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
|
||||||
|
.sample_rate = SAMPLE_RATE,
|
||||||
|
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||||
|
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||||
|
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||||
|
.intr_alloc_flags = 0, // Default interrupt priority
|
||||||
|
.dma_buf_count = 8,
|
||||||
|
.dma_buf_len = 64,
|
||||||
|
.use_apll = false,
|
||||||
|
.tx_desc_auto_clear = true,
|
||||||
|
.fixed_mclk = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
i2s_pin_config_t pin_config = {
|
||||||
|
.bck_io_num = BOARD_I2S_BCK,
|
||||||
|
.ws_io_num = BOARD_I2S_WS,
|
||||||
|
.data_out_num = BOARD_I2S_DOUT,
|
||||||
|
.data_in_num = I2S_PIN_NO_CHANGE
|
||||||
|
};
|
||||||
|
|
||||||
|
i2s_driver_install(BOARD_I2S_PORT, &i2s_config, 0, NULL);
|
||||||
|
i2s_set_pin(BOARD_I2S_PORT, &pin_config);
|
||||||
|
i2s_set_clk(BOARD_I2S_PORT, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void playTone(float frequency, int duration, int volume = 16383) {
|
||||||
|
volume = constrain(volume, 0, 32767); // Max volume is 32767, we default to half volume if not specified
|
||||||
|
const int wave_period = SAMPLE_RATE / frequency;
|
||||||
|
int16_t sample_buffer[wave_period];
|
||||||
|
|
||||||
|
for (int i = 0; i < wave_period; ++i)
|
||||||
|
sample_buffer[i] = (i < wave_period / 2) ? volume : -volume;
|
||||||
|
|
||||||
|
int total_samples = SAMPLE_RATE * duration / 1000;
|
||||||
|
int samples_written = 0;
|
||||||
|
|
||||||
|
while (samples_written < total_samples) {
|
||||||
|
int to_write = min(wave_period, total_samples - samples_written);
|
||||||
|
i2s_write(BOARD_I2S_PORT, sample_buffer, to_write * sizeof(int16_t), (size_t *)&to_write, portMAX_DELAY);
|
||||||
|
samples_written += to_write;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void playRTTTL(const char* rtttl, int volume = 16383, int bpm = -1) {
|
||||||
|
int default_duration = 4;
|
||||||
|
int default_octave = 6;
|
||||||
|
int internal_bpm = 63;
|
||||||
|
|
||||||
|
const char* p = rtttl;
|
||||||
|
|
||||||
|
// Skip name
|
||||||
|
while (*p && *p != ':') p++;
|
||||||
|
if (*p == ':') p++;
|
||||||
|
|
||||||
|
while (*p && *p != ':') {
|
||||||
|
char param = *p++;
|
||||||
|
if (*p == '=') p++;
|
||||||
|
int value = atoi(p);
|
||||||
|
while (*p && isdigit(*p)) p++;
|
||||||
|
if (*p == ',') p++;
|
||||||
|
switch (param) {
|
||||||
|
case 'd': default_duration = value; break;
|
||||||
|
case 'o': default_octave = value; break;
|
||||||
|
case 'b': internal_bpm = value; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == ':') p++;
|
||||||
|
|
||||||
|
if (bpm != -1)
|
||||||
|
internal_bpm = bpm;
|
||||||
|
|
||||||
|
int beat_duration = 60000 / internal_bpm;
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
int duration = 0;
|
||||||
|
if (isdigit(*p)) {
|
||||||
|
duration = atoi(p);
|
||||||
|
while (isdigit(*p)) p++;
|
||||||
|
} else {
|
||||||
|
duration = default_duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
char note = *p++;
|
||||||
|
int frequency = getNoteFrequency(note, default_octave);
|
||||||
|
|
||||||
|
if (*p == '#') {
|
||||||
|
frequency = getNoteFrequency(note + 1, default_octave);
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int octave = default_octave;
|
||||||
|
|
||||||
|
if (isdigit(*p))
|
||||||
|
octave = *p++ - '0';
|
||||||
|
|
||||||
|
if (*p == '.') {
|
||||||
|
duration = duration * 1.5;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int note_duration = (beat_duration * 4) / duration;
|
||||||
|
|
||||||
|
if (frequency > 0)
|
||||||
|
playTone(frequency, note_duration, volume);
|
||||||
|
else
|
||||||
|
delay(note_duration);
|
||||||
|
|
||||||
|
if (*p == ',') p++;
|
||||||
|
}
|
||||||
|
}
|
10
src/main.ino
10
src/main.ino
@ -14,8 +14,9 @@
|
|||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
|
||||||
// Local includes
|
// Local includes
|
||||||
#include "boot_screen.h"
|
#include "bootScreen.h"
|
||||||
#include "pins.h"
|
#include "pins.h"
|
||||||
|
#include "Speaker.h"
|
||||||
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
@ -81,7 +82,6 @@ bool screenOn = true;
|
|||||||
int selectedNetworkIndex = 0;
|
int selectedNetworkIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Main functions ---------------------------------------------------------------------------------
|
// Main functions ---------------------------------------------------------------------------------
|
||||||
void displayXBM() {
|
void displayXBM() {
|
||||||
tft.fillScreen(TFT_BLACK);
|
tft.fillScreen(TFT_BLACK);
|
||||||
@ -113,6 +113,7 @@ void displayXBM() {
|
|||||||
void setup() {
|
void setup() {
|
||||||
// Initialize serial communication
|
// Initialize serial communication
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
while (!Serial);
|
||||||
Serial.println("Booting device...");
|
Serial.println("Booting device...");
|
||||||
|
|
||||||
// Turn on the power to the board
|
// Turn on the power to the board
|
||||||
@ -132,6 +133,11 @@ void setup() {
|
|||||||
tft.invertDisplay(1);
|
tft.invertDisplay(1);
|
||||||
Serial.println("TFT initialized");
|
Serial.println("TFT initialized");
|
||||||
|
|
||||||
|
// Initialize the speaker
|
||||||
|
setupI2S(); // Do we want to keep this open or uninstall after each use to keep resources free?
|
||||||
|
const char* rtttl_boot = "ff6_victory:d=4,o=5,b=100:32d6,32p,32d6,32p,32d6,32p,d6,a#,c6,16d6,8p,16c6,2d6"; // This will go in preferences soon
|
||||||
|
playRTTTL(rtttl_boot);
|
||||||
|
|
||||||
// Display the boot screen
|
// Display the boot screen
|
||||||
displayXBM();
|
displayXBM();
|
||||||
|
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
// Board pin definitions ------------------------
|
// Board pin definitions ------------------------
|
||||||
#define BOARD_POWERON 10
|
#define BOARD_POWERON 10
|
||||||
|
|
||||||
|
// Speaker
|
||||||
#define BOARD_I2S_WS 5
|
#define BOARD_I2S_WS 5
|
||||||
#define BOARD_I2S_BCK 7
|
|
||||||
#define BOARD_I2S_DOUT 6
|
#define BOARD_I2S_DOUT 6
|
||||||
|
#define BOARD_I2S_BCK 7
|
||||||
|
|
||||||
#define BOARD_I2C_SDA 18
|
#define BOARD_I2C_SDA 18
|
||||||
#define BOARD_I2C_SCL 8
|
#define BOARD_I2C_SCL 8
|
||||||
@ -58,4 +59,3 @@
|
|||||||
#define READS 20 // Number of readings for averaging
|
#define READS 20 // Number of readings for averaging
|
||||||
|
|
||||||
#define LILYGO_KB_SLAVE_ADDRESS 0x55
|
#define LILYGO_KB_SLAVE_ADDRESS 0x55
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user