diff --git a/BicycleControl.ino b/BicycleControl.ino new file mode 100644 index 0000000..c7c4396 --- /dev/null +++ b/BicycleControl.ino @@ -0,0 +1,394 @@ +#include +#include +#include +#include "Musical.h" +#ifdef U8X8_HAVE_HW_I2C +#include +#endif + +// Software SPI setup: +#define OLED_MOSI 14 +#define OLED_CLK 15 +#define OLED_DC 16 +#define OLED_CS 12 +#define OLED_RESET 13 + +U8X8_SSD1306_128X64_NONAME_4W_SW_SPI screen(OLED_CLK, OLED_MOSI, OLED_CS, OLED_DC, OLED_RESET); + +//Global defines +#define BLINKER_DELAY 10000 +#define BLINKER_FLASH_ON 500 +#define BLINKER_FLASH_OFF 150 + +//Pin assignments +//Shift register control +#define DS 4 +#define STCP 2 +#define SHCP 7 +#define MR 8 +//Shift register pin assignments. +#define LED_blinkerRight 3 +#define LED_blinkerLeft 2 +#define LED_lampFront 0 +#define LED_lampBack 1 +//Other pinouts. +#define BUZZER 3 +#define LED_indicatorRed 9 +#define LED_indicatorGreen 10 +#define LED_indicatorBlue 11 +//Input pins. +#define INPUT_DELAY 300 +#define IN_mode 17 +#define IN_left 18 +#define IN_right 19 + +//Global variables +//Shift register buffer. +bool outputBuffer[8] = { 0/*Front lamp*/, 0/*Back lamp*/, 0/*Left blinker*/, 0/*Right blinker*/, 0, 0, 0, 0 }; +//-Blinker Variables +bool blinkerRight = 0; +bool blinkerLeft = 0; +unsigned long blinkerRightStart = 0; +unsigned long blinkerLeftStart = 0; +unsigned long blinkerRightLastUpdate = 0; +unsigned long blinkerLeftLastUpdate = 0; +//Input control variables +//Mode control button. +char mode = 0; +bool modePressed = 0; +unsigned long modePressedTime = 0; +//Left option button. +bool leftPressed = 0; +unsigned long leftPressedTime = 0; +//Right option button. +bool rightPressed = 0; +unsigned long rightPressedTime = 0; +//Music variables. +short BPM = 100; +short Q = 60000 / BPM; +short H = 2 * Q; +short E = Q / 2; +short S = Q / 4; +short W = Q * 4; + +const short beep1[] = { + 100, + NOTE_A4, Q, + NOTE_E7, Q, + NOTE_C2, Q +}; + +//-------------------| +//GRAPHICS FUNCTIONS:| +//-------------------| + +//Shortcut for writing text to the screen. +void writeText(const char* text, int x, int y) { + screen.setCursor(x, y); + screen.print(text); +} + +//Draws the template of the main screen onto the display. +void drawMainScreen() { + screen.clear(); + screen.setInverseFont(0); + screen.setFont(u8x8_font_amstrad_cpc_extended_f); + writeText("Bicycle Control", 0, 0); + screen.setFont(u8x8_font_artossans8_r); + writeText("+-+-+-+-+------+", 0, 1); + writeText("|L|R|F|B|Mode:0|", 0, 2); + writeText("+-+-+-+-+------+", 0, 3); +} + +//Draws the blinker indicators to the screen. +void drawBlinkerStatus() { + screen.setFont(u8x8_font_artossans8_r); + screen.setInverseFont(blinkerLeft); + writeText("L", 1, 2); + screen.setInverseFont(blinkerRight); + writeText("R", 3, 2); +} + +//Draws the front lamp indicator. +void drawFrontLampStatus() { + screen.setFont(u8x8_font_artossans8_r); + screen.setInverseFont(outputBuffer[LED_lampFront]); + writeText("F", 5, 2); +} + +//Draws the back lamp indicator. +void drawBackLampStatus() { + screen.setFont(u8x8_font_artossans8_r); + screen.setInverseFont(outputBuffer[LED_lampBack]); + writeText("B", 7, 2); +} + +//Draws the current mode number on the screen. +void drawModeStatus() { + screen.setFont(u8x8_font_artossans8_r); + screen.setInverseFont(0); + screen.setCursor(14, 2); + screen.print((int)mode); +} + +//-------------------| +//STARTUP FUNCTIONS: | +//-------------------| + +//Initialize the OLED screen. +void startupScreen() { + screen.begin(); + screen.setFont(u8x8_font_amstrad_cpc_extended_r); +} + +//Initialize peripherals. +void startupPeripherals() { + //Shift register outputs. + pinMode(DS, OUTPUT); + pinMode(STCP, OUTPUT); + pinMode(SHCP, OUTPUT); + pinMode(MR, OUTPUT); + digitalWrite(MR, HIGH); + //Other outputs. + pinMode(BUZZER, OUTPUT); + pinMode(LED_indicatorRed, OUTPUT); + pinMode(LED_indicatorGreen, OUTPUT); + pinMode(LED_indicatorBlue, OUTPUT); + //Inputs. + pinMode(IN_mode, INPUT); + pinMode(IN_left, INPUT); + pinMode(IN_right, INPUT); +} + +//-------------------| +//UPDATE FUNCTIONS: | +//-------------------| + +//Updates the display with blinker status. +void updateBlinkerStatus() { + bool needsRefresh = 0; + unsigned long currentTime = millis(); + //Check if the blinkers must shut off. + if (blinkerLeft) { + //Check if we should try blinking the light. + if (currentTime - blinkerLeftStart < BLINKER_DELAY) { + if (outputBuffer[LED_blinkerLeft]) { + //Test if the blinker needs to be flashed off. + if (currentTime - blinkerLeftLastUpdate > BLINKER_FLASH_ON) { + setPin(LED_blinkerLeft, 0); + blinkerLeftLastUpdate = currentTime; + } + } + else { + //Test if the blinker needs to be flashed on. + if (currentTime - blinkerLeftLastUpdate > BLINKER_FLASH_OFF) { + setPin(LED_blinkerLeft, 1); + blinkerLeftLastUpdate = currentTime; + } + } + } + else { + //Try to shut down the blinker. + setPin(LED_blinkerLeft, 0); + blinkerLeft = 0; + needsRefresh = 1; + } + } + if (blinkerRight) { + //Check if we should try blinking the light. + if (currentTime - blinkerRightStart < BLINKER_DELAY) { + if (outputBuffer[LED_blinkerRight]) { + //Test if the blinker needs to be flashed off. + if (currentTime - blinkerRightLastUpdate > BLINKER_FLASH_ON) { + setPin(LED_blinkerRight, 0); + blinkerRightLastUpdate = currentTime; + } + } + else { + //Test if the blinker needs to be flashed on. + if (currentTime - blinkerRightLastUpdate > BLINKER_FLASH_OFF) { + setPin(LED_blinkerRight, 1); + blinkerRightLastUpdate = currentTime; + } + } + } + else { + //Try to shut down the blinker. + setPin(LED_blinkerRight, 0); + blinkerRight = 0; + needsRefresh = 1; + } + } + //Only if a change has been made, update the display. + if (needsRefresh) drawBlinkerStatus(); +} + +//Checks for user button presses, and processes input. +void updateInputs() { + unsigned long currentTime = millis(); + if (!modePressed && digitalRead(IN_mode)) { + //Mode button was pressed. + modePressed = 1; + modePressedTime = currentTime; + if (mode == 0) { + mode = 1; + } + else { + mode = 0; + } + drawModeStatus(); + //Serial.println("Mode pressed."); + } + if (!leftPressed && digitalRead(IN_left)) { + //Left button was pressed. + leftPressed = 1; + leftPressedTime = currentTime; + switch (mode) { + case 0: + setFrontLamp(!outputBuffer[LED_lampFront]); + break; + case 1: + if (!blinkerLeft) activateBlinker(0); + break; + } + //Serial.println("Left pressed."); + } + if (!rightPressed && digitalRead(IN_right)) { + //Right button was pressed. + rightPressed = 1; + rightPressedTime = currentTime; + switch (mode) { + case 0: + setBackLamp(!outputBuffer[LED_lampBack]); + break; + case 1: + if (!blinkerRight) activateBlinker(1); + break; + } + //Serial.println("Right pressed."); + } + //Remove restrictions on time delays. + if (currentTime - modePressedTime > INPUT_DELAY) { + modePressed = 0; + } + if (currentTime - leftPressedTime > INPUT_DELAY) { + leftPressed = 0; + } + if (currentTime - rightPressedTime > INPUT_DELAY) { + rightPressed = 0; + } +} + +//Updates all modules. +void updateAll() { + updateBlinkerStatus(); + updateInputs(); +} + +//-------------------| +//STATE CHANGE FUNC: | +//-------------------| + +//Loads 8 bits into the register. +void loadRegister(bool values[8]) { + for (int i = 7; i >= 0; --i) { + digitalWrite(DS, values[i]); + digitalWrite(SHCP, HIGH); + delayMicroseconds(1); + digitalWrite(SHCP, LOW); + } + digitalWrite(STCP, HIGH); + delayMicroseconds(1); + digitalWrite(STCP, LOW); +} + +//Sets a pin on the register and reloads the bits. +void setPin(byte index, bool value) { + outputBuffer[index] = value; + loadRegister(outputBuffer); +} + +//Activates a blinker for BLINK_DELAY milliseconds, until it is shut off by the updateBlinkStatus. 0 = Left, 1 = Right +void activateBlinker(bool side) { + unsigned long currentTime = millis(); + if (side) { + setPin(LED_blinkerRight, 1); + blinkerRight = 1; + blinkerRightStart = currentTime; + blinkerRightLastUpdate = currentTime; + Serial.println("Activated Right blinker."); + } + else { + setPin(LED_blinkerLeft, 1); + blinkerLeft = 1; + blinkerLeftStart = currentTime; + blinkerLeftLastUpdate = currentTime; + Serial.println("Activated Left blinker."); + } + drawBlinkerStatus(); +} + +//Controls the front lamp. +void setFrontLamp(bool value) { + setPin(LED_lampFront, value); + drawFrontLampStatus(); +} + +//Controls the back lamp. +void setBackLamp(bool value) { + setPin(LED_lampBack, value); + drawBackLampStatus(); +} + +//Sets the color of the indicator LED. +void setIndicatorColor(byte red, byte green, byte blue) { + analogWrite(LED_indicatorRed, red); + analogWrite(LED_indicatorGreen, green); + analogWrite(LED_indicatorBlue, blue); +} + +//-------------------| +//ERROR CHECKING: | +//-------------------| + +void testOutput(short pinNumber, const char* name) { + screen.clear(); + writeText(name, 0, 0); + digitalWrite(pinNumber, HIGH); + delay(500); + digitalWrite(pinNumber, LOW); +} + +//Visually tests each output. +void testOutputs() { + screen.clear(); + writeText("Outputs test...", 0, 0); + delay(1000); + testOutput(LED_blinkerLeft, "Left blinker"); + testOutput(LED_blinkerRight, "Right blinker"); + testOutput(BUZZER, "Sound"); + testOutput(LED_indicatorRed, "Indic. Red"); + testOutput(LED_indicatorGreen, "Indic. Green"); + testOutput(LED_indicatorBlue, "Indic. Blue"); + testOutput(LED_lampFront, "Front lamp"); + testOutput(LED_lampBack, "Back lamp"); +} + +//-------------------| +//ARDUINO RUNTIME: | +//-------------------| + +void setup() { + Serial.begin(9600); + //Startup all the pins. + startupPeripherals(); + startupScreen(); + drawMainScreen(); + updateAll(); +} + +void loop() { + updateBlinkerStatus(); + updateInputs(); +} + diff --git a/BicycleControl.sln b/BicycleControl.sln new file mode 100644 index 0000000..026023e --- /dev/null +++ b/BicycleControl.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BicycleControl", "BicycleControl.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32 + {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32 + {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32 + {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/BicycleControl.vcxproj b/BicycleControl.vcxproj new file mode 100644 index 0000000..e809e15 --- /dev/null +++ b/BicycleControl.vcxproj @@ -0,0 +1,99 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C5F80730-F44F-4478-BDAE-6634EFC2CA88} + $safeprojectname$ + BicycleControl + + + + Application + true + + + MultiByte + + + Application + false + + + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + C:\Users\AndrewComputer\Documents\Arduino\libraries\U8g2\src;C:\Users\AndrewComputer\Documents\Arduino\libraries\U8g2\src\utility;D:\Arduino\hardware\arduino\avr\libraries\Wire\src;D:\Arduino\hardware\arduino\avr\libraries\Wire\src\utility;D:\Arduino\libraries;D:\Arduino\hardware\arduino\avr\libraries;C:\Users\AndrewComputer\Documents\Arduino\libraries;D:\Arduino\hardware\arduino\avr\cores\arduino;D:\Arduino\hardware\arduino\avr\variants\eightanaloginputs;C:\Users\AndrewComputer\Documents\Arduino\BicycleControl;D:\Arduino\hardware\tools\avr/avr/include/;D:\Arduino\hardware\tools\avr//avr/include/avr/;D:\Arduino\hardware\tools\avr/lib\gcc\avr\4.8.1\include;%(AdditionalIncludeDirectories) + C:\Users\AndrewComputer\Documents\Arduino\BicycleControl\__vm\.BicycleControl.vsarduino.h;%(ForcedIncludeFiles) + false + __AVR_ATmega328p__;__AVR_ATmega328P__;F_CPU=16000000L;ARDUINO=106012;ARDUINO_AVR_NANO;ARDUINO_ARCH_AVR;__cplusplus=201103L;%(PreprocessorDefinitions) + + + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + CppCode + + + + + + + + + + + VisualMicroDebugger + + + + + + + + + + \ No newline at end of file diff --git a/BicycleControl.vcxproj.filters b/BicycleControl.vcxproj.filters new file mode 100644 index 0000000..40ee299 --- /dev/null +++ b/BicycleControl.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/Musical.cpp b/Musical.cpp new file mode 100644 index 0000000..455c656 --- /dev/null +++ b/Musical.cpp @@ -0,0 +1,53 @@ +#include "Musical.h" + + + +Musical::Musical() +{ +} + + +Musical::~Musical() +{ +} + +void Musical::setBPM(short newBPM) +{ + BPM = newBPM; + Q = 60000 / BPM; + H = Q * 2; + E = Q / 2; + S = Q / 4; + W = Q * 4; +} + +void Musical::setBuzzerPin(short pinNumber) +{ + pin = pinNumber; +} + +void Musical::playNote(int note, int duration) +{ + tone(pin, note, duration); +} + +//Plays an array as a musical sequence. Array is: {bpm, note1, time1, note2, time2, ...} Length is the number of notes. +void Musical::playSequence(short * sequence, int length) +{ + setBPM(sequence[0]); + currentSong = sequence; + songLength = length; + currentNote = 0; + lastNoteTime = 0; +} + +//Updates the playing song to allow multitasking. +void Musical::update() +{ + if (lastNoteTime > millis() && currentNote < songLength) { + //It is necessary to play the next note. + playNote(currentSong[currentNote * 2 + 1], currentSong[currentNote * 2 + 2]); + lastNoteTime = millis(); + currentNote++; + } +} diff --git a/Musical.h b/Musical.h new file mode 100644 index 0000000..6b6f776 --- /dev/null +++ b/Musical.h @@ -0,0 +1,113 @@ +#pragma once + +//Musical notes: +#define NOTE_B0 31 +#define NOTE_C1 33 +#define NOTE_CS1 35 +#define NOTE_D1 37 +#define NOTE_DS1 39 +#define NOTE_E1 41 +#define NOTE_F1 44 +#define NOTE_FS1 46 +#define NOTE_G1 49 +#define NOTE_GS1 52 +#define NOTE_A1 55 +#define NOTE_AS1 58 +#define NOTE_B1 62 +#define NOTE_C2 65 +#define NOTE_CS2 69 +#define NOTE_D2 73 +#define NOTE_DS2 78 +#define NOTE_E2 82 +#define NOTE_F2 87 +#define NOTE_FS2 93 +#define NOTE_G2 98 +#define NOTE_GS2 104 +#define NOTE_A2 110 +#define NOTE_AS2 117 +#define NOTE_B2 123 +#define NOTE_C3 131 +#define NOTE_CS3 139 +#define NOTE_D3 147 +#define NOTE_DS3 156 +#define NOTE_E3 165 +#define NOTE_F3 175 +#define NOTE_FS3 185 +#define NOTE_G3 196 +#define NOTE_GS3 208 +#define NOTE_A3 220 +#define NOTE_AS3 233 +#define NOTE_B3 247 +#define NOTE_C4 262 +#define NOTE_CS4 277 +#define NOTE_D4 294 +#define NOTE_DS4 311 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_FS4 370 +#define NOTE_G4 392 +#define NOTE_GS4 415 +#define NOTE_A4 440 +#define NOTE_AS4 466 +#define NOTE_B4 494 +#define NOTE_C5 523 +#define NOTE_CS5 554 +#define NOTE_D5 587 +#define NOTE_DS5 622 +#define NOTE_E5 659 +#define NOTE_F5 698 +#define NOTE_FS5 740 +#define NOTE_G5 784 +#define NOTE_GS5 831 +#define NOTE_A5 880 +#define NOTE_AS5 932 +#define NOTE_B5 988 +#define NOTE_C6 1047 +#define NOTE_CS6 1109 +#define NOTE_D6 1175 +#define NOTE_DS6 1245 +#define NOTE_E6 1319 +#define NOTE_F6 1397 +#define NOTE_FS6 1480 +#define NOTE_G6 1568 +#define NOTE_GS6 1661 +#define NOTE_A6 1760 +#define NOTE_AS6 1865 +#define NOTE_B6 1976 +#define NOTE_C7 2093 +#define NOTE_CS7 2217 +#define NOTE_D7 2349 +#define NOTE_DS7 2489 +#define NOTE_E7 2637 +#define NOTE_F7 2794 +#define NOTE_FS7 2960 +#define NOTE_G7 3136 +#define NOTE_GS7 3322 +#define NOTE_A7 3520 +#define NOTE_AS7 3729 +#define NOTE_B7 3951 +#define NOTE_C8 4186 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 +#define NOTE_REST 0 + +class Musical +{ +public: + Musical(); + ~Musical(); + static void setBPM(short newBPM); + static void setBuzzerPin(short pinNumber); + static void playNote(int note, int duration); + static void playSequence(short* sequence, int length); + static void update(); +private: + static short BPM, Q, H, E, S, W, pin; + static short* currentSong; + static int songLength; + static bool isPlaying = false; + static int currentNote; + static unsigned long lastNoteTime; +}; + diff --git a/__vm/.BicycleControl.vsarduino.h b/__vm/.BicycleControl.vsarduino.h new file mode 100644 index 0000000..398c07f --- /dev/null +++ b/__vm/.BicycleControl.vsarduino.h @@ -0,0 +1,54 @@ +/* + Editor: http://www.visualmicro.com + visual micro and the arduino ide ignore this code during compilation. this code is automatically maintained by visualmicro, manual changes to this file will be overwritten + the contents of the Visual Micro sketch sub folder can be deleted prior to publishing a project + all non-arduino files created by visual micro and all visual studio project or solution files can be freely deleted and are not required to compile a sketch (do not delete your own code!). + note: debugger breakpoints are stored in '.sln' or '.asln' files, knowledge of last uploaded breakpoints is stored in the upload.vmps.xml file. Both files are required to continue a previous debug session without needing to compile and upload again + + Hardware: Arduino Nano w/ ATmega328, Platform=avr, Package=arduino +*/ + +#ifndef _VSARDUINO_H_ +#define _VSARDUINO_H_ +#define __AVR_ATmega328p__ +#define __AVR_ATmega328P__ +#define F_CPU 16000000L +#define ARDUINO 106012 +#define ARDUINO_AVR_NANO +#define ARDUINO_ARCH_AVR +#define __cplusplus 201103L +#define __AVR__ +#define __inline__ +#define __asm__(x) +#define __extension__ +#define __inline__ +#define __volatile__ +#define GCC_VERSION 40801 +#define volatile(va_arg) +#define _CONST +#define __builtin_va_start +#define __builtin_va_end +#define __attribute__(x) +#define NOINLINE __attribute__((noinline)) +#define prog_void +#define PGM_VOID_P int +#ifndef __builtin_constant_p + #define __builtin_constant_p __attribute__((__const__)) +#endif +#ifndef __builtin_strlen + #define __builtin_strlen __attribute__((__const__)) +#endif +#define NEW_H +typedef void *__builtin_va_list; +extern "C" void __cxa_pure_virtual() {;} + + + +#include +#include +#undef F +#define F(string_literal) ((const PROGMEM char *)(string_literal)) +#undef PSTR +#define PSTR(string_literal) ((const PROGMEM char *)(string_literal))") +#include "BicycleControl.ino" +#endif diff --git a/__vm/Compile.vmps.xml b/__vm/Compile.vmps.xml new file mode 100644 index 0000000..73ab50b --- /dev/null +++ b/__vm/Compile.vmps.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/__vm/Configuration.Debug.vmps.xml b/__vm/Configuration.Debug.vmps.xml new file mode 100644 index 0000000..92516cc --- /dev/null +++ b/__vm/Configuration.Debug.vmps.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/__vm/Upload.vmps.xml b/__vm/Upload.vmps.xml new file mode 100644 index 0000000..73ab50b --- /dev/null +++ b/__vm/Upload.vmps.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file