Most recent activity
-
Nothing new on that front yet but I just found out about https://www.npmjs.com/package/proxy-deep. Posting here to keep track.
-
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 ;)
See https://jgrizou.com/