Skip to content

Instantly share code, notes, and snippets.

@MikeiLL
Last active January 24, 2021 18:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MikeiLL/0ec8b8d39ed6d2dfa4bf4b557dc1f1cd to your computer and use it in GitHub Desktop.
Save MikeiLL/0ec8b8d39ed6d2dfa4bf4b557dc1f1cd to your computer and use it in GitHub Desktop.
// -----------------------
// TypeScript [2.6.2]
// -----------------------
import MSeriesReceiverParser from './mSeriesReceiverParser'
const RECEIVER_VENDOR_ID = 0x0483
const RECEIVER_PRODUCT_ID = 0x5740
const DEVICE_NAME = 'USB Multi-Bike Reciever'
if ("serial" in navigator) {
console.log("The Serial API is supported.")
} else {
console.error('Web serial doesn\'t seem to be enabled in your browser. Try enabling it by visiting:')
console.error('chrome://flags/#enable-experimental-web-platform-features');
console.error('opera://flags/#enable-experimental-web-platform-features');
console.error('edge://flags/#enable-experimental-web-platform-features');
}
export class Receiver {
callback: ((broadcast: Broadcast) => void)
targetPort: SerialPort | null
constructor (callback: ((broadcast: Broadcast) => void)) {
console.log("Receiver callback")
this.callback = callback
this.targetPort = null
navigator.serial.addEventListener('connect', event => { this.attached(event) })
navigator.serial.addEventListener('disconnect', event => { this.detached(event) })
}
async requestPort () {
console.log("requestPort")
console.log(this.targetPort)
if (!this.targetPort) {
try {
this.targetPort = await navigator.serial.requestPort({
filters: [{
usbVendorId: RECEIVER_VENDOR_ID
usbProductId: RECEIVER_PRODUCT_ID
}]
})
this.connect(this.targetPort)
} catch (error) {
console.log(error)
serial_state.deviceMessage = DEVICE_NAME + ': Permission Denied'
serial_state.deviceStatus = false
}
}
}
close () {
console.log("close")
if (this.targetPort) {
console.log(this.targetPort)
this.targetPort.stopTransfer()
this.targetPort = null
}
}
private attached (event: SerialUSB.ConnectionEvent) {
console.log("attached")
console.log(event) // Serial Connection Event
if (this.matchesTarget(event.port)) {
serial_state.deviceMessage = DEVICE_NAME + ': Connected'
serial_state.deviceStatus = true
this.connect(event.port)
}
}
private detached (event: SerialUSB.ConnectionEvent) {
console.log("detached")
if (this.matchesTarget(event.target) && this.targetPort && this.targetPort.isSameDevice(event.target)) {
serial_state.deviceMessage = DEVICE_NAME + ': Disconnected'
serial_state.deviceStatus = false
this.close()
}
}
private connect (port: SerialUSB.Port) {
console.log("connect")
console.log(port) // USB Port
this.targetPort = new SerialPortWrapper(port, this.callback)
serial_state.deviceMessage = DEVICE_NAME + ': Connected'
serial_state.deviceStatus = true
}
private matchesTarget (port: SerialUSB.Port) {
console.log("matchesTarget")
let info = port.getInfo()
return info.usbVendorId === RECEIVER_VENDOR_ID &&
info.usbProductId === RECEIVER_PRODUCT_ID
}
}
export default class SerialPortWrapper {
continueTransfer: boolean
callback: ((broadcast: Broadcast) => void)
portConnection: SerialUSB.Port
readerDone: boolean
constructor (port: SerialUSB.Port, callback: ((broadcast: Broadcast) => void)) {
console.log("Port Wrapper Class")
console.log(port)
this.reader = null
this.continueTransfer = false
this.callback = callback
this.portConnection = port
this.initialize()
}
/*****************************************
* Exposed Controls
*****************************************/
isSameDevice (port: SerialUSB.Port) {
let info = port.getInfo()
return info.serialNumber === this.portConnection.serialNumber
}
async disconnect () {
try {
this.stopTransfer()
this.portConnection.close()
serial_state.deviceMessage = 'Device closed or disconnected. Check connection, reload page or click button to reconnect.'
serial_state.deviceStatus = false
serial_state.dataStatus = false
} catch (error) {
serial_state.deviceMessage = 'Error closing device\n' + error
serial_state.deviceStatus = false
serial_state.dataStatus = false
}
render_serial_activity()
}
/*****************************************
* Control Flow
*****************************************/
async initialize () {
try {
await this.portConnection.open({ baudRate: 115200 })
this.startTransfer()
} catch (error) {
console.error('Initialization error\n', error)
console.log(this.portConnection)
}
}
/*****************************************
* Transfer
*****************************************/
async startTransfer () {
this.continueTransfer = true
while (this.portConnection.readable && this.continueTransfer) {
try {
this.reader = this.portConnection.readable.getReader()
try {
const { value, readerDone } = await this.reader.read()
} catch (error) {
console.error('Reader read failed\n', error)
serial_state.deviceMessage = DEVICE_NAME + ': Reader read failed. Disconnected?'
serial_state.deviceStatus = false
render_serial_activity()
}
if (readerDone) {
// Allow the serial port to be closed later.
this.reader.releaseLock();
break;
}
if (value && value.byteLength > 0) {
this.parse(value)
}
} catch (error) {
// TODO: Handle non-fatal read error.
console.log(error)
} finally {
this.reader.releaseLock()
}
}
await this.portConnection.close()
}
stopTransfer () {
this.continueTransfer = false
}
/*****************************************
* Parse
*****************************************/
parse (data: DataView) {
let rawStream = new Uint8Array(data.buffer)
let segments = String.fromCharCode.apply(null, rawStream).split(' ')
let firstSegmentValue = parseInt(segments[0], 10)
if (firstSegmentValue >= 201) {
console.log("No active Elliptical data")
render_serial_activity()
console.log(segments)
}
if (firstSegmentValue <= 200) {
let swValue = parseFloat(segments[6])
if (swValue >= 6.0 && swValue < 7.0) {
try {
let broadcast = MSeriesReceiverParser(segments)
if (broadcast.ordinalId == instructor_unit){
this.callback(broadcast)
}
} catch (error) {
console.error('M Series parse error\n', error)
}
} else if (swValue >= 8.0 && swValue < 9.0) {
console.error('Incorrect device connected')
}
}
}
}
/*
* Top level callback function through which Receiver does stuff
*/
export default function logEllipticalStats(broadcast){
console.log(broadcast)
}
const receiver = new Receiver( logEllipticalStats )
document.addEventListener('DOMContentLoaded', async () => {
// const receiver = new Receiver( myCallback )
const connectButton = document.querySelector('#connect') as HTMLInputElement
const disconnectButton = document.querySelector('#disconnect') as HTMLInputElement
if (connectButton) {
connectButton.addEventListener('click', async () => {
try {
await receiver.requestPort()
} catch (e) {
console.error(e.message)
}
})
}
if (disconnectButton) {
connectButton.addEventListener('click', async () => {
try {
await receiver.close()
} catch (e) {
console.error(e.message)
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment