127 lines
3.4 KiB
JavaScript
127 lines
3.4 KiB
JavaScript
// serial.js
|
|
|
|
const HEADER1 = 0xAA;
|
|
const HEADER2 = 0x55;
|
|
const BAUD_RATE = 1000000;
|
|
|
|
const CMD_ID_REQUEST = 0x01;
|
|
const CMD_FILE_LIST = 0x02;
|
|
const CMD_LOAD_FILE = 0x03;
|
|
const CMD_DELETE_FILE = 0x04;
|
|
|
|
export class SerialManager {
|
|
constructor() {
|
|
this.port = null;
|
|
this.writer = null;
|
|
this.reader = null;
|
|
}
|
|
|
|
async connect() {
|
|
this.port = await navigator.serial.requestPort();
|
|
await this.port.open({ baudRate: BAUD_RATE });
|
|
this.writer = this.port.writable.getWriter();
|
|
this.reader = this.port.readable.getReader();
|
|
}
|
|
|
|
async send(commandCode, payload = []) {
|
|
const length = payload.length;
|
|
const lengthHigh = (length >> 8) & 0xFF;
|
|
const lengthLow = length & 0xFF;
|
|
|
|
let checksum = commandCode ^ lengthHigh ^ lengthLow;
|
|
for (let byte of payload) {
|
|
checksum ^= byte;
|
|
}
|
|
|
|
const message = [HEADER1, HEADER2, commandCode, lengthHigh, lengthLow, ...payload, checksum];
|
|
console.log(new Uint8Array(message));
|
|
await this.writer.write(new Uint8Array(message));
|
|
}
|
|
|
|
|
|
async requestIDPacket() {
|
|
console.log("Requesting ID packet");
|
|
await this.send(CMD_ID_REQUEST);
|
|
}
|
|
|
|
async requestFileList() {
|
|
console.log("Requesting File List");
|
|
await this.send(CMD_FILE_LIST);
|
|
}
|
|
|
|
async requestFile(filename) {
|
|
const encoder = new TextEncoder();
|
|
const payload = Array.from(encoder.encode(filename));
|
|
await this.send(CMD_LOAD_FILE, payload); // CMD_LOAD_FILE
|
|
}
|
|
|
|
|
|
startReading(onPacket) {
|
|
const decoder = new TextDecoder();
|
|
let buffer = [];
|
|
|
|
const processBuffer = () => {
|
|
while (buffer.length >= 5) {
|
|
if (buffer[0] !== HEADER1 || buffer[1] !== HEADER2) {
|
|
buffer.shift(); // discard until headers align
|
|
continue;
|
|
}
|
|
|
|
const command = buffer[2];
|
|
const length = (buffer[3] << 8) | buffer[4];
|
|
|
|
if (buffer.length < 5 + length) return; // wait for full payload
|
|
|
|
const payload = buffer.slice(5, 5 + length);
|
|
onPacket(command, payload);
|
|
|
|
buffer = buffer.slice(5 + length); // remove processed packet
|
|
}
|
|
};
|
|
|
|
const loop = async () => {
|
|
while (this.port.readable) {
|
|
try {
|
|
const { value, done } = await this.reader.read();
|
|
if (done) break;
|
|
if (value) {
|
|
buffer.push(...value);
|
|
processBuffer();
|
|
}
|
|
} catch (err) {
|
|
console.error("Read error:", err);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
loop();
|
|
}
|
|
|
|
|
|
|
|
async receive(timeoutMs = 1000) {
|
|
const timeoutPromise = new Promise((_, reject) =>
|
|
setTimeout(() => reject(new Error("Timeout waiting for response")), timeoutMs)
|
|
);
|
|
|
|
const readPromise = this.reader.read();
|
|
|
|
try {
|
|
const { value } = await Promise.race([readPromise, timeoutPromise]);
|
|
return value;
|
|
} catch (err) {
|
|
console.error("Receive error:", err.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
disconnect() {
|
|
this.reader.releaseLock();
|
|
this.writer.releaseLock();
|
|
this.port.close();
|
|
}
|
|
}
|
|
|