@@ -6,8 +6,11 @@ const saveImageButton = document.getElementById('save-image');
6
6
const canvas = document . getElementById ( 'bitmapCanvas' ) ;
7
7
const ctx = canvas . getContext ( '2d' ) ;
8
8
9
- const UserActionAbortError = 8 ;
10
- const ArduinoUSBVendorId = 0x2341 ;
9
+ // TODO check for signals
10
+ // TODO implement transformer
11
+ // TODO get image format from device
12
+ // SEE: https://developer.chrome.com/articles/serial/#transforming-streams
13
+ // SEE: https://developer.chrome.com/articles/serial/#signals
11
14
12
15
config = {
13
16
"RGB565" : {
@@ -34,114 +37,8 @@ const baudRate = 115200; // Adjust this value based on your device's baud rate
34
37
const dataBits = 8 ; // Adjust this value based on your device's data bits
35
38
const stopBits = 2 ; // Adjust this value based on your device's stop bits
36
39
37
- let currentPort , currentReader ;
38
40
const imageDataProcessor = new ImageDataProcessor ( ctx , mode , imageWidth , imageHeight ) ;
39
-
40
- async function requestSerialPort ( ) {
41
- try {
42
- // Request a serial port
43
- const port = await navigator . serial . requestPort ( { filters : [ { usbVendorId : ArduinoUSBVendorId } ] } ) ;
44
- currentPort = port ;
45
- return port ;
46
- } catch ( error ) {
47
- if ( error . code != UserActionAbortError ) {
48
- console . log ( error ) ;
49
- }
50
- return null ;
51
- }
52
- }
53
-
54
- async function autoConnect ( ) {
55
- if ( currentPort ) {
56
- console . log ( '🔌 Already connected to a serial port.' ) ;
57
- return false ;
58
- }
59
-
60
- // Get all serial ports the user has previously granted the website access to.
61
- const ports = await navigator . serial . getPorts ( ) ;
62
-
63
- for ( const port of ports ) {
64
- console . log ( '👀 Serial port found with VID: 0x' + port . getInfo ( ) . usbVendorId . toString ( 16 ) ) ;
65
- if ( port . getInfo ( ) . usbVendorId === ArduinoUSBVendorId ) {
66
- currentPort = port ;
67
- return await connectSerial ( currentPort ) ;
68
- }
69
- }
70
- return false ;
71
- }
72
-
73
- async function connectSerial ( port , baudRate = 115200 , dataBits = 8 , stopBits = 2 , bufferSize = 4096 ) {
74
- try {
75
- // If the port is already open, close it
76
- if ( port . readable ) await port . close ( ) ;
77
- await port . open ( { baudRate : baudRate , parity : "even" , dataBits : dataBits , stopBits : stopBits , bufferSize : bufferSize } ) ;
78
- console . log ( '✅ Connected to serial port.' ) ;
79
- return true ;
80
- } catch ( error ) {
81
- return false ;
82
- }
83
- }
84
-
85
- async function readBytes ( port , numBytes , timeout = null ) {
86
- if ( port . readable . locked ) {
87
- console . log ( '🔒 Stream is already locked. Ignoring request...' ) ;
88
- return null ;
89
- }
90
-
91
- const bytesRead = new Uint8Array ( numBytes ) ;
92
- let bytesReadIdx = 0 ;
93
- let keepReading = true ;
94
-
95
- // As long as the errors are non-fatal, a new ReadableStream is created automatically and hence port.readable is non-null.
96
- // If a fatal error occurs, such as the serial device being removed, then port.readable becomes null.
97
-
98
- while ( port . readable && keepReading ) {
99
- const reader = port . readable . getReader ( ) ;
100
- currentReader = reader ;
101
- let timeoutID = null ;
102
- // let count = 0;
103
-
104
- try {
105
- while ( bytesReadIdx < numBytes ) {
106
- if ( timeout ) {
107
- timeoutID = setTimeout ( ( ) => {
108
- console . log ( '⌛️ Timeout occurred while reading.' ) ;
109
- if ( port . readable ) reader ?. cancel ( ) ;
110
- } , timeout ) ;
111
- }
112
-
113
- const { value, done } = await reader . read ( ) ;
114
- if ( timeoutID ) clearTimeout ( timeoutID ) ;
115
-
116
- if ( value ) {
117
- for ( const byte of value ) {
118
- bytesRead [ bytesReadIdx ++ ] = byte ;
119
- if ( bytesReadIdx >= numBytes ) break ;
120
- }
121
- // count += value.byteLength;
122
- // console.log(`Read ${value.byteLength} (Total: ${count}) out of ${numBytes} bytes. }`);
123
- }
124
-
125
- if ( done ) {
126
- // |reader| has been canceled.
127
- console . log ( '🚫 Reader has been canceled' ) ;
128
- break ;
129
- }
130
- }
131
-
132
- } catch ( error ) {
133
- // Handle |error|...
134
- console . log ( '💣 Error occurred while reading: ' ) ;
135
- console . log ( error ) ;
136
- } finally {
137
- keepReading = false ;
138
- // console.log('🔓 Releasing reader lock...');
139
- reader ?. releaseLock ( ) ;
140
- currentReader = null ;
141
- }
142
- }
143
- return bytesRead ;
144
- }
41
+ const connectionHandler = new SerialConnectionHandler ( baudRate , dataBits , stopBits , "even" , "hardware" , bufferSize ) ;
145
42
146
43
function renderBitmap ( bytes , width , height ) {
147
44
canvas . width = width ;
@@ -151,65 +48,31 @@ function renderBitmap(bytes, width, height) {
151
48
ctx . putImageData ( imageData , 0 , 0 ) ;
152
49
}
153
50
154
- async function requestFrame ( port ) {
155
- if ( ! port ?. writable ) {
156
- console . log ( '🚫 Port is not writable. Ignoring request...' ) ;
157
- return ;
158
- }
159
- // console.log('Writing 1 to the serial port...');
160
- // Write a 1 to the serial port
161
- const writer = port . writable . getWriter ( ) ;
162
- await writer . write ( new Uint8Array ( [ 1 ] ) ) ;
163
- await writer . close ( ) ;
164
- }
165
-
166
51
async function renderStream ( ) {
167
- while ( true && currentPort ) {
168
- await renderFrame ( currentPort ) ;
52
+ while ( connectionHandler . isConnected ( ) ) {
53
+ await renderFrame ( ) ;
169
54
}
170
55
}
171
56
172
- async function renderFrame ( port ) {
173
- if ( ! port ) return ;
174
- const bytes = await getFrame ( port ) ;
175
- if ( ! bytes ) return false ; // Nothing to render
57
+ async function renderFrame ( ) {
58
+ if ( ! connectionHandler . isConnected ( ) ) return ;
59
+ const bytes = await connectionHandler . getFrame ( totalBytes ) ;
60
+ if ( ! bytes || bytes . length == 0 ) return false ; // Nothing to render
176
61
// console.log(`Reading done ✅. Rendering image...`);
177
- // Render the bytes as a grayscale bitmap
178
62
renderBitmap ( bytes , imageWidth , imageHeight ) ;
179
63
return true ;
180
64
}
181
65
182
- async function getFrame ( port ) {
183
- if ( ! port ) return ;
184
-
185
- await requestFrame ( port ) ;
186
- // console.log(`Trying to read ${totalBytes} bytes...`);
187
- // Read the given amount of bytes
188
- return await readBytes ( port , totalBytes , 2000 ) ;
189
- }
190
-
191
- async function disconnectSerial ( port ) {
192
- if ( ! port ) return ;
193
- try {
194
- currentPort = null ;
195
- await currentReader ?. cancel ( ) ;
196
- await port . close ( ) ;
197
- console . log ( '🔌 Disconnected from serial port.' ) ;
198
- } catch ( error ) {
199
- console . error ( '💣 Error occurred while disconnecting: ' + error . message ) ;
200
- } ;
201
- }
202
-
203
66
startButton . addEventListener ( 'click' , renderStream ) ;
204
67
connectButton . addEventListener ( 'click' , async ( ) => {
205
- currentPort = await requestSerialPort ( ) ;
206
- if ( await connectSerial ( currentPort , baudRate , dataBits , stopBits , bufferSize , flowControl ) ) {
68
+ await connectionHandler . requestSerialPort ( ) ;
69
+ if ( await connectionHandler . connectSerial ( ) ) {
207
70
renderStream ( ) ;
208
71
}
209
72
} ) ;
210
- disconnectButton . addEventListener ( 'click' , ( ) => disconnectSerial ( currentPort ) ) ;
73
+ disconnectButton . addEventListener ( 'click' , ( ) => connectionHandler . disconnectSerial ( ) ) ;
211
74
refreshButton . addEventListener ( 'click' , ( ) => {
212
- renderFrame ( currentPort ) ;
75
+ renderFrame ( ) ;
213
76
} ) ;
214
77
215
78
saveImageButton . addEventListener ( 'click' , ( ) => {
@@ -223,7 +86,7 @@ saveImageButton.addEventListener('click', () => {
223
86
navigator . serial . addEventListener ( "connect" , ( e ) => {
224
87
// Connect to `e.target` or add it to a list of available ports.
225
88
console . log ( '🔌 Serial port became available. VID: 0x' + e . target . getInfo ( ) . usbVendorId . toString ( 16 ) ) ;
226
- autoConnect ( ) . then ( ( connected ) => {
89
+ connectionHandler . autoConnect ( ) . then ( ( connected ) => {
227
90
if ( connected ) {
228
91
renderStream ( ) ;
229
92
} ;
@@ -240,7 +103,7 @@ navigator.serial.addEventListener("disconnect", (e) => {
240
103
window . addEventListener ( 'load' , async ( ) => {
241
104
console . log ( '🚀 Page loaded. Trying to connect to serial port...' ) ;
242
105
setTimeout ( ( ) => {
243
- autoConnect ( ) . then ( ( connected ) => {
106
+ connectionHandler . autoConnect ( ) . then ( ( connected ) => {
244
107
if ( connected ) {
245
108
renderStream ( ) ;
246
109
} ;
0 commit comments