--- /dev/null
+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.
+
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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
+
--- /dev/null
+#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
+
+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);
+ }
+}
+
--- /dev/null
+Class to decode a mechanical rotary encoder.
+
+Follow the instructions in the test file to build and run.
+
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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
--- /dev/null
+#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();
+}
+
--- /dev/null
+Class to decode a Wiegand code.
+
+Follow the instructions in the test file to build and run.
--- /dev/null
+#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();
+}
+
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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
+