Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Collin Wentzien
Published © GPL3+

A (not so) Electric Bike

A system that gives my bike front and tail lights, a turn signal, and a speedometer, all in a clean and simple design.

AdvancedShowcase (no instructions)11,960
A (not so) Electric Bike

Things used in this project

Hardware components

Arduino Mega 2560
Arduino Mega 2560
×1
1.3" OLED Screen
×2
GPS Module (Generic)
Used a BN-180
×1
WS2812 Addressable LED Strip
Digilent WS2812 Addressable LED Strip
×1
Buzzer, Piezo
Buzzer, Piezo
×1
19mm Stainless Steel Momentary Pushbutton
×1
19mm Stainless Steel Toggle Pushbutton
Used one white LED button and one amber LED button
×2
Limit Switch
Could use other switches as well as long as they're momentary
×2
On Off On Toggle Switch
×1
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
Machine Screw, M2
Machine Screw, M2
For securing the limit switches, otherwise not needed
×4
Generic Battery Pack
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

Front Light Holder

May be different for other bike models / brands.

Front Light Diffuser

To be printed with white or clear filament, used as a cover for Front Light Holder.

Back Light Holder

May be different for other bike models / brands.

Back Light Diffuser

To be printed with white or clear filament, used as a cover for Back Light Holder.

Turn Signal Mount

May be different for other bike models / brands.

Alt. Horn Button Mount

May be different for other bike models / brands.

Brake Button Mount

May be different for other bike models / brands. May also change depending on how stiff the brake is.

Control Panel Left Front

May be different for other bike models / brands.

Control Panel Left Case

May be different for other bike models / brands.

Control Panel Right Front

May be different for other bike models / brands.

Control Panel Right Case

May be different for other bike models / brands.

Schematics

Main Schematic

Incomplete and is missing a couple of wires. Will update soon with updated wiring and more traditional layout.

Code

Bike System

Arduino
Contains code for bike system. Built and tested for Arduino MEGA and MEGA Pro.
#include <FastLED.h>
#include <TinyGPS++.h>
#include <EEPROM.h>
#include <avr/wdt.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#include <Fonts/nunitoTiny.h>
#include <Fonts/makoSmall.h>
#include <Fonts/gothamBold.h>

static const unsigned char PROGMEM icon[] = {
  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, 
  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, 
  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, 
  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, 
  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, 
  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, 
  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, 
  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, 
  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, 
  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, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x01, 0xfd, 0xfd, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x03, 0xf9, 0xfc, 0xff, 0x07, 0xff, 0xcf, 0xff, 0xe7, 0xfd, 0xf0, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x07, 0xf3, 0xf8, 0xff, 0x07, 0xff, 0x87, 0xff, 0xc3, 0xfb, 0xf0, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x0f, 0xe7, 0xf0, 0x7e, 0x07, 0xff, 0x07, 0xff, 0xc3, 0xf7, 0xe0, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x1f, 0xff, 0xe0, 0xfc, 0x01, 0xf0, 0x0f, 0xc0, 0x03, 0xe7, 0xc0, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x1f, 0xff, 0xc0, 0xf8, 0x03, 0xe0, 0x1f, 0x80, 0x07, 0xcf, 0x80, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x3f, 0xff, 0x81, 0xf0, 0x07, 0xc0, 0x1f, 0x00, 0x0f, 0xdf, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x7f, 0xff, 0x03, 0xe0, 0x0f, 0xc0, 0x3e, 0x00, 0x1f, 0xbf, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0xff, 0xfc, 0x07, 0xe0, 0x1f, 0x80, 0x7e, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x01, 0xfc, 0x00, 0x0f, 0xc0, 0x1f, 0x00, 0xfc, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x01, 0xfc, 0x00, 0x1f, 0x80, 0x3e, 0x01, 0xf8, 0x00, 0x7f, 0xf8, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x03, 0xf8, 0x00, 0x1f, 0x00, 0x7c, 0x01, 0xf0, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x07, 0xf0, 0x00, 0x3e, 0x00, 0xfc, 0x03, 0xe0, 0x01, 0xfb, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x0f, 0xe0, 0x00, 0x7e, 0x01, 0xf8, 0x07, 0xe0, 0x03, 0xf7, 0xe0, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x0f, 0xc0, 0x00, 0xfc, 0x03, 0xf0, 0x0f, 0xc0, 0x07, 0xe7, 0xc0, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x1f, 0xc0, 0x01, 0xf8, 0x03, 0xe0, 0x1f, 0xff, 0x07, 0xcf, 0x80, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x3f, 0x80, 0x01, 0xf0, 0x07, 0xc0, 0x1f, 0xfe, 0x0f, 0x9f, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x7f, 0x00, 0x03, 0xe0, 0x0f, 0xc0, 0x3f, 0xfc, 0x1f, 0xbe, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0xfe, 0x00, 0x07, 0xe0, 0x1f, 0x80, 0x1f, 0xf8, 0x3f, 0x7e, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

static const unsigned char PROGMEM light[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x01, 0x80, 
  0x19, 0x98, 
  0x1d, 0xb8, 
  0x0c, 0x30, 
  0x01, 0x80, 
  0x3b, 0xdc, 
  0x3b, 0xdc, 
  0x01, 0x80, 
  0x0c, 0x30, 
  0x1d, 0xb8, 
  0x19, 0x98, 
  0x01, 0x80, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM stp[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x80, 
  0x02, 0xa0, 
  0x02, 0xa8, 
  0x02, 0xa8, 
  0x02, 0xa8, 
  0x03, 0xf8, 
  0x33, 0xf8, 
  0x1b, 0xf8, 
  0x0f, 0xf8, 
  0x07, 0xf8, 
  0x03, 0xf0, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM horn[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x01, 0xc4, 
  0x03, 0xae, 
  0x07, 0x2c, 
  0x1f, 0x00, 
  0x7f, 0x6e, 
  0x7f, 0x6e, 
  0x1f, 0x00, 
  0x07, 0x2c, 
  0x03, 0xae, 
  0x01, 0xc4, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM battery[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0xff, 0xfe, 
  0x80, 0x02, 
  0xb6, 0xdb, 
  0xb6, 0xdb, 
  0xb6, 0xdb, 
  0xb6, 0xdb, 
  0x80, 0x02, 
  0xff, 0xfe, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM satellite[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x01, 0x00, 
  0x02, 0x80, 
  0x0c, 0x40, 
  0x0e, 0x20, 
  0x17, 0x40, 
  0x23, 0x80, 
  0x11, 0x92, 
  0x0a, 0x12, 
  0x04, 0x62, 
  0x00, 0x04, 
  0x00, 0x08, 
  0x00, 0x70, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM power[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x01, 0x80, 
  0x0d, 0xb0, 
  0x0d, 0xb0, 
  0x19, 0x98, 
  0x19, 0x98, 
  0x18, 0x18, 
  0x18, 0x18, 
  0x0c, 0x30, 
  0x0f, 0xf0, 
  0x03, 0xc0, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM miles[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x6e, 0x78, 
  0x7f, 0xfc, 
  0x3b, 0xdc, 
  0x31, 0x8c, 
  0x31, 0x8c, 
  0x31, 0x8c, 
  0x7b, 0xde, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM kilos[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x1c, 0x00, 
  0x0c, 0x00, 
  0x0c, 0x00, 
  0x0c, 0x70, 
  0x0c, 0xe0, 
  0x0d, 0xc0, 
  0x0f, 0x80, 
  0x0f, 0x00, 
  0x0f, 0x80, 
  0x0d, 0xc0, 
  0x0c, 0xe0, 
  0x1e, 0xf0, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM reset[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x78, 
  0x0c, 0x70, 
  0x1c, 0x78, 
  0x18, 0x58, 
  0x30, 0x0c, 
  0x30, 0x0c, 
  0x30, 0x0c, 
  0x30, 0x0c, 
  0x18, 0x18, 
  0x1c, 0x38, 
  0x0f, 0xf0, 
  0x03, 0xc0, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM back[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x03, 0x80, 
  0x07, 0x00, 
  0x0e, 0x00, 
  0x1c, 0x00, 
  0x3f, 0xfc, 
  0x3f, 0xfc, 
  0x1c, 0x00, 
  0x0e, 0x00, 
  0x07, 0x00, 
  0x03, 0x80, 
  0x00, 0x00, 
  0x00, 0x00, 
  0x00, 0x00
};

static const unsigned char PROGMEM error[] = {
  0x00, 0x00, 
  0x00, 0x00, 
  0x01, 0x80, 
  0x03, 0xc0, 
  0x03, 0xc0, 
  0x06, 0x60, 
  0x06, 0x60, 
  0x0e, 0x70, 
  0x0e, 0x70, 
  0x1e, 0x78, 
  0x1e, 0x78, 
  0x3f, 0xfc, 
  0x3e, 0x7c, 
  0x7e, 0x7e, 
  0x7f, 0xfe, 
  0x00, 0x00
};

#define LCLK   47
#define LMOSI  46
#define LRESET 44
#define LDC    45
#define LCS    42

Adafruit_SH1106 lscr(LMOSI, LCLK, LDC, LRESET, LCS);

#define RCLK   40
#define RMOSI  43
#define RRESET 31
#define RDC    41
#define RCS    29

Adafruit_SH1106 rscr(RMOSI, RCLK, RDC, RRESET, RCS);

#define frontPin     9
#define frontNum    12
#define brakePin    32
#define brakeNum    12

CRGB brakeLEDS[brakeNum];
CRGB frontLEDS[frontNum];

TinyGPSPlus gps;

#define CLK 36
#define DT  35
#define SW  34

int counter = 0;
int currentStateCLK;
int lastStateCLK;
unsigned long lastButtonPress = 0;
unsigned long lastDraw = 0;
unsigned long lastBatt = 0;

int lblkState = 0;
int rblkState = 0;
int brakeState = 0;
int lightState = 0;
int hornState = 0;
int ahornState = 0;
int menuState = 0;

#define CLEAR CRGB(0, 0, 0)
#define LRED CRGB(40, 0, 0)
#define RED CRGB(255, 0, 0)
#define ORANGE CRGB(255, 40, 0)
#define WHITE CRGB(255, 255, 255)

int x, y;
int16_t x1, y1;
uint16_t w, h;

boolean menu = false;
int uph = 0;
double perc = 0;
int strength = 0;
String currTime = "";
String currDate = "";
int battLevel = 0;

int positionsX[] = {81, 79, 77, 76, 75, 75, 75, 76, 77, 79, 81, 83, 86, 89, 92, 95, 98, 101, 104, 107, 109, 111, 113, 114, 115, 115, 115, 114, 113, 111, 109};
int positionsY[] = {45, 43, 40, 37, 34, 31, 28, 25, 22, 19, 17, 15, 13, 12, 11, 11, 11, 12,  13,  15,  17,  19,  22,  25,  28,  31,  34,  37,  40,  43,  45 };

boolean gpsEnabled = true;
boolean lightOn = false;
boolean tailOn = false;

void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(4, INPUT);
  pinMode(3, INPUT);
  pinMode(33, INPUT);

  pinMode(38, OUTPUT);

  pinMode(CLK,INPUT);
  pinMode(DT,INPUT);
  pinMode(SW, INPUT_PULLUP);

  lastStateCLK = digitalRead(CLK);

  rscr.begin(SH1106_SWITCHCAPVCC);
  lscr.begin(SH1106_SWITCHCAPVCC);
  delay(100);

  FastLED.addLeds<WS2812, brakePin, GRB>(brakeLEDS, brakeNum);
  FastLED.addLeds<WS2812, frontPin, GRB>(frontLEDS, frontNum);
  
  clr();
  FastLED.show();
  
  rscr.clearDisplay();
  rscr.setTextColor(WHITE);
  rscr.setFont(&makoSmall);
  rscr.setCursor(7, 25);
  rscr.print("Designed by");
  rscr.setFont(&nunitoTiny);
  rscr.setCursor(7, 45);
  rscr.print("Collin W");
  rscr.display();

  lscr.clearDisplay();
  lscr.setTextColor(WHITE);
  lscr.drawBitmap(0, 0, icon, 128, 64, WHITE);
  lscr.display();

  delay(2000);

  Serial.println("PITCHsmart by Collin W");

  drawLeftScreen();
  drawRightScreen();
}

void drawLeftScreen() {
  lscr.clearDisplay();
  lscr.drawLine(0, 19, 127, 19, WHITE);

  if(lblkState == HIGH) {
    lscr.drawLine(3, 8, 8, 3, WHITE);
    lscr.drawLine(3, 9, 9, 3, WHITE);
    lscr.drawLine(4, 9, 9, 4, WHITE);
    lscr.drawLine(3, 9, 8, 14, WHITE);
    lscr.drawLine(3, 8, 9, 14, WHITE);
    lscr.drawLine(4, 8, 9, 13, WHITE);
    
  } else if(rblkState == HIGH) {
    lscr.drawLine(119, 3, 124, 8, WHITE);
    lscr.drawLine(118, 3, 124, 9, WHITE);
    lscr.drawLine(118, 4, 123, 9, WHITE);
    lscr.drawLine(119, 14, 124, 9, WHITE);
    lscr.drawLine(118, 14, 124, 8, WHITE);
    lscr.drawLine(118, 13, 123, 8, WHITE);
  }

  if(brakeState == HIGH) {
    lscr.drawBitmap(30, 1, stp, 16, 16, WHITE);
  }

  if(hornState == HIGH || ahornState == HIGH) {
    digitalWrite(38, HIGH);
    lscr.drawBitmap(55, 1, horn, 16, 16, WHITE);
  } else {
    digitalWrite(38, LOW);
  }

  if(lightState == HIGH) {
    lscr.drawBitmap(80 , 1, light, 16, 16, WHITE);
  }

  lscr.drawBitmap(7, 25, battery, 16, 16, WHITE);
  lscr.drawBitmap(7, 44, satellite, 16, 16, WHITE);

  lscr.setFont(&makoSmall);
  lscr.setCursor(27, 36);
  lscr.print("100%");
  lscr.setCursor(27, 55);
  if(gpsEnabled == true)
    lscr.print(String(strength));
  else
    lscr.print("--");

  if(currTime == "")
    currTime = "--:--";
  lscr.getTextBounds(currTime, x, y, &x1, &y1, &w, &h);
  lscr.setCursor(119 - w, 36);
  lscr.print(currTime);
  if(currDate == "")
    currDate = "--/--";
  lscr.getTextBounds(currDate, x, y, &x1, &y1, &w, &h);
  lscr.setCursor(119 - w, 55);
  lscr.print(currDate);

  lscr.display();
}

void drawRightScreen() {
  rscr.clearDisplay();

  rscr.setFont(&nunitoTiny);
  rscr.setCursor(7, 54);
  if(EEPROM.read(0) == 0)
    rscr.print("mph");
  else
    rscr.print("kmph");

  rscr.setFont(&gothamBold16pt7b);
  rscr.setCursor(7, 34);
  if(gpsEnabled == true)
    rscr.print(String(uph));
  else
    rscr.print("--");

  rscr.drawCircle(95, 31, 30, WHITE);
  rscr.fillTriangle(95, 31, 66, 61, 124, 61, BLACK);
  rscr.drawLine(66, 31, 124, 31, WHITE);
  rscr.drawLine(95, 2, 95, 31, WHITE);
  rscr.drawLine(75, 11, 115, 51, WHITE);
  rscr.drawLine(75, 51, 115, 11, WHITE);
  rscr.fillCircle(95, 31, 23, BLACK);
  rscr.fillCircle(95, 31, 2, WHITE);

  rscr.setFont(&makoSmall);
  rscr.setCursor(81, 45);
  rscr.print("0");
  rscr.setCursor(99, 45);
  if(EEPROM.read(0) == 0)
    rscr.print("30");
  else
    rscr.print("60");

  rscr.getTextBounds(String(perc) + "%", x, y, &x1, &y1, &w, &h);
  rscr.setCursor(95 - (w / 2), 60);
  rscr.print(String(perc) + "%");

  if(uph <= 30 && EEPROM.read(0) == 0)
    rscr.drawLine(95, 31, positionsX[uph], positionsY[uph], WHITE);
  else if(uph <= 60 && EEPROM.read(0) == 1)
    rscr.drawLine(95, 31, positionsX[uph/2], positionsY[uph/2], WHITE);
  
  rscr.display();
}

void drawMenu(int option) {
  if(menu == false) {
    counter = option;
    menu = true;
    animate(true);

    lscr.clearDisplay();
    rscr.clearDisplay();
    lscr.display();
    rscr.display();
  }

  lscr.drawBitmap(3, 13, power, 16, 16, WHITE);
  lscr.drawBitmap(67, 13, reset, 16, 16, WHITE);
  if(EEPROM.read(0) == 0) {
    lscr.fillRect(3, 42, 16, 16, BLACK);
    lscr.drawBitmap(3, 42, miles, 16, 16, WHITE);
  } else {
    lscr.fillRect(3, 42, 16, 16, BLACK);
    lscr.drawBitmap(3, 42, kilos, 16, 16, WHITE);
  }
  lscr.drawBitmap(67, 42, back, 16, 16, WHITE);

  lscr.drawRect(1, 11, 61, 20, BLACK);
  lscr.drawRect(65, 11, 61, 20, BLACK);
  lscr.drawRect(1, 40, 61, 20, BLACK);
  lscr.drawRect(65, 40, 61, 20, BLACK);

  if(option == 0)
    lscr.drawRect(1, 11, 61, 20, WHITE);
  else if(option == 1)
    lscr.drawRect(65, 11, 61, 20, WHITE);
  else if(option == 2)
    lscr.drawRect(1, 40, 61, 20, WHITE);
  else if(option == 3)
    lscr.drawRect(65, 40, 61, 20, WHITE);

  lscr.setFont(&makoSmall);
  lscr.setCursor(22, 24);
  lscr.print("Power");
  lscr.setCursor(86, 24);
  lscr.print("Reset");
  lscr.setCursor(22, 53);
  lscr.print("Units");
  lscr.setCursor(86, 53);
  lscr.print("Back");

  lscr.display();

  if(menu == true && option == -1) {
    animate(false);
    menu = false;
  }
}

void animate(boolean in) {
  if(in == true) {
    drawLeftScreen();
    
    for(int x = 0; x < 64; x += 4) {
      lscr.drawLine(0, x, x * 2, 0, WHITE);
      if(x != 0) {
        lscr.drawLine(0, x - 1, (x * 2) - 2, 0, BLACK);
        lscr.drawLine(0, x - 2, (x * 2) - 4, 0, BLACK);
        lscr.drawLine(0, x - 3, (x * 2) - 6, 0, BLACK);
        lscr.drawLine(0, x - 4, (x * 2) - 8, 0, BLACK);
      }
      
      lscr.display();
    }

    lscr.drawLine(0, 62, 126, 0, BLACK);
    lscr.drawLine(0, 61, 124, 0, BLACK);
    lscr.drawLine(0, 60, 122, 0, BLACK);
    lscr.drawLine(0, 59, 120, 0, BLACK);
    
    for(int x = 0; x < 64; x += 4) {
      lscr.drawLine(x * 2, 63, 127, x, WHITE);
      if(x != 0) {
        lscr.drawLine((x * 2) - 2, 63, 127, x - 1, BLACK);
        lscr.drawLine((x * 2) - 4, 63, 127, x - 2, BLACK);
        lscr.drawLine((x * 2) - 6, 63, 127, x - 3, BLACK);
        lscr.drawLine((x * 2) - 8, 63, 127, x - 4, BLACK);
      }
      
      lscr.display();
    }
    
  } else if(in == false) {
    for(int x = 63; x > -1; x -= 4) {
      lscr.drawLine(x * 2, 63, 127, x, WHITE);
      if(x != 63) {
        lscr.drawLine((x * 2) + 2, 63, 127, x + 1, BLACK);
        lscr.drawLine((x * 2) + 4, 63, 127, x + 2, BLACK);
        lscr.drawLine((x * 2) + 6, 63, 127, x + 3, BLACK);
        lscr.drawLine((x * 2) + 8, 63, 127, x + 4, BLACK);
      }
      
      lscr.display();
    }

    lscr.drawLine(2, 63, 127, 1, BLACK);
    lscr.drawLine(4, 63, 127, 2, BLACK);
    lscr.drawLine(6, 63, 127, 3, BLACK);
    lscr.drawLine(8, 53, 127, 4, BLACK);
    
    for(int x = 63; x > -1; x -= 4) {
      lscr.drawLine(0, x, x * 2, 0, WHITE);
      if(x != 63) {
        lscr.drawLine(0, x + 1, (x * 2) + 2, 0, BLACK);
        lscr.drawLine(0, x + 2, (x * 2) + 4, 0, BLACK);
        lscr.drawLine(0, x + 3, (x * 2) + 6, 0, BLACK);
        lscr.drawLine(0, x + 4, (x * 2) + 8, 0, BLACK);
      }
      
      lscr.display();
    }

    drawLeftScreen();
    drawRightScreen();
  }
}

void checkData() {
  brakeState = digitalRead(7);
  lblkState = digitalRead(6);
  rblkState = digitalRead(5);
  lightState = digitalRead(4);
  hornState = digitalRead(3);
  ahornState = digitalRead(33);
  menuState = digitalRead(SW);

  if(gpsEnabled == true) {
    if(Serial1.available() > 0) {
      if(gps.encode(Serial1.read())) {
        if (gps.speed.isValid()) {
          if(EEPROM.read(0) == 0)
            uph = gps.speed.mph();
          else
            uph = gps.speed.kmph();
  
          if(EEPROM.read(0) == 0)
            perc = gps.speed.mph() / 0.3;
          else
            perc = gps.speed.kmph() / 0.6;
  
          int hr = 0;
          if(gps.time.hour() > 12)
            hr = gps.time.hour() - 12;
          else
            hr = gps.time.hour();
          if(hr < 9)
            hr = hr + 5;
          else
            hr = hr - 7;
            
          if(gps.time.minute() < 10)
            currTime = String(hr) + ":0" + String(gps.time.minute());
          else
            currTime = String(hr) + ":" + String(gps.time.minute());
  
          if(gps.time.hour() < 13)
            currDate = String(gps.date.month()) + "/" + String(gps.date.day() - 1);
          else
            currDate = String(gps.date.month()) + "/" + String(gps.date.day());
          
          strength = gps.satellites.value();
          
          drawRightScreen();
        }
      }
    }
    
    if(millis() > 5000 && gps.charsProcessed() < 10) {
      gpsEnabled = false;
      drawLeftScreen();
      drawRightScreen();
      lscr.display();
    }
  }

  if(brakeState == HIGH && lblkState == LOW && rblkState == LOW && tailOn == false) {
    tailOn = true;
    brake();
  } else if(brakeState == LOW && lblkState == LOW && rblkState == LOW && tailOn == true) {
    tailOn = false;
    if(lightState == HIGH) {
      for(int x = 0; x < brakeNum; x++) {
        brakeLEDS[x] = LRED;
      }
    } else {
      for(int x = 0; x < brakeNum; x++) {
        brakeLEDS[x] = CLEAR;
      }
    }

    FastLED.show();
  }

  if(lblkState == HIGH) {
    drawLeftScreen();
    lblink();
    
  } else if(rblkState == HIGH) {
    drawLeftScreen();
    rblink();
  }

  if(lightState == HIGH && lblkState == LOW && rblkState == LOW && lightOn == false) {
    lightOn = true;
    frontLight();
    if(lblkState == LOW && rblkState == LOW && brakeState == LOW)
      tailLight();
    
  } else if(lightState == LOW && lblkState == LOW && rblkState == LOW && lightOn == true) {
    lightOn = false;
    for(int x = 0; x < frontNum; x++) {
      frontLEDS[x] = CLEAR;
    }
    
    for(int x = 0; x < brakeNum; x++) {
      brakeLEDS[x] = CLEAR;
    }
    
    FastLED.show();
  }

  if(menuState == LOW) {
    if(millis() - lastButtonPress > 50) {
      drawMenu(0);
    }
    lastButtonPress = millis();
  }

  if(millis() - lastDraw > 100 && menu == false) {
    drawLeftScreen();
    lastDraw = millis();
  }
}

void clr() {
  for(int x = 0; x < brakeNum; x++) {
    brakeLEDS[x] = CLEAR;
  }

  for(int x = 0; x < frontNum; x++) {
    frontLEDS[x] = CLEAR;
  }
  
  FastLED.show();
}

void frontLight() {
  for(int x = 0; x < frontNum; x++) {
    frontLEDS[x] = WHITE;
  }

  FastLED.show();
}

void tailLight() {
  for(int x = 0; x < brakeNum; x++) {
    brakeLEDS[x] = LRED;
  }

  FastLED.show();
}

void brake() {
  for(int i = 0; i < 12; i++) {
    brakeLEDS[i] = RED;
  }
  
  FastLED.show();
}

void lblink() {
  for(int i = 7; i > -2; i -= 1) {
    for(int x = 0; x < brakeNum; x++) {
      brakeLEDS[x] = CLEAR;
    }
    
    for(int x = 0; x < frontNum; x++) {
      frontLEDS[x] = CLEAR;
    }
  
    if(i < 6 && i != -1) {
      brakeLEDS[i] = ORANGE;
      brakeLEDS[11 - i] = ORANGE;
    }
    
    if(i > 0) {
      brakeLEDS[i - 1] = ORANGE;
      brakeLEDS[12 - i] = ORANGE;
    }

    if(i > 1) {
      brakeLEDS[i - 2] = ORANGE;
      brakeLEDS[13 - i] = ORANGE;
    }

    if(i == 5 || i == 4) {
      frontLEDS[0] = ORANGE;
      frontLEDS[7] = ORANGE;
      frontLEDS[8] = ORANGE;
    }

    if(i == 4 || i == 3) {
      frontLEDS[1] = ORANGE;
      frontLEDS[6] = ORANGE;
      frontLEDS[9] = ORANGE;
    }

    if(i == 3 || i == 2) {
      frontLEDS[2] = ORANGE;
      frontLEDS[5] = ORANGE;
      frontLEDS[10] = ORANGE;
    }

    if(i == 2 || i == 1) {
      frontLEDS[3] = ORANGE;
      frontLEDS[4] = ORANGE;
      frontLEDS[11] = ORANGE;
    }

    FastLED.show();
    delay(75);
  }

  lightOn = false;
  tailOn = false;
}

void rblink() {
  for(int i = 0; i < 9; i++) {
    for(int x = 0; x < brakeNum; x++) {
      brakeLEDS[x] = CLEAR;
    }

    for(int x = 0; x < frontNum; x++) {
      frontLEDS[x] = CLEAR;
    }
  
    if(i < 6) {
      brakeLEDS[i] = ORANGE;
      brakeLEDS[11 - i] = ORANGE;
    }
    
    if(i != 0 && i < 7) {
      brakeLEDS[i - 1] = ORANGE;
      brakeLEDS[12 - i] = ORANGE;
    }

    if(i > 1 && i != 8) {
      brakeLEDS[i - 2] = ORANGE;
      brakeLEDS[13 - i] = ORANGE;
    }

    if(i == 2 || i == 3) {
      frontLEDS[3] = ORANGE;
      frontLEDS[4] = ORANGE;
      frontLEDS[11] = ORANGE;
    }

    if(i == 3 || i == 4) {
      frontLEDS[2] = ORANGE;
      frontLEDS[5] = ORANGE;
      frontLEDS[10] = ORANGE;
    }

    if(i == 4 || i == 5) {
      frontLEDS[1] = ORANGE;
      frontLEDS[6] = ORANGE;
      frontLEDS[9] = ORANGE;
    }

    if(i == 5 || i == 6) {
      frontLEDS[0] = ORANGE;
      frontLEDS[7] = ORANGE;
      frontLEDS[8] = ORANGE;
    }

    FastLED.show();
    delay(75);
  }
  
  lightOn = false;
  tailOn = false;
  
}

void lowPowerMode() {
  lscr.clearDisplay();
  rscr.clearDisplay();
  lscr.display();
  rscr.display();
  frontLight();
  tailLight();
  
  while(true) {}
}

void loop() {
  if(menu == false) {
    checkData();
    
  } else  {
    currentStateCLK = digitalRead(CLK);
    menuState = digitalRead(SW);
    
    if(currentStateCLK != lastStateCLK  && currentStateCLK == 1) {
      if(digitalRead(DT) != currentStateCLK) {
        if(counter != 3)
          counter ++;
      } else {
        if(counter != 0)
          counter --;
      }
      
      drawMenu(counter);
    }
  
    lastStateCLK = currentStateCLK;

    if(menuState == LOW && counter == 0) {
      if(millis() - lastButtonPress > 50) {
        lowPowerMode();
      }
      lastButtonPress = millis();
      
    } else if(menuState == LOW && counter == 1) {
      if(millis() - lastButtonPress > 50) {
        wdt_disable();
        wdt_enable(WDTO_15MS);
      }
      lastButtonPress = millis();
      
    } else if(menuState == LOW && counter == 2) {
      if(millis() - lastButtonPress > 50) {
        if(EEPROM.read(0) == 0)
          EEPROM.write(0, 1);
        else
          EEPROM.write(0, 0);
        drawMenu(2);
      }
      lastButtonPress = millis();
      
    } else if(menuState == LOW && counter == 3) {
      if(millis() - lastButtonPress > 50) {
        drawMenu(-1);
      }
      lastButtonPress = millis();
    }

    delay(1);
  }
}

Credits

Collin Wentzien

Collin Wentzien

1 project • 14 followers
Hello! My name is Collin Wentzien, and I am a high school student who enjoys electrical and mechanical engineering.

Comments