#include <Arduino.h>

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>

#include <ESP32Servo.h>

#include <U8g2lib.h>
#include <Wire.h>


#define PIN_SERVO 23
#define PIN_SDA 21
#define PIN_SCL 22

#define PIN_A 21
#define PIN_B 19
#define PIN_C 18
#define PIN_D 5

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);


void busyTime(uint32_t n) {
  static volatile uint32_t dummy = 1234;
  for (uint32_t i = 0; i < n; i++) {
    dummy = (dummy + 5678) / 27;
  }
}

void busyTime_ms(uint32_t ms) {
  static float calib = 0.0;
  if (calib == 0.0) {
    // First time: calibrate the busy loop
    for (int n = 8; n < 0x04000; n *= 2) {
      int32_t time_us = -micros();
      busyTime(n);
      time_us += micros();
      if (time_us > 10*1000) { /* > 10ms */
        calib = n * 1000.0f / time_us; /* n = loops / ms */
        Serial.printf("%u loops in %d µs\n", n, time_us);
        Serial.printf("Calibration factor: %3.3f loops per ms\n", calib);
        break;
      }
    }
  }
  busyTime(ms * calib);
}


void taskA(void *pvParameters) {
  int counter = 0;
  TickType_t xNextWakeTime = xTaskGetTickCount();
  while (1) {
    digitalWrite(PIN_A, HIGH);
    busyTime_ms(10);
    digitalWrite(PIN_A, LOW);
    vTaskDelayUntil(&xNextWakeTime, pdMS_TO_TICKS(40));
  }
}

void taskB(void *pvParameters) {
  TickType_t xNextWakeTime = xTaskGetTickCount();
  while (1) {
    digitalWrite(PIN_B, HIGH);
    busyTime_ms(20);
    digitalWrite(PIN_B, LOW);
    vTaskDelayUntil(&xNextWakeTime, pdMS_TO_TICKS(80));
  }
}

void taskC(void *pvParameters) {
  TickType_t xNextWakeTime = xTaskGetTickCount();
  while (1) {
    digitalWrite(PIN_C, HIGH);
    busyTime_ms(30);
    digitalWrite(PIN_C, LOW);
    vTaskDelayUntil(&xNextWakeTime, pdMS_TO_TICKS(120));
  }
}



void setup() {
  Serial.begin(115200);
  Serial.printf("\n\n\n");
  Serial.printf("Application " __FILE__ " compiled " __DATE__ " at " __TIME__ " \n");

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(PIN_A, OUTPUT);
  pinMode(PIN_B, OUTPUT);
  pinMode(PIN_C, OUTPUT);

  digitalWrite(PIN_A, LOW);
  digitalWrite(PIN_B, LOW);
  digitalWrite(PIN_C, LOW);

  // Calibrate the busy loop
  busyTime_ms(0);

  u8g2.begin();
  u8g2.clearBuffer();					// clear the internal memory
  u8g2.setFont(u8g2_font_ncenB08_tr);	// choose a suitable font
  u8g2.drawStr(15, 10, "LONELY BINARY");	// write something to the internal memory
  u8g2.sendBuffer();					// transfer internal memory to the display


  xTaskCreatePinnedToCore(taskA, "Task A", configMINIMAL_STACK_SIZE + 1024,
                          NULL, tskIDLE_PRIORITY + 3, NULL,
                          APP_CPU_NUM);

  xTaskCreatePinnedToCore(taskB, "Task B", configMINIMAL_STACK_SIZE + 1024,
                          NULL, tskIDLE_PRIORITY + 2, NULL,
                          APP_CPU_NUM);

  xTaskCreatePinnedToCore(taskC, "Task C", configMINIMAL_STACK_SIZE + 1024,
                          NULL, tskIDLE_PRIORITY + 1, NULL,
                          APP_CPU_NUM);

  Serial.printf("Ready to start!\n");

}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(100);
  digitalWrite(LED_BUILTIN, LOW);
  delay(400);
}
D0D1D2D3D4D5D6D7GNDLOGIC