#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/timers.h>
// Mendefinisikan variabel global
bool isPlayed = false;
bool isPaused = false;
unsigned int counter = 0; // Variabel untuk menyimpan waktu pemutaran
SemaphoreHandle_t mutex; // Membuat handle mutex
// PENJELASAN STATE
// ====================================
// | isPlayed | isPaused | STATE |
// ====================================
// | FALSE | FALSE | STOP |
// | TRUE | FALSE | PLAY/RESUME |
// | TRUE | TRUE | PAUSE |
// | FALSE | TRUE | Tidak valid |
// ====================================
// Membuat task yang mensimulasikan inisialisasi Bluetooth
void vBluetoothInit(void *parameters) {
TickType_t timestamp;
// Simulasi inisialisasi Bluetooth yang memasuki critical section
Serial.println("Task Bluetooth mencoba mengambil mutex...");
timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS;
xSemaphoreTake(mutex, portMAX_DELAY); // Percobaan mengambil mutex
// Mencetak informasi ke serial monitor
Serial.print("Task Bluetooth mendapatkan mutex setelah menunggu ");
Serial.print((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp);
Serial.println(" ms");
Serial.println("Connecting to Bluetooth...");
vTaskDelay(2000 / portTICK_PERIOD_MS); // Delay 2 detik
// Mencetak informasi ke serial monitor
Serial.print("Bluetooth connected after ");
Serial.print((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp);
Serial.println(" ms.");
// Mengembalikan mutex
xSemaphoreGive(mutex);
vTaskDelete(NULL); // Menghapus task
}
// Membuat task yang akan menggantikan task input serial jika diperlukan
// dengan priority inheritance
void vTaskSimulateBusy(void *parameters) {
TickType_t timestamp;
Serial.println("Simulasi pekerjaan, mohon tunggu sebentar...");
timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS;
// Simulasi pekerjaan sibuk
while ((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < 5000);
Serial.println("Simulasi pekerjaan selesai...");
vTaskDelete(NULL);
}
// Membuat fungsi callback yang akan dijalankan setiap timer berakhir
// Fungsi ini akan memeriksa state dan mencetak jika state == PLAY
void vMusicPlayer(void *parameters) {
while (1) {
// Memeriksa state saat ini, jika state == PLAY
if (isPlayed && !isPaused) {
// Menyalakan LED_BUILT-IN
// Menampilkan waktu pemutaran musik
digitalWrite(LED_BUILTIN, HIGH);
Serial.print("Waktu: ");
Serial.printf("%02d:%02d / 05:00\n", counter / 60, counter % 60);
counter++; // Menambahkan waktu pemutaran
// Jika waktu pemutaran sudah mencapai 300 detik (5 menit)
if (counter >= 300) {
// Mengubah state menjadi STOP
isPlayed = false;
// Mencetak informasi ke serial monitor
Serial.println("Lagu selesai. Pemutar dihentikan");
}
} else if (!isPlayed) { // Jika state == STOP
// Mematikan LED_BUILTIN
digitalWrite(LED_BUILTIN, LOW);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
// Task untuk mengontrol state dalam program menggunakan if bertingkat
void stateController(void *parameter) {
while (1) {
xSemaphoreTake(mutex, portMAX_DELAY); // Percobaan mengambil mutex
if (Serial.available() > 0) { // Memeriksa ketersediaan Serial Monitor
String command = Serial.readStringUntil('\n'); // Membaca serial monitor hingga \n
// Memeriksa input
if (command == "Play") { // Jika inputnya Putar
// Memeriksa state
if (!isPlayed) { // Jika state == STOP
isPlayed = true; // Mengubah state menjadi PLAY
counter = 0; // Mengatur waktu pemutaran ke 0
Serial.println("Memutar lagu"); // Mencetak informasi ke Serial Monitor
} else { // Jika statenya bukan PLAY
// Mencetak pesan kesalahan ke Serial Monitor
Serial.println("Kesalahan! Tidak dapat \"Putar\" saat ini.");
}
} else if (command == "Pause") { // Jika inputnya Jeda
// Memeriksa state
if (isPlayed && !isPaused) { // Jika state == PLAY
isPaused = true; // Mengubah state menjadi JEDA
// Mencetak informasi ke Serial Monitor
Serial.println("Pemutar Dijeda, Penghitung Waktu Dijeda");
digitalWrite(LED_BUILTIN, LOW); // Mematikan LED
} else { // Jika statenya bukan PLAY
// Mencetak pesan kesalahan ke Serial Monitor
Serial.println("Kesalahan! Tidak dapat \"Jeda\" saat ini.");
}
} else if (command == "Resume") { // Jika inputnya Lanjutkan
// Memeriksa state
if (isPlayed && isPaused) { // Jika statenya JEDA
isPaused = false; // Mengubah state menjadi PLAY
// Mencetak informasi ke Serial Monitor
Serial.println("Pemutar dilanjutkan. Penghitung Waktu dilanjutkan");
} else { // Jika statenya bukan JEDA
// Mencetak pesan kesalahan ke Serial Monitor
Serial.println("Kesalahan! Tidak dapat \"Lanjutkan\" saat ini.");
}
} else if (command == "Stop") { // Jika inputnya Hentikan
// Memeriksa state
if (isPlayed || isPaused) { // Jika statenya PLAY atau JEDA
// Mengubah state menjadi STOP
isPlayed = false;
isPaused = false;
counter = 0; // Mereset penghitung waktu
// Mencetak informasi ke Serial Monitor
Serial.println("Pemutar Dihentikan. Penghitung Waktu Direset");
} else { // Jika statenya bukan PLAY atau JEDA
// Mencetak pesan kesalahan ke Serial Monitor
Serial.println("Kesalahan! Pemutar sudah dihentikan");
}
} else { // Jika inputnya bukan salah satu dari empat kasus di atas
// Mencetak pesan kesalahan ke Serial Monitor
Serial.println("Input tidak valid");
}
}
vTaskDelay(100 / portTICK_PERIOD_MS);
xSemaphoreGive(mutex);
}
}
void setup() {
// Memulai serial
Serial.begin(115200);
// Mengatur pin LED sebagai output
vTaskDelay(1000 / portTICK_PERIOD_MS);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// Menggunakan binary semaphore yang tidak memiliki priority inheritance
mutex = xSemaphoreCreateMutex();
// Menjalankan ketiga task yang sudah dibuat fungsinya
xTaskCreatePinnedToCore(stateController, "Controller", 10000, NULL, 1, NULL, 1);
// Memberikan penundaan agar task stateControl memasuki critical section terlebih dahulu
vTaskDelay(1 / portTICK_PERIOD_MS);
xTaskCreatePinnedToCore(vBluetoothInit, "Init BT", 10000, NULL, 3, NULL, 1);
xTaskCreatePinnedToCore(vTaskSimulateBusy, "LED setter", 10000, NULL, 2, NULL, 1);
xTaskCreatePinnedToCore(vMusicPlayer, "Music Player", 10000, NULL, 2, NULL, 0);
// Menghapus task setup dan loop
vTaskDelete(NULL);
}
void loop() {
// Loop kosong
}