diff --git a/EXAMPLES/CPP/IR_RECEIVER/README b/EXAMPLES/CPP/IR_RECEIVER/README new file mode 100644 index 0000000..ccb0165 --- /dev/null +++ b/EXAMPLES/CPP/IR_RECEIVER/README @@ -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. + diff --git a/EXAMPLES/CPP/IR_RECEIVER/ir_hasher.cpp b/EXAMPLES/CPP/IR_RECEIVER/ir_hasher.cpp new file mode 100644 index 0000000..d2d01fd --- /dev/null +++ b/EXAMPLES/CPP/IR_RECEIVER/ir_hasher.cpp @@ -0,0 +1,91 @@ +#include + +#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); +} + diff --git a/EXAMPLES/CPP/IR_RECEIVER/ir_hasher.hpp b/EXAMPLES/CPP/IR_RECEIVER/ir_hasher.hpp new file mode 100644 index 0000000..2e9322a --- /dev/null +++ b/EXAMPLES/CPP/IR_RECEIVER/ir_hasher.hpp @@ -0,0 +1,44 @@ +#ifndef IR_RX_HASHER_HPP +#define IR_RX_HASHER_HPP + +#include + +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 + diff --git a/EXAMPLES/CPP/IR_RECEIVER/test_ir_hasher.cpp b/EXAMPLES/CPP/IR_RECEIVER/test_ir_hasher.cpp new file mode 100644 index 0000000..a852220 --- /dev/null +++ b/EXAMPLES/CPP/IR_RECEIVER/test_ir_hasher.cpp @@ -0,0 +1,44 @@ +#include + +#include + +#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); + } +} + diff --git a/EXAMPLES/CPP/ROTARY_ENCODER/README b/EXAMPLES/CPP/ROTARY_ENCODER/README new file mode 100644 index 0000000..f9e01cb --- /dev/null +++ b/EXAMPLES/CPP/ROTARY_ENCODER/README @@ -0,0 +1,4 @@ +Class to decode a mechanical rotary encoder. + +Follow the instructions in the test file to build and run. + diff --git a/EXAMPLES/CPP/ROTARY_ENCODER/rotary_encoder.cpp b/EXAMPLES/CPP/ROTARY_ENCODER/rotary_encoder.cpp new file mode 100644 index 0000000..f0b6fb7 --- /dev/null +++ b/EXAMPLES/CPP/ROTARY_ENCODER/rotary_encoder.cpp @@ -0,0 +1,84 @@ +#include + +#include + +#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); +} + diff --git a/EXAMPLES/CPP/ROTARY_ENCODER/rotary_encoder.hpp b/EXAMPLES/CPP/ROTARY_ENCODER/rotary_encoder.hpp new file mode 100644 index 0000000..e2ef8c0 --- /dev/null +++ b/EXAMPLES/CPP/ROTARY_ENCODER/rotary_encoder.hpp @@ -0,0 +1,35 @@ +#ifndef ROTARY_ENCODER_HPP +#define ROTARY_ENCODER_HPP + +#include + +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 diff --git a/EXAMPLES/CPP/ROTARY_ENCODER/test_rotary_encoder.cpp b/EXAMPLES/CPP/ROTARY_ENCODER/test_rotary_encoder.cpp new file mode 100644 index 0000000..f1d6a59 --- /dev/null +++ b/EXAMPLES/CPP/ROTARY_ENCODER/test_rotary_encoder.cpp @@ -0,0 +1,45 @@ +#include + +#include + +#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(); +} + diff --git a/EXAMPLES/CPP/WIEGAND_CODE/README b/EXAMPLES/CPP/WIEGAND_CODE/README new file mode 100644 index 0000000..28079b4 --- /dev/null +++ b/EXAMPLES/CPP/WIEGAND_CODE/README @@ -0,0 +1,3 @@ +Class to decode a Wiegand code. + +Follow the instructions in the test file to build and run. diff --git a/EXAMPLES/CPP/WIEGAND_CODE/test_wiegand.cpp b/EXAMPLES/CPP/WIEGAND_CODE/test_wiegand.cpp new file mode 100644 index 0000000..ca225a4 --- /dev/null +++ b/EXAMPLES/CPP/WIEGAND_CODE/test_wiegand.cpp @@ -0,0 +1,40 @@ +#include + +#include + +#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(); +} + diff --git a/EXAMPLES/CPP/WIEGAND_CODE/wiegand.cpp b/EXAMPLES/CPP/WIEGAND_CODE/wiegand.cpp new file mode 100644 index 0000000..598b76d --- /dev/null +++ b/EXAMPLES/CPP/WIEGAND_CODE/wiegand.cpp @@ -0,0 +1,116 @@ +#include + +#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); +} + diff --git a/EXAMPLES/CPP/WIEGAND_CODE/wiegand.hpp b/EXAMPLES/CPP/WIEGAND_CODE/wiegand.hpp new file mode 100644 index 0000000..4b5215c --- /dev/null +++ b/EXAMPLES/CPP/WIEGAND_CODE/wiegand.hpp @@ -0,0 +1,43 @@ +#ifndef WIEGAND_HPP +#define WIEGAND_HPP + +#include + +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 +