Random Target system

so seems you can drive the steppers using Adafruit_PWMServoDriver. That's a good start.

Can you clarify where you stand with the 433 RF receiver? do you have a piece of code that shows when a button is pressed?

Hello JML I have not started on the RF remote yet, I did think I was ready to start on that part but I was using delays and UKHELIBOB did recommend it would be wise to change to millis which since watching lots of vids many people say the same thing I also watched some people doing state machines but I think that went over my head so I watched another millis tutorial last night and tried to copy everything he said but still missing something I will post below my new attempt. The toaster in the kitchen didn't come on and the fish tank is still running haha does that mean I am getting any closer :roll_eyes: it compiles the servos move up and down random sequence but not right thankyou for asking.

#include <Wire.h> 
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver myServo = Adafruit_PWMServoDriver();

#define SERVOMIN 150
#define SERVOMAX 600

uint8_t sNum = 0; 
uint8_t numberOfServos = 9;                    
unsigned long startMillisIntervalOne;                     
unsigned long currentMillisIntervalOne;
const unsigned long desiredIntervalOne = 3000;

unsigned long startMillisIntervalTwo;                     
unsigned long currentMillisIntervalTwo;
const unsigned long desiredIntervalTwo = 10000;

const byte ledPin = 13;                        //using the built in LED
void setup() {
  pinMode(ledPin, OUTPUT);
  startMillisIntervalOne = millis();
  startMillisIntervalTwo = millis();
  myServo.begin();
  myServo.setPWMFreq(60);
  
}

void loop()
{
  currentMillisIntervalOne = millis();
  currentMillisIntervalTwo = millis();
  digitalWrite(ledPin, !digitalRead(ledPin));                           //if so, change the state of the LED.  
  static byte count = 0;                                               
  int servonum = random(0, 9);                                          // pick a servo                         
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)   
  myServo.setPWM(servonum, 0, pulselen);
if(currentMillisIntervalOne - startMillisIntervalOne >= desiredIntervalOne){
    startMillisIntervalOne = currentMillisIntervalOne;                                 
  }                                                   
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)
  myServo.setPWM(servonum, 0, pulselen);
if(currentMillisIntervalTwo - startMillisIntervalTwo >= desiredIntervalTwo){
    startMillisIntervalTwo = currentMillisIntervalTwo;
  }

  count++;
  if (count == 10);
  {
  if(currentMillisIntervalTwo - startMillisIntervalTwo >= desiredIntervalTwo){
    startMillisIntervalTwo = currentMillisIntervalTwo;
  }
    count = 0;
  }
}    

the code could look like this:

enum t_mode : uint8_t {STOP, LOOP, ALL_90, ALL_0} state = STOP;

void manageStateChange() {
  int c = Serial.read(); // simulating buttons through the Serial input
  t_mode newState = state;
  switch (c) {
    case 'a': newState = STOP; break;
    case 'b': newState = STOP; break;
    case 'c': newState = STOP; break;
    case 'd': newState = STOP; break;
    default: break; // ignore
  }

  if (state != newState) {
    Serial.print(F("State changed from #")); Serial.print(state);
    Serial.print(F(" to #")); Serial.println(newState);
    // here handle what needs to be done for the transition
    //...
  }
  state = newState;
}

void runState() {
  // here handle what needs to be done if a step needs to be taken depending on current state
  // ...
}

void setup() {
  Serial.begin(115200); Serial.println();
}

void loop() {
  manageStateChange();
  runState();
}

this is simulating the button detection by reading Serial input (at 115200 bauds)

Hare is an example of part of your required functionality using millis() for timing

enum states
{
  STOPPED,
  RUNNING,
  ALL_UP,
  ALL_DOWN
};
byte currentState;
const byte runPin = A3;
const byte upPin = A2;
const byte downPin = A1;
unsigned long startTime;
byte showCount;
boolean showing;
unsigned long showPeriod = 2000;  //show target for 2 seconds
unsigned long currentTime;
byte sNum;

void setup()
{
  Serial.begin(115200);
  pinMode(runPin, INPUT_PULLUP);
  pinMode(upPin, INPUT_PULLUP);
  pinMode(downPin, INPUT_PULLUP);
  currentState = STOPPED;
  Serial.println("in state STOPPED");
}

void loop()
{
  switch (currentState)
  {
    case STOPPED:
      if (digitalRead(runPin) == LOW)
      {
        currentState = RUNNING;
        showCount = 0;
        showing = false;
        Serial.println("in state RUNNING");
      }
      break;
    case RUNNING:
      currentTime = millis();
      if (!showing)
      {
        sNum = random(11);
        showing = true;
        showCount++;
        startTime = currentTime;
        Serial.print("showing servo number ");
        Serial.println(sNum);
      }
      else
      {
        if (currentTime - startTime >= showPeriod)
        {
          Serial.print("hiding servo number ");
          Serial.println(sNum);
          showing = false;
          if (showCount == 10)
          {
            currentState = STOPPED;
            Serial.println("in state STOPPED");
          }
        }
      }
      break;
    case ALL_UP:
      break;
    case ALL_DOWN:
      break;
  };
}

As written it uses a digital input to start the target sequence running so you will need to incorporate the receipt of the remote control signals

Hello JML that is great thanks for the help looking forward to trying to implement this I just need a couple of days as unwell at the moment.

get well soon!

Hello UKHELIBOB looking at what you have written and what I have been watching is now very apparent why you said its a long way off there is so much missing in what I have, you guys should have a voluntary donation page for the time and effort you all put in thank again.

No need for any donations as far as I am concerned. I help here because I like doing it and it is very satisfying when someone gets their project working and I often learn a lot too

1 Like

Hi JML up and moving again waiting for spine op so it puts me out of action every now and then. Ok I have tested out the RF remote and receiver with the digital button example to make sure it works ok, all good the led lights when button pressed I checked each channel and button. With the input pins will they all output to pin13 with the internal pullup led ? or that just does not matter. I have looked at what yourself and UKHELIBOB have written is all quite overwhelming it looked as though the code written by UKHELIBOB fits into what you have written so should I try and merge them together or get the RF working with your code first if so where does the pin information actually get inserted I am also assuming with what you have written I don't need a library, I am sorry but there is so much I have not seen before thanks.

My code was just an example for the structure of a state machine. There is no library because I just read the Serial port for commands. You'll have to replace the Serial input by the physical buttons and/or the remote.

@UKHeliBob code is a similar state machine approach, may be closer to your needs as he integrated a button for an example.

because buttons bounce, I would probably recommend to use a button library (eg Button in easyRun or OneButton from Matthias Hertel, or any other you fancy). It will make handling the events more straightforward


best wishes on that.

Hi UKHELIBOB and JML I have been putting together what I have so far and done my best to try and understand, the sketch I have together now does compile but will only run once when plugged in I am sorry to say still not getting the millis (delay) timing correct, I tried 2 debounce downloads but one had no library attached the other debounce event that was continuous faults more lightly me not putting in the correct order please take a look below.

#include <OneButton.h>
#include <Wire.h> 
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver myServo = Adafruit_PWMServoDriver();
#define SERVOMIN 150
#define SERVOMAX 600
#define EXE_showPeriod 3000
#define EXE_hidePeriod 10000
#define PIN_INPUT[] = {A0, A1, A2, A3};  // RF button inputs
int PinCount = 4;
#define PIN_LED 13
enum states
{
  RUNNING,
  ALL_UP,
  ALL_DOWN,
  STOPPED
};
byte currentState;
const byte runPin = A3;
const byte upPin = A2;
const byte downPin = A1;
const byte stopPin = A0;
unsigned long startTime;
byte showCount;
boolean showing;
boolean hiding;
unsigned long showPeriod = 3000;    // show target for 3 seconds
unsigned long hidePeriod = 10000;   // hide target for 10 seconds
unsigned long currentTime;
byte sNum;
uint8_t numberOfServos = 9;                    
const byte ledPin = 13;                          //using the built in LED


void setup(){ 

  pinMode(ledPin, OUTPUT),
  myServo.begin();
  myServo.setPWMFreq(60);
  
  Serial.begin(115200);
  pinMode(runPin, INPUT_PULLUP);
  pinMode(upPin, INPUT_PULLUP);
  pinMode(downPin, INPUT_PULLUP);
  pinMode(stopPin, INPUT_PULLUP);
  currentState = STOPPED;
  Serial.println("in state STOPPED");
}

void loop(){
  switch (currentState)
  {
    case STOPPED:
      if (digitalRead(runPin) == LOW)
      {
        currentState = RUNNING;
        showCount = 0;
        showing = false;
        Serial.println("in state RUNNING");
      }
      break;
    case RUNNING:
      currentTime = millis();
      if (!showing)
      {
        sNum = random(11);
        showing = true;
        showCount++;
        startTime = currentTime;
        Serial.print("showing servo number ");
        Serial.println(sNum);
      
  digitalWrite(ledPin, !digitalRead(ledPin));   //if so, change the state of the LED.  
  static byte count = 0;                                               
  int servonum = random(0, 9);                  // pick a servo 
  
                          
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)   
  myServo.setPWM(servonum, 0, pulselen); 
  if (currentTime - startTime >= showPeriod) {                           // show target for 3 seconds
      startTime = currentTime;
  }                               
                                                  
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)
  myServo.setPWM(servonum, 0, pulselen); 
  if (currentTime - startTime >= hidePeriod) {                          // hide target for 10 seconds
      startTime = currentTime;
  }   
  count++;

      }
      else
      {  
  if (currentTime - startTime >= showPeriod)
        {
          Serial.print("showing servo number ");
          Serial.println(sNum);
          showing = false;
          if (showCount == 10)
          {
            currentState = STOPPED;
            Serial.println("in state STOPPED");
          }
        }
      }
      break;
    {
    switch (currentState)
    {
    case STOPPED:
    if (digitalRead(upPin) == LOW)
       currentState = ALL_UP;
        Serial.println("in state ALL_UP");
    for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++);
      break;
    } 
   switch (currentState)  
    case STOPPED:
    if (digitalRead(downPin) == LOW)
       currentState = ALL_DOWN;
        Serial.println("in state ALL_DOWN");
    for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--);
      break;
  };
}    
}

Forget about debounce for now, I am not convinced that you need it.

I started to look at your code but the combination of switch/case and if/else had got very complicated so I added some more states to tidy it up

#include <OneButton.h>
#include <Wire.h>
#define SERVOMIN 150
#define SERVOMAX 600
#define PIN_INPUT[] = {A0, A1, A2, A3};  // RF button inputs
int PinCount = 4;
enum states
{
  STOPPED,
  REVEAL_TARGET,
  SHOWING_TARGET,
  HIDE_TARGET,
  ALL_UP,
  ALL_DOWN
};
byte currentState;
const byte runPin = A3;
const byte upPin = A2;
const byte downPin = A1;
const byte stopPin = A0;
unsigned long startTime;
byte showCount;
unsigned long showPeriod = 3000;    // show target for 3 seconds
unsigned long hidePeriod = 10000;   // hide target for 10 seconds
unsigned long currentTime;
byte servoNum;
uint8_t numberOfServos = 9;
const byte ledPin = 13;                          //using the built in LED


void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
  pinMode(runPin, INPUT_PULLUP);
  pinMode(upPin, INPUT_PULLUP);
  pinMode(downPin, INPUT_PULLUP);
  pinMode(stopPin, INPUT_PULLUP);
  currentState = STOPPED;
  Serial.println("in state STOPPED");
}

void loop()
{
  currentTime = millis();
  switch (currentState)
  {
    case STOPPED:
      if (digitalRead(runPin) == LOW)
      {
        currentState = REVEAL_TARGET;
        Serial.println("in state REVEAL_TARGET");
        showCount = 0;
      }
      break;
    case REVEAL_TARGET:
      //your servo code to reveal the target goes here
      showCount++;
      digitalWrite(ledPin, !digitalRead(ledPin));   //change the state of the LED.
      servoNum = random(0, 9);                  // pick a servo
      currentState = SHOWING_TARGET;
      Serial.println("in state SHOWING_TARGET");
      Serial.print("showing servo number ");
      Serial.println(servoNum);
      startTime = currentTime;
      break;
    case SHOWING_TARGET:
      if (currentTime - startTime >= showPeriod)                             // show target for 3 seconds
      {
        currentState = HIDE_TARGET;
        Serial.println("in state HIDE_TARGET");
      }
      break;
    case HIDE_TARGET:
      //your servo code to hide the target goes here
      Serial.print("hiding servo number ");
      Serial.println(servoNum);
      if (showCount == 10)
      {
        currentState = STOPPED;
        Serial.println("in state STOPPED");
      }
      else
      {
        currentState = REVEAL_TARGET;
      }
      break;
  }
}

I took out the code relating to the servos, apart from the servo number, because I don't have the libraries installed. Try this sketch as it is and look at the Serial monitor output which is quite verbose

When you are happy with it put the servo library and servo commands back and try it for real. As written a new target pops up as soon as one is hidden but it would be easy to add a state to provide a gap between the hide and reveal actions and that could be a random period if you want

Obviously there needs to be code added for the all up and all down states

Hello UKHELIBOB thankyou for doing that I have been having a little look will have more time tomorrow, I want to run something by you, when all plugged in the remote does not activate start stopped etc but if I disconnect the inputs A0, A1, A2, A3 and tap one of the wires on start or stopped it works so would that mean my code should be in pulldown I also searched for some info online for the receiver it is VT HIGH (5V TTL level ) when signal received. Another test with LED it has a faint glow and goes out when button pushed so do need to change something in the sketch I don't want to mess with it to much I read if I get it wrong it will ruin the Arduino.

Can you please post a link to the receiver that you are using

My code was written for my test setup which uses INPUT_PULLUP on the button pins and the buttons take the input LOW when pressed but there is no reason why you should not reverse the logic and hardware to match your requirements

  • use INPUT instead of INPUT_PULLUP in pinMode()
  • test for the input pin state being HIGH to indicate that it has been activated

As to the LED, I am not clear where you connected it/them and how so can you please provide more details

Hi UKHELIBOB I answered this morning through email reply but it just came back. Anyway it is a Parallax receiver with 4 button fob I brought it from RS components they have a small spec sheet it comes up as one of the first in the list when searched on google but I found this last night that has more details (4 channel RF rolling code controlled by Arduino uno) it even has a small sketch at the end of the spec page that looks to be using INPUT like you said, is it only changed where it says pinMode, and as for the LED I powered the receiver on a bread board with the pin wire to the LED you could see a faint glow that goes out when the button is pressed, as for the sketch I will need the millis delay for the 3 second showing and the 10 seconds hiding in the main part of the loop which will stop after 10 random targets many thanks for your help have a great day.

Try this simple sketch

const byte inPin = A3;    //choose a pin number to suit yourself

void setup()
{
  Serial.begin(115200);
  pinMode(inPin, INPUT);
}

void loop()
{
  if (digitalRead(inPin) == HIGH)
  {
    Serial.println("button is pressed");
  }
}

Connect the GND of the receiver to GND of the Arduino and one of the receiver output pins to your chosen input pin on the Arduino and press the buttons on the fob in turn. Try each of the outputs of the receiver with each button. What, if anything, happens ?

Hi UKHELIBOB I loaded the sketch as you said and with the serial port open 1 push on the button fills the page with (button is pressed) is that a bounce problem

Does the message stop being printed when you release the button ? Do you get the same effect with all 4 buttons and the corresponding outputs ?

Hi UKHELIBOB I have now checked the buttons and inputs a0, a1, a2, a3 and a couple of the other pins all with same results.
push RF button once and the screen fills with button is pressed, if you hold it down print continues and stops printing when released this is the same for each button and pin.

So, you are in business and have all of the information required to allow you to control the sketch with the key fob