def main(): # logging.basicConfig() monitor = Monitor() node = Node() node.set_network_key(0x00, NETWORK_KEY) channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) channel.on_broadcast_data = monitor.on_data_heartrate channel.on_burst_data = monitor.on_data_heartrate channel.set_period(8070) channel.set_search_timeout(12) channel.set_rf_freq(57) channel.set_id(0, 120, 0) channel_cadence_speed = node.new_channel( Channel.Type.BIDIRECTIONAL_RECEIVE) channel_cadence_speed.on_broadcast_data = monitor.on_data_cadence_speed channel_cadence_speed.on_burst_data = monitor.on_data_cadence_speed channel_cadence_speed.set_period(8085) channel_cadence_speed.set_search_timeout(30) channel_cadence_speed.set_rf_freq(57) channel_cadence_speed.set_id(0, 121, 0) try: channel.open() channel_cadence_speed.open() node.start() finally: node.stop()
def main(): print("ANT+ Open Rx Scan Mode Demo") logging.basicConfig(filename="example.log", level=logging.DEBUG) TimeProgramStart = time.time() # get start time node = Node() node.set_network_key(0x00, NETWORK_KEY) # 1. Set Network Key # CHANNEL CONFIGURATION channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE, 0x00, 0x00) # 2. Assign channel channel.on_broadcast_data = on_data_scan channel.on_burst_data = on_data_scan channel.on_acknowledge = on_data_scan channel.on_acknowledge_data = on_data_ack_scan # von mir channel.set_id(0, 0, 0) # 3. Set Channel ID channel.set_period(0) # 4. Set Channel Period channel.set_rf_freq(57) # 5. Set RadioFrequenzy channel.enable_extended_messages( 1) # 6. Enable Extended Messages, needed for OpenRxScanMode try: channel.open_rx_scan_mode() # 7. OpenRxScanMode node.start() except KeyboardInterrupt: print("Closing ANT+ Channel") channel.close() node.stop() finally: node.stop() logging.shutdown() # Shutdown Logger
def main(): fan.gearOff() logging.info("Setting:") i = 0 while i < len(fan.pinlist): info = "* gear " + str(i + 1) + " if heartrate greater " + str( myHeartrateLevel[i]) i += 1 logging.info(info) # ant device node = Node() node.set_network_key(0x00, NETWORK_KEY) channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) # define the callback channel.on_broadcast_data = on_data channel.on_burst_data = on_data # channel.set_period(8070) channel.set_search_timeout(12) channel.set_rf_freq(57) channel.set_id(0, 120, 0) try: channel.open() node.start() finally: fan.gearOff() node.stop() GPIO.cleanup()
def __init__(self): self.count = 0 logging.basicConfig(filename="example.log", level=logging.DEBUG) node = Node() node.set_network_key(0x00, NETWORK_KEY) # Master channel configuration for Generic Control Device self.channel = node.new_channel(Channel.Type.BIDIRECTIONAL_TRANSMIT) self.channel.set_id(1, 16, 5) self.channel.set_period(8192) self.channel.set_rf_freq(57) # Callbacks self.channel.on_broadcast_tx_data = self.on_tx_data self.channel.on_acknowledge_data = self.on_acknowledge_data self.channel.on_broadcast_data = self.on_broadcast_data try: print("Opening ANT+ Channel ...") self.channel.open() node.start() except KeyboardInterrupt: print("Closing ANT+ Channel ...") self.channel.close() node.stop() finally: logging.shutdown()
def main(): #logging.basicConfig(filename='example.log',level=logging.DEBUG) node = Node() node.set_network_key(0x00, NETWORK_KEY) channel_scan = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE,0x00,0x01) channel_scan.on_broadcast_data = scan_data channel_scan.on_burst_data = scan_data channel_scan.on_acknowledge = scan_data channel_scan.set_id(0, 120, 0) channel_scan.enable_extended_messages(1) channel_scan.set_search_timeout(0xFF) channel_scan.set_period(8070) channel_scan.set_rf_freq(57) channel_hrm = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) channel_hrm.on_broadcast_data = hrm_data channel_hrm.on_burst_data = hrm_data channel_hrm.on_acknowledge = hrm_data channel_hrm.set_id(49024, 120, 0) channel_hrm.enable_extended_messages(1) channel_hrm.set_search_timeout(0xFF) channel_hrm.set_period(32280) channel_hrm.set_rf_freq(57) channel_hrm2 = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) channel_hrm2.on_broadcast_data = hrm_data channel_hrm2.on_burst_data = hrm_data channel_hrm2.on_acknowledge = hrm_data channel_hrm2.set_id(25170, 120, 0) channel_hrm2.enable_extended_messages(1) channel_hrm2.set_search_timeout(0xFF) channel_hrm2.set_period(32280) channel_hrm2.set_rf_freq(57) try: channel_scan.open() time.sleep(10) channel_scan.close() channel_scan._unassign() channel_hrm.open() channel_hrm2.open() #channel_scan.open() node.start() finally: channel_hrm.close() channel_hrm._unassign() channel_hrm2.close() channel_hrm2._unassign() node.stop()
class AntEasyTests(unittest.TestCase): @unittest.skipUnless("ANT_TEST_USB_STICK" in os.environ, "Testing with USB stick not enabled") def test_search(self): try: logger = logging.getLogger("ant") logger.setLevel(logging.DEBUG) handler = logging.StreamHandler() handler.setFormatter( logging.Formatter( fmt="%(asctime)s %(name)-15s %(levelname)-8s %(message)s" )) logger.addHandler(handler) self.node = Node() print("Request basic information...") m = self.node.request_message(Message.ID.RESPONSE_ANT_VERSION) print(" ANT version: ", struct.unpack("<10sx", m[2])[0]) m = self.node.request_message(Message.ID.RESPONSE_CAPABILITIES) print(" Capabilities: ", m[2]) m = self.node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER) print(" Serial number:", struct.unpack("<I", m[2])[0]) print("Starting system...") NETWORK_KEY = [0xA8, 0xA4, 0x23, 0xB9, 0xF5, 0x5E, 0x63, 0xC1] # self.node.reset_system() self.node.set_network_key(0x00, NETWORK_KEY) c = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) c.set_period(4096) c.set_search_timeout(255) c.set_rf_freq(50) c.set_search_waveform([0x53, 0x00]) c.set_id(0, 0x01, 0) print("Open channel...") c.open() c.request_message(Message.ID.RESPONSE_CHANNEL_STATUS) print("Searching...") self.node.start() print("Done") except KeyboardInterrupt: print("Interrupted") self.node.stop() sys.exit(1) def stop(self): self.node.stop()
class AntEasyTests(unittest.TestCase): @unittest.skipUnless("ANT_TEST_USB_STICK" in os.environ, "Testing with USB stick not enabled") def test_search(self): try: logger = logging.getLogger("ant") logger.setLevel(logging.DEBUG) handler = logging.StreamHandler() handler.setFormatter(logging.Formatter(fmt='%(asctime)s %(name)-15s %(levelname)-8s %(message)s')) logger.addHandler(handler) self.node = Node() print("Request basic information...") m = self.node.request_message(Message.ID.RESPONSE_ANT_VERSION) print(" ANT version: ", struct.unpack("<10sx", m[2])[0]) m = self.node.request_message(Message.ID.RESPONSE_CAPABILITIES) print(" Capabilities: ", m[2]) m = self.node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER) print(" Serial number:", struct.unpack("<I", m[2])[0]) print("Starting system...") NETWORK_KEY = [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1] # self.node.reset_system() self.node.set_network_key(0x00, NETWORK_KEY) c = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) c.set_period(4096) c.set_search_timeout(255) c.set_rf_freq(50) c.set_search_waveform([0x53, 0x00]) c.set_id(0, 0x01, 0) print("Open channel...") c.open() c.request_message(Message.ID.RESPONSE_CHANNEL_STATUS) print("Searching...") self.node.start() print("Done") except KeyboardInterrupt: print("Interrupted") self.node.stop() sys.exit(1) def stop(self): self.node.stop()
def main(): def on_data(data): heartrate = data[7] string = "Heartrate: " + str(heartrate) + " [BPM]" sys.stdout.write(string) sys.stdout.flush() sys.stdout.write("\b" * len(string)) if len(data) > 8: print(data) deviceNumberLSB = data[9] deviceNumberMSB = data[10] deviceNumber = "{}".format(deviceNumberLSB + (deviceNumberMSB << 8)) deviceType = "{}".format(data[11]) print("New Device Found: %s of type %s" % (deviceNumber, deviceType)) result.append(deviceNumber) logging.basicConfig(filename="example.log", level=logging.DEBUG) result = [] node = Node() node.set_network_key(0x00, NETWORK_KEY) print(node.ant._driver._out) channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE, 0x00, 0x01) channel.on_broadcast_data = on_data channel.on_burst_data = on_data channel.on_acknowledge = on_data channel.set_id(0, 120, 0) channel.enable_extended_messages(1) channel.set_search_timeout(0xFF) channel.set_period(8070) channel.set_rf_freq(57) try: node.start() channel.open() time.sleep(10) channel.close() time.sleep(0.5) #just to give the event handler to treat incoming message before unassigning it channel._unassign() result = list(set(result)) result.sort() print("New Devices Found: \n %s " % (result)) finally: node.stop()
def main(): # logging.basicConfig() node = Node() node.set_network_key(0x00, NETWORK_KEY) channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) channel.on_broadcast_data = on_data channel.on_burst_data = on_data channel.set_period(8070) channel.set_search_timeout(12) channel.set_rf_freq(57) channel.set_id(0, 120, 0) try: channel.open() node.start() finally: node.stop()
def main(): node = Node() node.set_network_key(0x00, NETWORK_KEY) channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) channel.on_broadcast_data = on_data channel.on_burst_data = on_data channel.set_period(8070) channel.set_search_timeout(12) channel.set_rf_freq(57) channel.set_id(0, 120, 0) try: channel.open() node.start() finally: node.stop() GPIO.cleanup() colorWipe(strip, Color(0, 0, 0), 10)
def main(): logging.basicConfig(filename='example.log', level=logging.DEBUG) node = Node() node.set_network_key(0x00, NETWORK_KEY) channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE, 0x00, 0x01) channel.on_broadcast_data = on_data channel.on_burst_data = on_data channel.on_acknowledge = on_data channel.set_id(0, 120, 0) channel.enable_extended_messages(1) channel.set_search_timeout(0xFF) channel.set_period(8070) channel.set_rf_freq(57) try: channel.open() node.start() finally: node.stop()
class Application: _serial_number = 1337 _frequency = 19 # 0 to 124, x - 2400 (in MHz) def __init__(self): self._queue = Queue.Queue() self._beacons = Queue.Queue() self._node = Node(0x0fcf, 0x1008) print "Request basic information..." m = self._node.request_message(Message.ID.RESPONSE_VERSION) print " ANT version: ", struct.unpack("<10sx", m[2])[0] m = self._node.request_message(Message.ID.RESPONSE_CAPABILITIES) print " Capabilities: ", m[2] m = self._node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER) print " Serial number:", struct.unpack("<I", m[2])[0] print "Starting system..." NETWORK_KEY = [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1] self._node.reset_system() self._node.set_network_key(0x00, NETWORK_KEY) self._channel = self._node.new_channel( Channel.Type.BIDIRECTIONAL_RECEIVE) self._channel.on_broadcast_data = self._on_data self._channel.on_burst_data = self._on_data self.setup_channel(self._channel) self._worker_thread = threading.Thread(target=self._worker, name="ant.fs") self._worker_thread.start() def _worker(self): self._node.start() def _main(self): try: _logger.debug("Link level") beacon = self._get_beacon() if self.on_link(beacon): for i in range(0, 5): beacon = self._get_beacon() if beacon.get_client_device_state( ) == Beacon.ClientDeviceState.AUTHENTICATION: _logger.debug("Auth layer") if self.on_authentication(beacon): _logger.debug("Authenticated") beacon = self._get_beacon() self.on_transport(beacon) self.disconnect() break except Exception as e: print e for line in traceback.format_exc().splitlines(): _logger.error("%r", line) finally: _logger.debug("Run 5") self.stop() def _on_beacon(self, data): b = Beacon.parse(data) self._beacons.put(b) def _on_command(self, data): c = ant.fs.command.parse(data) self._queue.put(c) def _on_data(self, data): #print "_on_data", data, len(data) if data[0] == 0x43: self._on_beacon(data[:8]) if len(data[8:]) > 0: self._on_command(data[8:]) elif data[0] == 0x44: self._on_command(data) def _get_beacon(self): b = self._beacons.get() self._beacons.task_done() return b def _get_command(self, timeout=3.0): _logger.debug("Get command, t%d, s%d", timeout, self._queue.qsize()) c = self._queue.get(True, timeout) self._queue.task_done() return c def _send_command(self, c): data = c.get() if len(data) == 8: self._channel.send_acknowledged_data(data) else: self._channel.send_burst_transfer(data) # Application actions are defined from here # ======================================================================= # These should be overloaded: def setup_channel(self, channel): pass def on_link(self, beacon): pass def on_authentication(self, beacon): pass def on_transport(self, beacon): pass # Shouldn't have to touch these: def start(self): self._main() def stop(self): self._node.stop() def erase(self, index): pass def upload(self, index, data): pass def download(self, index, callback=None): offset = 0 initial = True crc = 0 data = array.array('B') while True: _logger.debug("Download %d, o%d, c%d", index, offset, crc) self._send_command(DownloadRequest(index, offset, True, crc)) _logger.debug("Wait for response...") try: response = self._get_command() if response._get_argument( "response") == DownloadResponse.Response.OK: remaining = response._get_argument("remaining") offset = response._get_argument("offset") total = offset + remaining data[offset:total] = response._get_argument( "data")[:remaining] #print "rem", remaining, "offset", offset, "total", total, "size", response._get_argument("size") if callback != None: callback( float(total) / float(response._get_argument("size"))) if total == response._get_argument("size"): return data crc = response._get_argument("crc") offset = total else: raise AntFSDownloadException( response._get_argument("response")) except Queue.Empty: _logger.debug("Download %d timeout", index) #print "recover from download failure" def download_directory(self, callback=None): data = self.download(0, callback) return Directory.parse(data) def link(self): self._channel.request_message(Message.ID.RESPONSE_CHANNEL_ID) self._send_command(LinkCommand(self._frequency, 4, self._serial_number)) # New period, search timeout self._channel.set_period(4096) self._channel.set_search_timeout(3) self._channel.set_rf_freq(self._frequency) def authentication_serial(self): self._send_command( AuthenticateCommand(AuthenticateCommand.Request.SERIAL, self._serial_number)) response = self._get_command() return (response.get_serial(), response.get_data_string()) def authentication_passkey(self, passkey): self._send_command( AuthenticateCommand(AuthenticateCommand.Request.PASSKEY_EXCHANGE, self._serial_number, passkey)) response = self._get_command() if response._get_argument( "type") == AuthenticateResponse.Response.ACCEPT: return response.get_data_array() else: raise AntFSAuthenticationException(response._get_argument("type")) def authentication_pair(self, friendly_name): data = array.array('B', map(ord, list(friendly_name))) self._send_command( AuthenticateCommand(AuthenticateCommand.Request.PAIRING, self._serial_number, data)) response = self._get_command(30) if response._get_argument( "type") == AuthenticateResponse.Response.ACCEPT: return response.get_data_array() else: raise AntFSAuthenticationException(response._get_argument("type")) def disconnect(self): d = DisconnectCommand(DisconnectCommand.Type.RETURN_LINK, 0, 0) self._send_command(d)
class AntPlusNode: def __init__(self, network_key): self.node = Node() self.node.set_network_key(0x00, network_key) self.ant_device_id = 1 def attach_hrm(self, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) hrm = AntPlusHRM(channel, device_number=device_number, transfer_type=transfer_type) channel.open() return hrm def attach_power_meter(self, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) pwr_meter = AntPlusPowerMeter(channel, device_number=device_number, transfer_type=transfer_type) pwr_meter._ant_device_id = self.check_id_of_power_meter( ) #id checked once at creation of pwr_meter channel.open() return pwr_meter def attach_speed_sensor(self, wheel_circumference_meters, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) sensor = AntPlusSpeedSensor( channel, wheel_circumference_meters=wheel_circumference_meters, device_number=device_number, transfer_type=transfer_type) channel.open() return sensor def attach_cadence_sensor(self, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) sensor = AntPlusCadenceSensor(channel, device_number=device_number, transfer_type=transfer_type) channel.open() return sensor def attach_speed_and_cadence_sensor(self, wheel_circumference_meters, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) sensor = AntPlusSpeedAndCadenceSensor( channel, wheel_circumference_meters=wheel_circumference_meters, device_number=device_number, transfer_type=transfer_type) channel.open() return sensor def attach_combined_speed_and_cadence_sensor(self, wheel_circumference_meters, speed_device_number=0, speed_transfer_type=0, cadence_device_number=0, cadence_transfer_type=0): channel1 = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) channel2 = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) sensor = AntPlusCombinedSpeedAndCadenceSensors( channel1, channel2, wheel_circumference_meters=wheel_circumference_meters, speed_device_number=speed_device_number, speed_transfer_type=speed_transfer_type, cadence_device_number=cadence_device_number, cadence_transfer_type=cadence_transfer_type) channel1.open() channel2.open() return sensor def check_id_of_power_meter(self, device_number=0, transfer_type=0): meter_id = self.node.request_message(Message.ID.RESPONSE_CHANNEL_ID) return meter_id def start(self): self.node.start() def stop(self): self.node.stop()
class Application: _serial_number = 1337 _frequency = 19 # 0 to 124, x - 2400 (in MHz) def __init__(self): self._queue = Queue.Queue() self._beacons = Queue.Queue() self._node = Node() try: NETWORK_KEY= [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1] self._node.set_network_key(0x00, NETWORK_KEY) print "Request basic information..." m = self._node.request_message(Message.ID.RESPONSE_CAPABILITIES) print " Capabilities: ", m[2] #m = self._node.request_message(Message.ID.RESPONSE_VERSION) #print " ANT version: ", struct.unpack("<10sx", m[2])[0] #m = self._node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER) #print " Serial number:", struct.unpack("<I", m[2])[0] print "Starting system..." #NETWORK_KEY= [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1] #self._node.set_network_key(0x00, NETWORK_KEY) print "Key done..." self._channel = self._node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) self._channel.on_broadcast_data = self._on_data self._channel.on_burst_data = self._on_data self.setup_channel(self._channel) self._worker_thread =threading.Thread(target=self._worker, name="ant.fs") self._worker_thread.start() except Exception as e: self.stop() raise e def _worker(self): self._node.start() def _main(self): try: _logger.debug("Link level") beacon = self._get_beacon() if self.on_link(beacon): for i in range(0, 5): beacon = self._get_beacon() if beacon.get_client_device_state() == Beacon.ClientDeviceState.AUTHENTICATION: _logger.debug("Auth layer") if self.on_authentication(beacon): _logger.debug("Authenticated") beacon = self._get_beacon() self.on_transport(beacon) self.disconnect() break #except Exception as e: # print e # traceback.print_exc() # for line in traceback.format_exc().splitlines(): # _logger.error("%r", line) finally: _logger.debug("Run 5") self.stop() def _on_beacon(self, data): b = Beacon.parse(data) self._beacons.put(b) def _on_command(self, data): c = ant.fs.command.parse(data) self._queue.put(c) def _on_data(self, data): #print "_on_data", data, len(data) if data[0] == 0x43: self._on_beacon(data[:8]) if len(data[8:]) > 0: self._on_command(data[8:]) elif data[0] == 0x44: self._on_command(data) def _get_beacon(self): b = self._beacons.get() self._beacons.task_done() return b def _get_command(self, timeout=3.0): _logger.debug("Get command, t%d, s%d", timeout, self._queue.qsize()) c = self._queue.get(True, timeout) self._queue.task_done() return c def _send_command(self, c): data = c.get() if len(data) == 8: self._channel.send_acknowledged_data(data) else: self._channel.send_burst_transfer(data) # Application actions are defined from here # ======================================================================= # These should be overloaded: def setup_channel(self, channel): pass def on_link(self, beacon): pass def on_authentication(self, beacon): pass def on_transport(self, beacon): pass # Shouldn't have to touch these: def start(self): self._main() def stop(self): self._node.stop() def erase(self, index): pass def _send_commandpipe(self, data): #print "send commandpipe", data self.upload(0xfffe, data) def _get_commandpipe(self): #print "get commandpipe" return ant.fs.commandpipe.parse(self.download(0xfffe)) def create(self, typ, data, callback=None): #print "create", typ request = CreateFile(len(data), 0x80, [typ, 0x00, 0x00], [0x00, 0xff, 0xff]) self._send_commandpipe(request.get()) result = self._get_commandpipe() #result._debug() if result.get_response() != Response.Response.OK: raise AntFSCreateFileException("Could not create file", result.get_response()) #print "create result", result, result.get_index(), result.get_data_type(), result.get_identifier() #d = self.download_directory() self.upload(result.get_index(), data, callback) return result.get_index() def upload(self, index, data, callback=None): #print "upload", index, len(data) iteration = 0 while True: # Request Upload # Continue using Last Data Offset (special MAX_ULONG value) request_offset = 0 if iteration == 0 else 0xffffffff self._send_command(UploadRequest(index, len(data), request_offset)) upload_response = self._get_command() #upload_response._debug() if upload_response._get_argument("response") != UploadResponse.Response.OK: raise AntFSUploadException("Upload request failed", upload_response._get_argument("response")) # Upload data offset = upload_response._get_argument("last_data_offset") max_block = upload_response._get_argument("maximum_block_size") #print " uploading", offset, "to", offset + max_block data_packet = data[offset:offset + max_block] crc_seed = upload_response._get_argument("crc") crc_val = crc(data_packet, upload_response._get_argument("crc")) # Pad with 0 to even 8 bytes missing_bytes = 8 - (len(data_packet) % 8) if missing_bytes != 8: data_packet.extend(array.array('B', [0] * missing_bytes)) #print " adding", str(missing_bytes), "padding" #print " packet", len(data_packet) #print " crc ", crc_val, "from seed", crc_seed self._send_command(UploadDataCommand(crc_seed, offset, data_packet, crc_val)) upload_data_response = self._get_command() #upload_data_response._debug() if upload_data_response._get_argument("response") != UploadDataResponse.Response.OK: raise AntFSUploadException("Upload data failed", upload_data_response._get_argument("response")) if callback != None and len(data) != 0: callback(float(offset) / float(len(data))) if offset + len(data_packet) >= len(data): #print " done" break #print " one more" iteration += 1 def download(self, index, callback=None): offset = 0 initial = True crc = 0 data = array.array('B') while True: _logger.debug("Download %d, o%d, c%d", index, offset, crc) self._send_command(DownloadRequest(index, offset, True, crc)) _logger.debug("Wait for response...") try: response = self._get_command() resp = response._get_argument("response") if resp == DownloadResponse.Response.OK: remaining = response._get_argument("remaining") offset = response._get_argument("offset") total = offset + remaining data[offset:total] = response._get_argument("data")[:remaining] #print "rem", remaining, "offset", offset, "total", total, "size", response._get_argument("size") # TODO: check CRC if callback != None and response._get_argument("size") != 0: callback(float(total) / float(response._get_argument("size"))) if total == response._get_argument("size"): return data crc = response._get_argument("crc") offset = total elif resp == DownloadResponse.Response.NOT_READABLE: return data else: raise AntFSDownloadException("Download request failed: ", response._get_argument("response")) except Queue.Empty: _logger.debug("Download %d timeout", index) #print "recover from download failure" def download_directory(self, callback=None): data = self.download(0, callback) return Directory.parse(data) def link(self): self._channel.request_message(Message.ID.RESPONSE_CHANNEL_ID) self._send_command(LinkCommand(self._frequency, 4, self._serial_number)) # New period, search timeout self._channel.set_period(4096) self._channel.set_search_timeout(3) self._channel.set_rf_freq(self._frequency) def authentication_serial(self): self._send_command(AuthenticateCommand( AuthenticateCommand.Request.SERIAL, self._serial_number)) response = self._get_command() return (response.get_serial(), response.get_data_string()) def authentication_passkey(self, passkey): self._send_command(AuthenticateCommand( AuthenticateCommand.Request.PASSKEY_EXCHANGE, self._serial_number, passkey)) response = self._get_command() if response._get_argument("type") == AuthenticateResponse.Response.ACCEPT: return response.get_data_array() else: raise AntFSAuthenticationException("Passkey authentication failed", response._get_argument("type")) def authentication_pair(self, friendly_name): data = array.array('B', map(ord, list(friendly_name))) self._send_command(AuthenticateCommand( AuthenticateCommand.Request.PAIRING, self._serial_number, data)) response = self._get_command(30) if response._get_argument("type") == AuthenticateResponse.Response.ACCEPT: return response.get_data_array() else: raise AntFSAuthenticationException("Pair authentication failed", response._get_argument("type")) def disconnect(self): d = DisconnectCommand(DisconnectCommand.Type.RETURN_LINK, 0, 0) self._send_command(d)
class SensorANT(Sensor): #for openant node = None NETWORK_KEY = [0xb9, 0xa5, 0x21, 0xfb, 0xbd, 0x72, 0xc3, 0x45] NETWORK_NUM = 0x00 CHANNEL = 0x00 DEVICE_ALL = 0 scanner = None device = {} def sensor_init(self): if not self.config.G_ANT['STATUS']: global _SENSOR_ANT _SENSOR_ANT = False if _SENSOR_ANT: self.node = Node() self.node.set_network_key(self.NETWORK_NUM, self.NETWORK_KEY) #initialize scan channel (reserve ch0) self.scanner = ANT_Device_MultiScan(self.node, self.config) self.searcher = ANT_Device_Search(self.node, self.config, self.values) self.scanner.setMainAntDevice(self.device) #auto connect ANT+ sensor from setting.conf if _SENSOR_ANT and not self.config.G_DUMMY_OUTPUT: for key in ['HR', 'SPD', 'CDC', 'PWR']: if self.config.G_ANT['USE'][key]: antID = self.config.G_ANT['ID'][key] antType = self.config.G_ANT['TYPE'][key] self.connectAntSensor(key, antID, antType, False) return #otherwise, initialize else: for key in ['HR', 'SPD', 'CDC', 'PWR']: self.config.G_ANT['USE'][key] = False self.config.G_ANT['ID'][key] = 0 self.config.G_ANT['TYPE'][key] = 0 #for dummy output if not _SENSOR_ANT and self.config.G_DUMMY_OUTPUT: #need to set dummy ANT+ device id 0 self.config.G_ANT['USE'] = { 'HR': True, 'SPD': True, 'CDC': True, #same as SPD 'PWR': True, } self.config.G_ANT['ID_TYPE'] = { 'HR': struct.pack('<HB', 0, 0x78), 'SPD': struct.pack('<HB', 0, 0x79), 'CDC': struct.pack('<HB', 0, 0x79), #same as SPD 'PWR': struct.pack('<HB', 0, 0x0B), } self.config.G_ANT['TYPE'] = { 'HR': 0x78, 'SPD': 0x79, 'CDC': 0x79, #same as SPD 'PWR': 0x0B, } ac = self.config.G_ANT['ID_TYPE'] self.values[ac['HR']] = {} self.values[ac['SPD']] = {'distance': 0} self.values[ac['PWR']] = {} for key in [0x10, 0x11, 0x12]: self.values[ac['PWR']][key] = {'accumulated_power': 0} def start(self): if _SENSOR_ANT: self.node.start() def update(self): if _SENSOR_ANT or not self.config.G_DUMMY_OUTPUT: return hr_value = random.randint(70, 130) speed_value = random.randint(5, 30) / 3.6 #5 - 30km/h [unit:m/s] cad_value = random.randint(0, 80) power_value = random.randint(0, 250) timestamp = datetime.datetime.now() if 0 < timestamp.second % 60 < 10: hr_value = speed_value = cad_value = power_value = self.config.G_ANT_NULLVALUE if 8 < timestamp.second % 60 < 10: speed_value = cad_value = power_value = 0 ac = self.config.G_ANT['ID_TYPE'] self.values[ac['HR']]['hr'] = hr_value self.values[ac['SPD']]['speed'] = speed_value self.values[ac['CDC']]['cadence'] = cad_value self.values[ac['PWR']][0x10]['power'] = power_value #TIMESTAMP self.values[ac['HR']]['timestamp'] = timestamp self.values[ac['SPD']]['timestamp'] = timestamp self.values[ac['PWR']][0x10]['timestamp'] = timestamp #DISTANCE, TOTAL_WORK if self.config.G_MANUAL_STATUS == "START": #DISTANCE: unit: m if not np.isnan(self.values[ac['SPD']]['speed']): self.values[ac['SPD']]['distance'] += \ self.values[ac['SPD']]['speed']*self.config.G_SENSOR_INTERVAL #TOTAL_WORK: unit: j if not np.isnan(self.values[ac['PWR']][0x10]['power']): self.values[ac['PWR']][0x10]['accumulated_power'] += \ self.values[ac['PWR']][0x10]['power']*self.config.G_SENSOR_INTERVAL def reset(self): for dv in self.device.values(): dv.resetValue() def quit(self): if not _SENSOR_ANT: return self.node.ant.set_wait_action() #stop scanner and searcher if not self.scanner.stop(): for dv in self.device.values(): dv.disconnect(isCheck=True, isChange=False, wait=0) #USE: True -> True self.searcher.stopSearch(resetWait=False) self.node.stop() def connectAntSensor(self, antName, antID, antType, connectStatus): if not _SENSOR_ANT: return self.config.G_ANT['ID'][antName] = antID self.config.G_ANT['TYPE'][antName] = antType self.config.G_ANT['ID_TYPE'][antName] = struct.pack( '<HB', antID, antType) antIDType = self.config.G_ANT['ID_TYPE'][antName] self.searcher.stopSearch(resetWait=False) self.config.G_ANT['USE'][antName] = True #existing connection if connectStatus: self.node.ant.set_wait_period() return #recconect if antIDType in self.device: self.device[antIDType].connect(isCheck=False, isChange=False) #USE: True -> True) self.node.ant.set_wait_period() return #newly connect self.values[antIDType] = {} if antType is 0x78: self.device[antIDType] \ = ANT_Device_HeartRate(self.node, self.config, self.values[antIDType], antName) elif antType is 0x79: self.device[antIDType] \ = ANT_Device_Speed_Cadence(self.node, self.config ,self.values[antIDType], antName) elif antType is 0x7A: self.device[antIDType] \ = ANT_Device_Cadence(self.node, self.config ,self.values[antIDType], antName) elif antType is 0x7B: self.device[antIDType] \ = ANT_Device_Speed(self.node, self.config ,self.values[antIDType], antName) elif antType is 0x0B: self.device[antIDType] \ = ANT_Device_Power(self.node, self.config ,self.values[antIDType], antName) self.node.ant.set_wait_period() def disconnectAntSensor(self, antName): antIDType = self.config.G_ANT['ID_TYPE'][antName] antNames = [] for k, v in self.config.G_ANT['USE'].items(): if v and k in self.config.G_ANT['ID_TYPE']: if self.config.G_ANT['ID_TYPE'][k] == antIDType: antNames.append(k) for k in antNames: #USE: True -> False self.device[self.config.G_ANT['ID_TYPE'][k]].disconnect( isCheck=True, isChange=True) self.config.G_ANT['ID_TYPE'][k] = 0 self.config.G_ANT['ID'][k] = 0 self.config.G_ANT['TYPE'][k] = 0 self.config.G_ANT['USE'][k] = False def continuousScan(self): if not _SENSOR_ANT: return self.node.ant.set_wait_action() for dv in self.device.values(): dv.disconnect(isCheck=True, isChange=False, wait=0.5) #USE: True -> True time.sleep(0.5) self.scanner.scan() self.node.ant.set_wait_scan() def stopContinuousScan(self): self.node.ant.set_wait_action() self.scanner.stopScan() antIDTypes = [] for k, v in self.config.G_ANT['USE'].items(): if v and not self.config.G_ANT['ID_TYPE'][k] in antIDTypes: antIDTypes.append(self.config.G_ANT['ID_TYPE'][k]) for antIDType in antIDTypes: self.device[antIDType].connect(isCheck=True, isChange=False) #USE: True -> True self.node.ant.set_wait_period()
class Application: _serial_number = 1337 _frequency = 19 # 0 to 124, x - 2400 (in MHz) def __init__(self): self._queue = queue.Queue() self._beacons = queue.Queue() self._node = Node() try: NETWORK_KEY = [0xA8, 0xA4, 0x23, 0xB9, 0xF5, 0x5E, 0x63, 0xC1] self._node.set_network_key(0x00, NETWORK_KEY) print("Request basic information...") m = self._node.request_message(Message.ID.RESPONSE_CAPABILITIES) print(" Capabilities: ", m[2]) # m = self._node.request_message(Message.ID.RESPONSE_ANT_VERSION) # print " ANT version: ", struct.unpack("<10sx", m[2])[0] # m = self._node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER) # print " Serial number:", struct.unpack("<I", m[2])[0] print("Starting system...") # NETWORK_KEY= [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1] # self._node.set_network_key(0x00, NETWORK_KEY) print("Key done...") self._channel = self._node.new_channel( Channel.Type.BIDIRECTIONAL_RECEIVE) self._channel.on_broadcast_data = self._on_data self._channel.on_burst_data = self._on_data self.setup_channel(self._channel) self._worker_thread = threading.Thread(target=self._worker, name="ant.fs") self._worker_thread.start() except Exception as e: self.stop() raise e def _worker(self): self._node.start() def _main(self): try: _logger.debug("Link level") beacon = self._get_beacon() if self.on_link(beacon): for i in range(0, 5): beacon = self._get_beacon() if (beacon.get_client_device_state() == Beacon.ClientDeviceState.AUTHENTICATION): _logger.debug("Auth layer") if self.on_authentication(beacon): _logger.debug("Authenticated") beacon = self._get_beacon() self.on_transport(beacon) self.disconnect() break finally: _logger.debug("Run 5") self.stop() def _on_beacon(self, data): b = Beacon.parse(data) self._beacons.put(b) def _on_command(self, data): c = ant.fs.command.parse(data) self._queue.put(c) def _on_data(self, data): # print "_on_data", data, len(data) if data[0] == 0x43: self._on_beacon(data[:8]) if len(data[8:]) > 0: self._on_command(data[8:]) elif data[0] == 0x44: self._on_command(data) def _get_beacon(self): b = self._beacons.get() self._beacons.task_done() return b def _get_command(self, timeout=15.0): _logger.debug("Get command, t%d, s%d", timeout, self._queue.qsize()) c = self._queue.get(True, timeout) self._queue.task_done() return c def _send_command(self, c): data = c.get() if len(data) == 8: self._channel.send_acknowledged_data(data) else: self._channel.send_burst_transfer(data) # Application actions are defined from here # ======================================================================= # These should be overloaded: def setup_channel(self, channel): pass def on_link(self, beacon): pass def on_authentication(self, beacon): pass def on_transport(self, beacon): pass # Shouldn't have to touch these: def start(self): self._main() def stop(self): self._node.stop() def _send_commandpipe(self, data): # print "send commandpipe", data self.upload(0xFFFE, data) def _get_commandpipe(self): # print "get commandpipe" return ant.fs.commandpipe.parse(self.download(0xFFFE)) def create(self, typ, data, callback=None): # print "create", typ request = CreateFile(len(data), 0x80, [typ, 0x00, 0x00], [0x00, 0xFF, 0xFF]) self._send_commandpipe(request.get()) result = self._get_commandpipe() # result._debug() if result.get_response() != Response.Response.OK: raise AntFSCreateFileException("Could not create file", result.get_response()) # print "create result", result, result.get_index(), result.get_data_type(), result.get_identifier() # d = self.download_directory() # Inform the application that the upload request was successfully created if callback is not None: callback(0) self.upload(result.get_index(), data, callback) return result.get_index() def upload(self, index, data, callback=None): # print "upload", index, len(data) iteration = 0 while True: # Request Upload # Continue using Last Data Offset (special MAX_ULONG value) request_offset = 0 if iteration == 0 else 0xFFFFFFFF self._send_command(UploadRequest(index, len(data), request_offset)) upload_response = self._get_command() # upload_response._debug() if upload_response._get_argument( "response") != UploadResponse.Response.OK: raise AntFSUploadException( "Upload request failed", upload_response._get_argument("response")) # Upload data offset = upload_response._get_argument("last_data_offset") max_block = upload_response._get_argument("maximum_block_size") # print " uploading", offset, "to", offset + max_block data_packet = data[offset:offset + max_block] crc_seed = upload_response._get_argument("crc") crc_val = crc(data_packet, upload_response._get_argument("crc")) # Pad with 0 to even 8 bytes missing_bytes = 8 - (len(data_packet) % 8) if missing_bytes != 8: data_packet.extend(array.array("B", [0] * missing_bytes)) # print " adding", str(missing_bytes), "padding" # print " packet", len(data_packet) # print " crc ", crc_val, "from seed", crc_seed self._send_command( UploadDataCommand(crc_seed, offset, data_packet, crc_val)) upload_data_response = self._get_command() # upload_data_response._debug() if (upload_data_response._get_argument("response") != UploadDataResponse.Response.OK): raise AntFSUploadException( "Upload data failed", upload_data_response._get_argument("response")) if callback is not None and len(data) != 0: callback((offset + len(data_packet)) / len(data)) if offset + len(data_packet) >= len(data): # print " done" break # print " one more" iteration += 1 def download(self, index, callback=None): offset = 0 initial = True crc = 0 data = array.array("B") while True: _logger.debug("Download %d, o%d, c%d", index, offset, crc) self._send_command(DownloadRequest(index, offset, True, crc)) _logger.debug("Wait for response...") try: response = self._get_command() if response._get_argument( "response") == DownloadResponse.Response.OK: remaining = response._get_argument("remaining") offset = response._get_argument("offset") total = offset + remaining data[offset:total] = response._get_argument( "data")[:remaining] # print "rem", remaining, "offset", offset, "total", total, "size", response._get_argument("size") # TODO: check CRC if callback is not None and response._get_argument( "size") != 0: callback(total / response._get_argument("size")) if total == response._get_argument("size"): return data crc = response._get_argument("crc") offset = total else: raise AntFSDownloadException( "Download request failed: ", response._get_argument("response")) except queue.Empty: _logger.debug("Download %d timeout", index) # print "recover from download failure" def download_directory(self, callback=None): data = self.download(0, callback) return Directory.parse(data) def set_time(self, time=datetime.datetime.utcnow()): """ :param time: datetime in UTC, or None to set to current time """ utc_tai_diff_seconds = 35 offset = time - datetime.datetime(1989, 12, 31, 0, 0, 0) t = Time( int(offset.total_seconds()) + utc_tai_diff_seconds, 0xFFFFFFFF, 0) self._send_commandpipe(t.get()) result = self._get_commandpipe() if result.get_response() != TimeResponse.Response.OK: raise AntFSTimeException("Failed to set time", result.get_response()) def erase(self, index): self._send_command(EraseRequestCommand(index)) response = self._get_command() if (response._get_argument("response") != EraseResponse.Response.ERASE_SUCCESSFUL): raise AntFSDownloadException("Erase request failed: ", response._get_argument("response")) def link(self): self._channel.request_message(Message.ID.RESPONSE_CHANNEL_ID) self._send_command(LinkCommand(self._frequency, 4, self._serial_number)) # New period, search timeout self._channel.set_period(4096) self._channel.set_search_timeout(10) self._channel.set_rf_freq(self._frequency) def authentication_serial(self): self._send_command( AuthenticateCommand(AuthenticateCommand.Request.SERIAL, self._serial_number)) response = self._get_command() return (response.get_serial(), response.get_data_string()) def authentication_passkey(self, passkey): self._send_command( AuthenticateCommand( AuthenticateCommand.Request.PASSKEY_EXCHANGE, self._serial_number, passkey, )) response = self._get_command() if response._get_argument( "type") == AuthenticateResponse.Response.ACCEPT: return response.get_data_array() else: raise AntFSAuthenticationException("Passkey authentication failed", response._get_argument("type")) def authentication_pair(self, friendly_name): data = array.array("B", map(ord, list(friendly_name))) self._send_command( AuthenticateCommand(AuthenticateCommand.Request.PAIRING, self._serial_number, data)) response = self._get_command(30) if response._get_argument( "type") == AuthenticateResponse.Response.ACCEPT: return response.get_data_array() else: raise AntFSAuthenticationException("Pair authentication failed", response._get_argument("type")) def disconnect(self): d = DisconnectCommand(DisconnectCommand.Type.RETURN_LINK, 0, 0) self._send_command(d)
class AntPlusNode: def __init__(self, network_key): self.node = Node() self.node.set_network_key(0x00, network_key) def attach_hrm(self, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) hrm = AntPlusHRM(channel, device_number=device_number, transfer_type=transfer_type) channel.open() return hrm def attach_power_meter(self, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) pwr_meter = AntPlusPowerMeter(channel, device_number=device_number, transfer_type=transfer_type) channel.open() return pwr_meter def attach_speed_sensor(self, wheel_circumference_meters, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) sensor = AntPlusSpeedSensor( channel, wheel_circumference_meters=wheel_circumference_meters, device_number=device_number, transfer_type=transfer_type) channel.open() return sensor def attach_cadence_sensor(self, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) sensor = AntPlusCadenceSensor(channel, device_number=device_number, transfer_type=transfer_type) channel.open() return sensor def attach_speed_and_cadence_sensor(self, wheel_circumference_meters, device_number=0, transfer_type=0): channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) sensor = AntPlusSpeedAndCadenceSensor( channel, wheel_circumference_meters=wheel_circumference_meters, device_number=device_number, transfer_type=transfer_type) channel.open() return sensor def attach_combined_speed_and_cadence_sensor(self, wheel_circumference_meters, speed_device_number=0, speed_transfer_type=0, cadence_device_number=0, cadence_transfer_type=0): channel1 = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) channel2 = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) sensor = AntPlusCombinedSpeedAndCadenceSensors( channel1, channel2, wheel_circumference_meters=wheel_circumference_meters, speed_device_number=speed_device_number, speed_transfer_type=speed_transfer_type, cadence_device_number=cadence_device_number, cadence_transfer_type=cadence_transfer_type) channel1.open() channel2.open() return sensor def start(self): self.node.start() def stop(self): self.node.stop()
class AntRower: """Ant+ FE Rower signal broadcaster class Start an Ant+ FE channel to broadcast the status of the source "Rower". """ def __init__(self, source: Rower, config: dict): # only take one parameter, source. # source is a object represents a rower, best to be an instance of Rower, # it should have a method of get_current_frame(), which returns a K-V dict, containing the rower info. # this method is called whenever the current rower data is needed, mostly, in a TX event to send out data. self.source = source # channel configurations self.channel_type = Channel.Type.BIDIRECTIONAL_TRANSMIT # Master TX self.network_key = [0xb9, 0xa5, 0x21, 0xfb, 0xbd, 0x72, 0xc3, 0x45] # Ant+ key self.RF_frequency = 57 # Ant+ frequency. self.transmission_type = 5 # MSN = 0x0, LSN = 0x5, detail see ant+ device profile document. self.device_type = 17 # Ant+ FE # if ant device ID not specified, use 1 as default. self.device_number = config.get( 'ANT_DEVICE_ID', 1 ) # Device number could be whatever between 1~65535. Change if you need. self.channel_period = 8192 # 4hz, as device profile requires. # openant radio objects. self.node = None # later, when opened, node will be a instance of Node. self.channel = None # later, when opened, will be the assigned channel object on the node. # outbound message count # used to implement specific transmission pattern, will be rollovered according to the specific pattern. # transmission pattern is implemented in function _get_next_page(), for detail check there. self.message_count = 1 # if transmission type does not specifed, use type b as default. self.transmission_pattern = config.get( 'TRANSMISSION_TYPE', 'b' ) # 1=a, 2=b, 3=c, 4=d, see Ant+ FE device profile page 24 of 74. self.transmission_pattern_func_dict = { 'a': self._get_next_page_transmission_pattern_a, 'b': self._get_next_page_transmission_pattern_b, 'c': self._get_next_page_transmission_pattern_c, 'd': self._get_next_page_transmission_pattern_d } self.transmission_pattern_d_internal_count = 1 # singleton, fixed data pages, 80 and 81 self.page_80 = DataPage80() self.page_81 = DataPage81() # thread handler self.ant_thread = None def on_tx_event(self, data): """Callback function for Ant+ (openant) Upon each TX time slot, a TX event is generated, and this function is called. When called, it should update the rower's data, """ # data = array.array('B', [1, 255, 133, 128, 8, 0, 128, 0]) page_to_send = self._get_next_page() data_payload = page_to_send.to_payload( ) # get new data payload to sent at this TX event. # call channel's send_broadcast_data to set the TX buffer to new data. self.channel.send_broadcast_data(data_payload) print("send TX") def _open_and_start(self): """Open ant+ channel, if no error, start broadcast immediately""" # todo: add the try, catch, maybe Node not available, or network key error, or channel can't acquire # initialize the ant device, a node represent an ant USB device. self.node = Node() # set network key at net#0, only net#0 is used. self.node.set_network_key(0x00, self.network_key) # try get a new TX channel self.channel = self.node.new_channel( Channel.Type.BIDIRECTIONAL_TRANSMIT) # set the callback function for TX tick, each TX tick, this function will be called. self.channel.on_TX_event = self.on_tx_event # set the channel configurations self.channel.set_period(self.channel_period) self.channel.set_rf_freq(self.RF_frequency) # channel id is defined as <device num, device type, transmission type> self.channel.set_id(self.device_number, self.device_type, self.transmission_type) # try open channel, # once opened, the channel could be found by other devices, but No data sending yet. try: self.channel.open() # start the message loop on the ant device. # once started, the messages will be dispatched to callback functions of each channel. self.node.start() finally: self.node.stop() def start(self): """Start the broadcast event loop, from now on, each TX tick, send new broadcast""" self.ant_thread = threading.Thread(target=self._open_and_start) self.ant_thread.start() # this ensures the function is returned immediately to main thread, not blocking other lines in caller. def close(self): # todo: cleaning things up. pass # Transmission pattern handling def _get_next_page(self) -> BaseDataPage: """Implemented the suggested transmission patterns illustrated in Ant+ FE device profile. This function will return the next new page which should be sent out, it handles when and what here, according the message count and the specific pattern. """ assert 0 < self.message_count < 133 if self.message_count == 65 or self.message_count == 66: # send page 80 next_page = self.page_80 elif self.message_count == 131 or self.message_count == 132: # send page 81 next_page = self.page_81 else: # send data page according to transmission type # use dict in a way of switch statement, call different functions of different transmission type. next_page = self.transmission_pattern_func_dict[ self.transmission_pattern]() # roll-over to 1 if msg count reaches 132 if self.message_count == 132: self.message_count = 1 assert self.message_count <= 132 else: self.message_count += 1 return next_page def _get_next_page_transmission_pattern_a(self): """Transmission pattern A Only send page 16, the minimum requirement. :return: data payload of page 16. """ # construct a data page 16 from current rower data of source Rower object. return DataPage16(self.source.get_current_frame()) def _get_next_page_transmission_pattern_b(self): """Transmission pattern B [16 16 X X ...(64)...] (65)80 (66)80 [(67)16 16 X X ....(130)] (131)81 (132)81 Mod left part directly, mode right part with minus 2. """ if self.message_count <= 64: # left half # 1-64, directly mod 4 will be fine, remainder is the sequence. # 1 % 4 = 1, 2%4 = 2, 3%4 = 3, 4%4 =0, 5%4 = 1, ..., 8%4 = 0. if self.message_count % 4 == 1 or self.message_count % 4 == 2: return DataPage16(self.source.get_current_frame()) else: # could only be 3, 0 return DataPage22(self.source.get_current_frame()) elif self.message_count >= 67: # right half, -2 before mod. if ((self.message_count - 2) % 4 == 1) or ((self.message_count - 2) % 4 == 2): return DataPage16(self.source.get_current_frame()) else: # could only be 3, 0 return DataPage22(self.source.get_current_frame()) def _get_next_page_transmission_pattern_c(self): """Transmission pattern C Note that the illustrated pattern in the document is first 64 message are 16 16 X 17 16 16 18 X but second 64 messages are 16 16 17 X 16 16 X 18 Don't know why, but better follow. simply look into message count, see which patter should used. mod by 8 (right part -2 first), then know which page to go. """ if self.message_count <= 64: # left half # 1-64, directly mod 8 will be fine, remainder is the sequence. msg_count_mod_8 = self.message_count % 8 assert 0 <= msg_count_mod_8 < 8 if msg_count_mod_8 == 1 or msg_count_mod_8 == 2: return DataPage16(self.source.get_current_frame()) elif msg_count_mod_8 == 3: return DataPage22(self.source.get_current_frame()) elif msg_count_mod_8 == 4: return DataPage17(self.source.get_current_frame()) elif msg_count_mod_8 == 5 or msg_count_mod_8 == 6: return DataPage16(self.source.get_current_frame()) elif msg_count_mod_8 == 7: return DataPage18(self.source.get_current_frame()) else: # could only be 0, so the 8th return DataPage22(self.source.get_current_frame()) elif self.message_count >= 67: # right half, -2 before mod. msg_count_mod_8 = (self.message_count - 2) % 8 assert 0 <= msg_count_mod_8 < 8 if msg_count_mod_8 == 1 or msg_count_mod_8 == 2: return DataPage16(self.source.get_current_frame()) elif msg_count_mod_8 == 3: return DataPage17(self.source.get_current_frame()) elif msg_count_mod_8 == 4: return DataPage22(self.source.get_current_frame()) elif msg_count_mod_8 == 5 or msg_count_mod_8 == 6: return DataPage16(self.source.get_current_frame()) elif msg_count_mod_8 == 7: return DataPage22(self.source.get_current_frame()) else: # could only be 0, so the 8th return DataPage18(self.source.get_current_frame()) def _get_next_page_transmission_pattern_d(self): """Transmission pattern D This pattern is a little bit different from others, pattern repeat in 20 messages, ignore others. 16 X X X X 16 X X X 18 16 X X X X 16 X X X 17 1,6,11,16 is page 16 10, is page 18 20, page 17 others, page 22 this pattern it self repeats, whenever it in time slot for 64 pages of data, after 80 and 81 interrupts, it resumes, it's independent from the 64-2-64-2 134 messages pattern. So we use a function property to maintain its own pattern counts. This property roll-over every 20 data messages. """ assert 0 < self.transmission_pattern_d_internal_count <= 20 if (self.transmission_pattern_d_internal_count == 1 or self.transmission_pattern_d_internal_count == 6 or self.transmission_pattern_d_internal_count == 11 or self.transmission_pattern_d_internal_count == 16): return DataPage16(self.source.get_current_frame()) elif self.transmission_pattern_d_internal_count == 10: return DataPage18(self.source.get_current_frame()) elif self.transmission_pattern_d_internal_count == 20: # roll-over to 1 self.transmission_pattern_d_internal_count = 1 # return page 17 for current time return DataPage17(self.source.get_current_frame()) else: # should be all page 22. return DataPage22(self.source.get_current_frame())
class AntSendDemo: def __init__(self): self.ANTMessageCount = 0 self.ANTMessagePayload = array.array("B", [0, 0, 0, 0, 0, 0, 0, 0]) # Init Variables, needed self.LastStrideTime = 0 self.StridesDone = 0 self.DistanceAccu = 0 self.Speed_Last = 0 self.TimeRollover = 0 self.TimeProgramStart = time.time() self.LastTimeEvent = time.time() # Building up the Datapages # This is just for demo purpose and can/will look diverent for every implementation def Create_Next_DataPage(self): # Define Variables UpdateLatency_7 = 0 self.ANTMessageCount += 1 # Time Calculations self.ElapsedSseconds = time.time() - self.LastTimeEvent self.LastTimeEvent = time.time() UpdateLatency_7 += self.ElapsedSseconds # 1Second / 32 = 0,03125 UL_7 = int(UpdateLatency_7 / 0.03125) # Stride Count, Accumulated strides. # This value is incremented once for every two footfalls. StrideCountUpValue = 60.0 / (TreadmillCadence / 2.0 ) # In our Example 0,75 while self.LastStrideTime > StrideCountUpValue: self.StridesDone += 1 self.LastStrideTime -= StrideCountUpValue self.LastStrideTime += self.ElapsedSseconds if self.StridesDone > 255: self.StridesDone -= 255 # DISTANCE # Accumulated distance, in m-Meters, Rollover = 256 self.DistanceBetween = self.ElapsedSseconds * TreadmillSpeed self.DistanceAccu += ( self.DistanceBetween ) # Add Distance beetween 2 ANT+ Ticks to Accumulated Distance if self.DistanceAccu > 255: self.DistanceAccu -= 255 self.distance_H = int(self.DistanceAccu) # just round it to INT self.DistanceLow_HEX = int((self.DistanceAccu - self.distance_H) * 16) # SPEED - Berechnung self.var_speed_ms_H = int(TreadmillSpeed) # INT-Value self.var_speed_ms_L = int( TreadmillSpeed * 1000) - (self.var_speed_ms_H * 1000) self.var_speed_ms_L_HEX = int( (TreadmillSpeed - self.var_speed_ms_H) * 256) # TIME (chnages to Distance or speed will effect if This byte needs to be calculated (<= check Specifikation) if self.Speed_Last != TreadmillSpeed or self.Distance_Last != self.DistanceAccu: self.TimeRollover += self.ElapsedSseconds if self.TimeRollover > 255: self.TimeRollover -= 255 self.TimeRollover_H = int(self.TimeRollover) # only integer if self.TimeRollover_H > 255: self.TimeRollover_H = 255 self.TimeRollover_L_HEX = int( (self.TimeRollover - self.TimeRollover_H) * 200) if self.TimeRollover_L_HEX > 255: self.TimeRollover_L_HEX -= 255 self.Speed_Last = TreadmillSpeed self.Distance_Last = self.DistanceAccu if self.ANTMessageCount < 3: self.ANTMessagePayload[0] = 80 # DataPage 80 self.ANTMessagePayload[1] = 0xFF self.ANTMessagePayload[2] = 0xFF # Reserved self.ANTMessagePayload[3] = 1 # HW Revision self.ANTMessagePayload[4] = 1 self.ANTMessagePayload[5] = 1 # Manufacturer ID self.ANTMessagePayload[6] = 1 self.ANTMessagePayload[7] = 1 # Model Number elif self.ANTMessageCount > 64 and self.ANTMessageCount < 67: self.ANTMessagePayload[0] = 81 # DataPage 81 self.ANTMessagePayload[1] = 0xFF self.ANTMessagePayload[2] = 0xFF # Reserved self.ANTMessagePayload[3] = 1 # SW Revision self.ANTMessagePayload[4] = 0xFF self.ANTMessagePayload[5] = 0xFF # Serial Number self.ANTMessagePayload[6] = 0xFF self.ANTMessagePayload[7] = 0xFF # Serial Number else: self.ANTMessagePayload[0] = 0x01 # Data Page 1 self.ANTMessagePayload[1] = self.TimeRollover_L_HEX self.ANTMessagePayload[2] = self.TimeRollover_H # Reserved self.ANTMessagePayload[ 3] = self.distance_H # Distance Accumulated INTEGER # BYTE 4 - Speed-Integer & Distance-Fractional self.ANTMessagePayload[4] = (self.DistanceLow_HEX * 16 + self.var_speed_ms_H ) # Instaneus Speed, Note: INTEGER self.ANTMessagePayload[ 5] = self.var_speed_ms_L_HEX # Instaneus Speed, Fractional self.ANTMessagePayload[6] = self.StridesDone # Stride Count self.ANTMessagePayload[7] = UL_7 # Update Latency # ANTMessageCount reset if self.ANTMessageCount > 131: self.ANTMessageCount = 0 return self.ANTMessagePayload # TX Event def on_event_tx(self, data): ANTMessagePayload = self.Create_Next_DataPage() self.ActualTime = time.time() - self.TimeProgramStart # ANTMessagePayload = array.array('B', [1, 255, 133, 128, 8, 0, 128, 0]) # just for Debuggung pourpose self.channel.send_broadcast_data( self.ANTMessagePayload) # Final call for broadcasting data print( self.ActualTime, "TX:", Device_Number, ",", Device_Type, ":", format_list(ANTMessagePayload), ) # Open Channel def OpenChannel(self): self.node = Node() # initialize the ANT+ device as node # CHANNEL CONFIGURATION self.node.set_network_key(0x00, NETWORK_KEY) # set network key self.channel = self.node.new_channel( Channel.Type.BIDIRECTIONAL_TRANSMIT, 0x00, 0x00) # Set Channel, Master TX self.channel.set_id( Device_Number, Device_Type, 5 ) # set channel id as <Device Number, Device Type, Transmission Type> self.channel.set_period(Channel_Period) # set Channel Period self.channel.set_rf_freq(Channel_Frequency) # set Channel Frequency # Callback function for each TX event self.channel.on_broadcast_tx_data = self.on_event_tx try: self.channel.open( ) # Open the ANT-Channel with given configuration self.node.start() except KeyboardInterrupt: print("Closing ANT+ Channel...") self.channel.close() self.node.stop() finally: print("Final checking...")
channel.set_period(8070) channel.set_search_timeout(255) channel.set_rf_freq(57) channel.set_id(0, 120, 0) return channel def on_data(self, data): self.write_file(data[7]) def write_file(self, heartrate): f = file('/tmp/messages/heartrate', 'w') f.write('Heartrate: ' + str(heartrate)) f.close() logging.basicConfig() logging.disable(logging.ERROR) node = Node() node.set_network_key(0x00, NETWORK_KEY) heartrate = HeartRate() channel_heart = heartrate.setup_channel(node) try: channel_heart.open() node.start() finally: node.stop()
class AntServer(): def __init__(self, netkey, ant_devices, websocket_server): self.ant_devices = ant_devices self.channels = [] self.netkey = netkey self.antnode = None self.websocket_server = websocket_server self.ant_modes = { "speed_cad" : { "device_id" : 121, "period" : 8086, "freq" : 57, "ant_name" : "C:CAD", "handler" : "_handle_speed_cad" }, "hr" : { "device_id" : 120, "period" : 8070, "freq" : 57, "ant_name" : "C:HRM", "handler" : "_handle_hr" }, "power" : { "device_id" : 11, "period" : 8182, "freq" : 57, "ant_name" : "C:PWR", "handler" : "_handle_power" } } self.parsers = { "hr" : antparsers.Hr(), "power" : antparsers.Power(), "speed_cad" : antparsers.SpeedCadence() } self._log_raw_data = False self._log_decoded = True @staticmethod def setup_and_start(websocket_server, config): NETKEY = [0xb9, 0xa5, 0x21, 0xfb, 0xbd, 0x72, 0xc3, 0x45] antserver = None for i in range(1, 3): try: antserver = AntServer(NETKEY, config["devices"], websocket_server) antserver._log_raw_data = config["raw_logging"] antserver._log_decoded = config["verbose"] try: antserver.start() except KeyboardInterrupt: pass finally: if antserver: antserver.stop() break except AntException: print("ERR: Failed to setup ant server. Retrying... %s" % i) def start(self): if not os.path.isdir("logs"): os.mkdir("logs") self.logfile = datetime.strftime(datetime.now(), "logs/%Y%m%d_%H%M.txt") self.logfile_handle = open(self.logfile, "w") self._setup_channels() def stop(self): if self.antnode: self.antnode.stop() def __enter__(self): return self def _setup_channels(self): try: self.antnode = Node() except USBError: self.antnode = None print(""" Fatal error: Could not connect to ANT USB dongle Please make sure no other application is using it, e.g. Garmin ANT Agent """) return self.antnode.set_network_key(0x00, self.netkey) for device in self.ant_devices: print("Registering device : %s : %s" % (device, self.ant_modes[device])) dev = self.ant_modes[device] c = self.antnode.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) c.set_id(0, dev["device_id"], 0) c.set_search_timeout(12) c.set_period(dev["period"]) c.set_rf_freq(dev["freq"]) m = getattr(self, dev["handler"]) c.on_broadcast_data = m c.on_burst_data = m c.open() self.channels.append(c) self.antnode.start() def _log_raw(self, event_type, msg): if self._log_raw_data: data = [] for m in msg: data.append(m) m = json.dumps({"raw" : "true", "event_type" : event_type, "time_millis" : int(time.time() * 1000), "data" : data}) self.logfile_handle.write(m) self.logfile_handle.write("\n") self.logfile_handle.flush() os.fsync(self.logfile_handle.fileno()) def _parse_and_send(self, event_type, data): self._log_raw(event_type, data) parser = self.parsers[event_type] values = parser.parse(data) if values: for value in antparsers.Parser.to_json(values): self.websocket_server.send_to_all(value) if self._log_decoded: print(value) def _handle_hr(self, data): self._parse_and_send("hr", data) def _handle_speed_cad(self, data): self._parse_and_send("speed_cad", data) def _handle_power(self, data): self._parse_and_send("power", data) def __exit__(self, type_, value, traceback): self.stop() self.logfile_handle.close()
class Application: _serial_number = 1337 _frequency = 19 # 0 to 124, x - 2400 (in MHz) def __init__(self): self._queue = Queue.Queue() self._beacons = Queue.Queue() self._node = Node(0x0fcf, 0x1008) print "Request basic information..." m = self._node.request_message(Message.ID.RESPONSE_VERSION) print " ANT version: ", struct.unpack("<10sx", m[2])[0] m = self._node.request_message(Message.ID.RESPONSE_CAPABILITIES) print " Capabilities: ", m[2] m = self._node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER) print " Serial number:", struct.unpack("<I", m[2])[0] print "Starting system..." NETWORK_KEY= [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1] self._node.reset_system() self._node.set_network_key(0x00, NETWORK_KEY) self._channel = self._node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE) self._channel.on_broadcast_data = self._on_data self._channel.on_burst_data = self._on_data self.setup_channel(self._channel) self._worker_thread =threading.Thread(target=self._worker, name="ant.fs") self._worker_thread.start() def _worker(self): self._node.start() def _main(self): try: _logger.debug("Link level") beacon = self._get_beacon() if self.on_link(beacon): for i in range(0, 5): beacon = self._get_beacon() if beacon.get_client_device_state() == Beacon.ClientDeviceState.AUTHENTICATION: _logger.debug("Auth layer") if self.on_authentication(beacon): _logger.debug("Authenticated") beacon = self._get_beacon() self.on_transport(beacon) self.disconnect() break except Exception as e: print e for line in traceback.format_exc().splitlines(): _logger.error("%r", line) finally: _logger.debug("Run 5") self.stop() def _on_beacon(self, data): b = Beacon.parse(data) self._beacons.put(b) def _on_command(self, data): c = ant.fs.command.parse(data) self._queue.put(c) def _on_data(self, data): #print "_on_data", data, len(data) if data[0] == 0x43: self._on_beacon(data[:8]) if len(data[8:]) > 0: self._on_command(data[8:]) elif data[0] == 0x44: self._on_command(data) def _get_beacon(self): b = self._beacons.get() self._beacons.task_done() return b def _get_command(self, timeout=3.0): _logger.debug("Get command, t%d, s%d", timeout, self._queue.qsize()) c = self._queue.get(True, timeout) self._queue.task_done() return c def _send_command(self, c): data = c.get() if len(data) == 8: self._channel.send_acknowledged_data(data) else: self._channel.send_burst_transfer(data) # Application actions are defined from here # ======================================================================= # These should be overloaded: def setup_channel(self, channel): pass def on_link(self, beacon): pass def on_authentication(self, beacon): pass def on_transport(self, beacon): pass # Shouldn't have to touch these: def start(self): self._main() def stop(self): self._node.stop() def erase(self, index): pass def upload(self, index, data): pass def download(self, index, callback=None): offset = 0 initial = True crc = 0 data = array.array('B') while True: _logger.debug("Download %d, o%d, c%d", index, offset, crc) self._send_command(DownloadRequest(index, offset, True, crc)) _logger.debug("Wait for response...") try: response = self._get_command() if response._get_argument("response") == DownloadResponse.Response.OK: remaining = response._get_argument("remaining") offset = response._get_argument("offset") total = offset + remaining data[offset:total] = response._get_argument("data")[:remaining] #print "rem", remaining, "offset", offset, "total", total, "size", response._get_argument("size") if callback != None: callback(float(total) / float(response._get_argument("size"))) if total == response._get_argument("size"): return data crc = response._get_argument("crc") offset = total else: raise AntFSDownloadException(response._get_argument("response")) except Queue.Empty: _logger.debug("Download %d timeout", index) #print "recover from download failure" def download_directory(self, callback=None): data = self.download(0, callback) return Directory.parse(data) def link(self): self._channel.request_message(Message.ID.RESPONSE_CHANNEL_ID) self._send_command(LinkCommand(self._frequency, 4, self._serial_number)) # New period, search timeout self._channel.set_period(4096) self._channel.set_search_timeout(3) self._channel.set_rf_freq(self._frequency) def authentication_serial(self): self._send_command(AuthenticateCommand( AuthenticateCommand.Request.SERIAL, self._serial_number)) response = self._get_command() return (response.get_serial(), response.get_data_string()) def authentication_passkey(self, passkey): self._send_command(AuthenticateCommand( AuthenticateCommand.Request.PASSKEY_EXCHANGE, self._serial_number, passkey)) response = self._get_command() if response._get_argument("type") == AuthenticateResponse.Response.ACCEPT: return response.get_data_array() else: raise AntFSAuthenticationException(response._get_argument("type")) def authentication_pair(self, friendly_name): data = array.array('B', map(ord, list(friendly_name))) self._send_command(AuthenticateCommand( AuthenticateCommand.Request.PAIRING, self._serial_number, data)) response = self._get_command(30) if response._get_argument("type") == AuthenticateResponse.Response.ACCEPT: return response.get_data_array() else: raise AntFSAuthenticationException(response._get_argument("type")) def disconnect(self): d = DisconnectCommand(DisconnectCommand.Type.RETURN_LINK, 0, 0) self._send_command(d)