Sunday, August 26, 2012

Kurt Kinetic Power Meter

As promised in a previous post, here is the Kurt Kinetic Power Meter Mark I. Note, that the ANT+ key HAS BEEN REMOVED so you will need to re-insert it in the code or else it won’t pick it up. I haven’t integrated the coast down to measure the mass moment of inertia, but based on the provided regression on the Kurt Kinetic website. This week I’ll try and capture some data. I’ve read that the fluid trainers need to stabilize thermally before you can get a consistent reading so I’ll try and do a trainer work out and capture several coast downs from 35+ km/hr to about 0 and see if I can get the math to line up.

DSC_8921

Ignore the AP2 module and the msop breakout board – as seen in a previous post, that was for testing the AP2 module with my .net board.

For this, I’m using an Arduino Leonardo (Atmega 32U4) which is 5V, and the Sparkfun ANT+ module is 3.3V, so  I’m using a simple resistor voltage divider circuit on the leonardo’s Tx lines. The magnetic reed switch from a cheap Cateye cycle computer is used for magnetic pickup.The reed switch connects between 3.3 (or 5v) an the output side is connected to digital pin 3. A 1k resistor runs from pin 3 to gnd as a pulldown. This circuit works equally well with a pullup resistor instead. The Tx/Rx lines of the ANT+ board are wired to the Rx/Tx lines of the microcontroller.

DSC_8923

I couldn’t quite capture swiping a magnet in front of the reed switch and capture the power on the cycle computer, but i was able to take a screen capture. The code needs work, the power listed on the COM16 is accumulated. As I’ve found before in testing, swinging a magnet back and forth in front of these things is very inconsistent.

Kurt_kinetic

Below is the code

Cheers Winking smile


#define UCHAR unsigned char
#define MESG_TX_SYNC ((UCHAR)0xA4)
#define MESG_SYSTEM_RESET_ID ((UCHAR)0x4A)
#define MESG_NETWORK_KEY_ID ((UCHAR)0x46)
#define MESG_ASSIGN_CHANNEL_ID ((UCHAR)0x42)
#define MESG_CHANNEL_ID_ID ((UCHAR)0x51)
#define MESG_CHANNEL_RADIO_FREQ_ID ((UCHAR)0x45)
#define MESG_CHANNEL_MESG_PERIOD_ID ((UCHAR)0x43) // Set channel period 0x43
#define MESG_RADIO_TX_POWER_ID ((UCHAR)0x47) // Set Tx Power 0x47
#define MESG_CHANNEL_SEARCH_TIMEOUT_ID ((UCHAR)0x44) // Set Channel Search Timeout 0x44
#define MESG_OPEN_CHANNEL_ID ((UCHAR)0x4B) // ID Byte 0x4B
#define MESG_BROADCAST_DATA_ID ((UCHAR)0x4E)

const int Mag_pickup = 3; // the number of the pushbutton pin
int pin = 13;
volatile int state = LOW;
unsigned long time1;
unsigned long time2;
unsigned long period;

double omega;
double velocity;
byte ANT_event = 0;
uint16_t ANT_INST_power = 0;
uint16_t ANT_power = 0;
double powerconst;

boolean recalc = 0;

long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 10; // the debounce time; increase if the output flickers
int buttonState; // the current reading from the input pin
int lastButtonState = LOW; // the previous reading from the input pin
boolean sent = 0;

void setup()
{
Serial1.begin(4800);
Serial.begin(115200);
pinMode(pin, OUTPUT);
//attachInterrupt(0, blink, RISING);
delay(5000);
Serial1.flush();
delay(50);
initiate();
delay(20);
}

void loop()
{

Crank_Pickup();

if (recalc == 1)
{
ANT_event++;
period = time1 - time2;
omega = 6283185.3072/period;
velocity = double(2096000)/double(period);
ANT_INST_power = uint16_t(11.73235736*velocity+0.220287158*velocity*velocity*velocity);
ANT_power += ANT_INST_power;
Serial.print("period: ");
Serial.print(period);
Serial.print("omega: ");
Serial.print(omega);
Serial.print("Vel: ");
Serial.print(velocity);
Serial.print(" Power: ");
Serial.println(ANT_power);
basicpower();
Serial1.flush();
recalc = 0;
}


}

void Crank_Pickup()
{
int reading = digitalRead(Mag_pickup);
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
sent = 0;
}

if ((millis() - lastDebounceTime) > debounceDelay) {
buttonState = reading;
if (buttonState == 1)
{
if (sent == 0)
{
sent = 1;
blink();
}
}
}
lastButtonState = reading;
}

void blink()
{
recalc = 1;
state = !state;
time2 = time1;
time1 = micros();
}

UCHAR checkSum(UCHAR *data, int length)
{
int i;
UCHAR chksum = data[0];
for (i = 1; i < length; i++)
chksum ^= data[i]; // +1 since skip prefix sync code, we already counted it
return chksum;
}

void reset ()
{
uint8_t buf[5];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x01; // LENGTH Byte
buf[2] = MESG_SYSTEM_RESET_ID; // ID Byte
buf[3] = 0x00; // Data Byte N (N=LENGTH)
buf[4] = checkSum(buf,4);
ANTsend(buf,5);
}


void SetNetwork() //thisisANT.com and become an ANT+ Adopter
{
uint8_t buf[13];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x09; // LENGTH Byte
buf[2] = MESG_NETWORK_KEY_ID; // ID Byte
buf[3] = 0x00; // Data Byte N (
buf[4] = 0x00; // Data Byte N (N=LENGTH)
buf[5] = 0x00; // Data Byte N (N=LENGTH)
buf[6] = 0x00; // Data Byte N (N=LENGTH)
buf[7] = 0x00; // Data Byte N (N=LENGTH)
buf[8] = 0x00; // Data Byte N (N=LENGTH)
buf[9] = 0x00; // Data Byte N (N=LENGTH)
buf[10] = 0x00; // Data Byte N (N=LENGTH)
buf[11] = 0x00; // Data Byte N (N=LENGTH)
buf[12] = checkSum(buf, 12);
ANTsend(buf,13);
}
void assignch()
{
uint8_t buf[7];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x03; // LENGTH Byte
buf[2] = MESG_ASSIGN_CHANNEL_ID; // ID Byte
buf[3] = 0x00; // Channel Number
buf[4] = 0x10; // Channel Type
buf[5] = 0x00; // Network ID
buf[6] = checkSum(buf,6);
ANTsend(buf,7);
}

void SetChID()
{
uint8_t buf[9];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x05; // LENGTH Byte
buf[2] = MESG_CHANNEL_ID_ID; // Assign Channel ID 0x51
buf[3] = 0x00; // channel number
buf[4] = 0x05; // Device number
buf[5] = 0x00; // Device number
buf[6] = 0x0B; //Device type ID
buf[7] = 0x00; //Transmission type -CHANGED
buf[8] = checkSum(buf, 8);
ANTsend(buf,9);
}

void ANTsend(uint8_t buf[], int length){
//Serial.print("ANTsend TX: ");
for(int i = 0 ; i <= length ; i++)
{
//Serial.print(buf[i], HEX);
//Serial.print(" ");
Serial1.write(buf[i]);
}
//Serial.println("");
}
void SetFreq()
{
uint8_t buf[6];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x02; // LENGTH Byte
buf[2] = MESG_CHANNEL_RADIO_FREQ_ID; // Set Channel RF Freq 0x45
buf[3] = 0x00; // Channel number
buf[4] = 0x39; // Frequency
buf[5] = checkSum(buf, 5);
ANTsend(buf,6);
}

void SetPeriod()
{
uint8_t buf[7];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x03; // LENGTH Byte
buf[2] = MESG_CHANNEL_MESG_PERIOD_ID; // Set channel period 0x43
buf[3] = 0x00; // Channel number
buf[4] = 0xF6; // Messaging Period byte1
buf[5] = 0x1f; // Messaging period byte2
buf[6] = checkSum(buf, 6);
ANTsend(buf,7);
}

void SetPower()
{
uint8_t buf[6];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x02; // LENGTH Byte
buf[2] = MESG_RADIO_TX_POWER_ID; // Set Tx Power 0x47
buf[3] = 0x00; // Channel Number
buf[4] = 0x03; // Tx power
buf[5] = checkSum(buf, 5);
ANTsend(buf,6);
}

void SetTimeout()
{
uint8_t buf[6];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x02; // LENGTH Byte
buf[2] = MESG_CHANNEL_SEARCH_TIMEOUT_ID; // Set Channel Search Timeout 0x44
buf[3] = 0x00; // Channel number
buf[4] = 0x1E; // Set timeout
buf[5] = checkSum(buf, 5);
ANTsend(buf,6);
}

void OpenChannel()
{
uint8_t buf[5];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x01; // LENGTH Byte
buf[2] = MESG_OPEN_CHANNEL_ID; // ID Byte 0x4B
buf[3] = 0x00;
buf[4] = checkSum(buf, 4);
ANTsend(buf,5);
}

void initiate()
{
SetNetwork();
delay(100);
assignch();
delay(100);
SetChID();
delay(100);
SetFreq();
delay(100);
SetPeriod();
delay(100);
SetPower();
delay(100);
SetTimeout();
delay(100);
OpenChannel();
delay(100);

}

void basicpower()
{
uint8_t buf[13];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x09; // LENGTH Byte
buf[2] = MESG_BROADCAST_DATA_ID; // 0x4E
buf[3] = 0x00; // Channel number
buf[4] = 0x10; // Basic power page identifier
buf[5] = ANT_event; // Event count
buf[6] = 0xFF; // Power differential
buf[7] = 0xFF; // Instant Cadence
buf[8] = byte(ANT_power & 0xFF); // Accumulated power LSB
buf[9] = byte((ANT_power >> 8) & 0xFF); // Accumulated power MSB
buf[10] = byte(ANT_INST_power & 0xFF);; // Instant power LSB
buf[11] = byte((ANT_INST_power >> 8) & 0xFF); // Instant power MSB
buf[12] = checkSum(buf, 12);
ANTsend(buf, 13);
}

6 comments:

  1. Love the work. Looking forward to seeing what comes out of it.

    Good luck.

    ReplyDelete
  2. Nice Article to Read. Our LED stroboscopes use solid-state light sources that are very durable and long lived with no heat generation or drift over time.

    ReplyDelete
  3. Nice Article to Read. Imada digital force gauges capture peak force data and output data for collection by optional data acquisition software in USB, RS232, Digimatic and analog formats.

    ReplyDelete
  4. Nice Article to Read. Imada torque testers capture peak torque or display torque measurement in real time mode. High and low setpoints facilitate Go/No Go testing.

    ReplyDelete
  5. Nice Article to Read. Digital torque wrenches capture peak torque or display torque measurement in real time mode. High and low setpoints with LED indicator and audible alarm facilitate Go/No Go testing.

    ReplyDelete
  6. Nice Article to Read. Digital torque gauges are designed for clockwise and counter-clockwise torque testing. The Jacob’s chuck is a versatile attachment and can be used for a variety of applications.

    ReplyDelete