Skip to content

Instantly share code, notes, and snippets.

@mpflaga
Last active July 2, 2018 17:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpflaga/d2f9a63ea7544d2b976f8665c97a4d3c to your computer and use it in GitHub Desktop.
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
# 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