I am creating an Arduino project that plays Rock Paper Scissors.
I have a part of the code reading the value of the button and whenever I click only one button, it outputs three times (View code and images of serial monitor to understand what I mean, I can't really explain it)
Here is my code:
// constants won't change. They're used here to
// set pin numbers:
const int buttonPin1 = A0; // the number of the pushbutton pin
const int buttonPin2 = A1;
const int buttonPin3 = A2;
const int ledPin = 12; // the number of the LED pin
// variables will change:
int buttonState1 = 0; // variable for reading the pushbutton status
int buttonState2 = 0;
int buttonState3 = 0;
char * choices[3] = {"Rock", "Paper", "Scissors"};
char * finalResult[3] = {"You Lost", "You Won!", "It's a Tie"};
byte Human = 0, Computer = 0, FR = 0;
char result[25];
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin1, INPUT);
pinMode(buttonPin2, INPUT);
pinMode(buttonPin3, INPUT);
digitalWrite(ledPin,LOW);
Serial.begin(9600);
unsigned long R;
randomSeed(R);
}
void loop(){
int choice = random(1,4);
int pick;
// read the state of the pushbutton value:
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
Serial.println("Entering Reading Loop...");
while(true)
{
buttonState1 = digitalRead(buttonPin1);
buttonState2 = digitalRead(buttonPin2);
buttonState3 = digitalRead(buttonPin3);
if (buttonState1 == HIGH && buttonState2 == LOW && buttonState3 == LOW) {
// turn LED on:
Throw('1');
} else if (buttonState1 == LOW && buttonState2 == HIGH && buttonState3 == LOW) {
// turn LED on:
pick = 2; //Paper
Throw('2');
}else if (buttonState1 == LOW && buttonState2 == LOW && buttonState3 == HIGH) {
// turn LED on:
pick = 3; //Scissor
Throw('3');
}
}
}
void Throw(char H)
{
bool thrown = false;
H -= '0'; //convert ascii to decimal
H -= 1; // instead of 1,2,3, H is now 0,1,2
byte C = random(0, 3);
sprintf(result, "The Computer chose: %s, You chose: %s", choices[C], choices[H]);
Serial.println(result);
if ( C == H)
Serial.println(F("Its a TIE"));
else
{
switch (C)
{
case 0:
switch (H)
{
case 1:
Serial.println(F("Paper wraps Rock, You WIN!"));
Human++;
break;
case 2:
Serial.println(F("Rock crushes Scissors, You LOSE!"));
Computer++;
break;
}
break;
case 1:
switch (H)
{
case 0:
Serial.println(F("Paper wraps Rock, You LOSE!"));
Computer++;
break;
case 2:
Serial.println(F("Scissors cuts Paper, You WIN!"));
Human++;
break;
}
break;
case 2:
switch (H)
{
case 0:
Serial.println(F("Rock crushes Scissors, You WIN!"));
Human++;
break;
case 1:
Serial.println(F("Scissors cuts Paper, You LOSE!"));
Computer++;
break;
}
break;
}
}
}
This is what I'm getting on the serial monitor when I click only ONE button(the rock button)
http://gyazo.com/ceb5c8329993339368cf5d52181ed4d7
As you can see, it chooses the right option for the button but it calls the throw function 3 times.
http://playground.arduino.cc/Main/RockPaperScissors)
From the Arduino docs they recommend using a Debounce strategy for handling inputs.
Without debouncing, pressing the button once can appear to the code as
multiple presses.
http://www.arduino.cc/en/Tutorial/Debounce
Here is another example illustrating the Debounce technique:
http://danthompsonsblog.blogspot.com/2011/12/arduino-push-button-onoff-example.html
There are pull-up resistors built in to the arduino that will allow you to filter out the erratic electrical signal you get when pressing a button. See the section on INPUT_PULLUP here.
I don't what the exact issue was but, I added a delay in my throw function and it stopped outputting the serial print statement 3 times!
Here is my updated function:
void Throw(char H)
{
bool thrown = false;
H -= '0'; //convert ascii to decimal
H -= 1; // instead of 1,2,3, H is now 0,1,2
byte C = random(0, 3);
sprintf(result, "The Computer chose: %s, You chose: %s", choices[C], choices[H]);
Serial.println(result);
delay(500);
if ( C == H)
Serial.println(F("Its a TIE"));
else
{
switch (C)
{
case 0:
switch (H)
{
case 1:
Serial.println(F("Paper wraps Rock, You WIN!"));
Human++;
break;
return;
case 2:
Serial.println(F("Rock crushes Scissors, You LOSE!"));
Computer++;
break;
}
break;
case 1:
switch (H)
{
case 0:
Serial.println(F("Paper wraps Rock, You LOSE!"));
Computer++;
break;
case 2:
Serial.println(F("Scissors cuts Paper, You WIN!"));
Human++;
break;
}
break;
case 2:
switch (H)
{
case 0:
Serial.println(F("Rock crushes Scissors, You WIN!"));
Human++;
break;
case 1:
Serial.println(F("Scissors cuts Paper, You LOSE!"));
Computer++;
break;
}
break;
}
}
}
Related
I'm working on a circuit that has two separate 4-bit binary counters with LEDs. I press a button and one counter begins counting to 15 in binary. I press a second button and the first counter pauses where it is and the second group of LEDs begin counting to 15 in binary. I got both counters working, but I can't get the first group to pause and the second to begin. I've tried using if statements with a boolean flag, but it messes up the first group of LEDs. How can I get ledPins1[] to pause when button2 is pressed, then resume when ledPins2[] finish?
int ledPin1[] = {2,3,4,5};
int ledPin2[] = {7,8,9,10};
int button1 = 11;
int button2 = 12;
boolean button1Last = LOW;
boolean button1Current = LOW;
boolean button2Last = LOW;
boolean button2Current = LOW;
void setup()
{
pinMode(button1, INPUT);
pinMode(button2, INPUT);
for(int i=0; i<4; i++)
{
pinMode(ledPin1[i], OUTPUT);
}
for(int i=0; i<4; i++)
{
pinMode(ledPin2[i], OUTPUT);
}
}
boolean waitForButtonPush1 (boolean lastStartSwitchState1)
{
boolean currentStartSwitchState1 = digitalRead(button1);
if(lastStartSwitchState1 != currentStartSwitchState1) delay(20);
currentStartSwitchState1 = digitalRead(button1);
return currentStartSwitchState1;
}
boolean waitForButtonPush2 (boolean lastStartSwitchState2)
{
boolean currentStartSwitchState2 = digitalRead(button2);
if(lastStartSwitchState2 != currentStartSwitchState2) delay(20);
currentStartSwitchState2 = digitalRead(button2);
return currentStartSwitchState2;
}
void loop()
{
button1Current = waitForButtonPush1(button1Last);
if(button1Last == LOW && button1Current == HIGH)
{
for (byte counter =0;counter<=15; counter++)
{
displayBinary(counter);
delay(500);
}
}
button2Current = waitForButtonPush2(button2Last);
if(button2Last == LOW && button2Current == HIGH)
{
for (byte counter =0;counter<=15; counter++)
{
displayBinary2(counter);
delay(500);
}
}
}
void displayBinary(byte numToShow)
{
for (int i =0;i<4;i++)
{
if (bitRead(numToShow, i)==1)
{
digitalWrite(ledPin1[i], HIGH);
}
else
{
digitalWrite(ledPin1[i], LOW);
}
}
}
void displayBinary2(byte numToShow)
{
for (int i =0;i<4;i++)
{
if (bitRead(numToShow, i)==1)
{
digitalWrite(ledPin2[i], HIGH);
}
else
{
digitalWrite(ledPin2[i], LOW);
}
}
}
Welcome to the world of embedded devices!
Getting a small microprocessor to do several things at the same time is a bit tricky.
The key is to never block. No calls to delay(), no sending large buffers on the serial port at 9600 bauds in one go, etc...
There are some simple techniques to do it, one of the most commonly used is finite state machines.
Let's analyse your app a bit.
2 similar dssplay counters, with delay
2 buttons, buttons usually need to be debounced, that also involves a delay.
Some code, for you to tinker with:
// ****************************
// pinout
static const byte ledPin1[] = { 2, 3, 4, 5 };
static const byte ledPin2[] = { 7, 8, 9, 10 };
constexpr byte button1 = 11; // using constexpr for these saves 2 bytes of RAM.
constexpr byte button2 = 12;
// ****************************
// Counter data
static constexpr unsigned int led_delay = 500; // 500 ms, for all counters.
// constexpr ?? arduino supports c++17. Not all features in the main .ino
// module and all features in .cpp modules.
// Hint: you could have a member variable in the structure below for delay,
// this would allow for counters running at different speeds, or add buttons
// to increase/decrease speed.
// we have only 2 states, but you could add more, like running
// backwards, or run a different chase pattern maybe?
enum class led_counter_state : byte
{
stopped,
running,
};
struct led_counter_data_t
{
led_counter_state state; // STATE
byte counter; // counter current value
unsigned int timestamp; // used for timing.
const byte* leds; // LED pins.
};
static led_counter_data_t led_counter[2];
void led_display_init()
{
for (byte i = 0; i < 2; ++i)
{
led_counter[i].state = led_counter_state::stopped;
led_counter[i].counter = 0;
led_counter[i].timestamp = 0;
}
led_counter[0].leds = ledPin1;
led_counter[1].leds = ledPin2;
}
// ****************************
// LED cotrrol
static void leds_display_value(const led_counter_data_t& cntr)
{
for (byte i = 0, val = cntr.counter; i < 4; ++i, val >>= 1)
digitalWrite(cntr.leds[i], val & 0x01);
}
static void leds_control(led_counter_data_t& cntr)
{
const auto now = (unsigned int)millis(); // keep track of time.
switch(cntr.state)
{
default: // something is wrong.. stop.
cntr.state = led_counter_state::stopped;
// fall through ...
case led_counter_state::stopped:
return; // if not running, do nothing
case led_counter_state::running:
if (now - cntr.timestamp >= led_delay) // check delay
{
if (++cntr.counter > 15) // advance counter.
cntr.counter = 0;
leds_display_value(cntr); // show value.
cntr.timestamp = now; // keep track of time.
}
break;
}
}
static void leds_start(led_counter_data_t& cntr)
{
if (cntr.state != led_counter_state::stopped)
return;
cntr.state = led_counter_state::running;
if (++cntr.counter > 15) // advance counter.
cntr.counter = 0;
led_display_value(cntr); // show value.
cntr.timestamp = (unsigned int)millis();
}
static void leds_stop(led_counter_data_t& cntr)
{
cntr.state = led_counter_state::stopped;
}
// ****************************
// switch inputs data
static constexpr byte switch_debounce_delay = 30; // 30ms is a good value for
// debouncing
struct switch_data_t
{
byte sw1_state : 1; // no need to waste more than 1 bit per switch
byte sw2_state : 1;
byte timestamp; // we'll only count to 30 ms, so 1 byte timestamp will do
};
static switch_data_t switch_data;
// ****************************
// switch inputs code
static void control_inputs()
{
const auto now = (byte)millis();
if (now - switch_data.timestamp < switch_debounce_delay)
return;
switch_data.timestamp = now;
// All switch control logic is regrouped here, and isolated
// form other control code, this makes the logic easier to
// write, read, and debug.
bool b = digitalRead(button1);
if (b & !switch_data.sw1_state) // button was pushed right now.
{
if (led_counter[0].state == led_counter_state::stopped)
{
leds_start(led_counter[0]); // start counter 1
leds_stop(led_counter[1]); // stop counter 2
}
else
{
leds_stop(led_counter[0]); // stop counter 1
}
}
switch_data.sw1_state = b;
b = digitalRead(button2);
if (b & !switch_data.sw2_state) // button was pushed right now.
{
if (led_counter[1].state == led_counter_state::stopped)
{
leds_start(led_counter[1]); // start counter 2
leds_stop(led_counter[0]); // stop counter 1
}
else
{
leds_stop(led_counter[1]); // stop counter 2
}
}
switch_data.sw2_state = b;
}
// ****************************
void setup()
{
pinMode(button1, INPUT);
pinMode(button2, INPUT);
for (byte i = 0; i < 4; ++i)
{
digitalWrite(ledPin1[i], LOW);
pinMode(ledPin1[i], OUTPUT);
digitalWrite(ledPin2[i], LOW);
pinMode(ledPin2[i], OUTPUT);
}
led_display_init();
}
// ****************************
// The goal, always, is to exit loop() as fast as possible, so
// everything will run smoothly, and appear to run simultaneously.
void loop()
{
control_inputs();
leds_control(led_counter[0]);
leds_control(led_counter[1]);
}
I do not have an arduino with me, so I did not comppile nor ran this, but it should be pretty close. Let me know if you're having issues or have any questions.
I am making an alarm system using an ultrasonic distance sensor. It can be armed and disarmed using a remote, and I want a green led to be on when it is disarmed, and a red led to be on when it is armed. The red led works fine, but the green one stays on.
I have tried commenting out the line that turns on the green led, but it seems to be on by default, so when I start the program, it is already on and won't turn off. The code in question is near the bottom, marked by a comment that says //HERE
#include "IRremote.h"
#include "SR04.h"
#define TRIG_PIN 12
#define ECHO_PIN 11
SR04 sr04 = SR04(ECHO_PIN,TRIG_PIN);
long distance;
int receiver = 10;
IRrecv irrecv(receiver); // create instance of 'irrecv'
decode_results results; // create instance of 'decode_results'
int buzzer = 3;
int red_led = 2;
int green_led = 0;
bool can_sense = false;
bool release_pressed = false;
bool alarm_off = false;
/*-----( Function )-----*/
void translateIR() // takes action based on IR code received
// describing Remote IR codes
{
switch(results.value)
{
case 0xFFA25D: Serial.println("POWER"); alarm_off = true; break;
case 0xFFE21D: Serial.println("FUNC/STOP"); break;
case 0xFF629D: Serial.println("VOL+"); break;
case 0xFF22DD: Serial.println("FAST BACK"); break;
case 0xFF02FD: Serial.println("PAUSE"); release_pressed = true; break;
case 0xFFC23D: Serial.println("FAST FORWARD"); break;
case 0xFFE01F: Serial.println("DOWN"); break;
case 0xFFA857: Serial.println("VOL-"); break;
case 0xFF906F: Serial.println("UP"); break;
case 0xFF9867: Serial.println("EQ");release_pressed = false; break;
case 0xFFB04F: Serial.println("ST/REPT"); break;
case 0xFF6897: Serial.println("0"); break;
case 0xFF30CF: Serial.println("1"); break;
case 0xFF18E7: Serial.println("2"); break;
case 0xFF7A85: Serial.println("3"); break;
case 0xFF10EF: Serial.println("4"); break;
case 0xFF38C7: Serial.println("5"); break;
case 0xFF5AA5: Serial.println("6"); break;
case 0xFF42BD: Serial.println("7"); break;
case 0xFF4AB5: Serial.println("8"); break;
case 0xFF52AD: Serial.println("9"); break;
case 0xFFFFFFFF: Serial.println(" REPEAT");break;
default:
Serial.println(" other button ");
}// End Case
delay(100);
}
void setup() {
Serial.begin(9600);
pinMode(buzzer,OUTPUT);
pinMode(red_led,OUTPUT);
pinMode(green_led,OUTPUT);
irrecv.enableIRIn();
}
void loop() {
if (irrecv.decode(&results)) // have we received an IR signal?
{
translateIR();
irrecv.resume(); // receive the next value
}
distance = sr04.Distance();
if (distance < 70)
{
can_sense = true;
}
if (can_sense==true and release_pressed==false)
{
while (alarm_off==false)
{
digitalWrite(buzzer,HIGH);
digitalWrite(red_led,HIGH);
delay(500);
digitalWrite(buzzer,LOW);
digitalWrite(red_led,LOW);
delay(250);
if (irrecv.decode(&results)) // have we received an IR signal?
{
translateIR();
irrecv.resume(); // receive the next value
}
}
}
if (release_pressed==false) //HERE
{
digitalWrite(red_led,HIGH);
digitalWrite(green_led,LOW); // This should turn off the green led, but it doesn't
}
if (release_pressed==true)
{
digitalWrite(red_led,LOW);
digitalWrite(green_led,HIGH);
}
alarm_off = false;
can_sense = false;
}
Thanks for any help :)
Pins 0 and 1 on an Uno are used for serial communication.
As soon as you did Serial.begin(9600);, you enabled those pins for serial comms, so you can't also use them as standard digital pins.
Simply use another pin for your green LED.
I am using an RGB LED with a keypad. Pressing '1' turns on a light while pressing '2' turns off the light. After I press '3' I want the LED to loop through colors and only if a different button is pressed is when the code leaves the loop. My problem is while looping the keypads state of HIGH or LOW is not changed therefore the key that is saved as pressed cannot change. I need some way to get out of this loop without stopping the loop.
#include <Keypad.h>
const int GreenLED=9;
const int BlueLED=10;
const int RedLED=11;
const byte numRows=4;
const byte numCols=4;
char keymap[numRows][numCols] =
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
};
byte rowPins[numRows] = {5,4,3,2};
byte colPins[numCols] = {13,8,7,6};
char keypressed;
boolean ledPin_stateGreen;
boolean ledPin_stateRed;
boolean ledPin_stateBlue;
Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows,
numCols);
void setup() {
pinMode(GreenLED, OUTPUT);
pinMode(BlueLED, OUTPUT);
pinMode(RedLED, OUTPUT);
ledPin_stateGreen = digitalRead(GreenLED);
ledPin_stateRed = digitalRead(RedLED);
ledPin_stateBlue = digitalRead(BlueLED);
Serial.begin(9600);
}
void loop() {
char key = myKeypad.getKey();
if(key != NO_KEY)
{
Serial.println(key);
}
//Serial.println(myKeypad.getState());
keypadEvent(key);
}
void setColor(int red, int green, int blue)
{
#ifdef COMMON_ANODE
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
#endif
analogWrite(RedLED, red);
analogWrite(GreenLED, green);
analogWrite(BlueLED, blue);
}
void keypadEvent (KeypadEvent key)
{
switch (myKeypad.getState())
{
case PRESSED:
if (key == '1')
{
digitalWrite(GreenLED, HIGH);
digitalWrite(BlueLED, HIGH);
digitalWrite(RedLED, HIGH);
}
if (key == '2')
{
digitalWrite(GreenLED, LOW);
digitalWrite(BlueLED, LOW);
digitalWrite(RedLED, LOW);
}
if (key == '3')
{
int previousState= myKeypad.getState();
while(key == '3')
{
key = myKeypad.getKey();
setColor(255, 0, 0); // red
delay(200);
Serial.println(myKeypad.getState());
setColor (50,50,50); //white
delay (200);
setColor (255,40,0);
delay(200);
setColor(0, 255, 0); // green
delay(200);
setColor(0, 0, 255); // blue
delay(200);
setColor(255, 255, 0); // yellow
delay(200);
setColor(80, 0, 80); // purple
delay(200);
setColor(0, 255, 255); // aqua
delay(200);
Serial.println(myKeypad.getState());
}
}
}
}
In your setup, you have GreenLED, BlueLED, RedLED set to OUTPUT, but then you try to digitalRead() from them...
void setup() {
pinMode(GreenLED, OUTPUT);
pinMode(BlueLED, OUTPUT);
pinMode(RedLED, OUTPUT);
ledPin_stateGreen = digitalRead(GreenLED);
ledPin_stateRed = digitalRead(RedLED);
ledPin_stateBlue = digitalRead(BlueLED);
Serial.begin(9600);
}
Your loop is preventing the keypad from being read for about 1.6 seconds. Then you have a very very small window to have the key pressed to pickup the key. Also, as you have stated, once you are in the loop you can't exit out of it due to the fact that you don't check for key presses. The tutorial on Arduino Playground states:
Consider, though, when you are writing your code that every delay()
you use will take processing time away from the keypad. Something as
short as delay(250) can make the keypad seem very unresponsive. And
the same thing will happen if you sprinkle a bunch of delay(10)'s all
through your code.
One way to solve this problem is to remove the delays from your loop and turn your program into a state machine which continuously polls the keypad for key presses. Now there are many ways to do this, of which I have chosen only one, and actually I really have turned it into 2 state machines. The top level one is the overall state of your program (i.e. what is our state based on the key pressed). The second one is a state machine to represent your loop which changes the colors of your LEDs. You can learn more about state machines here.
Here is your program turned into the above state machines:
#include <Keypad.h>
const int GreenLED=9;
const int BlueLED=10;
const int RedLED=11;
const byte numRows=4;
const byte numCols=4;
char keymap[numRows][numCols] =
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
};
byte rowPins[numRows] = {5,4,3,2};
byte colPins[numCols] = {13,8,7,6};
char keypressed;
boolean ledPin_stateGreen;
boolean ledPin_stateRed;
boolean ledPin_stateBlue;
enum MyState {
LIGHT_ON,
LIGHT_OFF,
LIGHT_LOOPING
};
enum LightState {
COLOR_1,
COLOR_2,
COLOR_3,
COLOR_4,
COLOR_5,
COLOR_6,
COLOR_7,
COLOR_8
};
//Our current state for lights
MyState currentState = LIGHT_LOOPING;
LightState currentLightState = COLOR_1;
//The previous time in milliseconds
unsigned long prevTimeMS = 0;
Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows,
numCols);
void setup() {
pinMode(GreenLED, OUTPUT);
pinMode(BlueLED, OUTPUT);
pinMode(RedLED, OUTPUT);
ledPin_stateGreen = digitalRead(GreenLED);
ledPin_stateRed = digitalRead(RedLED);
ledPin_stateBlue = digitalRead(BlueLED);
Serial.begin(9600);
prevTimeMS = millis();
}
void loop() {
char key = myKeypad.getKey();
if(key != NO_KEY)
{
Serial.println(key);
}
//Serial.println(myKeypad.getState());
//This function is really checking to see if we need to perform a state
//transition or not for the currentState
keypadEvent(key);
//Do stuff based on the state We are in
unsigned long currentTimeMS = millis();
switch(currentState)
{
case LIGHT_ON:
//Don't really need to do anything since we perform
//the work on the state transition
break;
case LIGHT_OFF:
//Don't really need to do anything since we perform
//the work on the state transition
break;
case LIGHT_LOOPING:
//Now switch based on the current color state to see if we
//need to change to the next state
switch(currentLightState)
{
case COLOR_1:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_2);
}
break;
case COLOR_2:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_3);
}
break;
case COLOR_3:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_4);
}
break;
case COLOR_4:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_5);
}
break;
case COLOR_5:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_6);
}
break;
case COLOR_6:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_7);
}
break;
case COLOR_7:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_8);
}
break;
case COLOR_8:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
//which is back to the first state so we loop
transitionLightState(COLOR_1);
}
break;
}
break;
}
}
//This will return true if the correct amount of time has passed
boolean checkDelay(unsigned long currentMS, unsigned long prevMS, unsigned long delayMS)
{
if((currentMS - prevMS) >= delayMS)
{
return true;
}
return false;
}
void transitionMyState(MyState newState)
{
switch(newState)
{
case LIGHT_ON:
digitalWrite(GreenLED, HIGH);
digitalWrite(BlueLED, HIGH);
digitalWrite(RedLED, HIGH);
break;
case LIGHT_OFF:
digitalWrite(GreenLED, LOW);
digitalWrite(BlueLED, LOW);
digitalWrite(RedLED, LOW);
break;
case LIGHT_LOOPING:
//We want to transition to the COLOR_1 state here
transitionLightState(COLOR_1);
break;
}
currentState = newState;
//need to save off a new prevTimeMS
prevTimeMS = millis();
}
void transitionLightState(LightState newState)
{
//perform the action for the state transition
switch(newState)
{
case COLOR_1:
setColor(255, 0, 0); // red
break;
case COLOR_2:
setColor (50,50,50); //white
break;
case COLOR_3:
setColor (255,40,0);
break;
case COLOR_4:
setColor(0, 255, 0); // green
break;
case COLOR_5:
setColor(0, 0, 255); // blue
break;
case COLOR_6:
setColor(255, 255, 0); // yellow
break;
case COLOR_7:
setColor(80, 0, 80); // purple
break;
case COLOR_8:
setColor(0, 255, 255); // aqua
break;
}
currentLightState = newState;
//need to save off a new prevTimeMS
prevTimeMS = millis();
}
void setColor(int red, int green, int blue)
{
#ifdef COMMON_ANODE
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
#endif
analogWrite(RedLED, red);
analogWrite(GreenLED, green);
analogWrite(BlueLED, blue);
}
void keypadEvent (KeypadEvent key)
{
switch (myKeypad.getState())
{
case PRESSED:
if (key == '1')
{
transitionMyState(LIGHT_ON);
}
if (key == '2')
{
transitionMyState(LIGHT_OFF);
}
if (key == '3')
{
transitionMyState(LIGHT_LOOPING);
}
}
}
The implementation is more for readability then minimizing code by the way. I added 2 state variables which hold what state we are in for the key handler state machine and the looping state machine. The important thing to note about this implementation is that key presses are checked every iteration of the loop and there are no delays implemented. This allows for us to break out of the looping color state machine at anytime. You could also say that the looping of colors changed into a poll of "Do I need to change my color now?"
The other important thing to note is that now the time needs to be tracked so the looping state machine can determine when the right amount of time has passed in order to know when to change states. When a state transition happens then the current time is saved off. Note: The rollover of the millis counter is not taken into account in this example. This happens roughly every 50 days according to the Arduino docs. So every 50 days you will get a glitch if you are in the LIGHT_LOOPING state.
In the following code segment I am obtaining latitude, longitude, time, date and speed using GPS SKG13BL with Arduino UNO and then send it to the database via GSM module at every 5 secs. And if the speed (stored to s) returned by GPS is greater than 0, then an LED glows else will be low. After this code segment I am performing the database insertion by calling a URL. This works fine and obtains the results as I thought.
Code:
#include <NeoSWSerial.h>
//#include <SoftwareSerial.h>
#include <AltSoftSerial.h>
#include <NMEAGPS.h>
NeoSWSerial GSM(2, 3); // RX, TX: Connect TXD to RX & RXD to TX
static const int RXPin = 8, TXPin = 9; //when looking from antenna of gps , Last TTL pin is to be connected to 8 and second one to 9
AltSoftSerial gpsPort(RXPin, TXPin);
static const uint32_t GPSBaud = 9600;
NMEAGPS gps;
gps_fix fix;
uint8_t fixCount = 0;
char dt[15],tm[15],lati[10],longi[10],rno[13]="kl-05-jb-007";
//int block = 0;
enum _parseState {
PS_DETECT_MSG_TYPE,
PS_IGNORING_COMMAND_ECHO,
PS_HTTPPARA_RESPONSE,
PS_HTTPACTION_TYPE,
PS_HTTPACTION_RESULT,
PS_HTTPACTION_LENGTH,
PS_HTTPREAD_LENGTH,
PS_HTTPREAD_CONTENT
};
enum _actionState {
AS_IDLE,
AS_WAITING_FOR_RESPONSE
};
byte actionState = AS_IDLE;
unsigned long lastActionTime = 0;
int s;
byte parseState = PS_DETECT_MSG_TYPE;
char buffer[160],url[160];
byte pos = 0;
int contentLength = 0;
void resetBuffer() {
memset(buffer, 0, sizeof(buffer));
pos = 0;
}
void sendGSM(const char* msg, int waitMs = 500) {
GSM.println(msg);
while(GSM.available()) {
parseATText(GSM.read());
}
//delay(waitMs);
}
void setup()
{
GSM.begin(9600);
Serial.begin(9600);
gpsPort.begin(GPSBaud);
pinMode(13, OUTPUT); //pin 13 to be connected to positive of LED via a resistor
Serial.println(F("Hinder LOADING....."));
Serial.println(F("obtain gps and store data to database"));
Serial.println(F("Testing by : "));
Serial.println(F("Nikhil,Muthumani and Mathews"));
Serial.println();
sendGSM("AT+SAPBR=3,1,\"APN\",\"vodafone\"");
delay(500);
sendGSM("AT+SAPBR=1,1",3000);
delay(500);
sendGSM("AT+HTTPINIT");
delay(500);
sendGSM("AT+HTTPPARA=\"CID\",1");
delay(500);
}
void loop()
{
unsigned long now = millis();
while (gps.available( gpsPort )) {
fix = gps.read();
//Serial.println(F("b"));
// Once every 5 seconds...
if (++fixCount >= 5) {
//Serial.println(F("a"));
displayInfo();
if(s>0) // Change led state when speed greater than 0 or less than 0
digitalWrite(13, HIGH);
else
digitalWrite(13, LOW);
//send the next report if previous one is finished
if ( actionState == AS_IDLE ) {
sprintf(url, "AT+HTTPPARA=\"URL\",\"http://hinder.000webhostapp.com/HInsert.php?rno=%s&lat=%s&lng=%s&speed=%d&date=%s&time=%s\"",rno,lati,longi,s,dt,tm );
sendGSM(url);
// lastActionTime = now;
parseState = PS_HTTPPARA_RESPONSE; // a new state
actionState = AS_WAITING_FOR_RESPONSE;
fixCount = 0;
}
}
}
/* if ((gps.statistics.chars < 10) && (millis() > 5000)) {
Serial.println( F("No GPS detected: check wiring.") );
while(true);
block=1;
}*/
while(GSM.available()) {
//lastActionTime = now;
parseATText(GSM.read());
}
}
void displayInfo()
{
Serial.print(F("Location: "));
if (fix.valid.location) {
dtostrf( fix.latitude(),7,5,lati);
//sprintf(lati,"%d",fix.latitude(),5);
Serial.print(lati);
Serial.print( ',' );
dtostrf( fix.longitude(),8,5,longi);
//sprintf(longi,"%d",fix.longitude(),5);
Serial.print(longi);
} else {
Serial.print(F("INVALID"));
}
Serial.print(F(" Speed: "));
if (fix.valid.speed) {
s=fix.speed_kph();
Serial.print(s);
Serial.print(F(" KMPH "));
} else {
Serial.print(F("INVALID"));
}
// Shift the date/time to local time
NeoGPS::clock_t localSeconds;
NeoGPS::time_t localTime;
if (fix.valid.date && fix.valid.time) {
using namespace NeoGPS; // save a little typing below...
localSeconds = (clock_t) fix.dateTime; // convert structure to a second count
localSeconds += 5 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE; // shift timezone
localTime = localSeconds; // convert back to a structure
}
Serial.print(F(" Date : "));
if (fix.valid.date) {
sprintf(dt,"%02d/%02d/20%d",fix.dateTime.date,fix.dateTime.month,fix.dateTime.year);
Serial.print(dt);
} else {
Serial.print(F("INVALID"));
}
Serial.print(F(" Time : "));
if (fix.valid.time) {
if (localTime.hours>12)localTime.hours-=12;//To convert 24 hr format to 12 hr format
sprintf(tm,"%02d:%02d:%02d",localTime.hours,localTime.minutes,localTime.seconds);
Serial.print(tm);
//block=1;
} else {
Serial.print(F("INVALID"));
}
Serial.println();
}
void parseATText(byte b) {
buffer[pos++] = b;
if ( pos >= sizeof(buffer) )
resetBuffer(); // just to be safe
/*
// Detailed debugging
Serial.println();
Serial.print("state = ");
Serial.println(state);
Serial.print("b = ");
Serial.println(b);
Serial.print("pos = ");
Serial.println(pos);
Serial.print("buffer = ");
Serial.println(buffer);*/
switch (parseState) {
case PS_HTTPPARA_RESPONSE:
{
parseState = PS_DETECT_MSG_TYPE;
sendGSM("AT+HTTPACTION=0");
/* while(GSM.available()) {
//lastActionTime = now;
parseATText(GSM.read());
}*/
}
break;
case PS_DETECT_MSG_TYPE:
{
if ( b == '\n' )
resetBuffer();
else {
if ( pos == 3 && strcmp(buffer, "AT+") == 0 ) {
parseState = PS_IGNORING_COMMAND_ECHO;
}
else if ( b == ':' ) {
//Serial.print("Checking message type: ");
//Serial.println(buffer);
if ( strcmp(buffer, "+HTTPACTION:") == 0 ) {
Serial.println("Received HTTPACTION");
parseState = PS_HTTPACTION_TYPE;
}
else if ( strcmp(buffer, "+HTTPREAD:") == 0 ) {
Serial.println("Received HTTPREAD");
parseState = PS_HTTPREAD_LENGTH;
}
resetBuffer();
}
}
}
break;
case PS_IGNORING_COMMAND_ECHO:
{
if ( b == '\n' ) {
Serial.print("Ignoring echo: ");
Serial.println(buffer);
parseState = PS_DETECT_MSG_TYPE;
resetBuffer();
}
}
break;
case PS_HTTPACTION_TYPE:
{
if ( b == ',' ) {
Serial.print("HTTPACTION type is ");
Serial.println(buffer);
parseState = PS_HTTPACTION_RESULT;
resetBuffer();
}
}
break;
case PS_HTTPACTION_RESULT:
{
if ( b == ',' ) {
Serial.print("HTTPACTION result is ");
Serial.println(buffer);
parseState = PS_HTTPACTION_LENGTH;
resetBuffer();
}
}
break;
case PS_HTTPACTION_LENGTH:
{
if ( b == '\n' ) {
Serial.print("HTTPACTION length is ");
Serial.println(buffer);
// now request content
GSM.print("AT+HTTPREAD=0,");
GSM.println(buffer);
parseState = PS_DETECT_MSG_TYPE;
resetBuffer();
}
}
break;
case PS_HTTPREAD_LENGTH:
{
if ( b == '\n' ) {
contentLength = atoi(buffer);
Serial.print("HTTPREAD length is ");
Serial.println(contentLength);
Serial.print("HTTPREAD content: ");
parseState = PS_HTTPREAD_CONTENT;
resetBuffer();
}
}
break;
case PS_HTTPREAD_CONTENT:
{
// for this demo I'm just showing the content bytes in the serial monitor
Serial.write(b);
contentLength--;
if ( contentLength <= 0 ) {
// all content bytes have now been read
parseState = PS_DETECT_MSG_TYPE;
resetBuffer();
Serial.print("\n\n\n");
actionState = AS_IDLE;
}
}
break;
}
}
Now I need a modification in this as, when the speed is greater than 0, along with the glowing of the LED, a variable flag (int flag=0; declared globally) is incremented. And flag is set to 0 when the speed is equal to 0. And if the flag value becomes 2 (i.e when speed is greater than 0 for two consecutive readings) I want to send latitude, longitude, date, time and speed at that instant to another database which is also done by calling a URL (url1). And after that I want to send that to the usual database by calling its URL (url).
I did a modification as follows :
if(s>0) // Change led state when speed greater than 0 or less than 0
{
digitalWrite(13, HIGH);
flag++;
}
else
{
digitalWrite(13, LOW);
flag=0;
}
if(flag==2)
{
sprintf(url1, "AT+HTTPPARA=\"URL\",\"http://speed.000webhostapp.com/HInsert.php?rno=%s&lat=%s&lng=%s&speed=%d&date=%s&time=%s\"",rno,lati,longi,s,dt,tm );
sendGSM(url1);
parseState = PS_HTTPPARA_RESPONSE; // a new state
actionState = AS_WAITING_FOR_RESPONSE;
}
This doesn't obtain result as I thought. Can anyone help me. Is it possible to have flag variable working this way in loop(). Is there a problem with my usage of flag or is it another problem.
It is bit tough to make arduino request for two URLs as we would have to handle two separate request and its responses, either the two request would merge in and wont obtain required result or the code would become large that arduino-uno wont be able to handle.
So i found an easy way for doing that, i.e to request for the second PHP page from the first PHP page (which is requested by arduino itself). The only difference between these two request was that the second one need an extra variable. I passed along with the first request so it was available for making request from first page.
I changed arduino code as follows :
First I modified the portion making the check - just after the displayinfo();, as I mentioned in the question, added a flag bit to it. So it become like this :
if(s>0) // s is speed
{
digitalWrite(13, HIGH); //if speed exceeds limit then LED is set to HIGH
flag++;
}
else
{
digitalWrite(13, LOW);
flag=0;
}
The URL for request from arduino was added with the flag variable too. (For the request for second page).
sprintf(url, "AT+HTTPPARA=\"URL\",\"http://hinder.000webhostapp.com/HInsert.php?flag=%d&rno=%s&lat=%s&lng=%s&speed=%d&date=%s&time=%s\"",flag,rno,lati,longi,s,dt,tm );
In the PHP code I added following lines :
if ( $flag == 2 )
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://masp203.000webhostapp.com/RTO.php?rno=".$rno."&date=".$date."&time=".$time."&speed=".$speed."&street=".$street."" );
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
}
This worked just the way I wanted it to be.
Hope this helps some one.
My project is a dead bolt that uses RF key fob. It checks for unlock or lock button press and also checks if the lock is locked or not. I use a case statement for the various choices. I think for some reason the lock state isn't changing because I am only getting cases 2 and 4.
I watered down the output actions to light blinks for each case (case 1 blinks once case 2 twice etc..)
After each case executes it changes the Boolean lock state (locked = true)
Code
/*
This code will open a deadbolt
with RF remote or buttons and
knows the position of the lock
based on last action
12 RF/Button lock
13 RF/Button unlock
10 button lock
11 button unlock
8 Buzzer
9 Servo
A0 Servo location
4 LED lock
5 LED unlock
*/
const int lockrf = 12; //input to lock rf
const int ulockrf = 13; //input to unlock rf
const int butlock = 10; // button lock
const int butulock = 11; // button unlock
const int ulockled = 4; //led in lock button
const int lockled = 2; //led in ulock button
#include <Servo.h>
boolean lockstate = true;
void setup()
{
Servo deadbolt;
deadbolt.attach(9);
pinMode(butlock,INPUT);
pinMode(butulock,INPUT);
pinMode(ulockrf,INPUT);
pinMode(lockrf,INPUT);
pinMode(ulockled,OUTPUT);
pinMode(lockled,OUTPUT);
}
void loop()
{
//variables for code
int lockdeg = 0;
int ulockdeg = 90;
int lock_case = 0;
digitalWrite(ulockled,LOW);
digitalWrite(lockled,LOW);
if (digitalRead(lockrf)==HIGH || digitalRead(butlock)==HIGH && lockstate == true ) // locked press lock
{
lock_case=1;
}
if (digitalRead(lockrf)==HIGH || digitalRead(butlock)==HIGH && lockstate == false) // ulocked press lock
{
lock_case=2;
}
if (digitalRead(ulockrf)==HIGH || digitalRead(butulock)==HIGH && lockstate == true) // locked press ulock
{
lock_case=3;
}
if (digitalRead(ulockrf)==HIGH || digitalRead(butulock)==HIGH && lockstate == false) // ulocked press ulock
{
lock_case=4;
}
switch(lock_case)
{
case 1:
{
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
delay(10);
lockstate=true;
break;
}
case 2:
{
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
delay(500);
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
lockstate=true;
break;
}
case 3:
{
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
delay(500);
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
delay(500);
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
lockstate=false;
break;
}
case 4:
{
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
delay(500);
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
delay(500);
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(lockled,LOW);
delay(500);
digitalWrite(lockled,HIGH);
delay(500);
digitalWrite(ulockled,LOW);
delay(10);
lockstate=false;
break;
}
}
}
Your problem is operator precedence. Arduino follows C-language precedence, in which && is evaluated before ||. For example, this means that your first if statement is evaluated as if you had parentheses like so:
digitalRead(lockrf)==HIGH ||
(digitalRead(butlock)==HIGH && lockstate == true)
What happens here is that if lockrf is high, both of the first two if statements are true, so you first set lock_case to 1, then to 2. You get a similar effect when ulockrf is high. This is why you get only cases 2 and 4.
To fix this, add parentheses to disambiguate:
if ((digitalRead(lockrf)==HIGH || digitalRead(butlock)==HIGH)
&& lockstate == true )
Good programming practice suggests using parentheses if there's any doubt.