-
Thanks fo the feedback!
Object.keys(global)
Yes!
for (a in global) { console.log(a, "is a", typeof eval(a)) }
. I will have a think if it makes sense to use this instead.Good point about eval(undefined). Does this actually fail, or is it that accessing a variable that doesn't exist is causing a reference error?
I meant
UART.eval('functionwithnoreturnstatement()', cb)
, will try to evaluate the result of the function call on the device, getundefined
back, then try to interpret is as a JSON (https://github.com/espruino/EspruinoWebTools/blob/13032d0e862d9976ed5eed33b42f07e93e6c25c3/uart.js#L492). It will fail, be send to the catch, and call the callback withnull
(https://github.com/espruino/EspruinoWebTools/blob/13032d0e862d9976ed5eed33b42f07e93e6c25c3/uart.js#L496). So we cannot make the difference between a successful call to a real function and a call to something that does not exist or failed to execute properly.In other words, in the example above,
await proxymiseRobot.toggle()
will returnnull
, instead ofundefined
.This try/catch mechanism is great when we got an error and should be kept, but would be nice to have a third case for
undefined
like:if (d.trim() == "undefined") { cb(undefined) } else { // do what is already being done }
but you might be able to make
get
return a function, but with avalueOf
methodDo you mean something like this?
var a = function() { return 12;} a.prototype.valueOf = function() { return 42;}; console.log(a) // would like 42, got function() { return 12;} console.log(a()) // would like 12, got 12
Would be perfect indeed, but does not seem to work for functions.
-
I might have found a way around my problem with an additional call eval('typeof prop') to check if the property exist and is a function or not.
Ok that approach work. It is a tricky code with a few promise and using https://github.com/kozhevnikov/proxymise on top.
Code is very rough right now, just working my way around things. But, if curious, for now code is here: https://editor.p5js.org/jgrizou/sketches/Si31amD_p (see robot.js and sketch.js) and testable at https://editor.p5js.org/jgrizou/present/Si31amD_p
For example, if you have a espruino device with a code like:
var speed = 0.5; var on; function toggle() { on = !on; digitalWrite(LED1, on); } function add(x, y) { return x+y; }
You can load this page https://editor.p5js.org/jgrizou/present/Si31amD_p, click connect button and connect to the device.
Then in the webbrowser console the following commands should work:
await proxymiseRobot.speed // 0.5 await proxymiseRobot.toggle() // light should toggle await proxymiseRobot.add(2,2) // 4
A lot of cleaning, refactoring, bulletproofing is required, but the conceptual basis are there and it seems to work so I have hope, see head of sketch.js at https://editor.p5js.org/jgrizou/sketches/Si31amD_p.
That was fun anyway! I will try to handle the
set
route in the proxy too.In time, I will make it a standalone and actually usable library but for now I am exploring.
-
Thanks @Robin
what you are after?
Imagine we have this code on the device:
var speed = 0.5; function move(arg) { // do something with arg }
I am trying to create a
robot
object in the webbrowser that is connected to the device via UART, such thatrobot.speed
translate into a call toUART.eval('speed')
, androbot.move(value)
translate to call toUART.eval('move(value)')
. But in a generic way, so that the objectrobot
does not really have the propertiesspeed
andmove
and any other properties could be called without defining them explicitly, hence the use of Proxy as per Gordon suggestion.Check again the first few posts for more details. I hope that makes some sense.
==
I might have found a way around my problem with an additional call
eval('typeof prop')
to check if the property exist and is a function or not. -
I am banging my head trying to make proxy work for all cases which seems very hard, if not impossible. Ideally I would like both
get
,set
, andapply
to work, so we can do:robot = EspruinoDevice(UART) var p = await robot.speed; // assuming speed is a variable defined on the repl var temp = await robot.getTemperature() // assuming getTemperature() is defined on the repl await robot.move(someparameters) await robot.speed = 2*p; // changing the speed variable
The problem I am having is knowing if
speed
ormove
are object or function which change the expected behaviour from the proxy. I tried many approaches, but I start to feel it is impossible because we can only know if it is a function or the object after we return something and see if it is later called as a function again. I then tried to go via promises to see if I could decide this later but the same problem came back in the end. I am running short of ideas right now.Some ressources I used:
- https://stackoverflow.com/questions/44441259/determining-if-get-handler-in-proxy-object-is-handling-a-function-call
- https://gist.github.com/mrharel/592df0228cebc017ca413f2f763acc5f and https://medium.com/front-end-weekly/using-proxy-to-track-javascript-class-50a33a6ccb
- https://github.com/kozhevnikov/proxymise
I am quite stuck so I will stop now. I am aware all can be solved by relying on setter and getter function on the device, e.g getSpeed() and setSpeed(), and then building the proxy assuming all
get
calls are function calls. But I enjoy the challenge, it would be so neat to get all cases working seamlessly.Any thoughts on this?
As an alternative route, is there a method to list all user defined variable and function on the Espruino device? That way we could ask for that list when connecting and decide what can and cannot be called.
==
One learning to maybe add to the issue -> it would be good to have the eval function return 'undefined' when it receives 'undefined' (right now it catch an error, print it, and call the cb with null instead). This way we can use eval for all actions and know if it actually crashed or ran well but got undefined back. That would be around here: https://github.com/espruino/EspruinoWebTools/blob/13032d0e862d9976ed5eed33b42f07e93e6c25c3/uart.js#L491-L497
Also I implemented a simple promise system on top of eval, works very well!
- https://stackoverflow.com/questions/44441259/determining-if-get-handler-in-proxy-object-is-handling-a-function-call
-
Quick update. I got UART.write, UART.eval, and a custom
UART.on('myevent', callback)
all working simultaneously.When I register a custom
connection.on('data', ...)
callback, it overwrites the on('data', ...) callback defined within UART.write (see https://github.com/espruino/EspruinoWebTools/blob/13032d0e862d9976ed5eed33b42f07e93e6c25c3/uart.js#L470-L474), so I had to copy those few lines back into my custom one. A bit hacky as I inject data back into the connection from the outside.Code is very rough right now, just working my way around things. But, if curious, for now code is here: https://editor.p5js.org/jgrizou/sketches/Si31amD_p (see robot.js and sketch.js) and testable at https://editor.p5js.org/jgrizou/present/Si31amD_p
Next I will try the proxy trick :)
-
Thanks @Robin @Gordon for sharing all this, it makes total sense and sounds feasible!
https://www.espruino.com/Web+Bluetooth#two-way-communications
I tried with good success.
UART.on('myevent', callback)
is a great way to implement this combined with a function on the embedded side and format events.I guess we could have a UART object defined on the device itself, and then just query the keys for that.
Another option is actually you can dynamically handle key accesses using Proxy: https://spin.atomicobject.com/2018/07/02/javascript-proxy-object/Amazing! The Proxy thing is great and even more hands-off for the user.
maybe it even makes sense to built it as a separate library on top of it, that is Espruino specific?
Understanding this better now, I agree that a separate library makes more sense. I will work on a first version and share it here when it is ready ;)
That looks like a really neat little robot design! Are you planning to publish it anywhere?
Thanks! It is a quick prototype but you are right, I will make a repo with some basic info (BOM, wiring, code, etc) and share it with the community.
-
Hi Gordon, all,
I started playing making a robot controlled via a webpage using Espruino, see photos attached and video here: https://youtu.be/xKEqqFrYgNQ
Building the robot took a few hours. And both embedded code + remote control via basic webluetooth page using uart.js less than one hour, I really didn't expect it would be so quick to get a MVP, the Espruino toolchain and REPL spirit is amazing. See very basic interface here: https://editor.p5js.org/jgrizou/present/osAAXLUtL. It also seem that I could connect several webpage to the same robot which is quite cool!
This was mind blowing to me on two aspects:
- the ease of uploading code to a robot via the WebEditor
- the ease of simply defining function in the embedded REPL that we can call directly from the uart.js library. No interface or protocol to define and deal with, it is just great!
So first, thanks for developing all this!
--
I would like to brainstorm uart.js extension ideas here and collect the community opinion as to wether it is technically feasible and where I should start to implement it if I wanted to.
At the moment, I can see two ways to use the UART, write and eval:
- UART.write('LED1.set();\n');
- UART.eval('E.getTemperature()', function(t) { // do something with t });
And my understanding is that eval is calling write with an eval statement
"\x10eval(process.env.CONSOLE).println(JSON.stringify('+expr+'))\n"
. I not 100% sure how this work but it makes sense.Source code here: https://github.com/espruino/EspruinoWebTools/blob/master/uart.js
--
I would be interested in:
1) The ability to publish information from the embedded device to the webpage. For example, I might want to send an event if a sensor changes value, say my robot bumped into an object. Ideally, we would be able to a callback in uart.js for all spontaneous incoming messages. And the user can then sort them as they wish.
Maybe via a function sendToUart(), which can be tested using something like:
setInterval(function() {sendToUart({'value': true}}), 500);
By doing
setInterval(function() {console.log({'value': true}}), 500);
, I could see the packet being sent to the webpage but they would need to be processed and formatted. It might also make it harder to differentiate between self-published messages and messages sent after an eval() was called maybe. Althought eval() calls seemed to work well despite that noise.Just to be clear, I am interested in self-published events from the robot. That is I want to avoid having to call eval() from the browser at fixed interval.
2) The ability to use an embedded device as if it was a javascript module. This is mostly to avoid having to use string calls in the write and eval function. That would require having an additional function that, on uart.js connection, could read all function available on the embedded device and publish them under an object so we could do something like this on the webpage:
var robot = UART.connect() robot.left(); var temp = robot.getTemperature();
In the background, all these call would use uart.js write and eval function. Function to be published could be made explicit via some sort of export statement in the embedded code like:
exportUart({stop, left, right})
Similar to module export in javascript.
--
Do you have thought on this? To be clear I am not asking anyone to implement this, but rather for some comments/discussions on:
- whether this is already possible?
- if it is theoretically/technically possible? If yes, what approach could be used?
- where should I look in the code and what approach should I consider if I wanted to make this happen?
Thank you for your time reading this long post, any insight from the community would be very appreciated!
Jonathan
- the ease of uploading code to a robot via the WebEditor
-
Obviously for most people, if your web browser asks for Bluetooth permissions you click 'Hell No' without even batting an eyelid. When you finally do get a bluetooth device you want to use with the web browser, clicking that button could have happened over a year ago alongside a barrage of other questions about app permissions :)
That is for sure what happened here ;)
-
It works now! It was the most stupid thing as often, it was due to MacOS "Security & Privacy"settings but no warning was shown in Chrome.
I tried
espruino --verbose
and indeed found Noble issues.Noble: getPorts - initialising... Noble: module couldn't be loaded, no
node.js Bluetooth Low Energy Error: Cannot find module
'@abandonware/noble'It was not installed, so tried installing it which didn't work at first. I followed the tracks and tried resintalling xcode (https://medium.com/@mrjohnkilonzi/how-to-resolve-no-xcode-or-clt-version-detected-d0cf2b10a750). Could install noble in the end but still did not work.
I then looked at npm page for noble (https://www.npmjs.com/package/@abandonware/noble#os-x), which says:
On newer versions of OSX, allow bluetooth access on the terminal app:
"System Preferences" —> "Security & Privacy" —> "Bluetooth" -> Add
terminal app (see Sandboxed terminal)I went there and found Chrome app unchecked :/, checked it and everything worked. I could use the app loader and uploaded the t-rex app.
Somehow Bluetility was in the list and already activated but not the two other apps there. I don't remember having done so for Bluetility otherwise I would have understood . There was no warning in Chrome either to indicate this could be an issue, it was blind to the macOS settings. Even
chrome://bluetooth-internals/#adapter
was showing Bluetooth as available and working. So I am not sure how you could catch that and show a message from the WebIDE.Good to have this mystery sorted!
Thanks again for your time Gordon and apologies if this is a known issue and I missed it in the documentation.
-
Thanks Gordon for providing so much help!
Pixl.js does not appear in Mac OS's own Bluetooth menu. I could not see it anywhere but in Bluetility so far.
I tried Espruino CLI with node.js with no luck either. Pixl is not listed in the
espruino --list
command either. See attached.If of any use to try an alternative method, I am reading these info from Bluetility
identifier: 7C699A5C-4CE1-44B8-893B-EC81B93996E1
MAC: E2-89-0F-82-FC-86
Local Name: Pixl.js fc86 Service
UUIDs: 6E400001-B5A3-F393-E0A9-E50E24DCCA9EI have no idea how Bluetility can see it but not the other tools.
Let me know if you have other thoughts, but I do not want to take too much of your time on this as very few people still run macbookpro mid-2014.
-
Yes Chrome up to date, no funny version either.
The Bluetility can find the Pixl.js, which is good news but this seems to confirm it is a Web Bleutooth issue most probably coming from Chrome.
Multiple connection is not the issue here, I have been careful to cut my phone Bluetooth to be sure. Also tried rebooting the computer.
-
Thanks for the quick reply. Yeah it all looks like it should work, it is quite mysterious.
WebBluetooth status is showing as Ok. It goes through the motion of scanning for devices but end-up finding nothing and asking for a re-scan. It works well on my Android phone in the exact same condition. I attached screenshots.
It is my personal laptop so no IT restriction. And the Bluetooth works fine with my usual headphone/speakers, etc.
It is quite strange, I will keep searching. Please let me know if something pops to mind. I am ok to go for a BLE USB dongle, if you have any recommendation.
-
Just to complement, I saw that you there is https://shop.espruino.com/accessories/usb-bluetooth on the official shop, but it seems to have the Chipset CSR8510A10 which is not recommended if I understood well from https://www.espruino.com/Quick+Start+BLE#requirements.
Any specific recommendation from the community would be of big help here.
-
Hello,
I could not detect my pixl.js from the IDE, in Chrome on my macbook pro mid-2014 model. See bluetooth specs attached.
I checked the troubleshooting https://www.espruino.com/Troubleshooting+BLE and tried to activate the Experimental Web Platform features as per http://forum.espruino.com/conversations/356192/#comment15677897 without luck.
Also, I tried with both the chrome app and the online IDE. It all works fine on my Android phone and another computer running windows.
My questions:
- is this a known issue and do you have an idea how solve it?
- do you have a recommendation for a recent bluetooth adaptor that will work on macOS and can be bought in the UK? (I saw your list here: https://www.espruino.com/Quick+Start+BLE#with_web_bluetooth and I understand I need one based on Broadcom BCM20702A1 but the links are US based and they do not seem available in the UK.)
Thank you for your work. I just discovered espruino and I find the idea to be able to code and upload directly from the browser fantastic!
- is this a known issue and do you have an idea how solve it?
Nothing new on that front yet but I just found out about https://www.npmjs.com/package/proxy-deep. Posting here to keep track.