├── README.md ├── AutomaticShifting ├── Simulation │ └── open-loop control.c └── AutomaticShiftingMatlab │ ├── usedMethods.h │ └── AutomaticShiftingMatlab.ino └── AutomaticShiftingNoMatlab └── AutomaticShiftingNoMatlab.ino /README.md: -------------------------------------------------------------------------------- 1 | # AutomaticGearShifting 2 | Using an Arduino, a Smartphone, a Bluetooth module and a Bike with Shimanos Di2 to create an automatic gear shifting bike. 3 | 4 | What does it do? 5 | With the right wiring of the above mentioned components, the code of this project, enables one to build a bike that will always shift into the fitting gear in respective to the chosen crank frequency and your current speed. 6 | 7 | The code of the AutomaticShiftingNoMatlab.ino is well commented. This code is more aimed at inspiring others to build an own automatic gear shifting bike, rather than being an step by step instruction how to do that. If someone actually wants do that and needs more help, you can still ask me. E-mail is in the header of the code files. 8 | 9 | The code for the mentioned app, which is necessary for changing settings without having to copile a new scetch might be uploaded soon. 10 | -------------------------------------------------------------------------------- /AutomaticShifting/Simulation/open-loop control.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Authors: Michael Petretti (michael@petretti.de) 4 | Björn Hagemeister 5 | @ University of Freiburg, Germany, 2015. 6 | 7 | 8 | * sfuntmpl_basic.c: Basic 'C' template for a level 2 S-function. 9 | * 10 | * Copyright 1990-2013 The MathWorks, Inc. 11 | */ 12 | 13 | /* 14 | * You must specify the S_FUNCTION_NAME as the name of your S-function 15 | * (i.e. replace sfuntmpl_basic with the name of your S-function). 16 | */ 17 | 18 | #define S_FUNCTION_NAME steuerung 19 | #define S_FUNCTION_LEVEL 2 20 | 21 | /* 22 | * Need to include simstruc.h for the definition of the SimStruct and 23 | * its associated macro definitions. 24 | */ 25 | #include "simstruc.h" 26 | #include "mex.h" 27 | #include "../AutomaticShiftingMatlab/usedMethods.h" 28 | 29 | 30 | 31 | static void mdlInitializeSizes(SimStruct *S) { 32 | // ssSetNumDiscStates(S, 1); 33 | ssSetNumSFcnParams(S, 0); 34 | if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { 35 | return; /* Parameter mismatch will be reported by Simulink */ 36 | } 37 | if (!ssSetNumInputPorts(S, 1)) return; 38 | ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED); 39 | ssSetInputPortDirectFeedThrough(S, 0, 1); 40 | if (!ssSetNumOutputPorts(S,1)) return; 41 | ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED); 42 | ssSetNumSampleTimes(S, 1); 43 | /* Take care when specifying exception free code - see sfuntmpl.doc */ 44 | ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE); 45 | 46 | initMethod(); 47 | } 48 | 49 | static void mdlInitializeSampleTimes(SimStruct *S) { 50 | ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); 51 | ssSetOffsetTime(S, 0, 0.0); 52 | } 53 | 54 | static void mdlOutputs(SimStruct *S, int_T tid) { 55 | InputRealPtrsType uZwi = ssGetInputPortRealSignalPtrs(S,0); 56 | 57 | y = ssGetOutputPortRealSignal(S, 0); // output 58 | u = uZwi[0]; // input 59 | // stepLength = u[4]; 60 | mainIt(); 61 | // for (int i = 0; i < 6; i++) { 62 | // y[i] = u[i]; 63 | //} 64 | } 65 | 66 | static void mdlUpdate(SimStruct *S, int_T tid) 67 | { 68 | 69 | y = ssGetOutputPortRealSignal(S,0); 70 | // int_T width = ssGetOutputPortWidth(S,0); 71 | u = (const real_T*) ssGetInputPortSignal(S,0); 72 | x = ssGetRealDiscStates(S); 73 | //InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); 74 | 75 | } 76 | 77 | static void mdlTerminate(SimStruct *S){} 78 | 79 | #ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */ 80 | #include "simulink.c" /* MEX-file interface mechanism */ 81 | #else 82 | #include "cg_sfun.h" /* Code generation registration function */ 83 | #endif -------------------------------------------------------------------------------- /AutomaticShifting/AutomaticShiftingMatlab/usedMethods.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Authors: Michael Petretti (michael@petretti.de) 4 | Björn Hagemeister 5 | @ University of Freiburg, Germany, 2015. 6 | */ 7 | 8 | //************************************************************************************************* 9 | //*********************************** DECLARATIONS ***************************************** 10 | //************************************************************************************************* 11 | 12 | //*********************************** BLUETOOTH SETTINGS ***************************************** 13 | #ifndef ARDUINO 14 | #else 15 | #include 16 | #endif 17 | 18 | #include 19 | 20 | #ifndef ARDUINO 21 | void printSomething() { 22 | printf("Hallo\n\r"); 23 | } 24 | #else 25 | #endif 26 | 27 | #ifndef ARDUINO// Variables, neccessary for Matlab simulation. 28 | double* u; // input 29 | double* y; // output 30 | double* x; // states 31 | #else 32 | double u[6]; 33 | double y[6]; 34 | double x[6]; 35 | #endif 36 | 37 | // defining pins for SoftwareSerial: 38 | const int 39 | bluetoothTX = 10, 40 | bluetoothRX = 11; 41 | 42 | 43 | 44 | 45 | // Send the sending information every x seconds. 46 | const int every_X_Seconds = 1; 47 | 48 | //************************************************************************************************* 49 | 50 | 51 | //************************************* MEASURING SETTINGS **************************************** 52 | // Using Pin2 of digital pins for input of reed sensor at wheel. 53 | const int inputPinS1 = 2; 54 | // Using Pin13 of digital pins for input of reed sensor at crank. 55 | const int crankRpmInputPin = 3; 56 | 57 | int 58 | currentFront, // current front chainblade 59 | currentRear; // current rear chainblade 60 | 61 | float 62 | currentSpeed, // current speed [km/h] 63 | currentRoundsWheel, // current rounds of wheel [U/min] 64 | frequency = 50.0f, // computed crank frequency [U/min] 65 | crankFrequency; // measured crank frequency [U/min] 66 | 67 | // the gears if correction happens to bee needed 68 | int 69 | currentFrontForCorrection, // front gear, which should be current gear for correction 70 | currentRearForCorrection, // rear gear, which should be current gear for correction 71 | correctionNeededCounter = 0; // counter for how often is measured that correction is needed 72 | 73 | // crankDebouncingTime: 74 | // We assume, that there is nobody who makes more than 120 RPM at the crank so our 75 | // crankDebouncingTime needs to 500 ms, for getting max 2 interrupts per second by the crank 76 | // sensor. 77 | int crankDebouncingTime = 500; 78 | 79 | long 80 | difference, // time difference between last and current peak wheel sensor [ms] 81 | crankDifference; // time difference between last current peak crank sensor [ms] 82 | 83 | //************************************************************************************************* 84 | 85 | // ****************************** SHIFTING SETTINGS *********************************************** 86 | // Using Pins 4,7,8,12 as Outputpins to wire up the Relais 87 | const int 88 | frontUpPin = 4, // pin 4 for shifting front up 89 | frontDownPin = 7, // pin 7 for shifting front down 90 | rearUpPin = 8, // pin 8 for shifting rear up 91 | rearDownPin = 12; // pin 12 for shifting rear down 92 | 93 | const float 94 | frontGear[] = {34, 50}, 95 | rearGear[] = {25, 23, 21, 19, 17, 16, 15, 14, 13, 12}; 96 | 97 | // Circumference in mm for 20", 24", 26", 28" 98 | const int 99 | circumference[] = {1530, 1860, 1940, 2110}; 100 | int rad = 2110; // wheel circumstance in mm 28" 101 | // ************************************************************************************************ 102 | // *********************************** END DECLARATIONS ******************************************* 103 | // ************************************************************************************************ 104 | 105 | 106 | 107 | // ************************************************************************************************ 108 | // ************************************** METHODS *********************************************** 109 | // ************************************************************************************************ 110 | 111 | // ********************************************* GENERATE SIGNAL ********************************* 112 | #ifndef ARDUINO 113 | #else 114 | // Generates positive signals on by specified pin. 115 | // Delay time between two positive signals is for the mechanical shifting time. 116 | void generateSignal(int count, int mode) { 117 | for ( int i = 0; i < count; i++) { 118 | digitalWrite(mode, LOW); 119 | delay(200); 120 | digitalWrite(mode, HIGH); 121 | delay(400); 122 | } 123 | } 124 | #endif 125 | 126 | // ********************************************* CHECK GEAR **************************************** 127 | void checkGear(){ 128 | int oneMatch = 0; 129 | float tmp = 0; 130 | if (currentRear != 1) { 131 | // check difference to crankFrequency if rear would be shifted down by one gear 132 | tmp = currentRoundsWheel / (frontGear[currentFront - 1] / rearGear[currentRear - 2]); 133 | if (tmp > crankFrequency * 0.98f && tmp < crankFrequency * 1.02f) { 134 | if (currentFrontForCorrection == currentFront && currentRearForCorrection == (currentRear - 1)) { 135 | correctionNeededCounter++; 136 | } else { 137 | correctionNeededCounter = 1; 138 | currentFrontForCorrection = currentFront; 139 | currentRearForCorrection = currentRear - 1; 140 | } 141 | oneMatch = 1; 142 | } 143 | } 144 | 145 | if (currentRear != 10) { 146 | // check difference to crankFrequency if rear would be shifted up by one gear 147 | tmp = currentRoundsWheel / (frontGear[currentFront - 1] / rearGear[currentRear]); 148 | if (tmp > crankFrequency * 0.98f && tmp < crankFrequency * 1.02f) { 149 | if (currentFrontForCorrection == currentFront && currentRearForCorrection == (currentRear + 1)) { 150 | correctionNeededCounter++; 151 | } else { 152 | correctionNeededCounter = 1; 153 | currentFrontForCorrection = currentFront; 154 | currentRearForCorrection = currentRear + 1; 155 | } 156 | oneMatch = 1; 157 | } 158 | } 159 | 160 | if (currentFront == 1) { 161 | // check difference to crankFrequency if front would be shifted up by one gear 162 | tmp = currentRoundsWheel / (frontGear[currentFront] / rearGear[currentRear - 1]); 163 | if (tmp > crankFrequency * 0.98f && tmp < crankFrequency * 1.02f) { 164 | if (currentFrontForCorrection == (currentFront + 1) && currentRearForCorrection == currentRear) { 165 | correctionNeededCounter++; 166 | } else { 167 | correctionNeededCounter = 1; 168 | currentFrontForCorrection = currentFront + 1; 169 | currentRearForCorrection = currentRear; 170 | } 171 | oneMatch = 1; 172 | } 173 | } else { 174 | // check difference to crankFrequency if front would be shifted down by one gear 175 | tmp = currentRoundsWheel / (frontGear[currentFront - 2] / rearGear[currentRear - 1]); 176 | if (tmp > crankFrequency * 0.98f && tmp < crankFrequency * 1.02f) { 177 | if (currentFrontForCorrection == (currentFront - 1) && currentRearForCorrection == currentRear) { 178 | correctionNeededCounter++; 179 | } else { 180 | correctionNeededCounter = 1; 181 | currentFrontForCorrection = currentFront - 1; 182 | currentRearForCorrection = currentRear; 183 | } 184 | oneMatch = 1; 185 | } 186 | } 187 | 188 | // the error was not one of the discrete values 189 | if (!oneMatch) { 190 | correctionNeededCounter = 0; 191 | } 192 | 193 | // if we have encountered the error often enough and it was allways the same we need a gear shift 194 | if (correctionNeededCounter == 3) { 195 | 196 | if (currentFrontForCorrection > currentFront) { 197 | // shift Front up by one. 198 | currentFront++; 199 | #ifdef ADRUINO 200 | generateSignal(1, frontUpPin); 201 | Serial.println("gear UP correction"); 202 | #endif 203 | 204 | } 205 | if (currentFrontForCorrection < currentFront) { 206 | // shift Front down by one. 207 | currentFront--; 208 | #ifdef ADRUINO 209 | generateSignal(1, frontDownPin); 210 | Serial.println("gear DOWN correction"); 211 | #endif 212 | 213 | } 214 | if (currentRearForCorrection > currentRear) { 215 | // shift Rear up by one. 216 | currentRear++; 217 | #ifdef ADRUINO 218 | generateSignal(1, rearUpPin); 219 | Serial.println("gear UP correction"); 220 | #endif 221 | 222 | } 223 | if (currentRearForCorrection < currentRear) { 224 | // shift Rear down by one. 225 | currentRear--; 226 | #ifdef ADRUINO 227 | generateSignal(1, rearDownPin); 228 | Serial.println("gear DOWN correction"); 229 | #endif 230 | 231 | } 232 | } 233 | correctionNeededCounter = 0; 234 | } 235 | 236 | 237 | //**************************************** GEAR UP ********************************* 238 | // Method for shifting upwards. 239 | void gearUp(){ 240 | // small front chainblade. 241 | if (currentFront == 1){ 242 | if (currentRear < 8){ 243 | // generate Signal to shift up rear by one gear. 244 | #ifdef ARDUINO 245 | generateSignal(1, rearUpPin); 246 | #endif 247 | currentRear++; 248 | return; 249 | } 250 | // rear has reached 8, so we have to shift to bigger front chainblade. 251 | else { 252 | // generate Signal to shift up Front by one gear and shift down rear by 4 gears. 253 | #ifdef ARDUINO 254 | generateSignal(1, frontUpPin); 255 | generateSignal(4, rearDownPin); 256 | #endif 257 | currentFront = 2; 258 | currentRear = 4; 259 | return; 260 | } 261 | } 262 | // big front chainblade 263 | else { 264 | // already in biggest gear. 265 | if (currentRear == 10){ 266 | return; 267 | } 268 | // not yet in biggest gear. Shift up. 269 | else { 270 | // generate Signal to shift up rear by one gear. 271 | #ifdef ARDUINO 272 | generateSignal(1, rearUpPin); 273 | #endif 274 | currentRear++; 275 | return; 276 | } 277 | } 278 | } 279 | 280 | // ***************************************** GEAR DOWN ********************************** 281 | // Method for shifting downwards. 282 | void gearDown(){ 283 | // small front chainblade. 284 | if (currentFront == 1){ 285 | // already smallest gear. Do nothing. 286 | if (currentRear == 1){ 287 | return; 288 | } 289 | // singel rear downshift. 290 | else { 291 | // generate signal to shift down rear by 1 gear. 292 | #ifdef ARDUINO 293 | generateSignal(1, rearDownPin); 294 | #endif 295 | currentRear--; 296 | return; 297 | } 298 | } 299 | // big front chainblade 300 | else { 301 | if (currentRear > 3){ 302 | // generate Signal to shift down rear by one gear. 303 | #ifdef ARDUINO 304 | generateSignal(1, rearDownPin); 305 | #endif 306 | currentRear--; 307 | return; 308 | } 309 | // rear has reached 3, so we have to shift to smaller front chainblade. 310 | else { 311 | // generate Signal to shift down Front by one gear and shift up rear by 4 gears. 312 | #ifdef ARDUINO 313 | generateSignal(1, frontDownPin); 314 | generateSignal(4, rearUpPin); 315 | #endif 316 | currentFront = 1; 317 | currentRear = 7; 318 | return; 319 | } 320 | } 321 | } 322 | 323 | 324 | // ****************************** RESET GEAR *************************************************** 325 | #ifndef ARDUINO 326 | // Reset gears means shifting to 1, 1. 327 | // Method called after receiving reset - Signal by smartphone. 328 | void resetGears() { 329 | currentFront = 1; 330 | currentRear = 1; 331 | 332 | y[0] = 1; // currentFront 333 | y[1] = 1; // currentRear 334 | } 335 | #else 336 | // Reset gears means shifting to 1, 1. 337 | // Method called after receiving reset - Signal by smartphone. 338 | void resetGears() { 339 | generateSignal(1,frontDownPin); 340 | generateSignal(10,rearDownPin); 341 | 342 | currentFront = 1; 343 | currentRear = 1; 344 | } 345 | #endif 346 | 347 | // ************************* COMPUTE SPEED AND CRANK FREQUENCY ***************************************** 348 | void computeSpeedAndFrequency() { 349 | // compute speed with km/h = (mm / ms) * 3.6 350 | currentSpeed = ((float)rad / (float)difference) * 3.6f; 351 | // compute rounds of wheel U / min = 60000 / difference[ms]. 352 | currentRoundsWheel = 60000 / (float)difference; 353 | // compute frequency of crank. 354 | frequency = currentRoundsWheel / (frontGear[currentFront - 1] / rearGear[currentRear - 1]); 355 | } 356 | 357 | 358 | // ******************************************** MAIN LOOP *************************************************** 359 | void mainIt() { 360 | #ifndef ARDUINO 361 | // Read input from simulation; 362 | if (u[1] == 20) { 363 | rad = circumference[0]; 364 | } else if (u[1] == 24) { 365 | rad = circumference[1]; 366 | } else if (u[1] == 26) { 367 | rad = circumference[2]; 368 | } else if (u[1] == 28) { 369 | rad = circumference[3]; 370 | } 371 | 372 | difference = u[3]; 373 | // crankDifference = u[4]; 374 | 375 | // target frequency - general Div. 376 | int tarFreqMinusDiv = u[0] - ((u[0] / 100.0f) * u[2]); 377 | // target frequency + general Div. 378 | int tarFreqPlusDiv = u[0] + ((u[0] / 100.0f) * u[2]); 379 | 380 | computeSpeedAndFrequency(); 381 | crankFrequency = frequency; 382 | // crankFrequency = 60000 / (float) crankDifference; 383 | y[0] = currentFront; 384 | y[1] = currentRear; 385 | y[2] = currentSpeed; 386 | y[3] = frequency; 387 | y[4] = frequency; 388 | 389 | // Adapt to changes in current frequency. 390 | if (frequency < tarFreqMinusDiv) { 391 | gearDown(); 392 | } 393 | if (frequency > tarFreqPlusDiv) { 394 | gearUp(); 395 | } 396 | #endif 397 | 398 | 399 | // The idea is that if we are in a wrong gear the error can only be a 400 | // discrete value because of the gears fix number of teeth. 401 | // If we are not turning the crank at the maximum possible that error will 402 | // change. If it does not change we should do a correction. 403 | // We assume that we are not more than 1 Gear away from the real one. 404 | // If so we should better implement a reset function. 405 | // It only checks if the crankSensor has a signal with about 60RPM that 406 | // means that 3 singals are 3 seconds. -> 3rd "sucessful" check will do the 407 | // correction. 408 | if (frequency < crankFrequency * 0.96f || frequency > crankFrequency * 1.04f) { 409 | checkGear(); 410 | } 411 | 412 | #ifndef ARDUINO 413 | if (u[5] == 1) { 414 | resetGears(); 415 | } 416 | #endif 417 | } 418 | 419 | void initMethod() { 420 | currentFront = 1; 421 | currentRear = 1; 422 | } 423 | -------------------------------------------------------------------------------- /AutomaticShifting/AutomaticShiftingMatlab/AutomaticShiftingMatlab.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Automatic gear shifting. 3 | 4 | Authors: Michael Petretti (michael@petretti.de) 5 | Björn Hagemeister 6 | @ University of Freiburg, Germany, 2015. 7 | 8 | ************************************************************************************** 9 | Front and rear in seperated Arrays now. 10 | Front: Small Chainblade is left and therefor no. 1 in Array. 11 | Rear: Biggest is left and therefor no. 1 in Array. 12 | Shifting up can be done by simply increase a position in either array. 13 | -> means currentFront++ is shifting to a bigger gear the same way as 14 | currentRear-- is shifting down and the other way round. 15 | 16 | To prevent wearing out the mechanics I decided to not use the gears 1-9, 1-10, 2-1, 2-2, 17 | where the first digit is front chainblade and second is rear chainblade. 18 | 19 | To avoid unnecessary shift operations I choose the following plan to shift gears: 20 | Shifting up: 1-1, 1-2, .... , 1-8, 2-4, 2-5, .... , 2-10, 21 | Shifting down: 2-10, 2-9, .... , 2-3, 1-7, 1-6, .... , 1-1 22 | 23 | I have decided to write a "void generateSignal(int count, int mode)" Methode. 24 | count = the number of gears one wants to shift 25 | mode: 1 = front up, 2 = front down, 3 = rear up, 4 = rear down 26 | 27 | Connections at arduino: 28 | Arduino <--> Relais: 29 | VCC -- 5V 30 | IN4 -- Pin 8 (rear up) 31 | IN3 -- Pin 12 (rear down) 32 | IN2 -- Pin 4 (front up) 33 | IN1 -- Pin 7 (front down) 34 | Arduino <--> board: 35 | TX(bluetooth) -- Pin 10 36 | RX(bluetooth) -- Pin 11 37 | Velocity senor pin -- Pin 2 38 | crank frequency sensor pin -- Pin 3 39 | Relais <--> shifting (bike) 40 | Connect the wires of the back shifting to the left from left to right: 41 | (free) red blue (free) green yellow 42 | 43 | Connect the wires of the front shifting to the right from left to right: 44 | (free) red blue (free) green yellow 45 | ************************************************************************************** 46 | Bluetooth - communication: 47 | Using SoftwareSerial(RX, TX) for bluetooth communication to beware hardware serial 48 | for debugging information (current speed, RPM, ...). 49 | 50 | set up a new serial port: 51 | SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin); 52 | What should be communicated to/from the smartphone via bluetoot? 53 | --> Key words are necessary for receiving and sending Key=Value - Pairs. 54 | Sending information: 55 | - current velocity --> Key = VELOCITY 56 | - current crank frequency --> Key = CURR_FREQUENCY 57 | - current gear (front gear) [ --> Key = FRONT_GEAR 58 | - current gear (rear gear) --> Key = REAR_GEAR 59 | - current gradient at hill (right now no sensor, sending 0) --> Key = GRADIENT 60 | Receiving information: 61 | - target crank frequency --> Key = TAR_FREQUENCY 62 | - frequency divergence at a hill [high, middel, low] --> Key = HILL_DIV 63 | - general frequency divergence (sensitivity) [%] --> Key = GENERAL_DIV 64 | - maybe RESET - signal for entering defined state (first gear). --> Key = RESET 65 | - wheel circumference [zoll] --> intern calculation in [mm] --> Key = CIRCUMFERENCE 66 | 67 | IDEA for SENDING: 68 | Interrupt timer: sending every X seconds VELOCITY, CURR_FREQUENCY, GEAR to smartphone 69 | IDEA for RECEIVING: 70 | Listen to serial port in main loop: 71 | 72 | How to send/receive data: 73 | Key=Value - Pairs. --> Delimiter is necessary to catch end of message. 74 | Use '!' as delimiter, example would be for sending velocity: "VELOCITY=35.5!" 75 | That the smartphone knows which data are from the arduino surround the information by "#...info...$". 76 | --> its easier for gino to extract the incoming information on the smartphone. 77 | 78 | Using pin 10 and 11 for SoftwareSerial. 79 | 80 | ************************************************************************************** 81 | Bisher: 82 | Messung der Zeit zwischen zwei Peaks des Reed - Sensors, was der Zeit einer Rad - 83 | drehung entspricht. 84 | Geschwindigkeit = Radumfang[mm] / Zeit[ms] * 3.6 = km/h 85 | Trittfrequenz = 60000 / (Zeit[ms] * Übersetzungsverhältnis(aktueller Gang)) = U/min 86 | 87 | Reedsensor ist entprellt, so dass nur auf eine Steigende Flanke bei Zeit reagiert wird. 88 | 89 | Geschwindigkeitsmessung passt sehr gut zu gekauftem Tacho. 90 | */ 91 | #define ARDUINO 92 | #include 93 | #include "usedMethods.h" 94 | 95 | // Create new SoftwareSerial for bluetooth communication. 96 | SoftwareSerial 97 | bluetoothSerial = SoftwareSerial(bluetoothRX, bluetoothTX); 98 | 99 | 100 | // Variables only used in the arduino file. 101 | long 102 | timePreviousHigh, // time of last peak of wheel sensor [ms] 103 | timeActualHigh, // time of current peak of wheel sensor [ms] 104 | crankTimePreviousHigh, // time of last peak of crank sensor [ms] 105 | crankTimeActualHigh; // time of current peak of crank sensor [ms] 106 | 107 | // WheelDebounceTime: 108 | float wheelDebouncingTime; 109 | 110 | 111 | // Defining inputString, key and value. 112 | String 113 | inputString = ""; // inputString for reading from bluetooth 114 | String 115 | key = ""; // key for storing incoming key String 116 | String 117 | value = ""; // key for storing incoming value String 118 | // Defining keys for bluetooth communication. 119 | const String 120 | VELOCITY = "VELOCITY"; 121 | // Sending information. 122 | const String 123 | CURR_FREQUENCY = "CURR_FREQUENCY"; 124 | const String 125 | FRONT_GEAR = "FRONT_GEAR"; 126 | const String 127 | REAR_GEAR = "REAR_GEAR"; 128 | const String 129 | GRADIENT = "GRADIENT"; 130 | // Receiving information. 131 | const String 132 | TAR_FREQUENCY = "TAR_FREQUENCY"; 133 | const String 134 | HILL_DIV = "HILL_DIV"; 135 | const String 136 | GENERAL_DIV = "GENERAL_DIV"; 137 | const String 138 | RESET = "RESET"; 139 | const String 140 | CIRCUMFERENCE = "CIRCUMFERENCE"; 141 | 142 | // Defining values for bluetooth communication. 143 | // Sending information. 144 | String 145 | VELOCITY_VAL = ""; 146 | String 147 | CURR_FREQUENCY_VAL = ""; 148 | String 149 | FRONT_GEAR_VAL = ""; 150 | String 151 | REAR_GEAR_VAL = ""; 152 | String 153 | GRADIENT_VAL = "0"; 154 | // Receiving information. 155 | String 156 | TAR_FREQUENCY_VAL = ""; 157 | String 158 | HILL_DIV_VAL = ""; 159 | String 160 | GENERAL_DIV_VAL = ""; 161 | String 162 | RESET_VAL = ""; 163 | String 164 | CIRCUMFERENCE_VAL = ""; 165 | 166 | // Separator. 167 | const String SEPARATOR = "!"; 168 | // Equalsign. 169 | const String EQUAL = "="; 170 | 171 | // Begin of information 172 | const String BEGIN = "#"; 173 | // End of information 174 | const String END = "$"; 175 | 176 | 177 | void setup() { 178 | // initialize the input pins as INPUT. 179 | pinMode(inputPinS1, INPUT_PULLUP); 180 | pinMode(crankRpmInputPin, INPUT_PULLUP); 181 | // initilize output pins for shifting. 182 | pinMode(frontUpPin, OUTPUT); 183 | pinMode(frontDownPin, OUTPUT); 184 | pinMode(rearUpPin, OUTPUT); 185 | pinMode(rearDownPin, OUTPUT); 186 | // initialize pins for sending/receiving data via bluetooth. 187 | pinMode(bluetoothRX, INPUT); 188 | pinMode(bluetoothTX, OUTPUT); 189 | 190 | digitalWrite(frontUpPin, HIGH); 191 | digitalWrite(frontDownPin, HIGH); 192 | digitalWrite(rearUpPin, HIGH); 193 | digitalWrite(rearDownPin, HIGH); 194 | 195 | // using interrupt for measuring time between to edges. 196 | // 0 stands for inputPin = 2 197 | // 1 stands for inputPin = 3 198 | attachInterrupt(0, triggerSensor1, FALLING); 199 | attachInterrupt(1, triggerSensor2, FALLING); 200 | 201 | 202 | // Setup Timer for timer interrupt. 203 | cli(); // stop interrupts 204 | //set timer1 interrupt at 1Hz 205 | TCCR1A = 0;// set entire TCCR1A register to 0 206 | TCCR1B = 0;// same for TCCR1B 207 | TCNT1 = 0;//initialize counter value to 0 208 | // set compare match register for 1hz increments 209 | OCR1A = 15624;// = (16*10^6) / (1*prescaler) - 1 (must be <65536) 210 | // turn on CTC mode 211 | TCCR1B |= (1 << WGM12); 212 | // Set CS12 and CS10 bits for 1024 prescaler 213 | TCCR1B |= (1 << CS12) | (1 << CS10); 214 | // enable timer compare interrupt 215 | TIMSK1 |= (1 << OCIE1A); 216 | sei(); // allow interrupts 217 | 218 | // Initilize some default values before receiving first information from smartphone. 219 | TAR_FREQUENCY_VAL = "90"; 220 | GENERAL_DIV_VAL = "10"; 221 | RESET_VAL = "0"; 222 | CIRCUMFERENCE_VAL = "28"; 223 | rad = 2110; // Circumference for 28" wheel. 224 | 225 | //initialize serial communication. 226 | Serial.begin(9600); 227 | bluetoothSerial.begin(9600); 228 | 229 | /******************************************************************************************/ 230 | /******************************************************************************************/ 231 | // IMPORTANT: AFTER ACTIVATING THE ARDUINO YOU HAVE 5 SECONDS BEFORE THE GEARS START SHIFTING 232 | // YOU MUST TURN THE PEDALS UNTILL THE DEFINED STATE OF 1-1 IS REACHED 233 | /******************************************************************************************/ 234 | delay(5000); 235 | generateSignal(1,frontDownPin); 236 | generateSignal(10,rearDownPin); 237 | 238 | currentFront = 1; 239 | currentRear = 1; 240 | /******************************************************************************************/ 241 | 242 | timePreviousHigh = millis(); 243 | crankTimePreviousHigh = millis(); 244 | 245 | // If we expecting a maximum speed of 70 km/h = 19,4 m/s, we get 19,4 m / 2,110 m = 9,2 246 | // interrupts per second. So the interrupt occures all 1000 / 9.2 = 108 ms. 247 | wheelDebouncingTime = 1000.0 / (19.4 / (float)rad); 248 | } 249 | 250 | void loop() { 251 | // Receive information from smartphone. 252 | receiveInformation(); 253 | 254 | // Display speed and frequency only every second. 255 | delay(1000); 256 | Serial.print("Speed: "); 257 | Serial.print(currentSpeed); 258 | Serial.println(" km/h"); 259 | 260 | Serial.print("Computed Crankfreq: "); 261 | Serial.print(frequency); 262 | Serial.println(" U/min"); 263 | 264 | Serial.print("Measured Crankfreq: "); 265 | Serial.println(crankFrequency); 266 | 267 | Serial.print("Current Front: "); 268 | Serial.println(currentFront); 269 | 270 | Serial.print("Current Rear: "); 271 | Serial.println(currentRear); 272 | Serial.println(""); 273 | 274 | mainIt(); 275 | 276 | if (timePreviousHigh < (millis() - 1500)) { 277 | currentSpeed = 0.0; 278 | frequency = 0.0; 279 | } 280 | 281 | // target frequency - general Div. 282 | int tarFreqMinusDiv = TAR_FREQUENCY_VAL.toInt() - (((float)TAR_FREQUENCY_VAL.toInt() / 100.0f) * (float)GENERAL_DIV_VAL.toInt()); 283 | // target frequency + general Div. 284 | int tarFreqPlusDiv = TAR_FREQUENCY_VAL.toInt() + (((float)TAR_FREQUENCY_VAL.toInt() / 100.0f) * (float)GENERAL_DIV_VAL.toInt()); 285 | 286 | // Adapt to changes in current frequency. 287 | if (frequency < tarFreqMinusDiv) { 288 | gearDown(); 289 | } 290 | if (frequency > tarFreqPlusDiv) { 291 | gearUp(); 292 | } 293 | } 294 | 295 | // define interrupt service routine for timer declared above. 296 | ISR(TIMER1_COMPA_vect){ 297 | sendInformation(); 298 | } 299 | 300 | // Method for triggering sensor one and cumpute current speed 301 | // and calculate current crank frequency. 302 | void triggerSensor1() { 303 | timeActualHigh = millis(); 304 | difference = timeActualHigh - timePreviousHigh; 305 | 306 | // If not, wait for next occuring interrupt. 307 | if (difference >= wheelDebouncingTime) { 308 | computeSpeedAndFrequency(); 309 | timePreviousHigh = millis(); 310 | } 311 | } 312 | 313 | // Method for handling triggered Signal of sensor two and cumputing actual 314 | // crank frequency. 315 | void triggerSensor2() { 316 | crankTimeActualHigh = millis(); 317 | crankDifference = crankTimeActualHigh - crankTimePreviousHigh; 318 | 319 | // Only compute new crank frequency, if crank difference is bigger than crankDebouncingTime. 320 | // If not, ignore current measurement and wait for next occuring interrupt. 321 | if (crankDifference >= crankDebouncingTime) { 322 | crankFrequency = 60000 / (float) crankDifference; 323 | 324 | crankTimePreviousHigh = millis(); 325 | } 326 | } 327 | 328 | // Send all current information (velocitiy, current frequency, front gear, rear gear 329 | // and gradient) to the smartphone via bluetooth. 330 | void sendInformation() { 331 | // Serial.print("Sending information ... "); 332 | // Begin sending information. 333 | bluetoothSerial.print(BEGIN); 334 | // VELOCITY 335 | bluetoothSerial.print(VELOCITY); 336 | bluetoothSerial.print(EQUAL); 337 | bluetoothSerial.print(currentSpeed); 338 | bluetoothSerial.print(SEPARATOR); 339 | 340 | // CURR_FREQUENCY 341 | bluetoothSerial.print(CURR_FREQUENCY); 342 | bluetoothSerial.print(EQUAL); 343 | bluetoothSerial.print(frequency); 344 | bluetoothSerial.print(SEPARATOR); 345 | 346 | // FRONT_GEAR 347 | bluetoothSerial.print(FRONT_GEAR); 348 | bluetoothSerial.print(EQUAL); 349 | bluetoothSerial.print(currentFront); 350 | bluetoothSerial.print(SEPARATOR); 351 | 352 | // REAR_GEAR 353 | bluetoothSerial.print(REAR_GEAR); 354 | bluetoothSerial.print(EQUAL); 355 | bluetoothSerial.print(currentRear); 356 | bluetoothSerial.print(SEPARATOR); 357 | 358 | // GRADIENT 359 | bluetoothSerial.print(GRADIENT); 360 | bluetoothSerial.print(EQUAL); 361 | bluetoothSerial.print(GRADIENT_VAL); 362 | bluetoothSerial.print(SEPARATOR); 363 | //End sending information. 364 | bluetoothSerial.print(END); 365 | // Serial.println("done"); 366 | } 367 | 368 | // Store received values to variables for controlling the shifting. 369 | void storeToValue() { 370 | if (key.equals(TAR_FREQUENCY)) { 371 | TAR_FREQUENCY_VAL = value; 372 | } else if (key.equals(HILL_DIV)) { 373 | HILL_DIV_VAL = value; 374 | } else if (key.equals(GENERAL_DIV)) { 375 | GENERAL_DIV_VAL = value; 376 | } else if (key.equals(RESET)) { 377 | RESET_VAL = value; 378 | } else if (key.equals(CIRCUMFERENCE)) { 379 | CIRCUMFERENCE_VAL = value; 380 | if (CIRCUMFERENCE_VAL.equals("20")) { 381 | rad = circumference[0]; 382 | } else if (CIRCUMFERENCE_VAL.equals("24")) { 383 | rad = circumference[1]; 384 | } else if (CIRCUMFERENCE_VAL.equals("26")) { 385 | rad = circumference[2]; 386 | } else if (CIRCUMFERENCE_VAL.equals("28")) { 387 | rad = circumference[3]; 388 | } 389 | } 390 | if (RESET_VAL.equals("1")) { 391 | // Reset the bike. Shift front to first and rear to first. 392 | resetGears(); 393 | RESET_VAL = "0"; 394 | } 395 | } 396 | 397 | // Handling the receiving information. 398 | void receiveInformation() { 399 | while (bluetoothSerial.available()) { 400 | char inChar = (char)bluetoothSerial.read(); 401 | // bluetoothSerial.print(inChar); 402 | Serial.print(inChar); 403 | if (inChar != '=' && inChar != '!') { 404 | inputString += inChar; 405 | } else if (inChar == '=') { 406 | key = inputString; 407 | //Serial.print("key = "); 408 | //Serial.println(key); 409 | inputString = ""; 410 | } else if (inChar == '!') { 411 | value = inputString; 412 | inputString = ""; 413 | storeToValue(); 414 | // bluetoothSerial.println(""); 415 | // Serial.println(""); 416 | // stringComplete = true; 417 | break; 418 | } 419 | } 420 | } 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | -------------------------------------------------------------------------------- /AutomaticShiftingNoMatlab/AutomaticShiftingNoMatlab.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Automatic gear shifting. 3 | 4 | Authors: Michael Petretti (michael@petretti.de) 5 | Björn Hagemeister 6 | @ University of Freiburg, Germany, 2015. 7 | 8 | 9 | ************************************************************************************** 10 | Front and rear in seperated Arrays now. 11 | Front: Small Chainblade is left and therefor no. 1 in Array. 12 | Rear: Biggest is left and therefor no. 1 in Array. 13 | Shifting up can be done by simply increase a position in either array. 14 | -> means currentFront++ is shifting to a bigger gear the same way as 15 | currentRear-- is shifting down and the other way round. 16 | 17 | To prevent wearing out the mechanics I decided to not use the gears 1-9, 1-10, 2-1, 2-2, 18 | where the first digit is front chainblade and second is rear chainblade. 19 | 20 | To avoid unnecessary shift operations I choose the following plan to shift gears: 21 | Shifting up: 1-1, 1-2, .... , 1-8, 2-4, 2-5, .... , 2-10, 22 | Shifting down: 2-10, 2-9, .... , 2-3, 1-7, 1-6, .... , 1-1 23 | 24 | I have decided to write a "void generateSignal(int count, int mode)" Methode. 25 | count = the number of gears one wants to shift 26 | mode: 1 = front up, 2 = front down, 3 = rear up, 4 = rear down 27 | 28 | Connection from arduino to relay 29 | (http://www.amazon.de/Kanal-Relais-Module-Arduino-TTL-Logik/dp/B00ALNJN72/ref=sr_1_7?ie=UTF8&qid=1422019451&sr=8-7&keywords=relais): 30 | VCC -- 5V 31 | IN4 -- Pin 8 (rear up) 32 | IN3 -- Pin 12 (rear down) 33 | IN2 -- Pin 4 (front up) 34 | IN1 -- Pin 7 (front down) 35 | ************************************************************************************** 36 | Bluetooth - communication: 37 | Using SoftwareSerial(RX, TX) for bluetooth communication to beware hardware serial 38 | for debugging information (current speed, RPM, ...). 39 | 40 | set up a new serial port: 41 | SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin); 42 | What should be communicated to/from the smartphone via bluetoot? 43 | --> Key words are necessary for receiving and sending Key=Value - Pairs. 44 | Sending information: 45 | - current velocity --> Key = VELOCITY 46 | - current crank frequency --> Key = CURR_FREQUENCY 47 | - current gear (front gear) [ --> Key = FRONT_GEAR 48 | - current gear (rear gear) --> Key = REAR_GEAR 49 | - current gradient at hill (right now no sensor, sending 0) --> Key = GRADIENT 50 | Receiving information: 51 | - target crank frequency --> Key = TAR_FREQUENCY 52 | - frequency divergence at a hill [high, middel, low] --> Key = HILL_DIV 53 | - general frequency divergence (sensitivity) [%] --> Key = GENERAL_DIV 54 | - maybe RESET - signal for entering defined state (first gear). --> Key = RESET 55 | - wheel circumference [zoll] --> intern calculation in [mm] --> Key = CIRCUMFERENCE 56 | 57 | IDEA for SENDING: 58 | Interrupt timer: sending every X seconds VELOCITY, CURR_FREQUENCY, GEAR to smartphone 59 | IDEA for RECEIVING: 60 | Listen to serial port in main loop: 61 | 62 | How to send/receive data: 63 | Key=Value - Pairs. --> Delimiter is necessary to catch end of message. 64 | Use '!' as delimiter, example would be for sending velocity: "VELOCITY=35.5!" 65 | That the smartphone knows which data are from the arduino surround the information by "#...info...$". 66 | --> its easier for gino to extract the incoming information on the smartphone. 67 | 68 | Using pin 10 and 11 for SoftwareSerial. 69 | */ 70 | 71 | //*********************************** BLUETOOTH SETTINGS **************************************** 72 | #include // library for using SoftwareSerial 73 | 74 | // Defining keys for bluetooth communication. 75 | const String 76 | // Sending information. 77 | VELOCITY = "VELOCITY", 78 | CURR_FREQUENCY = "CURR_FREQUENCY", 79 | FRONT_GEAR = "FRONT_GEAR", 80 | REAR_GEAR = "REAR_GEAR", 81 | GRADIENT = "GRADIENT", 82 | // Receiving information. 83 | TAR_FREQUENCY = "TAR_FREQUENCY", 84 | HILL_DIV = "HILL_DIV", 85 | GENERAL_DIV = "GENERAL_DIV", 86 | RESET = "RESET", 87 | CIRCUMFERENCE = "CIRCUMFERENCE"; 88 | 89 | // Defining values for bluetooth communication. 90 | String 91 | // Sending information. 92 | VELOCITY_VAL = "", 93 | CURR_FREQUENCY_VAL = "", 94 | FRONT_GEAR_VAL = "", 95 | REAR_GEAR_VAL = "", 96 | GRADIENT_VAL = "0", 97 | // Receiving information. 98 | TAR_FREQUENCY_VAL = "", 99 | HILL_DIV_VAL = "", 100 | GENERAL_DIV_VAL = "", 101 | RESET_VAL = "", 102 | CIRCUMFERENCE_VAL = ""; 103 | 104 | // Separator. 105 | const String SEPARATOR = "!"; 106 | // Equalsign. 107 | const String EQUAL = "="; 108 | 109 | // Begin of information 110 | const String BEGIN = "#"; 111 | // End of information 112 | const String END = "$"; 113 | 114 | // defining pins for SoftwareSerial: 115 | const int 116 | bluetoothTX = 10, 117 | bluetoothRX = 11; 118 | 119 | // Create new SoftwareSerial for bluetooth communication. 120 | SoftwareSerial 121 | bluetoothSerial = SoftwareSerial(bluetoothRX, bluetoothTX); 122 | 123 | // Defining inputString, key and value. 124 | String 125 | inputString = "", // inputString for reading from bluetooth 126 | key = "", // key for storing incoming key String 127 | value = ""; // key for storing incoming value String 128 | 129 | // Define stringComplete flag for knowing the last character. 130 | boolean stringComplete = false; 131 | 132 | // Send the sending information every x seconds. 133 | const int every_X_Seconds = 1; 134 | 135 | //************************************************************************************************ 136 | 137 | 138 | //************************************* MEASURING SETTINGS *************************************** 139 | // Using Pin2 of digital pins for input of reed sensor at wheel. 140 | const int inputPinS1 = 2; 141 | // Using Pin13 of digital pins for input of reed sensor at crank. 142 | const int crankRpmInputPin = 3; 143 | 144 | long 145 | timePreviousHigh, // time of last peak of wheel sensor [ms] 146 | timeActualHigh, // time of current peak of wheel sensor [ms] 147 | difference, // time difference between last and current peak wheel sensor [ms] 148 | crankTimePreviousHigh, // time of last peak of crank sensor [ms] 149 | crankTimeActualHigh, // time of current peak of crank sensor [ms] 150 | crankDifference; // time difference between last current peak crank sensor [ms] 151 | 152 | int 153 | currentFront, // current front chainblade 154 | currentRear; // current rear chainblade 155 | 156 | float 157 | currentSpeed, // current speed [km/h] 158 | currentRoundsWheel, // current rounds of wheel [U/min] 159 | frequency = 50.0f, // computed crank frequency [U/min] 160 | crankFrequency; // measured crank frequency [U/min] 161 | 162 | // the gears if correction happens to bee needed 163 | int 164 | currentFrontForCorrection, // front gear, which should be current gear for correction 165 | currentRearForCorrection, // rear gear, which should be current gear for correction 166 | correctionNeededCounter = 0; // counter for how often is measured that correction is needed 167 | 168 | // WheelDebounceTime: 169 | // If we expecting a maximum speed of 70 km/h = 19,4 m/s, we get 19,4 m / 2,035 m = 9,5 170 | // interrupts per second. So the interrupt occures all 104ms. 171 | int wheelDebouncingTime = 104; 172 | 173 | // crankDebouncingTime: 174 | // We assume, that there is nobody who makes more than 120 RPM at the crank so our 175 | // crankDebouncingTime needs to 500 ms, for getting max 2 interrupts per second by the crank 176 | // sensor. 177 | int crankDebouncingTime = 500; 178 | //************************************************************************************************* 179 | 180 | // ****************************** SHIFTING SETTINGS ********************************** 181 | // Using Pins 4,7,8,12 as Outputpins to wire up the Relais 182 | const int 183 | frontUpPin = 4, // pin 4 for shifting front up 184 | frontDownPin = 7, // pin 7 for shifting front down 185 | rearUpPin = 8, // pin 8 for shifting rear up 186 | rearDownPin = 12; // pin 12 for shifting rear down 187 | 188 | const float 189 | frontGear[] = {34, 50}, 190 | rearGear[] = {25, 23, 21, 19, 17, 16, 15, 14, 13, 12}; 191 | 192 | // Circumference in mm for 20", 24", 26", 28" 193 | const int 194 | circumference[] = {1530, 1860, 1940, 2110}; 195 | int rad = 2110; // wheel circumstance in mm 28" 196 | // *********************************************************************************** 197 | 198 | void setup() { 199 | // initialize the input pins as INPUT. 200 | pinMode(inputPinS1, INPUT_PULLUP); 201 | pinMode(crankRpmInputPin, INPUT_PULLUP); 202 | // initilize output pins for shifting. 203 | pinMode(frontUpPin, OUTPUT); 204 | pinMode(frontDownPin, OUTPUT); 205 | pinMode(rearUpPin, OUTPUT); 206 | pinMode(rearDownPin, OUTPUT); 207 | // initialize pins for sending/receiving data via bluetooth. 208 | pinMode(bluetoothRX, INPUT); 209 | pinMode(bluetoothTX, OUTPUT); 210 | 211 | digitalWrite(frontUpPin, HIGH); 212 | digitalWrite(frontDownPin, HIGH); 213 | digitalWrite(rearUpPin, HIGH); 214 | digitalWrite(rearDownPin, HIGH); 215 | 216 | // using interrupt for measuring time between to edges. 217 | // 0 stands for inputPin = 2 218 | // 1 stands for inputPin = 3 219 | attachInterrupt(0, triggerSensor1, FALLING); 220 | attachInterrupt(1, triggerSensor2, FALLING); 221 | 222 | 223 | // Setup Timer for timer interrupt. 224 | cli(); // stop interrupts 225 | //set timer1 interrupt at 1Hz 226 | TCCR1A = 0;// set entire TCCR1A register to 0 227 | TCCR1B = 0;// same for TCCR1B 228 | TCNT1 = 0;//initialize counter value to 0 229 | // set compare match register for 1hz increments 230 | OCR1A = 15624;// = (16*10^6) / (1*prescaler) - 1 (must be <65536) 231 | // turn on CTC mode 232 | TCCR1B |= (1 << WGM12); 233 | // Set CS12 and CS10 bits for 1024 prescaler 234 | TCCR1B |= (1 << CS12) | (1 << CS10); 235 | // enable timer compare interrupt 236 | TIMSK1 |= (1 << OCIE1A); 237 | sei(); // allow interrupts 238 | 239 | // Initilize some default values before receiving first information from smartphone. 240 | TAR_FREQUENCY_VAL = "50"; 241 | GENERAL_DIV_VAL = "10"; 242 | RESET_VAL = "0"; 243 | 244 | // Reserve space for inputString, key and value. 245 | inputString.reserve(50); 246 | key.reserve(50); 247 | value.reserve(50); 248 | 249 | //initialize serial communication. 250 | Serial.begin(9600); 251 | bluetoothSerial.begin(9600); 252 | 253 | /******************************************************************************************/ 254 | /******************************************************************************************/ 255 | // IMPORTANT: AFTER ACTIVATING THE ARDUINO YOU HAVE 5 SECONDS BEFORE THE GEARS START SHIFTING 256 | // YOU MUST TURN THE PEDALS UNTILL THE DEFINED STATE OF 1-1 IS REACHED 257 | /******************************************************************************************/ 258 | delay(5000); 259 | generateSignal(1,frontDownPin); 260 | generateSignal(10,rearDownPin); 261 | 262 | currentFront = 1; 263 | currentRear = 1; 264 | /******************************************************************************************/ 265 | 266 | timePreviousHigh = millis(); 267 | crankTimePreviousHigh = millis(); 268 | } 269 | 270 | void loop() { 271 | // Receive information from smartphone. 272 | receiveInformation(); 273 | 274 | // Display speed and frequency only every second. 275 | delay(1000); 276 | Serial.print("Speed: "); 277 | Serial.print(currentSpeed); 278 | Serial.println(" km/h"); 279 | 280 | Serial.print("Computed Crankfreq: "); 281 | Serial.print(frequency); 282 | Serial.println(" U/min"); 283 | 284 | Serial.print("Measured Crankfreq: "); 285 | Serial.println(crankFrequency); 286 | 287 | // target frequency - general Div. 288 | int tarFreqMinusDiv = TAR_FREQUENCY_VAL.toInt() - (((float)TAR_FREQUENCY_VAL.toInt() / 100.0f) * (float)GENERAL_DIV_VAL.toInt()); 289 | // target frequency + general Div. 290 | int tarFreqPlusDiv = TAR_FREQUENCY_VAL.toInt() + (((float)TAR_FREQUENCY_VAL.toInt() / 100.0f) * (float)GENERAL_DIV_VAL.toInt()); 291 | 292 | // Adapt to changes in current frequency. 293 | if (frequency < tarFreqMinusDiv) { 294 | gearDown(); 295 | } 296 | if (frequency > tarFreqPlusDiv) { 297 | gearUp(); 298 | } 299 | 300 | // The idea is that if we are in a wrong gear the error can only be a 301 | // discrete value because of the gears fix number of teeth. 302 | // If we are not turning the crank at the maximum possible that error will 303 | // change. If it does not change we should do a correction. 304 | // We assume that we are not more than 1 Gear away from the real one. 305 | // If so we should better implement a reset function. 306 | // It only checks if the crankSensor has a signal with about 60RPM that 307 | // means that 3 singals are 3 seconds. -> 3rd "sucessful" check will do the 308 | // correction. 309 | if (frequency < crankFrequency * 0.96f || frequency > crankFrequency * 1.04f) { 310 | checkGear(); 311 | } 312 | } 313 | 314 | void triggerSensor1() { 315 | timeActualHigh = millis(); 316 | difference = timeActualHigh - timePreviousHigh; 317 | 318 | // Only compute new speed, if difference is bigger than wheelDebouncingTime. 319 | // If not, wait for next occuring interrupt. 320 | if (difference >= wheelDebouncingTime) { 321 | // compute speed with km/h = (mm / ms) * 3.6 322 | currentSpeed = ((float)rad / (float)difference) * 3.6f; 323 | // compute rounds of wheel U / min = 60000 / difference[ms]. 324 | currentRoundsWheel = 60000 / (float)difference; 325 | // compute frequency of crank. 326 | frequency = currentRoundsWheel / (frontGear[currentFront - 1] / rearGear[currentRear - 1]); 327 | timePreviousHigh = millis(); 328 | } 329 | } 330 | 331 | void triggerSensor2() { 332 | crankTimeActualHigh = millis(); 333 | crankDifference = crankTimeActualHigh - crankTimePreviousHigh; 334 | 335 | // Only compute new crank frequency, if crank difference is bigger than crankDebouncingTime. 336 | // If not, ignore current measurement and wait for next occuring interrupt. 337 | if (crankDifference >= crankDebouncingTime) { 338 | crankFrequency = 60000 / (float) crankDifference; 339 | 340 | crankTimePreviousHigh = millis(); 341 | } 342 | } 343 | 344 | // define interrupt service routine for timer declared above. 345 | ISR(TIMER1_COMPA_vect){ 346 | sendInformation(); 347 | } 348 | 349 | void sendInformation() { 350 | // Serial.print("Sending information ... "); 351 | // Begin sending information. 352 | bluetoothSerial.print(BEGIN); 353 | // VELOCITY 354 | bluetoothSerial.print(VELOCITY); 355 | bluetoothSerial.print(EQUAL); 356 | bluetoothSerial.print(currentSpeed); 357 | bluetoothSerial.print(SEPARATOR); 358 | 359 | // CURR_FREQUENCY 360 | bluetoothSerial.print(CURR_FREQUENCY); 361 | bluetoothSerial.print(EQUAL); 362 | bluetoothSerial.print(frequency); 363 | bluetoothSerial.print(SEPARATOR); 364 | 365 | // FRONT_GEAR 366 | bluetoothSerial.print(FRONT_GEAR); 367 | bluetoothSerial.print(EQUAL); 368 | bluetoothSerial.print(currentFront); 369 | bluetoothSerial.print(SEPARATOR); 370 | 371 | // REAR_GEAR 372 | bluetoothSerial.print(REAR_GEAR); 373 | bluetoothSerial.print(EQUAL); 374 | bluetoothSerial.print(currentRear); 375 | bluetoothSerial.print(SEPARATOR); 376 | 377 | // GRADIENT 378 | bluetoothSerial.print(GRADIENT); 379 | bluetoothSerial.print(EQUAL); 380 | bluetoothSerial.print(GRADIENT_VAL); 381 | bluetoothSerial.print(SEPARATOR); 382 | //End sending information. 383 | bluetoothSerial.print(END); 384 | // Serial.println("done"); 385 | } 386 | 387 | void receiveInformation() { 388 | while (bluetoothSerial.available()) { 389 | char inChar = (char)bluetoothSerial.read(); 390 | // bluetoothSerial.print(inChar); 391 | Serial.print(inChar); 392 | if (inChar != '=' && inChar != '!') { 393 | inputString += inChar; 394 | } else if (inChar == '=') { 395 | key = inputString; 396 | inputString = ""; 397 | } else if (inChar == '!') { 398 | value = inputString; 399 | inputString = ""; 400 | storeToValue(); 401 | bluetoothSerial.println(""); 402 | Serial.println(""); 403 | stringComplete = true; 404 | break; 405 | } 406 | } 407 | } 408 | 409 | void storeToValue() { 410 | if (key.equals(TAR_FREQUENCY)) { 411 | TAR_FREQUENCY_VAL = value; 412 | } else if (key.equals(HILL_DIV)) { 413 | HILL_DIV_VAL = value; 414 | } else if (key.equals(GENERAL_DIV)) { 415 | GENERAL_DIV_VAL = value; 416 | } else if (key.equals(RESET)) { 417 | RESET_VAL = value; 418 | } else if (key.equals(CIRCUMFERENCE)) { 419 | CIRCUMFERENCE_VAL = value; 420 | if (CIRCUMFERENCE_VAL.equals("20")) { 421 | rad = circumference[0]; 422 | } else if (CIRCUMFERENCE_VAL.equals("24")) { 423 | rad = circumference[1]; 424 | } else if (CIRCUMFERENCE_VAL.equals("26")) { 425 | rad = circumference[2]; 426 | } else if (CIRCUMFERENCE_VAL.equals("20")) { 427 | rad = circumference[3]; 428 | } 429 | } 430 | if (RESET_VAL.equals("1")) { 431 | // Reset the bike. Shift front to first and rear to first. 432 | resetGears(); 433 | RESET_VAL = "0"; 434 | } 435 | } 436 | 437 | void resetGears() { 438 | generateSignal(1,frontDownPin); 439 | generateSignal(10,rearDownPin); 440 | 441 | currentFront = 1; 442 | currentRear = 1; 443 | } 444 | 445 | 446 | 447 | void gearUp(){ 448 | // small front chainblade. 449 | if (currentFront == 1){ 450 | if (currentRear < 8){ 451 | // generate Signal to shift up rear by one gear. 452 | generateSignal(1, rearUpPin); 453 | currentRear++; 454 | return; 455 | } 456 | // rear has reached 8, so we have to shift to bigger front chainblade. 457 | else { 458 | // generate Signal to shift up Front by one gear and shift down rear by 4 gears. 459 | generateSignal(1, frontUpPin); 460 | generateSignal(4, rearDownPin); 461 | currentRear = 4; 462 | currentFront = 2; 463 | return; 464 | } 465 | } 466 | // big front chainblade 467 | else { 468 | // already in biggest gear. 469 | if (currentRear == 10){ 470 | return; 471 | } 472 | // not yet in biggest gear. Shift up. 473 | else { 474 | // generate Signal to shift up rear by one gear. 475 | generateSignal(1, rearUpPin); 476 | currentRear++; 477 | return; 478 | } 479 | } 480 | } 481 | 482 | 483 | void gearDown(){ 484 | // small front chainblade. 485 | if (currentFront == 1){ 486 | // already smallest gear. Do nothing. 487 | if (currentRear == 1){ 488 | return; 489 | } 490 | // singel rear downshift. 491 | else { 492 | // generate signal to shift down rear by 1 gear. 493 | generateSignal(1, rearDownPin); 494 | currentRear--; 495 | return; 496 | } 497 | } 498 | // big front chainblade 499 | else { 500 | if (currentRear > 3){ 501 | // generate Signal to shift down rear by one gear. 502 | generateSignal(1, rearDownPin); 503 | currentRear--; 504 | return; 505 | } 506 | // rear has reached 3, so we have to shift to smaller front chainblade. 507 | else { 508 | // generate Signal to shift down Front by one gear and shift up rear by 4 gears. 509 | generateSignal(1, frontDownPin); 510 | generateSignal(4, rearUpPin); 511 | currentRear = 7; 512 | currentFront = 1; 513 | return; 514 | } 515 | } 516 | } 517 | 518 | /* 519 | Generates positive signals on by specified pin. 520 | Delay time between two positive signals is for the mechanical shifting time. 521 | */ 522 | void generateSignal(int count, int mode) { 523 | for ( int i = 0; i < count; i++) { 524 | digitalWrite(mode, LOW); 525 | delay(200); 526 | digitalWrite(mode, HIGH); 527 | delay(400); 528 | } 529 | } 530 | 531 | void checkGear(){ 532 | int oneMatch = 0; 533 | float tmp = 0; 534 | if (currentRear != 1) { 535 | // check difference to crankFrequency if rear would be shifted down by one gear 536 | tmp = currentRoundsWheel / (frontGear[currentFront - 1] / rearGear[currentRear - 2]); 537 | if (tmp > crankFrequency * 0.98f && tmp < crankFrequency * 1.02f) { 538 | if (currentFrontForCorrection == currentFront && currentRearForCorrection == (currentRear - 1)) { 539 | correctionNeededCounter++; 540 | } else { 541 | correctionNeededCounter = 1; 542 | currentFrontForCorrection = currentFront; 543 | currentRearForCorrection = currentRear - 1; 544 | } 545 | oneMatch = 1; 546 | } 547 | } 548 | 549 | if (currentRear != 10) { 550 | // check difference to crankFrequency if rear would be shifted up by one gear 551 | tmp = currentRoundsWheel / (frontGear[currentFront - 1] / rearGear[currentRear]); 552 | if (tmp > crankFrequency * 0.98f && tmp < crankFrequency * 1.02f) { 553 | if (currentFrontForCorrection == currentFront && currentRearForCorrection == (currentRear + 1)) { 554 | correctionNeededCounter++; 555 | } else { 556 | correctionNeededCounter = 1; 557 | currentFrontForCorrection = currentFront; 558 | currentRearForCorrection = currentRear + 1; 559 | } 560 | oneMatch = 1; 561 | } 562 | } 563 | 564 | if (currentFront == 1) { 565 | // check difference to crankFrequency if front would be shifted up by one gear 566 | tmp = currentRoundsWheel / (frontGear[currentFront] / rearGear[currentRear - 1]); 567 | if (tmp > crankFrequency * 0.98f && tmp < crankFrequency * 1.02f) { 568 | if (currentFrontForCorrection == (currentFront + 1) && currentRearForCorrection == currentRear) { 569 | correctionNeededCounter++; 570 | } else { 571 | correctionNeededCounter = 1; 572 | currentFrontForCorrection = currentFront + 1; 573 | currentRearForCorrection = currentRear; 574 | } 575 | oneMatch = 1; 576 | } 577 | } else { 578 | // check difference to crankFrequency if front would be shifted down by one gear 579 | tmp = currentRoundsWheel / (frontGear[currentFront - 2] / rearGear[currentRear - 1]); 580 | if (tmp > crankFrequency * 0.98f && tmp < crankFrequency * 1.02f) { 581 | if (currentFrontForCorrection == (currentFront - 1) && currentRearForCorrection == currentRear) { 582 | correctionNeededCounter++; 583 | } else { 584 | correctionNeededCounter = 1; 585 | currentFrontForCorrection = currentFront - 1; 586 | currentRearForCorrection = currentRear; 587 | } 588 | oneMatch = 1; 589 | } 590 | } 591 | 592 | // the error was not one of the discrete values 593 | if (!oneMatch) { 594 | correctionNeededCounter = 0; 595 | } 596 | 597 | // if we have encountered the error often enough and it was allways the same we need a gear shift 598 | if (correctionNeededCounter == 3) { 599 | if (currentFrontForCorrection > currentFront) { 600 | // shift Front up by one. 601 | generateSignal(1, frontUpPin); 602 | currentFront++; 603 | Serial.println("gear UP correction"); 604 | } 605 | if (currentFrontForCorrection < currentFront) { 606 | // shift Front down by one. 607 | generateSignal(1, frontDownPin); 608 | currentFront--; 609 | Serial.println("gear DOWN correction"); 610 | } 611 | if (currentRearForCorrection > currentRear) { 612 | // shift Rear up by one. 613 | generateSignal(1, rearUpPin); 614 | currentRear++; 615 | Serial.println("gear UP correction"); 616 | } 617 | if (currentRearForCorrection < currentRear) { 618 | // shift Rear down by one. 619 | generateSignal(1, rearDownPin); 620 | currentRear--; 621 | Serial.println("gear DOWN correction"); 622 | } 623 | } 624 | correctionNeededCounter = 0; 625 | } 626 | 627 | --------------------------------------------------------------------------------