The standard operation of this device is to give the standard pulse to the speedometer as long as there is no deceleration measured. If there is a deceleration, a series of pulses will be generated that shows for 100 revolutions of the wheel the energy loss in watt/10 as speed in km/hour.
The output is using the tri state property, by letting the output
float until an output pulse is needed. This pulse is then generated by making the output active for a short while, following an idea that I picked up from Horowitz and Hill the art of electronics (third edition) The outputs of an attiny85 are N mos enhancement FET drains, which act as a resistance towards ground. This is interpreted by the display unit of the speedometer as a closed contact.
After a measurement the power signal is given for 100 revolutions and the unit
switches to normal operation until the next deceleration.
The power calculation assumes an equivalent weight of 100 KG for bicycle and rider, which includes the effect of the momentum of the wheels. This value is set as a constant that can be changed.
The program is freeware distributed under the GNU licence
Power consumption is lowered by switching off the ADC convertor.
A supply current of about 1.3 milliamps while operating is needed. With a CR2032 battery it may last for 150 hours.
In the picture you see that the housing is made from a so called chocolate surprise egg. In the bottom a stereo female plug is mounted, in the top there is a male stereo plug. The reed relay contacts as they are going to the bicycle readout unit are put on the top two contacts of a male stereo plug. The lower two contacts are conected and are used to connect the minus connection of the CR2032 to the minus of the attiny85. The output contacts are between the two upper contacts of the male plug at the other side. It is necessary that the polarity of those contacts is correct. Measure the contacts pins of the display unit with a voltmeter and connect the male plug in such a way to the female plug coming from the display unit that the minus connection of the atttiny85 is connected to the minus of the display unit.
The circuit shown on the breadboard is a simulation circuit used to test the power meter. It generates shorts to ground as would result from reed relay closure that are constant for some time and then the time between shorts is gradually enlarged to simulate deceleration.
#include <avr/interrupt.h>
#include <avr/sleep.h>
// set pin numbers:
const byte contactpin = 2; // the number of the magnetic contact pin (pin 7 attiny)
const byte speedpin = 1; // the number of the output to the speed meter pin (pin 6)
const byte powerpin = 0; // pin 5
const byte ledpin = 3; // pin 2
const byte emptypin1 = 4;
const byte emptypin2 = 5;
const byte emptypin3 = 6;
// the following variables are long's because the time, measured in microseconds,
// will quickly become a bigger number than can be stored in an int.
// they are volatile because they need to be kept in memory between two interrupts
// or because they are used to transfer the power timing outside the interrupt
long T0 ; // most recent time
volatile long T1 ; // previous value of T0
volatile long trigger_time;
long DIFF1;
volatile long DIFF2;
long DIFF3;
long decel;
long power;
float temp;
volatile long power_interval;
volatile byte counter;
void setup()
{
set_sleep_mode(SLEEP_MODE_ADC); // 1256 microamps 400 reduction
pinMode(contactpin, INPUT);
GIMSK = 0b01000000; // turns on external interrupts
MCUCR = 0b00000010; // turns on the external interrupt as falling
// GIMSK = 0b0010000; // turns on pin change interrupts
PCMSK = 0b00000100; // turn on interrupts on pins PB2
T1=micros(); // initialization of trigger time
DIFF2=0;
counter=1;
trigger_time = micros();
power_interval = 160000000;
decel=0;
pinMode(speedpin, OUTPUT);
pinMode(powerpin, OUTPUT);
pinMode(ledpin, OUTPUT);
pinMode(emptypin1,INPUT);
pinMode(emptypin2,INPUT);
pinMode(emptypin3,INPUT);
// set initial LED state
digitalWrite(speedpin, LOW);
digitalWrite(powerpin, LOW);
digitalWrite(ledpin,LOW);
digitalWrite(emptypin1,LOW);
digitalWrite(emptypin2,LOW);
digitalWrite(emptypin3,LOW);
pinMode(speedpin,INPUT); // set the resistance to infinity
pinMode(powerpin,INPUT);
sei() ; // enables interrupts
}
ISR(INT0_vect)
{ // the interrupt routine takes about 1.5 milliseconds
// enough to debounce the reed contact (I hope)
pinMode(speedpin, OUTPUT); // shorten to ground
T0 = micros();
// new interval time
DIFF1 = T0 - T1;
// save old trigger time
T1 = T0;
// compute deceleration
decel = DIFF1 - DIFF2;
// ignore accelerations by assigning a low value
if (decel < 0 )
{
decel = 1;
// decel = -decel < 1; // send out accelerations as 2 times power
}
DIFF2 = DIFF1;
// DIFF3 = DIFF2 >> 8; // divide by 256 first scaling by shifting
// DIFF3 = (DIFF3 * DIFF3) >> 8; // second scaling divide by 256
temp= DIFF2/ 256;
temp=temp*temp /256;
temp=46*temp*temp/decel;
// power = (DIFF3 * DIFF3) / decel; // divide by 2^48 in total
// the computation is broken in parts because of overflow when the
// the full product is computed before division
// power = 46 * power ; // unit is microseconds
power=long(temp);
if ((power < 4000000)& (power > 100000) ) // speed indication > 2.16 km/hour (21 watts)
// and less than 864 watt (impossible to generate this power)
// this is the preparation for an asynchronous loop that checks if the next trigger time has passed, if so
// the next trigger time is computed and a pulse is generated if no acceleration or deceleration
// is measured the power indication does not change
// the highest power (smallest time) is kept for 100 revolutions to make the indication more stable
// it is reset to a low value (0.5 km/h) after 100 revolutions
{
if ((power < power_interval) or ( counter=0 ))
{ // digitalWrite(ledpin,HIGH); // to check if this part is triggered
// trigger time is adapted in order not to disturb the sync
trigger_time=trigger_time-power_interval+power;
power_interval= power;
// keep the speed indication for 100 revolutions
counter=20;
// uncomment the next line and the line above if you wish to see a led flash
// digitalWrite(ledpin,LOW);
}
}
else
{
if (counter=0)
{ digitalWrite(ledpin,HIGH);
trigger_time=trigger_time-power_interval+DIFF2;
power_interval=DIFF2;
digitalWrite(ledpin,LOW);
}
}
if (counter > 0)
{
counter=counter-1;
}
pinMode(speedpin, INPUT);
}
void loop()
{
if (micros() > trigger_time )
{ // this sets the powerpin active and grounds it to zero
pinMode(powerpin,OUTPUT);
trigger_time = trigger_time + power_interval;
delay(2);
// this pulls the powerpin to a very high resistance, simulating an open contact
pinMode(powerpin, INPUT);
}
// digitalWrite(ledpin,LOW);
sketch to generate timing pulses to test the power meter
/*
program to simulate the signal as generated from aa speedometer pickup by an arduino duemillenova
it will be fed into another arduino that contains the power measuring algorithm
*/
const int speedpin = 2;
void setup () {
pinMode(speedpin,OUTPUT);
digitalWrite(speedpin, HIGH);
}
void loop()
{ for (int i=1;i < 40;i++)
{
digitalWrite(speedpin, LOW);
delay(1);
digitalWrite(speedpin,HIGH);
delay(400);
}
for (int i=1;i < 10;i++)
{
digitalWrite(speedpin, LOW);
delay(5);
digitalWrite(speedpin,HIGH);
delay(400+i*10);
}
}
description of the underlying computation
Physics of energy loss computed from deceleration
Gerard te Meerman
Klooslaan 12 9721XN groningen
g.j.te.meerman@gmail.com
This is the explanation of how to compute cycling power from deceleration when power is no longer applied to the pedals. A website that computes enery delivered and energy used, taking into account a lot of variables (highly recommended) is
http://www.tribology-abc.com/calculators/cycling.htm
to give an idea of how much power is needed to propel a bike, some results of this site. Using standard conditions as present in the site (95KG mass bicycle+rider)
15 km/hour : 45 watts
20 km/hour : 86 watts
25 km/hour : 149 watts
30 km/hour : 240 watts
The power actually delivered will depend very much on the wind direction and speed.
Definitions:
diff1 : time for the last revolution of the wheel
diff2 : time for the pre-last revolution of the wheel
O : circumference of the wheel (2.25 meters for 28" wheel)
M mass of the bicycle and the rider and equivalent mass of the rotational energy (assumed 100KG)
Average speed of the bicycle in the last two revolutions :
V=2 x O /(diff1+diff2) (1)
Negative change in speed between first and second revolution
DV=O x (1/diff2-1/diff1)= O x ((diff1-diff2)/(diff1 x diff2)) (2)
energy difference as function of speed difference between two subsequent revolutions:
0.5 x M x (V1^2 -V2^2)= 0.5 x M x (V1+V2) x (V1-V2) ~ M x V x DV (3)
power in watts (energy / time) is derived from difference in speed between two subsequent revolutions
first use (1) and (2) and substitute in (3), then collect terms and rewrite
M x 2 x O^2 x (diff1-diff2) x / (diff1+diff2) ^2 (4)
This amount of energy is lost in ~diff1 seconds, which yields as power, substituting 2 x diff1 for (diff1+diff2)
M x 2 x O^2x (diff1-diff2) /((diff1+diff2)^2 x diff1)~ M x O^2 x (diff1-diff2) /(2 x diff1^3) (5)
A speedometer with a pulse once every rotation of the wheel will display a speed of O * 3.6 / time for one rotation= 8.1 / time for a 28" wheel.
(because 1 m/sec=3.6 KM /hour and one revolution is 2.25 meters)
artifical time to set in order that km/h matches the power in watts :
power=O* 3.6 /time, solving for time:
time = O* 3.6 / power. In milliseconds : O * 3600 /power = 8100/power (6)
example:
200 watts gives an interval of 40,5 milliseconds and an indication of 200 km/hour
computation of the number of milliseconds between simulated wheel pulses in order to read the power in km/hour as power : Fill (1) for V and (2) for DV and (4) for power in watts, time in seconds
O x 3.6 x 2 x diff1^3 / (M x O^2 x (diff1-diff2)) (7)
7.2 x diff1^3 /(M x O x (diff1-diff2)) (8)
when the resulting time is in microseconds,
7.2 x 10^6 x diff1^3/(M x O x (diff1-diff2))= (9)
filling in 100 for mass and 2.25 for circumference (9) can be simplified to
3.2 x 10^-4 x diff1^3/(diff1-diff2) (10)
At 30 km/hour the rotation time is 270 milliseconds. An arduino running at 1 Mhz can process about one instruction per cycle. There is therefore enough time to carry out the calculations and to generate the output pulses for the speedometer. For integer 32 bit arithmetic the result of multiplications should be below 2^31. By using microseconds as time unit, the numbers become too large. This is handled by dividing the rotation times (diff1) by 1024 through shifting them 10 bits to the right, before multiplying. The correction factor for the rotation time after shifting 10 bits to the right is 1024^3/10^12. in total :
3.2 x 10^-4 x 1.0124^3 x 10^9 x /10^18 x 10^-6= 3.436 x 10^-5. Inverse : multiply the denominator by 29103
34*diff1^3/(diff1-diff2)