Use mysensors built-in nrf24 library for RPi
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Marcelo Aquino <marceloaqno@gmail.org>
|
||||
* Copyright (C) 2016 Marcelo Aquino
|
||||
* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
|
||||
*
|
||||
* Documentation: http://www.mysensors.org
|
||||
* Support Forum: http://forum.mysensors.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* Based on wiringPi Copyright (c) 2012 Gordon Henderson.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stropts.h>
|
||||
#include <errno.h>
|
||||
#include "rpi_util.h"
|
||||
#include "SPI.h"
|
||||
#include "log.h"
|
||||
|
||||
extern "C" {
|
||||
int piHiPri(const int pri);
|
||||
}
|
||||
|
||||
struct ThreadArgs {
|
||||
void (*func)();
|
||||
int gpioPin;
|
||||
};
|
||||
|
||||
static pthread_mutex_t intMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static pthread_t *threadIds[64] = {NULL};
|
||||
|
||||
// sysFds:
|
||||
// Map a file descriptor from the /sys/class/gpio/gpioX/value
|
||||
static int sysFds[64] =
|
||||
{
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
#ifdef __RPI_BPLUS
|
||||
static uint8_t physToGpio[64] =
|
||||
{
|
||||
255, // 0
|
||||
255, 255, // 1, 2
|
||||
2, 255,
|
||||
3, 255,
|
||||
4, 14,
|
||||
255, 15,
|
||||
17, 18,
|
||||
27, 255,
|
||||
22, 23,
|
||||
255, 24,
|
||||
10, 255,
|
||||
9, 25,
|
||||
11, 8,
|
||||
255, 7, // 25, 26
|
||||
// B+
|
||||
0, 1,
|
||||
5, 255,
|
||||
6, 12,
|
||||
13, 255,
|
||||
19, 16,
|
||||
26, 20,
|
||||
255, 21,
|
||||
// the P5 connector on the Rev 2 boards:
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
28, 29,
|
||||
30, 31,
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
};
|
||||
#else
|
||||
static uint8_t physToGpio[64] =
|
||||
{
|
||||
255, // 0
|
||||
255, 255, // 1, 2
|
||||
0, 255,
|
||||
1, 255,
|
||||
4, 14,
|
||||
255, 15,
|
||||
17, 18,
|
||||
21, 255,
|
||||
22, 23,
|
||||
255, 24,
|
||||
10, 255,
|
||||
9, 25,
|
||||
11, 8,
|
||||
255, 7, // 25, 26
|
||||
255, 255, 255, 255, 255, // ... 31
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // ... 47
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // ... 63
|
||||
};
|
||||
#endif
|
||||
|
||||
void *interruptHandler(void *args) {
|
||||
int fd, ret;
|
||||
struct pollfd polls;
|
||||
char c;
|
||||
struct ThreadArgs *arguments = (struct ThreadArgs *)args;
|
||||
int gpioPin = arguments->gpioPin;
|
||||
void (*func)() = arguments->func;
|
||||
delete arguments;
|
||||
|
||||
(void)piHiPri(55); // Only effective if we run as root
|
||||
|
||||
if ((fd = sysFds[gpioPin]) == -1) {
|
||||
mys_log(LOG_ERR, "Failed to attach interrupt for pin %d\n", gpioPin);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Setup poll structure
|
||||
polls.fd = fd;
|
||||
polls.events = POLLPRI;
|
||||
|
||||
while (1) {
|
||||
// Wait for it ...
|
||||
ret = poll(&polls, 1, -1);
|
||||
if (ret < 0) {
|
||||
mys_log(LOG_ERR, "Error waiting for interrupt: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
// Do a dummy read to clear the interrupt
|
||||
// A one character read appars to be enough.
|
||||
// Followed by a seek to reset it.
|
||||
(void)read (fd, &c, 1) ;
|
||||
lseek (fd, 0, SEEK_SET) ;
|
||||
// Call user function.
|
||||
func();
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rpi_util::pinMode(uint8_t physPin, uint8_t mode) {
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "pinMode: invalid pin: %d\n", physPin);
|
||||
return;
|
||||
}
|
||||
// Check if SPI is in use and target pin is related to SPI
|
||||
if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
|
||||
return;
|
||||
} else {
|
||||
bcm2835_gpio_fsel(gpioPin, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void rpi_util::digitalWrite(uint8_t physPin, uint8_t value) {
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "digitalWrite: invalid pin: %d\n", physPin);
|
||||
return;
|
||||
}
|
||||
// Check if SPI is in use and target pin is related to SPI
|
||||
if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
|
||||
if (value == LOW && (gpioPin == RPI_GPIO_P1_24 || gpioPin == RPI_GPIO_P1_26)) {
|
||||
SPI.chipSelect(gpioPin);
|
||||
}
|
||||
} else {
|
||||
bcm2835_gpio_write(gpioPin, value);
|
||||
// Delay to allow any change in state to be reflected in the LEVn, register bit.
|
||||
delayMicroseconds(1);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t rpi_util::digitalRead(uint8_t physPin) {
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "digitalRead: invalid pin: %d\n", physPin);
|
||||
return 0;
|
||||
}
|
||||
// Check if SPI is in use and target pin is related to SPI
|
||||
if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
|
||||
return 0;
|
||||
} else {
|
||||
return bcm2835_gpio_lev(gpioPin);
|
||||
}
|
||||
}
|
||||
|
||||
void rpi_util::attachInterrupt(uint8_t physPin, void (*func)(), uint8_t mode) {
|
||||
FILE *fd;
|
||||
char fName[40];
|
||||
char c;
|
||||
int count, i;
|
||||
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "attachInterrupt: invalid pin: %d\n", physPin);
|
||||
return;
|
||||
}
|
||||
|
||||
if (threadIds[gpioPin] == NULL) {
|
||||
threadIds[gpioPin] = new pthread_t;
|
||||
} else {
|
||||
// Cancel the existing thread for that pin
|
||||
pthread_cancel(*threadIds[gpioPin]);
|
||||
// Wait a bit
|
||||
delay(1L);
|
||||
}
|
||||
|
||||
// Export pin for interrupt
|
||||
if ((fd = fopen("/sys/class/gpio/export", "w")) == NULL) {
|
||||
mys_log(LOG_ERR, "attachInterrupt: Unable to export pin %d for interrupt: %s\n", physPin, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
fprintf(fd, "%d\n", gpioPin);
|
||||
fclose(fd);
|
||||
|
||||
// Wait a bit the system to create /sys/class/gpio/gpio<GPIO number>
|
||||
delay(1L);
|
||||
|
||||
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/direction", gpioPin) ;
|
||||
if ((fd = fopen (fName, "w")) == NULL) {
|
||||
fprintf (stderr, "attachInterrupt: Unable to open GPIO direction interface for pin %d: %s\n", physPin, strerror(errno));
|
||||
exit(1) ;
|
||||
}
|
||||
fprintf(fd, "in\n") ;
|
||||
fclose(fd) ;
|
||||
|
||||
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/edge", gpioPin) ;
|
||||
if ((fd = fopen(fName, "w")) == NULL) {
|
||||
fprintf (stderr, "attachInterrupt: Unable to open GPIO edge interface for pin %d: %s\n", physPin, strerror(errno));
|
||||
exit(1) ;
|
||||
}
|
||||
switch (mode) {
|
||||
case CHANGE: fprintf(fd, "both\n"); break;
|
||||
case FALLING: fprintf(fd, "falling\n"); break;
|
||||
case RISING: fprintf(fd, "rising\n"); break;
|
||||
case NONE: fprintf(fd, "none\n"); break;
|
||||
default:
|
||||
mys_log(LOG_ERR, "attachInterrupt: Invalid mode\n");
|
||||
fclose(fd);
|
||||
return;
|
||||
}
|
||||
fclose(fd);
|
||||
|
||||
if (sysFds[gpioPin] == -1) {
|
||||
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/value", gpioPin);
|
||||
if ((sysFds[gpioPin] = open(fName, O_RDWR)) < 0) {
|
||||
fprintf (stderr, "Error reading pin %d: %s\n", physPin, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any initial pending interrupt
|
||||
ioctl(sysFds[gpioPin], FIONREAD, &count);
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (read(sysFds[gpioPin], &c, 1) == -1) {
|
||||
mys_log(LOG_ERR, "attachInterrupt: failed to read pin status: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
struct ThreadArgs *threadArgs = new struct ThreadArgs;
|
||||
threadArgs->func = func;
|
||||
threadArgs->gpioPin = gpioPin;
|
||||
|
||||
// Create a thread passing the pin and function
|
||||
pthread_create(threadIds[gpioPin], NULL, interruptHandler, (void *)threadArgs);
|
||||
}
|
||||
|
||||
void rpi_util::detachInterrupt(uint8_t physPin) {
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "detachInterrupt: invalid pin: %d\n", physPin);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel the thread
|
||||
if (threadIds[gpioPin] != NULL) {
|
||||
pthread_cancel(*threadIds[gpioPin]);
|
||||
delete threadIds[gpioPin];
|
||||
threadIds[gpioPin] = NULL;
|
||||
}
|
||||
|
||||
// Close filehandle
|
||||
if (sysFds[gpioPin] != -1) {
|
||||
close(sysFds[gpioPin]);
|
||||
sysFds[gpioPin] = -1;
|
||||
}
|
||||
|
||||
FILE *fp = fopen("/sys/class/gpio/unexport", "w");
|
||||
if (fp == NULL) {
|
||||
mys_log(LOG_ERR, "Unable to unexport pin %d for interrupt\n", gpioPin);
|
||||
exit(1);
|
||||
}
|
||||
fprintf(fp, "%d", gpioPin);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
uint8_t rpi_util::digitalPinToInterrupt(uint8_t physPin) {
|
||||
// No need to convert the pin to gpio, we do it in attachInterrupt().
|
||||
return physPin;
|
||||
}
|
||||
|
||||
void rpi_util::interrupts() {
|
||||
pthread_mutex_unlock(&intMutex);
|
||||
}
|
||||
|
||||
void rpi_util::noInterrupts() {
|
||||
pthread_mutex_lock(&intMutex);
|
||||
}
|
||||
Reference in New Issue
Block a user