|  | 
|  | 1 | +/********************************************** | 
|  | 2 | + * AntPlus Bike Sensor Converter | 
|  | 3 | + * LEV Display example | 
|  | 4 | + * MuscleOxygen monitor | 
|  | 5 | + * Shifting monitor | 
|  | 6 | + * | 
|  | 7 | + * Finds a nearby LEV Sensor, pairs | 
|  | 8 | + * to it and then reads the information | 
|  | 9 | + * out via the serial port. | 
|  | 10 | + * | 
|  | 11 | + * Author Bernd Woköck | 
|  | 12 | + * based on the work of Curtis Malainey | 
|  | 13 | + **********************************************/ | 
|  | 14 | +#include <Arduino.h> | 
|  | 15 | +#include "ANT.h" | 
|  | 16 | +#include "ANTPLUS.h" | 
|  | 17 | + | 
|  | 18 | +// get ANT network key | 
|  | 19 | +#include "arduino_secrets.h" | 
|  | 20 | + | 
|  | 21 | +#define BAUD_RATE 9600 | 
|  | 22 | + | 
|  | 23 | +#define ANT_DEVICE_NUMBER_MOXY 7369 | 
|  | 24 | +#define ANT_DEVICE_NUMBER_SHIFT 7370 | 
|  | 25 | +#define CHANNEL_0 0 | 
|  | 26 | +#define CHANNEL_1 1 | 
|  | 27 | +#define CHANNEL_2 2 | 
|  | 28 | + | 
|  | 29 | +const uint8_t NETWORK_KEY[] = SECRET_ANTNETWORK_KEY; | 
|  | 30 | + | 
|  | 31 | +AntWithCallbacks ant = AntWithCallbacks(); | 
|  | 32 | +AntPlusRouter router = AntPlusRouter(); | 
|  | 33 | +ProfileLevDisplay lev = ProfileLevDisplay(); | 
|  | 34 | +ProfileMuscleOxygenMonitor moxy = ProfileMuscleOxygenMonitor(ANT_DEVICE_NUMBER_MOXY); | 
|  | 35 | +ProfileShiftingShifter shift = ProfileShiftingShifter(ANT_DEVICE_NUMBER_SHIFT); | 
|  | 36 | + | 
|  | 37 | +void levBaseDataPageHandler(AntRxDataResponse& msg, uintptr_t data); | 
|  | 38 | +void levSpeedSystemInformation1Handler(LevSpeedSystemInformation1& msg, uintptr_t data); | 
|  | 39 | +void levSpeedDistanceInformationHandler(LevSpeedDistanceInformation& msg, uintptr_t data); | 
|  | 40 | +void levAltSpeedDistanceInformationHandler(LevAltSpeedDistanceInformation& msg, uintptr_t data); | 
|  | 41 | +void levSpeedSystemInformation2Handler(LevSpeedSystemInformation2& msg, uintptr_t data); | 
|  | 42 | +void levBatteryInfo(LevBatteryInfo& msg, uintptr_t data); | 
|  | 43 | +void levAntChannelEvent(ChannelEventResponse& msg, uintptr_t data); | 
|  | 44 | + | 
|  | 45 | +void moxyCreateMsgHandler(MuscleOxygenBaseMainDataPageMsg& msg, uintptr_t data); | 
|  | 46 | + | 
|  | 47 | +void shiftCreateMsgHandler(ShiftingBaseMainDataPageMsg& msg, uintptr_t data); | 
|  | 48 | + | 
|  | 49 | +void manufacturersInformationDataPageHandler(ManufacturersInformation& msg, uintptr_t data); | 
|  | 50 | +void productInformationDataPageHandler(ProductInformation& msg, uintptr_t data); | 
|  | 51 | + | 
|  | 52 | +void printStatus(uint8_t status); | 
|  | 53 | + | 
|  | 54 | +// mask for changed values | 
|  | 55 | +enum { | 
|  | 56 | + SPEED = 1, | 
|  | 57 | + ODOMETER = 2, | 
|  | 58 | + BATTERYSOC = 4, | 
|  | 59 | + SUPPORTLEVEL = 8, | 
|  | 60 | + SUPPORTLEVELCNT = 16, | 
|  | 61 | + PERCENTASSIST = 32, | 
|  | 62 | + WHEELCIRC = 64, | 
|  | 63 | +}; | 
|  | 64 | + | 
|  | 65 | +// LEV data | 
|  | 66 | +struct levDataST { | 
|  | 67 | + uint32_t changedValueMask; // bit field for changed values | 
|  | 68 | + uint16_t speed; // in 0.1km/h | 
|  | 69 | + uint32_t odometer; // in 0.01km | 
|  | 70 | + uint8_t batterySOC; // in percent | 
|  | 71 | + uint8_t supportLevel; // 0..3 | 
|  | 72 | + uint8_t supportLevelCount; // 4 | 
|  | 73 | + uint8_t percentAssist; // in percent | 
|  | 74 | + uint16_t wheelCircumference; // in mm | 
|  | 75 | +} | 
|  | 76 | +_levData = { 0, 0, 0, 0, 0, 0, 0, 0 }; | 
|  | 77 | + | 
|  | 78 | +void setup() { | 
|  | 79 | + Serial2.begin(BAUD_RATE); | 
|  | 80 | + ant.setSerial(Serial2); | 
|  | 81 | + delay(1000); | 
|  | 82 | + | 
|  | 83 | + router.setDriver(&ant); // never touch ant again | 
|  | 84 | + router.setAntPlusNetworkKey(NETWORK_KEY); | 
|  | 85 | + router.setProfile(CHANNEL_0, &lev); | 
|  | 86 | + router.setProfile(CHANNEL_1, &moxy); | 
|  | 87 | + router.setProfile(CHANNEL_2, &shift); | 
|  | 88 | + // Delay after initial setup to wait for user to connect on serial | 
|  | 89 | + | 
|  | 90 | + Serial.begin(BAUD_RATE); | 
|  | 91 | + Serial.println("Running"); | 
|  | 92 | + | 
|  | 93 | + // setup lev display | 
|  | 94 | + lev.onDataPage(levBaseDataPageHandler); | 
|  | 95 | + lev.onLevSpeedSystemInformation1(levSpeedSystemInformation1Handler); | 
|  | 96 | + lev.onLevSpeedDistanceInformation(levSpeedDistanceInformationHandler); | 
|  | 97 | + lev.onLevAltSpeedDistanceInformation(levAltSpeedDistanceInformationHandler); | 
|  | 98 | + lev.onLevSpeedSystemInformation2(levSpeedSystemInformation2Handler); | 
|  | 99 | + lev.onLevBatteryInfo(levBatteryInfo); | 
|  | 100 | + lev.onLevCapabilities(levCapabilities); | 
|  | 101 | + lev.onManufacturersInformation(manufacturersInformationDataPageHandler); | 
|  | 102 | + lev.onProductInformation(productInformationDataPageHandler); | 
|  | 103 | + lev.onChannelEvent(levAntChannelEvent); | 
|  | 104 | + lev.begin(); | 
|  | 105 | + delay(500); // wait for module initialization | 
|  | 106 | + | 
|  | 107 | + // setup muscle oxygen monitor | 
|  | 108 | + moxy.createMuscleOxygenDataMsg(moxyCreateMsgHandler); | 
|  | 109 | + moxy.begin(); | 
|  | 110 | + delay(500); | 
|  | 111 | + | 
|  | 112 | + // setup shifting oxygen monitor | 
|  | 113 | + shift.createShiftingDataMsg(shiftCreateMsgHandler); | 
|  | 114 | + shift.begin(); | 
|  | 115 | + delay(500); | 
|  | 116 | + | 
|  | 117 | +// uint8_t status = lev.waitForPair(); // todo no busy wait here | 
|  | 118 | +} | 
|  | 119 | + | 
|  | 120 | +void loop() { | 
|  | 121 | + router.loop(); | 
|  | 122 | +} | 
|  | 123 | + | 
|  | 124 | +void levAntChannelEvent(ChannelEventResponse& msg, uintptr_t data) { | 
|  | 125 | + if (msg.getCode() == STATUS_EVENT_CHANNEL_CLOSED) | 
|  | 126 | + { | 
|  | 127 | + Serial.println("channel closed - reconnect"); | 
|  | 128 | + lev.begin(); | 
|  | 129 | + } | 
|  | 130 | +} | 
|  | 131 | + | 
|  | 132 | +void levBaseDataPageHandler(AntRxDataResponse& msg, uintptr_t data) { | 
|  | 133 | + LevBaseMainDataPage dp = LevBaseMainDataPage(msg); | 
|  | 134 | + Serial.println("==========================="); | 
|  | 135 | + Serial.print("DataPage: "); | 
|  | 136 | + Serial.println(dp.getDataPageNumber()); | 
|  | 137 | +} | 
|  | 138 | + | 
|  | 139 | +void levSpeedSystemInformation1Handler(LevSpeedSystemInformation1& msg, uintptr_t data) { | 
|  | 140 | + _levData.speed = msg.getSpeed(); | 
|  | 141 | +_levData.supportLevel = decodeSupportLevel(msg.getTravelModeState()); | 
|  | 142 | + _levData.changedValueMask |= SPEED | SUPPORTLEVEL; | 
|  | 143 | + | 
|  | 144 | + Serial.print("Temperature state: "); | 
|  | 145 | + Serial.println(msg.getTemperatureState()); // TODO enums for temperature state | 
|  | 146 | + Serial.print("Travel mode state: "); | 
|  | 147 | + Serial.println(msg.getTravelModeState()); // TODO decode travel mode state | 
|  | 148 | + Serial.print("System state: "); | 
|  | 149 | + Serial.println(msg.getSystemState()); // TODO enums for system state | 
|  | 150 | + Serial.print("Gear state: "); | 
|  | 151 | + Serial.println(msg.getGearState()); // TODO decode gear state | 
|  | 152 | + Serial.print("Gear error: "); | 
|  | 153 | + Serial.println(msg.getErrorMessage()); // TODO enums for error message | 
|  | 154 | + Serial.print("Speed: "); | 
|  | 155 | + Serial.print(msg.getSpeed() / 10); | 
|  | 156 | + Serial.print("."); | 
|  | 157 | + Serial.println(msg.getSpeed() % 10); | 
|  | 158 | +} | 
|  | 159 | + | 
|  | 160 | +void levSpeedDistanceInformationHandler(LevSpeedDistanceInformation& msg, uintptr_t data) { | 
|  | 161 | + _levData.odometer = msg.getOdometer(); | 
|  | 162 | + _levData.speed = msg.getSpeed(); | 
|  | 163 | + _levData.changedValueMask |= SPEED | ODOMETER; | 
|  | 164 | + | 
|  | 165 | + Serial.print("Odometer: "); | 
|  | 166 | + Serial.println((float)msg.getOdometer() / 100); | 
|  | 167 | + Serial.print("Remaining range: "); | 
|  | 168 | + Serial.println(msg.getRemainingRange()); // TODO 0 = unknown | 
|  | 169 | + Serial.print("Speed: "); | 
|  | 170 | + Serial.print(msg.getSpeed() / 10); | 
|  | 171 | + Serial.print("."); | 
|  | 172 | + Serial.println(msg.getSpeed() % 10); | 
|  | 173 | +} | 
|  | 174 | + | 
|  | 175 | +void levAltSpeedDistanceInformationHandler(LevAltSpeedDistanceInformation& msg, uintptr_t data) { | 
|  | 176 | + _levData.odometer = msg.getOdometer(); | 
|  | 177 | + _levData.speed = msg.getSpeed(); | 
|  | 178 | + _levData.changedValueMask |= SPEED | ODOMETER; | 
|  | 179 | + | 
|  | 180 | + Serial.print("Total dist: "); | 
|  | 181 | + Serial.println((float)msg.getOdometer() / 100); | 
|  | 182 | + Serial.print("Fuel consumption: "); | 
|  | 183 | + Serial.println(msg.getFuelConsumption()); // TODO 0 = unknown | 
|  | 184 | + Serial.print("Speed: "); | 
|  | 185 | + Serial.print(msg.getSpeed() / 10); | 
|  | 186 | + Serial.print("."); | 
|  | 187 | + Serial.println(msg.getSpeed() % 10); | 
|  | 188 | +} | 
|  | 189 | + | 
|  | 190 | +void levSpeedSystemInformation2Handler(LevSpeedSystemInformation2& msg, uintptr_t data) { | 
|  | 191 | + _levData.batterySOC = msg.getBatterySOC(); | 
|  | 192 | +_levData.supportLevel = decodeSupportLevel(msg.getTravelModeState()); | 
|  | 193 | +_levData.percentAssist = msg.getPercentAssist(); | 
|  | 194 | + _levData.speed = msg.getSpeed(); | 
|  | 195 | + _levData.changedValueMask |= BATTERYSOC | SUPPORTLEVEL| PERCENTASSIST| SPEED; | 
|  | 196 | + | 
|  | 197 | + Serial.print("Battery SOC: "); | 
|  | 198 | + Serial.println(msg.getBatterySOC()); | 
|  | 199 | + Serial.print("Travel mode state: "); | 
|  | 200 | + Serial.println(msg.getTravelModeState()); // TODO decode travel mode state | 
|  | 201 | + Serial.print("System state: "); | 
|  | 202 | + Serial.println(msg.getSystemState()); // TODO enums for system state | 
|  | 203 | + Serial.print("Gear state: "); | 
|  | 204 | + Serial.println(msg.getGearState()); // TODO decode gear state | 
|  | 205 | + Serial.print("Percent Assist: "); | 
|  | 206 | + Serial.println(msg.getPercentAssist()); | 
|  | 207 | + Serial.print("Speed: "); | 
|  | 208 | + Serial.print(msg.getSpeed() / 10); | 
|  | 209 | + Serial.print("."); | 
|  | 210 | + Serial.println(msg.getSpeed() % 10); | 
|  | 211 | +} | 
|  | 212 | + | 
|  | 213 | +void levBatteryInfo(LevBatteryInfo& msg, uintptr_t data) { | 
|  | 214 | + Serial.print("Charging Cycle Count: "); | 
|  | 215 | + Serial.println(msg.getChargingCycleCount()); | 
|  | 216 | + Serial.print("Fuel consumption: "); | 
|  | 217 | + Serial.println(msg.getFuelConsumption()); | 
|  | 218 | + Serial.print("Battery voltage: "); | 
|  | 219 | + Serial.println(msg.getBatteryVoltage()); | 
|  | 220 | + Serial.print("Distance on current charge: "); | 
|  | 221 | + Serial.println(msg.getDistanceOnCurrentCharge()); | 
|  | 222 | +} | 
|  | 223 | + | 
|  | 224 | +void levCapabilities(LevCapabilities& msg, uintptr_t data) { | 
|  | 225 | + _levData.wheelCircumference = msg.getWheelCircumference(); // in mm | 
|  | 226 | +_levData.supportLevelCount = 4; // (msg.getTravelModesSupported() >> 3) & 0x07; // 4 | 
|  | 227 | + _levData.changedValueMask |= WHEELCIRC | SUPPORTLEVELCNT; | 
|  | 228 | + | 
|  | 229 | + Serial.print("Travel modes supported: "); | 
|  | 230 | + Serial.println(msg.getTravelModesSupported()); | 
|  | 231 | + Serial.print("Wheel circumference: "); | 
|  | 232 | + Serial.println(msg.getWheelCircumference()); | 
|  | 233 | +} | 
|  | 234 | + | 
|  | 235 | +void manufacturersInformationDataPageHandler(ManufacturersInformation& msg, uintptr_t data) { | 
|  | 236 | + Serial.print("DataPage: "); | 
|  | 237 | + Serial.println(msg.getDataPageNumber()); | 
|  | 238 | + Serial.print("HW Revision: "); | 
|  | 239 | + Serial.println(msg.getHWRevision()); | 
|  | 240 | + Serial.print("ManufacturerID: "); | 
|  | 241 | + Serial.println(msg.getManufacturerID()); | 
|  | 242 | + Serial.print("Model Number: "); | 
|  | 243 | + Serial.println(msg.getModelNumber()); | 
|  | 244 | +} | 
|  | 245 | + | 
|  | 246 | +void productInformationDataPageHandler(ProductInformation& msg, uintptr_t data) { | 
|  | 247 | + Serial.print("DataPage: "); | 
|  | 248 | + Serial.println(msg.getDataPageNumber()); | 
|  | 249 | + Serial.print("SW Revision Supplemental: "); | 
|  | 250 | + Serial.println(msg.getSWRevisionSupplemental()); | 
|  | 251 | + Serial.print("SW Revision Main: "); | 
|  | 252 | + Serial.println(msg.getSWRevisionMain()); | 
|  | 253 | + Serial.print("Serial Number: "); | 
|  | 254 | + Serial.println(msg.getSerialNumber()); | 
|  | 255 | +} | 
|  | 256 | + | 
|  | 257 | +void moxyCreateMsgHandler(MuscleOxygenBaseMainDataPageMsg& msg, uintptr_t data) | 
|  | 258 | +{ | 
|  | 259 | + static uint8_t _eventCnt = 0; | 
|  | 260 | + /* value mapping | 
|  | 261 | + batterySOC --> CurrentSaturatedHemoglobinPercentage | 
|  | 262 | + supportlevel.percentAssist --> TotalHemoglobinConcentration | 
|  | 263 | + */ | 
|  | 264 | + | 
|  | 265 | + if (_levData.changedValueMask & (BATTERYSOC | SUPPORTLEVEL | PERCENTASSIST)) { | 
|  | 266 | + _levData.changedValueMask &= ~(BATTERYSOC | SUPPORTLEVEL | PERCENTASSIST); | 
|  | 267 | + _eventCnt++; | 
|  | 268 | + } | 
|  | 269 | + msg.setCurrentSaturatedHemoglobinPercentage(_levData.batterySOC * 10 + _eventCnt % 2); // in 0.1 % (0 - 100)-- > 0 - 1000 | 
|  | 270 | + msg.setTotalHemoglobinConcentration(_levData.supportLevel * 100 + min(99, _levData.percentAssist)); // in 0.01g/dl (0-40) --> 0-4000 | 
|  | 271 | + msg.setEventCount(_eventCnt); | 
|  | 272 | +Serial.println("new moxy values--------------"); | 
|  | 273 | +} | 
|  | 274 | + | 
|  | 275 | +void shiftCreateMsgHandler(ShiftingBaseMainDataPageMsg& msg, uintptr_t data) | 
|  | 276 | +{ | 
|  | 277 | + static uint8_t _eventCnt = 0; | 
|  | 278 | + | 
|  | 279 | + /* value mapping | 
|  | 280 | + supportLevelCount (3) --> TotalNumbersGearFront | 
|  | 281 | + 10 --> TotalNumbersGearRear | 
|  | 282 | + supportLevel (1-3) --> CurrentGearFront | 
|  | 283 | + percentAssist/10 --> CurrentGearRear | 
|  | 284 | + */ | 
|  | 285 | + | 
|  | 286 | + if (_levData.changedValueMask & (SUPPORTLEVELCNT | SUPPORTLEVEL | PERCENTASSIST)) { | 
|  | 287 | + _levData.changedValueMask &= ~(SUPPORTLEVELCNT | SUPPORTLEVEL | PERCENTASSIST); | 
|  | 288 | + _eventCnt++; | 
|  | 289 | + } | 
|  | 290 | + msg.setTotalNumbersGearFront(3); | 
|  | 291 | + msg.setTotalNumbersGearRear(10); | 
|  | 292 | + msg.setCurrentGearFront(_levData.supportLevel-1); | 
|  | 293 | + msg.setCurrentGearRear(10 - (_levData.percentAssist / 10)); | 
|  | 294 | + msg.setEventCount(_eventCnt); | 
|  | 295 | +Serial.println("new shifting values---------------"); | 
|  | 296 | +} | 
|  | 297 | + | 
|  | 298 | + | 
|  | 299 | +uint8_t decodeSupportLevel(uint8_t inValue) { | 
|  | 300 | +const uint8_t slConv[] = {0,1,1,2,2,3,3,4,4}; // specialized: 0,1,3,5 | 
|  | 301 | +uint8_t sl = slConv[((inValue >> 3) & 0x07)]; | 
|  | 302 | +return sl; | 
|  | 303 | +} | 
|  | 304 | + | 
|  | 305 | +void printStatus(uint8_t status) { | 
|  | 306 | + Serial.print("Channel Status: "); | 
|  | 307 | + switch (status) { | 
|  | 308 | + case CHANNEL_STATUS_UNASSIGNED: | 
|  | 309 | + Serial.println("Unassigned"); | 
|  | 310 | + break; | 
|  | 311 | + case CHANNEL_STATUS_ASSIGNED: | 
|  | 312 | + Serial.println("Assigned"); | 
|  | 313 | + break; | 
|  | 314 | + case CHANNEL_STATUS_SEARCHING: | 
|  | 315 | + Serial.println("Searching"); | 
|  | 316 | + break; | 
|  | 317 | + case CHANNEL_STATUS_TRACKING: | 
|  | 318 | + Serial.println("Tracking"); | 
|  | 319 | + break; | 
|  | 320 | + } | 
|  | 321 | +} | 
0 commit comments