I have a Feather Huzzah and a project which will require all of the pins . . . so I need to use GPIO16 as an input . . . but, when I use the following lines of code in a python program or type them into the REPL, the Huzzah crashes
import board
import digitalio
button = digitalio.DigitalInOut(board.GPIO16)
The output on the REPL shows
[code]Fatal exception 9(LoadStoreAlignmentCause):
epc1=0x4023a85f, epc2=0x00000000, epc3=0x00000000, excvaddr=0x000000fe, depc=0x00000000
ets Jan 8 2013,rst cause:2, boot mode:(3,6)
load 0x40100000, len 30908, room 16
tail 12
chksum 0x44
ho 0 tail 12 room 4
load 0x3ffe8000, len 1084, room 12
tail 0
chksum 0xfa
load 0x3ffe8440, len 3248, room 8
tail 8
chksum 0x2f
csum 0x2f
I have tried this with Circuitpython 2.2.4 and 3.0.0, both of which crash. This should just work and is given in an example with the Featherwing OLED which connects button B to GPIO16 (along with a physical pull-up 100k resistor).
Has anyone else experienced/fixed this problem . . . or do I have a faulty board?
I posted this to the forum, but will add here as well. GPIO16 may require some special care -- still looking into this, but it may be related to this:
https://bbs.espressif.com/viewtopic.php?t=632
https://tttapa.github.io/ESP8266/Chap04%20-%20Microcontroller.html
In looking at the pin definitions in CircuitPython, GPIO16 is mapped as XPD_DCDC. XP_DCDC is the pin for "Deep Sleep Wakeup" according to this: https://github.com/esp8266/esp8266-wiki/wiki/Pin-definition
Is this relevant? https://www.esp8266.com/viewtopic.php?f=13&t=13118
does
button = digitalio.DigitalInOut(board.GPIO16)
cause CP to try to initialize it with a Pull-Up? If so, does that cause a problem
nah - it looks like the default is pull= None so this should not be a problem
https://github.com/adafruit/circuitpython/blob/master/shared-bindings/digitalio/DigitalInOut.c#L164
CP defaults to INPUT and Pull.NONE when the pin is assigned . . . so the pin would be floating (unless external pull-up / pull-down is applied) . . . when I connect the Featherwing OLED (where an external 100k pull-up is present) I experience the same crash.
I have also tried holding GPIO16 to ground / 3.3v (after the Feather Huzzah has booted and before/while I type the commands into the REPL) . . . same effect the ESP8266 crashes.
AH -- I know I have attached Featherwink OLED to ESP8266 before. I just plugged one into a working ESP8266 - with CP3.0 and it still works.
and I have rerproduced your crash with the OLED feathering when I assign the pin
tricky to apply 'special care' in Circuitpython . . . there is no way to switch the GPIO to output until it is assigned . . . and the crash has already happened at that point !
Will need some 'trickery' within Circuitpython itself !
agreed! At least the issue is reproducible...with or without the OLED connected.
I'm slowly digging through the source, but at this point I can say that it does treat GPIO16 specially in a lot of places...
I have tested it with a second Feather Huzzah and it behaves exactely they same (i.e. crashes when the GPIO16 pin is assigned). Essentially, assigning the GPIO16 pin is causing a reset.
And I have reproduced it on mine. I am convinced this is a CP bug. Now to find it!
I have used OLED feather wings on esp8266s but I鈥檓 not sure if it was ever with CP. I鈥檒l try to review this an see if I can find any clues to when GPIO16 became in issue.
It also may be worth trying it under Arduino just to see if it works. It may be a day or two before I can get to this but I鈥檓 interested in following up on it.
I haven't gotten much further on this. After scouring the interwebs, there is no _clear_ cause since there is so much conflicting & incomplete information out there. I studied the Eagle drawings for the Huzzah8266, and GPIO16 is not attached to the RST pin on the layout. To verify, hooked up the new "toy" and got this capture when creating the DigitalInOut object.
As you can see, GPIO16 goes high, and RST stays high (haven't learned how to sync the channel scales; if you even can).
MicroPython has apparently tackled this; see this issue. But, they handle the pins differently than we do.
I'll keep visiting this between working 3.x issues. Anyone feel free to take it on, as well.
One thing I failed to mention in the last post.
I have emulated all of the function calls and procedures from ESP8266_NONOS_SDK/.../gpio16.c (couldn't get our build to compile using gpio16.h). They resulted in the same manner of causing a reset.
I am really tempted to buy an ESP-12 module, and attempt to take off the metal cap to see if GPIO16 is connected to EXT_RST (external reset).
I have one that I will be willing to sacrifice. Any tips on how to remove the cap? Its a feather but I fried the cp2104 chip ad it still works via the UART.
Careful dremeling seems to be the most fault-proof method, if you don't care about putting it back. Hot air works for some, but you risk unsoldering some parts inside.
The crash is due to a load store alignment issue which is likely due to some math that computes the register location being wrong. GPIO16 is the 17th gpio so it doesn't surprise me that we're loading or storing from the wrong location.
There is a bit of that math here (behind PIN_FUNC_SELECT): https://github.com/adafruit/circuitpython/blob/master/ports/esp8266/common-hal/digitalio/DigitalInOut.c#L40 It looks like there is more special casing below but not there.
@tannewt I actually bypassed all of that on some local test; mimicking all the other digitalio functions (if pin->gpio_number == 16). Same result... 馃
Also, to the best of my knowledge, PIN_FUNC_SELECT won't work with GPIO16 anyway, since it isn't actually in the same register space as the other GPIOs. It is in the RTC register space so that it can remain accessible when in deep sleep. That's why there are specific functions in gpio16.c. (this is all from datasheet & GitHub reading)
You are clearly ahead of me. Carry on. :-)
If you need another Huzzah to hack on/destroy please order it on us.
FYI - I did get the lid off an ESP8266-12S - but I damaged a few components along the right side of the image in doing so. One of the damaged pads is connected to RST. None of the pads is connected to GPIO16 and I can't find any connection to GPIO16 other than Pin 8 of the ESP8266ex.

"HOW did I miss that?!" Update:
So, we (I?) were chasing the RESET culprit based on rst cause:2 being linked to External reset or wake-up from Deep-sleep from Espressif's documents. Since we have pretty much ruled out a hardware issue (THANKS @jerryneedell!!), I've gone back and started looking at pin/RTC setup to see if we have a problem there.
To aid in that, I _attempted_ to turn on debugging (didn't work; build failed; work that later?).
One thing I noticed before, and felt safe in changing upfront was mp_reset->reset_pins; it uses gpio_output_set for 17 pins. The 17th pin being GPIO16, which isn't in the normal GPIO register section, is extraneous.
Anywho, I turned on esp.osdebug in the REPL in hopes of getting some additional info. I didn't get _additional_ info, but I did notice something that I had been missing this whole time (even in the first post on this issue):
Fatal exception 9(LoadStoreAlignmentCause):
epc1=0x4023af5b, epc2=0x00000000, epc3=0x00000000, excvaddr=0x000000fe, depc=0x0000000
So, safe to say we can stop chasing the RESET-herring.
From the above exception reports, I noticed a familiar value: excvaddr=0x000000fe. In common-hal/microcontroller/__init__.c, GPIO16 is setup using #define SPECIAL_CASE 0xfe. To verify that is actually causing the exception, I changed SPECIAL_CASE's definition to 0xff. Here is the result:
>>> b = digitalio.DigitalInOut(board.GPIO16)
Fatal exception 9(LoadStoreAlignmentCause):
epc1=0x4023af59, epc2=0x00000000, epc3=0x00000000, excvaddr=0x000000ff, depc=0x0000000
Haven't gone beyond this yet, but wanted to get an update in...
@tannewt in reviewing your comment, you clearly caught this. :smile: any ideas beyond PIN_FUNC_SELECT?
@sommersoft good sleuthing! I think that SPECIAL_CASE insight is key. Its stored in gpio_function and peripheral for the pin. One of these must be used as an address (peripheral is my guess) and it isn't checked for the special case. You could compare to the other values to find the ones that are obviously memory locations.
Found the offending call!
shared-bindings/digitalio_digitalinout_make_new -> assert_pin_free calls common_hal_mcu_pin_is_free, which is checking for pin->gpio_number == SPECIAL_CASE.
SPECIAL_CASE isn't assigned as gpio_number for any pins like NO_GPIO is. Changing the conditional to gpio_number == 16 keeps the board from crashing. Now to find out how to actually check the status of GPIO16, so we can avoid this from always happening:
>>> b = digitalio.DigitalInOut(board.GPIO16)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Pin XPD_DCDC in use
Progress, at least.
Now to find out how to actually check the status of GPIO16
Kind of knew the answer to this when I wrote it, but wanted to verify. There is no "enable/disable" register for GPIO16, since it is tied to the RTC and needs to be available in deep sleep. It is always enabled, and is only configured for out or in.
So, I'll poll you all on this. Do we skip the assert_pin_free altogether, or setup a flag to track it's usage state?
And a slightly related question: which state do we want GPIO16 initialized? reset_pins is responsible during mp_reset.
Epic! Great work! I'd set up a flag.
I usually initialize it to the default state. Not sure what that is in this case.
Preliminary "Its Working" Update
I've got most of digitalio worked out I think. Will try to get the PR in tonight.
Test REPL:
Press any key to enter the REPL. Use CTRL-D to soft reset.
Adafruit CircuitPython 3.0.0-alpha.1-509-g58ba741-dirty on 2018-04-25; ESP module with ESP8266
>>> import time, board, digitalio
>>> b = digitalio.DigitalInOut(board.GPIO16)
>>> print(b.direction, ';', b.value)
digitalio.Direction.INPUT ; False
>>> b.direction = digitalio.Direction.OUTPUT
>>> print(b.direction, ';', b.value)
digitalio.Direction.OUTPUT ; False
>>> for i in range(5):
... b.value = True
... time.sleep(.5)
... b.value = False
... time.sleep(.5)
...
...
>>>
And result:
Want to do some re-reading before I test input.
Haven't tested input yet; will tomorrow. Pre-PR update, if anyone wants to give it a go: https://github.com/sommersoft/circuitpython/tree/bd5a5daaaebbdb36d4ad81541b452e9b485e8b3a
Fixed by #780. Thanks @sommersoft, @jerryneedell and @jepler.