Skip to content

Commit 2a82cca

Browse files
committed
Update to version with inputs
1 parent 23d21ab commit 2a82cca

5 files changed

Lines changed: 523 additions & 223 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
A browser interface for the DP100 digital power supply by Alientek.
44

5-
![screenshot](screenshot-UI-graph.png)
5+
![screenshot-2024-10-03.png](screenshot-2024-10-03.png)
66

77
## Features
88

99
- 🌐 Connect to the DP100 using your browser (no installation required).
10-
- 📈 Histogram of the voltage and current levels.
10+
- 📈 Power diagram of the voltage and current levels.
11+
- 📏 Set the voltage and current levels.
1112
- ❤️ Free, open source and build with love!
1213

1314
## Usage

assets/js/dp100.js

Lines changed: 147 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const FUNCTIONS = Object.freeze({
4848
* This class is used to interact with the DP100 power supply.
4949
* @example
5050
*
51-
* class MyPSU extends DP100 {
51+
* class MyPSU extends DP100() {
5252
* receiveBasicInfo ({vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt}) {
5353
* console.info('Input Voltage:', vIn, 'V')
5454
* console.info('Output Voltage:', vOut, 'V')
@@ -64,108 +64,162 @@ const FUNCTIONS = Object.freeze({
6464
*
6565
* const psu = new MyPSU()
6666
* await psu.connect()
67+
*
68+
* @param {*} Base - The base class to extend.
69+
* @mixin
70+
* @returns {Base} The new class.
6771
*/
68-
export class DP100 {
72+
export function DP100 (Base) {
73+
return class extends Base {
6974

70-
/** The connected DP100 device. */
71-
device = null
75+
/** The connected DP100 device. */
76+
settingsQueue = []
7277

73-
/** Connect to the DP100 device. */
74-
async connect () {
75-
[this.device] = await navigator.hid.requestDevice({
76-
filters: [{ vendorId, productId }]
77-
})
78-
await this.device.open()
79-
this.device.addEventListener('inputreport', this.inputReportHandler.bind(this))
80-
setInterval(
81-
() => this.sendReport(FUNCTIONS.BASIC_INFO), 10
82-
)
83-
}
78+
/** Connect to the DP100 device. */
79+
async connect () {
80+
[this.device] = await navigator.hid.requestDevice({
81+
filters: [{ vendorId, productId }]
82+
})
83+
await this.device.open()
84+
this.device.addEventListener('inputreport', this.inputReportHandler.bind(this))
85+
this.getBasicSettings().then(() => setInterval(() => this.sendReport(FUNCTIONS.BASIC_INFO), 100))
86+
}
8487

85-
/**
86-
* Send a report to the DP100.
87-
*
88-
* @param {Number} functionId -- The function to call on the DP100.
89-
* @param {Uint8Array} content -- The data to send to the DP100.
90-
* @returns {Promise<void>} -- A promise that resolves when the report is sent.
91-
*/
92-
async sendReport (functionId, content = null) {
93-
content = content || new Uint8Array(0)
94-
const report = new Uint8Array([
95-
deviceAddr,
96-
functionId,
97-
content.length,
98-
content,
99-
0, // checksum
100-
0 // checksum
101-
])
102-
const reportView = new DataView(report.buffer, report.byteOffset, report.byteLength)
103-
const checksum = crc16(report.buffer.slice(0, report.length - 2))
104-
reportView.setUint16(report.length - 2, checksum, true)
105-
console.debug('device.sendReport', reportView)
106-
return await this.device.sendReport(0, report)
107-
}
88+
/**
89+
* Send a report to the DP100.
90+
*
91+
* @param {Number} functionId -- The function to call on the DP100.
92+
* @param {Uint8Array} content -- The data to send to the DP100.
93+
* @param {Number} sequence -- The sequence number for the report.
94+
* @returns {Promise<void>} -- A promise that resolves when the report is sent.
95+
*/
96+
async sendReport (functionId, content = null, sequence = null) {
97+
content = content || new Uint8Array([0])
98+
const header = [deviceAddr, functionId, sequence, // sequence, unused if there is no content
99+
content.length, ...content, 0, // checksum
100+
0 // checksum
101+
]
102+
if (sequence === null) {
103+
header.splice(2, 1)
104+
}
105+
const report = new Uint8Array(header)
106+
const reportView = new DataView(report.buffer, report.byteOffset, report.byteLength)
107+
const checksum = crc16(report.buffer.slice(0, report.length - 2))
108+
reportView.setUint16(report.length - 2, checksum, true)
109+
console.debug('device.sendReport', reportView)
110+
return await this.device.sendReport(0, report)
111+
}
108112

109-
/** Handle input reports from the DP100
110-
* @param {HIDInputReportEvent} event
111-
*/
112-
inputReportHandler (event) {
113-
console.debug('device.inputreport', event)
114-
const data = event.data
115-
const headerLength = 4
116-
const header = {
117-
deviceAddr: data.getUint8(0),
118-
functionType: event.data.getUint8(1),
119-
sequence: event.data.getUint8(2),
120-
contentLength: event.data.getUint8(3),
113+
async getBasicSettings () {
114+
console.info('getBasicSettings')
115+
await this.sendReport(FUNCTIONS.BASIC_SET, new Uint8Array([0 | 0x80]), 0)
121116
}
122-
const contentView = new DataView(data.buffer.slice(headerLength, headerLength + header.contentLength))
123-
const checksum = data.getUint16(headerLength + header.contentLength, true)
124-
const computedChecksum = crc16(data.buffer.slice(0, headerLength + header.contentLength))
125-
if (computedChecksum !== checksum) {
126-
console.error('Checksum Failed', {
127-
expected: computedChecksum.toString(16),
128-
received: checksum.toString(16)
129-
})
130-
return
117+
118+
async setBasicSettings ({ state, vo_set, io_set, ovp_set, ocp_set }) {
119+
if (this.settings === undefined) {
120+
throw new Error('Settings not loaded')
121+
}
122+
console.info('setBasicSettings', { state, vo_set, io_set, ovp_set, ocp_set })
123+
const basicSet = Object.assign({}, this.settings, Object.fromEntries(Object.entries({
124+
state, vo_set, io_set, ovp_set, ocp_set
125+
}).filter(([k, v]) => v !== undefined)))
126+
const index = this.settingsQueue.length
127+
this.settingsQueue[index] = basicSet
128+
const out = new Uint8Array(10)
129+
const outDv = new DataView(out.buffer, out.byteOffset, out.length)
130+
outDv.setUint8(0, index | 0x20)
131+
outDv.setUint8(1, basicSet.state)
132+
outDv.setUint16(2, basicSet.vo_set * 1000, true)
133+
outDv.setUint16(4, basicSet.io_set * 1000, true)
134+
outDv.setUint16(6, basicSet.ovp_set * 1000, true)
135+
outDv.setUint16(8, basicSet.ocp_set * 1000, true)
136+
await this.sendReport(FUNCTIONS.BASIC_SET, out, 0)
131137
}
132-
console.debug('content', contentView)
133138

134-
switch (header.functionType) {
135-
case FUNCTIONS.BASIC_INFO:
136-
this.receiveBasicInfo({
137-
vIn: contentView.getUint16(0, true) / 1000,
138-
vOut: contentView.getUint16(2, true) / 1000,
139-
iOut: contentView.getUint16(4, true) / 1000,
140-
voMax: contentView.getUint16(6, true) / 1000,
141-
temp1: contentView.getUint16(8, true) / 10,
142-
temp2: contentView.getUint16(10, true) / 10,
143-
dc5V: contentView.getUint16(12, true) / 1000,
144-
outMode: contentView.getUint8(14),
145-
workSt: contentView.getUint8(15)
139+
/** Handle input reports from the DP100
140+
* @param {HIDInputReportEvent} event
141+
*/
142+
inputReportHandler (event) {
143+
console.debug('device.inputreport', event)
144+
const data = event.data
145+
const headerLength = 4
146+
const header = {
147+
deviceAddr: data.getUint8(0),
148+
functionType: event.data.getUint8(1),
149+
sequence: event.data.getUint8(2),
150+
contentLength: event.data.getUint8(3),
151+
}
152+
const contentView = new DataView(data.buffer.slice(headerLength, headerLength + header.contentLength))
153+
const checksum = data.getUint16(headerLength + header.contentLength, true)
154+
const computedChecksum = crc16(data.buffer.slice(0, headerLength + header.contentLength))
155+
if (computedChecksum !== checksum) {
156+
console.error('Checksum Failed', {
157+
expected: computedChecksum.toString(16), received: checksum.toString(16)
146158
})
147-
break
148-
default:
149-
console.warn('Unhandled function', header.functionType)
159+
return
160+
}
161+
switch (header.functionType) {
162+
case FUNCTIONS.BASIC_INFO:
163+
this.receiveBasicInfo({
164+
vIn: contentView.getUint16(0, true) / 1000,
165+
vOut: contentView.getUint16(2, true) / 1000,
166+
iOut: contentView.getUint16(4, true) / 1000,
167+
voMax: contentView.getUint16(6, true) / 1000,
168+
temp1: contentView.getUint16(8, true) / 10,
169+
temp2: contentView.getUint16(10, true) / 10,
170+
dc5V: contentView.getUint16(12, true) / 1000,
171+
outMode: contentView.getUint8(14),
172+
workSt: contentView.getUint8(15)
173+
})
174+
break
175+
case FUNCTIONS.BASIC_SET:
176+
if (contentView.byteLength === 1) {
177+
this.settings = this.settingsQueue.pop(contentView.getUint8(0))
178+
break
179+
}
180+
this.receiveBasicSettings({
181+
index: contentView.getUint8(0),
182+
state: contentView.getUint8(1),
183+
vo_set: contentView.getUint16(2, true) / 1000,
184+
io_set: contentView.getUint16(4, true) / 1000,
185+
ovp_set: contentView.getUint16(6, true) / 1000,
186+
ocp_set: contentView.getUint16(8, true) / 1000,
187+
})
188+
break
189+
default:
190+
console.warn('Unhandled function', header.functionType)
191+
}
192+
}
193+
194+
/** Handle basic info from the DP100
195+
* @param {Object} basicInfo
196+
* @param {Number} basicInfo.vIn - Input voltage in V.
197+
* @param {Number} basicInfo.vOut - Output voltage in V.
198+
* @param {Number} basicInfo.iOut - Output current in A.
199+
* @param {Number} basicInfo.voMax - Max output voltage in V.
200+
* @param {Number} basicInfo.temp1 - Temperature 1 in °C.
201+
* @param {Number} basicInfo.temp2 - Temperature 2 in °C.
202+
* @param {Number} basicInfo.dc5V - 5V rail in V.
203+
* @param {Number} basicInfo.outMode - Output mode.
204+
* @param {Number} basicInfo.workSt - Work state.
205+
*/
206+
receiveBasicInfo ({ vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }) {
207+
console.debug('receiveBasicInfo', { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt })
208+
this.info = { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }
150209
}
151-
}
152210

153-
/** Handle basic info from the DP100
154-
* @param {Object} basicInfo
155-
* @param {Number} basicInfo.vIn - Input voltage in mV.
156-
* @param {Number} basicInfo.vOut - Output voltage in mV.
157-
* @param {Number} basicInfo.iOut - Output current in mA.
158-
* @param {Number} basicInfo.voMax - Max output voltage in mV.
159-
* @param {Number} basicInfo.temp1 - Temperature 1 in 0.1°C.
160-
* @param {Number} basicInfo.temp2 - Temperature 2 in 0.1°C.
161-
* @param {Number} basicInfo.dc5V - 5V rail in mV.
162-
* @param {Number} basicInfo.outMode - Output mode.
163-
* @param {Number} basicInfo.workSt - Work state.
164-
*/
165-
receiveBasicInfo ({ vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }) {
166-
const BasicInfoEvent = new CustomEvent('basicInfo', {
167-
detail: { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }
168-
})
169-
document.dispatchEvent(BasicInfoEvent)
211+
/** Handle basic settings from the DP100
212+
* @param {Object} basicSettings
213+
* @param {Number} basicSettings.index - Setting index.
214+
* @param {Number} basicSettings.state - Setting state.
215+
* @param {Number} basicSettings.vo_set - Output voltage setting in V.
216+
* @param {Number} basicSettings.io_set - Output current setting in A.
217+
* @param {Number} basicSettings.ovp_set - Over-voltage protection setting in V.
218+
* @param {Number} basicSettings.ocp_set - Over-current protection setting in A.
219+
*/
220+
receiveBasicSettings ({ index, state, vo_set, io_set, ovp_set, ocp_set }) {
221+
console.debug('receiveBasicSettings', { index, state, vo_set, io_set, ovp_set, ocp_set })
222+
this.settings = { state, vo_set, io_set, ovp_set, ocp_set }
223+
}
170224
}
171225
}

0 commit comments

Comments
 (0)