Last active
July 2, 2018 17:39
-
-
Save mpflaga/d2f9a63ea7544d2b976f8665c97a4d3c to your computer and use it in GitHub Desktop.
IrSonicWand - Device to learn and simulate up to 4 MagiQuest wands, along with sound effects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# schematic - https://drive.google.com/open?id=1SUm5G0ulMR-NaRY3s6r-mbK_2PaoASEd | |
#define LENGTH_OF_ARRAY(x) ((sizeof(x)/sizeof(x[0]))) | |
// define pin locations | |
#define IR_RECV_PIN 2 | |
#define IR_LED_TX_PIN 3 | |
#define BUTTON_NOCAP_PIN 4 | |
#define BUTTON_BLK_PIN 5 | |
#define BUTTON_BLU_PIN 6 | |
#define BUTTON_GRN_PIN 7 | |
#define BUTTON_YLW_PIN 8 | |
#define BUTTON_RED_PIN 9 | |
#define LED_GRN_PIN 10 | |
#define PIEZO_PIN 11 | |
#define POT_ROTARY_PIN A5 | |
#define POT_LINEAR_PIN A4 | |
// Button Types | |
#define NOTHING 0 | |
#define LEARN_WAND_NUMBER 1 | |
#define WAND_BUTTON 2 | |
// Matrix of the different Button and types. | |
uint8_t pins[][2] = {// position, Type | |
{BUTTON_BLK_PIN, NOTHING}, | |
{BUTTON_NOCAP_PIN, LEARN_WAND_NUMBER}, | |
{BUTTON_BLU_PIN, WAND_BUTTON}, | |
{BUTTON_GRN_PIN, WAND_BUTTON}, | |
{BUTTON_YLW_PIN, WAND_BUTTON}, | |
{BUTTON_RED_PIN, WAND_BUTTON} | |
}; | |
#include <Bounce2.h> | |
Bounce debouncer[LENGTH_OF_ARRAY(pins)] = Bounce(); | |
#include <IRremote.h> // https://github.com/mpflaga/Arduino-IRremote.git | |
IRrecv irrecv(IR_RECV_PIN); | |
IRsend irsend; | |
decode_results results; | |
class Timer | |
{ | |
private: | |
unsigned long lastTime; // the latest time had doing Func( ) | |
void (*Func) (void); | |
void (*backupFunc) (void); // backup Func for start( ) | |
public: | |
unsigned long T_int; // interval in ms, can be set as in us (optional) | |
Timer(void (*userFunc)(), unsigned long T, bool isTinUs = false) | |
{ | |
Func = backupFunc = userFunc; // save the function pointer | |
T_int = T * 1000; | |
if (isTinUs) | |
T_int = T; // T is in micro second as per unit | |
lastTime = 0; // initialization should be in constructor | |
} | |
void setInterval(unsigned long T, bool isTinUs = false) | |
{ | |
T_int = T * 1000; | |
if (isTinUs) | |
T_int = T; // T is in micro second as per unit | |
} // setInterval | |
void check() | |
{ | |
if ( Func == 0 ) | |
return; // no function pointer | |
unsigned long _micros = micros(); // Local variable could be in Register | |
if (_micros - lastTime >= T_int) | |
{ | |
lastTime = _micros; // latest time doing Func( ) | |
Func(); | |
} | |
}// check | |
void start( ) // first run of doing Func( ) | |
{ | |
if (Func == 0) | |
Func = backupFunc; // restore after .stop( ) | |
lastTime = micros(); // latest time doing Func( ) | |
Func(); // First run | |
} // start | |
void stop( ) // stop the scheduled Func (userFunc) | |
{ | |
Func = 0; // set as NULL | |
} // stop | |
#define _MS_ false /*Milliseconds*/ | |
#define _US_ true /*Microseconds*/ | |
}; | |
void toggleGrnLED() | |
{ | |
digitalWrite(LED_GRN_PIN, !digitalRead(LED_GRN_PIN)); | |
//Serial.print("Uptime (s): "); Serial.println(millis() / 1000); | |
} // end of toggleGrnLED() | |
Timer BlinkGRN(toggleGrnLED, 250);; | |
#define OFFSET 20 | |
bool en_whirly = 0; | |
int16_t whirly_freq = 2000; | |
int8_t whirly_offset = +OFFSET; | |
bool whirly_direction = 0; | |
void whirly_update() | |
{ | |
// noTone(PIEZO_PIN); | |
if (whirly_freq > 2100) { | |
whirly_offset = -OFFSET; | |
} | |
else if (whirly_freq < 2000) { | |
whirly_offset = +OFFSET; | |
} | |
whirly_freq += whirly_offset; | |
if (en_whirly) { | |
tone(PIEZO_PIN,whirly_freq + analogRead(POT_LINEAR_PIN)); | |
} | |
else { | |
noTone(PIEZO_PIN); | |
} | |
} | |
Timer whirly(whirly_update, 25);; | |
#include <EEPROM.h> | |
#define CONFIG_START 32 | |
#define CONFIG_VERSION "ls1" | |
typedef struct StoreStruct_t { | |
uint8_t mute; | |
uint32_t wandSerialNumber[LENGTH_OF_ARRAY(debouncer)]; | |
uint16_t wandMagnitude[LENGTH_OF_ARRAY(debouncer)]; | |
char version_of_program[4]; // it is the last variable of the | |
} StoreStruct_t; | |
StoreStruct_t EEPROM_configuration = {0}; | |
int8_t learnMode = 0; // State Machine : 0 Not Learning, -1 pending Channel assignment, >0 pending learn of specified channel. | |
// the setup function runs once when you press reset or power the board | |
void setup() { | |
// initialize digital pin LED_BUILTIN as an output. | |
pinMode(LED_BUILTIN, OUTPUT); | |
pinMode(LED_GRN_PIN, OUTPUT); | |
//pinMode(IR_LED_TX_PIN, OUTPUT); | |
// initialize digital pin Button Inputs and Debouncer | |
for (uint8_t pinPosition = 0; pinPosition < LENGTH_OF_ARRAY(debouncer); pinPosition++) { | |
pinMode(pins[pinPosition][0], INPUT_PULLUP); | |
debouncer[pinPosition].attach(pins[pinPosition][0]); | |
debouncer[pinPosition].interval(5); // interval in ms | |
} | |
loadConfig(); | |
BlinkGRN.stop(); | |
irrecv.enableIRIn(); // Start the receiver | |
Serial.begin(115200); | |
Serial.println("IrSonicWand has Started."); | |
Serial.println("Initial State:"); | |
dumpInfo(); | |
// initial Bootup Beep | |
tone(PIEZO_PIN,1471); | |
delay(1000/8); | |
noTone(PIEZO_PIN); | |
whirly.stop(); | |
en_whirly = 0; | |
whirly.setInterval(5); | |
whirly.start(); | |
} // end of setup() | |
// the loop function runs over and over again forever | |
void loop() { | |
// This loop is trying to be None Blocking. | |
BlinkGRN.check(); | |
whirly.check(); | |
int wobble = map(analogRead(POT_ROTARY_PIN), 0, 1023, 1, 100); | |
whirly.setInterval(wobble); | |
// scan the debounce of each button | |
for (uint8_t pinPosition = 0; pinPosition < LENGTH_OF_ARRAY(debouncer); pinPosition++) { | |
if (debouncer[pinPosition].update()) { // check each individual button | |
Serial.print("Debouncer(") ; Serial.print(pinPosition); Serial.print(")="); Serial.println(debouncer[pinPosition].read()); | |
if ((debouncer[2].read() == LOW) && (debouncer[3].read() == LOW) && (debouncer[4].read() == LOW) && (debouncer[5].read() == LOW)) { | |
// if ALL the buttons then CLEAR the Config and EEPROM | |
Serial.print("Clearning EEPROM Wand ID's"); | |
for (uint8_t count = 0; count < LENGTH_OF_ARRAY(EEPROM_configuration.wandSerialNumber); count++) { | |
EEPROM_configuration.wandSerialNumber[count] = 0; | |
EEPROM_configuration.wandMagnitude[count] = 0; | |
} | |
saveConfig(); | |
loadConfig(); | |
dumpInfo(); | |
} | |
else if (debouncer[pinPosition].read() == LOW) { // if button being depressed | |
en_whirly = 1; | |
Serial.print("Tone ON!") ; | |
if (pins[pinPosition][1] == LEARN_WAND_NUMBER) { // and if button type is: enabled as a Learning Mode button | |
// then Prime to set the wand to Learn | |
if (!learnMode) { // and if not in learning mode | |
learnMode = -1 ; // then set learning mode to learn which wand number | |
BlinkGRN.setInterval(250); | |
BlinkGRN.start(); // at 250ms; // along with enable slow Green Blink | |
} | |
else { // otherwise disable and turn off Green Blink | |
learnMode = 0 ; | |
BlinkGRN.stop(); | |
digitalWrite(LED_GRN_PIN, LOW); | |
Serial.println("Nothing Learned "); | |
} | |
Serial.print("Learn Wand Number initialized to "); Serial.println(learnMode); | |
} | |
else if (pins[pinPosition][1] == WAND_BUTTON) { // else if Button Type is Wand Selection Buttons | |
if (learnMode == pinPosition) { // and if same pin so turn off | |
learnMode = 0 ; | |
BlinkGRN.stop(); | |
digitalWrite(LED_GRN_PIN, LOW); | |
Serial.println("Nothing Learned "); | |
Serial.print("Learn Wand Number cleared to "); Serial.println(learnMode); | |
} | |
else if (learnMode) { // else and if ready to learn wand number | |
// set the Wand to Learn and change blink rate | |
learnMode = pinPosition; | |
Serial.print("Change Learn Mode to "); Serial.println(learnMode); | |
BlinkGRN.setInterval(125); | |
BlinkGRN.start(); // at 125ms | |
Serial.print("Learn Wand Number changed to "); Serial.println(learnMode); | |
irrecv.resume(); // Receive the next value | |
} | |
else { // Transmit a Wands IR signature | |
noTone(PIEZO_PIN); // interfers with irsend. | |
en_whirly = 0; | |
Serial.print("Tone OFF!") ; | |
uint16_t magnitude = map(analogRead(POT_LINEAR_PIN), 0, 1023, 0, 65535); | |
Serial.print("Wand Number "); Serial.print(pinPosition); | |
Serial.print(" IR Sending 0x"); Serial.print(EEPROM_configuration.wandSerialNumber[pinPosition], HEX); | |
Serial.print(" Magnitude 0x"); Serial.println(magnitude, HEX); | |
digitalWrite(LED_GRN_PIN, HIGH); | |
irsend.sendMagiQuest(EEPROM_configuration.wandSerialNumber[pinPosition], magnitude); | |
digitalWrite(LED_GRN_PIN, LOW); | |
en_whirly = 1; | |
Serial.print("Tone ON!") ; | |
} | |
} | |
} | |
else if (debouncer[pinPosition].read() == HIGH) { | |
en_whirly = 0; | |
Serial.print("Tone OFF!") ; | |
} | |
} | |
} | |
// learn the IR code from the WAND if primed. | |
if (learnMode > 0) { | |
if (irrecv.decode(&results)) { | |
if (results.decode_type == MAGIQUEST) { | |
Serial.print("Rx'd wand_id=0x"); Serial.println(results.value, HEX); | |
Serial.print("Rx'd magiquestMagnitude=0x"); Serial.println(results.magiquestMagnitude, HEX); | |
BlinkGRN.stop(); | |
digitalWrite(LED_GRN_PIN, LOW); | |
EEPROM_configuration.wandSerialNumber[learnMode] = results.value; | |
EEPROM_configuration.wandMagnitude[learnMode] = results.magiquestMagnitude; | |
Serial.print("Learned - wand_id=0x"); | |
Serial.println(EEPROM_configuration.wandSerialNumber[learnMode], HEX); | |
saveConfig(); | |
loadConfig(); | |
Serial.print("EE Read Back - wand_id=0x"); | |
Serial.print(" wand_id=0x"); Serial.println(EEPROM_configuration.wandSerialNumber[learnMode], HEX); | |
Serial.print(" magiquestMagnitude=0x"); Serial.println(EEPROM_configuration.wandMagnitude[learnMode], HEX); | |
Serial.println(); | |
learnMode = 0; // disable learning Wand | |
} | |
} | |
} | |
} // end of loop() | |
void loadConfig() { | |
// To make sure there are EEPROM_configuration, and they are YOURS! | |
// If nothing is found it will use the default EEPROM_configuration. | |
if ( //EEPROM.read(CONFIG_START + sizeof(EEPROM_configuration) - 1) == EEPROM_configuration.version_of_program[3] // this is '\0' | |
EEPROM.read(CONFIG_START + sizeof(EEPROM_configuration) - 2) == EEPROM_configuration.version_of_program[2] && | |
EEPROM.read(CONFIG_START + sizeof(EEPROM_configuration) - 3) == EEPROM_configuration.version_of_program[1] && | |
EEPROM.read(CONFIG_START + sizeof(EEPROM_configuration) - 4) == EEPROM_configuration.version_of_program[0]) { // reads EEPROM_configuration from EEPROM | |
Serial.println("EEPROM being loaded."); | |
for (unsigned int t = 0; t < sizeof(EEPROM_configuration); t++) | |
*((char*)&EEPROM_configuration + t) = EEPROM.read(CONFIG_START + t); | |
} else { | |
// EEPROM_configuration aren't valid! will overwrite with default EEPROM_configuration | |
Serial.println("EEPROM being defaulted."); | |
saveConfig(); | |
} | |
} // end loadConfig() | |
void saveConfig() { | |
Serial.println("EEPROM being saved."); | |
for (unsigned int t = 0; t < sizeof(EEPROM_configuration); t++) | |
{ // writes to EEPROM | |
EEPROM.write(CONFIG_START + t, *((char*)&EEPROM_configuration + t)); | |
// and verifies the data | |
if (EEPROM.read(CONFIG_START + t) != *((char*)&EEPROM_configuration + t)) { | |
// error writing to EEPROM | |
} | |
} | |
} // end saveConfig() | |
void dumpInfo() { | |
Serial.println("EEPROM Configuration:"); | |
Serial.print("MUTE = "); Serial.println(EEPROM_configuration.mute); | |
for (uint8_t count = 0; count < LENGTH_OF_ARRAY(EEPROM_configuration.wandSerialNumber); count++) { | |
Serial.print("wandSerialNumber["); Serial.print(count); Serial.print("] = "); Serial.println(EEPROM_configuration.wandSerialNumber[count], HEX); | |
Serial.print("wandMagnitude["); Serial.print(count); Serial.print("] = "); Serial.println(EEPROM_configuration.wandMagnitude[count], HEX); | |
} | |
Serial.print("version_of_program = "); Serial.println(EEPROM_configuration.version_of_program); | |
} // dumpInfo() | |
uint8_t mute; | |
int32_t wandSerialNumber[4]; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment