D
Deno

help

Use Deno.run() to communicate with Arduino

MMqx1/21/2023
Hey I'm trying to communicate via USB serial with my Arduino. My goal is to have the result I get via USB serial available as a variable in Deno. I tried to do the whole thing with Deno.run(). This is my Arduino main file:
#define POTENTIOMETER_PIN_A A0

void setup() {
pinMode(POTENTIOMETER_PIN_A, INPUT);
Serial.begin(115200);
}

void loop() {
int potentiometerA = analogRead(POTENTIOMETER_PIN_A);

String data = map(potentiometerA, 0, 1023, 0, 100) + String('\n');

Serial.write(data.c_str());

delay(100);
}
#define POTENTIOMETER_PIN_A A0

void setup() {
pinMode(POTENTIOMETER_PIN_A, INPUT);
Serial.begin(115200);
}

void loop() {
int potentiometerA = analogRead(POTENTIOMETER_PIN_A);

String data = map(potentiometerA, 0, 1023, 0, 100) + String('\n');

Serial.write(data.c_str());

delay(100);
}
With this simple PowerShell script I can read the data from USB serial:
$port= new-Object System.IO.Ports.SerialPort COM4,115200,None,one
$port.Open()
do {
$line = $port.ReadLine()
Write-Host $line
}
while ($port.IsOpen)
$port= new-Object System.IO.Ports.SerialPort COM4,115200,None,one
$port.Open()
do {
$line = $port.ReadLine()
Write-Host $line
}
while ($port.IsOpen)
How can I run the whole thing in Deno now and get the output? I have tried the following which obviously does not work:
Deno.run({
cmd: [
'powershell',
'$port= new-Object System.IO.Ports.SerialPort COM4,115200,None,one',
'$port.Open()',
'do { $line = $port.ReadLine(); Write-Host $line }',
'while ($port.IsOpen)'
]
})
Deno.run({
cmd: [
'powershell',
'$port= new-Object System.IO.Ports.SerialPort COM4,115200,None,one',
'$port.Open()',
'do { $line = $port.ReadLine(); Write-Host $line }',
'while ($port.IsOpen)'
]
})
Can anyone help me with this? Or is there perhaps a better solution for that? Thanks!
AAapoAlas1/21/2023
You'd have to write the script in a .ps file and run powershell with that script file as the command argument.
MMqx1/21/2023
Okay like this?: usbSerial.ps1
$port = new-Object System.IO.Ports.SerialPort COM4, 115200, None, 8, one

function open {
$port.Open()

while ($port.IsOpen) {
$line = $port.ReadLine()
Write-Host $line
}
}

function close {
$port.Close()

}

open
$port = new-Object System.IO.Ports.SerialPort COM4, 115200, None, 8, one

function open {
$port.Open()

while ($port.IsOpen) {
$line = $port.ReadLine()
Write-Host $line
}
}

function close {
$port.Close()

}

open
main.ts
const process = Deno.run({
cmd: [
'powershell', './usbSerial.ps1'
],
stdout: "piped"
})
const process = Deno.run({
cmd: [
'powershell', './usbSerial.ps1'
],
stdout: "piped"
})
But how do I get the output?
AAapoAlas1/21/2023
IIRC the process will contain a readable stream in it.
MMqx1/21/2023
Okay I get the readable stream using this line:
const outputStream = process.stdout.readable.getReader()
const outputStream = process.stdout.readable.getReader()
How can I get my value from that?
AAapoAlas1/21/2023
Lookup WebStreams API and specifically TextDecoderStream: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoderStream I presume your Write-Host $line there is writing essentially text values, then you can use TextDecoderStream to turn the process.stdout.readable output Uint8Array into readable strings in JS.
TextDecoderStream - Web APIs | MDN
The TextDecoderStream interface of the Encoding API converts a stream of text in a binary encoding, such as UTF-8 etc., to a stream of strings. It is the streaming equivalent of TextDecoder.
MMqx1/21/2023
Thanks I look into this! The only thing I get are Uint8Arrays like this: Uint8Array(3) [ 49, 48, 48 ]
AAapoAlas1/21/2023
Yes, the stdout is bytes (output of programs doesn't need to be textual), so you use the TextDecoderStream to turn it into text. Except if you want the plain values then you'll probably want to use the Uint8Arrays directly. In this case it might eg. be that the data being read from the serial port is those sequential numbers: 49, changed to 48, and then 48 again.
MMqx1/21/2023
No not really the only thing that I am sending from the Arduino is the plain number. It is the percentage of the potentiometer. And If I am running the ps file directly I get the plain value And I also changed Write-Host $line to Write-Output $line but that does change nothing...
AAapoAlas1/21/2023
What is the "plain value"?
MMqx1/21/2023
Serial.write(data.c_str());
Serial.write(data.c_str());
34
34
AAapoAlas1/21/2023
Does it happen to be 100? 🙂
new TextDecoder().decode(new Uint8Array([49, 48, 48])) "100"
MMqx1/21/2023
The Values do not change. Okay I get the value now but the value does not change. Only when I am restarting the script.
const process = Deno.run({
cmd: [
'powershell', './usbSerial.ps1'
],
stdout: "piped"
})

const outputStream = process.stdout.readable.getReader()

if (outputStream) {
const result = await outputStream.read()
const value = new TextDecoder().decode(result.value)
while (value) {
console.log(value)
}
}
const process = Deno.run({
cmd: [
'powershell', './usbSerial.ps1'
],
stdout: "piped"
})

const outputStream = process.stdout.readable.getReader()

if (outputStream) {
const result = await outputStream.read()
const value = new TextDecoder().decode(result.value)
while (value) {
console.log(value)
}
}
What I am doing wrong? Okay this does kind of work:
if (outputStream) {
while (true) {
const result = await outputStream.read()
const value = new TextDecoder().decode(result.value)
console.log(value)
}
}
if (outputStream) {
while (true) {
const result = await outputStream.read()
const value = new TextDecoder().decode(result.value)
console.log(value)
}
}
How can I make it so that it reads the steam continuous? This only reads on time and that stops.
outputStream.read().then(data => {
return new TextDecoder().decode(data.value)
}).then(value => {
console.log(value);
})
outputStream.read().then(data => {
return new TextDecoder().decode(data.value)
}).then(value => {
console.log(value);
})
AAapoAlas1/21/2023
So, async streams can be read in a number of ways. while (true) is one, you can also do:
for await (const result of stream) {
// ...
}
for await (const result of stream) {
// ...
}
And, as said earlier you can use TextDecoderStream to do something like this:
const { writable, readable } = new TextDecoderStream();

process.stdout.readable.pipeTo(writable);

for await (const decodedValue of readable) {
console.log(decodedValue);
}
const { writable, readable } = new TextDecoderStream();

process.stdout.readable.pipeTo(writable);

for await (const decodedValue of readable) {
console.log(decodedValue);
}
MMqx1/21/2023
Ahh okay Would it be more efficient if I send a command to the arduino to read the potentiometer and that returns this value? Instead of continuous sending the data from the arduino?
AAapoAlas1/21/2023
Hmm, well, I don't know too much about Arduino so I cannot know really. I don't expect the stream to be a terrible resource hog but I may well be wrong. Checking with eg. htop on Linux or Task Manager on Windows should give you good indication. I guess at its core it depends on your use-case.
MMqx1/21/2023
I want to build a MacroPad
AAapoAlas1/21/2023
Oh, and if your "send a command" means starting a process using Deno.run then I'm quite sure it's more efficient to keep the stream.
MMqx1/21/2023
Okay Only Problem is that Deno or lets say JS is to fast... So It prints empty lines
AAapoAlas1/21/2023
Starting a program tends to be much more performance intensive than keeping one running 🙂
MMqx1/21/2023
Okay
AAapoAlas1/21/2023
You could do if (decodedValue) { console.log(decodedValue) } to avoid printing empty lines.
MMqx1/21/2023
okay Hmm it still prints empty lines... That was the key:
if (decodedValue.trim() != '') {
console.log(decodedValue);
}
if (decodedValue.trim() != '') {
console.log(decodedValue);
}
Is it possible to call a function inside of the powershell script using Deno? That way I could create one function to open, sending/receiving and closing the connection
AAapoAlas1/21/2023
Well, the Deno.run does have a writable which you could use to send data to stdin for the powershell script, but I have no idea how you could use that in the script to get messages from Deno and send them onwards to Arduino.
MMqx1/21/2023
Hmm is it possible for the shell to listen for instructions?
MMqx1/21/2023
Stack Overflow
Calling a specific PowerShell function from the command line
I have a PowerShell script that contains several functions. How do I invoke a specific function from the command line? This doesn't work: powershell -File script.ps1 -Command My-Func
AAapoAlas1/21/2023
Sorry, I do not know.
MMqx1/21/2023
Hmm okay The reason why I want to do this is, because I have his global scope where I create the $port = new-Object System.IO.Ports.SerialPort COM4, 115200, None, 8, one object And I want to use the ps file like a wrapper so that I can send instructions only if needed.
AAapoAlas1/21/2023
Maybe search for something about reading stdin in powershell, and then decide based on stdin what your script will do. Then in your Deno code you can do writableStreamWriter.write("command")
MMqx1/21/2023
👍🏻 Found this:
function read-hostuntilflag([String] $flag) {
while (($line = read-host) -cne $flag) { $line }
}
function read-hostuntilflag([String] $flag) {
while (($line = read-host) -cne $flag) { $line }
}
But I am not sure if read-host = stdin How do I write to the writable?
writable.getWriter().write()
writable.getWriter().write()
and than?
process.stdin?.writable.getWriter().write(new TextEncoder().encode('getValues()'))
process.stdin?.writable.getWriter().write(new TextEncoder().encode('getValues()'))
This does not work how I would expect it... Okay I have done some tests... and the Arduino is so **** slow while responding... Basically I send an instruction and the Arduino responds like a second later...
AAapoAlas1/21/2023
Hmm, might be related to the whole deno -> stdin -> script -> arduino -> script -> stdout -> deno loop, or it might just be a slow Arduino update loop or something 🙂
MMqx1/21/2023
I think it is the Arduion The delay also exists if I run the ps script directly
AAapoAlas1/21/2023
That's good to hear (from a Deno standpoint, not good for you though 🙂 )
MMqx1/21/2023
yea I think I am just gonna write constantly from the Arduino. That is the easiest solution...
AAapoAlas1/21/2023
BTW, it might be possible to open the serial port directly from Deno. I'm not quite sure here but Deno.openSync("\\\.\\COM4", { read: true, write: true, createNew: true }) might work. Or something to that effect.
MMqx1/21/2023
Is this a new feature? Never seen it Ahh okay Yea I think I have tried that YES That also works nice! Only problem is that I get these weird line breaks
{
a:36,b:50}

{
a:36,b:50}

{
a:3
6
,b:50}
{
a:36,b:50}

{
a:36,b:50}

{
a:3
6
,b:50}
How it should look like:
{a:36,b:50}
{a:36,b:50}
AAapoAlas1/22/2023
Hmm, you might need to buffer the data when you read it. Right now you have just console.log(data), right? Instead you'd do something like this:
let result: string = "";
for await (const data of stream) {
const trimmedData = data.trim();
if (!trimmedData) { continue; }
result = `${result}${trimmedData}`;
if (result.startsWith("{") && result.endsWith("}")) {
console.log(result);
result = "";
}
}
let result: string = "";
for await (const data of stream) {
const trimmedData = data.trim();
if (!trimmedData) { continue; }
result = `${result}${trimmedData}`;
if (result.startsWith("{") && result.endsWith("}")) {
console.log(result);
result = "";
}
}
So here you put the messages from the stream into a single result string until you have a complete message, starting with { and ending with } (theoretically it's possible that this would give you multiple messages in a single string but it's probably unlikely). Then you log that message, or if want to eg. call some callback then you'll call that, and then reset the string to an empty string to start gathering up the next message all over again.
MMqx1/22/2023
👍🏻 thanks Okay that somehow does not work anymore :D but what I am currently trying is to async read/write inside of the powershell. Only problem is that Read-Host is a promt instruction and so the script waits at there.
AAapoAlas1/22/2023
Huuuhh... 😄 Always save your work in a functional state to make sure you don't forget how you got it to work in the first place 🙂
MMqx1/22/2023
yea :D
AAapoAlas1/22/2023
(I always make that mistake, always keep writing without making a commit and then I have no idea where the previous "good state" was 😄 )
MMqx1/22/2023
Yea but thats not the problem... I do like the method with the stream much more... instead of opening the port
AAapoAlas1/22/2023
You can create streams from the Deno.openFile though?
MMqx1/22/2023
yea but that does not work correctly. Sometimes it works, sometimes it does not Currently I stick to the powershell method
AAapoAlas1/22/2023
If I had to guess it'd be that sometimes the file already exists / is opened by a previous Deno still dying or by your PS script. It might help if you forcibly delete the file first.
MMqx1/22/2023
yea Okay have found a reliable method. I use this PS script:
param ($port, $baudrate)

$portObject = new-Object System.IO.Ports.SerialPort $port, $baudrate, None, 8, one

$portObject.Open()

while ($portObject.IsOpen) {
$instruction = Read-Host
$portObject.WriteLine($instruction)
Write-Output $portObject.ReadLine()
}
param ($port, $baudrate)

$portObject = new-Object System.IO.Ports.SerialPort $port, $baudrate, None, 8, one

$portObject.Open()

while ($portObject.IsOpen) {
$instruction = Read-Host
$portObject.WriteLine($instruction)
Write-Output $portObject.ReadLine()
}
That way I can send an instruction and receive it. I also found the issue why the arduino is so slow... I have used Serial.readString() an this is a really inefficient way to read in data Why are these not the same...?
void loop() {

if (Serial.available() > 0) {
char instruction = Serial.read();

switch (instruction) {
default:
Serial.write(instruction); <-- This works
break;

case 'a':
Serial.write("getValues()\0");
break;
}
}
}
void loop() {

if (Serial.available() > 0) {
char instruction = Serial.read();

switch (instruction) {
default:
Serial.write(instruction); <-- This works
break;

case 'a':
Serial.write("getValues()\0");
break;
}
}
}
void loop() {

if (Serial.available() > 0) {
char instruction = Serial.read();

switch (instruction) {
default:
Serial.write("instruction\0"); <-- This does not work?!
break;

case 'a':
Serial.write("getValues()\0");
break;
}
}
}
void loop() {

if (Serial.available() > 0) {
char instruction = Serial.read();

switch (instruction) {
default:
Serial.write("instruction\0"); <-- This does not work?!
break;

case 'a':
Serial.write("getValues()\0");
break;
}
}
}
It just stops there
AAapoAlas1/22/2023
Hmm, no idea. I've never done PS scripts.
MMqx1/22/2023
No its c++ Thats my Arduino file I want to send a instruction with deno and than the ino file returns the result the top code works fine
AAapoAlas1/22/2023
Oh. Hmm... Does the getValues side work? Does your top code ever run that path?
MMqx1/22/2023
The "getValues()" is just a sting that I print Its just a placeholder
AAapoAlas1/22/2023
Yes, I mean to ask if the Serial.write call works as you expect on that path.
MMqx1/22/2023
Yea I think so. If I send 'a' it responds with "getValues()" and on any other instruction it responds with the instruction I send but if I want to send a string and "not use" the instruction value, it stops And I dont want to send back the instruction every time... I dont like C++ xD

Looking for more? Join the community!

Recommended Posts
Best place to host server side Deno AppI have a server side app I want to host in a specific region and I only want 1 instance of it runninNPM Import missing typesPlaying around with some deno code and testing a npm import, the original package has types (redaxioUpdate value by reference without duplicating the dataIm exploring the world of FFI, porting some C applications I have to TypeScript, with the help of Dehow to hash very large filesWe have deprecated the old std/hash code in favor of the crypto module, but I do not know what the sxstate - no types importedimport * as xstate from "https://deno.land/x/xstate@xstate@4.35.2/src/index.ts"; file is cached, yetexample of kysely running on deno deploy?Deno Deploy awesomely can connect to postgres directly from the edge https://deno.com/blog/deploy-poUpdating digest with web cryptoFor some context, I'm updating some code that was importing from `std/hash` (specifically `std/hash/What would be the best third party module implementing `deflate()`?Currently using `npm:pako` but I'd like to avoid npm specifiers (modules in general).Pass string to struct FFIQuick question about Struct-Value feature, what would be the best way to pass `*const u8` (or stringdeno blog without html sanitizationI want to be able to javascript Canvas API things in my blog - surely this is possible?Killing subprocess not workingAnyone know what I'm doing wrong when trying to kill a process? I have a process start another procPossible to check of a file is being run as worker?```ts if (import.meta.main) { main() } ``` is there a way to disable calling `main()` if the file Deno linter is telling me that writeAll from streams conversion is deprecatedIs import { writeAll } from "https://deno.land/std@0.171.0/streams/conversion.ts" deprecated?How to get Deno's default compilerOptions value?I want to add more compilerOptions.lib options like this: ``` "compilerOptions": { "jsx": "readeno.run not working as expectedso when i run `mysql --defaults-extra-file=./tmpcnf_from_f_execute_query.cnf -e "DROP DATABASE test_