Wokwi Simulations for Arduino Built-In Examples

This thread contains links to Wokwi simulations for some of the Arduino Built-In Examples The built-in examples are available within the IDE under the File/Examples/ tab.

Basics

Digital

Analog

Communication

Control Structures

Sensors

Non-Arduino Serial communication simulations:

Serial Input Basics by @Robin2

  • Example 1 -- Receiving single characters.
  • Example 2 -- Receive several chars with an end marker.
  • Example 3 -- Receive with start- and end-markers
  • Example 4 -- Receiving a single number from the Serial Monitor
  • Example 5 -- Receiving and parsing several pieces of data
  • Example 6 -- Program to receive binary data

How to process incoming serial data without blocking by @nickgammon

Misc

======

The list is an incomplete version of Arduino Built-In Examples. I think it is useful for folks that can't be bothered to wire up an example, or for folks without hardware, so they can examine the behavior of a tested working example. It might also help folks explain how one, can, for example, use the basic analog and LED examples to handle their very special devices that provide or consume digital or analog outputs.

If you know of, or want to build other simulations of the Arduino Built-In Examples, please point them out and I'll add them to this list.

If you want to discuss the dangers, failings, obvious improvements and next steps, or finer points of a particular built-in example, please start a separate forum thread for it along the lines of How to Wire and Program a Button or State Change Detection (Edge Detection) for pushbuttons and I can add that discuss to this list.

7 Likes

This one is easy to achieve with wokwi


you got the tutorial link wrong on that one

the tutorial link is https://docs.arduino.cc/built-in-examples/digital/toneKeyboard/

(which could be achieved in wokwi using wokwi-slide-potentiometer instead of the force sensor).

1 Like

Thanks. Added the Play a Melody and a toneKeyboard one. And also fixed the tutorial link.

I added a few Serial Communication simulations. However several of them depend on connecting to Processing and receiving binary bytes.

Yes wokwi gets limited when it comes to interfacing with a second device (two arduinos or arduino to computer)

To simulate some second device IO, I tried using a hack of the Wokwi Custom Chip UART API page's ROT13 example with a Mega here:

But it seems to drop random bytes:

Other Libraries might be interesting. Here are some stepper motor control library example simulations:

Stepper.h

Examples

MobaTools MobaStepper

This library moves steppers with acceleration in the background, and supplies a lot of other functionality (Servos, Buttons, LEDs, timers)

Simulations

AccelStepper

This non-blocking library uses a smooth acceleration scheme to move steppers.

Examples

  • AFMotor_ConstantSpeed
  • AFMotor_MultiStepper
  • Blocking
  • Bounce -- Simulation: Bounces +/-500 steps
  • ConstantSpeed -- Simulation: one stepper at 50steps/sec, 15RPM
  • DualMotorShield -- Simulation: two steppers bounce back and forth at different speeds.
  • MotorShield
  • MultiStepper -- Simulation: two steppers in coordinated motion to multiple positions (no acceleration)
  • MultipleSteppers -- Simulation: Three steppers with acceleration
  • Overshoot -- Simulation: Demonstrate overshooting past 300 and smoothly returning to 0.
  • ProportionalControl -- Simulation: Follow potentiometer position with acceleration.
  • Quickstop -- Simulation: Accelerate towards +/- 500 but stop ASAP after +300&0 subject to acceleration limits. (With an improved simulation)
  • Random -- Simulation: accelerate to random positions with random speeds and accelerations.

FastAccelStepper

Examples

Servo

Examples

  • Knob -- Simulation: Servo motion follows the movement of a potentiometer knob.
  • Sweep -- Simulation: Servo sweeps back and forth at controlled speeds without intervention.

Bonuses

Thank you for posting this.

1 Like

Here's a couple simulations for the Arduino_PID_Library

Simulations:

Examples built into the Encoder.h library:

Encoder.h Simulations:

  • Basic.ino -- one knob reporting to serial monitor, with Scope.
  • TwoKnobs.ino -- two knobs reporting position to serial monitor (scope triggered on first knob)

Other encoder examples from the wokwi-ky-040 Rotary Encoder Reference | Wokwi Docs

Bonus:

  • Encoder_6variables -- one encoder controls six variables, displayed on graphics screen
  • Encoder_ring -- Encoder controls NumberOfLeds, Hue, Saturation, and Value for a Neopixel ring.
1 Like

Doing several things at the same time:

The last link is a thread combining several of these already-doing-several-things-at-the-same-time codes:

And those programs are combined into two different simulations:

code for three combined non-blocking programs
// https://wokwi.com/projects/410224559941422081
// For https://forum.arduino.cc/t/millis-instead-of-delay-on-array-of-ledstrip/1304862/118
// Example of combining four non-blocking codes, cut-and-pasted together:
/*
  1) https://wokwi.com/projects/410122241449166849 -- Mobatools Reference2 Example by @MicroBahner
  2) https://wokwi.com/projects/366664987128970241 -- SeveralThingsAtTheSameTime by @Robin2
  3) https://wokwi.com/projects/410037772389117953 -- Some Fastleds by @DaveX
  4) Loop Timer https://forum.arduino.cc/t/millis-instead-of-delay-on-array-of-ledstrip/1304862/122?u=davex
*/
////////// Example Reference for MoToStepper - attaching a bipolar stepper with step/dir and enable ////////////
// https://github.com/MicroBahner/MobaTools/blob/master/examples/_Stepper/Stepper_Reference_2/Stepper_Reference_2.ino
// https://wokwi.com/projects/410122241449166849
// for https://forum.arduino.cc/t/wokwi-simulations-for-arduino-built-in-examples/1304754/6


/* An Example with 3 steppers ( 2 unipolar, 1 bipolar ).
    A reference run is started in setup.  2 limit switches are required for this.
    This example does not run on ESP8266
*/


//==============   Declarations

//======================================================
//================= Declarations First program
//======================================================

#include <MobaTools.h>
MoToTimer Pause;
MoToStepper Step_X(4096);                    // X-Achse, unipolarer Schrittmotor 28BYJ-48
MoToStepper Step_Y(4096);                    // Y-Achse, unipolarer Schrittmotor 28BYJ-48
MoToStepper Step_Z(200, STEPDIR);            // Z-Achse, bipolarer Schrittmotor mit Treiber wie A4988, DRV8825 oder vergleichbare
const byte pinRef[] = { A0, A1 };            // pinRef_X, pinRef_Y
const byte nbrOfButtons = sizeof(pinRef);    // Anzahl der angeschlossenen Taster
enum {X_AXIS = 0, Y_AXIS = 1};

//======================================================
//================= Declarations Second program
//======================================================
//Several Things At The Same Time
// -----LIBRARIES

#include <Servo.h>

// ----CONSTANTS (won't change)

const int onBoardLedPin =  14;      // the pin numbers for the LEDs
const int led_A_Pin = 15;
const int led_B_Pin = 16;
const int buttonLed_Pin = 17;

const int buttonPin = 20; // the pin number for the button

const int servoPin = 19; // the pin number for the servo signal

const int onBoardLedInterval = 500; // number of millisecs between blinks
const int led_A_Interval = 2500;
const int led_B_Interval = 4500;

const int blinkDuration = 500; // number of millisecs that Led's are on - all three leds use this

const int buttonInterval = 300; // number of millisecs between button readings

const int servoMinDegrees = 20; // the limits to servo movement
const int servoMaxDegrees = 150;

//------- VARIABLES (will change)

byte onBoardLedState = LOW;             // used to record whether the LEDs are on or off
byte led_A_State = LOW;           //   LOW = off
byte led_B_State = LOW;
byte buttonLed_State = LOW;

Servo myservo;  // create servo object to control a servo

int servoPosition = 90;     // the current angle of the servo - starting at 90.
int servoSlowInterval = 80; // millisecs between servo moves
int servoFastInterval = 10;
int servoInterval = servoSlowInterval; // initial millisecs between servo moves
int servoDegrees = 2;       // amount servo moves at each step
//    will be changed to negative value for movement in the other direction

unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousOnBoardLedMillis = 0;   // will store last time the LED was updated
unsigned long previousLed_A_Millis = 0;
unsigned long previousLed_B_Millis = 0;

unsigned long previousButtonMillis = 0; // time when button press last checked

unsigned long previousServoMillis = 0; // the time when the servo was last moved

//======================================================
//==== Declarations  Third program
//======================================================
//
// fastled DaveX from https://wokwi.com/projects/410037772389117953
//
#include "FastLED.h"

const int DaveXButton = 21;
const int DaveXLED = 18;
#define NUM_LEDS1 26
#define DATA_PIN1 35
CRGB array_LED_sconce [NUM_LEDS1];
uint32_t interval = 1000, timeout;
// subset of ledstrip to color
int ledBlink[] = {4, 12, 20, 0, 8, 16, 24};

int fastledState;
CHSV daveXringColor = CHSV(70, 255, 255);

extern volatile unsigned long timer0_millis; // might be faster than millis()

//======================================================
//================= Arduino minimal program requirements
//======================================================

void setup() {
  Serial.begin(115200);
  Serial.println("Starting a Combination of Four Programs");
  setupMoba();
  setupSTATST();
  setupDaveX();
}

void loop() {
  loopMoba();
  loopSTATST();
  loopDaveX();
  LoopCounter(); // the function runs as a task, the optimizer will inline the code.

}

//======================================================
//======== renamed functions from component programs
//======================================================

void setupMoba() {
  Serial.println("MobaTools Reference 2 Example  https://wokwi.com/projects/410123341513466881");  // so we know what sketch is running

  for (byte i = 0; i < nbrOfButtons; i++)  {
    pinMode(pinRef[i], INPUT_PULLUP);        // gedrückt = LOW
  }
  Step_X.attach( 12, 11, 10, 9 );            // IN1, IN2, IN3, IN4
  Step_Y.attach( 5, 4, 3, 2 );               // IN1, IN2, IN3, IN4

  Step_Z.attach( 7, 8 );                     // STEPpin, DIRpin
  Step_Z.setSpeed( 800 );                    // = 80 U/Min (motorspezifisch)
  Step_Z.setRampLen( 50 );                   // Beschleunigung (motorspezifisch)
  Step_Z.write(360);                         // Winkel 360 Grad drehen
 // Pause.setTime( 2500 );                     // Dreh- und Pausenzeit
}

void loopMoba() {
  enum {SEEK_ZERO, MOVE_FORWARD, MOVE_BACWARD};
  static byte step_X = SEEK_ZERO;          // Schrittkettenstatus X-Achse
  static byte step_Y = SEEK_ZERO;          // Schrittkettenstatus Y-Achse
  static int16_t angle = 360;

  //-- X-Achse
  switch (step_X) {
    case SEEK_ZERO:
      if (seekZeropoint(Step_X, X_AXIS)) {
        step_X = MOVE_FORWARD;
      }
      break;
    case MOVE_FORWARD:
      if ( !Step_X.moving() ) {              // warten bis die Bewegung abgeschlossen ist
        Step_X.setSpeed( 200 );              // = 20 U/Min (motorspezifisch)
        Step_X.setRampLen( 100 );            // Beschleunigung (motorspezifisch)
        Step_X.writeSteps(4096);
        step_X = MOVE_BACWARD;
      }
      break;
    case MOVE_BACWARD:
      if ( !Step_X.moving() ) {              // warten bis die Bewegung abgeschlossen ist
        Step_X.writeSteps(-4096);
        step_X = MOVE_FORWARD;
      }
      break;
  }
  //-- Y-Achse
  switch (step_Y) {
    case SEEK_ZERO:
      if (seekZeropoint(Step_Y, Y_AXIS)) {
        step_Y = MOVE_FORWARD;
      }
      break;
    case MOVE_FORWARD:
      if ( !Step_Y.moving() ) {              // warten bis die Bewegung abgeschlossen ist
        Step_Y.setSpeed( 150 );              // = 20 U/Min (motorspezifisch)
        Step_Y.setRampLen( 100 );            // Beschleunigung (motorspezifisch)
        Step_Y.writeSteps(4096);             // Bewegung starten
        step_Y = MOVE_BACWARD;
      }
      break;
    case MOVE_BACWARD:
      if ( !Step_Y.moving() ) {              // warten bis die Bewegung abgeschlossen ist
        Step_Y.writeSteps(-4096);            // Bewegung starten
        step_Y = MOVE_FORWARD;
      }
      break;
  }
  //-- Z-Achse
  if ( !Step_Z.moving() ) {                  // warten bis die Bewegung abgeschlossen ist
    if ( !Pause.running() ) {                // warten bis Ablauf der Zeit
      Pause.setTime( 5000 );                 // Zeit für Bewegung und Pause
      angle *= -1;                          // andere Richtung
      Step_Z.write(angle);                  // Bewegung starten
    }
  }

}

bool seekZeropoint(MoToStepper &Step, byte axis) {     // keine Kopie, sondern eine Referenz des Schrittmotorobjektes
  enum {REFLEAVE, REFREACH, REACHZERO};
  static byte step[] = {REFLEAVE, REFLEAVE};       // Schrittkettenstatus

  switch (step[axis]) {
    case REFLEAVE:
      Step.setSpeed( 50 );                   // = 5 U/Min (motorspezifisch) Schleichfahrt
      Step.setRampLen( 5 );                  // Beschleunigung (motorspezifisch)
      Step.rotate(1);                        // vom Referentpunkt runterbewegen
      if (digitalRead(pinRef[axis])) {      // Referenzsensor nicht (mehr) betätigt
        Step.doSteps(100);                   // mit etwas Entfernung anhalten
        step[axis]++;
      }
      break;
    case REFREACH:
      if ( !Step.moving() ) {                // warten bis die Bewegung abgeschlossen ist
        Step.rotate(-1);                     // Richtung Referentpunkt bewegen
        step[axis]++;
      }
      break;
    case REACHZERO:
      if (!digitalRead(pinRef[axis])) {     // Ref_X betätigt
        Step.setZero(100);                   // setze Nullpunkt 100 Schritte von Referenzpunkt entfernt
        Step.writeSteps(0);                  // zum Nullpunkt bewegen
        step[axis]++;
      }
      break;
    default:
      step[axis] = REFLEAVE;
      return true;
  }
  return false;
}

//======================================================
//================= Second program functions
//======================================================

//###########################################################

// SeveralThingsAtTheSameTimeRev1.ino
// from:
// https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158/1
// https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158/2
// implemented on a Wokwi simulation per
// https://forum.arduino.cc/t/circuit-with-1-button-and-3-leds/1134897/3
// Wokwi: https://wokwi.com/projects/366664987128970241

// Note that the Adafruit series at https://learn.adafruit.com/multi-tasking-the-arduino-part-1/all-together-now
// expands on this to do two steppers using classes
// An implementation of the class-based Adafruit one in Wokwi is at
// https://wokwi.com/projects/397172393690134529

// An expansion of the BlinkWithoutDelay concept to illustrate how a script
//  can appear to do several things at the same time

// this sketch does the following
//    it blinks the onboard LED (as in the blinkWithoutDelay sketch)
//    it blinks two external LEDs (LedA and LedB) that are connected to pins 12 and 11.
//    it turns another Led (buttonLed connected to pin 10) on or off whenever a button
//       connected to pin 7 is pressed
//    it sweeps a servo (connected to pin 5) back and forth at different speeds

//  One leg of each LED should be connected to the relevant pin and the other leg should be connected to a
//   resistor of 470 ohms or more and the other end of the resistor to the Arduino GND.
//   If the LED doesn't light its probably connected the wrong way round.

//  On my Uno and Mega the "button" is just a piece of wire inserted into pin 7.
//   Touching the end of the wire with a moist finger is sufficient to cause the switching action
//   Of course a proper press-on-release-off button switch could also be used!

//  The Arduino is not capable of supplying enough 5v power to operate a servo
//    The servo should have it's own power supply and the power supply Ground should
//      be connected to the Arduino Ground.

// The sketch is written to illustrate a few different programming features.
//    The use of many functions with short pieces of code.
//       Short pieces of code are much easier to follow and debug
//    The use of variables to record the state of something (e.g. onBoardLedState) as a means to
//       enable the different functions to determine what to do.
//    The use of millis() to manage the timing of activities
//    The definition of all numbers used by the program at the top of the sketch where
//       they can easily be found if they need to be changed

//=======


//========

void setupSTATST() {

  //  Serial.begin(9600);
  Serial.println("Starting SeveralThingsAtTheSameTimeRev1.ino");  // so we know what sketch is running

  // set the Led pins as output:
  pinMode(onBoardLedPin, OUTPUT);
  pinMode(led_A_Pin, OUTPUT);
  pinMode(led_B_Pin, OUTPUT);
  pinMode(buttonLed_Pin, OUTPUT);

  // set the button pin as input with a pullup resistor to ensure it defaults to HIGH
  pinMode(buttonPin, INPUT_PULLUP);

  myservo.write(servoPosition); // sets the initial position
  myservo.attach(servoPin);

}

//=======

void loopSTATST() {

  // Notice that none of the action happens in loop() apart from reading millis()
  //   it just calls the functions that have the action code

  currentMillis = millis();   // capture the latest value of millis()
  //   this is equivalent to noting the time from a clock
  //   use the same time for all LED flashes to keep them synchronized

  readButton();               // call the functions that do the work
  updateOnBoardLedState();
  updateLed_A_State();
  updateLed_B_State();
  switchLeds();
  servoSweep();

}

//========

void updateOnBoardLedState() {

  if (onBoardLedState == LOW) {
    // if the Led is off, we must wait for the interval to expire before turning it on
    if (currentMillis - previousOnBoardLedMillis >= onBoardLedInterval) {
      // time is up, so change the state to HIGH
      onBoardLedState = HIGH;
      // and save the time when we made the change
      previousOnBoardLedMillis += onBoardLedInterval;
      // NOTE: The previous line could alternatively be
      //              previousOnBoardLedMillis = currentMillis
      //        which is the style used in the BlinkWithoutDelay example sketch
      //        Adding on the interval is a better way to ensure that succesive periods are identical

    }
  }
  else {  // i.e. if onBoardLedState is HIGH

    // if the Led is on, we must wait for the duration to expire before turning it off
    if (currentMillis - previousOnBoardLedMillis >= blinkDuration) {
      // time is up, so change the state to LOW
      onBoardLedState = LOW;
      // and save the time when we made the change
      previousOnBoardLedMillis += blinkDuration;
    }
  }
}

//=======

void updateLed_A_State() {

  if (led_A_State == LOW) {
    if (currentMillis - previousLed_A_Millis >= led_A_Interval) {
      led_A_State = HIGH;
      previousLed_A_Millis += led_A_Interval;
    }
  }
  else {
    if (currentMillis - previousLed_A_Millis >= blinkDuration) {
      led_A_State = LOW;
      previousLed_A_Millis += blinkDuration;
    }
  }
}

//=======

void updateLed_B_State() {

  if (led_B_State == LOW) {
    if (currentMillis - previousLed_B_Millis >= led_B_Interval) {
      led_B_State = HIGH;
      previousLed_B_Millis += led_B_Interval;
    }
  }
  else {
    if (currentMillis - previousLed_B_Millis >= blinkDuration) {
      led_B_State = LOW;
      previousLed_B_Millis += blinkDuration;
    }
  }
}

//========

void switchLeds() {
  // this is the code that actually switches the LEDs on and off

  digitalWrite(onBoardLedPin, onBoardLedState);
  digitalWrite(led_A_Pin, led_A_State);
  digitalWrite(led_B_Pin, led_B_State);
  digitalWrite(buttonLed_Pin, buttonLed_State);
}

//=======

void readButton() {

  // this only reads the button state after the button interval has elapsed
  //  this avoids multiple flashes if the button bounces
  // every time the button is pressed it changes buttonLed_State causing the Led to go on or off
  // Notice that there is no need to synchronize this use of millis() with the flashing Leds

  if (millis() - previousButtonMillis >= buttonInterval) {

    if (digitalRead(buttonPin) == LOW) {
      buttonLed_State = ! buttonLed_State; // this changes it to LOW if it was HIGH
      //   and to HIGH if it was LOW
      previousButtonMillis += buttonInterval;
    }
  }

}

//========

void servoSweep() {

  // this is similar to the servo sweep example except that it uses millis() rather than delay()

  // nothing happens unless the interval has expired
  // the value of currentMillis was set in loop()

  if (currentMillis - previousServoMillis >= servoInterval) {
    // its time for another move
    previousServoMillis += servoInterval;

    servoPosition = servoPosition + servoDegrees; // servoDegrees might be negative

    if (servoPosition <= servoMinDegrees) {
      // when the servo gets to its minimum position change the interval to change the speed
      if (servoInterval == servoSlowInterval) {
        servoInterval = servoFastInterval;
      }
      else {
        servoInterval = servoSlowInterval;
      }
    }
    if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees))  {
      // if the servo is at either extreme change the sign of the degrees to make it move the other way
      servoDegrees = - servoDegrees; // reverse direction
      // and update the position to ensure it is within range
      servoPosition = servoPosition + servoDegrees;
    }
    // make the servo move to the next position
    myservo.write(servoPosition);
    // and record the time when the move happened
  }
}

//=====END


//======================================================
//================= Third program functions
//======================================================
// Non-blocking Fastled animation with a button-led function
//
// https://wokwi.com/projects/410037772389117953
// https://forum.arduino.cc/t/millis-instead-of-delay-on-array-of-ledstrip/1304862/89?u=davex

void setupDaveX()
{
  FastLED.addLeds<WS2812B, DATA_PIN1, GRB>(array_LED_sconce, NUM_LEDS1);
  FastLED.setBrightness(255);
  // Serial.begin(115200);
  pinMode(DaveXLED, OUTPUT);
  pinMode(DaveXButton, INPUT_PULLUP);
  Serial.println("DaveX FastLEDs https://wokwi.com/projects/410037772389117953");  // so we know what sketch is running

}

void loopDaveX()
{
  wiperNB();
  somethingElse();
}

void somethingElse() {
  auto now = millis();
  const typeof(now) interval = 100;
  static typeof(now) last = -interval;
  if (now - last >= interval) {
    last += interval;
    digitalWrite(DaveXLED, !digitalRead(DaveXButton));
  }
}

void wiperNB() {
  // size 7 gives 0-6 up 7 blank, 8-14 down, 15 blank
  const int rangeLen = sizeof(ledBlink) / sizeof(ledBlink[0]);
  const int maxState = 2 * rangeLen + 1;
  if (millis() - timeout > interval) { // wait for interval
    timeout += interval; // restart the "lap" timer
  //  Serial.print(fastledState); Serial.print("/");
  //  Serial.print(maxState); Serial.print(" ");
    switch (fastledState) {
      case 0 ... rangeLen-1: // wiping up
        ledOnProcessing(ledBlink[fastledState]);
        break;
      case rangeLen:
        allLedsOff();
        break;
      case rangeLen+1 ... maxState-1:  // wiping down
        {
          const int LastLedIndex = rangeLen - 1;
          int upwardStep = fastledState - (rangeLen + 1);
          int ledToChange = LastLedIndex - upwardStep;
          ledOnProcessing(ledBlink[ledToChange]);
        }
        break;
      case maxState:
        allLedsOff();
        break;
      default:
        Serial.print(fastledState);
    }
    ++fastledState; // prepare for next step
    if (fastledState > maxState) { // move to next cycle
      fastledState = 0;
    }
    FastLED.show();
  }
}

void ledOnProcessing(int fastledState) {
  array_LED_sconce[fastledState] = daveXringColor;
}

void allLedsOff() {
  fill_solid(array_LED_sconce, NUM_LEDS1, CHSV(0, 0, 0));
}


void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
  static unsigned long count; // only this function sees this
  static bool lastBit10Set; // only this function sees this
  word millis16 = timer0_millis;

  count++; // adds 1 to count after any use in an expression, here it just adds 1.

  bool currentBit10Set = millis16 & 0x0400; // leverage integral to bool implicit promotion
  if (currentBit10Set != lastBit10Set) // 1 second
  {
//    Serial.print( millis16 ); // 16-bit binary into decimal text, many micros
//    Serial.write('\t');
    Serial.print( count ); // 32-bit binary into decimal text, load of cycles!
    Serial.println(" loop/sec");
    count = 0; // don't forget to reset the counter
    lastBit10Set = currentBit10Set; //  changes 0<==>1 
  }
}

And five combined programs per Combining multiple non-blocking codes - #2 by DaveX :

image
-- five programs combined

Code in the simulation, or posted here:

These last two might be a bit of a Frankenstein, but I was trying to demonstrate that if the component programs are written to be cooperative within themselves, it is possible to combine them together with few changes.

3 Likes

Bravo, Dave! nicely done.

1 Like

I wrote those combinations a while ago, but forgot how to find them easily, so I thought I'd link them here. This thread shows up if you search for just "Wokwi":

https://forum.arduino.cc/search?q=wokwi

Guess I just missed them. Oh, wait, I had it bookmarked. (sheepish grin). DOH!

1 Like

A few bonus state-change detection examples:

1 Like

Paul Stoffregen has two libraries for using Timer1 to do Frequency counting and Frequency Measuring. Along with advice to measure frequencies less than 1000Hz, and count frequencies above 1000Hz:

FreqCount : best for 1 kHz to 8 MHz (up to 65 MHz with Teensy 3.0 - 3.6)
FreqMeasure : best for 0.1 Hz to 1 kHz

FreqCount measures the number of cycles that occur during a fixed "gate interval" time. This works well for relatively high frequencies, because many cycles are likely to be counted during gate interval. At lower frequencies, very few cycles are counted, giving limited resolution.

FreqMeasure measures the elapsed time during a single cycle. This works well for relatively low frequencies, because a substantial time elapses. At higher frequencies, the short time can only be measured at the processor's clock speed, which results in limited resolution.
-- FreqCount Library, for Measuring Frequencies in the 1 kHz to 5 MHz Range

The FreqCount example works, and you can experiment with the PWM generator, Scope, and FrequencyCounter/Measurer.

The FreqMeasure exmple does not work because Wokwi does not implement the InputCapture feature as per Github issue: