Hi Maik, yes that is expected. Not like Arduino but I think it is more obvious for beginners.
It you want to 'lock' the pin mode, use pinMode and then read/set/reset/write won't change the pin state
Oh dear I find it so confusing :)
Reading a pin is not a read-only operation. So I check if LED1 is on and it returns 1 meaning yes. Except it's not any more, LED1 is off now.
Could you tell a bit more about the reasoning for this? I'm just trying to make sense of it.
Reading pinMode documentation with its eight possible values is a bit rough. Do I have to know pullup, opendrain and the difference between "output" and "af_output" just to play around with LED1 like in op example?
I really admire Espruino's API and find it superior to other things I've tried, but this one seems like NaN in JS - weird.
Yeah, this is pretty bizzare. Does digitalRead() do that too? (I can't remember if I've run into this - but I almost always do pinmode)
Does digitalRead() do that too?
Yes, it does - pretty much all the IO functions do it.
Could you tell a bit more about the reasoning for this?
Sure, it's that in Arduino, you have to know what to do with
pinModebefore you do anything. It's a mess, because you can write perfectly good code, and it'll fail silently because the pin mode is wrong.
If you say
analogReadit's pretty obvious what you want - so why force the user to use
pinModebeforehand as well?
For instance you'd write
digitalWrite(LED1,1)and absolutely nothing would happen - no warning, no error. It's not exactly friendly.
As I'm trying to be easy to use for beginners, if nobody specifies
pinModethen Espruino tries to work out what you want from the function you called. If you did call
pinModeit behaves like an Arduino though - what you specified with
Reading pinMode documentation with its eight possible values is a bit rough
Yes, thanks for letting me know - I've put that on my to-do list and I'll try and improve it. I'll make sure
.readmention that they change the pin's state to input in order to read its value too.
Do I have to know pullup, opendrain and the difference between "output" and "af_output" just to play around with LED1 like in op example?
Yes and no. If you want to set LED1, you use
digitalWriteand it will just work, but the best way to know the current state of it is just to remember it in a variable.
The reason is that digitalRead is probably not doing what you expect. The actual GPIO is like in the circuit below. It's a bit overcomplicated, but basically the input value doesn't come from the same register as the output value - it's actually reading the voltage on the output pin.
So if you have a capacitor (for instance) connected to the pin, and you do:
pinMode(pin,"output"); digitalWrite(pin,1); for (var i=0;i<100;i++) console.log(digitalRead(pin));
You'd expect that it would just print a bunch of 1s - But it almost certainly won't. It'll actually print 0 until the capacitor charges up, and then 1.
Now maybe that's not a huge issue, but what if you were trying to pulse-width modulate the capacitor's voltage to 3.3v/2 by simply toggling the output high and low the way you'd think would work fine:
while (true) digitalWrite(pin,!digitalRead(pin));
and now it's going to do something really different from what you expect. Instead of a nice quick square wave it's actually going to wait until the capacitor has charged before discharging, and vice versa
It would be totally possible to add a
.readOutputValue()method to the Pins that would do what I guess you're expecting, but to be honest I think that's probably confusing matters.
Thank you @Gordon for such detailed explanation.
I have used the LED example but in fact my use case is a bit different. So I have a bunch of things toggling a relay from a number of places in the code. Then there is other things logic depending on relay's state, so I want to check if it's on or off.
Now I could simply use a variable just like you suggested but it adds this overhead of keeping it in sync with a real state - as I said in multiple places of the code. I just feel more confident when operating directly on a given pin with Pin.write and - I wish - Pin.read.
I hope you will change your mind and see some value in adding something like readOutputValue :)
In that case I'd really consider adding functions like
getRelay- it'd allow you to do other things at a later date like logging messages when the relay turns on and off, or turning an indicator light on and off as well.
While it does add some overhead, it could well be worth it for flexibility/readability (for instance what happens if you change to use a relay module that turns on when 0v is applied).
To be honest your relay case is probably a pretty good example of why a
digitalReadthat doesn't change state is a really bad idea on Arduino. While it would work most of the time, it could very easily inexplicably return the wrong result some of the time, which would cause some very strange bugs.
What does everyone else think about
readOutputValue? Worth it, or just a bit confusing?
You are right. I will have to delegate it to some utility classes. This project is mostly in my head at this time so you probably saved me some trouble later on.
I also realized that using pinMode is a good idea, wasn't even aware of it.
Am I right thinking that onInit is a good place for pin mode configuration?
That's where I always put my pinMode() calls.
I'd very much like a pin.mode() - Gordon indicated a willingness to implement this...
I oppose pin.readOutputValue(). Set the pin mode properly in the first place and use pin.read(), or remember what you've set the pin to. Unless we're interested in cases where the output value is not the same as the logical value on the pin because you're trying to draw so much current that the chip can't drive it hard enough.
Set the pin mode properly in the first place and use pin.read()
I suppose it all comes down to documentation so I'll make a PR and we're good ;)
Ehh say what you want but for me (that is a beginner with no understanding of Figure 16) this whole thing is getting hairy.
After reading example codes I assumed that
pin.write(!pin.read())simply toggles given pin right? Well no, it turns out it's not that simple. It works like a toggle for an LED, but it doesn't work for a relay module.
Yes you said charging capacitors and square waves, but I say toggling is more likely a beginner's thing.
After reading example codes I assumed that pin.write(!pin.read()) simply toggles given pin right? Well no, it turns out it's not that simple. It works like a toggle for an LED, but it doesn't work for a relay module.
What?! pin.write(!pin.read()); should absolutely toggle the pin, unless you did pinMode(pin,'input') or something which would prevent it from writing the pin... That's wacky if it's not...
The only way I can see that not working is if the relay draws so much current that even when the chip is trying to drive the pin high, the voltage isn't high enough to be a logical 1. Relays often draw more current that a microcontroller is designed to supply; if such a relay was connected directly, the 'high' output voltage (if it's active high) wouldn't be able to get up to 3.3v; the pico can only supply 20mA max, and I think at 20mA, it's significantly below Vcc - and if it was drooping low enough, it would be seen by pin.read() as a 0, not a 1.
What's the voltage on the pin when you drive it high with the relay module connected?
@DrAzzy I will try to get hold of a multimeter tomorrow and answer your question. For now I can only say that it's a one channel Arduino relay module connected with Espruino Board with shared GND, IN to C9 and VCC to VBat. I should also clarify that it fails when no explicit pin mode specified by me, but works when I set pin mode to "output" beforehand.
And don't get me wrong guys, I'm not claiming that there is no good explanation for this behaviour. All I'm saying is that it has nothing to do with beginners friendliness :)
Oh, if it works with pinMode() set to output, but not with pinMode() not set, it's not what I thought it was.
I think I see what's happening.... I'll bet it sets it to input to read it, but because there's a load on the pin, the voltage is dropping as soon as it's no longer an output - so fast that by the time it reads it, it's no longer high anymore...
Yeah, this behavior is kinda ugly. I always explicitly set pinMode() so I don't run into this sort of behavior.
When I said about
pin.readnot being reliable, it's really because it depends what you actually connect the pin to.
For one of those relay modules with the drive electronics, something like:
Will probably work fine. I still wouldn't recommend it though :)
I did some experiments with setting and reading LEDs using firmware 1v52:
The set() method lights up LED1 and the read() returns the right state. In addition, read() switches off the LED, so the next call returns false. Is this expected behaviour?