Added HTTP-API support, Standalone AP Support and Built-in Melodies

This commit is contained in:
2025-12-28 21:49:49 +02:00
parent 0f0b67cab9
commit db57b355b9
14 changed files with 1313 additions and 26 deletions

View 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
}
*/
// ═══════════════════════════════════════════════════════════════════════════════════

View 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`!