Monday, January 7, 2013

V3–Why it works now and the Power Meter CODE!

 

DSC_9045a

In the above picture you’ll see the MOSI (Master out Slave in) and RTS connection point for the AP2 module on the other side. There was a short. A super fine wire or a sliver of solder. Either way, there was a barely noticeable connection. More after the break including the working code!

I noticed this when I tried to incorporate the RTS line into the code to check when the AP2 thinks it’s okay to send. It didn’t work. I decided maybe the TX line floats and the signal isn’t getting through so I took a 10k resistor between TX and ground. Nothing. Then I thought, maybe the AP2 RTS line needs external termination. I didn’t think so, but figured I’d try by holding a resistor to the RTS. When I went to hold it in place I saw the sliver. I knew what could fix this!

DSC_9046

My trusty Gerber Clutch that cost me a total of 14.99 at Canadian Tire and has saved the day more than once. I cut the connection and voila! The event counts match up and the ANT+ USB stick with ANT Display Simulator never missed a beat! Below is the serial output with the counts. The last count is 20, and the last update is 20 in the Display Simulator. I included another one that has the received codes for a couple of updates.

image

image

My code is below. The ANT+ network key is VETTED as a requirement of ANT+ licence agreement as an ANT+ Adopter. I love ANT+, I want to promote it, but I don’t want to anger the good folks at Dynastream / Garmin / and any hard working ANT+ Alliance member. ANT+, it just works… so long as you are decent at soldering?

Cheers

/*
Keith Wakeham Power Meter
ANT+
*/
#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)
// inslude the SPI library:
#include <SPI.h>
#include <TimerOne.h>

const int slaveSelectPinL = 9; // set pin 8 as the slave select for Left ADC
const int slaveSelectPinR = 8; // set pin 8 as the slave select for Left ADC
const int Mag_pickup = 6; // set pin 8 as the slave select for Left ADC

//For ANT+ output
byte eventcount = 0;
uint16_t ANT_power = 0;
uint8_t ANT_icad = 0; // Instant Cadence
uint16_t accucrankperiod = 0; //Accumulated Crank Period
uint16_t ANT_Torque = 0;

//ADC data
short zeroL = 0;
short zeroR = 0;
long ADC_L = 0;
short ADCL_count = 0;
long ADC_R = 0;
short ADCR_count = 0;
boolean wipe = 0;

double TorqueL = 0;
double TorqueR = 0;
double Torque_sum = 0;

uint8_t ANT_LR = 0;
uint16_t ANT_Period = 0;
long ANT_cranktick = 0;
long ANT_cranktick_OLD = 0;
byte ANT_Cadence = 0;
boolean skipper = 0;

//message processor
boolean msgsync = 0;
uint8_t sync = 0;
uint8_t msglength = 0;
uint8_t msgbuf[10];
uint8_t sendcount = 0;

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

void setup() {
pinMode (slaveSelectPinL, OUTPUT); // set the slaveSelectPin Left as an output:
pinMode (slaveSelectPinR, OUTPUT); // set the slaveSelectPin Left as an output:
pinMode(Mag_pickup, INPUT);
digitalWrite(slaveSelectPinL,HIGH);
digitalWrite(slaveSelectPinR,HIGH);
SPI.setClockDivider(SPI_CLOCK_DIV4); // 8mhz, set the divider to 4
SPI.begin(); // initialize SPI:
Serial1.begin(4800);
Serial.begin(115200);
delay(10);
Serial1.flush();
reset();
delay(5000);
Serial1.flush();
delay(50);
initiate();
delay(20);
//Timer1.initialize(600000);
//Timer1.attachInterrupt(callback);
zeroL = zeroadcL();
zeroR = zeroadcR();
// Serial.print("LZ:");
// Serial.print(zeroL);
// Serial.print(" RZ:_");
// Serial.println(zeroR);
}
void loop() {
readadc_B();
ANTrec();
}

void ANTrec()
{
int sbuflength = Serial1.available();
uint8_t msg = 0;
while(sbuflength > 0)
{
//Serial.print("sbuf: ");
//Serial.println(sbuflength);
if (msgsync == 0)
{
msg = Serial1.read();
if (msg == 0xA4)
{
msgsync = 1;
sync = 0;
}
sbuflength = Serial1.available();

}
else if (msgsync == 1 && sync == 0)
{
msglength = Serial1.read();
sbuflength = Serial1.available();
msglength += 3;
msgbuf[0] = 0xA4;
msgbuf[1] = (msglength-3);
sync++;
}
else if (msgsync == 1 && sync > 0 && sync < msglength)
{
msgbuf[sync+1] = Serial1.read();
sbuflength = Serial1.available();
sync++;
}
else if (msgsync == 1 && sync == msglength)
{
msgbuf[sync+1] = Serial1.read();
sbuflength = Serial1.available();
ANTrecproc(msgbuf, msglength);
//Serial.println("");
msgsync = 0;
sync = 0;
msglength = 0;
}

}
}

void ANTrecproc(uint8_t ANTbuf[], uint8_t ANTlength)
{
if (ANTbuf[2] == 0x40)
{
if (ANTbuf[4] == 0x01) //Transmit Event
{
if (ANTbuf[5] ==3)
{
//Serial.println("transmit success");
if (sendcount < 4)
{
basicpower();
sendcount++;
//Serial.println("Crank Torque");
}
else if (sendcount == 4)
{
basicpower();
//cranktorque();
sendcount = 0;
//Serial.println("Basic Power");
}

}
}
else if (ANTbuf[4] == 0x46 ) //Set Network Key
{
if (ANTbuf[5] ==0)
{
Serial.println("Set Netowork Key: No Error");
}
}
else if (ANTbuf[4] == 0x42 ) // Assign Channel Assigned
{
if (ANTbuf[5] == 0)
{
Serial.println("Assign Channel: No Error");
}
}
else if (ANTbuf[4] == 0x51 ) // Set Channel ID
{
if (ANTbuf[5] == 0)
{
Serial.println("Set Channel ID: No Error");
}
}
else if (ANTbuf[4] == 0x45 ) // Set Frequency
{
if (ANTbuf[5] == 0)
{
Serial.println("Set Frequency: No Error");
}
}
else if (ANTbuf[4] == 0x43 ) // Set Period
{
if (ANTbuf[5] == 0)
{
Serial.println("Set Period: No Error");
}
}
else if (ANTbuf[4] == 0x47 ) //Transmit Power
{
if (ANTbuf[5] == 0)
{
Serial.println("Transmit Power: No Error");
}
}
else if (ANTbuf[4] == 0x44 ) //Set Timeout
{
if (ANTbuf[5] == 0)
{
Serial.println("Set Timeout: No Error");
}
}
else if (ANTbuf[4] == 0x4B ) //Open Channel
{
if (ANTbuf[5] == 0)
{
Serial.println("Open Channel: No Error");
}
}
}

// for(int i = 0 ; i <= ANTlength ; i++)
// {
// Serial.print(ANTbuf[i], HEX);
// Serial.print(" ");
// }
}

void callback()
{
wipe = 1;
eventcount++;
ANT_cranktick_OLD = ANT_cranktick;
ANT_cranktick = millis();
ANT_Period += uint16_t(double(ANT_cranktick - ANT_cranktick_OLD)*2.048); // divide 1000 and in
ANT_icad = uint8_t(long(60000/(ANT_cranktick - ANT_cranktick_OLD)));
readadc_B_reset();

ANT_power += uint16_t(double(Torque_sum * (double(ANT_icad)/60)*6.28));
}

void readadc_B()
{
if (wipe == 1)
{
ADC_L = 0;
ADCL_count = 0;
ADC_R = 0;
ADCR_count =0;
wipe = 0;
}
else if (wipe == 0)
{
for (int i = 0; i < 100; i++)
{
ADC_L += long(readadcL());
ADCL_count++;
ADC_R += long(readadcR());
ADCR_count++;
Crank_Pickup();
}
}
}

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)
{
// Serial.println(eventcount);
// eventcount++;
sent = 1;
callback();
}
}

}
lastButtonState = reading;
// if (digitalRead(Mag_pickup) == 1)
// {
// if ((millis()-ANT_cranktick_OLD) > 350)
// {
// //callback();
//
// }
// }

}

void readadc_B_reset()
{
ADC_L = ADC_L/ADCL_count;
ADC_R = ADC_R/ADCR_count;

TorqueL = double((double(zeroL) - double(ADC_L)) * 0.1725/22.7);
if (TorqueL < 0)
{
TorqueL = 0;
}
TorqueR = double((double(ADC_R) - double(zeroR)) * 0.1725/21.7);
if (TorqueR < 0)
{
TorqueR = 0;
}
Torque_sum = TorqueL + TorqueR;
ANT_Torque += uint16_t(double(Torque_sum*32.0));

if (Torque_sum < 0.5)
{
ANT_LR = 50;
ANT_LR = ANT_LR^0x80;
}
else
{
ANT_LR = uint8_t(double((TorqueR / Torque_sum) * 100));
ANT_LR = ANT_LR^0x80;
}
// Serial.print(" ADCsum(L/R)-C: ");
// Serial.print(ADC_L);
// Serial.print(" / ");
// Serial.print(ADC_R);
// Serial.print("Torque_L: ");
// Serial.print(TorqueL,2);
// Serial.print(" Torque_R: ");
// Serial.print(TorqueR,2);
// Serial.print(" Torque_Sum: ");
// Serial.print(Torque_sum,2);
// Serial.print(" ANT_Torque: ");
// Serial.print(ANT_Torque);
// Serial.print(" ANT_LR: ");
// Serial.println(ANT_LR);
ADC_L = 0;
ADCL_count = 0;
ADC_R = 0;
ADCR_count = 0;
}

short readadcL1000()
{
long adcl = 0; //initialize a long variable
for (int i=0; i < 1000; i++){adcl += long(readadcL());} //Read 1000 times
return (short((adcl/1000))); //Output the average
}

short readadcR1000()
{
long adcr = 0; //initialize a long variable
for (int i=0; i < 1000; i++){adcr += long(readadcR());} //Read 1000 times
return (short((adcr/1000))); //Output the average
}

short zeroadcL()
{
long adcl = 0; //initialize a long variable
for (int i=0; i < 20000; i++){adcl += long(readadcL());} //Read 1000 times
return (short((adcl/20000))); //Output the average
}

short zeroadcR()
{
long adcr = 0; //initialize a long variable
for (int i=0; i < 20000; i++){adcr += long(readadcR());} //Read 1000 times
return (short((adcr/20000))); //Output the average
}

short readadcL() {
digitalWrite(slaveSelectPinL,LOW); // take the SS pin low to select the chip:
byte one = SPI.transfer(0xFF); // send in the address and value via SPI:
byte two = SPI.transfer(0xFF);
byte three = SPI.transfer(0xFF);
digitalWrite(slaveSelectPinL,HIGH); // take the SS pin high to de-select the chip:
return (one << 14 | two << 6 | three >> 2); //output the value from the three bytes as one short
}

short readadcR() {
digitalWrite(slaveSelectPinR,LOW); // take the SS pin low to select the chip:
byte one = SPI.transfer(0xFF); // send in the address and value via SPI:
byte two = SPI.transfer(0xFF);
byte three = SPI.transfer(0xFF);
digitalWrite(slaveSelectPinR,HIGH); // take the SS pin high to de-select the chip:
return (one << 14 | two << 6 | three >> 2); //output the value from the three bytes as one short
}

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()
{
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] = 0xXX; // Data Byte N (N=LENGTH)
buf[5] = 0xXX; // Data Byte N (N=LENGTH)
buf[6] = 0xXX; // Data Byte N (N=LENGTH)
buf[7] = 0xXX; // Data Byte N (N=LENGTH)
buf[8] = 0xXX; // Data Byte N (N=LENGTH)
buf[9] = 0xXX; // Data Byte N (N=LENGTH)
buf[10] = 0xXX; // Data Byte N (N=LENGTH)
buf[11] = 0xXX; // 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] = eventcount; // Event count
buf[6] = ANT_LR; // Power differential
buf[7] = ANT_icad; // Instant Cadence
buf[8] = byte(ANT_power & 0xFF); // Accumulated power LSB
buf[9] = byte((ANT_power >> 8) & 0xFF); // Accumulated power MSB
buf[10] = 0x06; // Instant power LSB
buf[11] = 0x07; // Instant power MSB
buf[12] = checkSum(buf, 12);
ANTsend(buf, 13);
}

void cranktorque()
{
uint8_t buf[13];
buf[0] = MESG_TX_SYNC; // SYNC Byte
buf[1] = 0x09; // LENGTH Byte
buf[2] = MESG_BROADCAST_DATA_ID;
buf[3] = 0x00; // Channel number
buf[4] = 0x12; // Crank torque identifier
buf[5] = eventcount; // Update Event count
buf[6] = eventcount; // Crank ticks
buf[7] = ANT_icad; // Instant Cadence
buf[8] = byte(ANT_Period & 0xFF); // Accumulated Crank Period LSB
buf[9] = byte((ANT_Period >> 8) & 0xFF); // Accumulated Crank Period MSB
buf[10] = byte(ANT_Torque & 0xFF); // Accumulated crank Torque LSB
buf[11] = byte((ANT_Torque >> 8) & 0xFF); // Accumulated crank Torque MSB
buf[12] = checkSum(buf, 12);
ANTsend(buf, 13);
}

2 comments:

  1. I'm working on a project to send Cadence to the garmin unit. Where did you find the network key for the Garmin Device? Also it is always on Channel 0?

    Eoin

    ReplyDelete
    Replies
    1. You have to become an ANT+ adopter (free at www.thisisant.com) in order to get access to the network key. I'm not legally allowed to share it, sorry. Doesn't matter the channel.

      Delete