Untitled
unknown
c_cpp
9 months ago
10 kB
16
Indexable
/*
(Dalilul Falihin)
22/506495/TK/55478
TUGAS UTS
SISTEM TERTANAM DAN IOT
"Mini PLC on ESP32"
*/
#include <Arduino.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
// ================= CONFIG =================
#define NUM_INPUTS 4
#define NUM_OUTPUTS 4
const int inputPins[NUM_INPUTS] = {18, 19, 22, 23}; // tombol
const int outputPins[NUM_OUTPUTS] = {5, 12, 14, 15}; // lampu/relay
const int ANALOG_PIN = 34; // Potensiometer ADC input
#define MAX_IL_LINES 256
#define SCAN_CYCLE_MS 100 / portTICK_PERIOD_MS
#define DEBOUNCE_MS 40 / portTICK_PERIOD_MS
#define IO_POLL_MS 20 / portTICK_PERIOD_MS
// ================= Data Structures =================
typedef struct { int pin; bool value; } IOEvent;
typedef struct { String opcode; String operand; } ParsedLine;
// ================= Global State =================
String incomingLines[MAX_IL_LINES];
int incomingCount = 0;
ParsedLine program[MAX_IL_LINES];
int programLineCount = 0;
volatile bool receivingFile = false;
volatile bool fileReceived = false;
volatile bool programLoaded = false;
volatile bool programRunning = false;
int analogValue = 0; // global analog
bool lastStable[NUM_INPUTS] = {0};
TickType_t lastChangeTime[NUM_INPUTS] = {0};
// ================= Kernel Objects =================
SemaphoreHandle_t xConfigMutex;
SemaphoreHandle_t xFileReady;
QueueHandle_t ioQueue;
TimerHandle_t systemTimer;
// ================= Utility Functions =================
int operandToInputIndex(const String &op) {
String s = op; s.trim(); s.toUpperCase();
if (s == "IN1") return 0;
if (s == "IN2") return 1;
if (s == "IN3") return 2;
if (s == "IN4") return 3;
return -1;
}
int operandToOutputPin(const String &op) {
String s = op; s.trim(); s.toUpperCase();
if (s == "Q1") return outputPins[0];
if (s == "Q2") return outputPins[1];
if (s == "Q3") return outputPins[2];
if (s == "Q4") return outputPins[3];
return -1;
}
// ================= Analog Task =================
void analogTask(void *pvParameters) {
while (1) {
analogValue = analogRead(ANALOG_PIN);
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
// ================= Interrupt Handler Example =================
volatile bool interruptFlag = false;
void IRAM_ATTR inputISR() {
interruptFlag = true;
}
// ================= Serial Task =================
void serialTask(void *pvParameters) {
Serial.println("SerialTask Ready. Send PROGRAM_START ... PROGRAM_END");
String curLine = "";
while (1) {
if (Serial.available()) {
char c = Serial.read();
if (c == '\r') continue;
if (c == '\n') {
String L = curLine; L.trim();
if (xSemaphoreTake(xConfigMutex, (TickType_t)10) == pdTRUE) {
if (L == "PROGRAM_START") {
receivingFile = true;
incomingCount = 0;
fileReceived = false;
programLoaded = false;
programRunning = false;
Serial.println("[OK] START_RECEIVING");
} else if (L == "PROGRAM_END") {
receivingFile = false;
fileReceived = true;
Serial.println("[OK] END_RECEIVING");
} else if (L == "RUN") {
programRunning = true;
Serial.println("[OK] PROGRAM RUN");
} else if (L == "STOP") {
programRunning = false;
Serial.println("[OK] PROGRAM STOP");
} else if (receivingFile && L.length() > 0) {
if (incomingCount < MAX_IL_LINES) {
incomingLines[incomingCount++] = L;
Serial.printf("[LINE %d] %s\n", incomingCount, L.c_str());
}
}
xSemaphoreGive(xConfigMutex);
}
curLine = "";
} else curLine += c;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
// ================= Parser Task =================
void parserTask(void *pvParameters) {
while (1) {
if (fileReceived) {
if (xSemaphoreTake(xConfigMutex, portMAX_DELAY) == pdTRUE) {
programLineCount = 0;
for (int i = 0; i < incomingCount; i++) {
String ln = incomingLines[i]; ln.trim();
if (ln.length() == 0 || ln.startsWith(";")) continue;
String op = ln, operand = "";
int idx = ln.indexOf(' ');
if (idx > 0) { op = ln.substring(0, idx); operand = ln.substring(idx + 1); operand.trim(); }
op.toUpperCase();
program[programLineCount].opcode = op;
program[programLineCount].operand = operand;
programLineCount++;
}
fileReceived = false;
programLoaded = true;
xSemaphoreGive(xConfigMutex);
Serial.printf("PARSE COMPLETE | %d lines ready\n", programLineCount);
xSemaphoreGive(xFileReady);
}
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
// ================= IO Handler Task =================
void ioHandlerTask(void *pvParameters) {
for (int i = 0; i < NUM_INPUTS; i++) {
pinMode(inputPins[i], INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(inputPins[i]), inputISR, CHANGE);
lastStable[i] = digitalRead(inputPins[i]);
lastChangeTime[i] = xTaskGetTickCount();
}
for (int j = 0; j < NUM_OUTPUTS; j++) {
pinMode(outputPins[j], OUTPUT);
digitalWrite(outputPins[j], LOW);
}
IOEvent ev;
while (1) {
if (interruptFlag) {
interruptFlag = false;
Serial.println("[INT] Input changed!");
}
// Debounce check
for (int i = 0; i < NUM_INPUTS; i++) {
bool sample = digitalRead(inputPins[i]);
if (sample != lastStable[i]) {
TickType_t now = xTaskGetTickCount();
if ((now - lastChangeTime[i]) >= DEBOUNCE_MS) {
lastStable[i] = sample;
lastChangeTime[i] = now;
Serial.printf("IN%d -> %d\n", i + 1, sample);
}
}
}
while (xQueueReceive(ioQueue, &ev, 0) == pdTRUE) {
digitalWrite(ev.pin, ev.value ? HIGH : LOW);
Serial.printf("OUT PIN %d => %d\n", ev.pin, ev.value);
}
vTaskDelay(IO_POLL_MS);
}
}
// ================= Executor Task =================
void executorTask(void *pvParameters) {
bool acc = false;
bool inputState[NUM_INPUTS] = {0};
bool lastOutputState[NUM_OUTPUTS] = {0};
while (1) {
if (xSemaphoreTake(xFileReady, 0) == pdTRUE)
Serial.println("[EXEC] Program ready (use RUN).");
if (programLoaded && programRunning) {
for (int i = 0; i < NUM_INPUTS; i++) inputState[i] = lastStable[i];
acc = false;
if (xSemaphoreTake(xConfigMutex, portMAX_DELAY) == pdTRUE) {
for (int i = 0; i < programLineCount; i++) {
String op = program[i].opcode;
String operand = program[i].operand;
op.toUpperCase();
if (op == "LD") {
if (operand == "ANA1") acc = (analogValue > 0);
else {
int idx = operandToInputIndex(operand);
acc = (idx != -1) ? inputState[idx] : operand.toInt() != 0;
}
} else if (op == "GT") {
acc = (analogValue > operand.toInt());
} else if (op == "AND") {
int idx = operandToInputIndex(operand);
bool val = (idx != -1) ? inputState[idx] : operand.toInt() != 0;
acc = acc && val;
} else if (op == "OR") {
int idx = operandToInputIndex(operand);
bool val = (idx != -1) ? inputState[idx] : operand.toInt() != 0;
acc = acc || val;
} else if (op == "NOT") {
acc = !acc;
} else if (op == "ST" || op == "OUT") {
int pin = operandToOutputPin(operand);
if (pin != -1) {
int outIndex = -1;
for (int j = 0; j < NUM_OUTPUTS; j++)
if (outputPins[j] == pin) outIndex = j;
bool outVal = acc;
if (outIndex != -1 && lastOutputState[outIndex] != outVal) {
IOEvent ev = { pin, outVal };
xQueueSend(ioQueue, &ev, 0);
lastOutputState[outIndex] = outVal;
}
}
}
}
xSemaphoreGive(xConfigMutex);
}
vTaskDelay(SCAN_CYCLE_MS);
} else {
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
}
// ================= Timer Callback =================
void systemTimerCallback(TimerHandle_t xTimer) {
Serial.printf("[TIMER] Heap: %u | ANA1: %d | Running: %s\n",
esp_get_free_heap_size(), analogValue,
programRunning ? "YES" : "NO");
}
// ================= Setup =================
void setup() {
Serial.begin(115200);
while (!Serial) vTaskDelay(10 / portTICK_PERIOD_MS);
xConfigMutex = xSemaphoreCreateMutex();
xFileReady = xSemaphoreCreateBinary();
ioQueue = xQueueCreate(16, sizeof(IOEvent));
systemTimer = xTimerCreate("SysTimer", pdMS_TO_TICKS(5000), pdTRUE, 0, systemTimerCallback);
xTimerStart(systemTimer, 0);
pinMode(ANALOG_PIN, INPUT);
xTaskCreatePinnedToCore(serialTask, "Serial", 4096, NULL, 3, NULL, 1);
xTaskCreatePinnedToCore(parserTask, "Parser", 4096, NULL, 2, NULL, 1);
xTaskCreatePinnedToCore(ioHandlerTask, "IO", 4096, NULL, 2, NULL, 0);
xTaskCreatePinnedToCore(executorTask, "Exec", 4096, NULL, 2, NULL, 0);
xTaskCreatePinnedToCore(analogTask, "Analog", 2048, NULL, 1, NULL, 0);
Serial.println("=== PLC Mini FULL BONUS EDITION Ready ===");
Serial.println("Use IL:");
Serial.println("PROGRAM_START");
Serial.println("LD ANA1");
Serial.println("GT 2000");
Serial.println("ST Q1");
Serial.println("PROGRAM_END");
Serial.println("RUN");
}
void loop() {
vTaskDelete(NULL);
}
Editor is loading...
Leave a Comment