Added HTTP-API support, Standalone AP Support and Built-in Melodies
This commit is contained in:
253
vesper/src/BuiltInMelodies/BuiltInMelodies.hpp
Normal file
253
vesper/src/BuiltInMelodies/BuiltInMelodies.hpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════════════════════════════
|
||||
* BUILTINMELODIES.HPP - Firmware-Baked Melody Library
|
||||
* ═══════════════════════════════════════════════════════════════════════════════════
|
||||
*
|
||||
* 🎵 BUILT-IN MELODY LIBRARY FOR VESPER 🎵
|
||||
*
|
||||
* This file contains melodies baked directly into the firmware, eliminating
|
||||
* the need for SD card downloads. Each melody is stored in PROGMEM to save RAM.
|
||||
*
|
||||
* 🏗️ ARCHITECTURE:
|
||||
* • Melodies stored in PROGMEM (Flash memory, not RAM)
|
||||
* • Each melody step is 2 bytes (uint16_t bitmask)
|
||||
* • Metadata includes name, UID, default speed
|
||||
* • Easy to add new melodies
|
||||
*
|
||||
* 📦 STORAGE EFFICIENCY:
|
||||
* • Small melodies (~30 steps = 60 bytes)
|
||||
* • Large melodies (~200 steps = 400 bytes)
|
||||
* • 40 melodies average = ~6-10KB total (Flash, not RAM!)
|
||||
*
|
||||
* 🎶 MELODY FORMAT:
|
||||
* Each uint16_t is a bitmask:
|
||||
* - Bit 0-15: Which bells/notes to activate
|
||||
* - Example: 0x0001 = Bell 0, 0x0003 = Bells 0+1, 0x8000 = Bell 15
|
||||
*
|
||||
* 📋 VERSION: 1.0
|
||||
* 📅 DATE: 2025-12-28
|
||||
* 👨💻 AUTHOR: Advanced Bell Systems
|
||||
* ═══════════════════════════════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
#include <pgmspace.h>
|
||||
|
||||
namespace BuiltInMelodies {
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════════════
|
||||
// MELODY METADATA STRUCTURE
|
||||
// ═════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
struct MelodyInfo {
|
||||
const char* name; // Display name
|
||||
const char* uid; // Unique identifier
|
||||
const uint16_t* data; // Pointer to melody data in PROGMEM
|
||||
uint16_t stepCount; // Number of steps
|
||||
uint16_t defaultSpeed; // Default speed in milliseconds per beat
|
||||
};
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════════════
|
||||
// EXAMPLE MELODIES - Add your melodies here!
|
||||
// ═════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Example: Simple Scale (C-D-E-F-G-A-B-C)
|
||||
const uint16_t PROGMEM melody_simple_scale[] = {
|
||||
0x0001, 0x0002, 0x0004, 0x0008,
|
||||
0x0010, 0x0020, 0x0040, 0x0080
|
||||
};
|
||||
|
||||
// Example: Happy Birthday (simplified)
|
||||
const uint16_t PROGMEM melody_happy_birthday[] = {
|
||||
0x0001, 0x0001, 0x0002, 0x0001,
|
||||
0x0008, 0x0004, 0x0001, 0x0001,
|
||||
0x0002, 0x0001, 0x0010, 0x0008,
|
||||
0x0001, 0x0001, 0x0080, 0x0008,
|
||||
0x0004, 0x0002, 0x0040, 0x0040,
|
||||
0x0008, 0x0004, 0x0002
|
||||
};
|
||||
|
||||
// Example: Jingle Bells (simplified)
|
||||
const uint16_t PROGMEM melody_jingle_bells[] = {
|
||||
0x0004, 0x0004, 0x0004, 0x0000,
|
||||
0x0004, 0x0004, 0x0004, 0x0000,
|
||||
0x0004, 0x0008, 0x0001, 0x0002,
|
||||
0x0004, 0x0000, 0x0000, 0x0000,
|
||||
0x0008, 0x0008, 0x0008, 0x0008,
|
||||
0x0008, 0x0004, 0x0004, 0x0004,
|
||||
0x0002, 0x0002, 0x0004, 0x0002,
|
||||
0x0008, 0x0000, 0x0000, 0x0000
|
||||
};
|
||||
|
||||
// Example: Westminster Chimes
|
||||
const uint16_t PROGMEM melody_westminster_chimes[] = {
|
||||
0x0008, 0x0004, 0x0002, 0x0001,
|
||||
0x0001, 0x0002, 0x0008, 0x0004,
|
||||
0x0008, 0x0001, 0x0002, 0x0004,
|
||||
0x0002, 0x0008, 0x0004, 0x0001
|
||||
};
|
||||
|
||||
// Example: Alarm Pattern
|
||||
const uint16_t PROGMEM melody_alarm[] = {
|
||||
0x0001, 0x0080, 0x0001, 0x0080,
|
||||
0x0001, 0x0080, 0x0001, 0x0080,
|
||||
0x0000, 0x0000, 0x0001, 0x0080,
|
||||
0x0001, 0x0080, 0x0001, 0x0080
|
||||
};
|
||||
|
||||
// Example: Doorbell
|
||||
const uint16_t PROGMEM melody_doorbell[] = {
|
||||
0x0004, 0x0008, 0x0004, 0x0008
|
||||
};
|
||||
|
||||
// Example: Single Bell Test
|
||||
const uint16_t PROGMEM melody_single_bell[] = {
|
||||
0x0001
|
||||
};
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════════════
|
||||
// MELODY LIBRARY - Array of all built-in melodies
|
||||
// ═════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
const MelodyInfo MELODY_LIBRARY[] = {
|
||||
{
|
||||
"Simple Scale",
|
||||
"builtin_scale",
|
||||
melody_simple_scale,
|
||||
sizeof(melody_simple_scale) / sizeof(uint16_t),
|
||||
200 // 200ms per beat
|
||||
},
|
||||
{
|
||||
"Happy Birthday",
|
||||
"builtin_happy_birthday",
|
||||
melody_happy_birthday,
|
||||
sizeof(melody_happy_birthday) / sizeof(uint16_t),
|
||||
250
|
||||
},
|
||||
{
|
||||
"Jingle Bells",
|
||||
"builtin_jingle_bells",
|
||||
melody_jingle_bells,
|
||||
sizeof(melody_jingle_bells) / sizeof(uint16_t),
|
||||
180
|
||||
},
|
||||
{
|
||||
"Westminster Chimes",
|
||||
"builtin_westminster",
|
||||
melody_westminster_chimes,
|
||||
sizeof(melody_westminster_chimes) / sizeof(uint16_t),
|
||||
400
|
||||
},
|
||||
{
|
||||
"Alarm",
|
||||
"builtin_alarm",
|
||||
melody_alarm,
|
||||
sizeof(melody_alarm) / sizeof(uint16_t),
|
||||
150
|
||||
},
|
||||
{
|
||||
"Doorbell",
|
||||
"builtin_doorbell",
|
||||
melody_doorbell,
|
||||
sizeof(melody_doorbell) / sizeof(uint16_t),
|
||||
300
|
||||
},
|
||||
{
|
||||
"Single Bell Test",
|
||||
"builtin_single_bell",
|
||||
melody_single_bell,
|
||||
sizeof(melody_single_bell) / sizeof(uint16_t),
|
||||
100
|
||||
}
|
||||
};
|
||||
|
||||
const uint16_t MELODY_COUNT = sizeof(MELODY_LIBRARY) / sizeof(MelodyInfo);
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════════════
|
||||
// HELPER FUNCTIONS
|
||||
// ═════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief Check if a UID is a built-in melody
|
||||
* @param uid The melody UID to check
|
||||
* @return true if it's a built-in melody (starts with "builtin_")
|
||||
*/
|
||||
inline bool isBuiltInMelody(const String& uid) {
|
||||
return uid.startsWith("builtin_");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find a built-in melody by UID
|
||||
* @param uid The melody UID to find
|
||||
* @return Pointer to MelodyInfo if found, nullptr otherwise
|
||||
*/
|
||||
inline const MelodyInfo* findMelodyByUID(const String& uid) {
|
||||
for (uint16_t i = 0; i < MELODY_COUNT; i++) {
|
||||
if (uid == MELODY_LIBRARY[i].uid) {
|
||||
return &MELODY_LIBRARY[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load a built-in melody into a vector
|
||||
* @param uid The melody UID to load
|
||||
* @param melodySteps Vector to fill with melody data
|
||||
* @return true if melody was found and loaded, false otherwise
|
||||
*/
|
||||
inline bool loadBuiltInMelody(const String& uid, std::vector<uint16_t>& melodySteps) {
|
||||
const MelodyInfo* melody = findMelodyByUID(uid);
|
||||
if (!melody) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Resize vector and copy data from PROGMEM
|
||||
melodySteps.resize(melody->stepCount);
|
||||
for (uint16_t i = 0; i < melody->stepCount; i++) {
|
||||
melodySteps[i] = pgm_read_word(&(melody->data[i]));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get list of all built-in melodies as JSON string
|
||||
* @return JSON array string of melody names and UIDs
|
||||
*/
|
||||
inline String getBuiltInMelodiesJSON() {
|
||||
String json = "[";
|
||||
for (uint16_t i = 0; i < MELODY_COUNT; i++) {
|
||||
if (i > 0) json += ",";
|
||||
json += "{";
|
||||
json += "\"name\":\"" + String(MELODY_LIBRARY[i].name) + "\",";
|
||||
json += "\"uid\":\"" + String(MELODY_LIBRARY[i].uid) + "\",";
|
||||
json += "\"steps\":" + String(MELODY_LIBRARY[i].stepCount) + ",";
|
||||
json += "\"speed\":" + String(MELODY_LIBRARY[i].defaultSpeed);
|
||||
json += "}";
|
||||
}
|
||||
json += "]";
|
||||
return json;
|
||||
}
|
||||
|
||||
} // namespace BuiltInMelodies
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════
|
||||
// USAGE EXAMPLE:
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════
|
||||
/*
|
||||
// Check if melody is built-in
|
||||
if (BuiltInMelodies::isBuiltInMelody(uid)) {
|
||||
// Load it from firmware
|
||||
std::vector<uint16_t> melodyData;
|
||||
if (BuiltInMelodies::loadBuiltInMelody(uid, melodyData)) {
|
||||
// Use melodyData...
|
||||
}
|
||||
} else {
|
||||
// Load from SD card as usual
|
||||
}
|
||||
*/
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════
|
||||
187
vesper/src/BuiltInMelodies/README.md
Normal file
187
vesper/src/BuiltInMelodies/README.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# Built-In Melodies System
|
||||
|
||||
## Overview
|
||||
|
||||
The built-in melodies system allows you to bake melodies directly into the firmware, eliminating the need for SD card downloads. Melodies are stored in **PROGMEM** (Flash memory), so they don't consume precious RAM.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Check**: When a melody is requested, the Player first checks if the UID starts with `builtin_`
|
||||
2. **Load**: If it's built-in, the melody is loaded from Flash memory (PROGMEM)
|
||||
3. **Fallback**: If not built-in, it loads from SD card as usual
|
||||
|
||||
## Adding New Melodies
|
||||
|
||||
### Step 1: Create Your Melody Data
|
||||
|
||||
Each melody step is a **2-byte (uint16_t) bitmask** representing which bells to activate:
|
||||
|
||||
```cpp
|
||||
// Example: Simple pattern
|
||||
const uint16_t PROGMEM melody_my_tune[] = {
|
||||
0x0001, // Bell 0
|
||||
0x0002, // Bell 1
|
||||
0x0004, // Bell 2
|
||||
0x0008, // Bell 3
|
||||
0x0003, // Bells 0+1 together
|
||||
0x000F // Bells 0+1+2+3 together
|
||||
};
|
||||
```
|
||||
|
||||
**Bitmask Reference:**
|
||||
- `0x0001` = Bell 0 (bit 0)
|
||||
- `0x0002` = Bell 1 (bit 1)
|
||||
- `0x0004` = Bell 2 (bit 2)
|
||||
- `0x0008` = Bell 3 (bit 3)
|
||||
- `0x0010` = Bell 4 (bit 4)
|
||||
- `0x0020` = Bell 5 (bit 5)
|
||||
- `0x0040` = Bell 6 (bit 6)
|
||||
- `0x0080` = Bell 7 (bit 7)
|
||||
- `0x0100` = Bell 8 (bit 8)
|
||||
- ... up to `0x8000` = Bell 15 (bit 15)
|
||||
- `0x0000` = Silence/rest
|
||||
|
||||
**Combining Bells:**
|
||||
- `0x0003` = Bells 0+1 (0x0001 | 0x0002)
|
||||
- `0x0005` = Bells 0+2 (0x0001 | 0x0004)
|
||||
- `0x000F` = Bells 0+1+2+3
|
||||
- `0xFFFF` = All 16 bells
|
||||
|
||||
### Step 2: Add to BuiltInMelodies.hpp
|
||||
|
||||
Open `src/BuiltInMelodies/BuiltInMelodies.hpp` and:
|
||||
|
||||
1. **Add your melody array:**
|
||||
```cpp
|
||||
// Your new melody
|
||||
const uint16_t PROGMEM melody_my_awesome_tune[] = {
|
||||
0x0001, 0x0002, 0x0004, 0x0008,
|
||||
0x0010, 0x0020, 0x0040, 0x0080,
|
||||
// ... up to 200 steps
|
||||
};
|
||||
```
|
||||
|
||||
2. **Add to MELODY_LIBRARY array:**
|
||||
```cpp
|
||||
const MelodyInfo MELODY_LIBRARY[] = {
|
||||
// ... existing melodies ...
|
||||
|
||||
// Your new melody
|
||||
{
|
||||
"My Awesome Tune", // Display name
|
||||
"builtin_my_awesome_tune", // UID (must start with "builtin_")
|
||||
melody_my_awesome_tune, // Data array
|
||||
sizeof(melody_my_awesome_tune) / sizeof(uint16_t), // Step count
|
||||
200 // Default speed in milliseconds per beat
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Step 3: Use Your Melody
|
||||
|
||||
Send a play command with the built-in melody UID:
|
||||
|
||||
**MQTT:**
|
||||
```json
|
||||
{
|
||||
"group": "playback",
|
||||
"action": "play",
|
||||
"uid": "builtin_my_awesome_tune",
|
||||
"name": "My Awesome Tune",
|
||||
"speed": 200
|
||||
}
|
||||
```
|
||||
|
||||
**WebSocket/HTTP:**
|
||||
```json
|
||||
{
|
||||
"group": "playback",
|
||||
"action": "play",
|
||||
"uid": "builtin_my_awesome_tune",
|
||||
"name": "My Awesome Tune",
|
||||
"speed": 200
|
||||
}
|
||||
```
|
||||
|
||||
## Pre-Loaded Melodies
|
||||
|
||||
The following melodies are already built-in:
|
||||
|
||||
| UID | Name | Steps | Default Speed |
|
||||
|-----|------|-------|---------------|
|
||||
| `builtin_scale` | Simple Scale | 8 | 200ms |
|
||||
| `builtin_happy_birthday` | Happy Birthday | 23 | 250ms |
|
||||
| `builtin_jingle_bells` | Jingle Bells | 32 | 180ms |
|
||||
| `builtin_westminster` | Westminster Chimes | 16 | 400ms |
|
||||
| `builtin_alarm` | Alarm | 16 | 150ms |
|
||||
| `builtin_doorbell` | Doorbell | 4 | 300ms |
|
||||
| `builtin_single_bell` | Single Bell Test | 1 | 100ms |
|
||||
|
||||
## Memory Usage
|
||||
|
||||
### Flash Memory (PROGMEM)
|
||||
- Small melody (~30 steps): **60 bytes**
|
||||
- Large melody (~200 steps): **400 bytes**
|
||||
- 40 melodies average: **~6-10KB** (stored in Flash, not RAM!)
|
||||
|
||||
### RAM Usage
|
||||
Only the **currently playing melody** is loaded into RAM. Built-in melodies are copied from Flash when needed.
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Always use `builtin_` prefix** for UIDs to identify them as built-in
|
||||
2. **Test with small melodies first** before adding large ones
|
||||
3. **Use hex calculator** for complex bell combinations: `0x0001 | 0x0004 = 0x0005`
|
||||
4. **Add rests** with `0x0000` for silence between notes
|
||||
5. **Keep it simple** - most melodies work great with 30-50 steps
|
||||
|
||||
## Converting Binary Files to Code
|
||||
|
||||
If you have existing binary melody files and want to convert them to built-in format:
|
||||
|
||||
```python
|
||||
# Python script to convert binary file to C++ array
|
||||
with open('melody.bin', 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
print("const uint16_t PROGMEM melody_name[] = {")
|
||||
for i in range(0, len(data), 2):
|
||||
if i % 16 == 0:
|
||||
print(" ", end="")
|
||||
high = data[i]
|
||||
low = data[i+1]
|
||||
value = (high << 8) | low
|
||||
print(f"0x{value:04X}", end="")
|
||||
if i < len(data) - 2:
|
||||
print(", ", end="")
|
||||
if (i + 2) % 16 == 0:
|
||||
print()
|
||||
print("\n};")
|
||||
```
|
||||
|
||||
## Example: Creating a Custom Melody
|
||||
|
||||
Let's create "Mary Had a Little Lamb":
|
||||
|
||||
```cpp
|
||||
// Mary Had a Little Lamb
|
||||
// Notes: E D C D E E E, D D D, E G G
|
||||
// Mapping: E=0, D=1, C=2, G=3
|
||||
const uint16_t PROGMEM melody_mary_lamb[] = {
|
||||
0x0001, 0x0002, 0x0004, 0x0002, // E D C D
|
||||
0x0001, 0x0001, 0x0001, 0x0000, // E E E (rest)
|
||||
0x0002, 0x0002, 0x0002, 0x0000, // D D D (rest)
|
||||
0x0001, 0x0008, 0x0008 // E G G
|
||||
};
|
||||
|
||||
// Add to MELODY_LIBRARY:
|
||||
{
|
||||
"Mary Had a Little Lamb",
|
||||
"builtin_mary_lamb",
|
||||
melody_mary_lamb,
|
||||
sizeof(melody_mary_lamb) / sizeof(uint16_t),
|
||||
300 // 300ms per beat
|
||||
}
|
||||
```
|
||||
|
||||
Now you can play it with UID `builtin_mary_lamb`!
|
||||
Reference in New Issue
Block a user