Quantcast
Channel: NerfHaven - Mods
Viewing all articles
Browse latest Browse all 627

Another Nerf-puter project

$
0
0

Hey all, I recently rigged up a round counter/chrono/voltmeter/volt alarm gadget to add to my son's collection of nerf things. 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.

 

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 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...). ;)

 

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 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

LM7805 regulator (I used a spare TO-220, but this won't drawing much over 100mA anyways)

0.1uF & 0.33uF output/input anti-ripple caps for the above regulator

5k6 & 3k3 resistors (10% is OK)

LED + limiting resistor (you pick... stay under 20mA since it's pin driven)

5V peizo buzzer (doesn't require tone libraries... 5V in = buzzzzzzz)

3@ momentary NO buttons

Nerf Scope of choice (must obviously fit stuff, and must be hacked as needed)

 

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)

20170411_132318.jpg

20170411_132337.jpg

 

Fugly breadboard action... that extra multi-turn poti is there just to simulate low battery alarms for testing/debugging (not installed):

20170407_094940.jpg

 

Dryfitting the guts in the scope before cutting and soldering the wires:

20170407_172159.jpg

 

Shameless blocking splash screen, LOL...

20170411_132233.jpg

 

Setup mode after boot, we see live voltage, and can setup clip capacity and dart length...

20170411_132240.jpg

 

Fire mode, with live voltage, round counter, 1 round left LED (w/ single beep), 0 rounds left 3 beeps...

20170411_132254.jpg

 

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;) )

20170409_131706.jpg

 

Empty mode per dart data tables, with highlighted max/min values...

20170409_134341.jpg

 

Here's the code (I'm compiling with Arduino 1.8.1):

#define versionNo   "1.U"
/*  nerfGunU.ino
 *  by: Kevin B.
 *  
 *  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);

//Battery voltage divider ratio K, where Vin = ADCoutput / K, (hint, theoretically K ~ 1023*R1/(5*(R1+R1)))
//calibrated vbattRatio = [Current vbattRatio] * [Actual Volts]/[Displayed Volts]
const float vbattRatio =          75.7;        //theoretically 83.2 for a 5k6:3k3 divider
unsigned int dartLength =         2850;        //Default = elite darts
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 2&6 as outputs (led and beeper)
  PORTD= PORTD & 0b01111111;                   //turn off beeper
  PORTD= PORTD | 0b01111000;                   //pins 3-5 activate pullups (buttons), and turn on 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(3000);

    //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 ADC, and set vBattLow appropriately...
  float volts = float(analogRead(vbattPin)) / vbattRatio;
  if(volts < 5.3)  vBattLow = 0.0;            //<5.3V = usb power... battery should 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
//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 ();
}

Hope this leads to a more Nerfy world... please, if you make yourself one I'd love to see pics!

 

Cheers,

Kevin

Attached Thumbnails

  • 20170407_125319.jpg

Viewing all articles
Browse latest Browse all 627

Trending Articles