334 lines
7.0 KiB
C++
334 lines
7.0 KiB
C++
|
|
#include <Arduino.h>
|
|
#include "feedback.h"
|
|
#include "app_hal.h"
|
|
#include "displays/pins.h"
|
|
|
|
// A struct to hold a sequence of notes
|
|
struct NoteSequence
|
|
{
|
|
Note notes[MAX_NOTES_PER_SEQUENCE];
|
|
int count;
|
|
int repeat;
|
|
};
|
|
|
|
struct VibPattern
|
|
{
|
|
Vibration steps[MAX_VIB_PATTERN_LENGTH];
|
|
int count;
|
|
};
|
|
|
|
QueueHandle_t noteQueue;
|
|
TaskHandle_t toneTaskHandle = nullptr;
|
|
volatile bool playing = false;
|
|
volatile ToneType currentToneType = T_USER;
|
|
volatile bool forceInterrupt = false;
|
|
|
|
QueueHandle_t vibQueue;
|
|
TaskHandle_t vibTaskHandle = nullptr;
|
|
volatile bool vibrating = false;
|
|
|
|
Note tone_melody[] = {
|
|
{440, 300}, // A4
|
|
{0, 150}, // Rest
|
|
{494, 300}, // B4
|
|
{0, 150}, // Rest
|
|
{523, 300}, // C5
|
|
{0, 300}, // Longer rest
|
|
{587, 300} // D5
|
|
};
|
|
|
|
Note tone_startup[] = {
|
|
{TONE_EN * 2, 170},
|
|
{TONE_FS * 2, 170},
|
|
{TONE_GN * 2, 170}};
|
|
|
|
// 📩 Notification Received (Double Beep)
|
|
Note tone_notification[] = {
|
|
{1000, 100},
|
|
{0, 100},
|
|
{1200, 100},
|
|
};
|
|
|
|
// 📞 Call Received (Melody)
|
|
Note tone_call[] = {
|
|
{880, 200},
|
|
{988, 200},
|
|
{1046, 200},
|
|
{0, 100},
|
|
{1046, 300},
|
|
{880, 200},
|
|
{988, 200},
|
|
{0, 200},
|
|
};
|
|
|
|
// ⏰ Alarm Ring
|
|
Note tone_alarm[] = {
|
|
{1500, 150},
|
|
{0, 100},
|
|
{1500, 150},
|
|
{0, 100},
|
|
{1500, 150},
|
|
{0, 100},
|
|
{2000, 300},
|
|
};
|
|
|
|
Note tone_off[] = {
|
|
{0, 50}};
|
|
|
|
// ⏲️ Timer Ended
|
|
Note tone_timer[] = {
|
|
{880, 200},
|
|
{0, 100},
|
|
{660, 200},
|
|
{0, 100},
|
|
{880, 200},
|
|
{0, 100},
|
|
};
|
|
|
|
// 🔘 Button Press
|
|
Note tone_button[] = {
|
|
{1000, 50},
|
|
};
|
|
|
|
// 🎮 Simon Says - Intro
|
|
Note tone_simonsays_intro[] = {
|
|
{659, 150},
|
|
{784, 150},
|
|
{987, 150},
|
|
{1319, 200},
|
|
};
|
|
|
|
// 🎮 Simon Says - Game Over
|
|
Note tone_simonsays_gameover[] = {
|
|
{659, 300},
|
|
{622, 300},
|
|
{587, 300},
|
|
{554, 400},
|
|
};
|
|
|
|
Vibration v_notif[] = {
|
|
{true, 100}, // ON for 200ms
|
|
{false, 50}, // OFF for 100ms
|
|
};
|
|
|
|
Vibration pattern[] = {
|
|
{true, 200}, // ON for 200ms
|
|
{false, 100}, // OFF for 100ms
|
|
{true, 300}, // ON for 300ms
|
|
{false, 200}, // OFF for 200ms
|
|
};
|
|
|
|
void toneTask(void *param)
|
|
{
|
|
#if defined(BUZZER_PIN) && (BUZZER_PIN != -1)
|
|
|
|
NoteSequence sequence;
|
|
|
|
while (true)
|
|
{
|
|
if (xQueueReceive(noteQueue, &sequence, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
playing = true;
|
|
|
|
ledcAttachPin(BUZZER_PIN, BUZZER_CHANNEL);
|
|
|
|
if (sequence.repeat == 0)
|
|
{
|
|
sequence.repeat = 1;
|
|
}
|
|
|
|
for (int r = 0; r < sequence.repeat; r++)
|
|
{
|
|
for (int n = 0; n < sequence.count; n++)
|
|
{
|
|
if (forceInterrupt)
|
|
{
|
|
forceInterrupt = false;
|
|
break;
|
|
}
|
|
|
|
int pitch = sequence.notes[n].pitch;
|
|
int duration = sequence.notes[n].duration;
|
|
|
|
if (pitch > 0)
|
|
{
|
|
ledcWriteTone(BUZZER_CHANNEL, pitch);
|
|
}
|
|
else
|
|
{
|
|
ledcWriteTone(BUZZER_CHANNEL, 0); // Rest
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(duration));
|
|
}
|
|
}
|
|
|
|
ledcDetachPin(BUZZER_PIN);
|
|
|
|
playing = false;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void vibrationTask(void *param)
|
|
{
|
|
#if defined(VIBRATION_PIN) && (VIBRATION_PIN != -1)
|
|
#ifndef ELECROW_C3
|
|
pinMode(VIBRATION_PIN, OUTPUT);
|
|
#endif
|
|
VibPattern pattern;
|
|
|
|
while (true)
|
|
{
|
|
if (xQueueReceive(vibQueue, &pattern, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
vibrating = true;
|
|
|
|
#ifdef ELECROW_C3
|
|
vibratePin(true);
|
|
for (int i = 0; i < pattern.count; i++)
|
|
{
|
|
delay(pattern.steps[i].duration);
|
|
}
|
|
#else
|
|
for (int i = 0; i < pattern.count; i++)
|
|
{
|
|
vibratePin(pattern.steps[i].on);
|
|
delay(pattern.steps[i].duration);
|
|
}
|
|
#endif
|
|
|
|
vibratePin(false);
|
|
vibrating = false;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void startToneSystem()
|
|
{
|
|
#if defined(BUZZER_PIN) && (BUZZER_PIN != -1)
|
|
|
|
if (!noteQueue)
|
|
{
|
|
noteQueue = xQueueCreate(MAX_QUEUE_SIZE, sizeof(NoteSequence));
|
|
}
|
|
if (!toneTaskHandle)
|
|
{
|
|
xTaskCreatePinnedToCore(toneTask, "toneTask", 2048, nullptr, 1, &toneTaskHandle, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void startVibrationSystem()
|
|
{
|
|
#if defined(VIBRATION_PIN) && (VIBRATION_PIN != -1)
|
|
if (!vibQueue)
|
|
{
|
|
vibQueue = xQueueCreate(MAX_QUEUE_SIZE, sizeof(VibPattern));
|
|
}
|
|
if (!vibTaskHandle)
|
|
{
|
|
xTaskCreatePinnedToCore(vibrationTask, "vibrationTask", 2048, nullptr, 1, &vibTaskHandle, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void feedbackTone(Note *notes, int count, ToneType type, int repeat)
|
|
{
|
|
#if defined(BUZZER_PIN) && (BUZZER_PIN != -1)
|
|
if (check_alert_state(ALERT_SOUND))
|
|
{
|
|
if (!noteQueue)
|
|
startToneSystem();
|
|
|
|
if (count > MAX_NOTES_PER_SEQUENCE)
|
|
count = MAX_NOTES_PER_SEQUENCE;
|
|
|
|
if (playing && currentToneType >= type)
|
|
{
|
|
// If the current tone is of the same or higher priority, reset the queue
|
|
forceInterrupt = true;
|
|
xQueueReset(noteQueue);
|
|
}
|
|
|
|
NoteSequence sequence;
|
|
sequence.count = count;
|
|
sequence.repeat = repeat;
|
|
memcpy(sequence.notes, notes, count * sizeof(Note));
|
|
|
|
xQueueSend(noteQueue, &sequence, 0);
|
|
currentToneType = type;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void feedbackVibrate(Vibration *steps, int count, bool force)
|
|
{
|
|
#if defined(VIBRATION_PIN) && (VIBRATION_PIN != -1)
|
|
if (check_alert_state(ALERT_VIBRATE))
|
|
{
|
|
if (!vibQueue)
|
|
startVibrationSystem();
|
|
|
|
if (count > MAX_VIB_PATTERN_LENGTH)
|
|
count = MAX_VIB_PATTERN_LENGTH;
|
|
|
|
if (force && vibrating)
|
|
{
|
|
xQueueReset(vibQueue);
|
|
}
|
|
|
|
VibPattern pattern;
|
|
pattern.count = count;
|
|
memcpy(pattern.steps, steps, count * sizeof(Vibration));
|
|
|
|
xQueueSend(vibQueue, &pattern, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void feedbackRun(ToneType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case T_CALLS:
|
|
feedbackTone(tone_call, 8, T_CALLS, 3);
|
|
feedbackVibrate(pattern, 4, true);
|
|
break;
|
|
case T_NOTIFICATION:
|
|
feedbackTone(tone_notification, 3, T_NOTIFICATION, 2);
|
|
feedbackVibrate(v_notif, 2, true);
|
|
break;
|
|
case T_TIMER:
|
|
feedbackTone(tone_alarm, 7, T_TIMER);
|
|
feedbackVibrate(pattern, 4, true);
|
|
break;
|
|
case T_ALARM:
|
|
feedbackTone(tone_timer, 6, T_TIMER, 3);
|
|
feedbackVibrate(pattern, 4, true);
|
|
break;
|
|
case T_SYSTEM:
|
|
feedbackTone(tone_button, 1, T_SYSTEM);
|
|
feedbackVibrate(pattern, 2, true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
if (check_alert_state(ALERT_SCREEN))
|
|
{
|
|
switch (type)
|
|
{
|
|
case T_CALLS:
|
|
screen_on(50);
|
|
break;
|
|
default:
|
|
screen_on();
|
|
break;
|
|
}
|
|
}
|
|
}
|