How to determine button press duration with wiringpi in C - c

For a aspberry pi project I have buttons connected to GPIO and I want to take different actions depending on how long a button has been pressed. The following python code (extract) is working as expected:
on_button2(channel):
t1 = time.time()
# Wait for button release
while GPIO.input(channel) == 0:
pass
duration = time.time() - t1
print "duration: %f" % (duration)
if duration > 0.75:
mpd_client.previous()
else:
mpd_client.next()
GPIO.add_event_detect(BUTTON2_PIN, GPIO.FALLING, callback=on_button2, bouncetime=700);
I would like to convert this into a C program (don't ask why, I really don't like python and I'm much more familiar with C, so I'd like to do it in C)
Trying to convert this to C with wiringPi, I came up with this, but it is not working as expected:
unsigned long btn2_t0;
static void on_button_2_pressed() {
unsigned long duration, t1;
int level;
level = digitalRead(BUTTON_2_PIN);
// Debounce button
t1 = millis();
if (t1 - btn2_t0 < 700) {
return;
}
btn2_t0 = t1;
// Wait for button being released
while (digitalRead(BUTTON_2_PIN) == LOW) {
delay(100);
}
duration = millis() - t1;
if (duration > 5000) {
printf("Self destruction sequence initiated!\n");
}
else if (duration > 700) {
player_previous();
}
else {
player_next();
}
}
int main() {
// Setup WiringPi Lib
wiringPiSetupGpio();
pinMode(BUTTON_2_PIN, INPUT);
// Register callbacks on button press
wiringPiISR(BUTTON_2_PIN, INT_EDGE_FALLING, on_button_2_pressed);
for (;;) {
delay(500);
}
return 0;
}
It seems that the loop that should wait for button release is not being executed or that the while-condition is always true, and thus, duration always is zero.
Is the digitalRead(BUTTON_2_PIN) function equivalent to the GPIO.input(channel) in the python code at all?
If somebody could point me in the right direction on how to detect button press (software-debounced) and measure the duration of the button press in C.
Thanks a lot.
EDIT: Working solution
After playing around a lot and with the help of Francesco Boi I came around with a working solution, although I do not really understand why the compare logic with HIGH / LOW is swapped compared to the python code (I thought that the button press will cause the pin to FALL to LOW and releasing it would RAISE it to HIGH ...)
static void on_button_2_pressed() {
unsigned long duration;
static unsigned long button_pressed_timestamp;
int level = digitalRead(BUTTON_2_PIN);
if (level == HIGH) { // Why HIGH ?!?
button_pressed_timestamp = millis();
}
else {
duration = millis() - button_pressed_timestamp;
button_pressed_timestamp = millis();
if (duration < 100) {
// debounce...
return;
}
else if (duration < 700) {
syslog(LOG_NOTICE, ">> NEXT\n");
}
else if (duration < 3000) {
syslog(LOG_NOTICE, "<< PREV\n");
}
else {
syslog(LOG_NOTICE, "!! REBOOT\n");
}
}
}
int main() {
...
wiringPiISR(BUTTON_2_PIN, INT_EDGE_BOTH, on_button_2_pressed);
...
}

First of all you are reading the value of the pin into level then in the while loop you are re-reading it: why is that? Can't you do something like:
// Wait for button being released
while (level == LOW) {
delay(100);
}
?
Also don't you want to reassign the btn2_t0 = t1; even when the time was smaller than 700 ms? Like:
t1 = millis();
if (t1 - btn2_t0 < 700) {
btn2_t0 = t1;
return;
}
The behaviour depends on how you built your electronic circuit: is the button-press suppose to make the pin high or low? Be sure about the behaviour is the one you are expecting by either connecting a led and a resistor or using a Voltmeter.
However since your Python code worked I am assuming the electronic is correct and your algorithm too.
Be sure of course you are pressing the button long enough. Add some prints to your code to understand which branches it is executing because when there is electronic involved it is difficult to understand only by the code.
While waiting for news from you, in my opinion is better to do the following: define the callback as: INT_EDGE_BOTH so that it got called when the bottom is pressed and when the button is released. You can keep the time elapsed with a static variable.
void yourCallback()
{
unsigned long ela, t;
int level = digitalRead(BUTTON_2_PIN);
static unsigned long button_pressed_timestamp;
//if button pressed:
if(level==LOW)
{
//start counting
button_pressed_timestamp = millis();
}
else //button released
{
duration = millis()-button_pressed_timestamp;
button_pressed_timestamp = millis(); //just to be sure....
if (duration > 5000) {
printf("Self destruction sequence initiated!\n");
}
else if (duration > 700) {
player_previous();
}
else {
player_next();
}
}
}

Related

Delay within a timer (what is the correct approach)

I'm in a pickle regarding concepts relating to timers. How can I can I operate a "delay" inside a timer? This is the best way I can frame the question knowing full well what I'm trying to do is nonsense. The objective is: I wish to test the pinState condition 2 times (once initially and then 4 seconds later) but this all needs to happen periodically (hence a timer).
The platform is NodeMCU running a WiFi (ESP8266 chip) and coding done inside Arduino IDE.
#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
BlynkTimer timer;
char auth[] = "x"; //Auth code sent via Email
char ssid[] = "x"; //Wifi name
char pass[] = "x"; //Wifi Password
int flag=0;
void notifyOnFire()
{
int pinState = digitalRead(D1);
if (pinState==0 && flag==0) {
delay(4000);
int pinStateAgain = digitalRead(D1);
if (pinStateAgain==0) {
Serial.println("Alarm has gone off");
Blynk.notify("House Alarm!!!");
flag=1;
}
}
else if (pinState==1)
{
flag=0;
}
}
void setup()
{
Serial.begin(9600);
Blynk.begin(auth, ssid, pass);
pinMode(D1,INPUT_PULLUP);
timer.setInterval(1000L,notifyOnFire);
}
void loop()
{
//Serial.println(WiFi.localIP());
Blynk.run();
timer.run();
}
an easy fix would be to set the periodicity of the timer to be 4000L timer.setInterval(4000L,notifyOnFire); and in notifyOnFire use a static variable and toggle its value whenever notifyOnFire is called
void notifyOnFire()
{
static char state = 0;
if( state == 0)
{
/* Write here the code you need to be executed before the 4 sec delay */
state = 1;
}
else
{
/* Write here the code you need to be executed after the 4 sec delay */
state = 0;
}
}
The nice thing about static variables is that they are initialized only once at compile time and they retain their values after the scope of code changes (In this case function notifyOnFire exits).

How can I have a conditional statement that performs two tasks? first one and then the other in a loop

So I am trying to code a program in arduino that does a task for 2 minutes and then another task for 5 mins. first one and then the other in a loop until a different condition is met.
Ive been trying to do this with if statements and while loops but im getting lost in the time part i think
//Pneumatic feed system
double minute = 1000*60;
double vibTime = 2 * minute; //2 mins
//double vibTime = 5000;
double execTime = millis();
double waitTime = 5 * minute; //time waits between vibrating
//double waitTime = 10000;
void DoPneuFeed() {
//Serial.print("Delta:");
//Serial.println(millis()-execTime);
if(Temp_Data[T_9] < 500)
{
if(execTime + vibTime < millis())
{
//turn on vibration for 2 mins
execTime = millis();
Serial.println("VIBRATING");
}
if(execTime + waitTime < millis())
{
//turn off vibration for 5 mins
execTime = millis();
Serial.println("WAITING");
}
}
if(Temp_Data[T_9] > 500)
{
relayOff(3);
relayOff(7);
}
}
void PneuFeed()
{
if(execTime + vibTime > millis())
{
//air pressure open
relayOn(3);
//vibrator on
relayOn(7);
}
else if(execTime + waitTime > millis())
{
relayOff(3);
relayOff(7);
}
}
I want to turn on the vibration mode for 2 mins and then turn it off for 5 mins. as long as the Temp_Data(T_9) < 500. if it is greater than 500 it should stay off.
Without going into the specifics of your code (since I cannot build it to test.) I only have one quick suggestion:
Instead of using a series of if-then-else (or variations of it) consider using a state machine. Implementations often include use of a switch() inside a while(expression){...} loop. The following is a very simple example of how you might be able to do the steps you need in this construct:
(Note, this is a mix of C and pseudo code for illustration. It is close to compilable , but contains a few undefined items.)
typedef enum {
START,
SET_VIB,
CHECK_TEMP,
GET_TIME,
CLEANUP,
SLEEP,
MAX_STATE
}STATE;
enum {
IN_WORK,
FAILED,
SUCCESS
};
enum {// durations in minutes
MIN_2,
MIN_5,
MIN_10,
MAX_DUR// change/add durations as profile needs
};
const int dur[MAX_DUR] = {120, 300, 600};
int RunProcess(STATE state);
int main(void)
{
STATE state = START;
RunProcess(state);
return 0;
}
int RunProcess(STATE state)//Add arguments here for temperature,
{ //Sleep duration, Cycles, etc. so they are not hardcoded.
int status;
time_t elapsed = 0; //s
BOOL running = TRUE;
double temp, setpoint;
int duration;
time_t someMsValue;
while(running)
switch (state) {
case START:
//Power to oven
//Power to vibe
//initialize state and status variables
duration = dur[MIN_2];
state = SLEEP;
status = IN_WORK;
break;
case SET_VIB:
//test and control vibe
if(duration == dur[MIN_2])
{
duration = dur[MIN_5];
//& turn off vibe
}
else
{
duration = dur[MIN_2];
//& turn on vibe
}
//init elapsed
elapsed = 0;
status = IN_WORK;
state = CHECK_TEMP;
break;
case CHECK_TEMP:
//read temperature
if(temp < setpoint)
{
state = SLEEP;
status = IN_WORK;
}
else
{
state = CLEANUP;
status = SUCCESS;
}
break;
case GET_TIME:
elapsed = getTime();
if(elapsed > duration) state = SET_VIB;
else state = SLEEP;
status = IN_WORK;
break;
case CLEANUP:
//turn off heat
//turn off vibe
running = FALSE;
break;
case SLEEP:
Sleep(someMsValue);
state = GET_TIME;
status = IN_WORK;
break;
default:
// called with incorrect state value
// error message and exit
status = FAILED;
state = CLEANUP;
break;
}
return status;
}
Suggestion for improvement to this illustration:
Expand this code to read in a "profile" file. It could include such things as parameter values typical to your process, such as temperature profiles, vibration profiles, number of cycles, etc. With such information, all of the hard-coding used here as illustration could be replaced with run-time configurable parameters, allowing the system to use many different profiles
without having to recompile the executable each time.
Another state machine example.

Arduino Switch to Turn a Relay timer

Briefly: I would like to turn on a relay for 30 seconds, after I toggle a switch on.
I'm trying to do a blinds automation at home.
I have a simple ON-OFF-ON switch, attached to an Arduino connected to Relays.
I want to turn on Relay#1 for a maximum of 30 seconds if I toggle the switch down from center. In other words, relay turns on when I switch, and when timer reaches 30 seconds relay turns off.
similarly I want to turn on Relay#2 for exactly 30 seconds if I toggle the switch up from center
And when I switch back to center, I would like the timer to reset.
I could not figure out how. Could anyone help?
I have been trying to use elapsedMillis library for this, which is a nice library that helps me avoid using Delays:
http://playground.arduino.cc/Code/ElapsedMillis
However even though I could work the relays without the 30 second limitation, I couldn't figure out the code to end working of the relays. Here is my current code:
#include <elapsedMillis.h>
#define RELAY_ON 0
#define RELAY_OFF 1
#define RELAY1_TURNS_ON_BLINDS 5
#define RELAY2_SHUTS_DOWN_BLINDS 6
#define shutswitch A0
#define openswitch A1
bool LocalCommandToOpen;
bool LocalCommandToShut;
void setup() ////////SETUP////////
{
digitalWrite(RELAY1_TURNS_ON_BLINDS, RELAY_OFF);
digitalWrite(RELAY2_SHUTS_DOWN_BLINDS, RELAY_OFF);
pinMode(RELAY1_TURNS_ON_BLINDS, OUTPUT);
pinMode(RELAY2_SHUTS_DOWN_BLINDS, OUTPUT);
pinMode(shutswitch, INPUT);
pinMode(openswitch, INPUT);
} ////SETUP
void loop() { ///////LOOP
if (digitalRead(shutswitch) == 1)
{
LocalCommandToOpen = 1;
}
else
{
LocalCommandToOpen = 0;
}
if ( digitalRead(openswitch) == 1)
{
LocalCommandToShut = 1;
}
else
{
LocalCommandToShut = 0;
}
unsigned int CloseInterval = 14000;
elapsedMillis timeElapsedSinceCloseButtonPush = 0;
unsigned int OpenInterval = 14000;
elapsedMillis timeElapsedSinceOpenButtonPush = 0;
//MANUAL SWITCH OPERATION
if ( LocalCommandToShut == 1 )
{
digitalWrite(RELAY1_TURNS_ON_BLINDS, RELAY_OFF);
digitalWrite(RELAY2_SHUTS_DOWN_BLINDS, RELAY_ON);
}
else
{
digitalWrite(RELAY2_SHUTS_DOWN_BLINDS, RELAY_OFF);
}
//MANUEL DUGME ILE ACMA
if ( LocalCommandToOpen == 1)
{
digitalWrite(RELAY2_SHUTS_DOWN_BLINDS, RELAY_OFF);
digitalWrite(RELAY1_TURNS_ON_BLINDS, RELAY_ON);
}
else
{
digitalWrite(RELAY1_TURNS_ON_BLINDS, RELAY_OFF);
}
delay(500);
} /////////////////LOOP////////////////////////////////////
One suggestion here is to use a "state machine", so that upon a switch transition, you get 'State 1'; in that state, a timer starts, and puts you in 'State 2'. In 'State 2', you check the time, and if it goes beyond X seconds, you go to 'State 3'. You can monitor the transition of a switch from low to high (or high to low), and use this to reset the state of the system.
A sample bit of code gives you an idea of how to implement this. The variable 'SystemState' is an integer, and SYSTEM_ABORT, SYSTEM_IDLE, etc. are constants.
The beauty of this is that the transition is easy to figure out. For example, if you are in the SYSTEM_WAIT state, the only thing you are looking for is the time to be greater than 5 seconds. Also, you can look at input transitions or values to set states (like SystemStopButton == 0, to set the state to be 'SYSTEM_ABORT').
// SystemStopButton is an input
void SystemStateMachine(void)
///////////////////////////////////////////////////////////////////////////////////////////
{
if (SystemStopButton == 0)
{
SystemState = SYSTEM_ABORT;
}
switch (SystemState)
{
case SYSTEM_IDLE:
{
RunPinState = OFF;
StopPinState = OFF;
if (SystemRunButton == 0)
{
SystemState = SYSTEM_START;
ShowStep();
}
break;
}
case SYSTEM_START:
{
StandardMessage = "START ";
RunPinState = ON;
StopPinState = OFF;
SystemState = SYSTEM_WAIT;
ShowStep();
break;
}
case SYSTEM_WAIT: // wait for 5 seconds
{
StandardMessage = "WAIT ";
if ((CurrentMillis - StateStepTimeMillis) > 5000)
{
SystemState = SYSTEM_RETRACT;
ShowStep();
}
break;
}
case SYSTEM_RETRACT: //
{
StandardMessage = "RETRACT";
/* motor stuff goes here... */
if ((CurrentMillis - StateStepTimeMillis) > 5000)
{
SystemState = SYSTEM_ADVANCE_TAPE_WAIT
ShowStep();
}
break;
}
// etc. etc. etc.
case SYSTEM_ABORT: //
{
StandardMessage = "ABORT";
/* motor stop stuff goes here... */
SystemState=SYSTEM_IDLE;
break;
}
default:
{
break;
}
}
}
void ShowStep(void)
/////////////////////////////////////////////////////////////////////
// show step and set time so we can keep track of time in each step
{
Serial.print("SystemState = ");
Serial.println(String(SystemState));
SetStepTime();
}
void SetStepTime(void)
/////////////////////////////////////////////////////////////////////
{
StateStepTimeMillis = CurrentMillis;
}
You might use a state machine; this makes things a bit easier to follow.
Similar to:
Arduino Switch to Turn a Relay timer
A nice discussion of state machines is here:
Complicated state transitions: best practices

Arduino Motor Ramp

I need some help with my arduino ramp motor code.
I am making a remote control robot. The remote has one button.
If the button is high: The motor should gradually increase its speed using pwm (Starting with pwm=0).The time period from standstill speed to maximum speed should be 1 second. Once it has reached maximum speed it should maintain the maximum speed (pwm=255).
The moment the button is released the motor should gradually decrease from the current speed to a complete standstill.
So far I have managed to write the ramp up and maintain speed part of the code; the code doesn't include the button part.
int motor;
int motorpwm=11;
int x=1;
int i;
void setup()
{
pinMode(11,OUTPUT);
Serial.begin(9600);
}
void loop()
{
for(int i=0;i<256;i=i+x)
{
analogWrite(motorpwm,i);
Serial.println(i);
if(i==255)
{
x=0;
Serial.println("PWM is maximum");
}
}
}
Please include the complete code including the button control part of the code.
I do not assure you if this is bug-free but you can try this and post your restults here, so that I can debug it for you. Here you go:
int motor;
int motorpwm = 11;
int x = 1;
int i;
int pwmValue;
void setup()
{
pinMode(11, OUTPUT);
pinMode(2, INPUT); //Button connected to Vcc and pulled down using a 10k resistor.
Serial.begin(9600);
}
void loop()
{
if (digitalRead(2)) //if button is pressed it will read High signal
{
if (pwmValue <= 255)
{
analogWrite(motorpwm, pwmValue++);
Serial.print("Going Up with value: ");
Serial.println(pwmValue);
}
else
{
Serial.println("PWM is maximum");
}
}
else
{
if (motorpwm > 0)
{
analogWrite(motorpwm, pwmValue--);
Serial.print("Going down with value: ");
Serial.print(pwmValue);
}
else
{
Serial.println("PWM is minimum");
}
}
}

Set a switch on until a flag is reached and then set the switch to off

What would be the best and easiest(simplest) way to do this..?
I am writing code in C or Capel (Vector CANoe)
I have a switch on a panel which I have designed in Vector CANoe , turns a motor on and off (0 == off and 1 == on) and I want to turn the motor off when it reaches its limit .. to stop the motor burning out ! (we know it has reached its limit when the a sensor says that the limit is reached (sensor = 1 for limit reached and 0 for not reached)
How would I do this?
i want to do something like this
WHILE (clutchMotorPower==1 & sensorClutchDisengaged !=1)
break;
else clutchMotorPower==0
WHILE (clutchMotorPower==1 & sensorClutchEngaged !=1)
break;
else clutchMotorPower==0
I don't know much about the system you're working on, so this is going to be some bare-bones code. Anyway, let's go through this step-by-step.
Assumptions:
button is a variable that is 1 if the button is currently pressed and 0 otherwise.
motor is a variable that we set to 1 to turn the motor on, or 0 to turn it off.
sensor is a variable that is 1 when the sensor is activated, or 0 otherwise
First, we need code to toggle the motor state when the button is pressed. (Assume all code samples are inside a function called from your main loop)
//values of static variables are preserved between function calls
static char bActivateMotor = 0; //1 if we should activate motor, 0 otherwise
static char bButtonHeld = 0; //1 if button was pressed last loop
//(to differentiate between button being held and it being released and pressed again)
if(!button) {
bButtonHeld = 0; //if button is not being pressed, it can't be being held down.
}
if(!bActivateMotor) { //we aren't running the motor
if(button && !bButtonHeld) { //button was pressed this loop (not held from previous loop)
bButtonHeld = 1; //Don't toggle motor state next loop just because button was held down
bActivateMotor = 1; //we should be running the motor
}
else {
motor = 0;
}
}
if(bActivateMotor) { //not else because could be activated this loop
if(button && !bButtonHeld) { //button toggles motor; if we're running, stop.
bButtonHeld = 1;
bActivateMotor = 0;
}
else {
motor = 1;
}
}
Now, the next part is to stop the motor when the sensor has activated:
//values of static variables are preserved between function calls
static char bActivateMotor = 0; //1 if we should activate motor, 0 otherwise
static char bButtonHeld = 0; //1 if button was pressed last loop
//(to differentiate between button being held and it being released and pressed again)
if(!button) {
bButtonHeld = 0; //if button is not being pressed, it can't be being held down.
}
if(!bActivateMotor) { //we aren't running the motor
if(button && !bButtonHeld) { //button was pressed this loop (not held from previous loop)
bButtonHeld = 1; //Don't toggle motor state next loop just because button was held down
bActivateMotor = 1; //we should be running the motor
}
else {
motor = 0;
}
}
if(bActivateMotor) { //not else because could be activated this loop
if(button && !bButtonHeld) { //button toggles motor; if we're running, stop.
bButtonHeld = 1;
bActivateMotor = 0;
}
/////////////////////
// Added Code Here //
/////////////////////
else if(sensor) {
bActivateMotor = 0; //motor will shut off next loop
}
else {
motor = 1;
}
}
With the lack of specifics about your code, it's hard to figure out exactly what difficulties you might be having, but hopefully the algorithm above will be a good starting point.

Resources