Pysense accelerometer wake up issues

  • Hi all,

    [Note: there is no category for issues related to the Pysense/Pytrack, or for issues related to the associated libraries]

    I checked out the accelerometer wake up function of the PySense, and wanted to find out the correct values to reliably detect a "knock", and found a few issues:

    • I believe the computation of ACT_THS is simply wrong. Instead of:

        _ths = int((threshold * self.scales[self.full_scale]) / 2000 / 128) & 0x7F

      it should be:

        _ths = int(127 * threshold / self.scales[self.full_scale]) & 0x7F
    • the enable_activity_interrupt is missing bounds checking, for both max and min (below the resolution of the register, which would simply disable the feature):

            if threshold > self.scales[self.full_scale]:
                error = "threshold %d exceeds full scale %d" % (thresold, self.scales[self.full_scale])
                raise ValueError(error)
            if threshold < self.scales[self.full_scale] / 128:
                error = "threshold %d below resolution %d" % (thresold, self.scales[self.full_scale]/128)
                raise ValueError(error)
            if duration > 255 * 1000 * 8 / self.odrs[self.odr]:
                error = "duration %d exceeds max possible value %d" % (duration, 255 * 1000 * 8 / self.odrs[self.odr])
                raise ValueError(error)
            if duration < 1000 * 8 / self.odrs[self.odr]:
                error = "duration %d below resolution %d" % (duration, 1000 * 8 / self.odrs[self.odr])
                raise ValueError(error)
            _ths = int(127 * threshold / self.scales[self.full_scale]) & 0x7F
            _dur = int((duration * self.odrs[self.odr]) / 1000 / 8)
            # Useful for debug
            print("actual threshold: %d (%d)" % (_ths * self.scales[self.full_scale] / 128, _ths))
            print("actual duration: %d (%d)" % (_dur * 8 * 1000 / self.odrs[self.odr], _dur))
    • I haven't found any actual documentation for enable_activity_interrupt or activity (just the examples), but it is worth pointing out (if I understand the ST documentation correctly) that:

      • the duration is not the minimum duration to signal activity (a single sample above the threshold is enough)
      • it is the minimum duration below the threshold to go to inactivity mode (switch down to ODR 10 Hz to save power)
      • in this mode, the accelerometer will draw about 50 µA (according to the doc, I haven't checked the actual power draw yet). In higher modes it's >100 µA
    • it would be worth documenting the set_odr function and the power draw for each of the modes

    • I added a function to manipulate bits in registers, and used it to simplify other methods:

        def set_register(self, register, value, offset, mask):
            reg = bytearray(self.i2c.readfrom_mem(ACC_I2CADDR, register, 1))
            reg[0] &= ~(mask << offset)
            reg[0] |= ((value & mask) << offset)
            self.i2c.writeto_mem(ACC_I2CADDR, register, reg)
        def set_full_scale(self, scale):
            self.set_register(CTRL4_REG, scale, 4, 3)
            self.full_scale = scale
        def set_odr(self, odr):
            self.set_register(CTRL1_REG, odr, 4, 7)
            self.odr = odr

    (and a few other places)

    • adding a function to enable the high-pass filter for output (set bit 2 of register 0x21) is useful to see the values of the accelerations as they are actually used by the activity/inactivity feature:
        def set_high_pass(self, hp):
            self.set_register(CTRL2_REG, 1 if hp else 0, 2, 1)
    • Whatever I do, int_pin() always returns 1. Haven't checked yet if there isn't an issue coming from the PIC configuration (and/or what the PIC sees), will investigate further, but let me know if I missed something.

    EDIT: set_register was missing a ~

  • Hi @jcaron, yesterday I posted some findings about this issue. Today I've discovered that if I comment the line:

    self.set_register(CTRL3_REG, 1, 5, 1)

    in, then my multimeter shows the accelerometer works as its supposed in terms of current consumption: ~130 µA for 5 seconds (inactivity duration) and then ~60 µA. If I shake a little bit the board then the current rises up to ~130 µA (activity detection) and after 5 seconds lowers to ~60 µA again. Off course, the SiPy doesn't wake up because accelerometer's inactivity interrupt (INT1_INACT bit in CTRL3 register) is not enabled (the line I commented), but this reinforces my theory that there is a problem regarding to the way the PIC handles the interruption. I.e., an issue in Pysense/Pytrack firmware or in library.

  • Hi, I'm trying to wake up my SiPy + Pysense setup by using the accelerometer and I'm experimenting the same issues you tell.

    By now I've discovered that if I write

    acc.enable_activity_interrupt(1200, 5000)

    in order to set 5 seconds of inactivity duration, when I measure the current consumption I can see It lowers to ~130 µA (when the boards enter deep sleep mode) by around 5 seconds and then It rises up to ~380 µA. For me, It seems like an issue with module.

  • @sslupsky Setting ODR to 0 will disable the accelerometer, when the whole goal here was to wake up using the accelerometer. Note that power draw is still sub-milliamp, it's just a lot higher than excepted (>300 µA instead of about 50), which drastically reduces battery life...

  • Hi @jcaron I know you are aware of this but for anyone else coming across this topic, @dmayorquin created a topic that discusses setting the ODR to 0 to keep the deep sleep power consumption under 20uA. As I recall the current consumption of the accelerometer is a function of the ODR. So, reducing the ODR will correspondingly reduce the current. In the case where ODR is 0, the accelerometer is disabled and in its sleep power state.

  • Thanks @seb and @jmarcelino! Don't get exactly the same figures, but we're definitely in the same ballpark.

    I'll set this aside for now as I don't quite have the time and it was just a "nice to have" feature, but I suppose this should be investigated further at some point, as the sensor itself should only use 50 µA, not over 300, and it renders the wake-on-accelerometer feature a lot less useful.

    A few things that could be explored:

    • it could be that keeping the accelerometer powered on means that all the other sensors are powered on as well and it is them using the rest of the current
    • it could be the fact that the accelerometer INT pins are set to open drain
    • it could be the powered down ESP32 (which looks like it's connected to ground when powered down) sinking current coming from a pull-up somewhere
    • or it could be that the data sheet is just plain wrong :-)

  • @jcaron

    I got @jmarcelino to run the test on his OTII, and the results confirm what you measured:

    Average consumption with accel wake disabled: 5.19 μA
    With accel wake enabled: 336 μA

  • @jcaron


    I'll add it to my to-do list, I'll get back to you as soon as I can

  • Hi @seb, can you please just confirm that you get the same 380 µA current while in deep sleep with accelerometer wake up enabled?


  • Hi @seb, can you or @jmarcelino or some other member of the team confirm the fact that enabling accelerometer wake up brings the deep sleep power draw to about 380 µA?

    I would really love to be able to use a "knock to activate config mode" feature, but while I can probably accept the 50 µA promised by the ST data sheet, 380 µA is just way too much.

    If it is indeed 380 µA, any way to reduce that?

    I'm wondering if we're not facing the same kind of issue I had when using INT pin wake up on the Pysense, where I ended up cutting the associated pin on the LoPy to make it work reliably.

    Let me know what you find out.


  • Will do.

    One more thing on the topic: I just measured power draw, and if I enable wake-up on accelerometer the deep sleep current goes from about 10 µA to ... 380 µA! Is that something normal in your experience? The ST docs talk about 50 to 180 µA, so there's quite a difference. My EE skills are limited, but isn't that related to the configuration of the INT pins as open drain?

  • @jcaron

    These all look like good changes, if you could do a github pull request I will review all the code properly and check the datasheets to see if, as you explained, the formulas are wrong.

Pycom on Twitter