// Question: How pre-emptive is FreeRTOS on a single core ?
// Answer  : It is fully pre-emptive in every way, you won't notice it. 
// This Wokwi project: 
//   https://wokwi.com/projects/403103153803776001
// Previous test with classic ESP32:
//   https://wokwi.com/projects/403090705288099841

const int pinLedZero  = 7;
const int pinLedOne   = 6;
const int pinLedTwo   = 5;
const int pinLedThree = 4;

volatile unsigned long counterOne;
bool stateOne = false;

void setup() 
{
  Serial.begin(115200);
  Serial.println();
  Serial.println("Test with ESP32-C3, single core RISC-V");

  pinMode(pinLedZero, OUTPUT);

  // Not only the priority can be set,
  // some systems have a extra "portPRIVILEGE_BIT"
  xTaskCreate(taskOne,   "One"  , 1000, NULL, 1, NULL);
  xTaskCreate(taskTwo,   "Two"  , 1000, NULL, 1, NULL);
  xTaskCreate(taskThree, "Three", 1000, NULL, 1, NULL);
}

void loop() 
{
  Serial.print("Task Zero: counter of task One is = ");
  Serial.println(counterOne);
  delay(1000);
  digitalWrite(pinLedZero, HIGH);
  delay(200);
  digitalWrite(pinLedZero, LOW);
}

void taskOne(void * pvParameters)
{
  pinMode(pinLedOne, OUTPUT);

  // Blocking code ! No delay, no wait.
  // Comment out the digitalWrite to be 100% sure.
  // The other tasks run as well.
  for(;;)
  {
    counterOne++;

    if(counterOne % 500000UL == 0)
    {
      digitalWrite(pinLedOne, stateOne ? HIGH : LOW);
      stateOne = !stateOne;
    }
  }
}

void taskTwo(void * pvParameters)
{
  pinMode(pinLedTwo, OUTPUT);
  for(;;)
  {
    digitalWrite(pinLedTwo, HIGH);
    delay(1000);  // delay calls vTaskDelay
    digitalWrite(pinLedTwo, LOW);
    delay(1000);  // delay calls vTaskDelay
  }
}

void taskThree(void * pvParameters)
{
  pinMode(pinLedThree, OUTPUT);
  for(;;)
  {
    digitalWrite(pinLedThree, HIGH);
    vTaskDelay(500 / portTICK_PERIOD_MS);
    digitalWrite(pinLedThree, LOW);
    vTaskDelay(500 / portTICK_PERIOD_MS);
  }
}