1+ /**
2+ * @fileoverview This file contains the SerialConnectionHandler class.
3+ * It handles the connection between the browser and the Arduino board via Web Serial.
4+ * @author Sebastian Romero
5+ */
6+
17const ArduinoUSBVendorId = 0x2341 ;
28const UserActionAbortError = 8 ;
39
410/**
511 * Handles the connection between the browser and the Arduino board via Web Serial.
12+ * Please note that for board with software serial over USB, the baud rate and other serial settings have no effect.
613 */
714class SerialConnectionHandler {
15+ /**
16+ * Represents a serial connection handler.
17+ * @constructor
18+ * @param {number } [baudRate=115200] - The baud rate of the serial connection.
19+ * @param {number } [dataBits=8] - The number of data bits.
20+ * @param {number } [stopBits=1] - The number of stop bits.
21+ * @param {string } [parity="none"] - The parity setting.
22+ * @param {string } [flowControl="none"] - The flow control setting.
23+ * @param {number } [bufferSize=2097152] - The size of the buffer in bytes. Max buffer size is 16MB
24+ * @param {number } [timeout=2000] - The connection timeout value in milliseconds.
25+ */
826 constructor ( baudRate = 115200 , dataBits = 8 , stopBits = 1 , parity = "none" , flowControl = "none" , bufferSize = 2 * 1024 * 1024 , timeout = 2000 ) {
927 this . baudRate = baudRate ;
1028 this . dataBits = dataBits ;
1129 this . stopBits = stopBits ;
1230 this . flowControl = flowControl ;
13- // Max buffer size is 16MB
1431 this . bufferSize = bufferSize ;
1532 this . parity = parity ;
1633 this . timeout = timeout ;
1734 this . currentPort = null ;
1835 this . currentReader = null ;
36+ this . currentTransformer = null ;
1937 this . readableStreamClosed = null ;
20- this . transformer = new BytesWaitTransformer ( ) ;
2138 this . registerEvents ( ) ;
2239 }
2340
@@ -38,14 +55,6 @@ class SerialConnectionHandler {
3855 }
3956 }
4057
41- /**
42- * Sets the transformer that is used to convert bytes into higher-level data types.
43- * @param {* } transformer
44- */
45- setTransformer ( transformer ) {
46- this . transformer = transformer ;
47- }
48-
4958 /**
5059 * Checks if the browser is connected to a serial port.
5160 * @returns {boolean } True if the browser is connected, false otherwise.
@@ -93,7 +102,7 @@ class SerialConnectionHandler {
93102 this . currentPort = null ;
94103 await this . currentReader ?. cancel ( ) ;
95104 await this . readableStreamClosed . catch ( ( ) => { } ) ; // Ignores the error
96- this . transformer . flush ( ) ;
105+ this . currentTransformer ? .flush ( ) ;
97106 await port . close ( ) ;
98107 console . log ( '🔌 Disconnected from serial port.' ) ;
99108 if ( this . onDisconnect ) this . onDisconnect ( ) ;
@@ -126,21 +135,31 @@ class SerialConnectionHandler {
126135 return false ;
127136 }
128137
138+
139+ /**
140+ * Reads a specified number of bytes from the serial connection.
141+ * @param {number } numBytes - The number of bytes to read.
142+ * @returns {Promise<Uint8Array> } - A promise that resolves to a Uint8Array containing the read bytes.
143+ */
144+ async readBytes ( numBytes ) {
145+ return await this . readData ( new BytesWaitTransformer ( numBytes ) ) ;
146+ }
147+
129148 /**
130149 * Reads the specified number of bytes from the serial port.
131- * @param {number } numBytes The number of bytes to read.
132- * @param {number } timeout The timeout in milliseconds.
150+ * @param {Transformer } transformer The transformer that is used to process the bytes.
133151 * If the timeout is reached, the reader will be canceled and the read lock will be released.
134152 */
135- async readBytes ( numBytes , timeout = null ) {
153+ async readData ( transformer ) {
154+ if ( ! transformer ) throw new Error ( 'Transformer is null' ) ;
136155 if ( ! this . currentPort ) return null ;
137156 if ( this . currentPort . readable . locked ) {
138157 console . log ( '🔒 Stream is already locked. Ignoring request...' ) ;
139158 return null ;
140159 }
141160
142- this . transformer . setBytesToWait ( numBytes ) ;
143- const transformStream = new TransformStream ( this . transformer ) ;
161+ const transformStream = new TransformStream ( transformer ) ;
162+ this . currentTransformer = transformer ;
144163 // pipeThrough() cannot be used because we need a promise that resolves when the stream is closed
145164 // to be able to close the port. pipeTo() returns such a promise.
146165 // SEE: https://stackoverflow.com/questions/71262432/how-can-i-close-a-web-serial-port-that-ive-piped-through-a-transformstream
@@ -150,12 +169,12 @@ class SerialConnectionHandler {
150169 let timeoutID = null ;
151170
152171 try {
153- if ( timeout ) {
172+ if ( this . timeout ) {
154173 timeoutID = setTimeout ( ( ) => {
155174 console . log ( '⌛️ Timeout occurred while reading.' ) ;
156175 if ( this . currentPort ?. readable ) reader ?. cancel ( ) ;
157176 this . transformer . flush ( ) ;
158- } , timeout ) ;
177+ } , this . timeout ) ;
159178 }
160179 const { value, done } = await reader . read ( ) ;
161180 if ( timeoutID ) clearTimeout ( timeoutID ) ;
@@ -173,9 +192,16 @@ class SerialConnectionHandler {
173192 await this . readableStreamClosed . catch ( ( ) => { } ) ; // Ignores the error
174193 reader ?. releaseLock ( ) ;
175194 this . currentReader = null ;
195+ this . currentTransformer = null ;
176196 }
177197 }
178198
199+ /**
200+ * Sends the provided byte array data through the current serial port.
201+ *
202+ * @param {ArrayBuffer } byteArray - The byte array data to send.
203+ * @returns {Promise<void> } - A promise that resolves when the data has been sent.
204+ */
179205 async sendData ( byteArray ) {
180206 if ( ! this . currentPort ?. writable ) {
181207 console . log ( '🚫 Port is not writable. Ignoring request...' ) ;
@@ -196,10 +222,19 @@ class SerialConnectionHandler {
196222 return this . sendData ( [ 1 ] ) ;
197223 }
198224
225+ /**
226+ * Requests the camera configuration from the board by writing a 2 to the serial port.
227+ * @returns {Promise } A promise that resolves with the configuration data.
228+ */
199229 async requestConfig ( ) {
200230 return this . sendData ( [ 2 ] ) ;
201231 }
202232
233+ /**
234+ * Requests the camera resolution from the board and reads it back from the serial port.
235+ * The configuration simply consists of two bytes: the mode and the resolution.
236+ * @returns {Promise<ArrayBuffer> } The raw configuration data as an ArrayBuffer.
237+ */
203238 async getConfig ( ) {
204239 if ( ! this . currentPort ) return ;
205240
@@ -211,15 +246,12 @@ class SerialConnectionHandler {
211246 /**
212247 * Requests a frame from the Arduino board and reads the specified number of bytes from the serial port afterwards.
213248 * Times out after the timeout in milliseconds specified in the constructor.
214- * @param {number } totalBytes The number of bytes to read.
249+ * @param {Transformer } transformer The transformer that is used to process the bytes.
215250 */
216- async getFrame ( totalBytes ) {
251+ async getFrame ( transformer ) {
217252 if ( ! this . currentPort ) return ;
218-
219253 await this . requestFrame ( ) ;
220- // console.log(`Trying to read ${totalBytes} bytes...`);
221- // Read the given amount of bytes
222- return await this . readBytes ( totalBytes , this . timeout ) ;
254+ return await this . readData ( transformer , this . timeout ) ;
223255 }
224256
225257 /**
0 commit comments