Obousměrná komunikace pomocí Serial API

Jak jsem přislíbil, tak pokračuji se Serial API, tentokráte využití v obousměrné komunikaci. Po připojení Arduino posílá co deset sekund hlášení o běhu (asynchronně) a při odeslání textového řetězce ho pošle zpět na sériový port.

Důležité upozornění: Soubor HTML je nutno otevřít buď lokálně nebo ze zabezpečeného HTTPS, jinak je Serial API prohlížečem blokováno!

https://www.ctvrtky.info/wp-content/uploads/2025/06/Serial-API-bidirectional.html

Sketch do Arduina

unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
const long interval = 10000;
String instr;

void setup() {
  instr.reserve(800);
  Serial.begin(115200);
}

void loop() {
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    Serial.print("{\"time\":");
    Serial.print(millis());
    Serial.println("}");
  }

  if (Serial.available())  {
    instr = Serial.readStringUntil(char(10));
    if (instr != "") {
      Serial.print("{\"out\":\"");
      Serial.print(instr);
      Serial.println("\"}");
    }
  }
}

HTML + JS

<!DOCTYPE html>
<html>
	<head>
		<title>Serial API - bidirectional</title>
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
		<meta charset="UTF-8">
	</head>
	
	<style>
	body {
	    font-family: Arial, Helvetica, sans-serif;
	    font-size: 0.8em;
    }
    
	#p_out {
		height: 50vh;
		overflow: auto;
		border: 1px solid;
	}

	.in {
		color: green;
	}

	.out {
		color: blue;
	}
	.success {
		color: orange;
	}

	div>span {
		color: black;
	}
	</style>

	<body>
	    <h1>Serial API - bidirectional communication</h1>
        <select id="sel_baudrate">
            <option value="9600">9600 bauds</option>
            <option value="115200" selected="selected">115200 bauds</option>
        </select>
		<button id="btn_open">Open port</button><br><br>
		<button id="btn_send">Send to port</button> <input type="text" id="text_send" maxlength="19"><br><br>
		<p id="p_out"></p>
		<button id="btn_clear">Clear serial monitor</button>
		<script>
		select_baudrate = document.getElementById('sel_baudrate');
		button_open = document.getElementById('btn_open');
		button_send = document.getElementById('btn_send');
		text_output = document.getElementById('p_out');
		text_output.innerHTML = "";
		text_send = document.getElementById('text_send');
		button_clear = document.getElementById('btn_clear');
		
		let port;
		let reader;
		let inputDone;
		let outputDone;
		let inputStream;
		let outputStream;
		
		if (!navigator.serial){
		        alert("Your browser does not support Serial API or page is opened from insecured HTTP!")
		};
		    
		button_open.addEventListener('click', async () => {
			try {
				port = await navigator.serial.requestPort();
				const baudRate = select_baudrate.value;
				await port.open({
					baudRate: baudRate
				});
				var d = new Date();
				text_output.innerHTML = "<div class='success'><span>" + d.toLocaleTimeString() + "</span><b> Successfully connected!</b>";
				let decoder = new TextDecoderStream();
				inputDone = port.readable.pipeTo(decoder.writable);
				inputStream = decoder.readable;
				reader = inputStream.getReader();
				readLoop(text_output);
				const encoder = new TextEncoderStream();
				outputDone = encoder.readable.pipeTo(port.writable);
				outputStream = encoder.writable;
			} catch (error) {
				alert("Error during port opening!");
			}
		});
		
		button_send.addEventListener('click', async () => {
			writeToStream(text_send.value);
			text_send.value = "";
			text_send.focus();
		});
		
		text_send.addEventListener('keyup', function(event) {
			if (event.key === "Enter") {
				writeToStream(text_send.value);
				text_send.value = "";
				text_send.focus();
			}
		});
		
		button_clear.addEventListener('click', async () => {
			text_output.innerHTML = "";
		});
		
		async function readLoop(el_output) {
			while (true) {
				const {
					value,
					done
				} = await reader.read();
				if (value) {
					try {
						var o = JSON.parse(value);
						if (o && typeof o === "object") {
							var d = new Date();
							if (o.time) {
								el_output.innerHTML += "<div class='in'><span>" + d.toLocaleTimeString() + "</span>  Program is running <b>" + o.time / 1000 + ' seconds</b></div>';
							}
							if (o.out) {
								el_output.innerHTML += "<div class='out'><span>" + d.toLocaleTimeString() + "</span> Answer from arduino <b>" + o.out + "</b></div>";
							}
							el_output.scrollTop = el_output.scrollHeight;
						}
					} catch (e) {
						console.log(value);
					}
				}
				if (done) {
					reader.releaseLock();
					break;
				}
			}
		}
		
		async function writeToStream(line) {
			const writer = outputStream.getWriter();
			writer.write(line);
			writer.write('\n');
			writer.releaseLock();
		}
		</script>
	</body>

</html>

Leave a Reply