/*************************************************************************************************************************
TUGAS 6 Mailbox/Queue, Task Deletion and Task Priority Change
**************************************************************************************************************************
SISTEM TERTANAM LANJUT, S2TE-36-R1
KELOMPOK 4:
* Andre Suwardana Adiwidya
* Ardiansyah Ramadhan
* Bahtiar Yoga Prasetyo
* Shalma Nur Hida
Program yang dirancang sebagai pemenuhan Tugas 6 dengan mengambil dari case usulan tugas besar berupa Temperature Control
System. Terdapat 4 task dimana task1 untuk membaca membaca temperature, task2 untuk menampilkan data di LCD maupun serial,
task3 untuk kendali fan secara otomatis dan task4 untuk kendali fan secara manual berdasarkan input potensiometer.
Objectives :
* Queue digunakan untuk transfer data dari task1, task3 dan task4 (producer) ke task2 (consumer), bentuk data berupa
package structure yg berisi pointer dan value
* vTaskDelete dan xTaskCreate untuk memilih aktivasi taks3 atau task4 berdasarkan state dari button, karena kedua
task ini dioperasikan secara bergantian dan menghasilkan data value yang sama (presentasi putaran Fan).
* vTaskPrioritySet untuk mengatur prioritas dari task3 atau task4 terhadap prioritas task1 untuk menghindari race
condition, karena kedua task ini memiliki kesamaan sebagai producer.
Reference :
* F̲r̲e̲e̲R̲T̲O̲S̲_R̲e̲f̲e̲r̲e̲n̲c̲e̲_M̲a̲n̲u̲a̲l̲_V̲1̲0̲.0̲.0̲ (A̲P̲I̲ r̲e̲f̲e̲r̲e̲n̲c̲e̲)
* https://microcontrollerslab.com/arduino-freertos-structure-queue-receive-data-multiple-resources/
* https://microcontrollerslab.com/changing-task-priority-using-freertos-arduino/
*************************************************************************************************************************/
// Library
#include <Arduino_FreeRTOS.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal_I2C.h>
#include <task.h>
#include <queue.h>
// I2C LCD 20x4
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Onewire DS18B20
#define ONE_WIRE_BUS 13
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensor(&oneWire);
// Global resources
#define control_In 0
#define button_In 2
#define fan_Out 3
// Global variable
int manual;
int driving;
float reading_Temp;
// Local variable untuk display LCD
char temp[7];
char temp_buf[7];
char *mode[] = {"Auto Mode", "Manual Mode"};
char fan[7];
char fan_buf[7];
// Structure data untuk queue / mailbox
struct data
{
float Pointer;
float Value;
};
// Fungsi default membaca temperature
void readingFunction() {
sensor.requestTemperatures();
reading_Temp = sensor.getTempCByIndex(0);
}
// Fungsi default untuk LCD Display
static void display_Func(char *temp, char *mode, char *fan) {
lcd.setCursor (0,0);
lcd.print ("Temp Control Systems");
lcd.setCursor (4,1);
lcd.print (mode);
lcd.setCursor (0,2);
lcd.print ("Room_Temp : ");
lcd.setCursor (13,2);
lcd.print (temp);
lcd.setCursor (19,2);
lcd.print ("C");
lcd.setCursor (0,3);
lcd.print ("Fan_Speed : ");
lcd.setCursor (13,3);
lcd.print (fan);
lcd.setCursor (19,3);
lcd.print ("%");
}
// Fungsi default untuk Serial
static void serial_Func(char *temp, char *mode, char *fan) {
Serial.println (mode);
Serial.print ("Room_Temp : ");
Serial.print (temp);
Serial.println ("C");
Serial.print ("Fan_Speed : ");
Serial.print (fan);
Serial.println ("%");
Serial.print("\n");
}
// Fungsi default Auto Mode
static void auto_Func(float reading_Temp) {
analogWrite(fan_Out, driving);
if(reading_Temp <= 24) {
driving = 0;
}
else if (reading_Temp > 24 && reading_Temp <= 28) {
driving = 128;
}
else if (reading_Temp > 29) {
driving = 255;
}
}
// Fungsi default Manual Mode
static void manual_Func() {
manual = analogRead(control_In);
driving = map(manual, 0, 1023, 0, 255);
analogWrite(fan_Out, driving);
}
// Reference Handler
QueueHandle_t structQueue;
TaskHandle_t xTask1Handle;
TaskHandle_t xTask2Handle;
TaskHandle_t xTask3Handle;
TaskHandle_t xTask4Handle;
/************************************************************************************************************************/
void setup() {
sensor.begin();
lcd.init();
lcd.backlight();
Serial.begin(9600);
Serial.println ("Temp Control Systems");
Serial.print("\n");
pinMode(button_In, INPUT);
pinMode(fan_Out, OUTPUT);
structQueue = xQueueCreate(10, sizeof(struct data));
if (structQueue != NULL) {
xTaskCreate(Task_1, "Task 1", 128, NULL, 2, &xTask1Handle);
xTaskCreate(Task_2, "Task 2", 128, NULL, 3, &xTask2Handle);
xTaskCreate(Task_3, "Task 3", 128, NULL, 1, &xTask3Handle);
}
vTaskStartScheduler();
}
void loop() {
// Dikosongkan
}
/************************************************************************************************************************/
// Task1 Membaca temperature
void Task_1(void *pvParameters) {
UBaseType_t uxPriority = uxTaskPriorityGet(NULL);
for (;;) {
struct data currentData;
readingFunction();
currentData.Pointer = 0;
currentData.Value = reading_Temp;
xQueueSend(structQueue, ¤tData, portMAX_DELAY);
vTaskPrioritySet(xTask3Handle, (uxPriority +1));
vTaskPrioritySet(xTask4Handle, (uxPriority +1));
}
taskYIELD();
}
// Task2 Menampilkan data di LCD maupun Serial
void Task_2(void *pvParameters) {
for (;;) {
struct data currentData;
if (digitalRead(button_In)==LOW && xQueueReceive(structQueue, ¤tData, portMAX_DELAY) == pdPASS) {
if (currentData.Pointer==0) {
dtostrf (currentData.Value, 3, 2, temp);
sprintf (temp_buf, "%.2f", temp);
display_Func(temp, mode[0], fan);
serial_Func(temp, mode[0], fan);
}
else if (currentData.Pointer==1) {
dtostrf (currentData.Value, 3, 2, fan);
sprintf (fan_buf, "%.2f", fan);
display_Func(temp, mode[0], fan);
serial_Func(temp, mode[0], fan);
}
}
else if (digitalRead(button_In)==HIGH && xQueueReceive(structQueue, ¤tData, portMAX_DELAY) == pdPASS) {
if (currentData.Pointer==0) {
dtostrf (currentData.Value, 3, 2, temp);
sprintf (temp_buf, "%.2f", temp);
display_Func(temp, mode[1], fan);
serial_Func(temp, mode[1], fan);
}
else if (currentData.Pointer==1) {
dtostrf (currentData.Value, 3, 2, fan);
sprintf (fan_buf, "%.2f", fan);
display_Func(temp, mode[1], fan);
serial_Func(temp, mode[1], fan);
}
}
}
taskYIELD();
}
// Task3 Kendali Fan secara otomatis
void Task_3(void *pvParameters) {
UBaseType_t uxPriority = uxTaskPriorityGet(NULL);
for (;;) {
if (digitalRead(button_In)==LOW) {
auto_Func(reading_Temp);
struct data currentData;
currentData.Pointer = 1;
currentData.Value = (driving*100)/255;
xQueueSend(structQueue, ¤tData, portMAX_DELAY);
vTaskPrioritySet(xTask3Handle, (uxPriority - 1));
}
else if (digitalRead(button_In)==HIGH) {
xTaskCreate(Task_4, "Task 4", 128, NULL, 1, &xTask4Handle);
vTaskDelete(xTask3Handle);
}
}
taskYIELD();
}
// Task4 Kendali Fan secara manual berdasar input potensiometer
void Task_4(void *pvParameters) {
UBaseType_t uxPriority = uxTaskPriorityGet(NULL);
for (;;) {
if (digitalRead(button_In)==HIGH) {
manual_Func();
struct data currentData;
currentData.Pointer = 1;
currentData.Value = (driving*100)/255;
xQueueSend(structQueue, ¤tData, portMAX_DELAY);
vTaskPrioritySet(xTask4Handle, (uxPriority - 1));
}
else if (digitalRead(button_In)==LOW) {
xTaskCreate(Task_3, "Task 3", 128, NULL, 1, &xTask3Handle);
vTaskDelete(xTask4Handle);
}
}
taskYIELD();
}