Electronic target system > Seeking advice on technical choices !

Hi everyone and thank you for taking interest in my exciting project !

Context
I have a little experience with arduino/esp32 projects (sensors, radio, oled mainly) and have been initiated to airsoft recently (pistol toys propelling plastic balls). My friend and I quickly realised that aiming precisely and switching target were key skills to train, and that we could easily improve and have fun at the same time in my garage with a bit of tin cans, duct tape and a bit of electronics.

So my project is to build a target system with different games that help us improve and enjoy the process. Since this is my biggest project so far, and that I have not yet gone all the way on the technical aspects I anticipate, I'm seeking for advices.

Goal of the project and points of interrogation

1. Targets and shots

  • So the targets consist in my baby powder milk cans covered with a cardboard piece, fixed on the wall, each backlit with WS2812B led ring (more on that later). The main idea is to light up the target when it is active, detect a shot and turn of the backlight.
  • It occured that detection might be best through vibration since the airsoft replicas are quite powerful and the cans can be practically isolated from the wall vibrations. I thought of a piezoelectric buzzer + 1MOhm resistor + diod rectifier for this, but have never used it, so I don't know if this is a relevant choice

Any thoughts on that ?

2. Controlling several cans separately

  • The second constraint is to multitask all of this, because several cans could be activated at the same time or not. I plan on using an ESP32 for memory issue, potentiality and availability.
  • Multitasking through timers has been a pain in the *ss so far, and I have recently discovered that FreeRTOS could be a huge relief in managing this.

My question is : is this the right direction, or I'm missing/not anticipating something big ?

3. Many colors and 8 cans

  • WS2812B seems the right technology
  • because of color choice (for instance a versus game gives a color for each player, at the end of a countdown whoever shot the most target wins),
  • but also the possibility to form a clock-like countdown (for instance a time challenge might leave less and less time to shot each target, visualizing the remaining time to shot the target by how many leds of the ring remain lit)

Since I will probably be using 24-led rings on 8 different cans, I know I must be using an additional power supply. But since there are so many cans, and maybe in the future I might want to differentiate the size of cans (with less leds on their respective rings), I am unsure weather it would be more appropriate to

  • adress each can separately or serialize them
  • have only one microcontroller to rule them all (but how do I expand my I/O ? are expansion board compatible with FreeRTOS ?)
  • or have a slave microcontroller for each can (which will make the price of the project big, not optimize resources and I fear increase a lot complexity as I have never worked with BLE/Wifi and doubt it could show very low latency (which is required for UX purpose))

If you have advices I take them !

5. Game choice

  • You undestood it : I plan on programming several games : solo or versus or team, quickest time to 20 hits or max hit in 30s, color challenge etc. But so far I have close to no idea on how to make people choose and reset their choice.
  • The first idea was to use an incremental coder to select a game and a button to reset it (not very evolutive)
  • The second idea was to publish a website on the local network and go through it (never done it)
  • And another constraint is that sometime I'd like to light up my garage only using the targets, but to use this mode using a light switch in the garage, not having to go through wifi connection, web browsing etc.

Any return on experience and advice is also welcome here !

6. Game results

  • Lastly, arcade style, I would like to show directly the result of the game (chrono, number of hit target etc.) to the players. I think the result for each player should be displayable on 2 digits per player (or 00-99 + 2 decimals on solo mode), which led me to think about 7 segments digits
  • Problem is : big digits (5+ cm) are not very available on websites, and the one I find seem expensive with only red available.

My question is : for such a display,

  • are there any other technology (or more specific technology) that might do well the job ?
  • are there providers I should know that would allow a good value for money and choice of colors ?

I know that might seem a lot, but I'm very very excited about it and not afraid to learn / try / fail / improve as I did learning arduino DIY so far, and I'd love to share a video at the end ! Also, this will probably represent some money buying the components and I'm sure you understand why I'd like to prepare and do the right technical choices beforehands.

Thanks in advance for you help, I'm looking forward to reading you !
Albert


Edit to centralize some resources :

Are you looking for someone to design and test your project and then tell you what to use and do or are you going to develop the project one step at a time? If you are going to do it, can you tell us where you are starting?

3 Likes

Relevant question !

I intend to do it myself one step at a time, the main steps I had in mind being the following :

  1. Trying the piezo solution and finding the right code to define a hit detection, and how to make the detection reliable on the target
  2. Lighting up a target with WS2812B rings and linking a target to the sensor, so when it's hit, it lights off : learning how to use neopixel library and experimenting with class to wrap all the target's functions at one place (seems like a powerful option)
  3. Lighting 2 targets at once until they are all hit, experimenting with RTOS programming at that moment and power supplying of the rings
  4. Setting up a timer system to light all the targets after a random duration, then timing how long it takes to hit them all, and displaying the result on 7 segments,
  5. Expanding from 2 to 8 targets, thus experimenting with I/O expansion, RTOS compability and all the required wiring and power supplying extansion as well
  6. Fine-tuning this first game and experimenting with it for overall return on experience
  7. Finding a solution for game selection and light only mode
  8. Developping the other games I have in mind (5 so far)

So, having this in mind, I'm rather looking for critical feedback on the technical choice (sensors, RTOS, global design etc.) as well as advices on how to progress efficiently on such a big project (such as what you implied : doing it one step at the time, feedback on the steps I have in mind if not relevant or missing something important, etc) :slight_smile:

What do you think ?

More Ws2812 LEDs. Use the self-adhesive strips. Cut the strip into sections to make segments for your 7-seg displays, as I did here for a clock:

You can chain all your led rings and 7-seg displays to a single ESP pin. If that makes wiring inconvenient, you can use several ESP pins, but you do not need to use a separate ESP pin for each ring or each display.

2 Likes

https://docs.arduino.cc/tutorials/generic/tilt-sensor/

WS2812 rings will need protection.
Either pivot each ring (with spring return) using the black square, or with the properties of light in a medium and use one WS2812 (on the black square) to illuminate individual plexiglass rings.

xfpd --> https://docs.arduino.cc/tutorials/generic/tilt-sensor/
Your link had me search for tilt sensor vs hit sensor, which led me to this awesome video that compares different technologies with practical showcase. If this can help someone later on with the same questions, here is the link to the video (thanks to the author of the video) --> https://www.youtube.com/watch?v=9-r86QABHCs

The most adapted sensor concerning my use case seems to be vibration sensors rather than tilt switches. But their downside clearly is that they need analog input, and I need 8 sensors ! Do you have recommandations on how I should convert using minimum I/O ? Maybe an external analog to digital converter with an I/O expansion board ?

WS2812 rings will need protection.
Either pivot each ring (with spring return) using the black square, or with the properties of light in a medium and use one WS2812 (on the black square) to illuminate individual plexiglass rings.

I think you're right, and this + PaulRB remark on using more leds makes me think that I don't really need rings : with a WS2812B strip wrapped arount a cynlindrical piece of wood behing the target, I can :

  • protect the leds while dismissing the plastic ring protection problem
  • still hide the leds behind the target (the whole backlit idea)
  • enhance the light power since leds will throw their light parallel to the wall instead of directly against the wall
  • probably reduce cost
  • maybe ease the led chaining

You can chain all your led rings and 7-seg displays to a single ESP pin.
That's a great idea that'll make it way easier to manage pin-wise than 7 segments : since led have a fixed position, numbers can be stored directly as adresses within the code. It'll take a bit more to write, but I think I enjoy the challenge ! Thanks for the suggestion !

I really like the segment color you made, and it gives me even more ideas ! If you have one more idea to hide the led strip and reducing the led spot effect, I'm interested as well :slight_smile:

Thanks for your participation ! :folded_hands:

The way I did that was to use the "rainbow fade" effect to fill the entire strip with animated colour, then, to display the time, set the segments that should not be lit to black, and finally send the update to the strip. This means I can easily swap to any other effect.

There is now a type of ws2812 strip available with the LEDs embedded in a flexible translucent strip which diffuses the light. I've never used it, so I don't know how easy it is to work with.

Thanks for the feedback, lighting off the leds afterwards seems indeed as a very efficient way to do it ! I'm currently documenting on WS2812b and Fastled library, this is enlightening.

For the strip you reference, I have one that is driven by a tuya device, and the person I bought it from cut a piece of it. Basically, it is a non waterproof strip that is put inside a silicon "tunnel". It is not glued except for the begining and ending tips, so that when you twist the whole thing, it can move.

Pros :

  • the led spot is quite well reduced
  • and the light renders nice

Cons:

  • I had quite a hard time soldering and keeping the stuff together, so for DIY one needs to be extra careful
  • they are not regulating heat nicely (compared to aluminium profiles that dissipates better the heat). After something like 2 hours of lightning at full power, some leds doesn't answer well, and changing the patterns doesn't work well anymore.

Also, I looked into COB strips, but am worried as well for the DIY aspect of it, I think the way you did it would render better finishing anyways. My current idea to work around it is to put the leds behind a tintless mirror. It won't reduce the led spot effect, but will at least hide everything nicely. Maybe combining this with a COB or silicon tube might be the best idea after all for nice segments :thinking:

I'm not sure about that. I have used the non-waterproof ws2812 inside a silicone tube myself, but the tube was clear/transparent.

Maybe the strip I linked to is similar but the tube is opaque and dissipates the light more, but maybe not.

Ok, so firstly, your project goal is huge so take a deep breath and give yourself a year timeline just to get something remotely close to your idea. It is doable, likely, just start breaking down the concepts into subparts you can work on in a modular way.

Endorsed by the mighty @alto777 in another thread today, and oft referenced by yours truly, you need to learn the magic of State Machines, aka Finite State Machines. It's the best way in this humble hobbyist's opinion to go from blink to blink when a button is pressed, but only if the room is dark, say.
This video will change the way you think about Arduino projects, since you are interested in game design, might as well do it like the best in the business, Nintendo.

Here's a code I wrote for some student in the forums a while back that approaches some of what I think you're thinking of. You should be able to try it out yourself with only a few parts and a few minutes to slap the circuit together.

Note that, if nothing else, there are State Machines used, millis timers, EEPROM high score keeping and resetting as an option, and something like a splash "screen" awaiting players.

/* Blast 'Em! Note to self: DO NOT MODIFY!
 
  by Hallowed31
    May 14-15, 2024

    Additional parts needed: 
     - One or two light-dependent resistors. No 
       additional pullup or pulldown resistors needed.
     - One or two servos. Attach some target to the servo
       horn after installing LDR through each one, like a 
       bullseye.
     - One pushbutton. 
     - Some light source as blaster. Cat laser toy, perhaps
     - External power supply for servos. Never use Arduino
       +5V regulator to power servos.
     - Computer connected to Arduino to use Serial Monitor
       or why not try your favorite terminal emulation program
       such as puTTY or CoolTerm?  

    Gameplay circuit:
     - LDRs from A0 and A1 to ground.

     - Servos powered externally (+5V) from Arduino, signal pins
        to D9 and D10 and don't forget
        to ground servos to both Arduino AND external power supply

     - Start button (pushbutton) to D2 and Arduino GND. 

     - Works with one sensor/servo combo or two. 

     - Known issue: the timer will occasionally overflow/keep running
          when cycling between modes such as manual, BIOS, gameplay

     - Ensure Serial monitor is open, default size prints prettiest.
        Autoscroll should be checked, show timestamp unchecked, 
        set to 115200 baud, no line ending.

     - CoolTerm settings: "blastEm.stc" 
        
*/
#include <EEPROM.h>
#include <Servo.h>

Servo servoOne, servoTwo;

int targetOne = A0;
int targetTwo = A1;
int led = 13;
int playerButton = 2;
int target1Value, target2Value;
int gameMode, highScore, playerState, lastPlayerState;
int eeAddress;
unsigned long score, gameTime, lastGameTime, gameTimeLimit, targetDown, lastTargetDown, tar2Down, lastTar2Down;
unsigned long previousGameTimer;
const long interval = 1000;
const long biosWait = 3200;
unsigned long prevBios = 0;
int timer = 30;
int biosTimer = 3;
int bios;

void  setup() {
  Serial.begin(115200);
  eeAddress = 0;
  pinMode(targetOne, INPUT_PULLUP);
  pinMode(targetTwo, INPUT_PULLUP);
  pinMode(playerButton, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  /* uncomment resetHighScore() function and upload to reset
    saved high score to zero,
    then comment out again, upload sketch again
    to resume playing normal game */
  // resetHighScore();
  servoOne.attach(10);
  servoTwo.attach(11);
  servoOne.write(0);
  servoTwo.write(0);
  cycleServos();
  gameTimeLimit = 31330;
  targetDown = 500;
  tar2Down = 500;
  lastTargetDown = 0;
  lastTar2Down = 0;
  lastPlayerState = LOW;
  target1Value = 0;
  target2Value = 0;
  credits();
  biosPrompt();
  unsigned long biosT = millis();
  while (biosT + biosWait >= millis()) {
    unsigned long thisBiosTimer = millis();
    if (Serial.available() > 0) {
      char biosMode = Serial.read();
      switch (biosMode) {
        case 'b':
          bios = true;
          break;
        case 'B':
          bios = true;
          break;
      }
    }
    if (thisBiosTimer - prevBios >= interval) {
      if (biosTimer > 0) {
        Serial.println(biosTimer);
      }
      biosTimer --;
      if (biosTimer == -1) {
        timer = 0;
      }
      prevBios = thisBiosTimer;
    }
  }
  playerState = digitalRead(playerButton);
  while (playerState != lastPlayerState) {
    playerState = digitalRead(playerButton);
    awaitingPlayerMessage();
    viewHighScore();
  }
  lastPlayerState = playerState;
  score = 0;
  gameTime = 0;
  lastGameTime = 0;
  previousGameTimer = 0;
  if (bios == true) {
    gameMode = 1;
  }
  else if (bios == false) {
    gameMode = 2;
  }
}

void loop() {
  // start game timer
  gameTime = millis();
  if (Serial.available() > 0) {
    char switchMode = Serial.read();
    switch (switchMode) {
      case 'b':
        gameMode = 1; // viewBios/menu
        break;
      case 'n':
        gameMode = 2; // new player
        break;
      case ' ':
        gameMode = 3; // play game
        break;
      case 'r':
        gameMode = 4; // gameover
        break;
      case '?':
        gameMode = 5; // view raw
        break;
      case 'h':
        gameMode = 6; // view hi scores
        break;
      case 'm':
        gameMode = 7; // read manual
        break;
    }
  }
  switch (gameMode) {
    case 1:
      viewBios();
      lastGameTime = gameTime;
      break;
    case 2:
      newPlayer();
      lastGameTime = gameTime;
      break;
    case 3:
      normalGameplay();
      if (gameTime - lastGameTime > gameTimeLimit) { // outta time
        gameMode = 4;
      }
      break;
    case 4:
      gameOver();
      break;
    case 5:
      viewRawTargetReadings();
      break;
    case 6:
      viewHighScore();
      break;
    case 7:
      manual();
      break;
  }
}
// gameMode 2
void newPlayer() {
  newPlayerMessage();
  lastGameTime = 0;
  score = 0;
  timer = 30;
  gameMode = 3;
}
// gameMode 3
void normalGameplay() {
  unsigned long thisGameTimer = millis();
  if (thisGameTimer - previousGameTimer >= interval) {
    timer --;
    if (timer == -1) {
      timer = 30;
    }
    previousGameTimer = thisGameTimer;
  }
  target1Value = analogRead(targetOne);
  target2Value = analogRead(targetTwo);
  if (target1Value < 75) {
    lastTargetDown = gameTime;
    if (gameTime - lastTargetDown <= targetDown) {
      digitalWrite(led,  HIGH);
      servoOne.write(90);
      score = score + 1;
      if (score > highScore) {
        highScore = score;
        EEPROM.put(eeAddress, highScore);
      }
    }
  }
  if (target2Value < 75) {
    lastTar2Down = gameTime;
    if (gameTime - lastTar2Down <= tar2Down) {
      digitalWrite(led,  HIGH);
      servoTwo.write(90);
      score = score + 1;
      if (score > highScore) {
        highScore = score;
        EEPROM.put(eeAddress, highScore);
      }
    }
  }
  if (gameTime - lastTargetDown >= targetDown) {
    digitalWrite(led, LOW);
    servoOne.write(0);
  }
  if (gameTime - lastTar2Down >= tar2Down) {
    digitalWrite(led, LOW);
    servoTwo.write(0);
  }
  Serial.print("score ");
  Serial.print(score);
  Serial.print("\t\t");
  if (timer >= 10) {
    Serial.print("Time ");
  }
  else if (timer < 10) {
    Serial.print("Time 0");
  }
  Serial.println(timer);
}

// gameMode 4
void gameOver() {
  gameOverMessage();
  awaitingPlayerMessage();
  playerState = digitalRead(playerButton);
  delay(20); // simple debouncing
  playerState = digitalRead(playerButton);
  if (playerState == LOW) {
    gameMode = 2; // reset
  }
}

/*****************************************************
 ***********        UTILITIES       ******************
 *****************************************************/
 unsigned long ma, mb, mc, lastMa, lastMb, lastMc;

void viewBios() {
  Serial.println();
  Serial.println(F("                              Blast 'Em v2"));
  Serial.println();
  Serial.println(F("            Type SPACE during game to resume normal gameplay"));
  Serial.println();
  Serial.println(("                   Type r during game to reset game"));
  Serial.println();
  Serial.println(("                        Type m to view manual"));
  Serial.println(("               Type ? during game to view raw sensor readings"));
  Serial.println(("           Type h during game to pause game and see high score"));
  EEPROM.get(eeAddress, highScore);
  Serial.println();
  Serial.print(("                 All Time High Score: "));
  Serial.println(highScore);
  Serial.println();
  Serial.println(("                     Type X to exit BIOS and return"));
  Serial.println();
  while (!Serial.available()) {
  }
  if (Serial.available() > 0) {
    char myBios = Serial.read();
    switch (myBios) {
      case 'x':
        gameMode = 2;
        bios = false;
        lastGameTime = gameTime;
        break;
      case 'X':
        gameMode = 2;
        bios = false;
        lastGameTime = gameTime;
        break;
      case ' ':
        gameMode = 3; // play game
        break;
      case 'r':
        gameMode = 4; // gameover
        break;
      case '?':
        gameMode = 5; // view raw
        break;
      case 'h':
        gameMode = 6; // view hi scores
        break;
      case 'm':
        gameMode = 7; // read manual
        break;
    }
  }
}


void manual() {
  Serial.println(F("press button when prompted to begin normal play"));
  Serial.println();
  Serial.println(F("Enter key commands during game and press ENTER"));
  Serial.println();
  Serial.println(("Key Commands: "));
  Serial.println(("  ? during game to view sensor data for calibration"));
  Serial.println(("  h to view high score"));
  Serial.println(("  r to reset game"));
  Serial.println(("  h to view high score"));
  Serial.println(("  Space bar + Enter to return to game time and score"));
  Serial.println(("  r to reset game"));
  Serial.println();
  Serial.println();
  Serial.println(("                             Type X to return"));
  Serial.println();
   while (!Serial.available()) {
  }
    if (Serial.available() > 0) {
      char escape = Serial.read();
      switch (escape) {
        case 'x':
          gameMode = 2;
          lastGameTime = gameTime;
          break;
        case 'X':
          gameMode = 2;
          lastGameTime = gameTime;
          break;
      }
    }
}

// called in newPlayer() gameMode 2
void newPlayerMessage() {
  score = 0;
  Serial.println();
  Serial.println(F("                         New Player Blast Em!"));
  Serial.println();
  delay(2000);
  Serial.print("score ");
  Serial.println(score);
}

// called in gameOver() gameMode 4
void awaitingPlayerMessage() {
  ma = millis();
  const unsigned long maTime = 5000;
  if (ma - lastMa >= maTime) {
    lastMa = ma;
    Serial.println();
    Serial.println(F("                          Press Button to Play"));
    Serial.println();
    Serial.println(F("                            Awaiting Player"));
    Serial.println();
  }
}

// called in gameOver() gameMode 4
void gameOverMessage() {
  mb = millis();
  const unsigned long mbTime = 7000;
  if (mb - lastMb >= mbTime) {
    lastMb = mb;
    Serial.println();
    Serial.println(F("                     Time's Up -------- Game Over"));
    Serial.println();
    Serial.print(F("                   Your Score: "));
    Serial.print(score);
    Serial.print(F("     High Score: "));
    Serial.println(highScore);
    Serial.println();
  }
}

// gameMode 6
void viewHighScore() {
  mc = millis();
  EEPROM.get(eeAddress, highScore);
  const unsigned long mcTime = 11000;
  if (mc - lastMc >= mcTime) {
    lastMc = mc;
    Serial.println();
    Serial.print(F("                            High Score: "));
    Serial.println(highScore);
    Serial.println();
  }
}

void credits() {
  Serial.println(F("Blast 'Em!"));
  Serial.println(F("by Hallowed31"));
  delay(1000);
  for (int i = 0; i < 3; i++) {
    Serial.println();
    delay(200);
  }
}

void biosPrompt() {
  Serial.println(F("Type in B and use button"));
  Serial.println(F("at prompt to enter BIOS"));
  delay(1500);
  for (int i = 0; i < 3; i++) {
    Serial.println();
    delay(200);
  }
}

// gameMode 5
void viewRawTargetReadings() {
  unsigned long readInterval = 1000;
  if (gameTime - lastGameTime >= readInterval) {
    lastGameTime = gameTime;
    target1Value = analogRead(targetOne);
    target2Value = analogRead(targetTwo);
    Serial.print(F("ADC readings :  Target 1 (A0) value is "));
    Serial.print(target1Value);  // for checking Photoresistor  input
    Serial.print(F("  Target 2 (A1) value is "));
    Serial.println(target2Value);
    Serial.println();
  }
}

void resetHighScore() {
  highScore = 0;
  EEPROM.put(eeAddress, highScore);
  while (1);
}

void cycleServos() {
  servoOne.write(90);
  servoTwo.write(90);
  delay(250);
  servoOne.write(0);
  servoTwo.write(0);
}

Thank you so much for sharing your code, I need to plan some time to go through it.

I looked at your video though and found it very interesting, thus watched several more videos to try to understand more specifically what are state machines and how it takes place within the code. It gives a perfect solution for game selection and mode switch (gaming or lighting the room + lighting modes).

And by watching all this, I get why you warn me about such a huge timeline : even with state machine there are lots of layered states machines to think about, and it doesn't seem humanly possible to come with all up at once. I realize that I really need to sketch each of my games, for even the sole purpose of clarifying what I want to do and draw some kind of roadmap of the elementary bits, how they connect, where to start.

It also draw 2 questions that I havn't figured out nor found resources on yet :

  1. How do you do when you need to layer a state machine inside a state machine, and so on so forth, Inception-like ? I found many tutorials on building one state machine, sometimes idling with what could be a state machine (such as the blinking yellow light in the stop light famous example), but was programmed directly with millis() loops, so I couldn't get how another layer of state machine could work. --> If you have resources on this, I'm taking it !

  2. State machines solve a big problem concerning managing several interactions and game progression. But somehow (and this is probably very naive), I had the feeling that programming the games using RTOS would be easier and more straight forward regarding code readibility and maintenance than with state machine. Unfortunately I couldn't find resources either on which implementation would be better. Maybe I'm opposing two things that are the same or working together, but I can't quite get it right in my head. Any clarification would be helpful !

Also when you say EEPROM high score, you mean storing the high score data in a part of the memory that survives power on/off on the microcontroller ?? I didn't know this as possible !! Do you know much space is avaiblable for such a use case on an ESP32 ?

You can have as many state machines as you want, and there's no limit to how tangled is the logic of each, so one state machine can consider what state another is in and so forth.

RTOS may sound like the bomb, but both FSM and RTOS will take some learning. I would argue that if you can't manage to write a few FSM-orientated sketches using millis() plus plain C code, an RTOS would make things harder, not easier. Opinions vary, but TBH many advocates for RTOSs can't remember not knowing a thing about them. I bet many used, or were at least familiar with FSMs.

You will be helped by careful planning and taking small steps towards success. And mastering the separate components before tossing them all in the soup, hoping for the best.

You might enjoy working some things out in a simulator before you waste spend a penny.

Here's a sketch that turns on and off the LED by pushbutton. On when you are pushing, off what you aren't.

Until you can code it so the LED goes on for three seconds every time you press the button, don't expect to have much fun coding an electronic target system. Make sure the LED goes on when you first press the button, and stays on for three seconds no matter you are still pressing or not… and watch out! LEDs can look like they turned on but the code may allow them to turn off and on a few dozen times faster that you can see before they are on fully, stable and for the period.

Try the simple version here:

Wokwi - Online ESP32, STM32, Arduino Simulator

// https://wokwi.com/projects/433233127008743425

const byte led1 = 6;

const byte button1 = 3;

void setup() {
  pinMode(led1, OUTPUT);

  pinMode(button1, INPUT_PULLUP);
}

void loop() {
  if (digitalRead(button1) == LOW) {
    digitalWrite(led1, HIGH);
  }
  else digitalWrite(led1, LOW);
}

HTH

a7

State machines work well within other state machines. I find the key is really defining your break/exit points, and good ole trial and error. Mostly error. Don't give up.

Keep @alto777 and other helpers here in your graces and you make it, if you follow the basics that @PerryBebbington drew out in his famous thread "how to get the best of this forum". I am very amateur compared to pros like @alto777 but keep an open mind and they will teach you four score and seven tips of things to make your goals happen.

Thanks for the challenge, I accept it ! :stuck_out_tongue:

Wokwi seems like a genius option, thank you very much for the resource :folded_hands:

I'll give it a try, and check by myself how far do I manage to go before considering buying parts or documenting on RTOS. I heard many times lately that a good way to implement strong basics in any language is to use the raw capabilities of the said language, as is, before using any framework, so that when one starts using a framework, one really understands what it does, and why it has been concieved that way. So I'll give it a (whole bunch of) shot(s) and keep you informed :slight_smile: (Propably BRB in a fews days, hopefully a bit more than that :sweat_smile: )

@hallowed31 I'll also follow your advice, I'll probably start drawing FSM schemes of the project after solving @alto777 's challenge (which I'll draw too), then try to code it in Wokwi. That cheers me up since I no longer need to get as well as possible my components purchase before getting hands on the project :slight_smile:

If I have to follow through a whole year, I might as well post my progress somewhere alongside here : are there any easy, lightweight, simple blogs for such projects you would recommend ?

I don't know if any, but whatever you do, please continue here. Some of us have no life of their own, and truly enjoy watching the heroic struggle and helping when we think we can!

a7

1 Like

Be aware that WOKWI does not have a simulated sensor for absolutely everything out there.  Doesn't matter.  A simple pushbutton can substitute for almost any ON/OFF sensor and a potentiometer can stand in for sensors with analog signals.

Good luck!

1 Like

Yes. I use all kinds of substitutes or proxies for parts they don't have.

Pro tip: the slide fader potentiometer is way easier than the rotary pot to use and see the current setting for an analog signal.

I go further and say for some sensors that are simulated, it is actually more convenient and easier to just use slide faders and a bit of maths. In this way you can develop without any worrying about the sensor or its library if you are using one; many sensors really need to be exercised IRL anyway.

a7

You mentioned this a while back in another thread, so I bought some and can confirm that slide pots are indeed a little faster to hook up and easier to see where you are in the range when you want something up-and-running, quickly.

Think of like like an onion, with layers that wrap around layers to guard some conditions (I am making some of the gurus here cringe with my brutal explanation, I'm sure), but also think of tree branches, maybe an onion tree!

Regarding ESP32 EEPROM, is doesn't have any but you can still preserve data beyond power cycling:

In fact, for this RandomNerdsTutorials has tons of great stuff on the ESP32 if that's your board of choice.

EDIT: the link had been updated by the author, so I updated the link to the current one here.

Also, IIRC, I tried to get high scores saved to an ESP32 and it wasn't working out for me, then I think I realized that it wasn't needed in my application anyway (laser maze, it was a nice to have but it runs all Hallowe'en so power cycling was not a biggie)