diff --git a/mc_labs/mc_lab_01/Panechko_Taras_Lab_1/lab.ino b/mc_labs/mc_lab_01/Panechko_Taras_Lab_1/lab.ino new file mode 100644 index 0000000..c077eb6 --- /dev/null +++ b/mc_labs/mc_lab_01/Panechko_Taras_Lab_1/lab.ino @@ -0,0 +1,150 @@ +#include +#include + +const char* ssid = "Galaxy S20 FE 5G"; +const char* password = "0985211834"; + +ESP8266WebServer server(80); + +const int ledPins[] = {13, 0, 2}; // LEDs on pins 13, 0, 2 +const int buttonPin = 15; // Button on pin 15 + +const int ledInterval = 500; // Time between LED changes +const int debounceTime = 50; // Button debounce time +const int doubleClickTime = 300; // Time for double click + +int currentLedIndex = 0; +bool reverseDirection = false; +bool isRunning = false; +bool buttonIsPressed = false; + +unsigned long lastDebounce = 0; +unsigned long lastClick = 0; +unsigned long lastWebClick = 0; + +int clickCount = 0; +int webClickCount = 0; + +void setup() { + WiFi.begin(ssid, password); + Serial.begin(115200); + + while (WiFi.status() != WL_CONNECTED) { + delay(1000); + Serial.print("."); + } + + Serial.println("\nIP Address: " + WiFi.localIP().toString()); + + pinMode(buttonPin, INPUT_PULLUP); + for (int ledPin : ledPins) { + pinMode(ledPin, OUTPUT); + } + + server.on("/", handleWebPage); + server.on("/click", handleButtonClick); + server.begin(); +} + +void loop() { + server.handleClient(); + handleButtonInput(); + handleDoubleClick(); + updateLeds(); +} + +void handleWebPage() { + String html = " +

LED Control

+ + "; + server.send(200, "text/html", html); +} + +void handleButtonClick() { + unsigned long currentTime = millis(); + + if (currentTime - lastWebClick < doubleClickTime) { + webClickCount++; + } else { + webClickCount = 1; + } + lastWebClick = currentTime; + + if (webClickCount == 2) { + isRunning = !isRunning; + if (isRunning) { + reverseDirection = !reverseDirection; + Serial.println("Direction changed (Web)"); + } + webClickCount = 0; + } + server.send(200, "text/plain", "OK"); +} + +void handleButtonInput() { + unsigned long currentTime = millis(); + int buttonState = digitalRead(buttonPin); + static bool lastButtonState = HIGH; + + if (buttonState != lastButtonState) { + lastDebounce = currentTime; + lastButtonState = buttonState; + } + + if ((currentTime - lastDebounce) > debounceTime) { + if (buttonState == LOW && !buttonIsPressed) { + buttonIsPressed = true; + if (currentTime - lastClick < doubleClickTime) { + clickCount++; + } else { + clickCount = 1; + } + lastClick = currentTime; + lastButtonState = HIGH; + } else if (buttonState == HIGH && buttonIsPressed) { + buttonIsPressed = false; + } + } +} + +void handleDoubleClick() { + if (clickCount == 2) { + isRunning = !isRunning; + clickCount = 0; + if (isRunning) { + reverseDirection = !reverseDirection; + Serial.println("Direction changed (Button)"); + } + } +} + +void updateLeds() { + if (!isRunning) { + for (int ledPin : ledPins) { + digitalWrite(ledPin, LOW); + } + return; + } + + unsigned long currentTime = millis(); + static unsigned long lastLedChange = 0; + + if (currentTime - lastLedChange > ledInterval) { + lastLedChange = currentTime; + + digitalWrite(ledPins[currentLedIndex], LOW); + + if (reverseDirection) { + currentLedIndex = (currentLedIndex - 1 + 3) % 3; + } else { + currentLedIndex = (currentLedIndex + 1) % 3; + } + + digitalWrite(ledPins[currentLedIndex], HIGH); + } +} \ No newline at end of file diff --git a/mc_labs/mc_lab_01/Panechko_Taras_Lab_1/schema.png b/mc_labs/mc_lab_01/Panechko_Taras_Lab_1/schema.png new file mode 100644 index 0000000..17d6c14 Binary files /dev/null and b/mc_labs/mc_lab_01/Panechko_Taras_Lab_1/schema.png differ diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/.gitignore b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/.gitignore new file mode 100644 index 0000000..0e36b7e --- /dev/null +++ b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/.gitignore @@ -0,0 +1,2 @@ +.pio +/.idea diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/include/README b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/include/README new file mode 100644 index 0000000..e69de29 diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/lib/README b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/lib/README new file mode 100644 index 0000000..9379397 --- /dev/null +++ b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into the executable file. + +The source code of each library should be placed in a separate directory +("lib/your_library_name/[Code]"). + +For example, see the structure of the following example libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Example contents of `src/main.c` using Foo and Bar: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +The PlatformIO Library Dependency Finder will find automatically dependent +libraries by scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/scheme/scheme.jpg b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/scheme/scheme.jpg new file mode 100644 index 0000000..eae82d2 Binary files /dev/null and b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/scheme/scheme.jpg differ diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/src/main.cpp b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/src/main.cpp new file mode 100644 index 0000000..c1a2de1 --- /dev/null +++ b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/src/main.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include + +#define SSID "bodyaFrayer" +#define PASSWORD "12345678" +#define BLINK_INTERVAL 500 +#define HOLD_INTERVAL 500 +#define DEBOUNCE_TIME 50 +#define DOUBLE_CLICK_TIME 300 + +enum class Color { RED, YELLOW, GREEN }; + +typedef struct led_s { + const uint8_t pin; + bool state; + led_s *next; + led_s *prev; + Color color; +} led_t; + +typedef struct button_s { + uint8_t pin; + bool state; + bool wasPressed; + uint32_t pressStartTime; + uint32_t lastClickTime; + uint32_t lastDebounce; + uint8_t clickCount; + bool hardIsHeld; + bool webIsHeld; + bool serialIsHeld; + bool isPressed; + bool lastState; +} button_t; + +led_t redLED = {13, LOW, nullptr, nullptr, Color::RED}; +led_t yellowLED = {12, LOW, nullptr, nullptr, Color::YELLOW}; +led_t greenLED = {14, LOW, nullptr, nullptr, Color::GREEN}; +led_t *currentLED = &redLED; +button_t button = {16, LOW, false, 0, 0, 0, 0, false, false, false, false, HIGH}; + +AsyncWebServer server(80); +AsyncWebSocket ws("/ws"); + +bool reverseDirection = false; +bool isRunning = false; +uint32_t previousBlinkTime = 0; + +enum class Command { NONE, START, STOP }; +Command webCommand = Command::NONE; + +void setupLEDOrder() { + redLED.next = &yellowLED; + redLED.prev = &greenLED; + + yellowLED.next = &greenLED; + yellowLED.prev = &redLED; + + greenLED.next = &redLED; + greenLED.prev = &yellowLED; +} + +void pinSetup() { + pinMode(redLED.pin, OUTPUT); + pinMode(yellowLED.pin, OUTPUT); + pinMode(greenLED.pin, OUTPUT); + pinMode(button.pin, INPUT_PULLUP); +} + +void lightLEDs() { + digitalWrite(redLED.pin, redLED.state); + digitalWrite(yellowLED.pin, yellowLED.state); + digitalWrite(greenLED.pin, greenLED.state); +} + +void lightNextLED() { + uint32_t now = millis(); + if (now - previousBlinkTime >= BLINK_INTERVAL) { + previousBlinkTime = now; + currentLED->state = LOW; + + currentLED = reverseDirection ? currentLED->prev : currentLED->next; + currentLED->state = HIGH; + + lightLEDs(); + switch (currentLED->color) { + case Color::RED: ws.textAll("red"); break; + case Color::YELLOW: ws.textAll("yellow"); break; + case Color::GREEN: ws.textAll("green"); break; + } + } +} + +void turnOffLEDs() { + redLED.state = LOW; + yellowLED.state = LOW; + greenLED.state = LOW; + lightLEDs(); +} + +// --- Web control handlers --- +void handleHold(AsyncWebServerRequest *request) { + button.webIsHeld = true; + request->send_P(200, "text/html", "ok"); +} + +void handleReleased(AsyncWebServerRequest *request) { + button.webIsHeld = false; + request->send_P(200, "text/html", "ok"); +} + +void sendStartSignal(AsyncWebServerRequest *request) { + webCommand = Command::START; + request->send_P(200, "text/html", "ok"); +} + +void sendStopSignal(AsyncWebServerRequest *request) { + webCommand = Command::STOP; + request->send_P(200, "text/html", "ok"); +} + +void serverSetup() { + WiFi.softAP(SSID, PASSWORD); + LittleFS.begin(); + server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html"); + server.on("/hold", HTTP_GET, handleHold); + server.on("/release", HTTP_GET, handleReleased); + server.on("/start", HTTP_GET, sendStartSignal); + server.on("/stop", HTTP_GET, sendStopSignal); + server.addHandler(&ws); + server.begin(); +} + +void handleButtonInput(uint32_t now) { + bool state = digitalRead(button.pin) == LOW; + if (state != button.lastState) { + button.lastDebounce = now; + button.lastState = state; + } + + if ((now - button.lastDebounce) > DEBOUNCE_TIME) { + if (state && !button.isPressed) { + button.isPressed = true; + if (now - button.lastClickTime < DOUBLE_CLICK_TIME) { + button.clickCount++; + } else { + button.clickCount = 1; + } + button.lastClickTime = now; + } else if (!state && button.isPressed) { + button.isPressed = false; + } + } + + if (button.clickCount == 2) { + isRunning = !isRunning; + if (isRunning) { + reverseDirection = !reverseDirection; + Serial.println("Direction changed (Button)"); + } + button.clickCount = 0; + } +} + +void checkWebCommand() { + if (webCommand == Command::START) + { + isRunning = true; + reverseDirection = !reverseDirection; + Serial.println("Direction changed (Web)"); + webCommand = Command::NONE; + } + else if (webCommand == Command::STOP) + { + isRunning = false; + webCommand = Command::NONE; + } +} + +void setup() { + Serial.begin(115200); + setupLEDOrder(); + pinSetup(); + serverSetup(); +} + +void loop() { + static uint32_t now = millis(); + + handleButtonInput(now); + checkWebCommand(); + + if (isRunning || button.webIsHeld || button.serialIsHeld || button.hardIsHeld) + { + lightNextLED(); + } + else + { + turnOffLEDs(); + } +} \ No newline at end of file diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/test/README b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/index.html b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/index.html new file mode 100644 index 0000000..53dba45 --- /dev/null +++ b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/index.html @@ -0,0 +1,23 @@ + + + + + + + +

ESP Pushbutton Web Server

+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/main.js b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/main.js new file mode 100644 index 0000000..6ba8211 --- /dev/null +++ b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/main.js @@ -0,0 +1,59 @@ +const button1 = document.getElementById("algo1"); +const button2 = document.getElementById("algo2"); + +const holdTime = 500; +let timer1, wasHeldEnough1, timer2, wasHeldEnough2; + +button1.addEventListener('mousedown', handleHold); +button1.addEventListener('mouseup', handleRelease); +button1.addEventListener('touchstart', handleHold); +button1.addEventListener('touchend', handleRelease); + +button2.addEventListener('mousedown', handleStart); +button2.addEventListener('mouseup', handleStop); +button2.addEventListener('touchstart', handleStart); +button2.addEventListener('touchend', handleStop); + +function handleHold() { + wasHeldEnough1 = false; + timer1 = setTimeout(() => { + wasHeldEnough1 = true; + fetch('/hold').then(); + }, holdTime); +} + +function handleRelease() { + if (wasHeldEnough1) { + fetch('/release').then(); + } + clearTimeout(timer1); + wasHeldEnough1 = false; +} + +function handleStart() { + wasHeldEnough2 = false; + timer2 = setTimeout(() => { + wasHeldEnough2 = true; + fetch('/start').then(); + }, holdTime); +} + +function handleStop() { + + if (wasHeldEnough2) { + fetch('/stop').then(); + } + clearTimeout(timer2); + wasHeldEnough2 = false; +} + +const socket = new WebSocket("ws://192.168.4.1/ws"); + +socket.onmessage = (event) => { + let data = event.data; + document.querySelectorAll('.led').forEach(led => led.classList.remove('active')); + + if (data === "red") document.getElementById("red").classList.add("active"); + if (data === "yellow") document.getElementById("yellow").classList.add("active"); + if (data === "green") document.getElementById("green").classList.add("active"); +} diff --git a/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/style.css b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/style.css new file mode 100644 index 0000000..44b5edf --- /dev/null +++ b/mc_labs/mc_lab_02/Panechko_Taras_lab_02/web/style.css @@ -0,0 +1,75 @@ +body { + text-align: center; + margin: 0 auto; + padding-top: 30px; + background-color: #c4c4c4; +} + +.button { + padding: 10px 20px; + font-size: 24px; + text-align: center; + outline: none; + color: #fff; + background-color: #2f4468; + border: none; + border-radius: 5px; + box-shadow: 0 6px #999; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +.button:hover { + background-color: #1f2e45 +} + +.button:active { + background-color: #1f2e45; + box-shadow: 0 4px #666; + transform: translateY(2px); +} + +.ledsContainer { + width: 500px; + max-width: 100vw; + padding: 50px 10px 0 10px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + + .led { + width: 50px; + height: 50px; + border-radius: 50%; + + &#red { + border: 5px solid red; + + &.active { + background: red; + } + } + + &#green { + border: 5px solid green; + + &.active { + background: green; + } + } + + &#yellow { + border: 5px solid yellow; + + &.active { + background: yellow; + } + } + } +} \ No newline at end of file