lantzvillian
Posts: 15
Joined: Sat Nov 08, 2014 5:39 pm
Contact: Website

Button Detection Using WiringPi in C

Mon Jan 19, 2015 8:07 pm

Hi all,

I am having some difficulties detecting button presses, sometimes they are detected if held long enough, and other times not at all. I am using regular buttons, but not those on the ADAfruit LCD backpack.

Maybe you all have some inputs:

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <ctype.h>

#include <wiringPi.h>
#include <mcp23017.h>
#include <lcd.h>
#include "main.h"

#define DELAY_PERIOD 10

// Commands to retrieve information from the sensors
static const char *AM2302_CMD = "/usr/bin/dht22_interface";
static const char *DSPROBE_CMD = "cat /sys/devices/w1_bus_master1/28-*/w1_slave | sed -n 2p";

// Pins used (wiringPi numbers)
const int RELAY_PIN = 0;
const int UP_TMP_PIN = 6;
const int DN_TMP_PIN = 13;
const int MODE_PIN = 2;
const int BEEPER_PIN = 12;

// Default temperature to be used as a setpoint
static float set_point;

// Device mode {RUNNING, NOT_RUNNING}
static int device_mode;

// LCD handle
static int lcdHandle;

/**
 * usage(const char *progName)
 * @brief program usage
 * @param progName
 * @return -1 for failure
 */
int usage(const char *progName)
{
	fprintf(stderr, "Usage: %s colour (0-7 where 0 is no backlight)\n", progName);
	return EXIT_FAILURE;
}

/**
 * print_banner()
 * @brief Prints app banner
 */
static inline void print_banner()
{
	printf("\nMaplePI GPIO/Monitoring App\n\n");
	printf("Author: Ronnie brash (ron.brash@gmail.com)\n");
	printf("------------------------------------------\n");
}

/**
 * setBacklightColour(int colour)
 * @brief The colour outputs are inverted.
 * @param color
 */
static inline void setBacklightColour(int colour)
{
	colour &= 7;

	digitalWrite(AF_RED, !(colour & 1));
	digitalWrite(AF_GREEN, !(colour & 2));
	digitalWrite(AF_BLUE, !(colour & 4));
}

/**
 * adafruitLCDSetup(int colour)
 * @param color
 * @brief Setup the Adafruit board by making sure the additional pins are
 *	set to the correct modes, etc.
 */
static void adafruitLCDSetup(int colour)
{
	int i = 0;
	lcdHandle = 0;

	// Backlight LEDs
	pinMode(AF_RED, OUTPUT);
	pinMode(AF_GREEN, OUTPUT);
	pinMode(AF_BLUE, OUTPUT);
	setBacklightColour(colour);

	// Input buttons
	for (i = 0; i <= 4; ++i) {
		pinMode(AF_BASE + i, INPUT);
		pullUpDnControl(AF_BASE + i, PUD_UP);	// Enable pull-ups, switches close to 0v
	}

	// Control signals
	pinMode(AF_RW, OUTPUT);
	digitalWrite(AF_RW, LOW);	// Not used with wiringPi - always in write mode

	// The other control pins are initialised with lcdInit ()
	lcdHandle = lcdInit(2, SIZE_OF_LCD, 4, AF_RS, AF_E, AF_DB4, AF_DB5, AF_DB6, AF_DB7, 0, 0, 0, 0);

	if (lcdHandle < 0) {
		fprintf(stderr, "lcdInit failed\n");
		exit(EXIT_FAILURE);
	}
}

/**
 * mode_button()
 * @brief Handles mode button functionality
 */
static inline void mode_button(button_s * state)
{

	if ((digitalRead(MODE_PIN) == LOW)) {	// Wait for release

		if (device_mode == RUNNING) {
			setBacklightColour(RED_COLOR);
			printf("Mode changed to NOT_RUNNING!\n");
			device_mode = NOT_RUNNING;
		} else {
			printf("Mode changed to RUNNING!\n");
			setBacklightColour(GREEN_COLOR);
			device_mode = RUNNING;
		}

		state->waitForRelease = TRUE;
	}

}

/**
 * up_temp_button()
 * @brief Handles tmp up button functionality
 */
static void up_temp_button(button_s * state)
{
	if ((digitalRead(UP_TMP_PIN) == LOW)) {
		printf("Up button pressed\n");
		set_point += 0.5;
		state->waitForRelease = TRUE;

	}
}

/**
 * down_temp_button()
 * @brief Handles tmp down button functionality
 */
static void down_temp_button(button_s * state)
{
	if ((digitalRead(DN_TMP_PIN) == LOW)) {
		printf("Down button pressed\n");
		set_point -= 0.5;
		state->waitForRelease = TRUE;
	}
}

/**
 * open_relay()
 * @brief Opens relay at RELAY_PIN
 */
static void open_relay()
{
	printf("\topened relay\n");
	digitalWrite(RELAY_PIN, HIGH);
}

/**
 * close_relay()
 * @brief Closes relay at RELAY_PIN
 */
static void close_relay()
{
	printf("\tclosed relay\n");
	digitalWrite(RELAY_PIN, LOW);
}

/**
 * manage_relay(float actual_temp)
 * @brief manages relay
 * @param actual_temp
 */
static void manage_relay(float actual_temp)
{

	if (actual_temp >= set_point) {
		open_relay();
	} else {
		close_relay();
	}
}

/**
 * setup_buttons()
 * @brief Setup buttons to be used by the external 
 * user interface - to be manipulated by the user
 */
static void setup_buttons()
{
	/*
	 * For each button:
	 * 1.) Set button as INPUT
	 * 2.) Enable pull-up resistor on button
	 */
	pinMode(UP_TMP_PIN, INPUT);
	pullUpDnControl(UP_TMP_PIN, PUD_UP);

	pinMode(DN_TMP_PIN, INPUT);
	pullUpDnControl(DN_TMP_PIN, PUD_UP);

	pinMode(MODE_PIN, INPUT);
	pullUpDnControl(MODE_PIN, PUD_UP);

	pinMode(RELAY_PIN, OUTPUT);

}

/**
 * get_am2302_data(char *top_buffer)
 * @brief Retrieves DS data
 * @param top_buffer
 */
static void get_am2302_data(char *top_buffer[])
{
	// Retrieve out the AM2302 sensor data
	FILE *am_file = NULL;
	am_file = popen(AM2302_CMD, "r");

	fgets(top_buffer, SIZE_OF_LCD, am_file);
	pclose(am_file);

	//printf("AM2302  output:%s\n", top_buffer);
}

/**
 * get_ds_data(float *actual_temp)
 * @brief Retrieves DS data
 * @param actual_temp
 */
static void get_ds_data(float *actual_temp)
{
	// Retrieve out DS probe sensor data
	char probe_buffer[1024] = { 0 };
	int len = 0;
	int i = 0;

	FILE *probe_file = NULL;
	probe_file = popen(DSPROBE_CMD, "r");
	fgets(probe_buffer, 1024, probe_file);
	pclose(probe_file);

	//printf("DS18B20 output:%s\n", probe_buffer);

	// Raw buffer will be a string - read until a '='
	len = strlen(probe_buffer);
	for (i = len; i > 0; i--) {
		if ((probe_buffer[i] == '=')) {
			break;
		}
	}

	// Convert the temperature string to a float
	char tmp_buffer[8] = { 0 };
	char *buff_ptr = (char *)&probe_buffer;

	// Increment buffer to remove the '=' from the string
	buff_ptr += 1;
	memcpy(tmp_buffer, buff_ptr + i, (len - i));

	// Convert string to a float and divide by 1000
	(*actual_temp) = (1.8 * (atof(tmp_buffer) / 1000)) + 32;
}

/**
 *  build_bottom_string( char *bottom_buffer, float actual_temp)
 *  @brief Builds bottom string to be displayed on LCD
 *  @param bottom_buffer
 *  @param actual_temp
 */
static inline void build_bottom_string(char *bottom_buffer[], float actual_temp)
{

	snprintf(bottom_buffer, SIZE_OF_LCD, "%3.2f-%3.2f ", actual_temp, set_point);

	if (device_mode == RUNNING) {
		memcpy(((char *)&bottom_buffer)+12, "Oui",3);
	} else {
		memcpy(((char *)&bottom_buffer)+13, "No",2);
	}

}

/**
 * check_time(time_t old_time)
 * @brief Checks for time
 */
static int check_time(time_t * old_time)
{

	// If this is the first time running, time will be 0;
	if (old_time == 0) {
		return (1);
	}

	time_t cur_time = time(0);

	// Compare if difference in time is greater than X seconds
	if ((difftime(cur_time, (time_t) old_time)) > 2) {
		(*old_time) = cur_time;
		return (1);
	}

	return (0);
}

/**
 * main(int argc, char *argv[])
 * @brief main function
 * @param argc
 * @param argv
 * @return 0 for success, -1 for error
 */
int main(int argc, char *argv[])
{
	button_s b_state = { 0 };
	b_state.colour = RED_COLOR;
	b_state.waitForRelease = FALSE;
	set_point = DEFAULT_SETPOINT;
	float actual_temp = 0;
	time_t cur_time = 0;

	char top_buffer[SIZE_OF_LCD] = { 0 };
	char bottom_buffer[SIZE_OF_LCD] = { 0 };	//"123.45-123.45 {Ou,No}"

	print_banner();

	// Check arguments
	if (argc != 2) {
		return usage(argv[0]);
	}

	b_state.colour = atoi(argv[1]);

	wiringPiSetup();

	// Setup LCD
	mcp23017Setup(AF_BASE, 0x20);
	adafruitLCDSetup(b_state.colour);

	// Set device mode to not running
	device_mode = NOT_RUNNING;

	// Initialize the user interface buttons (not on plate)
	setup_buttons();

	for (;;) {

		// Open/Close Relay
		if (device_mode == RUNNING) {
			manage_relay(actual_temp);
		}

		/*
		 * Check time to reduce system calls which are polling
		 * the sensors - set to half of a second for default
		 */
		if (check_time(&cur_time) > 0) {
			// Retrieve sensor data through pipes
			get_am2302_data(&top_buffer);
			get_ds_data(&actual_temp);
			
			// Output AM2302 data onto the top row of the LCD
			lcdPosition(lcdHandle, 0, 0);
			lcdPuts(lcdHandle, top_buffer);

		}

		// Build the final string (obviously not efficient)
		build_bottom_string(&bottom_buffer, actual_temp);

		// Output DS data & set_point onto the bottom row of the LCD
		lcdPosition(lcdHandle, 0, 1);
		lcdPuts(lcdHandle, bottom_buffer);
	
		/* 
		 * Check buttons to change:
		 * 1.) temperature for set point (up or down)
		 * 2.) Mode of the device (Running or NOT_RUNNING)
		 * If buttons are still pushed then skip
		 */
		if (b_state.waitForRelease) {
			if ((digitalRead(UP_TMP_PIN) == LOW) || (digitalRead(DN_TMP_PIN) == LOW)
			    || (digitalRead(MODE_PIN) == LOW)) {
				continue;
			} else {
				b_state.waitForRelease = FALSE;
			}
		
		// Check GPIO/buttons for input
		mode_button(&b_state);
		up_temp_button(&b_state);
		down_temp_button(&b_state);
		

	}

	return 0;
}


Quadcube
Posts: 14
Joined: Fri Nov 30, 2012 6:03 am

Re: Button Detection Using WiringPi in C

Thu Jan 22, 2015 8:19 am

did u debounce the button either with software or hardware(capacitor parallel to button)?

User avatar
SpazzTechTom
Posts: 15
Joined: Sat Jan 31, 2015 12:50 am
Location: Glen Rock, PA. USA
Contact: Website

Re: Button Detection Using WiringPi in C

Sat Jan 31, 2015 10:11 pm

I didn't look through the entire code, but it seems like you are using a polling method to detect when a button is pressed. This can be unreliable unless you have very little else happening in the same loop of the program. It appears to me like it could be that you just have too much else happening in your forever loop. The program is only checking for a button press for a small percentage of the time. Triggering an interrupt with the button press might be a better approach.

The comment about the capacitor is a good one as well. There will be some ripple in the signal when the button is pressed. There is a good thread on capacitors over on the robotics projects area.
SpazzTechTom
Having fun with technology!

Return to “C/C++”