Pysense pinout / wake on pin

  • @daniel @bucknall would it be possible to know which ports of the PIC the external I/O header pins (EXT_IO4 and EXT_IO5) are connected to?

    Also, is there any documentation for the 3V3_Sensors and 3V3_Pymodule pins? I would have expected the former to stay up during deep sleep and the latter to go down, but it seems to be exactly the opposite?


  • @jcaron It's working as you said. Thank you

  • @prashanth I have cut the pin p9 as you said, but it is not able to detect it as interrupt. I have used 1 if py.peek_memory(0xE) & 0x02 else 0 to read the status of the pins. It actually prints the data when detected. But, only problem it is not able to detect as interrupt. It toggles between 1 and 0 using sensor. If possible please share any example code which you have tested will be helpful to me. Thank You

  • @prashanth in our case, physically cutting the pin on the module so it’s isolated from the Pysense. That was the only way to have Pysense-controlled wake-on-pin work reliably with the sensor we used. YMMV.

  • @jcaron what does cutting the pin actually refers too?

  • @jcaron Thanks for the clarification. Sounds like a bit of a mess really. I was hoping we could end up with a fairly standard codebase, changing modules depending upon the communication methods required. If there are so many variations between revisions and further differences depending on which board you have them seated in, it doesn't sound like a common codebase would be that easy. If the board library contains a [board].deepsleep function I would have expected it to shut down the sensors I'm not using then call machine.deepsleep. Having to reverse engineer a library to find out how to drive the hardware directly sort of defeats one of the advantages of having Python ... but maybe that's just me :-)

    Thanks for the information, I appreciate it.

  • @john-baird The "old" boards (LoPy 1 but not LoPy 4, WiPy 2 but not 3, SiPy, possibly a few others) had an issue with deep sleep taking too much power.

    On those, machine.deepsleep would use >10 mA in deep sleep. More recent boards (WiPy 3, LoPy 4, FiPy, and I believe the GPy as well) don't have that issue, and use about 10 µA in deep sleep (with the possible complications related to LTE etc. of course).

    If you don't use external sensors, then machine.deepsleep is the recommended way of doing deep sleep.

    If you use a PySense or PyTrack, you have also to take into account the PIC and the sensors on those boards: machine.deepsleep will not shut them down, so they can continue drawing power during deep sleep.

    If you don't need those sensors during sleep, you can probably just power them off before going to sleep (and power them back on when you resume). The simplest way is probably to change the relevant PIC registers to cut the power to those. Not sure if there's enough information in the Pysense library for that, but it's not too difficult.

    Then you still have the PIC that remains active (for the UART-to-USB bridge, mostly). Don't know how much power than one draws. If you really want to minimise your power draw, you'll have to put the PIC to sleep as well, and I'm not sure if there's a way to do that without also cutting power to the module.

    If you need to wake up based on an interrupt from a sensor, you may also have to go through the PIC, though experience with that has not been quite positive (way too much power drawn when trying to use the accelerometer for wake up, for instance).

    So it all really depends on your exact scenario. In my case I was using LoPy 1 modules, so I had no choice but PySense-controlled deep sleep. With a GPy the situation is quite different.

  • @jcaron Hang on, I've read elsewhere that if you are using a pysense or pytrack you should use the deepsleep from the board library, yet here you say to use the one from the module. Which is it? Does using the board library only apply to older modules? If so, what classes as 'old'?

    I'm running a GPy on a Pysense board and need both deepsleep and wake-on-pin. Which should be called and what is the difference in functionality?


  • @sslupsky The Pysense/Pytrack firmware was updated to support wake on pin (along with changes to the libraries), so it’s no longer needed to patch the firmware.

    However you will still need to cut that pin to achieve good results.

    Note that this only applies to Pysense/Pytrack controlled deep sleep / wake on pin. If you use one of the more recent modules (WiPy 3, LoPy 4, FiPy...), you should use the module’s native deep sleep and wake on pin functionality.

  • @jcaron Are you aware if Pycom has updated the Pysense firmware and library to support pin wake up or is the hack you described still required to enable this functionality?

  • @charly86 The data sheet was updated with correct information for the external I/O header, but as far as I know there is no official detail about the (other) pins of the PIC.

  • @jcaron did you had a chance to get PyCom official answer on which PIC pins are connected where? Having schematics would really help and give us more time to focus on firmware instead of debugging and trying to know how things are working because we have no documentation.

  • Quick update: the fact that the pin was connected to a LoPy pin was just causing too many headaches.

    I ended up cutting pin P9 on the LoPy, and things are much better. No additional resistor needed, no funky voltages, no weird signals all over (especially while going to sleep or waking up). It probably also saves quite a bit of power. It does wake up correctly on changes on the pin.

    Of course, that means that you have to read the pin via the PIC:

    1 if py.peek_memory(0xE) & 0x02 else 0

    And you can't have callback triggers on pin changes while the ESP32 is active, but in my case it's not a problem as the ESP32 is only up long enough to read the pin and send a LoRa packet before going back to sleep.

  • Victory! I managed wake on pin with a Pysense!

    Adding a 1.2K resistor between 3V3_Sensor and the input pin resolved the situation, with the high/low voltages within range. Coupled with the PIC firmware change, and changes to the Pysense library, the PIC wakes up and the Pymodule as well!

    As I am definitely not a hardware guy, I'm pretty sure this is not really the best solution, as it results in very high "low" voltages on the input pin (1.63V when the LoPy is powered, 1.23V when it's not), but at least it validates the fact that you can definitely wake on pin with Pysense.

    So, here are the details:

    1. The firmware needs to be modified to (at least) clear INTF when it occurs, otherwise the ISR will be called forever.

    @bucknall or @daniel, can you please release a new version of the firmware that does this? Ideally it would also set/clear a flag somewhere else that could be checked via peek_memory. In my case I don't need this as the sensor I use keeps the signal high for a few seconds at least, so I just check if the pin is up on boot, but others may need it (and it would be required for a correct "wake up reason").

    Until such a firmware is released, you can start from the existing Pysense 0.0.4 firmware .dfu, and do the following:

    • make a copy (let's call it pysense_0.0.4-patched.dfu)
    • strip the DFU header with dfu-suffix -D pysense_0.0.4-patched.dfu
    • change bytes 40A & 40B from 82 31 to 8B 10 (that's a BCF 0B, 01 replacing an unnecessary MOVLP 02)
    • changes bytes 3EFE & 3EFF from A6 04 to C2 1B (that's the new application checksum for the bootloader)
    • re-add a new DFU header with dfu-suffix -p 0xF011 -v 0x04D8 -d 0xFFFF -a pysense_0.0.4-patched.dfu
    1. Flash the new firmware onto the Pysense:
    • power-on the Pysense with the button pressed
    • within a few seconds, run dfu-util -D pysense_0.0.4-patched.dfu
      If you don't get your timing right it may fail, just retry a couple of times and it should work.
    1. Change the Pysense library so that the line just before sending the GO_TO_SLEEP command reads:

      py.poke_memory(ANSELC_ADDR, ~((1 << 7)|(1 << 1)))

    This is to keep RC1 a digital input.

    1. Either in the Pysense library, or between the calls to setup_sleep and go_to_sleep, add:

      py.poke_memory(0x0b, py.peek_memory(0x0b) | 0x10)

    This is to enable interrupts on RC1. Register 0x0b is INTCON and bit 0x10 is INTE.

    Option: you can change the edge on which you want interrupts with bit 6 (INTEDG) of 0x95 (OPTION_REG).

    1. Connect your sensor to:
    • GND (pin 1 on the External I/O header of the Pysense)
    • 3V3_Sensor (pin 4)
    • EXT_IO1 (pin 6).

    Also add a resistor between 3V3_Sensor and EXT_IO1. I used a 1K2 resistor, but:

    • this will probably vary based on the sensor
    • there's probably a much better way to do this to achieve the right voltages in all cases!

    Now you can:

    • check the status of your sensor with pin P9 or via the Pysense: py.peek_memory(0xE) & 0x02 (bit RC1 of PORTC)
    • go to deep sleep using the Pysense library
    • see your Pysense and LoPy wake up when the sensor sets its output high!

    Again, if someone has a better way of wiring this up so that the levels are really correct whether the ESP32 is powered or not, that would be most welcome!

  • @jcaron Sure, making notes of those changes! Thanks!

  • @this-wiederkehr @bucknall

    Ah indeed, I hadn't seen the update. @bucknall, don't forget to update the PNGs as well, they're often out of sync.

    I'm not sure removing the EXT_IOx names or their mention on the module pinout make much sense, they require additional cross-referencing. Keeping the name and having the list of all connections both on the module pinout and the external I/O header pinout would be more useful I think.

    E.g. P8 would have P8 / SD_DAT/ G15 / EXT_IO5 and EXT_IO5 would be labelled EXT_IO5 / P8 / G15 (and possibly SD_DAT)

    This lets you see at once all the uses of any of the pins.

    While you're in there, I think many people would find it helpful if the 3V3 and 5V pins (both on the modules and boards) were labeled as inputs or outputs, with their accepted voltage range and max current requirements for inputs, and max current draw for outputs.

  • @this.wiederkehr @jcaron

    Hi all,

    Apologies for the lack of communication; we've been super busy this month as well as the fact that it is holiday season for many European countries, meaning that a number of our engineers are out of office.

    I'll double-check this with Daniel but I believe that this should now be the correct version of the pinout.

    Thanks for your patience,


  • looks like @bucknall finally correct the pinouts after that topic was brought up several times:

    Although it seems still incomplete:

    I have EXT_IO2 (external I/O header pin 7) connected to RC3 (verified, as I get the same value reading through the ESP32 and the PIC).

  • OK, so still stuck with this, and the lack of any response from Pycom is a little bit frustrating...

    There was another issue with the Pysense library setting ANSELC for most pins which I addressed, but that still isn't enough.

    Now what is hope is the last remaining issue is that the pin is also connected to the ESP32, and with the ESP32 powered down, there's quite a change, as an high signal only results in half the voltage, which I suppose is not enough to be recognised as high by the PIC. Probably something related to the ESP32 no longer providing the internal pull-up when not powered, but I'm not quite sure. I'm also not sure if the PIC provides a pull-up on RC1 or not (there's no register for that for port C).

    Being a software guy and not a hardware guy, I don't have at hand all the necessary stuff to test, but I'll try to find out later today.

    Any hints from anyone more versed on the hardware side of things would of course be most welcome!

  • Still on my ongoing quest for wake-on-pin...

    Finally found the bootloader that is used (, and the checksum that goes with it, so I was able to upload a new firmware version which clears INTF in the ISR.

    There's some progress, as now a change on EXT_IO1 doesn't crash the Pysense even with INTE set, but it's not waking up the board yet...

    So the remaining question is: how does the board get out of sleep? Is there a test for some specific conditions in the ISR which triggers the wake-up, or simply resuming after the SLEEP should be enough? The investigation continues...

    (but @daniel and @bucknall can chime in... It's easier with the source code...)

Log in to reply

Pycom on Twitter