Hey, everyone. I am new to Arduino and admittedly rusty on C++. We are making a bomb prop for a new company that is going to do outdoor laser tag. Arming it will involve doing a bunch of things in order, and this snippet is testing for four switches to be switched on in order to advance to the next step. As you will see below, it tests for the proper order, then advances the ARMING_STAGE variable appropriately. What is not there yet, is the other tests, to see if any switch has been flipped OUT of order. As I wrote it, it just seems like some sort of array, for example, SWITCH_ALL could be defined and then examined instead of an insane amount of if/then. Maybe it would have 6 elements, a null, positions 1-4, and then the arming state. Several arming states would be essentially flagged as the same variable, indicating a false alignment and then triggering the ARM_SEQUENCE_FAIL variable to go to true, which would then leave the while loop and make the player have to start over. Am I close? Would anyone have an example of something similar as to how it would look?
Thanks!
while (ARMING_STAGE > 0 && ARM_SEQUENCE_FAIL == 0)
{
while (TIMEOUT_ARMING == 0)
{
if (SWITCH_GRN == 0 && SWITCH_BLUE == 0 && SWITCH_YELL == 0 && SWITCH_RED == 0) //This needs an array to check that switches are flipped and maintained in the correct order.
{
ARMING_STAGE = 2;
}
if (SWITCH_GRN == 1 && SWITCH_BLUE == 0 && SWITCH_YELL == 0 && SWITCH_RED == 0)
{
ARMING_STAGE = 3;
digitalWrite(SWITCH_LED_GRN,HIGH);
}
if (SWITCH_GRN == 1 && SWITCH_BLUE == 1 && SWITCH_YELL == 0 && SWITCH_RED == 0)
{
ARMING_STAGE = 4;
digitalWrite(SWITCH_LED_BLUE,HIGH);
}
if (SWITCH_GRN == 1 && SWITCH_BLUE == 1 && SWITCH_YELL == 1 && SWITCH_RED == 1)
{
ARMING_STAGE = 5;
digitalWrite(SWITCH_LED_YELL,HIGH);
}
if (SWITCH_RED == 1 && SWITCH_YELL ==1 && SWITCH_GRN == 1 && SWITCH_BLUE == 1)
{
ARMING_STAGE = 6;
// EFFECT - Nodes activated sound
// VOICE - Nodes activated. Prime ignition.
digitalWrite(SWITCH_LED_RED,HIGH); // Turn on LED for green button
}
use tests for conditions to advance a state... a state engine.
void loop()
{
if (state == 0)
{
//do zero state stuff
//
if (condition == true)
{
state ++;
}
}
else if (state == 1)
{
// do state = 1 stuff
//
if (condition2 == true)
{
state++
}
else
{
state = 0;// start over
}
}
}
Yes, let me try that. I think that may work. Still curious, though. Would there be an array solution that could reduce that code to just a few lines? It would work similar to what you wrote, but would examine the elements of the array to be valid before advancing the state??
Had to Google that one. That looks like what I have seen when addressing a keypad, which, yes, I was thinking something along that line.
After writing it the way you suggested, it looks very good, and it only advances to the next state if things are switched properly, which definitely reduces the code by half.
I can think of a few different ways to program it.
But without the whole sketch, it is hard to figure out what the best option is.
Suppose you convert the switches into 4 bits of a variable.
variable: gbyr
red: bit 0
yellow: bit 1
blue: bit 2
green: bit 3
Then you could do this:
switch (gbyr)
{
// the numbers are the bits for g(reen)b(lue)y(ellow)r(ed).
case B0000:
ARMING_STAGE = 2;
break;
case B1000:
ARMING_STAGE = 3;
digitalWrite(SWITCH_LED_GRN,HIGH);
break;
case B1001:
... and so on.
}
And also with an array is possible.
However, I don't see a problem with the many if-else
Suppose the switches are booleans.
boolean switch_red, switch_blue, switch_green, switch_yellow;
if (!switch_green && !switch_blue && !switch_yellow && !switch_red)
arming_state = 2;
else if( switch_green && !switch_blue && !switch_yellow && !switch_red)
arming_state = 3;
else if ... and so on
switch (arming_state)
{
case 2:
break;
case 3:
digitalWrite(SWITCH_LED_GRN,HIGH);
break;
... and so on.
}
You do know that in your sketch the 'if'-condition for arming state 5 is the same as for arming state 6 ? As a result the ARMING_STAGE = 5; is always overwritten by ARMING_STAGE = 6;
const int numberOfSwitches = 4;
const byte switchPins [numberOfSwitches] = { 8, 9, 10, 11 }; // or whatever
const byte wantedOrder [numberOfSwitches] = { 4, 2, 1, 3 }; // press in this order
byte state = 0;
void disArm ()
{
Serial.println ("Bomb disarmed.");
} // end of disArm
void setup ()
{
Serial.begin (115200);
Serial.println ();
// activate pull-ups, press switch by grounding it
for (byte i = 0; i < numberOfSwitches; i++)
pinMode (switchPins [i], INPUT_PULLUP);
Serial.println ("Ready.");
} // end of setup
void loop ()
{
byte whichSwitch = 0;
byte numberOn = 0;
// read all switches
for (byte i = 0; i < numberOfSwitches; i++)
{
if (digitalRead (switchPins [i]) == LOW)
{
whichSwitch = i;
numberOn++;
Serial.print ("Switch ");
Serial.print (whichSwitch + 1);
Serial.println (" pressed.");
} // end of switch pressed
} // end for loop
// nothing pressed?
if (numberOn == 0)
return;
delay (100); // debounce
// detect cheating attempts to press more than one switch
if (numberOn > 1)
{
state = 0;
Serial.println ("More than one switch pressed, state reset.");
return;
} // end of if too many
// check this is the right one
if ((whichSwitch + 1) == wantedOrder [state])
{
Serial.println ("Correct switch pressed.");
state++;
if (state >= numberOfSwitches)
{
disArm ();
state = 0;
} // end of all switches pressed correctly
} // end of if correct switch
else
{
state = 0;
Serial.println ("Incorrect switch pressed, state reset.");
} // end of wrong switch
} // end of loop
This allows for the actual switches to be connected to any pins (see switchPins). The correct sequence is in wantedOrder variable (numbered 1 to 4 in this case).
The "state" variable keeps track of how far through the correct sequence you are. If you get one wrong it goes back to state 0, which is no correct switches pressed.
Peter_n, YES!! and YES!!!. Essentially a character array. That was exactly what I had in my head, but I couldn't quite think of how it should look. And I should definitely redefine all the boolean states AS booleans not int. Forgot that was a valid type. Most of the variables are exactly that, as they test whether something has happened or not, and good catch on the Stage 6. I was switching the order around, and I didn't change the last one back to 0 as it should be or simply !variable going forward.
The only part that changes from there is the "barograph" section, where the player has to hold a button down to "charge" the device. I didn't post all the code, as it's still mostly in outline form, as I wrote the overall doc in word. Now I am converting the outline to code, with lots of commenting on what should happen in each section, and then finally to actual code.
NIck, I will have to ponder over yours, but keep in mind there is a ton more going on, and the switches are just a single subset of the procedure of the arm and disarm sequence.
Nick, and I almost forgot, as your idea about defining pins from the beginning is a great idea. I did that in definitions, so that no matter how it is wired, it just needs to be edited at the beginning for that exact reason… and being new to Arduino, and using pretty much every pin on the Mega, I am leaving it up to the fabricator to determine where each pin will go.
For a large project, did you already create more files ?
On the right of the Arduino IDE is a drop down menu. You could create a file for the buttons. And make functions for example : "ButtonsToArmingState".
NIck, also thank you for the disarm function call. I haven't delved into how those should work, but I know I will need to call them to get out of the loop for termination based on a fail_to_arm or fail_to_disarm, as well as possibly the arm or disarm functions as well.
No, I haven't, Peter_n. I assume that is where you would then use the #include function? At least at the moment I was thinking of calling various functions inside the file, as Nick suggested, instead of separate files and calling functions that way. I will definitely consider it. What would you say is the benefit to internal functions versus external functions? It would almost seem like extra work in that variables would have to be defined in both places. Thanks!
Regardless of the complexity of the rest of the code, what I posted should handle the general case. You have a "state" which is how far through the sequence they got. You could even have switches repeated, because as we work through the state array, only one switch is valid at any point (eg. it could be 2-4-2-4-4-1). Thus the fact that 4 appears more than once doesn't affect the behaviour, because the important thing is that they have to step through the states (ie. 1-2-3-4-5-6) with only one input being valid for each transition.
In fact, I think you should allow for the same switch being pressed more than once, because after all, if you can only press each switch once, and you have four switches, then once you have pressed three of them correctly, the remaining one must be the correct one for the last press. That makes it too easy.
Firestorm:
What would you say is the benefit to internal functions versus external functions? It would almost seem like extra work in that variables would have to be defined in both places. Thanks!
The benefit is neatness, or more organized code. Only you can say if it is worth it in this particular case. You can always pass variables to functions, they don't have to be global.
About the more files: When you add files to your project (using the drop down menu on the upper-right) they are the same. They are not external. It is only to prevent a lot of code in a single file and scrolling all the time to find that piece of code you are looking for.
To clarify, if you add .ino files they are concatenated by the IDE into one file, and thus global variable are shared between all the .ino files.
If you add a .cpp file (or a .h file) they behave in the usual way, and you have to manually take steps to share variables between them (eg. using extern).
In my experience the extra .ino files are added in alphabetcal order and code in a later file can access global variables in an earlier file, but not vice versa. So it's probably a good idea to define global variables in the principal .ino file.
Yes, I think it is the principal file (the one the sketch is named after) followed by the other files in alphabetical order. I don't like this system personally, it makes things confusing. However you are right that global variables would logically go, therefore, in the principal .ino file.
As I look more at your code, some light bulbs are definitely turning on, Nick. Thinking about it, instead of all these nested while and if loops, each boolean state and arming stage could be a function possibly. For example, it would call the ArmingStage2() function, which would then test the conditions similar to what you suggest. BombArm() would do the operations when it actually arms, etc, etc. I will play around with it some more. I also realized that I had to add digtialRead(SWITCH_GRN) for each one, since SWITCH_GRN is the IO number not actually a boolean. One of the things you wrote made me catch that.
Hey, on a side note, I notice you said Lua Rules. Are you an expert Lua programmer? Where are good resources to get examples and tutorials on learning it? Control4 uses it almost exclusively, and we would love to be able to write drivers, but talking to them, it's like a dark art.