-
• #2
Hi Kim, I'm afraid it isn't possible on the Espruino board... I'll update the documentation.
For the latest revision I chose to use pins for the LEDs that were basically useless for anything else (so there was the maximum amount of IO). It means that there aren't any timers for those pins, so no PWM.
The pins it's available on are detailed here: http://www.espruino.com/ReferenceESPRUINOBOARD
I've just put an issue in for implementing PWM (analogWrite) in software though - as it would be nice to be able to do it on the LEDs (https://github.com/espruino/Espruino/issues/193)
-
• #3
Sounds like fun. Might try a JavaScript implementation of PWM tonight.
-
• #4
Heh, it won't be that fast - but doing 50/50 using setInterval would probably work ok. There's some internal stuff for doing digitalPulse that would actually handle it really well.
-
• #5
I have myself some pulsing LEDs ... but the journey there wasn't all that smooth though.
First, the code:
function fakeAnalogWrite(value) { if (value >= 0 && value <= 1) { var on_time = (value*25)|0; on_time += 1; digitalPulse(LED1, 1, on_time); digitalPulse(LED1, 0, 27 - on_time); } else { digitalPulse(LED1, 0, 26); } } // pulse LED1 from on to off every *freq* seconds function pulse(freq) { while (true) { for (i = 0; i <= Math.PI + 1; i = i + (Math.PI)/(freq*27)) { fakeAnalogWrite(Math.sin(i)); } } }
I would be happy to write a tutorial for this code, but first I want to know if any improvements can be suggested.
The problem with this code is that it halts all other things that are going on. Which isn't all that efficient. So any suggestions on how to improve this would be appreciated. I realise that this can be easily implemented on a lower level (and it should be implemented on a lower level), but the example is great in finding out how well this compact version of JavaScript works. And I have a number of suggestions:
- a delay function would be great. I had to do a lot of tinkering to get this to work, and essentially I'm abusing the digitalPulse by setting it off for some time. A dedicated delay function should be better, as it can be improved over time to allow other functions to run while this part is just waiting.
- using
setTimeOut
was pure misery. I honestly failed to write any of it down, but I got bugs, missing functions, memory overflow ... . If someone is brave enough to rewrite it using setTimeOut, maybe we can get started with making it a bit more useful :). - there does not appear to be tail recursion. This is a shame. It is so easy to detect (just a function at the end) and it makes code so much more compact. Any plans on implementing this (soon)?
- Using the LEDs could be easier.
Why not add some prototype functions to them such as on() and off()?Okay, seems like you have set() and reset(), but those names aren't that obvious :).
quick update:
the problem withsetTimeout
is that these things don't seem to work:function pulse(freq) { setTimeout(doPulse(freq), 1000); }
which fails with ERROR: Function or String not supplied!.
another update:
This is already a bit better and at least allows you to stop the execution:function doPulse(freq) { for (i = 0; i <= Math.PI + 1; i = i + (Math.PI)/(freq*27)) { fakeAnalogWrite(Math.sin(i)); } // send an extra short pulse on to turn the LED off // the more obvious LED1.reset(); does not seem to work digitalPulse(LED1, 1, 1); } function pulse(freq) { setInterval(function() { doPulse(freq); }, freq*1000); }
But some messy stuff is going on with the pulse at the end ;).
- a delay function would be great. I had to do a lot of tinkering to get this to work, and essentially I'm abusing the digitalPulse by setting it off for some time. A dedicated delay function should be better, as it can be improved over time to allow other functions to run while this part is just waiting.
-
• #6
If you only want slow-ish PWM, you can do something like this:
Pin.prototype.pwm = function(dutyCycle, speed) { var pin = this; if (Pin.intervals===undefined) Pin.intervals = {}; if (Pin.intervals[pin]!==undefined) clearInterval(Pin.intervals[pin]); var pulseLen = dutyCycle*1000/speed; Pin.intervals[pin] = setInterval(function() { digitalPulse(pin, 1, pulseLen); }, 1000/speed); }; LED1.pwm(0.1, 100); // 100Hz
Note that because digitalPulse uses a timer, you're only having to run JavaScript once per pulse, and not once.
If you want to do that glowing effect then you could do something like this:
Pin.prototype.glow = function() { var pin = this; var pos = 0.001; var interval = setInterval(function() { digitalPulse(pin, 1, Math.sin(pos*Math.PI)*20); // up to 20ms pos += 0.01; if (pos>=1) clearInterval(interval); }, 20); // 50Hz }; LED1.glow();
Which shouldn't halt Espruino while it's working.
-
• #7
Your code needs comments :). I like the approach of prototyping. But there is also a bit of dirty playing here, like starting from
pos = 0.001
. Might not be so obvious to most. The first code is too much though, no idea what it is doing I'm afraid. Furthermore, I think that the intervals function is undocumented?Brings me to something else though, why not
- Build a reference of the javascript that is implemented, so that it is easy to see what is and isn't in there; and
- Provide space and speed complexity for the functions, so that you can figure out which functions you should use.
The second piece of code is nice though. I'll try to elaborate on that version.
- Build a reference of the javascript that is implemented, so that it is easy to see what is and isn't in there; and
-
• #8
Here is an extended version of your last code to glow an LED. There has been a bit of a comment explosion, it allows you to specify the duration and the Hz, and it has an improved glow by taking the power of the sine function, so that only the peak is very bright. I would like to go to
pow( .. , 3)
but ... floating point error ;).// add a new function to all pins to glow Pin.prototype.glow = function(milliseconds, Hz) { // see if the user specified a Hz // if she/he did not, we default to 60Hz Hz = ((typeof Hz) === "undefined") ? 60 : Hz; // save the pin: this is important, as in an internal function // 'this' would refer to the function, and no longer the pin. var pin = this; // remember that to achieve a certain Hz, we need to // have cycles every 1000/Hz milliseconds var cycle = 1000/Hz; // we need to cheat a bit and take pos > 0 since // sin(0) = 0 and digitalPulse does not accept 0 var pos = 0.001; // next, we create the function that will call our digital pulse var interval = setInterval(function() { // we send our pulse, and we use the sine function to determine the length // the use of Math.pow( .. ) will epsecially lower the lowest values, while // keeping 1 almost 1. This will help to ensure that only the brightest // part of the glow is truly bright. digitalPulse(pin, 1, Math.pow(Math.sin(pos*Math.PI), 2)*cycle); // we advance to the next position, determined by // how long we want one glow to take pos += 1/(milliseconds/cycle); // if we are done, we clear this interval if (pos>=1) clearInterval(interval); // finally, we launch this every cycle, where the dimmer cycles // will have a shorter pulse and the brighter cycles a longer pulse }, cycle); };
-
• #9
Just a bit of magic and you have a nice collection of fireflies on an Espruino:
function createFireFly(milliseconds) { milliseconds = ((typeof milliseconds) === "undefined") ? 0 : milliseconds; setTimeout(function() { var led; var next = Math.random(); if (next <= 0.05) { led = LED1; } else if (next <= 0.1) { led = LED3; } else { led = LED2; } var duration = 1000 + Math.random() * 2000; led.glow(duration); createFireFly(duration); // activate the new firefly after the current one is done, // with an added delay of at most 1 second // though every so often ... it may take a bit longer }, milliseconds + Math.random() * 2000 + (Math.random() < 0.01 ? 5000 + Math.random() * 10000 : 0 )); }
-
• #10
:) Just so you know - intervals is just a variable. The idea is that if you call LED1.pwm more than once, it knows that it was already doing PWM on that pin and knows to get rid of the old setInterval...
pos=0.001
is a minor hack - it's because digitalPulse complains if it gets given a value of 0. It just makes more sense to ensure that it is never given that value :)Can you give an example of which
Math.pow(.., 3)
doesn't work? I just triedMath.pow(Math.sin(1), 3)
and it's fine.I'm unsure if the speed/complexity would help much... I do already have a page on performance as Espruino has some particularly weird behaviour ;)
-
• #11
Thanks for clarifying the code. It was already late yesterday and details such as that tend to get lost in those cases. The code is definitely JavaScript, but the JS code you write on an Espruino definitely has a different flavour.
No idea which value causes the pow error, but it occurred when writing
Math.sin(pos*Math.PI), 2)
in the line that saysdigitalPulse(pin, 1, Math.pow(Math.sin(pos*Math.PI), 2)*cycle);
.Thanks for the performance page, first time I see it.
Overall, it would be good to have this software PWM on a lower, more barebone level for performance reasons. This code is nice to get your feet wet, but it does show how the Espruino can struggle with relatively simple things due to the embedded nature. I would also still suggest to introduce a delay() function. Where a timeout allows you to asynchronously (I think?) execute a function, a delay would allow similar behaviour during the execution of a function. I think this will simplify a lot of the code.
And now that I think of it: are there any pages on power management on the Espruino? I know that there were a lot of things you could do such as enter a lower power state or even turn off USB, but information on those things is hard to find.
-
• #12
The delay function is tricky - it'd be nice to add it but in a way it encourages coding in a style that works very badly with Espruino. As you'd see with your previous code, having loops that don't return will effectively stall the interpreter and will stop other things from being executed - it's one of the reasons why web browser JavaScript doesn't have anything similar (afaik) either.
It also exposes the slow-ish speed of execution, for instance:
digitalWrite(LED1,1); delay(1); digitalWrite(LED1,0);
Won't keep the LED on for 1ms, it'll probably end up being maybe 0.1ms more (don't know - haven't tested it) because of the relatively slow execution speed. I can pretty much guarantee that as soon as I add the delay function I'll get complaints about exactly that kind of problem :)
There aren't pages on power management at the moment, but I've put it on my list of things to add. In short, something like this:
setInterval(function() { doStuff(); }, 60000); setDeepSleep(1);
or
setWatch(function() { doStuff(); }, BTN, true); setDeepSleep(1);
Will instantly be very power efficient, dropping the device into a state where it draws around 100uA.
However at the moment I haven't got
wake from USB working, so you're best off always having a setWatch on BTN so you can insert the USB cable and press BTN to wake the device up so that it connects again. -
• #13
Thanks for the quick response :).
About the delay: yes and no. You can make it very clear in the function description that this is only a rough delay, and that it may or may not be accurate (as I recall, you already do that at some points). While it does block the execution, it could be used with e.g. a setInterval to call it every 2000 milliseconds. Having a dedicated delay function would then hopefully free up that time, eg
delay(1000)
to do more meaningful things than just sit around and wait for that part of the code to execute. But getting a good implementation may take some time, I'm sure. Furthermore, other things might be more meaningful for the time being. For example an array sort or search? Or a binary tree structure? Such items are easy enough to implement on a low level, but would be prohibitively expensive to do in Javascript.On the power issue: I actually though you couldn't do that. It says Espruino will only enter Deep Sleep when there are no timers in the reference and I assumed that both are timers. Well, especially the one with setInterval(). It then worsens things further by saying that * The System Timer will also pause*. Ouch :). Good to know though that this already works.
-
• #14
Unfortunately doing stuff during the
delay(1000)
is actually pretty difficult, as you definitely can't run JavaScript (the execution stack will already be taken up with what you're currently executing). That's the issue really - a delay of 1ms is fine, but people **will ** use delay(1000) and then the whole experience of using Espruino will really degrade.By when there are no timers it means PWM. I'll update the docs on that along with a Power consumption page. The issue is that when it enters deep sleep, all the clocks stop (except the real time clock) so anything that depends on that clock (SPI,I2C,timers,usart,etc) stop working.
-
• #15
No problem, I think this is just the curse of having a very high level language at your disposal.
Thanks for the explanation on the timers. Do remember that a lot of people buying these Espruino's will be coming from the software side and have virtually no experience with electronics. For most of them a clock is a clock and they never consider that there might be other clocks at work as well. I definitely didn't think of it :).
-
• #16
This might be handy: http://www.espruino.com/Power+Consumption
-
• #17
Excellent :) Thanks!
-
• #18
Sleep mode is available on the STM32 chip (very low power, but ALL DATA is lost from RAM). It is not currently used in Espruino (see Other sources of Power Draw below for why not).
I assume you mean Stop mode is available ... ?
such a bit problem* -> *such a big problem*
because the Bluetooth module draws 30mA when active*
Could you add information on how to reduce this power? Can we write to the pins to put it to sleep? Maybe at a word on how to enable the sniff mode, as that will reduce consumption to about 2mA? See also http://www.edaboard.com/thread267303.html as you may in fact need to change the query duration. I don't have a good enough energy meter to test these things, so someone else would need to go through the trouble to figure out how you can save quite a bit by altering the settings of the HC-05 module.
There are no current results for deep sleep.
Adding a glow light with variable duration and brightness might be a very good idea, consumption-wise. Something like Math.pow(Math.sin( ..), 3) will reduce power consumption by about 60% while giving a nice glow. Brightness could push this down even further.
Any ability for a reduce speed mode where the clock speed is lowered, e.g. like the micro Python board?
It would definitely be interesting to see how low the board can go with all these things combined, e.g. have bluetooth on there where you can still communicate with the board, have a light flashing and have input (e.g. BTN1) working. I think this is also a fairly reasonable use-case. If you have the Espruino disconnected from USB you either are going to use it for the wireless capabilities, or to have it lay dormant and react to e.g. someone passing by to turn on the lights. The last use-case is pretty self-explaining, but the first use-case would definitely benefit from squeezing out as much of the savings as possible. Just my thought though :).
-
• #19
Thanks, I've updated the docs. Maybe someone else can give more info on the Bluetooth module - I'm not too sure on low power but there may be something available via AT commands.
As far as clocking down, Espruino can do it, but then the frequency for PWM (and also USART/SPI baud rates) will change. Some kind of compensation needs to be built in for that - otherwise it's very possible to do. The obvious change would be to clock everything down to a lower speed when entering Sleep mode. That would probably halve power consumption.
-
• #20
Yes, the HC-0x bluetooth modules support SNIFF to go into sleep mode.
AT+SNIFF=Param1,Param2,Param3,Param4
Param1: maximum time
Param2: minimum time
Param3: test time
Param4: limited timeI don't know what the best values are.
There's also AT+ENSNIFF=Param and AT+EXSNIFF=Param (Param: Bluetooth address of device).
-
• #21
Thanks for the info!
There's some info on sending the AT commands at http://www.espruino.com/Bluetooth so it should be pretty easy to enable this...
In the reference on the function analogWrite an example is given where you use it on LED1, namely
analogWrite(LED1,0.5,{ freq : 10 });
. However, the code example on Pulse Width Modulation (PWM) specifies that PWM is only available on certain pins, and LED1 is not one of those according to the reference.Is it possible to have a pulsing light or not?