-
Notifications
You must be signed in to change notification settings - Fork 96
/
Copy pathserial_web_serial.js
203 lines (190 loc) · 7.49 KB
/
serial_web_serial.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
(function() {
// TODO: Pass USB vendor and product ID filter when supported by Chrome.
// - maybe not? We might want to connect to a non-official Espruino board
// TODO: Use device name when/if supported
function getStatus(ignoreSettings) {
if (typeof navigator == "undefined") {
return {warning:"Not running in a browser"};
}
if (getPortsErrorMessage!==undefined) {
return {error:getPortsErrorMessage};
}
if (!navigator.serial) {
if (Espruino.Core.Utils.isChrome())
return {error:`Chrome currently requires <code>chrome://flags/#enable-experimental-web-platform-features</code> to be enabled.`};
else if (Espruino.Core.Utils.isFirefox())
return {error:`Firefox doesn't support Web Serial - try using Chrome`};
else
return {error:"No navigator.serial. Do you have a supported browser?"};
}
if (window && window.location && window.location.protocol=="http:" &&
window.location.hostname!="localhost") {
return {error:"Serving off HTTP (not HTTPS)"};
}
if (!ignoreSettings && !Espruino.Config.WEB_SERIAL)
return {warning:`"Web Serial" disabled in settings`};
return true;
}
var OK = true;
var getPortsErrorMessage = undefined;
var testedCompatibility = false;
/// List of previously paired devices that we could reconnect to without the chooser
var pairedDevices = [];
var serialPort;
var serialPortReader;
function init() {
Espruino.Core.Config.add("WEB_SERIAL", {
section : "Communications",
name : "Connect over Serial (Web Serial)",
descriptionHTML : 'Allow connection to Espruino from the Web Browser via Serial. The API must currently be enabled by pasting <code>chrome://flags#enable-experimental-web-platform-features</code> into the address bar and clicking <code>Enable</code>',
type : "boolean",
defaultValue : true,
});
// If we're ok and have the getDevices extension, use it to remember previously paired devices
if (getStatus(true)===true && navigator.serial.getPorts) {
console.log("Serial> serial.getPorts exists - grab known devices");
navigator.serial.getPorts().then(devices=>{
pairedDevices = devices;
}, err=>{
getPortsErrorMessage = err.toString();
console.log("Serial> "+err.toString());
});
}
}
function getSerialDeviceInfo(device) {
var idx = pairedDevices.indexOf(device);
var deviceInfo = { path : "webserial:"+(idx>=0?idx:""), type : "usb", description : "Previously connected device"};
if (device.getInfo) {
var info = device.getInfo();
if (info.usbVendorId && info.usbProductId) {
deviceInfo.path = "webserial:"+(63356+info.usbVendorId).toString(16).substr(-4)+":"+(63356+info.usbProductId).toString(16).substr(-4);
}
}
return deviceInfo;
}
function getPorts(callback) {
if (!testedCompatibility) {
testedCompatibility = true;
if (getStatus(true)!==true)
OK = false;
}
if (Espruino.Config.WEB_SERIAL && OK) {
var list = [{path:'Web Serial', description:'Serial', type : "serial", promptsUser:true}];
pairedDevices.forEach(function(dev) {
list.push(getSerialDeviceInfo(dev));
});
callback(list, true/*instantPorts*/);
} else
callback(undefined, true/*instantPorts*/);
}
function openSerial(path, openCallback, receiveCallback, disconnectCallback) {
var promise;
// Check for pre-paired devices
serialPort = pairedDevices.find(dev=>getSerialDeviceInfo(dev).path == path);
if (serialPort) {
console.log("Serial> Pre-paired Web Serial device already found");
promise = Promise.resolve(serialPort);
} else {
console.log("Serial> Starting device chooser");
promise = navigator.serial.requestPort({});
}
promise.then(function(port) {
Espruino.Core.Status.setStatus("Connecting to serial port");
serialPort = port;
var br = parseInt(Espruino.Config.BAUD_RATE);
return port.open({
baudrate: br/*old*/,
baudRate: br/*new*/,
dataBits: 8, databits: 8,
stopBits: 1, stopbits: 8,
parity: "none",
flowControl: "none",
rtscts: false });
}).then(function () {
var getReaderSuccess = false;
function readLoop() {
serialPortReader = serialPort.readable.getReader();
serialPortReader.read().then(function ({ value, done }) {
getReaderSuccess = true;
serialPortReader.releaseLock();
serialPortReader = undefined;
if (value) {
receiveCallback(value.buffer);
}
if (done) {
console.log("Serial> serialPortReader done");
if (serialPort) {
console.log("Serial> serialPort.close()");
serialPort.close();
serialPort = undefined;
if (disconnectCallback) disconnectCallback();
disconnectCallback = undefined;
}
} else {
readLoop();
}
}).catch(function(e) {
if (getReaderSuccess == false && e == "BreakError: Break received") {
// This fixes a longstanding issue (since 2017) that affected ESP32 devices.
// Espruino Web IDE, sometimes did not connect to an ESP32 device, especially the first time you tried.
// The workaround was to use another tool to connect to the ESP32, like minicom or cutecom
// and once connected using one of these tools, you tried again using Espruino Web IDE.
console.log("Condition break received and ignored");
console.log("Retrying the read loop...");
getReaderSuccess = true;
readLoop();
} else {
serialPortReader.releaseLock();
console.log("Serial> serialPortReader rejected", e);
}
});
}
serialPort.addEventListener("disconnect", (event) => {
console.log("Serial> Port disconnected", event);
if (serialPort) {
serialPort.close();
serialPort = undefined;
}
if (disconnectCallback) disconnectCallback();
disconnectCallback = undefined;
});
readLoop();
Espruino.Core.Status.setStatus("Serial connected. Receiving data...");
if (!pairedDevices.includes(serialPort))
pairedDevices.push(serialPort);
openCallback({ portName : getSerialDeviceInfo(serialPort).path });
}).catch(function(error) {
console.log('Serial> ERROR: ' + error);
pairedDevices = pairedDevices.filter(dev=>getSerialDeviceInfo(dev).path != path); // error connecting, remove from paired devices
if (disconnectCallback) disconnectCallback();
disconnectCallback = undefined;
});
}
function closeSerial() {
if (serialPortReader)
serialPortReader.cancel();
/* serialPortReader will handle tidying up
and calling disconnect */
}
function writeSerial(data, callback) {
var writer = serialPort.writable.getWriter();
writer.write(Espruino.Core.Utils.stringToArrayBuffer(data)).then(function() {
writer.releaseLock();
callback();
}).catch(function(error) {
writer.releaseLock();
console.log('Serial> SEND ERROR: ' + error);
closeSerial();
});
}
// ----------------------------------------------------------
Espruino.Core.Serial.devices.push({
"name" : "Web Serial",
"init" : init,
"getStatus": getStatus,
"getPorts": getPorts,
"open": openSerial,
"write": writeSerial,
"close": closeSerial,
});
})();