Hey all, I recently rigged up this ammo counter/chrono/voltmeter/volt alarm gadget to add to my 9yo son's collection of nerf things (yes, he did ask me to put this circuit in his red dot scope). This project is more in line for the custom builders/diyers, especially those adept with Arduino stuff. The pinout is written at the top of the code (please note this is ported 328p specific). This should be more than enough info for those willing/able to make one of these to proceed with success. I hacked up one of my son's red dot scopes to house the circuit; this way it's modular for use with many guns. Although it would actually be easier to build in to a gun if you prefer (usually there's much more room than inside a rail attachment anyways).
What does it do: It constantly measures voltage and warns the shooter of a low battery (long beep + highlighted Voltage value). The low voltage alarm threshold is automatically set (for 2s and 3s lipo only, as coded... I may add more chemistry/cell count options if ppl need). It measures and stores FPS, RPM, and voltage for every dart fired from the clip. After the clip is empty, min/avg/max values are displayed for FPS/RPM/Volts, then tables are scrolled through showing all of the dart data in the order fired (max/mins values are highlighted). The method of timing used for FPS yields +-65nsec precision (~30000 counts for a 100FPS Elite dart... overkill really). More details are written in the comments in the top of the code. Other than that, it's a pretty dumb code that could be improved upon for your specific application (recode it for clip reed sensors, etc...)... and oh yeah, it does also show rounds left. It also does 1-beep&lights and LED when there's one round left... then 3beeps when empty... but what Nerf arduino thing doesn't do that?
How do we get FPS with 1 sensor: We know more or less the dart length, so we know how fast it is from how long it blocks a single sensor. This means variances in dart length go in to speed calculation. This is not a problem with 2 sensor FPS measurement, but the simplified hardware is well worth it as long as we understand we get some relative measurement errors due to dart length. Note that if we are really needing to go 2 sensors, we could wire both sensors to the same pin using a simple circuit, and change the code to interrupt on a pair of falling edges instead (vs a falling then a rising edge). Then a 2 sensor barrel can be used with this code no problem.
Some folks would say I'm not smart for sharing this code here. I personally hope that posting my open source code will result in it being 'borrowed'... hopefully we'll see these ideas make their way in to the current crop of commercially available ammo counters. Those folks making those things probably won't charge extra for these features anyways (takes less time to solder i2c than 7-seg anyways, LOL). That would be IMHO a big plus for Nerfers. I'm not putting time in to make/sell these myself, so please don't bother asking.
Items needed:
16MHz 5V 328p (I used a spare pro-mini)
OPL550 IR photo logic sensor
OP240a IR photo diode + 100ohm 1/4W resistor
128x64 I2C SSD1306 monochrome display
5V regulator (I used a spare LM7805 TO-220 sans heatsink... this circuit won't draw much over 100mA unless beeping (very brief & low duty), so the smaller package will also work fine)
0.1uF & 0.33uF output & input anti-ripple caps for the above regulator
5k6 & 3k3 resistors (10% is OK... you should be calibrating the vbattRatio anyways)
LED + limiting resistor (stay under 20mA since it's pin driven... I hacked the PCB in my son's red dot scope so I could wire in the stock LED instead... this involved cutting a trace and soldering some wires)
5V peizo buzzer (doesn't require tone libraries... 5V in = buzzzzzzz)
3@ momentary NO buttons
A power switch (again, 100mA... I hacked the stock switch on my son's red dot Nerf scope to work as my on/off switch)
Nerf Scope of choice (must obviously be big enough fit everything, and hacked as needed... or I guess you could just put it in a box and velcro it to your gun LOL)
Electrical project tools & sundries (connectors, solder & iron, dremel, hookup wire, hotglue, bits of proto PCB to mount your buttons & regulator, plastic/foam/glue/paint to make it aesthetically pleasing... whatever else you need to git'r'done)
Notes:
1- The 5k6 and 3k3 resistors make a voltage divider which is used to read your main Nerf battery. These values are good for batteries up to 13.4V, which should work for most. If you need more (like 4s), please adjust the values accordingly... vbattRatio will also have to be adjusted to match your divider.
2- A 100ohm current limiting resistor for the photo diode was needed for a 1 1/4" PVC barrel adaptor I made for mega darts. However this may result in a somewhat shorter doide life. If your sensor/diode will be less than 1" apart, 150ohm will work and will last forever.
Oh yeah baby... modular nerftec!!! The Deans plug taps in to the Stryfe's 2s lithium battery, for both power and voltage monitoring (velcro a tiny 2s to the front of the 'scope' for the springers)
Erm, yeah the cat6 guts aren't sehksee, but it's what I had and I wanted my son to be able to use it on several guns (thus not built in to this Stryfe, which would be very sehksee... the Dupont plug in the middle is for 'built in' setups)
Fugly breadboard action... that extra multi-turn poti is there just to simulate low battery alarms for testing/debugging (not installed):
Dry fitting the guts in the scope before cutting and soldering the wires... my favorite part of any project is the dry fit/brainstorming stage:
Shameless blocking splash screen, LOL...
Setup mode after boot, we see live voltage, and can setup clip capacity and dart length...
Fire mode, with live voltage, round counter, 1 round left LED (w/ single beep), 0 rounds left 3 beeps... (LED not on since there's 8 left in the clip for this pic)
I think the format of this screen can be improved upon by putting volts, fps, and rpm on the left, and enlarging the counter font so it reaches the bottom of the screen... todo list.
Empty mode min/avg/max values, and live voltage... (yes this photo was shot before I fixed a typo bug in the avg& max voltages;) )
Empty mode per dart data tables, with highlighted max/min values... yeah using the stock red LED for the 1round left/empty warning wasn't such a great idea (better off mounting a wide angle LED pointed toward the shooter's face, instead of using reflection of the stock LED off the foam paneling as I did)
Some prototype 1-sensor housings, alongside a more traditional 2-sensor housing which I use for faster stuff (& to verify the 1-sensor thing works well)
Here's the open source code (I'm compiling with Arduino 1.8.1):
nerfGunU.zip 12.69KB 271 downloads
#define versionNo "1.U" //Splash screen version /* nerfGun_.ino * by: Kevin Bernas * * Nerftec... deeper down the rabbit hole. * * Note: U=T, with a workaround for input capture interrupts resulting in failure of the * 'last buzzer off' command. Version ID is also #defined for convenience. * * Overview: * A Nerf gun chronograph/computer, which uses an IR emitter/detector and OLED display * to show remaining rounds, voltage, fps, and rpm. This is taken to the next level with * post clip averages and dart data tables with highlighted max and min values. The code * is all non-blocking, uses direct port access (versus digitalRead/Write), and Timer1 * input capture methods, ported for a 16MHz m328 target. An OPL550 IR sensor with an * OP240a IR emitter work well for the applications used by author of this code (100ohm * on the emitter >1" from sensor). The pair are mechanically matched and posess a sub * clock response times, which goes well with the +/-65ns input capture methods used * (with a 550/240 set, any measurement error will stem from dart length variances). The * code is also written for use with a Banggood 0.96" 128x64 i2c monochrome OLED screen * (SSD1306). Other displays and/or targets may be used... just be mindful of your portD * and Timer1 pinouts. * * Operation: * Setup mode: Boots in to setup mode, where the screen guides the user to adjust clip size * and dart length. Upon entering setup, the clip size value is highlighted, and pressing * the right or left buttons will adjust capacity. A short click of select will highlight * the dart length, which can then be adjusted with the R/L buttons. Holding the R/L buttons * in dart length mode will jump between Mega and Elite defined lengths as a convenience. * Live voltage is displayed on the bottom. Holding Select will go to fire mode. * * Fire mode: When entering fire mode, the LED turns off, and live voltage, FPS, and RPM * are displayed under remaining rounds. When there is one round remaining, the LED will * turn on and the beeper will emit 1 short beep. When the clip is empty, the beeper emits * 3 short beeps and the code enters "Empty Mode". * * Empty mode: Upon entering empty mode, a statistics page is displayed showing Min, Avg, * and Max values for FPS, RPM, and Voltage. Live voltage is also show on the bottom of the * page. After a few seconds, dart data tables are shown. The tables list round #, volts, * FPS, and RPM for all rounds fired, with highlighted max and min values. Hitting the * select button returns to fire mode using the previously selected clip size and dart * length. Either the right or left button will return to setup mode. * * Low voltage warning: After boot up, voltage is checked, and the low voltage threshold is * set to 6.4 or 9.6V, depending the the inferred source (2s lithium or 3s lithium). On * screens where live voltage is available, the value will be highlighted when it drops * below the threshold. In all modes, if the battery is low, the beeper will emit one long * beep. If voltage remains low, the long beep will repeat after the defined warning delay. * * Physical wiring: * Pin________Connected to_________________________ * * A0 Vbatt divider (default: 5k6:3k3 divider for 13.4V max) * A4 OLED SDA * A5 OLED SCL * D3 Select button * D4 Right button * D5 Left button * D6 LED indicator * D7 5V Buzzer * D8 IR Sensor signal * * ***D0-8 are ported for m328 (Uno, etc)... * Please verify/edit to match portD & Timer1 pinouts for other targets * */ #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <avr/pgmspace.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); //Vin = ADCoutput / vbattRatio (theoretically, vbattRatio = 1023*R2/(5*(R1+R2)) //calibrate vbattRatio... first upload with vbattRatio = 100.0. Then using a DMM, //find the calibrated value: vbattRatio = 100.0 * [DMM Volts]/[Displayed Volts] const float vbattRatio = 75.7; //theoretically 83.2 for a 5k6:3k3 divider unsigned int dartLength = 2850; //Default = elite darts [1/1000 inch] uint8_t clipSize = 10; //Default clip capacity #define maxClipSize 35 //Controls size of dart data arrays (uses up memory!!!) #define vbattPin A0 //vbatt divider (A0) #define vbattLow2s 7.0 //Low battery voltage threshold for 2s lipo #define vbattLow3s 10.5 //Low battery threshold for 3s lipo #define lipo2sHigh 8.4 //Voltage above which vbattLow = vbattLow3s #define buttonDelayNorm 250 //mSec before registering a button as held (also a debounce) #define buttonDelayShort 80 //mSec between held button repeats #define screenDelayLong 4000 //mSec to display a detailed density screen #define screenDelayMed 3000 //mSec to display a standard density screen #define voltCheckDelay 500 //mSec between live voltage updates #define beepDelayShort 50 //mSec for short beeps #define beepDelayLong 1000 //mSec for long beeps (low voltage) #define warningDelay 10000 //msec between low voltage beeps #define dartLengthMega 3700 //Jumps to when below this value with long press #define dartLengthElite 2850 //Jumps to when above this value with long press #define splashScreen //Uncomment to enable splash screen (~15% of progmem) //Globals-------------------------------------- bool lowBattWarning = 0; //Low battery alarm state, off bool beepState = 0; //Beeper state, off bool longSelect = 0; //Saves previous status of select button for long/short presses bool longLeft = 0; //Saves previous status of left button for long/short presses bool longRight = 0; //Saves previous status of right button for long/short presses uint8_t mode = 0; //Mode switch, start in setup mode (0) uint8_t setupItem = 0; //Switch for selected items in setup mode (0) uint8_t currentScreen = 0; //Index for switching pages in empty mode uint8_t currentRound = 0; //Index for rotating data tables in empty mode uint8_t remaining = 0; //Remaining rounds in fire mode uint8_t beepCount = 0; //Beep counter uint8_t fastestRound = 0; //Saves max FPS round # uint8_t slowestRound = 0; //... min FPS... uint8_t fastestRate = 0; //... max RPM... uint8_t slowestRate = 0; //... min RPM... uint8_t highestVolt = 0; //... max Volt... uint8_t lowestVolt = 0; //... min Volt... unsigned int dartSpeedData[maxClipSize]; //Dart data storage unsigned int dartRPMdata[maxClipSize]; //Dart data storage unsigned int dartVoltData[maxClipSize]; //Dart data storage unsigned int liveVoltage = 0; //Raw ADC reading unsigned int vBattLow = 0; //Holder for low voltage threshold (raw adc) unsigned long previousWarning = 0; //Time saver for low battery warnings unsigned long previousVoltCheck = 0; //Time saver for battery checks unsigned long previousScreenTime = 0; //Time saver for empty screens unsigned long screenDelay = 0; //Time to show automated pages (empty mode) unsigned long dartTime = 0; //Saves micros() for RPM unsigned long previousDartTime = 0; //Saves micros() for RPM unsigned long clipStartTime = 0; //Saves micros() for average RPM unsigned long beepTime = 0; //Beep time saver unsigned long beepDelay = 0; //Beep length saver unsigned long buttonDelay = buttonDelayNorm; unsigned long previousButtonTime = 0; //Time saver for buttons volatile boolean first; //Flag for ISR modes (startTime or finishTime) volatile boolean triggered; //Flag for ISR to ignore additional interrupts until the code saves them volatile unsigned long overflowCount; //Timer1 overflow count volatile unsigned long startTime; //Timer1 count when the dart head blocks the sensor volatile unsigned long finishTime; //Timer1 count when the dart tail clears the sensor //SETUP---------------------------------------- void setup() { analogReference(DEFAULT); //5V reference for the default divider & input range //Atmega328 pin config & init (there may be more work below... ;-) ) DDRD = DDRD & 0b11000111; //PortD- pins 3-5 as inputs (buttons) DDRD = DDRD | 0b11000000; //PortD- pins 6&7 as outputs (led and beeper) PORTD= PORTD & 0b01111111; //turn off beeper PORTD= PORTD | 0b01111000; //pins 3-5 activate pullups (buttons), and turn on pin 7 (LED) display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Banggood Monochrome i2c OLED #ifdef splashScreen //display.display(); //Adafruit props... //delay(1000); #define h 64 #define w 128 //Nerftec logo for 128x64 static const unsigned char PROGMEM nerftec_bmp[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0x83, 0x87, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xf0, 0x18, 0xe7, 0xf0, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xfe, 0x67, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0x6f, 0xf0, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0x2f, 0xe0, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x89, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0x1e, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xf0, 0x09, 0xff, 0xc0, 0xff, 0x1f, 0xe3, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xe7, 0xfe, 0x07, 0xc9, 0xfe, 0x00, 0xfe, 0x1f, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe7, 0xc0, 0xff, 0x8d, 0xfc, 0x1e, 0xfe, 0x1f, 0xff, 0xf8, 0x00, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xe0, 0x0f, 0xff, 0x9b, 0xfc, 0xfd, 0xfe, 0x3f, 0xff, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xc1, 0xff, 0xfe, 0x1b, 0xfc, 0xf9, 0xfc, 0x3f, 0xf0, 0x00, 0x00, 0xff, 0xff, 0x03, 0xff, 0x3f, 0xc3, 0xff, 0xc0, 0x33, 0xf9, 0xe3, 0xf8, 0x3f, 0xc0, 0x00, 0x01, 0xff, 0xf0, 0xf9, 0xff, 0x7f, 0xc3, 0xfc, 0x00, 0xf7, 0xf8, 0x0f, 0xf1, 0x7f, 0x80, 0x00, 0x01, 0xff, 0x8f, 0xfd, 0xff, 0x7f, 0x93, 0xf8, 0x1f, 0xf7, 0xf8, 0xff, 0xe3, 0x7f, 0x1c, 0x00, 0x01, 0xff, 0x3f, 0xfc, 0xff, 0x7f, 0x97, 0xf9, 0xff, 0xe7, 0xff, 0xff, 0x86, 0x7f, 0x38, 0x00, 0x03, 0xff, 0x1f, 0xfe, 0xfe, 0xff, 0x97, 0xf9, 0xf0, 0x6f, 0xff, 0xfc, 0x0e, 0xff, 0x38, 0x00, 0x03, 0xff, 0xbf, 0xfe, 0x7e, 0xff, 0x27, 0xf3, 0x00, 0x6f, 0xff, 0xfc, 0x3e, 0xfe, 0x70, 0x00, 0x07, 0xff, 0xbf, 0xff, 0x3e, 0xff, 0x2f, 0xf0, 0x7c, 0xcf, 0xfb, 0xfe, 0xfc, 0xfe, 0x70, 0x00, 0x07, 0xff, 0xbf, 0xff, 0xbd, 0xff, 0x2f, 0xff, 0xfc, 0xcf, 0xe1, 0xfe, 0x7d, 0xfe, 0x60, 0x00, 0x0f, 0xff, 0x3f, 0xff, 0x9d, 0xfe, 0x4f, 0xff, 0xf9, 0xdf, 0xe1, 0xff, 0x7d, 0xfc, 0xc0, 0x00, 0x1f, 0xff, 0x7f, 0xff, 0xdd, 0xfe, 0x5f, 0xff, 0xf1, 0x9f, 0xcc, 0xff, 0x31, 0xfc, 0xc0, 0x00, 0x3f, 0xff, 0x7f, 0xff, 0xcb, 0xfe, 0x5f, 0xfe, 0x03, 0x9f, 0xce, 0xff, 0x83, 0xfc, 0x80, 0x00, 0x7f, 0xfe, 0x7f, 0xff, 0xeb, 0xfc, 0x9f, 0xe0, 0x07, 0xbf, 0xce, 0x7f, 0xf3, 0xfc, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xf3, 0xfc, 0xbf, 0xc0, 0xff, 0x3f, 0x9f, 0x7f, 0xf3, 0xfc, 0x00, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xf7, 0xfc, 0xbf, 0xcf, 0xff, 0x3f, 0x9f, 0x3f, 0xe7, 0xc0, 0x00, 0x03, 0xff, 0xfc, 0xff, 0x7f, 0xff, 0xf9, 0x3f, 0x9f, 0xfc, 0x7f, 0x9f, 0xbf, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xfd, 0xff, 0x3f, 0xff, 0xf9, 0x7f, 0x9f, 0xf8, 0x7f, 0x3f, 0xbf, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfd, 0xfe, 0x3f, 0xff, 0xf9, 0x7f, 0x9f, 0xc2, 0xff, 0x3f, 0xc0, 0x0f, 0x80, 0x00, 0x3f, 0xff, 0xf9, 0xfe, 0x5f, 0xff, 0xf2, 0x7f, 0x38, 0x0c, 0xff, 0x1f, 0xc0, 0x7f, 0x00, 0x00, 0x7f, 0xff, 0xfb, 0xfe, 0x4f, 0xff, 0xf2, 0xff, 0x01, 0xfc, 0xff, 0x9f, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xff, 0xfb, 0xfc, 0xef, 0xff, 0xf2, 0xff, 0x3f, 0xfd, 0xf8, 0x3f, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xf3, 0xfc, 0xe7, 0xff, 0xe4, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0xff, 0xf7, 0xfc, 0xf7, 0xff, 0xe5, 0xff, 0xff, 0xe0, 0x03, 0xff, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xf7, 0xf9, 0xf3, 0xff, 0xe5, 0xff, 0xfc, 0x03, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xe7, 0xf9, 0xfb, 0xff, 0xc9, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xf8, 0x00, 0x01, 0xff, 0xff, 0xff, 0xef, 0xf9, 0xfd, 0xff, 0xcb, 0xf0, 0x01, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x07, 0xff, 0xff, 0xff, 0xef, 0xf3, 0xfc, 0xff, 0xc9, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xcf, 0xf3, 0xfe, 0xff, 0x9c, 0x07, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xdf, 0xf3, 0xfe, 0x7f, 0x9e, 0x7f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xdf, 0xe7, 0xff, 0x7f, 0x1f, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0x9f, 0xe7, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xe0, 0x38, 0x3f, 0x0f, 0xbf, 0xe7, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0xff, 0xe0, 0x30, 0x1c, 0x07, 0xbf, 0xe7, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0xe1, 0x0c, 0x23, 0x3f, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf0, 0xe1, 0x0c, 0x23, 0x7f, 0x07, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf0, 0xe0, 0x0c, 0x23, 0x70, 0x07, 0xfe, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf0, 0xe0, 0x0c, 0x3f, 0x00, 0x7f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0xe1, 0xfc, 0x3f, 0x87, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xe1, 0x0c, 0x23, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xe1, 0x0c, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xe1, 0x0c, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x30, 0x1e, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x38, 0x3e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; //Inverted nerftec logo static const unsigned char PROGMEM nerftecInvert_bmp[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x78, 0x01, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x0f, 0xe7, 0x18, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x01, 0x98, 0x0f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x90, 0x0f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 0xd0, 0x1f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0xe1, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0xf6, 0x00, 0x3f, 0x00, 0xe0, 0x1c, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x01, 0xf8, 0x36, 0x01, 0xff, 0x01, 0xe0, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x18, 0x3f, 0x00, 0x72, 0x03, 0xe1, 0x01, 0xe0, 0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, 0xf0, 0x00, 0x64, 0x03, 0x02, 0x01, 0xc0, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3e, 0x00, 0x01, 0xe4, 0x03, 0x06, 0x03, 0xc0, 0x0f, 0xff, 0xff, 0x00, 0x00, 0xfc, 0x00, 0xc0, 0x3c, 0x00, 0x3f, 0xcc, 0x06, 0x1c, 0x07, 0xc0, 0x3f, 0xff, 0xfe, 0x00, 0x0f, 0x06, 0x00, 0x80, 0x3c, 0x03, 0xff, 0x08, 0x07, 0xf0, 0x0e, 0x80, 0x7f, 0xff, 0xfe, 0x00, 0x70, 0x02, 0x00, 0x80, 0x6c, 0x07, 0xe0, 0x08, 0x07, 0x00, 0x1c, 0x80, 0xe3, 0xff, 0xfe, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x68, 0x06, 0x00, 0x18, 0x00, 0x00, 0x79, 0x80, 0xc7, 0xff, 0xfc, 0x00, 0xe0, 0x01, 0x01, 0x00, 0x68, 0x06, 0x0f, 0x90, 0x00, 0x03, 0xf1, 0x00, 0xc7, 0xff, 0xfc, 0x00, 0x40, 0x01, 0x81, 0x00, 0xd8, 0x0c, 0xff, 0x90, 0x00, 0x03, 0xc1, 0x01, 0x8f, 0xff, 0xf8, 0x00, 0x40, 0x00, 0xc1, 0x00, 0xd0, 0x0f, 0x83, 0x30, 0x04, 0x01, 0x03, 0x01, 0x8f, 0xff, 0xf8, 0x00, 0x40, 0x00, 0x42, 0x00, 0xd0, 0x00, 0x03, 0x30, 0x1e, 0x01, 0x82, 0x01, 0x9f, 0xff, 0xf0, 0x00, 0xc0, 0x00, 0x62, 0x01, 0xb0, 0x00, 0x06, 0x20, 0x1e, 0x00, 0x82, 0x03, 0x3f, 0xff, 0xe0, 0x00, 0x80, 0x00, 0x22, 0x01, 0xa0, 0x00, 0x0e, 0x60, 0x33, 0x00, 0xce, 0x03, 0x3f, 0xff, 0xc0, 0x00, 0x80, 0x00, 0x34, 0x01, 0xa0, 0x01, 0xfc, 0x60, 0x31, 0x00, 0x7c, 0x03, 0x7f, 0xff, 0x80, 0x01, 0x80, 0x00, 0x14, 0x03, 0x60, 0x1f, 0xf8, 0x40, 0x31, 0x80, 0x0c, 0x03, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x03, 0x40, 0x3f, 0x00, 0xc0, 0x60, 0x80, 0x0c, 0x03, 0xff, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x08, 0x03, 0x40, 0x30, 0x00, 0xc0, 0x60, 0xc0, 0x18, 0x3f, 0xff, 0xfc, 0x00, 0x03, 0x00, 0x80, 0x00, 0x06, 0xc0, 0x60, 0x03, 0x80, 0x60, 0x40, 0x1f, 0xff, 0xff, 0xf8, 0x00, 0x02, 0x00, 0xc0, 0x00, 0x06, 0x80, 0x60, 0x07, 0x80, 0xc0, 0x40, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x02, 0x01, 0xc0, 0x00, 0x06, 0x80, 0x60, 0x3d, 0x00, 0xc0, 0x3f, 0xf0, 0x7f, 0xff, 0xc0, 0x00, 0x06, 0x01, 0xa0, 0x00, 0x0d, 0x80, 0xc7, 0xf3, 0x00, 0xe0, 0x3f, 0x80, 0xff, 0xff, 0x80, 0x00, 0x04, 0x01, 0xb0, 0x00, 0x0d, 0x00, 0xfe, 0x03, 0x00, 0x60, 0x00, 0x03, 0xff, 0xfe, 0x00, 0x00, 0x04, 0x03, 0x10, 0x00, 0x0d, 0x00, 0xc0, 0x02, 0x07, 0xc0, 0x00, 0x0f, 0xff, 0xfc, 0x00, 0x00, 0x0c, 0x03, 0x18, 0x00, 0x1b, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x08, 0x03, 0x08, 0x00, 0x1a, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xc0, 0x00, 0x00, 0x08, 0x06, 0x0c, 0x00, 0x1a, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0x00, 0x18, 0x06, 0x04, 0x00, 0x36, 0x00, 0x7f, 0xf0, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x10, 0x06, 0x02, 0x00, 0x34, 0x0f, 0xfe, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x10, 0x0c, 0x03, 0x00, 0x36, 0xff, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x01, 0x00, 0x63, 0xf8, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x20, 0x0c, 0x01, 0x80, 0x61, 0x80, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x20, 0x18, 0x00, 0x80, 0xe0, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x60, 0x18, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0x80, 0x1f, 0xc7, 0xc0, 0xf0, 0x40, 0x18, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xcf, 0xe3, 0xf8, 0x40, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xf0, 0x00, 0x0f, 0x1e, 0xf3, 0xdc, 0xc0, 0x18, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x80, 0x00, 0x0f, 0x1e, 0xf3, 0xdc, 0x80, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x0f, 0x1f, 0xf3, 0xdc, 0x8f, 0xf8, 0x01, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x0f, 0x1f, 0xf3, 0xc0, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0x1e, 0x03, 0xc0, 0x78, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x1e, 0xf3, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x1e, 0xf3, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x1e, 0xf3, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xcf, 0xe1, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc7, 0xc1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //By Kev autobot logo 128x64 static const unsigned char PROGMEM bykev_bmp[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0x9f, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xbf, 0xff, 0x8f, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xfe, 0x7f, 0xff, 0xdd, 0xff, 0xff, 0xbf, 0xff, 0x8f, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xfc, 0xf7, 0x6a, 0x80, 0x67, 0x1f, 0x8e, 0xdb, 0x83, 0xfe, 0x0f, 0xff, 0xff, 0xff, 0xfc, 0x1f, 0xf8, 0xf5, 0x66, 0xdd, 0xdb, 0x6f, 0xb6, 0xdb, 0x83, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xf0, 0xf5, 0x6e, 0xdd, 0xc3, 0x6f, 0xb6, 0xdf, 0xc3, 0xfe, 0x1f, 0xfe, 0x00, 0x1f, 0xfe, 0x1f, 0xf0, 0xfa, 0xee, 0xdd, 0xdf, 0x6f, 0xb7, 0x3b, 0xc1, 0xfe, 0x0f, 0xf8, 0x00, 0x03, 0xfc, 0x1f, 0xf0, 0xfa, 0xee, 0xe6, 0x63, 0x6f, 0x8f, 0xbb, 0xc1, 0xfe, 0x0f, 0xfc, 0x00, 0x07, 0xfc, 0x1f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc1, 0xfe, 0x03, 0xfe, 0x00, 0x1f, 0xf8, 0x1f, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc1, 0xff, 0x81, 0xff, 0x00, 0x3f, 0xe0, 0x3f, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xe0, 0x7f, 0xc0, 0xff, 0xc1, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xf9, 0xf8, 0x3f, 0xf3, 0xff, 0x83, 0xf3, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xe0, 0xfc, 0x7c, 0x0f, 0xf3, 0xfe, 0x0f, 0x87, 0xe1, 0xff, 0xff, 0xff, 0xbd, 0xff, 0xff, 0xff, 0xe0, 0xfc, 0x0e, 0x07, 0xff, 0xf8, 0x1e, 0x07, 0xc3, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xe0, 0xfc, 0x01, 0x80, 0xff, 0xf0, 0x70, 0x0f, 0xc3, 0xff, 0xff, 0xff, 0xb7, 0xc7, 0x76, 0xc3, 0xf0, 0xff, 0x80, 0xe0, 0xff, 0xc1, 0xe0, 0x3f, 0xc7, 0xff, 0xff, 0xff, 0xaf, 0xbb, 0x76, 0xdd, 0xf0, 0x7f, 0xe0, 0x30, 0x3f, 0x83, 0x80, 0xff, 0xc7, 0xff, 0xff, 0xff, 0x8f, 0x83, 0xae, 0xdd, 0xf8, 0x7d, 0xf8, 0x38, 0x0c, 0x03, 0x03, 0xef, 0xc7, 0xff, 0xff, 0xff, 0xb7, 0xbf, 0xae, 0xdd, 0xf8, 0x7c, 0x7f, 0x38, 0x00, 0x07, 0x3f, 0x8f, 0x87, 0xff, 0xff, 0xff, 0xbb, 0xbb, 0xde, 0xdd, 0xf8, 0x7c, 0x1f, 0xf8, 0x00, 0x07, 0xfe, 0x0f, 0x87, 0xff, 0xff, 0xff, 0xbd, 0xc7, 0xde, 0xdd, 0xf8, 0x7e, 0x03, 0xf8, 0x40, 0x47, 0xf8, 0x0f, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0x00, 0x78, 0x71, 0xc7, 0x80, 0x3f, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x0f, 0xe0, 0x1c, 0x7f, 0x87, 0x00, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xfc, 0x1c, 0x7f, 0x86, 0x07, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0x0e, 0x7f, 0x8e, 0x1f, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x8e, 0x7f, 0x8e, 0x3f, 0xc0, 0x7f, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0x88, 0x3f, 0xfe, 0x7f, 0x8f, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x06, 0x0f, 0xfe, 0x3f, 0x9f, 0xfc, 0x0c, 0x1f, 0xff, 0xff, 0xbb, 0xc7, 0x48, 0x78, 0xf1, 0xfe, 0x07, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x3c, 0x1f, 0xff, 0xff, 0x83, 0xbb, 0x3b, 0xbf, 0x6f, 0xfe, 0x07, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x7c, 0x1f, 0xff, 0xff, 0xbd, 0x83, 0x7b, 0xb8, 0x67, 0xfe, 0x07, 0xc0, 0x00, 0x3f, 0x80, 0x00, 0x7c, 0x1f, 0xff, 0xff, 0xbd, 0xbf, 0x7b, 0xb7, 0x79, 0xfe, 0x07, 0xc0, 0x00, 0x3f, 0x80, 0x00, 0x7c, 0x1f, 0xff, 0xff, 0xbd, 0xbb, 0x7b, 0xb7, 0x7d, 0xff, 0x07, 0xc0, 0x02, 0x3f, 0x88, 0x00, 0x7c, 0x3f, 0xff, 0xff, 0x83, 0xc7, 0x7b, 0xb8, 0x63, 0xff, 0x07, 0xe0, 0x06, 0x3f, 0x8c, 0x00, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0x1e, 0x3f, 0x8f, 0x03, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfc, 0x7e, 0x3f, 0x8f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfc, 0x7e, 0x3f, 0x8f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfc, 0x7e, 0x3f, 0x8f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfc, 0x7e, 0x3f, 0x8f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfc, 0x7e, 0x3f, 0x8f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xfc, 0x7e, 0x3f, 0x8f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0x83, 0xfc, 0x7e, 0x3f, 0x8f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xef, 0xf6, 0xff, 0xff, 0xff, 0x83, 0xfc, 0x7e, 0x3f, 0x8f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xef, 0xe7, 0x7f, 0xff, 0xff, 0x83, 0xfc, 0x7e, 0x00, 0x0f, 0x8f, 0xf8, 0x3f, 0xff, 0xfc, 0x0f, 0xef, 0xef, 0x7f, 0xff, 0xff, 0x83, 0xfc, 0x7e, 0x00, 0x0f, 0x8f, 0xf8, 0x3f, 0xff, 0xff, 0xe7, 0xef, 0xcf, 0x3f, 0xff, 0xff, 0x8d, 0xfc, 0x7e, 0x00, 0x0f, 0x8f, 0xe6, 0x7f, 0xff, 0xff, 0xf7, 0xdf, 0xdf, 0xbf, 0xff, 0xff, 0xd0, 0xfc, 0x7f, 0xff, 0xff, 0x8f, 0xc1, 0x7f, 0xff, 0xff, 0xf7, 0xdf, 0xdf, 0xbf, 0xff, 0xff, 0xe0, 0x7c, 0x7f, 0xff, 0xff, 0x8f, 0x80, 0xff, 0xff, 0xff, 0xf7, 0x9f, 0xdf, 0xbf, 0xff, 0xff, 0xc0, 0x1c, 0x7f, 0x00, 0x1f, 0x8f, 0x01, 0xff, 0xff, 0xff, 0xf7, 0x3f, 0xdf, 0x3f, 0xff, 0xff, 0xe0, 0x1c, 0x7e, 0x00, 0x0f, 0x8e, 0x03, 0xff, 0xff, 0xff, 0xf6, 0x7f, 0xdc, 0x7f, 0xff, 0xff, 0xf0, 0x04, 0x7e, 0x00, 0x07, 0x8c, 0x03, 0xff, 0xff, 0xff, 0xf6, 0xff, 0xd9, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x7c, 0x3f, 0x87, 0x88, 0x0f, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xd3, 0xfb, 0xff, 0xff, 0xfc, 0x00, 0x7c, 0x3f, 0xc7, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xc3, 0xf7, 0xff, 0xff, 0xff, 0x00, 0x78, 0x7f, 0xc3, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xdc, 0xef, 0xff, 0xff, 0xff, 0x00, 0x78, 0x7f, 0xc3, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xf5, 0xff, 0xde, 0x9f, 0xff, 0xff, 0xff, 0xf8, 0x70, 0x7f, 0xe1, 0x83, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xff, 0xde, 0x7f, 0xff, 0xff, 0xff, 0xf9, 0x10, 0xff, 0xf1, 0x13, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x7f, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x31, 0x00, 0x11, 0x87, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x32, 0x00, 0x09, 0x87, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x00, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; //Flashing Nerftec logo display.clearDisplay(); display.drawBitmap( 0, 0, nerftec_bmp, w, h, WHITE); display.display(); delay(200); display.clearDisplay(); display.drawBitmap( 0, 0, nerftecInvert_bmp, w, h, WHITE); display.display(); delay(200); display.clearDisplay(); display.drawBitmap( 0, 0, nerftec_bmp, w, h, WHITE); display.display(); delay(150); display.clearDisplay(); display.drawBitmap( 0, 0, nerftecInvert_bmp, w, h, WHITE); display.display(); delay(150); display.clearDisplay(); display.drawBitmap( 0, 0, nerftec_bmp, w, h, WHITE); display.display(); delay(100); display.clearDisplay(); display.drawBitmap( 0, 0, nerftecInvert_bmp, w, h, WHITE); display.display(); delay(100); display.clearDisplay(); display.drawBitmap( 0, 0, nerftec_bmp, w, h, WHITE); display.display(); delay(50); display.clearDisplay(); display.drawBitmap( 0, 0, nerftecInvert_bmp, w, h, WHITE); display.display(); delay(50); display.clearDisplay(); display.drawBitmap( 0, 0, nerftec_bmp, w, h, WHITE); display.display(); delay(25); display.clearDisplay(); display.drawBitmap( 0, 0, nerftecInvert_bmp, w, h, WHITE); display.display(); delay(25); display.clearDisplay(); display.drawBitmap( 0, 0, nerftec_bmp, w, h, WHITE); display.display(); delay(10); display.clearDisplay(); display.drawBitmap( 0, 0, nerftecInvert_bmp, w, h, WHITE); display.display(); delay(10); display.clearDisplay(); display.drawBitmap( 0, 0, nerftec_bmp, w, h, WHITE); display.display(); delay(5); display.clearDisplay(); display.drawBitmap( 0, 0, nerftecInvert_bmp, w, h, WHITE); display.display(); delay(5); display.clearDisplay(); display.drawBitmap( 0, 0, nerftec_bmp, w, h, WHITE); display.display(); delay(1); display.clearDisplay(); display.drawBitmap( 0, 0, nerftecInvert_bmp, w, h, WHITE); display.display(); delay(1); //Author's autobot logo display.clearDisplay(); display.drawBitmap( 0, 0, bykev_bmp, w, h, WHITE); display.display(); delay(2000); //Some text infos display.clearDisplay(); display.setTextSize(2); display.setCursor(0,0); display.setTextColor(WHITE); display.print(F(" ")); //(1,2) display.setTextColor(BLACK,WHITE); //1234567890 display.println(F("Nerftec")); display.setTextSize(1); //123456789012345678901 display.setTextColor(WHITE); display.println(); //(3) display.print(F(" Version - ")); //(4) display.println(F(versionNo)); display.println(); //(5) display.println(); //(6) display.println(F("For the nerf")); //(7) //123456789012345678901 display.print(F(" connoisseur! ;-)"));//(8) display.display(); delay(2000); #endif //Read the ADC and set vBattLow appropriately... float volts = float(analogRead(vbattPin)) / vbattRatio; if(volts < 5.3) vBattLow = 0.0; //<5.3V = usb power... battery may be disconnected else if(volts > lipo2sHigh) vBattLow = vbattLow3s * vbattRatio;//More than 2s, must be 3s else vBattLow = vbattLow2s * vbattRatio; //If it's 3s, it must be 2s updateSetupScreen(); //Initialize setup mode display }//END OF SETUP //LOOP---------------------------------------- void loop() { switch (mode) { case 0: { //'setup' Mode... //Time to reset the battery warning if(lowBattWarning && millis() - previousWarning > warningDelay) { lowBattWarning = 0; //Ready to beep again if battery reads low } //No active beepers, and it's time to check voltage if(!beepCount && millis() - previousVoltCheck > voltCheckDelay) { liveVoltage = analogRead(vbattPin);//Read Vold adc previousVoltCheck = millis(); //Update timer //Battery is low and we haven't warned yet if(!lowBattWarning && liveVoltage <= vBattLow) { beepCount = 2; //1 long beep beepDelay = beepDelayLong; previousWarning = millis(); lowBattWarning = 1; } updateSetupScreen(); //Update voltage on screen } beeper(); //Update the beeper uint8_t buttonValue = readButtons();//Read buttons //Button debounce & hold/repeat timer if(millis() - previousButtonTime > buttonDelay) { if(!buttonValue) { //No buttons... longSelect = 0; //Reset long press variables longLeft = 0; longRight = 0; buttonDelay = buttonDelayNorm; } else { //We have a button previousButtonTime = millis(); //Save the time if(buttonValue <= 1) { //Select button if(longSelect) { //We have a long press... go to fire mode remaining = clipSize; //Clip should be loaded PORTD = PORTD & 0b10111111; //Turn off LED mode = 1; //Switch to fire mode updateFireScreen(0.0,0.0); //Initialized fire mode screen prepareForInterrupts (); //Get interrupts ready longSelect = 0; //Reset for a possible next setup mode break; } else { //Every time we se select... longSelect = 1; //Flag for a long press if(setupItem < 1) { //Cycle to next item setupItem++; } else setupItem = 0; } } else if(buttonValue <= 2 && setupItem == 0) {//Right button, item 0... if(longRight) { buttonDelay = buttonDelayShort; } clipSize++; //Increase clip size longRight = 1; if(clipSize > maxClipSize) { //Limit to maxClipSize clipSize = maxClipSize; //Enforce beepCount = 2; //1 short beep beepDelay = beepDelayShort; } remaining = clipSize; } else if(buttonValue <= 2 && setupItem == 1) {//Right button, item 1... if(longRight && dartLength < dartLengthMega) { dartLength = dartLengthMega-1; //Long right, jump to bigger size if smaller } else if(longRight) { buttonDelay = buttonDelayShort; } dartLength=dartLength+10; if(dartLength > 9994) { dartLength = 9994; } longRight = 1; } else if(buttonValue <= 3 && setupItem == 0) {//Left button, item 0 if(longLeft) { buttonDelay = buttonDelayShort; } clipSize--; //Decrease clip size longLeft = 1; if(clipSize < 1) { //Limit to smallest clip clipSize = 1; //Enforce beepCount = 2; //1 short beep beepDelay = beepDelayShort; } remaining = clipSize; } else if(buttonValue <= 3 && setupItem == 1) {//Left button, item 1 if(longLeft && dartLength > dartLengthElite) { dartLength = dartLengthElite+1;//Long left, jump to smaller size if bigger } else if(longLeft) { buttonDelay = buttonDelayShort; } dartLength=dartLength-10; if(dartLength < 10) { dartLength = 10; } longLeft = 1; } updateSetupScreen(); }//end of button processing }//end button timer }//end of setup mode break; case 1: {//'Fire' mode... capture and display each dart //It's been a while since we warned of a low battery... if(millis() - previousWarning > warningDelay) { lowBattWarning = 0; //Ready to beep again if battery reads low } //It's time to update live voltage, and no other beepers are running if(millis() - previousVoltCheck > voltCheckDelay && !beepCount) { liveVoltage = analogRead(vbattPin);//Read adc //We have a low battery... if(liveVoltage <= vBattLow && !lowBattWarning) { beepCount = 2; beepDelay = beepDelayLong; previousWarning = millis(); lowBattWarning = 1; } updateFireScreen(dartRPMdata[clipSize - remaining-1],dartSpeedData[clipSize - remaining-1]); previousVoltCheck = millis(); } beeper(); //Update the beeper if (triggered) { //We have new data from the ISR... dartTime = micros(); //Save Timer0 for RPM related stuff //Read adc and store voltage dartVoltData[clipSize - remaining] = analogRead(vbattPin); unsigned long elapsedTime = finishTime - startTime;//Calculate # of ticks it took for dart to go by prepareForInterrupts (); //Done with the volatiles, get interrupts ready again //elapsedTime = (1000000 * elapsedTime) / F_CPU;//Calculate ET in microseconds float microsecs = (float)elapsedTime * 62.5e-9 * 1.0e6;//Calculate ET in microseconds //Calculate and save FPS * 100 dartSpeedData[clipSize - remaining] = dartLength * 100000 / (12 * microsecs); //Calc RPM*100 if(remaining == clipSize) { //On the first shot only... clipStartTime = dartTime; //Save clip timer dartRPMdata[clipSize - remaining] = 0;//Zero RPM for the first round } else { //The rest of the shots... dartRPMdata[clipSize - remaining] = 6000000000 / (dartTime - previousDartTime);//Calc and save instant RPM } remaining--; //Decrease rounds remaining //Update fire screen now (with updated remaining, thus the -1 updateFireScreen(dartRPMdata[clipSize - remaining-1],dartSpeedData[clipSize - remaining-1]); previousDartTime = dartTime; //Save dart timer if(remaining == 1) { //One round left... beepCount = 2; //1 short beep beepDelay = beepDelayShort; PORTD = PORTD | 0b01000000; //LED on } if(remaining == 0) { //Clip empty... beepCount = 6; //3 short beeps beepDelay = beepDelayShort; previousScreenTime = millis(); //So we show the last fire mode screen screenDelay = screenDelayMed; //...for a bit mode = 2; //Switch to 'Empty' mode } }//done processing new data }//end of fire mode... cases {} wrapped to control variable scope (and SRAM usage) break; case 2: { //"Empty" mode... Scroll dart data //Ready to beep again if battery reads low if(millis() - previousWarning > warningDelay && lowBattWarning) { lowBattWarning = 0; } //It's time to update live voltage, and no other beepers are running if(millis() - previousVoltCheck > voltCheckDelay && !beepCount) { liveVoltage = analogRead(vbattPin);//Read adc //We have a low battery & haven't warned... if(liveVoltage <= vBattLow && !lowBattWarning) { beepCount = 2; beepDelay = beepDelayLong; previousWarning = millis(); lowBattWarning = 1; } previousVoltCheck = millis(); } beeper(); //Update the beeper uint8_t buttonValue = readButtons();//Read the buttons if(buttonValue == 1) { //Select button remaining = clipSize; //Refill the clip screenDelay = screenDelayMed; currentScreen = 0; //Reset empty mode indexes currentRound = 0; updateFireScreen(0,0); //Show initialized fire screen PORTD = PORTD & 0b10111111; //Turn off LED prepareForInterrupts (); //...in case something triggered it while reloading mode = 1; //Switch to fire mode break; //Break early } if(buttonValue == 2 || buttonValue == 3) {//Right or Left buttons screenDelay = screenDelayMed; currentScreen = 0; //Reset empty mode indexes currentRound = 0; mode = 0; //Switch to setup mode previousButtonTime = millis(); //Update timer so we don't change values upon entrering setup mode break; //Break early } //If it is time, show the next screen if(millis() - previousScreenTime > screenDelay) { if(currentScreen <= 0) { //First screen: min/avg/max V, FPS, & RPM, and live voltage float voltFloat = (float)liveVoltage / vbattRatio;//Convert to float Volts float avgVolts = 0.0; //Calculate average Voltage for clip float avgFps = 0.0; //Calculate average FPS for clip for(uint8_t i=0; i<clipSize; i++) { avgVolts += (float)dartVoltData[i]/vbattRatio; avgFps += (float)dartSpeedData[i]/100.0; } avgVolts = avgVolts / (float)clipSize; avgFps = avgFps / (float)clipSize; //Calculate average RPM for clip float avgRpm = 60000000.0 * (float)(clipSize - 1) / (float)(dartTime - clipStartTime); float maxFps = 0.0; //Find max FPS and save it float minFps = 1000000.0; //... min FPS... float maxRpm = 0.0; //... max RPM... float minRpm = 1000000.0; //... min RPM... float maxVolt = 0.0; //... max V... float minVolt = 1000000.0; //... min V... for(uint8_t i=0; i<clipSize; i++) { if((float)dartSpeedData[i] > maxFps) { maxFps = (float)dartSpeedData[i]; fastestRound = i; } if((float)dartSpeedData[i] < minFps) { minFps = (float)dartSpeedData[i]; slowestRound = i; } if((float)dartRPMdata[i] > maxRpm) { maxRpm = (float)dartRPMdata[i]; fastestRate = i; } if((float)dartRPMdata[i] < minRpm && i > 0) { minRpm = (float)dartRPMdata[i]; slowestRate = i; } if((float)dartVoltData[i] > maxVolt) { maxVolt = (float)dartVoltData[i]; highestVolt = i; } if((float)dartVoltData[i] < minVolt) { minVolt = (float)dartVoltData[i]; lowestVolt = i; } } maxFps = maxFps / 100.0; minFps = minFps / 100.0; maxRpm = maxRpm / 100.0; minRpm = minRpm / 100.0; maxVolt = maxVolt / vbattRatio; minVolt = minVolt / vbattRatio; display.clearDisplay(); display.setTextSize(2); //10x5? display.setTextColor(WHITE); display.setCursor(0,0); display.println(F("Statistics"));//Title row (1-2) display.setTextSize(1); display.setTextColor(BLACK,WHITE);//Header row (3) //123456789012345678901 display.println(F("Desc MIN AVG MAX ")); display.print(F("FPS ")); //FPS line (4), 1-4 display.setTextColor(WHITE); display.print(F(" ")); //padding, 4-5 if(minFps < 10) { //Mininum FPS, 6-9 display.print(minFps,2); } else if(minFps < 100) { display.print(minFps,1); } else if(minFps < 1000) { display.print(F(" ")); display.print((int)minFps); } else if(minFps >= 1000) { display.print((int)minFps); } display.print(F(" ")); //Padding, 10-11 if(avgFps < 10) { //Average FPS, 12-15 display.print(avgFps,2); } else if(avgFps < 100) { display.print(avgFps,1); } else if(avgFps < 1000) { display.print(F(" ")); display.print((int)avgFps); } else if(avgFps >= 1000) { display.print((int)avgFps); } display.print(F(" ")); //Padding, 15-16 if(maxFps < 10) { //Maximum FPS, 18-21 display.println(maxFps,2); } else if(maxFps < 100) { display.println(maxFps,1); } else if(maxFps < 1000) { display.print(F(" ")); display.println((int)maxFps); } else if(maxFps >= 1000) { display.println((int)maxFps); } display.setTextColor(BLACK,WHITE); display.print(F("RPM ")); //RPM line (5), 1-4 display.setTextColor(WHITE); display.print(F(" ")); //padding, 4-5 if(minRpm < 10) { //Minimum RPM, 6-9 display.print(minRpm,2); } else if(minRpm < 100) { display.print(minRpm,1); } else if(minRpm < 1000) { display.print(F(" ")); display.print((int)minRpm); } else if(minRpm >= 1000) { display.print((int)minRpm); } display.print(F(" ")); //Padding 10-11 if(avgRpm < 10) { //Average RPM, 12-15 display.print(avgRpm,2); } else if(avgRpm < 100) { display.print(avgRpm,1); } else if(avgRpm < 1000) { display.print(F(" ")); display.print((int)avgRpm); } else if(avgRpm >= 1000) { display.print((int)avgRpm); } display.print(F(" ")); //Padding 16-17 if(maxRpm < 10) { //Maximum RPM, 18-21 display.println(maxRpm,2); } else if(maxRpm < 100) { display.println(maxRpm,1); } else if(maxRpm < 1000) { display.print(F(" ")); display.println((int)maxRpm); } else if(maxRpm >= 1000) { display.println((int)maxRpm); } display.setTextColor(BLACK,WHITE); display.print(F("BATT")); //Voltage line (6), 1-4 display.setTextColor(WHITE); display.print(F(" ")); //padding, 4-5 if(minVolt < 10) { //Minimum voltage, 6-9 display.print(minVolt,2); } else { display.print(minVolt,1); } display.print(F(" ")); //Padding 10-11 if(avgVolts < 10) { //Average voltage, 12-15 display.print(avgVolts,2); } else { display.print(avgVolts,1); } display.print(F(" ")); //Padding 16-17 if(maxVolt < 10) { //Maximum voltage, 18-21 display.println(maxVolt,2); } else { display.println(maxVolt,1); } display.println(); //line (7) display.print(F("Live Batt: "));//Live voltage line (8), 1-15 if(liveVoltage <= vBattLow) { //Print voltage inverted if low display.setTextColor(BLACK,WHITE); } if(voltFloat < 10) { //Live voltage, 16-19 display.print(voltFloat,2); } else { display.print(voltFloat,1); } display.setTextColor(WHITE); //In case of low battery display.print(F(" V")); //20-21 display.display(); screenDelay = screenDelayLong; //Show stats for a short time currentScreen = 1; previousScreenTime = millis(); }//end screen 0 else if(currentScreen <= 1) { //2nd+ screen(s)... dart data display.clearDisplay(); display.setTextSize(1); //21x8 display.setTextColor(BLACK, WHITE); display.setCursor(0,0); display.println(F(" RND VOLT FPS RPM "));//Header row // 123456789012345678901 display.setTextColor(WHITE); for(int i=0 ; i<7 ; i++) { //7 rows of data per screen (under the header row) if(currentRound < clipSize) { //Check that we only print a row when data exists if(currentRound+1 < 10) { //Left side padding display.print(F(" ")); } else { display.print(F(" ")); } display.print(currentRound+1);//Print Round #, 1-3 display.print(F(" ")); //Round-Volts padding, 4-5 //Convert int*100's to float's float voltFloat = (float)dartVoltData[currentRound] / vbattRatio; float fpsFloat = (float)dartSpeedData[currentRound] / 100.0; float rpmFloat = (float)dartRPMdata[currentRound] / 100.0; if(currentRound == lowestVolt || currentRound == highestVolt) {//Highlight max & min volts display.setTextColor(BLACK,WHITE); } if(voltFloat < 10) { //Print Volts, 6-9 display.print(voltFloat,2); } else { display.print(voltFloat,1); } display.setTextColor(WHITE); display.print(F(" ")); //Volts-FPS padding, 10-11 if(currentRound == fastestRound || currentRound == slowestRound) {//Highlight max & min velocities display.setTextColor(BLACK,WHITE); } if(fpsFloat < 10) { //Print Velocity display.print(fpsFloat,2);//12-15 } else if(fpsFloat < 100) { display.print(fpsFloat,1); } else if(fpsFloat < 1000) { display.print((int)fpsFloat); display.print(F(".")); } else { display.print((int)fpsFloat); } display.setTextColor(WHITE); display.print(F(" ")); //FPS-RPM padding, 16-17 if(currentRound == fastestRate || currentRound == slowestRate) {//Highlight max & min rates display.setTextColor(BLACK,WHITE); } if(rpmFloat < 10) { //Print RPM, 18-21 display.println(rpmFloat,2); } else if(rpmFloat < 100) { display.println(rpmFloat,1); } else if(rpmFloat < 1000) { display.print((int)rpmFloat); display.println(F(".")); } else if(rpmFloat >= 1000) { display.println((int)rpmFloat); } display.setTextColor(WHITE); } currentRound++; } if(currentRound >= clipSize) { //No more dart data currentScreen = 0; //Go back to screen 0 currentRound = 0; //Reset line index for next time around } screenDelay = screenDelayLong; //Show dart data screens a longer time display.display(); previousScreenTime = millis(); }//end screen 2 }//end screen timer }//end empty mode break; default: //This should never happen... mode = 0; break; } }//END OF LOOP //Setup Mode Display update.............................................. void updateSetupScreen() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); display.print(F("Clip Size: ")); //1-11,(1) display.setTextSize(2); //11-16,(1-2) if(clipSize < 10) { display.print(F(" ")); } else if(clipSize < 100) { display.print(F(" ")); } if(!setupItem) { //Clip size selected display.setTextColor(BLACK,WHITE); } display.println(clipSize); float dartLengthy = float(dartLength)/1000.0; display.setTextColor(WHITE); display.setTextSize(1); display.print(F("Dart Length:")); //1-12,(3) display.setTextSize(2); //12-16,(4-5) if(setupItem == 1) { //Clip size selected display.setTextColor(BLACK,WHITE); } display.println(dartLengthy,2); display.setTextSize(1); display.setTextColor(BLACK,WHITE); display.println(); // 12345678901234567890 display.println(F("<[-] [Next] [+]>"));//(6) display.println(); //(7) display.setTextColor(WHITE); display.print(F(" Battery: ")); //(8) if(liveVoltage < vBattLow) { display.setTextColor(BLACK,WHITE); } float voltFloat = (float)liveVoltage / vbattRatio; display.print(voltFloat,2); display.println(F(" V")); display.display(); } //Fire Mode Display update................................................... void updateFireScreen(unsigned int rpm, unsigned int fps) { display.clearDisplay(); display.setTextColor(WHITE); display.setCursor(0,0); display.setTextSize(6); //Print remaining rounds display.print(F(" ")); //6 "rows" if(remaining < 10) { display.print(F(" ")); } display.println(remaining); display.setTextSize(1); //21x8 display.setTextColor(BLACK, WHITE); //Print Header Row //123456789012345678901 display.println(F(" BATT RPM FPS ")); //Convert int*100's to float's float voltFloat = (float)liveVoltage / vbattRatio; float fpsFloat = (float)fps / 100.0; float rpmFloat = (float)rpm / 100.0; if(liveVoltage > vBattLow) { //Print voltage non-inverted if OK display.setTextColor(WHITE); } //...otherwise it stays highlighted display.print(F(" ")); //1-2 if(voltFloat < 10) { display.print(voltFloat,2); //3-6 } else { display.print(voltFloat,1); //3-6 } display.setTextColor(WHITE); //Set to non-inverted again, in case of low voltage display.print(F(" ")); //Print fire rate, 7-9 if(rpmFloat < 10) { //10-13 display.print(rpmFloat,2); } else if(rpmFloat < 100) { display.print(rpmFloat,1); } else if(rpmFloat < 1000) { display.print(F(" ")); display.print((int)rpmFloat); } else if(rpmFloat >= 1000) { display.print((int)rpmFloat); } display.print(F(" ")); //14-16 //Print velocity if(fpsFloat < 10) { //17-20 display.print(fpsFloat,2); } else if(fpsFloat < 100) { display.print(fpsFloat,1); } else if(fpsFloat < 1000) { display.print(F(" ")); display.print((int)fpsFloat); } else if(fpsFloat >= 1000) { display.print((int)fpsFloat); } display.display(); } //Read digital buttons & return a value......................................... uint8_t readButtons(void) { if(!(PIND & 0b00001000)) { //Select button return 1; } else if(!(PIND & 0b00010000)) { //Right button return 2; } else if(!(PIND & 0b00100000)) { //Left button return 3; } else { //No buttons return 0; } } //Update beeper: reads global beep variables, and toggles the beeper accordingly //set beepCount = 2 * desired#ofBeeps //set beepDelay = desired length of beep in mSec //note: length of beep = length of silence between repeated beeps void beeper(void) { //beepCount = 0 and buzzer is still on... if(!beepCount && (PIND & 0b10000000)) {//For the rare case when 'beep off' is 'skipped' during a timer interrupt PORTD = PORTD & 0b01111111; //Buzzer off again } //We have beeps to do, and it's time to act on them if(beepCount && millis() - beepTime > beepDelay) { if(beepState == 0) { PORTD = PORTD | 0b10000000; //Buzzer on beepState = 1; } else { PORTD = PORTD & 0b01111111; //Buzzer off beepState = 0; } beepCount--; beepTime = millis(); } } //Timer overflow counter ISR (ICR1 overflows every 65536 counts)..................... ISR (TIMER1_OVF_vect) { overflowCount++; } //Timer capture ISR............................................................. ISR (TIMER1_CAPT_vect) { //Get counter value before it changes any more unsigned int timer1CounterValue; timer1CounterValue = ICR1; //Timer1 input capture count register unsigned long overflowCopy = overflowCount; //Just missed an overflow... if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF) overflowCopy++; if (triggered) return; //Wait until the last capture is processed if (first) { //This is the falling edge startTime = (overflowCopy << 16) + timer1CounterValue; TIFR1 |= bit (ICF1); //Clear Timer1 input capture count flag TCCR1B = bit (CS10) | bit (ICES1);//No prescaler + capture rising edge first = false; return; } //This must be the rising edge finishTime = (overflowCopy << 16) + timer1CounterValue; triggered = true; TIMSK1 = 0; //Disable Timer1 interrupts } //Reset timer1 registers for the next capture............................................... void prepareForInterrupts () { noInterrupts (); //Protected code first = true; //Re-arm for next time triggered = false; TCCR1A = 0; //Reset Timer1 TCCR1B = 0; TIFR1 = bit (ICF1) | bit (TOV1); //Clear Timer1 input capture and overflow flags TCNT1 = 0; //Reset counter overflowCount = 0; //Reset overflows //Timer1 - counts clock pulses TIMSK1 = bit (TOIE1) | bit (ICIE1); //Enable Timer1 overflow and input capture interrupts TCCR1B = bit (CS10); //Start Timer1, with no prescaler + capture falling edge interrupts (); }
Cheers,
Kevin
- 2