webserial-core
Version:
Webserial Core to easy connections with serial devices
317 lines (262 loc) • 9.05 kB
Markdown
# WebSerial Core
And easy way to connect to a serial port from a web page.
> [!NOTE]
> Since version 1.0.7 default response is an instance of Uint8Array.
> To change the response put this code inside your constructor to change the default response.
> `this.getResponseAsArrayBuffer()`
> `this.getResponseAsArrayHex()`
> `this.getResponseAsUint8Array()`
> `this.getResponseAsString()`
> Only choose one of them.
## Installation
```bash
npm install webserial-core
```
## Usage
> [!NOTE]
> If you are using Linux, you need to add your user to the `dialout` group to access the serial port.
> ```bash
> sudo usermod -a -G dialout $USER
> ```
> After that, you need to log out and log in again to apply the changes.
You need to create a new class to configure your device functions. In this example, we are going to create a class to
connect to an Arduino device.
The first step is having the Arduino code ready. In this case, we are going to use the following code.
```cpp
// serial.ino
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) { // Check if data to read is available
String comando = Serial.readStringUntil('\n'); // read the data until a new line is found
if(comando.startsWith("CONNECT")){
Serial.println("connected");
} else if (comando.startsWith("CREDITS")) {
Serial.println("created by danidoble");
} else if (comando.startsWith("HI")) {
Serial.println("hello there");
} else {
Serial.println("ara ara, what are you doing?");
}
}
}
```
Create a new class to connect to the device.
```javascript
// arduino.js
import { Core } from 'webserial-core';
export class Arduino extends Core {
constructor(
{
filters = null,
config_port = {
baudRate: 9600,
dataBits: 8,
stopBits: 1,
parity: "none",
bufferSize: 32768,
flowControl: "none",
},
no_device = 1,
} = {
filters: null,
config_port: {
baudRate: 9600,
dataBits: 8,
stopBits: 1,
parity: "none",
bufferSize: 32768,
flowControl: "none",
},
no_device: 1,
}
) {
super({ filters, config_port, no_device });
this.__internal__.device.type = "arduino";
Devices.registerType(this.__internal__.device.type);
if (Devices.getByNumber(this.typeDevice, no_device)) {
throw new Error(`Device ${this.typeDevice} ${no_device} already exists`);
}
this.__internal__.time.response_connection = 2e3;
this.__internal__.time.response_general = 2e3;
this.__internal__.serial.delay_first_connection = 1_000;
this.#registerAvailableListenersArduino();
Devices.add(this);
this.getResponseAsString();
}
#registerAvailableListenersArduino() {
/*const _ = [
'my_own_event_dispatched',
'my_other_own_event_dispatched',
];
for (const event of _) {
this.serialRegisterAvailableListener(event)
}
*/
}
serialMessage(codex) {
const message = {
code: [],
name: "",
description: "",
request: "",
no_code: 0,
};
message.code = codex;
switch (codex) {
case "connected":
message.name = "connected";
message.description = "Connection established";
message.request = "connect";
message.no_code = 100;
break;
case "created by danidoble":
message.name = "thanks";
message.description = "thanks for using this software";
message.request = "credits";
message.no_code = 101;
break;
case "hello there":
message.name = "hello there";
message.description = "hi human";
message.request = "hi";
message.no_code = 102;
break;
case "ara ara":
message.name = "ara ara";
message.description = "troll";
message.request = "ara ara";
message.no_code = 404;
break;
default:
message.name = "unknown";
message.description = "Unknown command";
message.request = "unknown";
message.no_code = 400;
break;
}
this.dispatch("serial:message", message);
}
serialSetConnectionConstant() {
return this.add0x(this.parseStringToBytes("CONNECT"));
}
async sayCredits() {
const arr = this.parseStringToBytes("CREDITS");
await this.appendToQueue(arr, "credits");
}
async sayHi() {
const arr = this.parseStringToBytes("HI");
await this.appendToQueue(arr, "hi");
}
async sayAra() {
const arr = this.parseStringToBytes("OTHER");
await this.appendToQueue(arr, "ara");
}
async sendCustomCode({ code = "" } = { code: "" }) {
if (typeof code !== "string") throw new Error("Invalid string");
const arr = this.parseStringToBytes(code);
await this.appendToQueue(arr, "custom");
}
}
```
Then you can use the class to connect to your device.
```javascript
// serialConnection.js
import { Arduino } from './arduino.js';
const arduino = new Arduino();
arduino.on('serial:message', (message) => {
console.log(message);
});
arduino.on('serial:timeout', (data) => {
console.log('serial:timeout', data.detail);
});
// if you need to debug the data sent
// arduino.on('serial:sent', data => {
// console.log('serial:sent',data.detail);
// });
arduino.on('serial:error', (event) => {
document.getElementById('log').innerText += event.detail.message + '\n\n';
});
// eslint-disable-next-line no-unused-vars
arduino.on('serial:disconnected', (event) => {
document.getElementById('log').innerText += 'Disconnected\n\n';
document.getElementById('disconnected').classList.remove('hidden');
document.getElementById('connect').classList.remove('hidden');
document.getElementById("disconnect")?.classList.add("hidden");
});
// eslint-disable-next-line no-unused-vars
arduino.on('serial:connecting', (event) => {
document.getElementById('log').innerText += 'Connecting\n\n';
});
// eslint-disable-next-line no-unused-vars
arduino.on('serial:connected', (event) => {
document.getElementById('log').innerText += 'Connected\n\n';
document.getElementById('disconnected').classList.add('hidden');
document.getElementById('need-permission').classList.add('hidden');
document.getElementById('connect').classList.add('hidden');
document.getElementById("disconnect")?.classList.remove("hidden");
});
// eslint-disable-next-line no-unused-vars
arduino.on('serial:need-permission', (event) => {
document.getElementById('disconnected').classList.remove('hidden');
document.getElementById('need-permission').classList.remove('hidden');
document.getElementById('connect').classList.remove('hidden');
document.getElementById("disconnect")?.classList.add("hidden");
});
// eslint-disable-next-line no-unused-vars
arduino.on('serial:soft-reload', (event) => {
// reset your variables
});
// eslint-disable-next-line no-unused-vars
arduino.on('serial:unsupported', (event) => {
document.getElementById('unsupported').classList.remove('hidden');
});
function tryConnect() {
arduino
.connect()
.then(() => {})
.catch(console.error);
}
document.addEventListener('DOMContentLoaded', () => {
tryConnect();
document.getElementById('connect')?.addEventListener('click', tryConnect);
document.getElementById("disconnect")?.addEventListener("click", async () => {
await board.disconnect().catch(console.error);
document.getElementById('log')?.innerText += 'Disconnected by user\n\n';
});
});
```
But wait still need to create the HTML file.
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Webserial</title>
<script src="./serialConnection.js" type="module"></script>
</head>
<body class="bg-neutral-950 text-white p-4 w-full">
<div class="webserial w-full max-w-xl mx-auto grid grid-cols-1 gap-y-4">
<div class="my-6"></div>
<button id="connect" class="hidden px-4 py-3 bg-gray-800 rounded-lg">Connect to serial</button>
<button id="disconnect" class="hidden px-4 py-3 bg-rose-800 rounded-lg">Disconnect device</button>
<div id="need-permission" class="hidden p-4 bg-rose-900 rounded-lg">
Woooah! It seems that you need to give permission to access the serial port.
Please, click the button 'Connect to serial' to try again.
</div>
<div id="disconnected" class="hidden p-4 bg-neutral-900 w-full">
The arduino is disconnected. Please, check the connection.
</div>
<div id="unsupported" class="hidden p-4 bg-orange-700 w-full absolute bottom-0 left-0">
This browser does not support the WebSerial API. Please, use a compatible browser.
</div>
<div id="log" class="bg-neutral-800 p-4 rounded-lg">
Log: <br>
</div>
</div>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio,container-queries"></script>
</body>
</html>
```