// Toy for comparing rate limiting schemes
// Wokwi simulation: https://wokwi.com/projects/406476811823126529
// Slide the pot back and forth to change between schemes
// read how many times the code goes through loop() and gets
// a chance to do other things.
//
// See https://forum.arduino.cc/t/best-first-5-topics-to-read-to-reach-trust-level-1/1287592
// in particular the
//  https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158
//
// attach a potentiometer wiper pin to A0/modePin and the potentiometer extremes to GND and +
//
const byte modePin = A0;
const unsigned long delayIntervalMs = 30; //
int choice = 0;
unsigned long currentMillis;

void setup() {
  Serial.begin(115200); // Baud rate can limit loop speed too.
  // Try 115200 vs 9600 vs 300 to see baud rate effects
  Serial.print("rateLimitingToy -- https://wokwi.com/projects/406476811823126529\n"
               "Slide the pot to change between modes:\n"
               "while(..); , delay(), millis()/BWOD, unlimited\n"
              );
}

void loop() {
  Input(); // Read sensors //  Input of IPO programming model https://en.wikipedia.org/wiki/IPO_model
  Process();

  // Output
  report(); // count loop() cycles/second and print
}

void rateLimitedThing() {
  // Check the millis() clock to see if it is time to do a Thing.
  const unsigned long interval = delayIntervalMs;
  static unsigned long lastMs = 0;
  unsigned long now = millis();
  if (now - lastMs >= interval) {
    lastMs = now; // or lastMs += interval;
    Serial.print("r");
  }
}


void Input() { // Input of the IPO model https://en.wikipedia.org/wiki/IPO_model
  // this separates the input from the other bits of the program so the rest of it is
  // all working with the same data
  //This rate-limits the analogRead because we don't need to respond instantly
  const unsigned long interval = 50;
  static unsigned long lastMs = 0;
  currentMillis = millis();
  if (currentMillis - lastMs >= interval) {
    lastMs = currentMillis; // or lastMs += interval;
    choice = analogRead(modePin) / 256; // recode range as 0-3
  }
}

void Process() {
  // Processing
  switch (choice) { // choose a method of rate limiting to use for this cycle of loop()

    case 0: // stop while(waiting for something to end)
      Serial.print("while(something);");
      while (analogRead(modePin) / 256 == 0) {
        ;
      }
      Serial.print("...done\n");
      report();
      break;

    case 1: // slow down iterations with delay()
      Serial.print("d");
      delay(delayIntervalMs);
      break;

    case 2: // rate limited by testing time
      rateLimitedThing();
      break;

    case 3: // unlimited
      Serial.print("u");
      break;

    default:
      Serial.print("unexpected choice value");
      Serial.println(choice);
  }
}

void report() {
  // this routine cooperatively counts how many opportunities it
  // was given to run in a second
  static unsigned long loopcount = 0; // remember count
  const unsigned long interval = 1000;
  static unsigned long lastMs = 0;
  unsigned long now = millis();
  if (now - lastMs >= interval) { //
    // choose & uncomment one of these:
    //lastMs = now; // catch up
    //lastMs += interval; // phase locked count each
    while (now - lastMs >= interval) lastMs += interval; // phase loc with skip

    Serial.println();
    Serial.print(now);
    Serial.print("ms LoopCount: ");
    Serial.print(loopcount);
    Serial.print("/sec\n");
    loopcount = 0;
  }
  ++loopcount; // count this cycle
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
pot1:VCC
pot1:SIG
pot1:GND