/*
Bicycle Automatic Shifter Version 2 by Edward Rose 2020
Uses arduino bike speedometer w serial.print()code by Amanda Ghassaei 2012
http://www.instructables.com/id/Arduino-Bike-Speedometer/
*/
#include <SPI.h>
#include <NRFLite.h>
#include <avdweb_AnalogReadFast.h>
#include <PWMServo.h>
#include <EEPROM.h>
PWMServo myservo; // create servo object to control a servo
//define pins
//V1
//#define potpin A0 // analog pin used to detect handlebar button press
//#define derailSwitch A2 // pin connected to deraileur position reed switch
//#define reed A3 // pin connected to wheel reed switch
//#define reed2 6 // pin connected to cadence reed switch
//V2
#define derailSwitch 6 // pin connected to deraileur position reed switch
#define reed A2 // pin connected to wheel reed switch
#define reed2 A3 // pin connected to cadence reed switch
#define buttonPinL 3 // pin connected to left shifter button
#define buttonPinR 2 // pin connected to right shifter button
#define lightSwitch 4 // pin to operate rear light
//BOTH
int btLED = 5; // pin connected to BT LED
//#define servopot A6 // pin reading voltage of servos internal pot
#define batVolt A7 // Li-ion voltage via potential divider OLD 10k/ 3.9k NEW 3.3k/ 2.7k
// changable values stored in EEPROM some calculated on spreadsheet
// use mode 5 to change via Serial monitor or Bluetooth
//Ratio set up
byte DScalarB = 0; // big chainring
byte DScalarS = 0; // small chainring
//Shifting
int ratioOffset = 0; // compensates for differances in ratio profile due to position of hub interface
int shiftSlope = 0; // shift slope depends on hub under/overdrive ratio and servo position
//Servo position ajustment values
int servoLowlimit = 0; // lower value used in servopos constrain function
int servoUpLimit = 0; // upper value used in servopos constrain function
//PID for cadence correction
float scalarP = 0;
float scalarI = 0;
//float scalarD = 500; // never found D to be necessary
//Power equation used to get multipler which = ((sin(powerAv / powerDiv)) * powerMult) + powerAdd;
int powerDiv = 0;
byte powerMult = 0;
byte powerAdd = 0;
//Distance measurment
int cirWheel = 0; // wheel circumference in cm for odometer
int firstMult = 200; // increase to 2000 if using servopos 540 - 2450
int reedVal; // status of rear wheel reed switch
int reedVal2; // status of crank reed switch
long timer; // time for one full rotation (in ms)
long timer2; // time for one full rotation (in ms)
float Wheel; // outputs of reed switch timer
int Cadence;
int Speed;
int Power; // power recieved from power meter
float multipler; // used in main servopos equation to get correct cadence
int errorC; // cadence error
float prevErrorTotal; // store offSetI
//float prevError; // store prev errorC for offSetD
float offSetC; // total constrained cadence offset
float offSetP; // proportional offset
float offSetI; // integral offset
//float offSetD; // derivative offset
int maxReedCounter = 10; // min time (in ms) of one rotation (for debouncing)
int reedCounter;
int reedCounter2;
int buttonCounter = 0; // push button press counter
volatile int Counter = 0;
//int buttons;
bool buttonL; // left shifter button status
bool buttonR; // right shifter button status
int servopos; // value written to servo
//int preServopos;
int switchState2 = 0; // state of mode switch2 derailleur position
int DScalar; // sets the correct scalar depending on derailleur position
int BTmode = 0; // read from bluetooth
int mode = 3; // mode set by Bluetooth
int cadenceSet; // cadence setting
int setting = 0; // gear/ cadence/ torque setting
int prevCadenceSet = 60; // delay gives time for the servo to move before being checked for error
int lipoPercent; // auto shifter battery percent approx
int lipoPercentP; // power meter battery percent approx
int servovolt; // voltage read from servo pot by pin A6
int servoposError;
int zeroError = 0; // From power meter diffence between min value and sensorValue
int torqueSet = 0; // torque setting using shifter
struct dataStruct { // data recived from power meter via nRF24L01
int value1; // power from power meter
int value2; // zeroError from power meter
int value3; // lipoPercent from power meter
} myData;
const int numReadings = 5; // power average divide by 2 for time in secs
int readings[numReadings]; // for power average code
int index = 0; // for power average code
int total = 0; // for power average code
float powerAv = 0; // averaged power to avoid oscillations
int codeTimer = 0; // triggerd when cadence reed switch closes
int delayTime = 0; // time of a crank rotation
int inputTorque = 0;
int outputTorque = 0;
int offTimer = 0; // save time since bike last moved
int lightMode = 0; // save rear light setting
int inPut = 0; // light setting input from serial
int BTinPut = 0; // program mode serial input
bool btStatus = 0; // status of BT LED
unsigned long distance = 0; // add up cm travelled
unsigned long odometer; // total distance (100th of a km) travelled, stored in EEPROM
NRFLite _radio;
void setup() {
Serial.begin(9600);
_radio.init(0, 7, 10); // radio id, CE pin, CSN pin
myservo.attach(SERVO_PIN_A); // attaches the servo on pin 9 to the servo object
pinMode(reed, INPUT);
pinMode(reed2, INPUT_PULLUP); // POWER LATCH
pinMode(derailSwitch, INPUT);
pinMode(batVolt, INPUT);
pinMode(btLED, INPUT);
//pinMode(servopot, INPUT);
//V2 only
pinMode(buttonPinL, INPUT_PULLUP);
pinMode(buttonPinR, INPUT_PULLUP);
pinMode(lightSwitch, INPUT);
EEPROM.get(20, DScalarB);
EEPROM.get(21, DScalarS);
EEPROM.get(22, ratioOffset);
EEPROM.get(24, shiftSlope);
EEPROM.get(26, servoLowlimit);
EEPROM.get(28, servoUpLimit);
EEPROM.get(30, scalarP);
EEPROM.get(34, scalarI);
EEPROM.get(38, powerDiv);
EEPROM.get(40, powerMult);
EEPROM.get(41, powerAdd);
EEPROM.get(42, cirWheel);
reedCounter = maxReedCounter;
reedCounter2 = maxReedCounter;
myservo.write(servoLowlimit); // give servo a position while setup code finishes
inPut = EEPROM.read(15); // get light setting from before last switched off
while (inPut != lightMode) {
buttonPress();
lightMode ++;
if (lightMode > 7) {
lightMode = 0;
}
}
// TIMER SETUP- the timer interrupt allows precise timed measurements of the reed switch
// for more info about configuration of arduino timers see http://arduino.cc/playground/Code/Timer1
cli();//stop interrupts
//set timer2 interrupt at 1kHz
TCCR2A = 0;// set entire TCCR1A register to 0
TCCR2B = 0;// same for TCCR1B
TCNT2 = 0;
// set timer count for 1kHz increments
OCR2A = 249;// = (1/1000) / (16*10^6)/(1000*64) - 1
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS11 bit for 64 prescaler
TCCR2B |= (1 << CS22);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();//allow interrupts
//END TIMER SETUP
}
ISR(TIMER2_COMPA_vect) { // Interrupt at freq of 1kHz to measure reed switch
reedVal = digitalRead(reed); // get status of wheel reed switch
if (reedVal) { // if reed switch is closed
if (reedCounter == 0) { // min time between pulses has passed
Wheel = ((DScalar * firstMult) / float(timer)); // calculate rotation rate of wheel, includes multipler for big/small chainrings
Speed = ((4817) / float(timer));
timer = 0; // reset timer
reedCounter = maxReedCounter; // reset reedCounter
distance = distance + cirWheel; // add another wheel circumference to distance travelled
}
}
else { // if reed switch is open
if (reedCounter > 0) { // don't let reedCounter go negative
reedCounter -= 1; // decrement reedCounter
}
}
if (timer > 2000) {
Wheel = 0;
Speed = 0; // if no new pulses from reed switch tire is still, set mph to 0
}
else {
timer += 1; // increment timer
}
//TIMER1 CADENCE
reedVal2 = digitalRead(reed2); // get status of crank reed switch
if (!reedVal2) {
if (reedCounter2 == 0) {
Cadence = (60000 / float(timer2));
codeTimer = 1; // used to time shift at dead spot of pedal stroke
delayTime = timer2;
timer2 = 0;
reedCounter2 = maxReedCounter;
}
}
else {
if (reedCounter2 > 0) {
reedCounter2 -= 1;
}
}
if (timer2 > 2000) {
Cadence = 0;
}
else {
timer2 += 1;
}
//V1 two buttons on one pin with resistors, analog read
/* buttons = analogReadFast(potpin); // check if a handlebar button has been pressed
if (buttons > 600) // time button press
{
buttonCounter ++;
}
else if (buttons < 400)
{
buttonCounter --;
}
else
{
Counter = buttonCounter;
}*/
// V2 one button per pin
buttonL = digitalRead(buttonPinL);
buttonR = digitalRead(buttonPinR);
if (buttonL == LOW)
{
buttonCounter ++;
}
else {
if (buttonR == LOW)
{
buttonCounter --;
}
else
{
Counter = buttonCounter;
}
}
}
void buttonPress() { // press button on rear light
pinMode(lightSwitch, OUTPUT);
digitalWrite(lightSwitch, LOW);
delay(30);
pinMode(lightSwitch, INPUT);
delay(30);
}
void loop() {
// time shift to happen at dead spots of pedal stroke
if (Cadence > 38) {
if (codeTimer) {
delay (delayTime * 0.2); //0.25
codeTimer = 0;
mainCode();
delay (delayTime * 0.7); //0.5
mainCode();
}
}
else {
mainCode();
delay (500);
}
}
void mainCode() {
// power latch circiut switch off and save data
/*if (Speed == 0) {
offTimer++;
}
else {
offTimer = 0;
}*/
// if BT LED connected to D5
if (Speed > 0) {
offTimer = 0;
}
else {
btStatus = digitalRead(btLED);
if (btStatus == LOW) {
offTimer++;
}
else {
offTimer = offTimer;
}
}
if (offTimer > 30) { // switch off after approx secs
EEPROM.get(10, odometer); // read last saved 100th of kms from EEPROM
odometer = odometer + (distance / 1000); // divide by 1000 to convert cm to 100th of a km
EEPROM.put(10, odometer); // save distance to odometer
pinMode(reed2, OUTPUT); // unlatch power latch circuit
digitalWrite(reed2, LOW);
distance = 0; // reset distance if Arduino doesn't switch off
}
// check for input from bluetooth to change mode
if (Serial.available() > 0) {
BTmode = Serial.read();
if (BTmode == '1') {
mode = 1;
Serial.println("Manual");
}
else if (BTmode == '2') {
mode = 2;
Serial.println("CC on");
}
else if (BTmode == '3') {
mode = 3;
Serial.println("PowerOffset");
}
else if (BTmode == '4') {
mode = 4;
Serial.println("PowerSlope");
}
else if (BTmode == '5') {
mode = 5;
Serial.println("Program Mode");
}
else if (BTmode == '6') {
mode = 6;
Serial.println("Light Control");
}
else if (BTmode == '7') {
mode = 7;
Serial.println("EEPROM Read");
}
else if (BTmode == '8') {
mode = 8;
Serial.println("Servo manaul control");
}
}
// change gear, cadence or torque setting
if (Counter > 50 && Counter <= 500 && setting < 8) {
buttonCounter = 0;
setting ++;
}
else if (Counter < -50 && Counter >= -500 && setting > -4) {
buttonCounter = 0;
setting --;
}
else if (Counter > 500 || Counter < -500) {
buttonCounter = 0;
setting = 0;
}
// measure battery %
lipoPercent = ((analogReadFast(batVolt) * 0.55) - 385); // give approx battery percentage but must be calibrated to the voltage divider used
//data recived from power meter
while (_radio.hasData()) {
_radio.readData(&myData);
Power = myData.value1;
zeroError = myData.value2;
lipoPercentP = myData.value3;
}
// get derailluer position and calculate torque if required and power data available
switchState2 = digitalRead(derailSwitch); // check status of derailleur position reed switch
if (switchState2 == HIGH) // derailleur position check, DScaler setting and torque calcs
{
(DScalar = DScalarS); // small chainring
//inputTorque = ((Power * 9.5488)/(Cadence*1.89)); // calculate ratio chainring/sprocket, example 34/18
}
else
{
(DScalar = DScalarB); // big chainring
//inputTorque = ((Power * 9.5488)/(Cadence*2.78)); // calculate ratio chainring/sprocket, example 50/18
}
//outputTorque = ((Power * 9.5488)/(Speed * 12.46));
if (mode == 7) // EEPROM Read
{
EEPROM.get(10, odometer);
odometer = odometer + (distance / 1000); // divide by 1000 to convert cm to 100th of a km
Serial.print("Odometer ");
Serial.print(odometer / 160); // divide by 100 for km, 160 for approx miles
Serial.println(" miles");
/*Serial.println("Press any key to continue");
while (!Serial.available()) delay(10);
inPut = Serial.read();*/
delay(5000);
mode = 3;
}
if (mode == 6) //Light control
{
Serial.println("Enter mode 1 - 7 or 0 for off");
while (Serial.available() == 0) {}
inPut = (Serial.parseFloat());
if (inPut > 7) {
Serial.println("Error");
}
else {
EEPROM.write(15, inPut);
while (inPut != lightMode) {
buttonPress();
Serial.println(lightMode);
lightMode ++;
if (lightMode > 7) {
lightMode = 0;
}
}
mode = 3;
}
}
if (mode == 5) //Program mode
{
Serial.println();
Serial.println("Select value to change or 13 to exit");
Serial.print("1. DScalarB ");
Serial.println(DScalarB);
Serial.print("2. DScalarS ");
Serial.println(DScalarS);
Serial.print("3. ratioOffset ");
Serial.println(ratioOffset);
Serial.print("4. shiftSlope ");
Serial.println(shiftSlope);
Serial.print("5. servoLowlimit ");
Serial.println(servoLowlimit);
Serial.print("6. servoUpLimit ");
Serial.println(servoUpLimit);
Serial.print("7. scalarP ");
Serial.println(scalarP);
Serial.print("8. scalarI ");
Serial.println(scalarI);
Serial.print("9. powerDiv ");
Serial.println(powerDiv);
Serial.print("10. powerMult ");
Serial.println(powerMult);
Serial.print("11. powerAdd ");
Serial.println(powerAdd);
Serial.print("12. cirWheel ");
Serial.println(cirWheel);
while (Serial.available() == 0) {}
BTinPut = Serial.parseInt();
if (BTinPut == 1) {
Serial.println();
Serial.print("DScalarB Enter value ");
while (Serial.available() == 0) {}
DScalarB = (Serial.parseInt());
EEPROM.put(20, DScalarB);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 2) {
Serial.println();
Serial.print("DScalarS Enter value ");
while (Serial.available() == 0) {}
DScalarS = (Serial.parseInt());
EEPROM.put(21, DScalarS);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 3) {
Serial.println();
Serial.print("ratioOffset Enter value ");
while (Serial.available() == 0) {}
ratioOffset = (Serial.parseInt());
EEPROM.put(22, ratioOffset);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 4) {
Serial.println();
Serial.print("shiftSlope Enter value ");
while (Serial.available() == 0) {}
shiftSlope = (Serial.parseInt());
EEPROM.put(24, shiftSlope);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 5) {
Serial.println();
Serial.print("servoLowlimit Enter value ");
while (Serial.available() == 0) {}
servoLowlimit = (Serial.parseInt());
EEPROM.put(26, servoLowlimit);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 6) {
Serial.println();
Serial.print("servoUpLimit Enter value ");
while (Serial.available() == 0) {}
servoUpLimit = (Serial.parseInt());
EEPROM.put(28, servoUpLimit);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 7) {
Serial.println();
Serial.print("scalarP Enter value ");
while (Serial.available() == 0) {}
scalarP = (Serial.parseFloat());
EEPROM.put(30, scalarP);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 8) {
Serial.println();
Serial.print("scalarI Enter value ");
while (Serial.available() == 0) {}
scalarI = (Serial.parseFloat());
EEPROM.put(34, scalarI);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 9) {
Serial.println();
Serial.print("powerDiv Enter value ");
while (Serial.available() == 0) {}
powerDiv = (Serial.parseInt());
EEPROM.put(38, powerDiv);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 10) {
Serial.println();
Serial.print("powerMult Enter value ");
while (Serial.available() == 0) {}
powerMult = (Serial.parseInt());
EEPROM.put(40, powerMult);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 11) {
Serial.println();
Serial.print("powerAdd Enter value ");
while (Serial.available() == 0) {}
powerAdd = (Serial.parseInt());
EEPROM.put(41, powerAdd);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 12) {
Serial.println();
Serial.print("cirWheel Enter value ");
while (Serial.available() == 0) {}
cirWheel = (Serial.parseInt());
EEPROM.put(42, cirWheel);
Serial.println();
Serial.println(" Saved");
}
else if (BTinPut == 13) {
mode = 3;
Serial.println("Return to run");
}
}
else if (mode == 8) //servo manual control
{
Serial.println("Use buttons to move servo or enter a value");
Serial.println("Enter negative number to exit");
while (Serial.available() == 0) {
if (buttonCounter > 50) {
servopos ++;
buttonCounter = 0;
myservo.write(servopos);
Serial.println(servopos);
}
else if (buttonCounter < -50) {
servopos --;
buttonCounter = 0;
myservo.write(servopos);
Serial.println(servopos);
}
}
servopos = (Serial.parseInt());
if (servopos >= 0) {
myservo.write(servopos);
Serial.println(servopos);
}
else {
mode = 3;
Serial.println("Return to run");
}
}
else if (mode == 1) //manual
{ /*// alternative manaul
if (buttonCounter > 80 && servopos < servoUpLimit){
servopos ++;
buttonCounter = 0; }
else if (buttonCounter < -80 && servopos > servoLowlimit){
servopos --;
buttonCounter = 0; }
myservo.write(servopos);
*/
if (setting == 8)
{
servopos = (servoUpLimit);
}
else if (setting <= 7 && setting >= -3)
{
servopos = ((((servoUpLimit - servoLowlimit) / 12) * (setting + 4)) + servoLowlimit);
}
else if (setting == -4)
{
servopos = (servoLowlimit);
}
myservo.write(servopos);
//servovolt = analogRead(servopot); // read servos internal pot to get real position
//servoposError = servovolt - (((servopos - 2) * 2.84) + 83); // calculate error in servos position
Serial.print("E");
Serial.print(Power);
Serial.print(", ");
Serial.print(Cadence);
Serial.print(", ");
Serial.print(Speed);
Serial.print(", ");
Serial.print(setting);
Serial.print(", ");
Serial.print(servopos); // (servopos);
Serial.print(", ");
Serial.print(lipoPercentP); //(servoposError);
Serial.print(", ");
Serial.println(zeroError);
}
else if (Power == 0 || mode == 2) // Constant cadence
{
if (setting == 4)
{
cadenceSet = 50;
}
else if (setting == 3)
{
cadenceSet = 60;
}
else if (setting == 2)
{
cadenceSet = 65;
}
else if (setting == 1)
{
cadenceSet = 68;
}
else if (setting == 0)
{
cadenceSet = 72;
}
else if (setting == -1)
{
cadenceSet = 75;
}
else if (setting == -2)
{
cadenceSet = 80;
}
else if (setting == -3)
{
cadenceSet = 90;
}
else if (setting == -4)
{
cadenceSet = 100;
}
multipler = shiftSlope / cadenceSet;
errorC = (Cadence - prevCadenceSet);
// calculate offsetP
if ((Cadence > (cadenceSet + 10)) && (Cadence < 200))
{
offSetP = (multipler / scalarP);
}
else if (Cadence < 38)
{
offSetP = 0;
}
else {
offSetP = (sin((errorC) * 0.15)) * (multipler / scalarP);
}
// calculate offsetI
if (Cadence < 38)
{
offSetI = 0;
}
else {
offSetI = constrain ((prevErrorTotal + (errorC * scalarI)), -1.5, 3); //-1.5, 1.5
}
prevErrorTotal = offSetI;
/*
// calculate offsetD
if (Cadence < 38)
{offSetI = 0;}
else {
offSetD = constrain(((errorC - prevError) * scalarD),-3,15);
}
prevError = errorC;
*/
offSetC = offSetP + offSetI; // + offSetD;
servopos = constrain((ratioOffset + (Wheel * (multipler + offSetC))), servoLowlimit, servoUpLimit); // calculates servopos, sets the limits on under/overdrive.
//multiplier sets the cadence, fixed offset compensates for difference in ratio profile.
myservo.write(servopos);
//CALIBRATE
/*servovolt = analogRead(servopot);
servoposError = servovolt-(((servoposD-2)*2.84)+83);
Serial.print("E"); // send values via bluetooth or serial monitor for graphing or logging.
Serial.print(Power); // "E" enables an app called Bluetooth Graphics to recognise it as data.
Serial.print(", ");
Serial.print(Cadence);
Serial.print(", ");
Serial.print(Speed);
Serial.print(", ");
Serial.print(switchState2);
Serial.print(", ");
Serial.print(servoposD); //cadenceSet
Serial.print(", ");
Serial.print(servoposError); //lipoPercentP
Serial.print(", ");
Serial.println(zeroError);
*/
Serial.print("E"); // send values via bluetooth or serial monitor for graphing or logging.
Serial.print(Power); // "E" enables an app called Bluetooth Graphics to recognise it as data.
Serial.print(", ");
Serial.print(Cadence);
Serial.print(", ");
Serial.print(Speed);
Serial.print(", ");
Serial.print(setting); // lipoPercent
Serial.print(", ");
Serial.print(lipoPercent); //cadenceSet, servopos, outputTorque
Serial.print(", ");
Serial.print(lipoPercentP);
Serial.print(", ");
Serial.println(zeroError);
//APP
/*
Serial.print(" "); //Prevents The operation * cannot accept the arguments: , [*empty-string*], [-0.24]
Serial.print(Power);
Serial.print(",");
Serial.print(Cadence);
Serial.print(",");
Serial.print(Speed);
Serial.print(",");
Serial.print(cadenceSet);
Serial.print(",");
Serial.print(offSetC);
Serial.print(",");
Serial.print(setting);
Serial.print(",");
Serial.print(lipoPercent);
Serial.print(",");
Serial.print(lipoPercentP);
Serial.print(",");
Serial.print(zeroError);
Serial.print(",");
Serial.print(inputTorque);
Serial.print(",");
Serial.println(outputTorque); */
prevCadenceSet = cadenceSet; //store prev cadence setpoint
// preServoposD = servoposD;
}
else if (Power > 0 && mode == 3) // Power1 buttons change powerAdd
{
// average power 2.5 secs worked best less results in oscillations, more to much lag
total = total - readings[index];
readings[index] = Power;
total = total + readings[index];
index = index + 1;
if (index >= numReadings)
index = 0;
powerAv = total / numReadings;
multipler = ((sin(powerAv / powerDiv)) * powerMult) + (setting + powerAdd);
cadenceSet = shiftSlope / multipler;
errorC = (Cadence - cadenceSet);
// calculate offsetP
if ((Cadence > (cadenceSet + 10)) && (Cadence < 200))
{
offSetP = (multipler / 11);
}
else if (Cadence < 38)
{
offSetP = 0;
}
else {
offSetP = (sin((errorC) * 0.15)) * (multipler / 12);
}
// calculate offsetI
if (Cadence < 38)
{
offSetI = 0;
}
else {
offSetI = constrain ((prevErrorTotal + (errorC * scalarI)), -1.5, 1.5);
}
prevErrorTotal = offSetI;
offSetC = offSetP + offSetI; // + offSetD;
servopos = constrain((ratioOffset + (Wheel * (multipler + offSetC))), servoLowlimit, servoUpLimit);
myservo.write(servopos);
//CALIBRATE
/*servovolt = analogRead(servopot);
servoposError = servovolt-(((servoposD-2)*2.84)+83);
Serial.print("E"); // send values via bluetooth or serial monitor for graphing or logging.
Serial.print(Power); // "E" enables an app called Bluetooth Graphics to recognise it as data.
Serial.print(", ");
Serial.print(Cadence);
Serial.print(", ");
Serial.print(Speed);
Serial.print(", ");
Serial.print(switchState2);
Serial.print(", ");
Serial.print(servoposD); //cadenceSet
Serial.print(", ");
Serial.print(servoposError); //lipoPercentP
Serial.print(", ");
Serial.println(zeroError);
*/
Serial.print("E");
Serial.print(Power);
Serial.print(", ");
Serial.print(Cadence);
Serial.print(", ");
Serial.print(Speed);
Serial.print(", ");
Serial.print(setting); // lipoPercent,inputTorque
Serial.print(", ");
Serial.print(lipoPercent); //cadenceSet, servopos, outputTorque
Serial.print(", ");
Serial.print(lipoPercentP);
Serial.print(", ");
Serial.println(zeroError);
/* //APP
Serial.print(" "); //Prevents The operation * cannot accept the arguments: , [*empty-string*], [-0.24]
Serial.print(Power);
Serial.print(",");
Serial.print(Cadence);
Serial.print(",");
Serial.print(Speed);
Serial.print(",");
Serial.print(cadenceSet);
Serial.print(",");
Serial.print(offSetC);
Serial.print(",");
Serial.print(setting);
Serial.print(",");
Serial.print(lipoPercent);
Serial.print(",");
Serial.print(lipoPercentP);
Serial.print(",");
Serial.print(zeroError);
Serial.print(",");
Serial.print(inputTorque);
Serial.print(",");
Serial.println(outputTorque);
*/
}
else if (Power > 0 && mode == 4) // Power2 buttons change powerMult
{
// average power
total = total - readings[index];
readings[index] = Power;
total = total + readings[index];
index = index + 1;
if (index >= numReadings)
index = 0;
powerAv = total / numReadings;
multipler = ((sin(powerAv / powerDiv)) * (setting + powerMult)) + powerAdd;
cadenceSet = shiftSlope / multipler;
errorC = (Cadence - cadenceSet);
// calculate offsetP
if ((Cadence > (cadenceSet + 10)) && (Cadence < 200))
{
offSetP = (multipler / 11);
}
else if (Cadence < 38)
{
offSetP = 0;
}
else {
offSetP = (sin((errorC) * 0.15)) * (multipler / 12);
}
// calculate offsetI
if (Cadence < 38)
{
offSetI = 0;
}
else {
offSetI = constrain ((prevErrorTotal + (errorC * scalarI)), -1.5, 1.5);
}
prevErrorTotal = offSetI;
offSetC = offSetP + offSetI; // + offSetD;
servopos = constrain((ratioOffset + (Wheel * (multipler + offSetC))), servoLowlimit, servoUpLimit);
myservo.write(servopos);
Serial.print("E");
Serial.print(Power);
Serial.print(", ");
Serial.print(Cadence);
Serial.print(", ");
Serial.print(Speed);
Serial.print(", ");
Serial.print(setting); // lipoPercent,inputTorque
Serial.print(", ");
Serial.print(lipoPercent); //cadenceSet, servopos, outputTorque
Serial.print(", ");
Serial.print(lipoPercentP);
Serial.print(", ");
Serial.println(zeroError);
/*
Serial.print("E");
Serial.print(Power);
Serial.print(", ");
Serial.print(Cadence);
Serial.print(", ");
Serial.print(Speed);
Serial.print(", ");
Serial.print(setting); //lipoPercent
Serial.print(", ");
Serial.print(cadenceSet); //servopos
Serial.print(", ");
Serial.print(offSetC); //lipoPercentP
Serial.print(", ");
Serial.println(zeroError);
//APP
Serial.print(" "); //Prevents The operation * cannot accept the arguments: , [*empty-string*], [-0.24]
Serial.print(Power);
Serial.print(",");
Serial.print(Cadence);
Serial.print(",");
Serial.print(Speed);
Serial.print(",");
Serial.print(cadenceSet);
Serial.print(",");
Serial.print(offSetC);
Serial.print(",");
Serial.print(setting);
Serial.print(",");
Serial.print(lipoPercent);
Serial.print(",");
Serial.print(lipoPercentP);
Serial.print(",");
Serial.print(zeroError);
Serial.print(",");
Serial.print(inputTorque);
Serial.print(",");
Serial.println(outputTorque);
*/
}
}
Comments