1
1
/**
2
2
* @fileoverview This file contains the main application logic.
3
+ *
4
+ * The application uses the Web Serial API to connect to the serial port.
5
+ * Check the following links for more information on the Web Serial API:
6
+ * https://developer.chrome.com/articles/serial/
7
+ * https://wicg.github.io/serial/
8
+ *
9
+ * The flow of the application is as follows:
10
+ * 1. The user clicks the "Connect" button or the browser automatically connects
11
+ * to the serial port if it has been previously connected.
12
+ * 2. The application requests the camera configuration (mode and resolution) from the board.
13
+ * 3. The application starts reading the image data stream from the serial port.
14
+ * It waits until the calculated number of bytes have been read and then processes the data.
15
+ * 4. The processed image data is rendered on the canvas.
16
+ *
3
17
* @author Sebastian Romero
4
18
*/
5
19
@@ -10,14 +24,11 @@ const saveImageButton = document.getElementById('save-image');
10
24
const canvas = document . getElementById ( 'bitmapCanvas' ) ;
11
25
const ctx = canvas . getContext ( '2d' ) ;
12
26
13
- // Check the following links for more information on the Web Serial API:
14
- // https://developer.chrome.com/articles/serial/
15
- // https://wicg.github.io/serial/
27
+ const imageDataTransfomer = new ImageDataTransformer ( ctx ) ;
28
+ const connectionHandler = new SerialConnectionHandler ( ) ;
16
29
17
30
18
- const imageDataProcessor = new ImageDataProcessor ( ) ;
19
- let imageDataTransfomer = new ImageDataTransformer ( ) ;
20
- const connectionHandler = new SerialConnectionHandler ( ) ;
31
+ // Connection handler event listeners
21
32
22
33
connectionHandler . onConnect = async ( ) => {
23
34
connectButton . textContent = 'Disconnect' ;
@@ -32,46 +43,55 @@ connectionHandler.onConnect = async () => {
32
43
console . error ( `🚫 Invalid camera configuration: ${ cameraConfig [ 0 ] } , ${ cameraConfig [ 1 ] } . Aborting...` ) ;
33
44
return ;
34
45
}
35
- imageDataProcessor . setMode ( imageMode ) ;
36
- imageDataProcessor . setResolution ( imageResolution . width , imageResolution . height ) ;
37
46
imageDataTransfomer . setImageMode ( imageMode ) ;
38
47
imageDataTransfomer . setResolution ( imageResolution . width , imageResolution . height ) ;
39
- connectionHandler . setTransformer ( imageDataTransfomer ) ;
40
48
renderStream ( ) ;
41
49
} ;
42
50
43
51
connectionHandler . onDisconnect = ( ) => {
44
52
connectButton . textContent = 'Connect' ;
45
- imageDataProcessor . reset ( ) ;
53
+ imageDataTransfomer . reset ( ) ;
46
54
} ;
47
55
48
- function renderBitmap ( width , height , imageData ) {
49
- canvas . width = width ;
50
- canvas . height = height ;
51
- ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
52
- ctx . putImageData ( imageData , 0 , 0 ) ;
53
- }
56
+
57
+ // Rendering logic
54
58
55
59
async function renderStream ( ) {
56
60
while ( connectionHandler . isConnected ( ) ) {
57
- if ( imageDataProcessor . isConfigured ( ) ) await renderFrame ( ) ;
61
+ if ( imageDataTransfomer . isConfigured ( ) ) await renderFrame ( ) ;
58
62
}
59
63
}
60
64
65
+ /**
66
+ * Renders the image data for one frame from the board and renders it.
67
+ * @returns {Promise<boolean> } True if a frame was rendered, false otherwise.
68
+ */
61
69
async function renderFrame ( ) {
62
70
if ( ! connectionHandler . isConnected ( ) ) return ;
63
- const bytes = await connectionHandler . getFrame ( imageDataProcessor . getTotalBytes ( ) ) ;
64
- if ( ! bytes || bytes . length == 0 ) return false ; // Nothing to render
65
- // console.log(`Reading done ✅. Rendering image...`);
66
- const imageData = ctx . createImageData ( 320 , 240 ) ;
67
- const data = imageDataProcessor . getImageData ( bytes ) ;
68
- imageData . data . set ( data ) ;
69
-
70
- renderBitmap ( imageDataProcessor . width , imageDataProcessor . height , imageData ) ;
71
+ const imageData = await connectionHandler . getFrame ( imageDataTransfomer ) ;
72
+ if ( ! imageData ) return false ; // Nothing to render
73
+ if ( ! ( imageData instanceof ImageData ) ) throw new Error ( '🚫 Image data is not of type ImageData' ) ;
74
+ renderBitmap ( ctx , imageData ) ;
71
75
return true ;
72
76
}
73
77
78
+ /**
79
+ * Renders the image data on the canvas.
80
+ * @param {CanvasRenderingContext2D } context The canvas context to render on.
81
+ * @param {ImageData } imageData The image data to render.
82
+ */
83
+ function renderBitmap ( context , imageData ) {
84
+ context . canvas . width = imageData . width ;
85
+ context . canvas . height = imageData . height ;
86
+ context . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
87
+ context . putImageData ( imageData , 0 , 0 ) ;
88
+ }
89
+
90
+
91
+ // UI Event listeners
92
+
74
93
startButton . addEventListener ( 'click' , renderStream ) ;
94
+
75
95
connectButton . addEventListener ( 'click' , async ( ) => {
76
96
if ( connectionHandler . isConnected ( ) ) {
77
97
connectionHandler . disconnectSerial ( ) ;
@@ -80,8 +100,9 @@ connectButton.addEventListener('click', async () => {
80
100
await connectionHandler . connectSerial ( ) ;
81
101
}
82
102
} ) ;
103
+
83
104
refreshButton . addEventListener ( 'click' , ( ) => {
84
- if ( imageDataProcessor . isConfigured ( ) ) renderFrame ( ) ;
105
+ if ( imageDataTransfomer . isConfigured ( ) ) renderFrame ( ) ;
85
106
} ) ;
86
107
87
108
saveImageButton . addEventListener ( 'click' , ( ) => {
0 commit comments