Use Deno.run() to communicate with Arduino
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:
With this simple PowerShell script I can read the data from USB serial:
How can I run the whole thing in Deno now and get the output?
I have tried the following which obviously does not work:
Can anyone help me with this? Or is there perhaps a better solution for that?
Thanks!51 Replies
You'd have to write the script in a .ps file and run powershell with that script file as the command argument.
Okay like this?:
usbSerial.ps1
main.ts
But how do I get the output?
IIRC the
process
will contain a readable stream in it.Okay I get the readable stream using this line:
How can I get my value from that?
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.
Thanks I look into this!
The only thing I get are
Uint8Arrays
like this: Uint8Array(3) [ 49, 48, 48 ]
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.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...What is the "plain value"?
Does it happen to be 100? 🙂
new TextDecoder().decode(new Uint8Array([49, 48, 48])) "100"
The Values do not change.
Okay I get the value now but the value does not change. Only when I am restarting the script.
What I am doing wrong?
Okay this does kind of work:
How can I make it so that it reads the steam continuous?
This only reads on time and that stops.
So, async streams can be read in a number of ways.
while (true)
is one, you can also do:
And, as said earlier you can use TextDecoderStream
to do something like this:
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?
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.I want to build a MacroPad
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.Okay
Only Problem is that Deno or lets say JS is to fast...
So It prints empty lines
Starting a program tends to be much more performance intensive than keeping one running 🙂
Okay
You could do
if (decodedValue) { console.log(decodedValue) }
to avoid printing empty lines.okay
Hmm it still prints empty lines...
That was the key:
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
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.Hmm is it possible for the shell to listen for instructions?
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
Sorry, I do not know.
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.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")
👍🏻
Found this:
But I am not sure if
read-host = stdin
How do I write to the writable
?
and than?
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...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 🙂
I think it is the Arduion
The delay also exists if I run the ps script directly
That's good to hear (from a Deno standpoint, not good for you though 🙂 )
yea I think I am just gonna write constantly from the Arduino. That is the easiest solution...
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.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
How it should look like:
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:
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.👍🏻 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.
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 🙂
yea :D
(I always make that mistake, always keep writing without making a commit and then I have no idea where the previous "good state" was 😄 )
Yea but thats not the problem... I do like the method with the stream much more... instead of opening the port
You can create streams from the
Deno.openFile
though?yea
but that does not work correctly. Sometimes it works, sometimes it does not
Currently I stick to the powershell method
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.
yea
Okay have found a reliable method. I use this PS script:
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...?
It just stops thereHmm, no idea. I've never done PS scripts.
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
Oh. Hmm... Does the getValues side work? Does your top code ever run that path?
The "getValues()" is just a sting that I print
Its just a placeholder
Yes, I mean to ask if the
Serial.write
call works as you expect on that path.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