TVZ2022 / Mbed OS Pavetic_BozicnePjesme

Dependencies:   Pavetic_BuzzerLib Pavetic_MusicLib

Revision:
0:e9d0eb34d345
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sun Nov 27 13:01:54 2022 +0000
@@ -0,0 +1,282 @@
+// Program koji simulira božične lampice //
+
+// Gumb - Mijenja melodiju na klik (1...n) - Hardverski interrupt
+// Potenciometar - Mjerenje vrijednosti na ulazu (0.0-1.0) - Potrebno za
+// mjerenje vremena promjene pjesme Zvučnik - Reprodukcija melodije Zuta LED -
+// Signalizacija da traje melodija jingle bells Crvena LED - Signalizacija da
+// traje melodija We wish you a merry christmas Zelena LED - Signalizacija da
+// traje melodija Silent night
+
+// Ticker - Automatsko mijenjanje melodije u vremenu ovisnom o potenciometru
+// (samo u modu MODE_AUTO)
+//  --> 0V = 5s
+//  --> 1V = 25s
+//  --> f(x) = 5 + 20*x [s] - Skaliranje funkcije pomaknute za 5s (min =
+// 5s, max = 25s)
+// Timer #1 - Mjerenje vremena trajanja melodije
+// Timer #2 - Debounce efekt za gumb
+
+// Opis rada programa:
+// Postoje 3 moda rada:
+//      --> MODE_OFF - Sustav je ugašen - Buzzer ugašen kao i sve LEDice
+//      --> MODE_SONG - Repetetivno ponavljanje odabrane melodije - Buzzer
+// svira, te svijetli jedna od pripadajucih LEDica za trenutnu melodiju
+//      --> MODE_AUTO - Automatsko mijenjanje melodije u vremenskom
+//razmaku odabranom putem potenciometra - Buzzer svira, te svijetli jedna od
+//pripadajucih LEDica za trenutnu melodiju
+//
+// Postoje 3 melodije: Jingle bells, We wish you, Silent night. Ovisno o
+// trenutnoj melodiji, svijetli pripadajuca LEDica.
+//
+// Na početku programa postavljen je mod MODE_OFF te je sustav ugašen.
+// Pritiskom na gumb u modu MODE_OFF (prvi pritisak gumba), mijenja se mod u
+// MODE_SONG te počinje svirati prva od ponudenih melodija. Nakon sto melodija
+// zavrsi, ukoliko nije došlo do promjene melodije ili prekida, ponovno se
+// započinje sa reprodukcijom iste melodije. Ukoliko tijekom reprodukcije dođe
+// do promjene moda ili melodije, trenutna melodija prekida sa reprodukcijom te
+// počinje reprodukcija sljedeće melodije, ili se sustav gasi ukoliko je došlo
+// do promjene moda u MODE_OFF.
+//
+// Program je u modu MODE_SONG te svira prva pjesma i upaljena je zuta LEDica.
+// Drugim pritiskom gumba, mode ostaje isti, no mijenja se melodija te svira
+// druga melodija i svijetli crvena LEDica. Trećim pritiskom gumba, mod i dalje
+// ostaje isti i svira treca melodija uz upaljenu zelenu LEDicu.
+//
+// Cetvrtim pritiskom gumba mijenja se mod u MODE_OFF te se resetira ciklus
+// melodija i započinje reprodukcija prve melodije. Melodija se reproducira
+// ovisno o vremenu izračunatom iz vrijednosti na potenciometru prema određenoj
+// formuli koja je navedena na vrhu. Nakon isteka vremena, mijenja se melodija i
+// započinje reprodukcija sljedeće melodije, te tako u krug sa svim melodijama.
+//  - Vrijednost potenciometra očitava se cijelo vrijeme na zasebnom
+// Threadu.
+//  - Vrijeme reprodukcije melodije u modu MODE_OFF izračunava se kod
+// dolaska u taj mode.
+//
+// Petim pritiskom gumba, prekida se reprodukcija melodije te se mod mijenja u
+// MODE_OFF i program nastavlja ispočetka.
+
+#include "DigitalOut.h"
+#include "Music.h"
+#include "PinNameAliases.h"
+#include "PinNames.h"
+#include "ThisThread.h"
+#include "Thread.h"
+#include "Buzzer.h"
+#include "mbed.h"
+#include "mbed_rtc_time.h"
+#include "mbed_thread.h"
+#include "rtos.h"
+#include <chrono>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+
+#define SONG_COUNT 3
+
+#define MODE_OFF 0
+#define MODE_SONG 1
+#define MODE_AUTO 2
+
+#define JINGLE_BELLS 1
+#define WISH 2
+#define SILENT_NIGHT 3
+
+DigitalOut jingleBellsLed(PA_10); // Yellow
+DigitalOut wishLed(PB_5);         // Red
+DigitalOut silentNightLed(PA_9);  // Green
+
+// -- START Sounds and music -- //
+Timer songMeasure; // Measuring how long did song play
+// PwmOut buzzer(D5);
+Buzzer buzzer(D5);
+
+void playMusic(int array[], float rithm[], int size);
+// -- END Sounds and music -- //
+
+// -- START Mode changing -- //
+InterruptIn button(D7); // Button for changing melodies
+Timer debounce;         // Debounce timer for the button
+void onButtonPress();
+// -- END Mode changing -- //
+
+// -- START Main variables -- //
+int currentMode = MODE_OFF;       // Mode that is currently selected
+int currentMelody = JINGLE_BELLS; // Melody that is currently being played
+int songStartedOnMode = -1;       // Variable ensuring mode change is detected
+int songStartedOnMelody = -1;     // Vriable ensuring melody change is detected
+// -- END Main variables -- //
+
+// -- START Auto changing transitions -- //
+AnalogIn potenciometer(
+    A0); // Analog input for potenciometer - detecting song change speed
+
+Ticker changeSongTicker; // Ticker for changing song every x time - depends on
+                         // measuredVoltage
+Thread analogMeasureThread(osPriorityNormal, 300, nullptr, nullptr);
+float measuredVoltage = 0; // Currently measured voltage on potenciometer - For
+                           // determining time between transitions
+void readValue();
+// -- END Auto changing transitions -- //
+
+// -- START Common functions -- //
+void changeMode();
+void changeSong();
+void toggleLeds(int song);
+// -- END Common functions -- //
+
+int main() {
+  debounce.start();
+  button.mode(PullUp); // Required - otherwise button does not work properly
+  button.rise(&onButtonPress);
+
+  analogMeasureThread.start(readValue);
+
+  while (true) {
+    toggleLeds(currentMelody);
+
+    // If MODE_OFF, turn off buzzer and skip the code
+    if (currentMode == MODE_OFF) {
+      buzzer.silence();
+      continue;
+    }
+
+    songStartedOnMode = currentMode;
+    songStartedOnMelody = currentMelody;
+
+    switch (currentMelody) {
+    case JINGLE_BELLS: {
+      int size = (unsigned int)sizeof jingle_bells_melody /
+                 sizeof jingle_bells_melody[0];
+      playMusic(jingle_bells_melody, jingle_bells_note_durations, size);
+      break;
+    }
+
+    case WISH: {
+      int size = (unsigned int)sizeof wish_melody / sizeof wish_melody[0];
+      playMusic(wish_melody, wish_note_durations, size);
+      break;
+    }
+
+    case SILENT_NIGHT: {
+      int size = (unsigned int)sizeof silent_night_melody /
+                 sizeof silent_night_melody[0];
+      playMusic(silent_night_melody, silent_night_note_durations, size);
+      break;
+    }
+    }
+  }
+}
+
+void playMusic(int array[], float rithm[], int size) {
+  songMeasure.start();
+
+  int i = 0;
+  for (i = 0; i < size; i++) {
+    // Stop executing if mode or song change in the meantime
+    // Otherwise song would remain playing after mode or song is changed
+    if (currentMode != songStartedOnMode ||
+        currentMelody != songStartedOnMelody)
+      break;
+    printf("Entering iteration %d for song %d. Measured voltage: %f\n", i,
+           currentMelody, measuredVoltage);
+    buzzer.buzz(array[i], rithm[i]);
+  }
+
+  // Printing how long did song played in seconds
+  int elapsedTime_sec =
+      duration_cast<std::chrono::seconds>(songMeasure.elapsed_time()).count();
+  printf("Song played for %d seconds\n", elapsedTime_sec);
+  songMeasure.stop();
+  songMeasure.reset();
+
+  // Pause for 1 second after song has finished WITHOUT interruption - Silent
+  // transition Otherwise, move straight to the next song
+  buzzer.silence();
+  if (i == size)
+    ThisThread::sleep_for(1s);
+}
+
+// Callback executed when button is pressed - initiated by the Interrupt.
+// Uses debounce effect of 200ms and if button really pressed following code is
+// executed.
+// 1. If any song is currently playing in MODE_SONG AND it is not last song,
+// change song.
+// 2. Otherwise, if any other mode, or it is last song - Change mode.
+void onButtonPress() {
+  if (debounce.elapsed_time() > 200ms) {
+    if (currentMode == MODE_SONG && currentMelody < SONG_COUNT)
+      changeSong();
+    else
+      changeMode();
+    debounce.reset();
+  }
+}
+
+void changeMode() {
+  // Reset melody because mode has changed - Initial melody should be played
+  // (Melody cycle reset)
+  currentMelody = JINGLE_BELLS;
+
+  // Temp var used to prevent multiple mode changes at once because new mode
+  // could be unknown.
+  int newMode = currentMode + 1; // Increase temp mode variable
+  switch (newMode) {
+  case MODE_AUTO: {
+    // Calculate time base on potenciometer reading and attach Ticker
+    // 0V = 5s
+    // 1V = 25s
+    // f(x) = 5 + 20*x [s] - Skaliranje funkcije pomaknute za 5s (min = 5s,
+    // max = 25s)
+    int changeTime_sec = 5 + (20 * measuredVoltage);
+    changeSongTicker.attach_us(&changeSong, changeTime_sec * 1000000);
+    break;
+  }
+
+  case MODE_SONG:
+  case MODE_OFF:
+    // Detach timer to prevent song change after time expires
+    changeSongTicker.detach();
+    break;
+
+  default:
+    // Unrecognised mode - Turn off + detach timer to prevent song change
+    changeSongTicker.detach();
+    newMode = MODE_OFF;
+  }
+
+  // Change source of truth mode
+  currentMode = newMode;
+}
+
+// Changes current song.
+// If current melody is the last melody, reset the cyle of the songs and
+// play the first song.
+void changeSong() {
+  int newMelody = currentMelody + 1;
+  if (newMelody > SILENT_NIGHT)
+    newMelody = 1;
+  currentMelody = newMelody;
+}
+
+// Constantly reads the analog input value in separate thread.
+void readValue() {
+  while (true) {
+    measuredVoltage = potenciometer.read();
+  }
+}
+
+// Toggles LEDs based on the current song.
+// If MODE_OFF, turn off all LEDs.
+// Only 1 LED can be turned on at the same time.
+void toggleLeds(int song) {
+  if (currentMode == MODE_OFF) {
+    jingleBellsLed.write(false);
+    wishLed.write(false);
+    silentNightLed.write(false);
+    return;
+  }
+
+  jingleBellsLed.write(song == JINGLE_BELLS);
+  wishLed.write(song == WISH);
+  silentNightLed.write(song == SILENT_NIGHT);
+}