#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#include <Arduino_FreeRTOS.h>

#define SERVO_PIN 9
#define POTENTIOMETER_PIN A0

#define LCD_ADDRESS 0x27
#define LCD_COLUMNS 16
#define LCD_ROWS 2

Servo myservo;
LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLUMNS, LCD_ROWS);
TaskHandle_t xHandle_LCD;

volatile int potValue = 0;
volatile int servoPos = 0;
volatile bool blinkingState = false;

void TaskReadPotentiometer(void *pvParameters) {
  (void)pvParameters;

  for (;;) {
    potValue = analogRead(POTENTIOMETER_PIN);
    servoPos = map(potValue, 0, 1023, 0, LCD_COLUMNS); 
    
    vTaskDelay(10 / portTICK_PERIOD_MS);
  }
}

void TaskControlServo(void *pvParameters) {
  (void)pvParameters;

  for (;;) {
    int targetPosition = map(potValue, 0, 1023, 0, 180); 
    myservo.write(targetPosition);
    vTaskDelay(20 / portTICK_PERIOD_MS); 
  }
}

void TaskUpdateLCD(void *pvParameters) {
  (void)pvParameters;

  for (;;) {
    blinkingState = !blinkingState;
    vTaskDelay(500 / portTICK_PERIOD_MS);
  }
}

void TaskDisplayLCD(void *pvParameters) {
  (void)pvParameters;

  lcd.init();
  lcd.backlight();

  const char blinkingChar[8] = {
    B11111,
    B10001,
    B10001,
    B10001,
    B10001,
    B10001,
    B10001,
    B11111
  };
  lcd.createChar(1, blinkingChar);
  
  byte squareChar[8] = {
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111
  };
  lcd.createChar(0, squareChar);
  

  bool displayBlink = false;

  for (;;) {
    lcd.setCursor(0, 0);
    for (int i = 0; i < LCD_COLUMNS; ++i) {
      lcd.write(i < servoPos ? 0 : ' ');
    }

    lcd.setCursor(0, 1);
    if (displayBlink) {
      lcd.print(char(1));
    } else {
      lcd.print(' ');  
    }
    displayBlink = !displayBlink;

    vTaskDelay(100 / portTICK_PERIOD_MS);
  }
}


void setup() {
  myservo.attach(SERVO_PIN);

  xTaskCreate(TaskReadPotentiometer, "ReadPot", 128, NULL, 1, NULL);
  xTaskCreate(TaskUpdateLCD, "UpdateLCD", 128, NULL, 2, NULL);
  xTaskCreate(TaskDisplayLCD, "DisplayLCD", 128, NULL, 3, &xHandle_LCD);
  xTaskCreate(TaskControlServo, "ControlServo", 128, NULL, 4, NULL);

  vTaskStartScheduler();
}

void loop() {
 
}