first re-commit.

This commit is contained in:
2025-08-05 22:33:23 +02:00
commit e1ff579d1a
295 changed files with 107130 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
A program showing how to use the gpioSetAlertFunc function to set a callback for gpio state changes.
A frequency count is generated for each monitored gpio (frequencies up to 500KHz and beyond).

View File

@@ -0,0 +1,238 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <pigpio.h>
/*
2014-08-20
gcc -o freq_count_1 freq_count_1.c -lpigpio -lpthread
$ sudo ./freq_count_1 4 7 8
This program uses the gpioSetAlertFunc function to request
a callback (the same one) for each gpio to be monitored.
EXAMPLES
Monitor gpio 4 (default settings)
sudo ./freq_count_1 4
Monitor gpios 4 and 8 (default settings)
sudo ./freq_count_1 4 8
Monitor gpios 4 and 8, sample rate 2 microseconds
sudo ./freq_count_1 4 8 -s2
Monitor gpios 7 and 8, sample rate 4 microseconds, report every second
sudo ./freq_count_1 7 8 -s4 -r10
Monitor gpios 4,7, 8, 9, 10, 23 24, report five times a second
sudo ./freq_count_1 4 7 8 9 10 23 24 -r2
Monitor gpios 4, 7, 8, and 9, report once a second, sample rate 1us,
generate 2us edges (4us square wave, 250000 highs per second).
sudo ./freq_count_1 4 7 8 9 -r 10 -s 1 -p 2
*/
#define MAX_GPIOS 32
#define OPT_P_MIN 1
#define OPT_P_MAX 1000
#define OPT_P_DEF 20
#define OPT_R_MIN 1
#define OPT_R_MAX 10
#define OPT_R_DEF 5
#define OPT_S_MIN 1
#define OPT_S_MAX 10
#define OPT_S_DEF 5
static volatile int g_pulse_count[MAX_GPIOS];
static volatile int g_reset_counts;
static uint32_t g_mask;
static int g_num_gpios;
static int g_gpio[MAX_GPIOS];
static int g_opt_p = OPT_P_DEF;
static int g_opt_r = OPT_R_DEF;
static int g_opt_s = OPT_S_DEF;
static int g_opt_t = 0;
void usage()
{
fprintf
(stderr,
"\n" \
"Usage: sudo ./freq_count_1 gpio ... [OPTION] ...\n" \
" -p value, sets pulses every p micros, %d-%d, TESTING only\n" \
" -r value, sets refresh period in deciseconds, %d-%d, default %d\n" \
" -s value, sets sampling rate in micros, %d-%d, default %d\n" \
"\nEXAMPLE\n" \
"sudo ./freq_count_1 4 7 -r2 -s2\n" \
"Monitor gpios 4 and 7. Refresh every 0.2 seconds. Sample rate 2 micros.\n" \
"\n",
OPT_P_MIN, OPT_P_MAX,
OPT_R_MIN, OPT_R_MAX, OPT_R_DEF,
OPT_S_MIN, OPT_S_MAX, OPT_S_DEF
);
}
void fatal(int show_usage, char *fmt, ...)
{
char buf[128];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
fprintf(stderr, "%s\n", buf);
if (show_usage) usage();
fflush(stderr);
exit(EXIT_FAILURE);
}
static int initOpts(int argc, char *argv[])
{
int i, opt;
while ((opt = getopt(argc, argv, "p:r:s:")) != -1)
{
i = -1;
switch (opt)
{
case 'p':
i = atoi(optarg);
if ((i >= OPT_P_MIN) && (i <= OPT_P_MAX))
g_opt_p = i;
else fatal(1, "invalid -p option (%d)", i);
g_opt_t = 1;
break;
case 'r':
i = atoi(optarg);
if ((i >= OPT_R_MIN) && (i <= OPT_R_MAX))
g_opt_r = i;
else fatal(1, "invalid -r option (%d)", i);
break;
case 's':
i = atoi(optarg);
if ((i >= OPT_S_MIN) && (i <= OPT_S_MAX))
g_opt_s = i;
else fatal(1, "invalid -s option (%d)", i);
break;
default: /* '?' */
usage();
exit(-1);
}
}
return optind;
}
void edges(int gpio, int level, uint32_t tick)
{
int g;
if (g_reset_counts)
{
g_reset_counts = 0;
for (g=0; g<MAX_GPIOS; g++) g_pulse_count[g] = 0;
}
/* only record low to high edges */
if (level == 1) g_pulse_count[gpio]++;
}
int main(int argc, char *argv[])
{
int i, rest, g, wave_id, mode;
gpioPulse_t pulse[2];
int count[MAX_GPIOS];
/* command line parameters */
rest = initOpts(argc, argv);
/* get the gpios to monitor */
g_num_gpios = 0;
for (i=rest; i<argc; i++)
{
g = atoi(argv[i]);
if ((g>=0) && (g<32))
{
g_gpio[g_num_gpios++] = g;
g_mask |= (1<<g);
}
else fatal(1, "%d is not a valid g_gpio number\n", g);
}
if (!g_num_gpios) fatal(1, "At least one gpio must be specified");
printf("Monitoring gpios");
for (i=0; i<g_num_gpios; i++) printf(" %d", g_gpio[i]);
printf("\nSample rate %d micros, refresh rate %d deciseconds\n",
g_opt_s, g_opt_r);
gpioCfgClock(g_opt_s, 1, 1);
if (gpioInitialise()<0) return 1;
gpioWaveClear();
pulse[0].gpioOn = g_mask;
pulse[0].gpioOff = 0;
pulse[0].usDelay = g_opt_p;
pulse[1].gpioOn = 0;
pulse[1].gpioOff = g_mask;
pulse[1].usDelay = g_opt_p;
gpioWaveAddGeneric(2, pulse);
wave_id = gpioWaveCreate();
/* monitor g_gpio level changes */
for (i=0; i<g_num_gpios; i++) gpioSetAlertFunc(g_gpio[i], edges);
mode = PI_INPUT;
if (g_opt_t)
{
gpioWaveTxSend(wave_id, PI_WAVE_MODE_REPEAT);
mode = PI_OUTPUT;
}
for (i=0; i<g_num_gpios; i++) gpioSetMode(g_gpio[i], mode);
while (1)
{
for (i=0; i<g_num_gpios; i++) count[i] = g_pulse_count[g_gpio[i]];
g_reset_counts = 1;
for (i=0; i<g_num_gpios; i++)
{
printf(" %d=%d", g_gpio[i], count[i]);
}
printf("\n");
gpioDelay(g_opt_r * 100000);
}
gpioTerminate();
}

View File

@@ -0,0 +1,6 @@
A program showing how to use the gpioSetGetSamplesFunc function to set a callback for accumulated gpio state changes over the last millisecond.
A frequency count is generated for each monitored gpio (frequencies up to 500KHz and beyond).
Generally the method used is more complicated but more efficient than frequency counter 1.

View File

@@ -0,0 +1,257 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <pigpio.h>
/*
2014-08-20
gcc -o freq_count_2 freq_count_2.c -lpigpio -lpthread
$ sudo ./freq_count_2 4 7 8
This program uses the gpioSetGetSamplesFunc function to request
a callback once a millisecond for all accumulated gpio changes
in that millisecond.
This tends to be more efficient then calling a callback for each
gpio change during the millisecond.
EXAMPLES
Monitor gpio 4 (default settings)
sudo ./freq_count_2 4
Monitor gpios 4 and 8 (default settings)
sudo ./freq_count_2 4 8
Monitor gpios 4 and 8, sample rate 2 microseconds
sudo ./freq_count_2 4 8 -s2
Monitor gpios 7 and 8, sample rate 4 microseconds, report every second
sudo ./freq_count_2 7 8 -s4 -r10
Monitor gpios 4,7, 8, 9, 10, 23 24, report five times a second
sudo ./freq_count_2 4 7 8 9 10 23 24 -r2
Monitor gpios 4, 7, 8, and 9, report once a second, sample rate 1us,
generate 2us edges (4us square wave, 250000 highs per second).
sudo ./freq_count_2 4 7 8 9 -r 10 -s 1 -p 2
*/
#define MAX_GPIOS 32
#define OPT_P_MIN 1
#define OPT_P_MAX 1000
#define OPT_P_DEF 20
#define OPT_R_MIN 1
#define OPT_R_MAX 10
#define OPT_R_DEF 5
#define OPT_S_MIN 1
#define OPT_S_MAX 10
#define OPT_S_DEF 5
static volatile int g_pulse_count[MAX_GPIOS];
static volatile int g_reset_counts;
static uint32_t g_mask;
static int g_num_gpios;
static int g_gpio[MAX_GPIOS];
static int g_opt_p = OPT_P_DEF;
static int g_opt_r = OPT_R_DEF;
static int g_opt_s = OPT_S_DEF;
static int g_opt_t = 0;
void usage()
{
fprintf
(stderr,
"\n" \
"Usage: sudo ./freq_count_2 gpio ... [OPTION] ...\n" \
" -p value, sets pulses every p micros, %d-%d, TESTING only\n" \
" -r value, sets refresh period in deciseconds, %d-%d, default %d\n" \
" -s value, sets sampling rate in micros, %d-%d, default %d\n" \
"\nEXAMPLE\n" \
"sudo ./freq_count_2 4 7 -r2 -s2\n" \
"Monitor gpios 4 and 7. Refresh every 0.2 seconds. Sample rate 2 micros.\n" \
"\n",
OPT_P_MIN, OPT_P_MAX,
OPT_R_MIN, OPT_R_MAX, OPT_R_DEF,
OPT_S_MIN, OPT_S_MAX, OPT_S_DEF
);
}
void fatal(int show_usage, char *fmt, ...)
{
char buf[128];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
fprintf(stderr, "%s\n", buf);
if (show_usage) usage();
fflush(stderr);
exit(EXIT_FAILURE);
}
static int initOpts(int argc, char *argv[])
{
int i, opt;
while ((opt = getopt(argc, argv, "p:r:s:")) != -1)
{
i = -1;
switch (opt)
{
case 'p':
i = atoi(optarg);
if ((i >= OPT_P_MIN) && (i <= OPT_P_MAX))
g_opt_p = i;
else fatal(1, "invalid -p option (%d)", i);
g_opt_t = 1;
break;
case 'r':
i = atoi(optarg);
if ((i >= OPT_R_MIN) && (i <= OPT_R_MAX))
g_opt_r = i;
else fatal(1, "invalid -r option (%d)", i);
break;
case 's':
i = atoi(optarg);
if ((i >= OPT_S_MIN) && (i <= OPT_S_MAX))
g_opt_s = i;
else fatal(1, "invalid -s option (%d)", i);
break;
default: /* '?' */
usage();
exit(-1);
}
}
return optind;
}
void samples(const gpioSample_t *samples, int numSamples)
{
static uint32_t state = 0;
uint32_t high, level;
int g, s;
if (g_reset_counts)
{
g_reset_counts = 0;
for (g=0; g<g_num_gpios; g++) g_pulse_count[g] = 0;
}
for (s=0; s<numSamples; s++)
{
level = samples[s].level;
high = ((state ^ level) & g_mask) & level;
state = level;
/* only interested in low to high */
if (high)
{
for (g=0; g<g_num_gpios; g++)
{
if (high & (1<<g_gpio[g])) g_pulse_count[g]++;
}
}
}
}
int main(int argc, char *argv[])
{
int i, rest, g, wave_id, mode;
gpioPulse_t pulse[2];
int count[MAX_GPIOS];
/* command line parameters */
rest = initOpts(argc, argv);
/* get the gpios to monitor */
g_num_gpios = 0;
for (i=rest; i<argc; i++)
{
g = atoi(argv[i]);
if ((g>=0) && (g<32))
{
g_gpio[g_num_gpios++] = g;
g_mask |= (1<<g);
}
else fatal(1, "%d is not a valid g_gpio number\n", g);
}
if (!g_num_gpios) fatal(1, "At least one gpio must be specified");
printf("Monitoring gpios");
for (i=0; i<g_num_gpios; i++) printf(" %d", g_gpio[i]);
printf("\nSample rate %d micros, refresh rate %d deciseconds\n",
g_opt_s, g_opt_r);
gpioCfgClock(g_opt_s, 1, 1);
if (gpioInitialise()<0) return 1;
gpioWaveClear();
pulse[0].gpioOn = g_mask;
pulse[0].gpioOff = 0;
pulse[0].usDelay = g_opt_p;
pulse[1].gpioOn = 0;
pulse[1].gpioOff = g_mask;
pulse[1].usDelay = g_opt_p;
gpioWaveAddGeneric(2, pulse);
wave_id = gpioWaveCreate();
/* monitor g_gpio level changes */
gpioSetGetSamplesFunc(samples, g_mask);
mode = PI_INPUT;
if (g_opt_t)
{
gpioWaveTxSend(wave_id, PI_WAVE_MODE_REPEAT);
mode = PI_OUTPUT;
}
for (i=0; i<g_num_gpios; i++) gpioSetMode(g_gpio[i], mode);
while (1)
{
for (i=0; i<g_num_gpios; i++) count[i] = g_pulse_count[i];
g_reset_counts = 1;
for (i=0; i<g_num_gpios; i++)
{
printf(" %d=%d", g_gpio[i], count[i]);
}
printf("\n");
gpioDelay(g_opt_r * 100000);
}
gpioTerminate();
}

View File

@@ -0,0 +1,2 @@
Program to show status changes for a Hall effect sensor.

View File

@@ -0,0 +1,51 @@
#include <stdio.h>
#include <pigpio.h>
/*
OH3144E or equivalent Hall effect sensor
Pin 1 - 5V
Pin 2 - Ground
Pin 3 - gpio (here P1-8, gpio 14, TXD is used)
The internal gpio pull-up is enabled so that the sensor
normally reads high. It reads low when a magnet is close.
gcc -o hall hall.c -lpigpio -lrt -lpthread
sudo ./hall
*/
#define HALL 14
void alert(int gpio, int level, uint32_t tick)
{
static uint32_t lastTick=0;
if (lastTick) printf("%d %.2f\n", level, (float)(tick-lastTick)/1000000.0);
else printf("%d 0.00\n", level);
lastTick = tick;
}
int main(int argc, char *argv[])
{
int secs=60;
if (argc>1) secs = atoi(argv[1]); /* program run seconds */
if ((secs<1) || (secs>3600)) secs = 3600;
if (gpioInitialise()<0) return 1;
gpioSetMode(HALL, PI_INPUT);
gpioSetPullUpDown(HALL, PI_PUD_UP);
gpioSetAlertFunc(HALL, alert);
sleep(secs);
gpioTerminate();
}

View File

@@ -0,0 +1,4 @@
A program to passively sniff I2C transactions (100kHz bus maximum) and display the results.
This C program uses pigpio notifications.

View File

@@ -0,0 +1,205 @@
/*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include "pigpio.h"
/*
This software reads pigpio notification reports monitoring the I2C signals.
Notifications are pipe based so this software must be run on the Pi
being monitored.
It should be able to handle a 100kHz bus. You are unlikely to get any
usable results if the bus is running at 400kHz.
gcc -o pig2i2c pig2i2c.c
Do something like
sudo pigpiod -s 2
# get a notification handle, assume handle 0 was returned
pigs no
# start notifications for SCL/SDA
e.g. pigs nb 0 0x3 # Rev. 1 select gpios 0/1
e.g. pigs nb 0 0xC # Rev. 2 select gpios 2/3
e.g. pigs nb 0 0xA00 # select gpios 9/11 (1<<9|1<<11)
# run the program, specifying SCL/SDA and notification pipe
./pig2i2c SCL SDA </dev/pigpioN # specify gpios for SCL/SDA and pipe N
e.g. ./pig2i2c 1 0 </dev/pigpio0 # Rev.1 I2C gpios
e.g. ./pig2i2c 3 2 </dev/pigpio0 # Rev.2 I2C gpios
e.g. ./pig2i2c 9 11 </dev/pigpio0 # monitor external bus
*/
#define RS (sizeof(gpioReport_t))
#define SCL_FALLING 0
#define SCL_RISING 1
#define SCL_STEADY 2
#define SDA_FALLING 0
#define SDA_RISING 4
#define SDA_STEADY 8
static char * timeStamp()
{
static char buf[32];
struct timeval now;
struct tm tmp;
gettimeofday(&now, NULL);
localtime_r(&now.tv_sec, &tmp);
strftime(buf, sizeof(buf), "%F %T", &tmp);
return buf;
}
void parse_I2C(int SCL, int SDA)
{
static int in_data=0, byte=0, bit=0;
static int oldSCL=1, oldSDA=1;
int xSCL, xSDA;
if (SCL != oldSCL)
{
oldSCL = SCL;
if (SCL) xSCL = SCL_RISING;
else xSCL = SCL_FALLING;
}
else xSCL = SCL_STEADY;
if (SDA != oldSDA)
{
oldSDA = SDA;
if (SDA) xSDA = SDA_RISING;
else xSDA = SDA_FALLING;
}
else xSDA = SDA_STEADY;
switch (xSCL+xSDA)
{
case SCL_RISING + SDA_RISING:
case SCL_RISING + SDA_FALLING:
case SCL_RISING + SDA_STEADY:
if (in_data)
{
if (bit++ < 8)
{
byte <<= 1;
byte |= SDA;
}
else
{
printf("%02X", byte);
if (SDA) printf("-"); else printf("+");
bit = 0;
byte = 0;
}
}
break;
case SCL_FALLING + SDA_RISING:
break;
case SCL_FALLING + SDA_FALLING:
break;
case SCL_FALLING + SDA_STEADY:
break;
case SCL_STEADY + SDA_RISING:
if (SCL)
{
in_data = 0;
byte = 0;
bit = 0;
printf("]\n"); // stop
fflush(NULL);
}
break;
case SCL_STEADY + SDA_FALLING:
if (SCL)
{
in_data = 1;
byte = 0;
bit = 0;
printf("["); // start
}
break;
case SCL_STEADY + SDA_STEADY:
break;
}
}
int main(int argc, char * argv[])
{
int gSCL, gSDA, SCL, SDA, xSCL;
int r;
uint32_t level, changed, bI2C, bSCL, bSDA;
gpioReport_t report;
if (argc > 2)
{
gSCL = atoi(argv[1]);
gSDA = atoi(argv[2]);
bSCL = 1<<gSCL;
bSDA = 1<<gSDA;
bI2C = bSCL | bSDA;
}
else
{
exit(-1);
}
/* default to SCL/SDA high */
SCL = 1;
SDA = 1;
level = bI2C;
while ((r=read(STDIN_FILENO, &report, RS)) == RS)
{
report.level &= bI2C;
if (report.level != level)
{
changed = report.level ^ level;
level = report.level;
if (level & bSCL) SCL = 1; else SCL = 0;
if (level & bSDA) SDA = 1; else SDA = 0;
parse_I2C(SCL, SDA);
}
}
return 0;
}

View File

@@ -0,0 +1,3 @@
Function to hash a code from an IR receiver (reading an IR remote control).
Follow the instructions in the test file to build and run.

View File

@@ -0,0 +1,136 @@
#include <stdlib.h>
#include <pigpio.h>
#include "ir_hasher.h"
/*
This code forms a hash over the IR pulses generated by an
IR remote.
The remote key press is not converted into a code in the manner of
the lirc module. No attempt is made to decode the type of protocol
used by the remote. The hash is likely to be unique for different
keys and different remotes but this is not guaranteed.
This hashing process works for some remotes/protocols but not for
others. The only way to find out if it works for one or more of
your remotes is to try it and see.
*/
struct _Pi_Hasher_s
{
int gpio;
Pi_Hasher_CB_t callback;
int timeout;
int in_code;
uint32_t hash_val;
int edges;
int t1;
int t2;
int t3;
int t4;
};
static uint32_t _hash(uint32_t hv, int old_val, int new_val)
{
int val;
if (new_val < (old_val * 0.60)) val = 13;
else if (old_val < (new_val * 0.60)) val = 23;
else val = 2;
hv ^= val;
hv *= 16777619; /* FNV_PRIME_32 */
return hv;
}
static void _cb(int gpio, int level, uint32_t tick, void *user)
{
Pi_Hasher_t * hasher;
hasher = user;
if (level != PI_TIMEOUT)
{
if (hasher->in_code == 0)
{
hasher->in_code = 1;
gpioSetWatchdog(gpio, hasher->timeout);
hasher->hash_val = 2166136261U; /* FNV_BASIS_32 */
hasher->edges = 1;
hasher->t1 = 0;
hasher->t2 = 0;
hasher->t3 = 0;
hasher->t4 = tick;
}
else
{
hasher->edges++;
hasher->t1 = hasher->t2;
hasher->t2 = hasher->t3;
hasher->t3 = hasher->t4;
hasher->t4 = tick;
if (hasher->edges > 3)
{
hasher->hash_val =
_hash(hasher->hash_val,
(hasher->t2)-(hasher->t1),
(hasher->t4)-(hasher->t3));
}
}
}
else
{
if (hasher->in_code)
{
hasher->in_code = 0;
gpioSetWatchdog(gpio, 0);
if (hasher->edges > 12) /* Anything less is probably noise. */
{
(hasher->callback)(hasher->hash_val);
}
}
}
}
Pi_Hasher_t *Pi_Hasher(int gpio, Pi_Hasher_CB_t callback, int timeout)
{
Pi_Hasher_t *hasher;
hasher = malloc(sizeof(Pi_Hasher_t));
hasher->gpio = gpio;
hasher->callback = callback;
hasher->timeout = 5;
hasher->in_code = 0;
gpioSetMode(gpio, PI_INPUT);
gpioSetAlertFuncEx(gpio, _cb, hasher);
return hasher;
}
void Pi_Hasher_cancel(Pi_Hasher_t *hasher)
{
if (hasher)
{
gpioSetAlertFunc(hasher->gpio, 0);
free(hasher);
hasher = NULL;
}
}

View File

@@ -0,0 +1,33 @@
#ifndef IR_HASHER_H
#define IR_HASHER_H
#include <stdint.h>
typedef void (*Pi_Hasher_CB_t)(uint32_t);
struct _Pi_Hasher_s;
typedef struct _Pi_Hasher_s Pi_Hasher_t;
Pi_Hasher_t * Pi_Hasher(int gpio, Pi_Hasher_CB_t callback, int timeout);
/*
This function establishes an IR hasher on the gpio.
A gap of timeout milliseconds without a new bit indicates
the end of a code.
When code end is detected the callback function is called
with the code hash.
A pointer to a private data type is returned. This should be passed
to Pi_Hasher_cancel if the hasher is to be cancelled.
*/
void Pi_Hasher_cancel(Pi_Hasher_t *hasher);
/*
This function releases the resources used by the hasher.
*/
#endif

View File

@@ -0,0 +1,47 @@
#include <stdio.h>
#include <pigpio.h>
#include "ir_hasher.h"
/*
REQUIRES
An IR receiver output pin connected to a Pi gpio.
TO BUILD
gcc -o ir_hash_c test_ir_hasher.c ir_hasher.c -lpigpio -lrt -lpthread
TO RUN
sudo ./ir_hash_c
*/
void callback(uint32_t hash)
{
printf("hash=%u\n", hash);
}
int main(int argc, char *argv[])
{
Pi_Hasher_t *hasher;
if (gpioInitialise() < 0) return 1;
/*
This assumes the output pin of an IR receiver is
connected to gpio 7.
*/
hasher = Pi_Hasher(7, callback, 5);
sleep(300);
Pi_Hasher_cancel(hasher);
gpioTerminate();
}

View File

@@ -0,0 +1,131 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <ncurses.h> /* libncurses5-dev */
/*
2014-08-26 PCF8591.c
sudo apt-get install libncurses5-dev
gcc -o PCF8591 PCF8591.c -lcurses -lpigpio -lpthread
sudo ./PCF8591
*/
/*
Connect Pi 3V3 - VCC, Ground - Ground, SDA - SDA, SCL - SCL.
*/
#define PCF8591_I2C_ADDR 0x48
/*
P4 The thermister voltage is provided at AIN 1.
P5 The photocell voltage is provided at AIN 0.
P6 The single turn 10K ohm trim pot voltage is provided at AIN 3.
*/
/*
7 6 5 4 3 2 1 0
0 X X X 0 X X X
| | | | | |
A B B C D D
0 1 0 0 0 1 0 0
A 0 D/A inactive
1 D/A active
B 00 single ended inputs
01 differential inputs
10 single ended and differential
11 two differential inputs
C 0 no auto inc
1 auto inc
D 00 select channel 0
01 select channel 1
10 select channel 2
11 select channel 3
*/
int main(int argc, char *argv[])
{
int i;
int r;
int handle;
char aout;
unsigned char command[2];
unsigned char value[4];
unsigned char str[8];
int j;
int key;
if (gpioInitialise() < 0) return 1;
initscr();
noecho();
cbreak();
nodelay(stdscr, true);
curs_set(0);
printw("PCF8591 + or - to change aout, any other key to quit.");
mvaddstr(10, 0, "Brightness");
mvaddstr(12, 0, "Temperature");
mvaddstr(14, 0, "?");
mvaddstr(16, 0, "Resistor");
refresh();
handle = i2cOpen(1, PCF8591_I2C_ADDR, 0);
command[1] = 0;
aout = 128;
while (1)
{
for (i=0; i<4; i++)
{
command[1] = aout;
command[0] = 0x40 | ((i + 1) & 0x03); // output enable | read input i
i2cWriteDevice(handle, &command, 2);
usleep(20000);
// the read is always one step behind the selected input
value[i] = i2cReadByte(handle);
sprintf(str, "%3d", value[i]);
mvaddstr(10+i+i, 12, str);
value[i] = value[i] / 4;
move(10 + i + i, 16);
for(j = 0; j < 64; j++)
if(j < value[i]) addch('*'); else addch(' ');
}
refresh();
key = getch();
if ((key == '+') || (key == '=')) aout++;
else if ((key == '-') || (key == '_')) aout--;
else if (key != -1) break;
}
endwin();
i2cClose(handle);
gpioTerminate();
return (0);
}

View File

@@ -0,0 +1 @@
A program to display readings from the (I2C) PCF8591.

View File

@@ -0,0 +1,3 @@
Function to time capacitor charging (through a resistance).
The time can be used to estimate the resistance.

View File

@@ -0,0 +1,94 @@
#include <stdio.h>
#include <pigpio.h>
/*
Measure how long a capacitor takes to charge through a resistance.
A potentimeter is used to vary the resistance.
The time taken will be proportional to the resistance.
3V3 ----- Potentiometer --+-- Capacitor ----- Ground
|
+-- gpio
gcc -o pot_cap_charge pot_cap_charge.c -lpigpio -lpthread -lrt
sudo ./pot_cap_charge
*/
#define GPIO 25
#define MAX_READING 1000
static uint32_t rechargeTick = 0;
void callback(int gpio, int level, uint32_t tick)
{
static uint32_t smooth = 0;
static int reading = 0;
uint32_t raw;
if (level == 1) /* measure recharge time */
{
++reading;
if (rechargeTick)
{
raw = tick - rechargeTick; /* set in main */
if (raw < MAX_READING) /* ignore outliers */
{
/* smooth using 0.8 * smooth + 0.2 * raw */
smooth = (raw + (4 * smooth)) / 5;
printf("%d %d %d\n", reading, raw, smooth);
}
else
{
/* ignore outlier, set dot at fixed position */
printf("%d %d %d\n", reading, 40, smooth);
}
}
else
{
/* ignore reschedule, set dot at fixed position */
printf("%d %d %d\n", reading, 20, smooth);
}
}
}
int main (int argc, char *argv[])
{
uint32_t t1, t2;
int tDiff;
if (gpioInitialise()<0) return 1;
gpioSetAlertFunc(GPIO, callback); /* callback when GPIO changes state */
while (1)
{
gpioWrite(GPIO, PI_OFF); /* drain capacitor */
gpioDelay(200); /* microseconds */
t1 = gpioTick();
gpioSetMode(GPIO, PI_INPUT); /* start capacitor recharge */
t2 = gpioTick();
/* dump reading if rechargeTick not accurate to 3 micros */
if ((t2 - t1) < 3) rechargeTick = t1; else rechargeTick = 0;
gpioDelay(5000); /* microseconds, nominal 200 readings per second */
}
gpioTerminate();
}

View File

@@ -0,0 +1,4 @@
Function to decode a mechanical rotary encoder.
Follow the instructions in the test file to build and run.

View File

@@ -0,0 +1,94 @@
#include <stdio.h>
#include <stdlib.h>
#include <pigpio.h>
#include "rotary_encoder.h"
struct _Pi_Renc_s
{
int gpioA;
int gpioB;
Pi_Renc_CB_t callback;
int levA;
int levB;
int lastGpio;
};
/*
+---------+ +---------+ 0
| | | |
A | | | |
| | | |
+---------+ +---------+ +----- 1
+---------+ +---------+ 0
| | | |
B | | | |
| | | |
----+ +---------+ +---------+ 1
*/
static void _cb(int gpio, int level, uint32_t tick, void *user)
{
Pi_Renc_t *renc;
renc = user;
if (gpio == renc->gpioA) renc->levA = level; else renc->levB = level;
if (gpio != renc->lastGpio) /* debounce */
{
renc->lastGpio = gpio;
if ((gpio == renc->gpioA) && (level == 1))
{
if (renc->levB) (renc->callback)(1);
}
else if ((gpio == renc->gpioB) && (level == 1))
{
if (renc->levA) (renc->callback)(-1);
}
}
}
Pi_Renc_t * Pi_Renc(int gpioA, int gpioB, Pi_Renc_CB_t callback)
{
Pi_Renc_t *renc;
renc = malloc(sizeof(Pi_Renc_t));
renc->gpioA = gpioA;
renc->gpioB = gpioB;
renc->callback = callback;
renc->levA=0;
renc->levB=0;
renc->lastGpio = -1;
gpioSetMode(gpioA, PI_INPUT);
gpioSetMode(gpioB, PI_INPUT);
/* pull up is needed as encoder common is grounded */
gpioSetPullUpDown(gpioA, PI_PUD_UP);
gpioSetPullUpDown(gpioB, PI_PUD_UP);
/* monitor encoder level changes */
gpioSetAlertFuncEx(gpioA, _cb, renc);
gpioSetAlertFuncEx(gpioB, _cb, renc);
}
void Pi_Renc_cancel(Pi_Renc_t *renc)
{
if (renc)
{
gpioSetAlertFunc(renc->gpioA, 0);
gpioSetAlertFunc(renc->gpioB, 0);
free(renc);
}
}

View File

@@ -0,0 +1,25 @@
#ifndef ROTARY_ENCODER_H
#define ROTARY_ENCODER_H
typedef void (*Pi_Renc_CB_t)(int);
struct _Pi_Renc_s;
typedef struct _Pi_Renc_s Pi_Renc_t;
Pi_Renc_t * Pi_Renc(int gpioA, int gpioB, Pi_Renc_CB_t callback);
/*
This function establishes a rotary encoder on gpioA and gpioB.
When the encoder is turned the callback function is called.
A pointer to a private data type is returned. This should be passed
to Pi_Renc_cancel if the rotary encoder is to be cancelled.
*/
void Pi_Renc_cancel(Pi_Renc_t *renc);
/*
This function releases the resources used by the decoder.
*/
#endif

View File

@@ -0,0 +1,47 @@
#include <stdio.h>
#include <pigpio.h>
#include "rotary_encoder.h"
/*
REQUIRES
A rotary encoder contacts A and B connected to separate gpios and
the common contact connected to Pi ground.
TO BUILD
gcc -o rot_enc_c test_rotary_encoder.c rotary_encoder.c -lpigpio -lrt
TO RUN
sudo ./rot_enc_c
*/
void callback(int way)
{
static int pos = 0;
pos += way;
printf("pos=%d\n", pos);
}
int main(int argc, char *argv[])
{
Pi_Renc_t * renc;
if (gpioInitialise() < 0) return 1;
renc = Pi_Renc(7, 8, callback);
sleep(300);
Pi_Renc_cancel(renc);
gpioTerminate();
}

View File

@@ -0,0 +1,4 @@
Function to decode a Wiegand code.
Follow the instructions in the test file to build and run.

View File

@@ -0,0 +1,42 @@
#include <stdio.h>
#include <pigpio.h>
#include "wiegand.h"
/*
REQUIRES
Wiegand contacts 0 and 1 connected to separate gpios.
TO BUILD
gcc -o wiegand_c test_wiegand.c wiegand.c -lpigpio -lrt
TO RUN
sudo ./wiegand_c
*/
void callback(int bits, uint32_t value)
{
printf("bits=%d value=%u\n", bits, value);
}
int main(int argc, char *argv[])
{
Pi_Wieg_t * w;
if (gpioInitialise() < 0) return 1;
w = Pi_Wieg(14, 15, callback, 5);
sleep(300);
Pi_Wieg_cancel(w);
gpioTerminate();
}

View File

@@ -0,0 +1,137 @@
#include <stdlib.h>
#include <pigpio.h>
#include "wiegand.h"
struct _Pi_Wieg_s
{
int mygpio_0;
int mygpio_1;
int mytimeout;
int in_code;
int bits;
Pi_Wieg_CB_t mycallback;
uint32_t num;
uint32_t code_timeout;
};
void _cb(int gpio, int level, uint32_t tick, void *user)
{
/*
Accumulate bits until both gpios 0 and 1 timeout.
*/
Pi_Wieg_t *wieg;
wieg = user;
if (level == 0) /* a falling edge indicates a new bit */
{
if (!wieg->in_code)
{
wieg->bits = 1;
wieg->num = 0;
wieg->in_code = 1;
wieg->code_timeout = 0;
gpioSetWatchdog(wieg->mygpio_0, wieg->mytimeout);
gpioSetWatchdog(wieg->mygpio_1, wieg->mytimeout);
}
else
{
wieg->bits++;
wieg->num <<= 1;
}
if (gpio == wieg->mygpio_0)
{
wieg->code_timeout &= 2; /* clear gpio 0 timeout */
}
else
{
wieg->code_timeout &= 1; /* clear gpio 1 timeout */
wieg->num |= 1;
}
}
else if (level == PI_TIMEOUT)
{
if (wieg->in_code)
{
if (gpio == wieg->mygpio_0)
{
wieg->code_timeout |= 1; /* timeout gpio 0 */
}
else
{
wieg->code_timeout |= 2; /* timeout gpio 1 */
}
if (wieg->code_timeout == 3) /* both gpios timed out */
{
gpioSetWatchdog(wieg->mygpio_0, 0);
gpioSetWatchdog(wieg->mygpio_1, 0);
wieg->in_code = 0;
(wieg->mycallback)(wieg->bits, wieg->num);
}
}
}
}
Pi_Wieg_t * Pi_Wieg(
int gpio_0,
int gpio_1,
Pi_Wieg_CB_t callback,
int timeout)
{
/*
Instantiate with the gpio for 0 (green wire), the gpio for 1
(white wire), the callback function, and the timeout in
milliseconds which indicates the end of a code.
The callback is passed the code length in bits and the value.
*/
Pi_Wieg_t *wieg;
wieg = malloc(sizeof(Pi_Wieg_t));
wieg->mygpio_0 = gpio_0;
wieg->mygpio_1 = gpio_1;
wieg->mycallback = callback;
wieg->mytimeout = timeout;
wieg->in_code = 0;
gpioSetMode(gpio_0, PI_INPUT);
gpioSetMode(gpio_1, PI_INPUT);
gpioSetPullUpDown(gpio_0, PI_PUD_UP);
gpioSetPullUpDown(gpio_1, PI_PUD_UP);
gpioSetAlertFuncEx(gpio_0, _cb, wieg);
gpioSetAlertFuncEx(gpio_1, _cb, wieg);
return wieg;
}
void Pi_Wieg_cancel(Pi_Wieg_t *wieg)
{
/*
Cancel the Wiegand decoder.
*/
if (wieg)
{
gpioSetAlertFunc(wieg->mygpio_0, 0);
gpioSetAlertFunc(wieg->mygpio_1, 0);
free(wieg);
}
}

View File

@@ -0,0 +1,32 @@
#ifndef WIEGAND_H
#define WIEGAND_H
#include <stdint.h>
typedef void (*Pi_Wieg_CB_t)(int, uint32_t);
struct _Pi_Wieg_s;
typedef struct _Pi_Wieg_s Pi_Wieg_t;
Pi_Wieg_t *Pi_Wieg(int gpio_0, int gpio_1, Pi_Wieg_CB_t callback, int timeout);
/*
This function establishes a Wiegand decoder on gpio_0 and gpio_1.
A gap of timeout milliseconds without a new bit indicates the
end of a code.
When the code is ended the callback function is called with the code
bit length and value.
A pointer to a private data type is returned. This should be passed
to Pi_Wieg_cancel if the decoder is to be cancelled.
*/
void Pi_Wieg_cancel(Pi_Wieg_t *wieg);
/*
This function releases the resources used by the decoder.
*/
#endif

View File

@@ -0,0 +1,4 @@
Class to hash a code from an IR receiver (reading an IR remote control).
Follow the instructions in the test file to build and run.

View File

@@ -0,0 +1,91 @@
#include <pigpio.h>
#include "ir_hasher.hpp"
void Hasher::_hash(int old_val, int new_val)
{
int val;
if (new_val < (old_val * 0.60)) val = 13;
else if (old_val < (new_val * 0.60)) val = 23;
else val = 2;
hash_val ^= val;
hash_val *= 16777619; /* FNV_PRIME_32 */
}
void Hasher::_callback(int gpio, int level, uint32_t tick)
{
if (level != PI_TIMEOUT)
{
if (in_code == 0)
{
in_code = 1;
gpioSetWatchdog(mygpio, mytimeout);
hash_val = 2166136261U; /* FNV_BASIS_32 */
edges = 1;
t1 = 0;
t2 = 0;
t3 = 0;
t4 = tick;
}
else
{
edges++;
t1 = t2;
t2 = t3;
t3 = t4;
t4 = tick;
if (edges > 3) _hash(t2-t1, t4-t3);
}
}
else
{
if (in_code)
{
in_code = 0;
gpioSetWatchdog(mygpio, 0);
if (edges > 12) /* Anything less is probably noise. */
{
(mycallback)(hash_val);
}
}
}
}
void Hasher::_callbackExt(int gpio, int level, uint32_t tick, void *user)
{
/*
Need a static callback to link with C.
*/
Hasher *mySelf = (Hasher *) user;
mySelf->_callback(gpio, level, tick); /* Call the instance callback. */
}
Hasher::Hasher(int gpio, HasherCB_t callback, int timeout)
{
/*
Initialises an IR remote hasher on a gpio. A gap of timeout
milliseconds indicates the end of the remote key press.
*/
mygpio = gpio;
mycallback = callback;
mytimeout = timeout;
in_code = 0;
gpioSetMode(gpio, PI_INPUT);
gpioSetAlertFuncEx(gpio, _callbackExt, (void *)this);
}

View File

@@ -0,0 +1,44 @@
#ifndef IR_RX_HASHER_HPP
#define IR_RX_HASHER_HPP
#include <stdint.h>
typedef void (*HasherCB_t)(uint32_t);
class Hasher
{
/*
This class forms a hash over the IR pulses generated by an
IR remote.
The remote key press is not converted into a code in the manner of
the lirc module. No attempt is made to decode the type of protocol
used by the remote. The hash is likely to be unique for different
keys and different remotes but this is not guaranteed.
This hashing process works for some remotes/protocols but not for
others. The only way to find out if it works for one or more of
your remotes is to try it and see.
*/
int mygpio, mytimeout;
HasherCB_t mycallback;
int in_code;
uint32_t hash_val;
int edges;
uint32_t t1, t2, t3, t4;
void _hash(int old_val, int new_val);
void _callback(int gpio, int level, uint32_t tick);
/* Need a static callback to link with C. */
static void _callbackExt(int gpio, int level, uint32_t tick, void *user);
public:
Hasher(int gpio, HasherCB_t callback, int timeout=5);
};
#endif

View File

@@ -0,0 +1,44 @@
#include <iostream>
#include <pigpio.h>
#include "ir_hasher.hpp"
/*
REQUIRES
An IR receiver output pin connected to a Pi gpio.
TO BUILD
g++ -o ir_hash_cpp test_ir_hasher.cpp ir_hasher.cpp -lpigpio -lrt -lpthread
TO RUN
sudo ./ir_hash_cpp
*/
void callback(uint32_t hash)
{
std::cout << "hash=" << hash << std::endl;
}
int main(int argc, char *argv[])
{
if (gpioInitialise() >= 0)
{
/* Can't instantiate a Hasher before pigpio is initialised. */
/*
This assumes the output pin of an IR receiver is
connected to gpio 7.
*/
Hasher ir(7, callback);
sleep(300);
}
}

View File

@@ -0,0 +1,4 @@
Class to decode a mechanical rotary encoder.
Follow the instructions in the test file to build and run.

View File

@@ -0,0 +1,84 @@
#include <iostream>
#include <pigpio.h>
#include "rotary_encoder.hpp"
/*
+---------+ +---------+ 0
| | | |
A | | | |
| | | |
+---------+ +---------+ +----- 1
+---------+ +---------+ 0
| | | |
B | | | |
| | | |
----+ +---------+ +---------+ 1
*/
void re_decoder::_pulse(int gpio, int level, uint32_t tick)
{
if (gpio == mygpioA) levA = level; else levB = level;
if (gpio != lastGpio) /* debounce */
{
lastGpio = gpio;
if ((gpio == mygpioA) && (level == 1))
{
if (levB) (mycallback)(1);
}
else if ((gpio == mygpioB) && (level == 1))
{
if (levA) (mycallback)(-1);
}
}
}
void re_decoder::_pulseEx(int gpio, int level, uint32_t tick, void *user)
{
/*
Need a static callback to link with C.
*/
re_decoder *mySelf = (re_decoder *) user;
mySelf->_pulse(gpio, level, tick); /* Call the instance callback. */
}
re_decoder::re_decoder(int gpioA, int gpioB, re_decoderCB_t callback)
{
mygpioA = gpioA;
mygpioB = gpioB;
mycallback = callback;
levA=0;
levB=0;
lastGpio = -1;
gpioSetMode(gpioA, PI_INPUT);
gpioSetMode(gpioB, PI_INPUT);
/* pull up is needed as encoder common is grounded */
gpioSetPullUpDown(gpioA, PI_PUD_UP);
gpioSetPullUpDown(gpioB, PI_PUD_UP);
/* monitor encoder level changes */
gpioSetAlertFuncEx(gpioA, _pulseEx, this);
gpioSetAlertFuncEx(gpioB, _pulseEx, this);
}
void re_decoder::re_cancel(void)
{
gpioSetAlertFuncEx(mygpioA, 0, this);
gpioSetAlertFuncEx(mygpioB, 0, this);
}

View File

@@ -0,0 +1,35 @@
#ifndef ROTARY_ENCODER_HPP
#define ROTARY_ENCODER_HPP
#include <stdint.h>
typedef void (*re_decoderCB_t)(int);
class re_decoder
{
int mygpioA, mygpioB, levA, levB, lastGpio;
re_decoderCB_t mycallback;
void _pulse(int gpio, int level, uint32_t tick);
/* Need a static callback to link with C. */
static void _pulseEx(int gpio, int level, uint32_t tick, void *user);
public:
re_decoder(int gpioA, int gpioB, re_decoderCB_t callback);
/*
This function establishes a rotary encoder on gpioA and gpioB.
When the encoder is turned the callback function is called.
*/
void re_cancel(void);
/*
This function releases the resources used by the decoder.
*/
};
#endif

View File

@@ -0,0 +1,45 @@
#include <iostream>
#include <pigpio.h>
#include "rotary_encoder.hpp"
/*
REQUIRES
A rotary encoder contacts A and B connected to separate gpios and
the common contact connected to Pi ground.
TO BUILD
g++ -o rot_enc_cpp test_rotary_encoder.cpp rotary_encoder.cpp -lpigpio -lrt
TO RUN
sudo ./rot_enc_cpp
*/
void callback(int way)
{
static int pos = 0;
pos += way;
std::cout << "pos=" << pos << std::endl;
}
int main(int argc, char *argv[])
{
if (gpioInitialise() < 0) return 1;
re_decoder dec(7, 8, callback);
sleep(3000);
dec.re_cancel();
gpioTerminate();
}

View File

@@ -0,0 +1,3 @@
Class to decode a Wiegand code.
Follow the instructions in the test file to build and run.

View File

@@ -0,0 +1,40 @@
#include <iostream>
#include <pigpio.h>
#include "wiegand.hpp"
/*
REQUIRES
Wiegand contacts 0 and 1 connected to separate gpios.
TO BUILD
g++ -o wiegand_cpp test_wiegand.cpp wiegand.cpp -lpigpio -lrt
TO RUN
sudo ./wiegand_cpp
*/
void callback(int bits, uint32_t value)
{
std::cout << "bits=" << bits << " value=" << value << std::endl;
}
int main(int argc, char *argv[])
{
if (gpioInitialise() < 0) return 1;
Wiegand dec(14, 15, callback);
sleep(300);
dec.cancel();
gpioTerminate();
}

View File

@@ -0,0 +1,116 @@
#include <pigpio.h>
#include "wiegand.hpp"
Wiegand::Wiegand(int gpio_0, int gpio_1, WiegandCB_t callback, int timeout)
{
/*
Instantiate with the gpio for 0 (green wire), the gpio for 1
(white wire), the callback function, and the bit timeout in
milliseconds which indicates the end of a code.
The callback is passed the code length in bits and the value.
*/
mygpio_0 = gpio_0;
mygpio_1 = gpio_1;
mycallback = callback;
mytimeout = timeout;
in_code = 0;
gpioSetMode(gpio_0, PI_INPUT);
gpioSetMode(gpio_1, PI_INPUT);
gpioSetPullUpDown(gpio_0, PI_PUD_UP);
gpioSetPullUpDown(gpio_1, PI_PUD_UP);
gpioSetAlertFuncEx(gpio_0, _cbEx, this);
gpioSetAlertFuncEx(gpio_1, _cbEx, this);
}
void Wiegand::_cb(int gpio, int level, uint32_t tick)
{
/*
Accumulate bits until both gpios 0 and 1 timeout.
*/
if (level == 0) /* a falling edge indicates a new bit */
{
if (!in_code)
{
bits = 1;
num = 0;
in_code = 1;
code_timeout = 0;
gpioSetWatchdog(mygpio_0, mytimeout);
gpioSetWatchdog(mygpio_1, mytimeout);
}
else
{
bits++;
num <<= 1;
}
if (gpio == mygpio_0)
{
code_timeout &= 2; /* clear gpio 0 timeout */
}
else
{
code_timeout &= 1; /* clear gpio 1 timeout */
num |= 1;
}
}
else if (level == PI_TIMEOUT)
{
if (in_code)
{
if (gpio == mygpio_0)
{
code_timeout |= 1; /* timeout gpio 0 */
}
else
{
code_timeout |= 2; /* timeout gpio 1 */
}
if (code_timeout == 3) /* both gpios timed out */
{
gpioSetWatchdog(mygpio_0, 0);
gpioSetWatchdog(mygpio_1, 0);
in_code = 0;
(mycallback)(bits, num);
}
}
}
}
void Wiegand::_cbEx(int gpio, int level, uint32_t tick, void *user)
{
/*
Need a static callback to link with C.
*/
Wiegand *mySelf = (Wiegand *) user;
mySelf->_cb(gpio, level, tick); /* Call the instance callback. */
}
void Wiegand::cancel(void)
{
/*
Cancel the Wiegand decoder.
*/
gpioSetAlertFuncEx(mygpio_0, 0, this);
gpioSetAlertFuncEx(mygpio_1, 0, this);
}

View File

@@ -0,0 +1,43 @@
#ifndef WIEGAND_HPP
#define WIEGAND_HPP
#include <stdint.h>
typedef void (*WiegandCB_t)(int, uint32_t);
class Wiegand
{
int mygpio_0, mygpio_1, mytimeout, in_code, bits;
WiegandCB_t mycallback;
uint32_t num;
uint32_t code_timeout;
void _cb(int gpio, int level, uint32_t tick);
/* Need a static callback to link with C. */
static void _cbEx(int gpio, int level, uint32_t tick, void *user);
public:
Wiegand(int gpio_0, int gpio_1, WiegandCB_t callback, int timeout=5);
/*
This function establishes a Wiegand decoder on gpio_0 and gpio_1.
A gap of timeout milliseconds without a new bit indicates
the end of a code.
When the code is ended the callback function is called with the code
bit length and value.
*/
void cancel(void);
/*
This function releases the resources used by the decoder.
*/
};
#endif

View File

@@ -0,0 +1,2 @@
Class to read the relative humidity and temperature from a DHT11 sensor.
It implements the iterator python protocol.

View File

@@ -0,0 +1,163 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
import time
import pigpio
class DHT11(object):
"""
The DHT11 class is a stripped version of the DHT22 sensor code by joan2937.
You can find the initial implementation here:
- https://github.com/srounet/pigpio/tree/master/EXAMPLES/Python/DHT22_AM2302_SENSOR
example code:
>>> pi = pigpio.pi()
>>> sensor = DHT11(pi, 4) # 4 is the data GPIO pin connected to your sensor
>>> for response in sensor:
.... print("Temperature: {}".format(response['temperature']))
.... print("Humidity: {}".format(response['humidity']))
"""
def __init__(self, pi, gpio):
"""
pi (pigpio): an instance of pigpio
gpio (int): gpio pin number
"""
self.pi = pi
self.gpio = gpio
self.high_tick = 0
self.bit = 40
self.temperature = 0
self.humidity = 0
self.either_edge_cb = None
self.setup()
def setup(self):
"""
Clears the internal gpio pull-up/down resistor.
Kills any watchdogs.
"""
self.pi.set_pull_up_down(self.gpio, pigpio.PUD_OFF)
self.pi.set_watchdog(self.gpio, 0)
self.register_callbacks()
def register_callbacks(self):
"""
Monitors RISING_EDGE changes using callback.
"""
self.either_edge_cb = self.pi.callback(
self.gpio,
pigpio.EITHER_EDGE,
self.either_edge_callback
)
def either_edge_callback(self, gpio, level, tick):
"""
Either Edge callbacks, called each time the gpio edge changes.
Accumulate the 40 data bits from the dht11 sensor.
"""
level_handlers = {
pigpio.FALLING_EDGE: self._edge_FALL,
pigpio.RISING_EDGE: self._edge_RISE,
pigpio.EITHER_EDGE: self._edge_EITHER
}
handler = level_handlers[level]
diff = pigpio.tickDiff(self.high_tick, tick)
handler(tick, diff)
def _edge_RISE(self, tick, diff):
"""
Handle Rise signal.
"""
val = 0
if diff >= 50:
val = 1
if diff >= 200: # Bad bit?
self.checksum = 256 # Force bad checksum
if self.bit >= 40: # Message complete
self.bit = 40
elif self.bit >= 32: # In checksum byte
self.checksum = (self.checksum << 1) + val
if self.bit == 39:
# 40th bit received
self.pi.set_watchdog(self.gpio, 0)
total = self.humidity + self.temperature
# is checksum ok ?
if not (total & 255) == self.checksum:
raise
elif 16 <= self.bit < 24: # in temperature byte
self.temperature = (self.temperature << 1) + val
elif 0 <= self.bit < 8: # in humidity byte
self.humidity = (self.humidity << 1) + val
else: # skip header bits
pass
self.bit += 1
def _edge_FALL(self, tick, diff):
"""
Handle Fall signal.
"""
self.high_tick = tick
if diff <= 250000:
return
self.bit = -2
self.checksum = 0
self.temperature = 0
self.humidity = 0
def _edge_EITHER(self, tick, diff):
"""
Handle Either signal.
"""
self.pi.set_watchdog(self.gpio, 0)
def read(self):
"""
Start reading over DHT11 sensor.
"""
self.pi.write(self.gpio, pigpio.LOW)
time.sleep(0.017) # 17 ms
self.pi.set_mode(self.gpio, pigpio.INPUT)
self.pi.set_watchdog(self.gpio, 200)
time.sleep(0.2)
def close(self):
"""
Stop reading sensor, remove callbacks.
"""
self.pi.set_watchdog(self.gpio, 0)
if self.either_edge_cb:
self.either_edge_cb.cancel()
self.either_edge_cb = None
def __iter__(self):
"""
Support the iterator protocol.
"""
return self
def next(self):
"""
Call the read method and return temperature and humidity informations.
"""
self.read()
response = {
'humidity': self.humidity,
'temperature': self.temperature
}
return response
if __name__ == '__main__':
pi = pigpio.pi()
sensor = DHT11(pi, 4)
for d in sensor:
print("temperature: {}".format(d['temperature']))
print("humidity: {}".format(d['humidity']))
time.sleep(1)
sensor.close()

View File

@@ -0,0 +1,283 @@
#!/usr/bin/env python
# 2014-07-11 DHT22.py
import time
import atexit
import pigpio
class sensor:
"""
A class to read relative humidity and temperature from the
DHT22 sensor. The sensor is also known as the AM2302.
The sensor can be powered from the Pi 3V3 or the Pi 5V rail.
Powering from the 3V3 rail is simpler and safer. You may need
to power from 5V if the sensor is connected via a long cable.
For 3V3 operation connect pin 1 to 3V3 and pin 4 to ground.
Connect pin 2 to a gpio.
For 5V operation connect pin 1 to 5V and pin 4 to ground.
The following pin 2 connection works for me. Use at YOUR OWN RISK.
5V--5K_resistor--+--10K_resistor--Ground
|
DHT22 pin 2 -----+
|
gpio ------------+
"""
def __init__(self, pi, gpio, LED=None, power=None):
"""
Instantiate with the Pi and gpio to which the DHT22 output
pin is connected.
Optionally a LED may be specified. This will be blinked for
each successful reading.
Optionally a gpio used to power the sensor may be specified.
This gpio will be set high to power the sensor. If the sensor
locks it will be power cycled to restart the readings.
Taking readings more often than about once every two seconds will
eventually cause the DHT22 to hang. A 3 second interval seems OK.
"""
self.pi = pi
self.gpio = gpio
self.LED = LED
self.power = power
if power is not None:
pi.write(power, 1) # Switch sensor on.
time.sleep(2)
self.powered = True
self.cb = None
atexit.register(self.cancel)
self.bad_CS = 0 # Bad checksum count.
self.bad_SM = 0 # Short message count.
self.bad_MM = 0 # Missing message count.
self.bad_SR = 0 # Sensor reset count.
# Power cycle if timeout > MAX_TIMEOUTS.
self.no_response = 0
self.MAX_NO_RESPONSE = 2
self.rhum = -999
self.temp = -999
self.tov = None
self.high_tick = 0
self.bit = 40
pi.set_pull_up_down(gpio, pigpio.PUD_OFF)
pi.set_watchdog(gpio, 0) # Kill any watchdogs.
self.cb = pi.callback(gpio, pigpio.EITHER_EDGE, self._cb)
def _cb(self, gpio, level, tick):
"""
Accumulate the 40 data bits. Format into 5 bytes, humidity high,
humidity low, temperature high, temperature low, checksum.
"""
diff = pigpio.tickDiff(self.high_tick, tick)
if level == 0:
# Edge length determines if bit is 1 or 0.
if diff >= 50:
val = 1
if diff >= 200: # Bad bit?
self.CS = 256 # Force bad checksum.
else:
val = 0
if self.bit >= 40: # Message complete.
self.bit = 40
elif self.bit >= 32: # In checksum byte.
self.CS = (self.CS << 1) + val
if self.bit == 39:
# 40th bit received.
self.pi.set_watchdog(self.gpio, 0)
self.no_response = 0
total = self.hH + self.hL + self.tH + self.tL
if (total & 255) == self.CS: # Is checksum ok?
self.rhum = ((self.hH << 8) + self.hL) * 0.1
if self.tH & 128: # Negative temperature.
mult = -0.1
self.tH = self.tH & 127
else:
mult = 0.1
self.temp = ((self.tH << 8) + self.tL) * mult
self.tov = time.time()
if self.LED is not None:
self.pi.write(self.LED, 0)
else:
self.bad_CS += 1
elif self.bit >= 24: # in temp low byte
self.tL = (self.tL << 1) + val
elif self.bit >= 16: # in temp high byte
self.tH = (self.tH << 1) + val
elif self.bit >= 8: # in humidity low byte
self.hL = (self.hL << 1) + val
elif self.bit >= 0: # in humidity high byte
self.hH = (self.hH << 1) + val
else: # header bits
pass
self.bit += 1
elif level == 1:
self.high_tick = tick
if diff > 250000:
self.bit = -2
self.hH = 0
self.hL = 0
self.tH = 0
self.tL = 0
self.CS = 0
else: # level == pigpio.TIMEOUT:
self.pi.set_watchdog(self.gpio, 0)
if self.bit < 8: # Too few data bits received.
self.bad_MM += 1 # Bump missing message count.
self.no_response += 1
if self.no_response > self.MAX_NO_RESPONSE:
self.no_response = 0
self.bad_SR += 1 # Bump sensor reset count.
if self.power is not None:
self.powered = False
self.pi.write(self.power, 0)
time.sleep(2)
self.pi.write(self.power, 1)
time.sleep(2)
self.powered = True
elif self.bit < 39: # Short message receieved.
self.bad_SM += 1 # Bump short message count.
self.no_response = 0
else: # Full message received.
self.no_response = 0
def temperature(self):
"""Return current temperature."""
return self.temp
def humidity(self):
"""Return current relative humidity."""
return self.rhum
def staleness(self):
"""Return time since measurement made."""
if self.tov is not None:
return time.time() - self.tov
else:
return -999
def bad_checksum(self):
"""Return count of messages received with bad checksums."""
return self.bad_CS
def short_message(self):
"""Return count of short messages."""
return self.bad_SM
def missing_message(self):
"""Return count of missing messages."""
return self.bad_MM
def sensor_resets(self):
"""Return count of power cycles because of sensor hangs."""
return self.bad_SR
def trigger(self):
"""Trigger a new relative humidity and temperature reading."""
if self.powered:
if self.LED is not None:
self.pi.write(self.LED, 1)
self.pi.write(self.gpio, pigpio.LOW)
time.sleep(0.017) # 17 ms
self.pi.set_mode(self.gpio, pigpio.INPUT)
self.pi.set_watchdog(self.gpio, 200)
def cancel(self):
"""Cancel the DHT22 sensor."""
self.pi.set_watchdog(self.gpio, 0)
if self.cb is not None:
self.cb.cancel()
self.cb = None
if __name__ == "__main__":
import time
import pigpio
import DHT22
# Intervals of about 2 seconds or less will eventually hang the DHT22.
INTERVAL = 3
pi = pigpio.pi()
s = DHT22.sensor(pi, 22, LED=16, power=8)
r = 0
next_reading = time.time()
while True:
r += 1
s.trigger()
time.sleep(0.2)
print("{} {} {} {:3.2f} {} {} {} {}".format(
r, s.humidity(), s.temperature(), s.staleness(),
s.bad_checksum(), s.short_message(), s.missing_message(),
s.sensor_resets()))
next_reading += INTERVAL
time.sleep(next_reading-time.time()) # Overall INTERVAL second polling.
s.cancel()
pi.stop()

View File

@@ -0,0 +1,2 @@
Class to read the relative humidity and temperature from a DHT22/AM2302 sensor.

View File

@@ -0,0 +1,2 @@
Script to display the status of gpios 0-31.

View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python
import time
import curses
import atexit
import sys
import pigpio
GPIOS=32
MODES=["INPUT", "OUTPUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"]
def cleanup():
curses.nocbreak()
curses.echo()
curses.endwin()
pi.stop()
pi = pigpio.pi()
if not pi.connected:
sys.exit(1)
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
atexit.register(cleanup)
cb = []
for g in range(GPIOS):
cb.append(pi.callback(g, pigpio.EITHER_EDGE))
# disable gpio 28 as the PCM clock is swamping the system
cb[28].cancel()
stdscr.nodelay(1)
stdscr.addstr(0, 23, "Status of gpios 0-31", curses.A_REVERSE)
while True:
for g in range(GPIOS):
tally = cb[g].tally()
mode = pi.get_mode(g)
col = (g // 11) * 25
row = (g % 11) + 2
stdscr.addstr(row, col, "{:2}".format(g), curses.A_BOLD)
stdscr.addstr(
"={} {:>6}: {:<10}".format(pi.read(g), MODES[mode], tally))
stdscr.refresh()
time.sleep(0.1)
c = stdscr.getch()
if c != curses.ERR:
break

View File

@@ -0,0 +1,2 @@
Program to show status changes for a Hall effect sensor.

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env python
import time
import pigpio
#
# OH3144E or equivalent Hall effect sensor
#
# Pin 1 - 5V
# Pin 2 - Ground
# Pin 3 - gpio (here P1-8, gpio 14, TXD is used)
#
# The internal gpio pull-up is enabled so that the sensor
# normally reads high. It reads low when a magnet is close.
#
HALL=14
pi = pigpio.pi() # connect to local Pi
pi.set_mode(HALL, pigpio.INPUT)
pi.set_pull_up_down(HALL, pigpio.PUD_UP)
start = time.time()
while (time.time() - start) < 60:
print("Hall = {}".format(pi.read(HALL)))
time.sleep(0.2)
pi.stop()

View File

@@ -0,0 +1,163 @@
#!/usr/bin/env python
import time
import pigpio
class sniffer:
"""
A class to passively monitor activity on an I2C bus. This should
work for an I2C bus running at 100kbps or less. You are unlikely
to get any usable results for a bus running any faster.
"""
def __init__(self, pi, SCL, SDA, set_as_inputs=True):
"""
Instantiate with the Pi and the gpios for the I2C clock
and data lines.
If you are monitoring one of the Raspberry Pi buses you
must set set_as_inputs to False so that they remain in
I2C mode.
The pigpio daemon should have been started with a higher
than default sample rate.
For an I2C bus rate of 100Kbps sudo pigpiod -s 2 should work.
A message is printed for each I2C transaction formatted with
"[" for the START
"XX" two hex characters for each data byte
"+" if the data is ACKd, "-" if the data is NACKd
"]" for the STOP
E.g. Reading the X, Y, Z values from an ADXL345 gives:
[A6+32+]
[A7+01+FF+F2+FF+06+00-]
"""
self.pi = pi
self.gSCL = SCL
self.gSDA = SDA
self.FALLING = 0
self.RISING = 1
self.STEADY = 2
self.in_data = False
self.byte = 0
self.bit = 0
self.oldSCL = 1
self.oldSDA = 1
self.transact = ""
if set_as_inputs:
self.pi.set_mode(SCL, pigpio.INPUT)
self.pi.set_mode(SDA, pigpio.INPUT)
self.cbA = self.pi.callback(SCL, pigpio.EITHER_EDGE, self._cb)
self.cbB = self.pi.callback(SDA, pigpio.EITHER_EDGE, self._cb)
def _parse(self, SCL, SDA):
"""
Accumulate all the data between START and STOP conditions
into a string and output when STOP is detected.
"""
if SCL != self.oldSCL:
self.oldSCL = SCL
if SCL:
xSCL = self.RISING
else:
xSCL = self.FALLING
else:
xSCL = self.STEADY
if SDA != self.oldSDA:
self.oldSDA = SDA
if SDA:
xSDA = self.RISING
else:
xSDA = self.FALLING
else:
xSDA = self.STEADY
if xSCL == self.RISING:
if self.in_data:
if self.bit < 8:
self.byte = (self.byte << 1) | SDA
self.bit += 1
else:
self.transact += '{:02X}'.format(self.byte)
if SDA:
self.transact += '-'
else:
self.transact += '+'
self.bit = 0
self.byte = 0
elif xSCL == self.STEADY:
if xSDA == self.RISING:
if SCL:
self.in_data = False
self.byte = 0
self.bit = 0
self.transact += ']' # STOP
print (self.transact)
self.transact = ""
if xSDA == self.FALLING:
if SCL:
self.in_data = True
self.byte = 0
self.bit = 0
self.transact += '[' # START
def _cb(self, gpio, level, tick):
"""
Check which line has altered state (ignoring watchdogs) and
call the parser with the new state.
"""
SCL = self.oldSCL
SDA = self.oldSDA
if gpio == self.gSCL:
if level == 0:
SCL = 0
elif level == 1:
SCL = 1
if gpio == self.gSDA:
if level == 0:
SDA = 0
elif level == 1:
SDA = 1
self._parse(SCL, SDA)
def cancel(self):
"""Cancel the I2C callbacks."""
self.cbA.cancel()
self.cbB.cancel()
if __name__ == "__main__":
import time
import pigpio
import I2C_sniffer
pi = pigpio.pi()
s = I2C_sniffer.sniffer(pi, 1, 0, False) # leave gpios 1/0 in I2C mode
time.sleep(60000)
s.cancel()
pi.stop()

View File

@@ -0,0 +1,2 @@
A program to passively sniff I2C transactions (100kHz bus maximum) and display the results.

View File

@@ -0,0 +1,2 @@
Class to hash a code from an IR receiver (reading an IR remote control).

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python
import pigpio
class hasher:
"""
This class forms a hash over the IR pulses generated by an
IR remote.
The remote key press is not converted into a code in the manner of
the lirc module. No attempt is made to decode the type of protocol
used by the remote. The hash is likely to be unique for different
keys and different remotes but this is not guaranteed.
This hashing process works for some remotes/protocols but not for
others. The only way to find out if it works for one or more of
your remotes is to try it and see.
EXAMPLE CODE
#!/usr/bin/env python
import time
import pigpio
import ir_hasher
def callback(hash):
print("hash={}".format(hash));
pi = pigpio.pi()
ir = ir_hasher.hasher(pi, 7, callback, 5)
print("ctrl c to exit");
time.sleep(300)
pi.stop()
"""
def __init__(self, pi, gpio, callback, timeout=5):
"""
Initialises an IR remote hasher on a pi's gpio. A gap of timeout
milliseconds indicates the end of the remote key press.
"""
self.pi = pi
self.gpio = gpio
self.code_timeout = timeout
self.callback = callback
self.in_code = False
pi.set_mode(gpio, pigpio.INPUT)
self.cb = pi.callback(gpio, pigpio.EITHER_EDGE, self._cb)
def _hash(self, old_val, new_val):
if new_val < (old_val * 0.60):
val = 13
elif old_val < (new_val * 0.60):
val = 23
else:
val = 2
self.hash_val = self.hash_val ^ val
self.hash_val *= 16777619 # FNV_PRIME_32
self.hash_val = self.hash_val & ((1<<32)-1)
def _cb(self, gpio, level, tick):
if level != pigpio.TIMEOUT:
if self.in_code == False:
self.in_code = True
self.pi.set_watchdog(self.gpio, self.code_timeout)
self.hash_val = 2166136261 # FNV_BASIS_32
self.edges = 1
self.t1 = None
self.t2 = None
self.t3 = None
self.t4 = tick
else:
self.edges += 1
self.t1 = self.t2
self.t2 = self.t3
self.t3 = self.t4
self.t4 = tick
if self.t1 is not None:
d1 = pigpio.tickDiff(self.t1,self.t2)
d2 = pigpio.tickDiff(self.t3,self.t4)
self._hash(d1, d2)
else:
if self.in_code:
self.in_code = False
self.pi.set_watchdog(self.gpio, 0)
if self.edges > 12:
self.callback(self.hash_val)
if __name__ == "__main__":
import time
import pigpio
import ir_hasher
hashes = {
142650387: '2', 244341844: 'menu', 262513468: 'vol-',
272048826: '5', 345069212: '6', 363685443: 'prev.ch',
434191356: '1', 492745084: 'OK', 549497027: 'mute',
603729091: 'text', 646476378: 'chan-', 832916949: 'home',
923778138: 'power', 938165610: 'power', 953243510: 'forward',
1009731980:'1', 1018231875:'TV', 1142888517:'c-up',
1151589683:'chan+', 1344018636:'OK', 1348032067:'chan+',
1367109971:'prev.ch', 1370712102:'c-left', 1438405361:'rewind',
1452589043:'pause', 1518578730:'chan-', 1554432645:'8',
1583569525:'0', 1629745313:'rewind', 1666513749:'record',
1677653754:'c-down', 1825951717:'c-right', 1852412236:'6',
1894279468:'9', 1904895749:'vol+', 1941947509:'ff',
2076573637:'0', 2104823531:'back', 2141641957:'home',
2160787557:'record', 2398525299:'7', 2468117013:'8',
2476712746:'play', 2574308838:'forward', 2577952149:'4',
2706654902:'stop', 2829002741:'c-up', 2956097083:'back',
3112717386:'5', 3263244773:'ff', 3286088195:'pause',
3363767978:'c-down', 3468076364:'vol-', 3491068358:'stop',
3593710134:'c-left', 3708232515:'3', 3734134565:'back',
3766109107:'TV', 3798010010:'play', 3869937700:'menu',
3872715523:'7', 3885097091:'2', 3895301587:'text',
3931058739:'mute', 3983900853:'c-right', 4032250885:'4',
4041913909:'vol+', 4207017660:'9', 4227138677:'back',
4294027955:'3'}
def callback(hash):
if hash in hashes:
print("key={} hash={}".format(hashes[hash], hash));
pi = pigpio.pi()
ir = ir_hasher.hasher(pi, 7, callback, 5)
print("ctrl c to exit");
time.sleep(300)
pi.stop()

View File

@@ -0,0 +1,2 @@
Script to transmit the morse code corresponding to a text string.

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env python
import pigpio
morse={
'a':'.-' , 'b':'-...' , 'c':'-.-.' , 'd':'-..' , 'e':'.' ,
'f':'..-.' , 'g':'--.' , 'h':'....' , 'i':'..' , 'j':'.---' ,
'k':'-.-' , 'l':'.-..' , 'm':'--' , 'n':'-.' , 'o':'---' ,
'p':'.--.' , 'q':'--.-' , 'r':'.-.' , 's':'...' , 't':'-' ,
'u':'..-' , 'v':'...-' , 'w':'.--' , 'x':'-..-' , 'y':'-.--' ,
'z':'--..' , '1':'.----', '2':'..---', '3':'...--', '4':'....-',
'5':'.....', '6':'-....', '7':'--...', '8':'---..', '9':'----.',
'0':'-----'}
GPIO=22
MICROS=100000
NONE=0
DASH=3
DOT=1
GAP=1
LETTER_GAP=3-GAP
WORD_GAP=7-LETTER_GAP
def transmit_string(pi, gpio, str):
pi.wave_clear() # start a new waveform
wf=[]
for C in str:
c=C.lower()
print(c)
if c in morse:
k = morse[c]
for x in k:
if x == '.':
wf.append(pigpio.pulse(1<<gpio, NONE, DOT * MICROS))
else:
wf.append(pigpio.pulse(1<<gpio, NONE, DASH * MICROS))
wf.append(pigpio.pulse(NONE, 1<<gpio, GAP * MICROS))
wf.append(pigpio.pulse(NONE, 1<<gpio, LETTER_GAP * MICROS))
elif c == ' ':
wf.append(pigpio.pulse(NONE, 1<<gpio, WORD_GAP * MICROS))
pi.wave_add_generic(wf)
pi.wave_tx_start()
pi = pigpio.pi()
pi.set_mode(GPIO, pigpio.OUTPUT)
transmit_string(pi, GPIO, "Now is the winter of our discontent")
while pi.wave_tx_busy():
pass
transmit_string(pi, GPIO, "made glorious summer by this sun of York")
while pi.wave_tx_busy():
pass
pi.stop()

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python
# 2014-08-26 PCF8591.py
import time
import curses
import pigpio
# sudo pigpiod
# ./PCF8591.py
# Connect Pi 3V3 - VCC, Ground - Ground, SDA - SDA, SCL - SCL.
YL_40=0x48
pi = pigpio.pi() # Connect to local Pi.
handle = pi.i2c_open(1, YL_40, 0)
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
aout = 0
stdscr.addstr(10, 0, "Brightness")
stdscr.addstr(12, 0, "Temperature")
stdscr.addstr(14, 0, "AOUT->AIN2")
stdscr.addstr(16, 0, "Resistor")
stdscr.nodelay(1)
try:
while True:
for a in range(0,4):
aout = aout + 1
pi.i2c_write_byte_data(handle, 0x40 | ((a+1) & 0x03), aout&0xFF)
v = pi.i2c_read_byte(handle)
hashes = v / 4
spaces = 64 - hashes
stdscr.addstr(10+a*2, 12, str(v) + ' ')
stdscr.addstr(10+a*2, 16, '#' * hashes + ' ' * spaces )
stdscr.refresh()
time.sleep(0.04)
c = stdscr.getch()
if c != curses.ERR:
break
except:
pass
curses.nocbreak()
curses.echo()
curses.endwin()
pi.i2c_close(handle)
pi.stop()

View File

@@ -0,0 +1,2 @@
Script to display readings from the (I2C) PCF8591.

View File

@@ -0,0 +1,2 @@
Script to benchmark the pigpio Python module's performance.

View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python
#
# WARNING!
#
##############################################################################
# #
# Unless you know what you are doing don't run this script with anything #
# connected to the gpios. You will CAUSE damage. #
# #
##############################################################################
#
# WARNING!
#
import time
import pigpio
delay = 30
class gpioTest:
def __init__(self, pi, gpio, edge, freq, duty):
self.pi = pi
self.gpio = gpio
self.edge = edge
self.freq = freq
self.duty = duty
self.calls = 0
def cb(self, g, t, l):
self.calls = self.calls + 1
# print g,t,l
def num(self):
return self.calls
def start(self):
self.pi.set_PWM_frequency(self.gpio, self.freq)
self.pi.set_PWM_range(self.gpio, 25)
self.pi.set_PWM_dutycycle(self.gpio, self.duty)
self.n = self.pi.callback(self.gpio, self.edge, self.cb)
def stop(self):
self.pi.set_PWM_dutycycle(self.gpio, 0)
self.n.cancel()
pi = pigpio.pi()
t1 = gpioTest(pi, 4, pigpio.EITHER_EDGE, 4000, 1)
t2 = gpioTest(pi, 7, pigpio.RISING_EDGE, 8000, 2)
t3 = gpioTest(pi, 8, pigpio.FALLING_EDGE, 8000, 3)
t4 = gpioTest(pi, 9, pigpio.EITHER_EDGE, 4000, 4)
t5 = gpioTest(pi,10, pigpio.RISING_EDGE, 8000, 5)
t6 = gpioTest(pi,11, pigpio.FALLING_EDGE, 8000, 6)
t7 = gpioTest(pi,14, pigpio.EITHER_EDGE, 4000, 7)
t8 = gpioTest(pi,15, pigpio.RISING_EDGE, 8000, 8)
t9 = gpioTest(pi,17, pigpio.FALLING_EDGE, 8000, 9)
t10 = gpioTest(pi,18, pigpio.EITHER_EDGE, 4000, 10)
t11 = gpioTest(pi,22, pigpio.RISING_EDGE, 8000, 11)
t12 = gpioTest(pi,23, pigpio.FALLING_EDGE, 8000, 12)
t13 = gpioTest(pi,24, pigpio.EITHER_EDGE, 4000, 13)
t14 = gpioTest(pi,25, pigpio.RISING_EDGE, 8000, 14)
# R1: 0 1 4 7 8 9 10 11 14 15 17 18 21 22 23 24 25
# R2: 2 3 4 7 8 9 10 11 14 15 17 18 22 23 24 25 27 28 29 30 31
tests = [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14]
for i in tests: i.start()
time.sleep(delay)
for i in tests: i.stop()
pi.stop()
tot = 0
msg = ""
for i in tests:
tot += i.num()
msg += str(i.num()) + " "
print(msg)
print("eps={} ({}/{})".format(tot/delay, tot, delay))

View File

@@ -0,0 +1,2 @@
Class to decode a mechanical rotary encoder.

View File

@@ -0,0 +1,135 @@
#!/usr/bin/env python
import pigpio
class decoder:
"""Class to decode mechanical rotary encoder pulses."""
def __init__(self, pi, gpioA, gpioB, callback):
"""
Instantiate the class with the pi and gpios connected to
rotary encoder contacts A and B. The common contact
should be connected to ground. The callback is
called when the rotary encoder is turned. It takes
one parameter which is +1 for clockwise and -1 for
counterclockwise.
EXAMPLE
import time
import pigpio
import rotary_encoder
pos = 0
def callback(way):
global pos
pos += way
print("pos={}".format(pos))
pi = pigpio.pi()
decoder = rotary_encoder.decoder(pi, 7, 8, callback)
time.sleep(300)
decoder.cancel()
pi.stop()
"""
self.pi = pi
self.gpioA = gpioA
self.gpioB = gpioB
self.callback = callback
self.levA = 0
self.levB = 0
self.lastGpio = None
self.pi.set_mode(gpioA, pigpio.INPUT)
self.pi.set_mode(gpioB, pigpio.INPUT)
self.pi.set_pull_up_down(gpioA, pigpio.PUD_UP)
self.pi.set_pull_up_down(gpioB, pigpio.PUD_UP)
self.cbA = self.pi.callback(gpioA, pigpio.EITHER_EDGE, self._pulse)
self.cbB = self.pi.callback(gpioB, pigpio.EITHER_EDGE, self._pulse)
def _pulse(self, gpio, level, tick):
"""
Decode the rotary encoder pulse.
+---------+ +---------+ 0
| | | |
A | | | |
| | | |
+---------+ +---------+ +----- 1
+---------+ +---------+ 0
| | | |
B | | | |
| | | |
----+ +---------+ +---------+ 1
"""
if gpio == self.gpioA:
self.levA = level
else:
self.levB = level;
if gpio != self.lastGpio: # debounce
self.lastGpio = gpio
if gpio == self.gpioA and level == 1:
if self.levB == 1:
self.callback(1)
elif gpio == self.gpioB and level == 1:
if self.levA == 1:
self.callback(-1)
def cancel(self):
"""
Cancel the rotary encoder decoder.
"""
self.cbA.cancel()
self.cbB.cancel()
if __name__ == "__main__":
import time
import pigpio
import rotary_encoder
pos = 0
def callback(way):
global pos
pos += way
print("pos={}".format(pos))
pi = pigpio.pi()
decoder = rotary_encoder.decoder(pi, 7, 8, callback)
time.sleep(300)
decoder.cancel()
pi.stop()

View File

@@ -0,0 +1,28 @@
# Python Class for Reading Single Edge Nibble Transmission (SENT) using the Raspberry Pi
A full description of this Python script is described at [www.surfncircuits.com](https://surfncircuits.com) in the blog entry: [Implementing a Single Edge Nibble Transmission (SENT) protocol in Python for the Raspberry Pi Zero](https://surfncircuits.com/?p=3725)
This python library will read a Raspberry Pi GPIO pin connected. Start the pigpiod daemon with one microsecond sampling to read SENT transmissions with three microsecond tick times.
## To start the daemon on Raspberry Pi
- sudo pigpiod -s 1
## SENT packet frame summary
- Sync Pulse: 56 ticks
- 4 bit Status and Message Pulse: 17-32 ticks
- 4 bit (9:12) Data1 Field: 17-32 ticks
- 4 bit (5:8) Data1 Field: 17-32 ticks
- 4 bit (1:4) Data1 Field: 17-32 ticks
- 4 bit (9-12) Data2 Field: 17-32 ticks
- 4 bit (5-8) Data2 Field: 17-32 ticks
- 4 bit (1-4) Data2 Field: 17-32 ticks
- 4 bit CRC: 17-32 ticks
## requirements
[pigpiod](http://abyz.me.uk/rpi/pigpio/) library required
## To run the script for a signal attached to GPIO BCD 18 (pin 12)
- python3 sent_READ.py

View File

@@ -0,0 +1,322 @@
#!/usr/bin/env python3
# read_PWM.py
# Public Domain by mark smith, www.surfncircuits.com
# blog:https://surfncircuits.com/2020/11/27/implementing-a-single-edge-nibble-transmission-sent-protocol-in-python-for-the-raspberry-pi-zero/
import time
import pigpio # http://abyz.co.uk/rpi/pigpio/python.html
import threading
class SENTReader:
"""
A class to read short Format SENT frames
(see the LX3302A datasheet for a SENT reference from Microchip)
(also using sent transmission mode where )
from wikiPedia: The SAE J2716 SENT (Single Edge Nibble Transmission) protocol
is a point-to-point scheme for transmitting signal values
from a sensor to a controller. It is intended to allow for
transmission of high resolution data with a low system cost.
Short sensor format:
The first is the SYNC pulse (56 ticks)
first Nibble : Status (4 bits)
2nd NIbble : DAta1 (4 bits)
3nd Nibble : Data2 (4 bits)
4th Nibble : Data3 (4 bits)
5th Nibble : Data1 (4 bits)
6th Nibble : Data2 (4 bits)
7th Nibble : Data3 (4 bits)
8th Nibble : CRC (4 bits)
"""
def __init__(self, pi, gpio, Mode = 0):
"""
Instantiate with the Pi and gpio of the SENT signal
to monitor.
SENT mode = A0: Microchip LX3302A where the two 12 bit data values are identical. there are other modes
"""
self.pi = pi
self.gpio = gpio
self.SENTMode = Mode
# the time that pulse goes high
self._high_tick = 0
# the period of the low tick
self._low_tick = 0
# the period of the pulse (total data)
self._period = 0
# the time the item was low during the period
self._low = 0
# the time the output was high during the period
self._high = 0
# setting initial value to 100
self.syncTick = 100
#keep track of the periods
self.syncWidth = 0
self.status = 0
self.data1 = 0
self.data2 = 0
self.data3 = 0
self.data4 = 0
self.data5 = 0
self.data6 = 0
self.crc = 0
#initize the sent frame . Need to use hex for data
#self.frame = [0,0,0,'0x0','0x0','0x0','0x0','0x0','0x0',0]
self.frame = [0,0,0,0,0,0,0,0,0,0]
self.syncFound = False
self.frameComplete = False
self.nibble = 0
self.numberFrames = 0
self.SampleStopped = False
self.pi.set_mode(gpio, pigpio.INPUT)
#self._cb = pi.callback(gpio, pigpio.EITHER_EDGE, self._cbf)
#sleep enougth to start reading SENT
#time.sleep(0.05)
#start thread to sample the SENT property
# this is needed for piGPIO sample of 1us and sensing the 3us
self.OutputSampleThread = threading.Thread(target = self.SampleCallBack)
self.OutputSampleThread.daemon = True
self.OutputSampleThread.start()
#give time for thread to start capturing data
time.sleep(.05)
def SampleCallBack(self):
# this will run in a loop and sample the SENT path
# this sampling is required when 1us sample rate for SENT 3us tick time
while True:
self.SampleStopped = False
self._cb = self.pi.callback(self.gpio, pigpio.EITHER_EDGE, self._cbf)
# wait until sample stopped
while self.SampleStopped == False:
#do nothing
time.sleep(.001)
# gives the callback time to cancel so we can start again.
time.sleep(0.20)
def _cbf(self, gpio, level, tick):
# depending on the system state set the tick times.
# first look for sync pulse. this is found when duty ratio >90
#print(pgio)
#print("inside _cpf")
#print(tick)
if self.syncFound == False:
if level == 1:
self._high_tick = tick
self._low = pigpio.tickDiff(self._low_tick,tick)
elif level == 0:
# this may be a syncpulse if the duty is 51/56
self._period = pigpio.tickDiff(self._low_tick,tick)
# not reset the self._low_tick
self._low_tick = tick
self._high = pigpio.tickDiff(self._high_tick,tick)
# sync pulse is detected by finding duty ratio. 51/56
# but also filter if period is > 90us*56 = 5040
if (100*self._high/self._period) > 87 and (self._period<5100):
self.syncFound = True
self.syncWidth = self._high
self.syncPeriod = self._period
#self.syncTick = round(self.syncPeriod/56.0,2)
self.syncTick = self.syncPeriod
# reset the nibble to zero
self.nibble = 0
self.SampleStopped = False
else:
# now look for the nibble information for each nibble (8 Nibbles)
if level == 1:
self._high_tick = tick
self._low = pigpio.tickDiff(self._low_tick,tick)
elif level == 0:
# This will be a data nibble
self._period = pigpio.tickDiff(self._low_tick,tick)
# not reset the self._low_tick
self._low_tick = tick
self._high = pigpio.tickDiff(self._high_tick,tick)
self.nibble = self.nibble + 1
if self.nibble == 1:
self.status = self._period
elif self.nibble == 2:
#self.data1 = hex(int(round(self._period / self.syncTick)-12))
self.data1 = self._period
elif self.nibble == 3:
self.data2 = self._period
elif self.nibble == 4:
self.data3 = self._period
elif self.nibble == 5:
self.data4 = self._period
elif self.nibble == 6:
self.data5 = self._period
elif self.nibble == 7:
self.data6 = self._period
elif self.nibble == 8:
self.crc = self._period
# now send all to the SENT Frame
self.frame = [self.syncPeriod,self.syncTick,self.status,self.data1,self.data2,self.data3,self.data4,self.data5,self.data6,self.crc]
self.syncFound = False
self.nibble = 0
self.numberFrames += 1
if self.numberFrames > 2:
self.cancel()
self.SampleStopped = True
self.numberFrames = 0
def ConvertData(self,tickdata,tickTime):
if tickdata == 0:
t = '0x0'
else:
t = hex(int(round(tickdata / tickTime)-12))
if t[0] =='-':
t='0x0'
return t
def SENTData(self):
# check that data1 = Data2 if they are not equal return fault = True
# will check the CRC code for faults. if fault, return = true
# returns status, data1, data2, crc, fault
#self._cb = self.pi.callback(self.gpio, pigpio.EITHER_EDGE, self._cbf)
#time.sleep(0.1)
fault = False
SentFrame = self.frame[:]
SENTTick = round(SentFrame[1]/56.0,2)
# the greatest SYNC sync is 90us. So trip a fault if this occurs
if SENTTick > 90:
fault = True
#print(SentFrame)
# convert SentFrame to HEX Format including the status and Crc bits
for x in range (2,10):
SentFrame[x] = self.ConvertData(SentFrame[x],SENTTick)
SENTCrc = SentFrame[9]
SENTStatus = SentFrame[2]
SENTPeriod = SentFrame[0]
#print(SentFrame)
# combine the datafield nibbles
datanibble = '0x'
datanibble2 = '0x'
for x in range (3,6):
datanibble = datanibble + str((SentFrame[x]))[2:]
for x in range (6,9):
datanibble2 = datanibble2 + str((SentFrame[x]))[2:]
# if using SENT mode 0, then data nibbles should be equal
#if self.SENTMode == 0 :
# if datanibble != datanibble2:
# fault = True
# if datanibble or datanibble2 == 0 then fault = true
if (int(datanibble,16) == 0) or (int(datanibble2,16) ==0):
fault = True
# if datanibble or datanibble2 > FFF (4096) then fault = True
if ( (int(datanibble,16) > 0xFFF) or (int(datanibble2,16) > 0xFFF)):
fault = True
#print(datanibble)
# CRC checking
# converting the datanibble values to a binary bit string.
# remove the first two characters. Not needed for crcCheck
InputBitString = bin(int((datanibble + datanibble2[2:]),16))[2:]
# converting Crcvalue to bin but remove the first two characters 0b
# format is set to remove the leading 0b, 4 charactors long
crcBitValue = format(int(str(SENTCrc),16),'04b')
#checking the crcValue
# polybitstring is 1*X^4+1*X^3+1*x^2+0*X+1 = '11101'
if self.crcCheck(InputBitString,'11101',crcBitValue) == False:
fault = True
# converter to decimnal
returnData = int(datanibble,16)
returnData2 = int(datanibble2,16)
#returns both Data values and if there is a FAULT
return (SENTStatus, returnData, returnData2,SENTTick, SENTCrc, fault, SENTPeriod)
def tick(self):
status, data1, data2, ticktime, crc, errors, syncPulse = self.SENTData()
return ticktime
def crcNibble(self):
status, data1, data2, ticktime, crc, errors, syncPulse = self.SENTData()
return crc
def dataField1(self):
status, data1, data2, ticktime, crc, errors, syncPulse = self.SENTData()
return data1
def dataField2(self):
status, data1, data2, ticktime, crc, errors, syncPulse = self.SENTData()
return data2
def statusNibble(self):
status, data1, data2, ticktime, crc, errors, syncPulse = self.SENTData()
return status
def syncPulse(self):
status, data1, data2, ticktime, crc, errors, syncPulse = self.SENTData()
return syncPulse
def errorFrame(self):
status, data1, data2, ticktime, crc, errors, syncPulse = self.SENTData()
return errors
def cancel(self):
self._cb.cancel()
def stop(self):
self.OutputSampleThread.stop()
def crcCheck(self, InputBitString, PolyBitString, crcValue ):
# the input string will be a binary string all 6 nibbles of the SENT data
# the seed value ( = '0101) is appended to the input string. Do not use zeros for SENT protocal
# this uses the SENT CRC recommended implementation.
checkOK = False
LenPolyBitString = len(PolyBitString)
PolyBitString = PolyBitString.lstrip('0')
LenInput = len(InputBitString)
InputPaddedArray = list(InputBitString + '0101')
while '1' in InputPaddedArray[:LenInput]:
cur_shift = InputPaddedArray.index('1')
for i in range(len(PolyBitString)):
InputPaddedArray[cur_shift + i] = str(int(PolyBitString[i] != InputPaddedArray[cur_shift + i]))
if (InputPaddedArray[LenInput:] == list(crcValue)):
checkOK = True
return checkOK
if __name__ == "__main__":
import time
import pigpio
import read_SENT
SENT_GPIO = 18
RUN_TIME = 6000000000.0
SAMPLE_TIME = 0.1
pi = pigpio.pi()
p = read_SENT.SENTReader(pi, SENT_GPIO)
start = time.time()
while (time.time() - start) < RUN_TIME:
time.sleep(SAMPLE_TIME)
status, data1, data2, ticktime, crc, errors, syncPulse = p.SENTData()
print("Sent Status= %s - 12-bit DATA 1= %4.0f - DATA 2= %4.0f - tickTime(uS)= %4.2f - CRC= %s - Errors= %s - PERIOD = %s" % (status,data1,data2,ticktime,crc,errors,syncPulse))
print("Sent Stat2s= %s - 12-bit DATA 1= %4.0f - DATA 2= %4.0f - tickTime(uS)= %4.2f - CRC= %s - Errors= %s - PERIOD = %s" % (p.statusNibble(),p.dataField1(),p.dataField2(),p.tick(),p.crcNibble(),p.errorFrame(),p.syncPulse()))
# stop the thread in SENTReader
p.stop()
# clear the pi object instance
pi.stop()

View File

@@ -0,0 +1,2 @@
Class to read sonar rangers with separate trigger and echo pins.

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python
import time
import pigpio
class ranger:
"""
This class encapsulates a type of acoustic ranger. In particular
the type of ranger with separate trigger and echo pins.
A pulse on the trigger initiates the sonar ping and shortly
afterwards a sonar pulse is transmitted and the echo pin
goes high. The echo pins stays high until a sonar echo is
received (or the response times-out). The time between
the high and low edges indicates the sonar round trip time.
"""
def __init__(self, pi, trigger, echo):
"""
The class is instantiated with the Pi to use and the
gpios connected to the trigger and echo pins.
"""
self.pi = pi
self._trig = trigger
self._echo = echo
self._ping = False
self._high = None
self._time = None
self._triggered = False
self._trig_mode = pi.get_mode(self._trig)
self._echo_mode = pi.get_mode(self._echo)
pi.set_mode(self._trig, pigpio.OUTPUT)
pi.set_mode(self._echo, pigpio.INPUT)
self._cb = pi.callback(self._trig, pigpio.EITHER_EDGE, self._cbf)
self._cb = pi.callback(self._echo, pigpio.EITHER_EDGE, self._cbf)
self._inited = True
def _cbf(self, gpio, level, tick):
if gpio == self._trig:
if level == 0: # trigger sent
self._triggered = True
self._high = None
else:
if self._triggered:
if level == 1:
self._high = tick
else:
if self._high is not None:
self._time = tick - self._high
self._high = None
self._ping = True
def read(self):
"""
Triggers a reading. The returned reading is the number
of microseconds for the sonar round-trip.
round trip cms = round trip time / 1000000.0 * 34030
"""
if self._inited:
self._ping = False
self.pi.gpio_trigger(self._trig)
start = time.time()
while not self._ping:
if (time.time()-start) > 5.0:
return 20000
time.sleep(0.001)
return self._time
else:
return None
def cancel(self):
"""
Cancels the ranger and returns the gpios to their
original mode.
"""
if self._inited:
self._inited = False
self._cb.cancel()
self.pi.set_mode(self._trig, self._trig_mode)
self.pi.set_mode(self._echo, self._echo_mode)
if __name__ == "__main__":
import time
import pigpio
import sonar_trigger_echo
pi = pigpio.pi()
sonar = sonar_trigger_echo.ranger(pi, 23, 18)
end = time.time() + 600.0
r = 1
while time.time() < end:
print("{} {}".format(r, sonar.read()))
r += 1
time.sleep(0.03)
sonar.cancel()
pi.stop()

View File

@@ -0,0 +1,2 @@
Class to send and receive radio messages compatible with the Virtual Wire library for Arduinos.

View File

@@ -0,0 +1,372 @@
#!/usr/bin/env python
"""
This module provides a 313MHz/434MHz radio interface compatible
with the Virtual Wire library used on Arduinos.
It has been tested between a Pi, TI Launchpad, and Arduino Pro Mini.
"""
# 2014-08-14
# vw.py
import time
import pigpio
MAX_MESSAGE_BYTES=77
MIN_BPS=50
MAX_BPS=10000
_HEADER=[0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x38, 0x2c]
_CTL=3
_SYMBOL=[
0x0d, 0x0e, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c,
0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34]
def _sym2nibble(symbol):
for nibble in range(16):
if symbol == _SYMBOL[nibble]:
return nibble
return 0
def _crc_ccitt_update(crc, data):
data = data ^ (crc & 0xFF);
data = (data ^ (data << 4)) & 0xFF;
return (
(((data << 8) & 0xFFFF) | (crc >> 8)) ^
((data >> 4) & 0x00FF) ^ ((data << 3) & 0xFFFF)
)
class tx():
def __init__(self, pi, txgpio, bps=2000):
"""
Instantiate a transmitter with the Pi, the transmit gpio,
and the bits per second (bps). The bps defaults to 2000.
The bps is constrained to be within MIN_BPS to MAX_BPS.
"""
self.pi = pi
self.txbit = (1<<txgpio)
if bps < MIN_BPS:
bps = MIN_BPS
elif bps > MAX_BPS:
bps = MAX_BPS
self.mics = int(1000000 / bps)
self.wave_id = None
pi.wave_add_new()
pi.set_mode(txgpio, pigpio.OUTPUT)
def _nibble(self, nibble):
for i in range(6):
if nibble & (1<<i):
self.wf.append(pigpio.pulse(self.txbit, 0, self.mics))
else:
self.wf.append(pigpio.pulse(0, self.txbit, self.mics))
def _byte(self, crc, byte):
self._nibble(_SYMBOL[byte>>4])
self._nibble(_SYMBOL[byte&0x0F])
return _crc_ccitt_update(crc, byte)
def put(self, data):
"""
Transmit a message. If the message is more than
MAX_MESSAGE_BYTES in size it is discarded. If a message
is currently being transmitted it is aborted and replaced
with the new message. True is returned if message
transmission has successfully started. False indicates
an error.
"""
if len(data) > MAX_MESSAGE_BYTES:
return False
self.wf = []
self.cancel()
for i in _HEADER:
self._nibble(i)
crc = self._byte(0xFFFF, len(data)+_CTL)
for i in data:
if type(i) == type(""):
v = ord(i)
else:
v = i
crc = self._byte(crc, v)
crc = ~crc
self._byte(0, crc&0xFF)
self._byte(0, crc>>8)
self.pi.wave_add_generic(self.wf)
self.wave_id = self.pi.wave_create()
if self.wave_id >= 0:
self.pi.wave_send_once(self.wave_id)
return True
else:
return False
def ready(self):
"""
Returns True if a new message may be transmitted.
"""
return not self.pi.wave_tx_busy()
def cancel(self):
"""
Cancels the wireless transmitter, aborting any message
in progress.
"""
if self.wave_id is not None:
self.pi.wave_tx_stop()
self.pi.wave_delete(self.wave_id)
self.pi.wave_add_new()
self.wave_id = None
class rx():
def __init__(self, pi, rxgpio, bps=2000):
"""
Instantiate a receiver with the Pi, the receive gpio, and
the bits per second (bps). The bps defaults to 2000.
The bps is constrained to be within MIN_BPS to MAX_BPS.
"""
self.pi = pi
self.rxgpio = rxgpio
self.messages = []
self.bad_CRC = 0
if bps < MIN_BPS:
bps = MIN_BPS
elif bps > MAX_BPS:
bps = MAX_BPS
slack = 0.20
self.mics = int(1000000 / bps)
slack_mics = int(slack * self.mics)
self.min_mics = self.mics - slack_mics # Shortest legal edge.
self.max_mics = (self.mics + slack_mics) * 4 # Longest legal edge.
self.timeout = 8 * self.mics / 1000 # 8 bits time in ms.
if self.timeout < 8:
self.timeout = 8
self.last_tick = None
self.good = 0
self.bits = 0
self.token = 0
self.in_message = False
self.message = [0]*(MAX_MESSAGE_BYTES+_CTL)
self.message_len = 0
self.byte = 0
pi.set_mode(rxgpio, pigpio.INPUT)
self.cb = pi.callback(rxgpio, pigpio.EITHER_EDGE, self._cb)
def _calc_crc(self):
crc = 0xFFFF
for i in range(self.message_length):
crc = _crc_ccitt_update(crc, self.message[i])
return crc
def _insert(self, bits, level):
for i in range(bits):
self.token >>= 1
if level == 0:
self.token |= 0x800
if self.in_message:
self.bits += 1
if self.bits >= 12: # Complete token.
byte = (
_sym2nibble(self.token & 0x3f) << 4 |
_sym2nibble(self.token >> 6))
if self.byte == 0:
self.message_length = byte
if byte > (MAX_MESSAGE_BYTES+_CTL):
self.in_message = False # Abort message.
return
self.message[self.byte] = byte
self.byte += 1
self.bits = 0
if self.byte >= self.message_length:
self.in_message = False
self.pi.set_watchdog(self.rxgpio, 0)
crc = self._calc_crc()
if crc == 0xF0B8: # Valid CRC.
self.messages.append(
self.message[1:self.message_length-2])
else:
self.bad_CRC += 1
else:
if self.token == 0xB38: # Start message token.
self.in_message = True
self.pi.set_watchdog(self.rxgpio, self.timeout)
self.bits = 0
self.byte = 0
def _cb(self, gpio, level, tick):
if self.last_tick is not None:
if level == pigpio.TIMEOUT:
self.pi.set_watchdog(self.rxgpio, 0) # Switch watchdog off.
if self.in_message:
self._insert(4, not self.last_level)
self.good = 0
self.in_message = False
else:
edge = pigpio.tickDiff(self.last_tick, tick)
if edge < self.min_mics:
self.good = 0
self.in_message = False
elif edge > self.max_mics:
if self.in_message:
self._insert(4, level)
self.good = 0
self.in_message = False
else:
self.good += 1
if self.good > 8:
bitlen = (100 * edge) / self.mics
if bitlen < 140:
bits = 1
elif bitlen < 240:
bits = 2
elif bitlen < 340:
bits = 3
else:
bits = 4
self._insert(bits, level)
self.last_tick = tick
self.last_level = level
def get(self):
"""
Returns the next unread message, or None if none is avaiable.
"""
if len(self.messages):
return self.messages.pop(0)
else:
return None
def ready(self):
"""
Returns True if there is a message available to be read.
"""
return len(self.messages)
def cancel(self):
"""
Cancels the wireless receiver.
"""
if self.cb is not None:
self.cb.cancel()
self.pi.set_watchdog(self.rxgpio, 0)
self.cb = None
if __name__ == "__main__":
import time
import pigpio
import vw
RX=11
TX=25
BPS=2000
pi = pigpio.pi() # Connect to local Pi.
rx = vw.rx(pi, RX, BPS) # Specify Pi, rx gpio, and baud.
tx = vw.tx(pi, TX, BPS) # Specify Pi, tx gpio, and baud.
msg = 0
start = time.time()
while (time.time()-start) < 300:
msg += 1
while not tx.ready():
time.sleep(0.1)
time.sleep(0.2)
tx.put([48, 49, 65, ((msg>>6)&0x3F)+32, (msg&0x3F)+32])
while not tx.ready():
time.sleep(0.1)
time.sleep(0.2)
tx.put("Hello World #{}!".format(msg))
while rx.ready():
print("".join(chr (c) for c in rx.get()))
rx.cancel()
tx.cancel()
pi.stop()

View File

@@ -0,0 +1,2 @@
Class to decode a Wiegand code.

View File

@@ -0,0 +1,135 @@
#!/usr/bin/env python
import pigpio
class decoder:
"""
A class to read Wiegand codes of an arbitrary length.
The code length and value are returned.
EXAMPLE
#!/usr/bin/env python
import time
import pigpio
import wiegand
def callback(bits, code):
print("bits={} code={}".format(bits, code))
pi = pigpio.pi()
w = wiegand.decoder(pi, 14, 15, callback)
time.sleep(300)
w.cancel()
pi.stop()
"""
def __init__(self, pi, gpio_0, gpio_1, callback, bit_timeout=5):
"""
Instantiate with the pi, gpio for 0 (green wire), the gpio for 1
(white wire), the callback function, and the bit timeout in
milliseconds which indicates the end of a code.
The callback is passed the code length in bits and the value.
"""
self.pi = pi
self.gpio_0 = gpio_0
self.gpio_1 = gpio_1
self.callback = callback
self.bit_timeout = bit_timeout
self.in_code = False
self.pi.set_mode(gpio_0, pigpio.INPUT)
self.pi.set_mode(gpio_1, pigpio.INPUT)
self.pi.set_pull_up_down(gpio_0, pigpio.PUD_UP)
self.pi.set_pull_up_down(gpio_1, pigpio.PUD_UP)
self.cb_0 = self.pi.callback(gpio_0, pigpio.FALLING_EDGE, self._cb)
self.cb_1 = self.pi.callback(gpio_1, pigpio.FALLING_EDGE, self._cb)
def _cb(self, gpio, level, tick):
"""
Accumulate bits until both gpios 0 and 1 timeout.
"""
if level < pigpio.TIMEOUT:
if self.in_code == False:
self.bits = 1
self.num = 0
self.in_code = True
self.code_timeout = 0
self.pi.set_watchdog(self.gpio_0, self.bit_timeout)
self.pi.set_watchdog(self.gpio_1, self.bit_timeout)
else:
self.bits += 1
self.num = self.num << 1
if gpio == self.gpio_0:
self.code_timeout = self.code_timeout & 2 # clear gpio 0 timeout
else:
self.code_timeout = self.code_timeout & 1 # clear gpio 1 timeout
self.num = self.num | 1
else:
if self.in_code:
if gpio == self.gpio_0:
self.code_timeout = self.code_timeout | 1 # timeout gpio 0
else:
self.code_timeout = self.code_timeout | 2 # timeout gpio 1
if self.code_timeout == 3: # both gpios timed out
self.pi.set_watchdog(self.gpio_0, 0)
self.pi.set_watchdog(self.gpio_1, 0)
self.in_code = False
self.callback(self.bits, self.num)
def cancel(self):
"""
Cancel the Wiegand decoder.
"""
self.cb_0.cancel()
self.cb_1.cancel()
if __name__ == "__main__":
import time
import pigpio
import wiegand
def callback(bits, value):
print("bits={} value={}".format(bits, value))
pi = pigpio.pi()
w = wiegand.decoder(pi, 14, 15, callback)
time.sleep(300)
w.cancel()
pi.stop()

View File

@@ -0,0 +1,7 @@
This bash script tests the user gpios.
Everything should be disconnected from the gpios.
The pigpio daemon must be running (sudo pigpiod).
Video at http://youtu.be/sCJFLKWaxHo

View File

@@ -0,0 +1,126 @@
#!/bin/bash
skipped=""
tested=""
failed=""
usage()
{
cat <<EOF
This program checks the Pi's (user) gpios.
The program reads and writes all the gpios. Make sure NOTHING
is connected to the gpios during this test.
The program uses the pigpio daemon which must be running.
To start the daemon use the command sudo pigpiod.
Press the ENTER key to continue or ctrl-C to abort...
EOF
read a
}
restore_mode()
{
# $1 gpio
# $2 mode
case "$2" in
0) m="r" ;;
1) m="w" ;;
2) m="5" ;;
3) m="4" ;;
4) m="0" ;;
5) m="1" ;;
6) m="2" ;;
7) m="3" ;;
*)
echo "invalid mode $2 for gpio $1"
exit 1
esac
$(pigs m $1 $m)
}
check_gpio()
{
# $1 gpio
# $2 i2c
m=$(pigs mg $1) # save mode
L=$(pigs r $1) # save level
s=$(pigs m $1 w)
if [[ $s = "" ]]
then
f=0
tested+="$1 "
# write mode tests
$(pigs w $1 0)
r=$(pigs r $1)
if [[ $r -ne 0 ]]; then f=1; echo "Write 0 to gpio $1 failed."; fi
$(pigs w $1 1)
r=$(pigs r $1)
if [[ $r -ne 1 ]]; then f=1; echo "Write 1 to gpio $1 failed."; fi
# read mode tests using pull-ups and pull-downs
$(pigs m $1 r)
if [[ $2 -eq 0 ]]
then
$(pigs pud $1 d)
r=$(pigs r $1)
if [[ $r -ne 0 ]]; then f=1; echo "Pull down on gpio $1 failed."; fi
fi
$(pigs pud $1 u)
r=$(pigs r $1)
if [[ $r -ne 1 ]]; then f=1; echo "Pull up on gpio $1 failed."; fi
$(pigs pud $1 o) # switch pull-ups/downs off
$(pigs w $1 $L) # restore original level
restore_mode $1 $m # restore original mode
if [[ $f -ne 0 ]]; then failed+="$1 "; fi
else
skipped+="$1 "
fi
} 2>/dev/null
usage
v=$(pigs hwver)
if [[ $v < 0 ]]
then
echo "The pigpio daemon wasn't found. Did you sudo pigpiod?"
exit
fi
echo "Testing..."
for ((i=0; i<4; i++)) do check_gpio $i 1; done
for ((i=4; i<16; i++)) do check_gpio $i 0; done
if [[ $v -ge 16 ]];
then
check_gpio 16 0
else
skipped+="16 "
fi
for ((i=17; i<28; i++)) do check_gpio $i 0; done
for ((i=28; i<30; i++)) do check_gpio $i 1; done
for ((i=30; i<32; i++)) do check_gpio $i 0; done
if [[ $failed = "" ]]; then failed="None"; fi
echo "Skipped non-user gpios: $skipped"
echo "Tested user gpios: $tested"
echo "Failed user gpios: $failed"