summaryrefslogtreecommitdiff
path: root/sample_editor.html
blob: 732230533b8b12467cbd752b5554cd95563e83d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebUSB App</title>
</head>
<body>
<button id="connect">Connect...</button>
<button id="send">Send</button>
<br/>
<canvas id="canvas" width="2048" height="256"></canvas>
<script>
let device;
let interfaceNumber;
let endpointIn;
let endpointOut;
let state = 0, cmd = 0, idx = 0, offset = 0, len = 0, big = 0, payload = [];

function drawWaveform(bytes) {
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const width = canvas.width = 2048; // ensure canvas is 2048 pixels wide
    const height = canvas.height;

    // Clear canvas
    ctx.clearRect(0, 0, width, height);

    // Interpret bytes as 16-bit little-endian signed integers
    const values = [];
    for (let i = 0; i < bytes.length; i += 2) {
        const value = bytes[i] | (bytes[i + 1] << 8); // Little-endian conversion
        values.push(value < 0x8000 ? value : value - 0x10000); // Interpret as signed
    }

    // Scale and draw waveform
    ctx.beginPath();
    for (let x = 0; x < values.length && x < width; x++) {
        const y = ((-values[x] + 32768) / 65535) * height; // Map y range to canvas height
        x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
    }
    ctx.stroke();
}


async function connectDevice() {
    try {
        device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xcafe }] });
        await device.open();
        if (device.configuration === null) await device.selectConfiguration(1);

        const interfaces = device.configuration.interfaces;
        for (const element of interfaces) {
            for (const alternate of element.alternates) {
                if (alternate.interfaceClass === 0xFF) {  // Vendor-specific interface class
                    interfaceNumber = element.interfaceNumber;
                    for (const endpoint of alternate.endpoints) {
                        if (endpoint.direction === 'in') {
                            endpointIn = endpoint.endpointNumber;
                        }
                        if (endpoint.direction === 'out') {
                            endpointOut = endpoint.endpointNumber;
                        }
                    }
                    break;
                }
            }
        }

        if (endpointIn === undefined || endpointOut === undefined) {
            throw new Error('Endpoints not found');
        }

        await device.claimInterface(interfaceNumber);
        await device.selectAlternateInterface(interfaceNumber, 0);
        reset(0);  // Reset state machine
        console.log("Device connected:", device, endpointIn, endpointOut);

        // Change button to "Disconnect"
        document.getElementById('connect').textContent = 'Disconnect';
        
        // Send initial message and start reading loop
        readLoop();
    } catch (error) {
        console.error("Connection error:", error);
    }
}

async function disconnectDevice() {
    try {
        if (device) {
            await device.close();
            device = null;
            console.log("Device disconnected.");
        }
        reset(0);
        document.getElementById('connect').textContent = 'Connect...';
    } catch (error) {
        console.error("Disconnection error:", error);
    }
}

async function sendMsg(cmd, idx, offset, len, optionalData = []) {
    const big = (offset>=65536 || len>=65536);
    const header = big ? 
    [0xf3, 0x0f, 0xab, 0xcb, cmd, idx, offset & 255, (offset >> 8) & 255, (offset >> 16) & 255, offset >> 24, len & 255, (len >> 8) & 255, (len >> 16) & 255, len >> 24] : 
    [0xf3, 0x0f, 0xab, 0xca, cmd, idx, offset & 255, offset >> 8, len & 255, len >> 8];
    const message = header.concat(optionalData);
    for (let i = 0; i < message.length; i += 64) {
        const chunk = message.slice(i, i + 64);
        console.log("Sending data:", chunk);
        await device.transferOut(endpointOut, new Uint8Array(chunk));
    }
}

async function readLoop() {
    while (device) {
        try {
            const result = await device.transferIn(endpointIn, 64);
            const data = new Uint8Array(result.data.buffer);
            console.log("Received data:", data);
            data.forEach(byte => processByte(byte));
            console.log("State:", state, big, cmd, idx, offset, len, payload.length);
        } catch (error) {
            console.error("Read error, device disconnected:", error);
            disconnectDevice();  // Clean up on error
            break;
        }
    }
}

function reset(x) {
    state = big = cmd = idx = offset = len = 0;
    payload = [];
    if (x === 0xf3) state = 1;
    console.log("State reset. Starting state:", state);
}

function processByte(x) {
    switch (state) {
        case 0: if (x === 0xf3) state++; else reset(x); break;
        case 1: if (x === 0x0f) state++; else reset(x); break;
        case 2: if (x === 0xab) state++; else reset(x); break;
        case 3: big = (x=== 0xcb) ? 1 : 0; if (x === 0xca || x==0xcb) state++; else reset(x); break;
        case 4: cmd = x; state++; break;
        case 5: idx = x; state++; break;
        case 6: offset = x; state++; break;
        case 7: offset += x * 256; state=big ? 8 : 10; break;
        case 8: offset += x * 65536; state++; break;
        case 9: offset += x * 16777216; state++; break;
        case 10: len = x; state++; break;
        case 11: len += x * 256; 
            if (big) state++; else {
                state=14; 
                if (len < 0) reset(x); 
                else if (len === 0) processPacket(); 
            }
            break;
        case 12: len += x * 65536; state++; break;
        case 13: len += x * 16777216; state++; 
            if (len < 0) reset(x); 
            else if (len === 0) processPacket();
            break;
        case 14: payload.push(x); len--; 
                 if (len === 0) processPacket(); 
                 break;
    }
}

function processPacket() {
    console.log("Packet received:", { cmd, idx, offset, len, payload });
    drawWaveform(payload);
    reset();
}

document.getElementById('connect').addEventListener('click', async () => {
    if (device) {
        await disconnectDevice();
    } else {
        await connectDevice();
    }
});

document.getElementById('send').addEventListener('click', async () => {
    if (device) {
        idx=5;
        await sendMsg(4, 0, 1031*2*idx, 1031*2);
    }
});
</script>
</body>
</html>