/* Copyright (c) 2014, "John Brzustowski" < jbrzusto is at the domain of fastmail in the tld of fm > All rights reserved. Based on: action_timer.ino which has this notice: Copyright (c) 2013, "David Hilton" All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ #define FREERAM #include #include #include #undef LED // Constants #define ACC_ADDRESS 0x4C #define ACC_REG_XOUT 0 #define ACC_REG_YOUT 1 #define ACC_REG_ZOUT 2 #define ACC_REG_TILT 3 #define ACC_REG_INTS 6 #define ACC_REG_MODE 7 // Pin assignments #define DPIN_RLED_SW 2 #define DPIN_GLED 5 #define DPIN_PWR 8 #define DPIN_DRV_MODE 9 #define DPIN_DRV_EN 10 hexbright hb; char waiting_for_button_release = 0; const int ON_LIGHT_LEVEL = 1000; const int IR_CLOCK_USEC = 1276; // real clock is 1.39166 msec, but we need to account for delay. const long CMD_RETRY_INT_MSEC = 100; // taps cycle between these modes const int MODE_OFF = 0; const int MODE_FLASHLIGHT = 1; const int MODE_ACTIVATION = 2; const int MODE_DEACTIVATION = 3; static int flashlight_level = 0; int mode = MODE_OFF; unsigned long lastGLEDchange = 0; bool GLEDstate = false; unsigned long tapTime; void setup() { mode = MODE_FLASHLIGHT; hb.init_hardware(); // Configure accelerometer byte config[] = { ACC_REG_INTS, // First register (see next line) 0xE4, // Interrupts: shakes, taps 0x00, // Mode: not enabled yet 0x00, // Sample rate: 120 Hz 0x1b, // Tap threshold 0x05 // Tap debounce samples }; Wire.beginTransmission(ACC_ADDRESS); Wire.write(config, sizeof(config)); Wire.endTransmission(); // Enable accelerometer byte enable[] = {ACC_REG_MODE, 0x01}; // Mode: active! Wire.beginTransmission(ACC_ADDRESS); Wire.write(enable, sizeof(enable)); Wire.endTransmission(); tapTime = 0; pinMode(DPIN_PWR, OUTPUT); digitalWrite(DPIN_PWR, HIGH); pinMode(DPIN_RLED_SW, INPUT); delay(250); } // each slot in the following table corresponds to a 2.786 ms // window in time. If the code is 1, then the output level // toggles at the start of the window and stays constant // over the window. If the code is 0, then the output level // toggles at the start of the window, then again 1/2 way through // the window. const char message[2][59] = { {1, 1, 0, 1, 1, 1, 0, 1, // ACTIVATION MESSAGE 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 0, 1, 1, 1, // DEACTIVATION MESSAGE 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; /* Mode Function ==== ======== 0 power off. Red + Green OFF 1 flashlight with adjustable brightness. Red + Green OFF 2 activate Lotek tag Green LED solid 3 deactivate Lotek tag. Red LED solid Mode Event Action ==== ===== ====== 0 press switch to mode 1 at previous brightness (default to 50%) 1 hold ramp slowly through brightness levels, wrapping to dimmest 1 press switch to mode 0 1 tap switch to mode 2 2 press send tag activation code 2 hold > 3s switch to mode 0 2 tap switch to mode 3 3 press send tag deactivation code 3 tap switch to mode 1 3 hold > 3s switch to mode 0 */ void greenShowMode() { /* if in flashlight mode, turn off GLED if in activate mode, turn on GLED if in deactivate mode, blink GLED */ switch(mode) { case MODE_FLASHLIGHT: greenOff(); break; case MODE_ACTIVATION: greenOn(); break; case MODE_DEACTIVATION: if (millis() - lastGLEDchange > 150) { lastGLEDchange = millis(); if (GLEDstate) greenOff(); else greenOn(); GLEDstate = ! GLEDstate; break; }; } } void greenOn() { analogWrite(DPIN_GLED, 0xff); } void greenOff() { digitalWriteFast(DPIN_GLED, LOW); } void loop() { hb.update(); if (mode == MODE_OFF) { if(hb.button_just_released() && waiting_for_button_release) { waiting_for_button_release = 0; mode = MODE_FLASHLIGHT; hb.set_light_level(flashlight_level); } return; } greenShowMode(); // Poll the accelerometer Wire.beginTransmission(ACC_ADDRESS); Wire.write(ACC_REG_TILT); Wire.endTransmission(false); // End, but do not stop! Wire.requestFrom(ACC_ADDRESS, 1); // This one stops. delay(5); if (Wire.read() & 0x20 && millis() - tapTime > 1000) { // Tap detected! tapTime = millis(); // Switch to new mode switch(mode) { case MODE_FLASHLIGHT: hb.set_light(0, 0, NOW); mode = MODE_ACTIVATION; break; case MODE_ACTIVATION: mode = MODE_DEACTIVATION; break; case MODE_DEACTIVATION: mode = MODE_FLASHLIGHT; hb.set_light_level(flashlight_level); } delay(500); return; } int modemap = mode - 2; if (hb.button_just_pressed()) { waiting_for_button_release = 1; } if(hb.button_just_released() && waiting_for_button_release) { waiting_for_button_release = 0; int bpt = hb.button_pressed_time(); if ((mode == MODE_FLASHLIGHT && bpt < 200) || (mode != MODE_FLASHLIGHT && bpt >= 1500)) { // turn off hb.set_light(OFF_LEVEL, OFF_LEVEL, NOW); hb.shutdown(); } else { switch(mode) { case MODE_ACTIVATION: case MODE_DEACTIVATION: // activate/deactivate the tag - try 3 times for (int j = 0; j < 3; ++j) { int level = 0; for (int i = 0; i < 59; ++i) { level = ON_LIGHT_LEVEL - level; hb.set_light_level(level); delayMicroseconds(IR_CLOCK_USEC); // a bit too long if (!message[modemap][i]) level = ON_LIGHT_LEVEL - level; hb.set_light_level(level); delayMicroseconds(IR_CLOCK_USEC); // a bit too long } hb.set_light_level(0); delay(CMD_RETRY_INT_MSEC); // wait 1/10th of a second to retry } break; case MODE_FLASHLIGHT: break; } } } else if (mode == MODE_FLASHLIGHT && hb.button_pressed()) { ++flashlight_level; if (flashlight_level > 1000) flashlight_level = 0; hb.set_light_level(flashlight_level); delayMicroseconds(1000); } }