Cat Flap

Posted on
  • Hi,

    I received my V2 Pucks today (thanks Gordon, for sending out so quickly) and I thought I'd have a little play with the new functionality.

    I have been using a motion sensor above the cat flap to monitor our cat going in and out; the problem is I have no way of deducing which direction he's going!

    So I thought I'd stick one of the Pucks on the flap and use the accelerometer to determine whether he's going in or out. It's all coded in, sends a notification much like the original sensor and works a treat!

    It still needs to be cat tested; I hope he still fits through and I hope he doesn't pull it off! I've had to move other Pucks out of his reach as he enjoys batting them around!

    Just a note: Puck.accel() didn't work with 2v05 shipped on the device. Upgraded to cutting edge build and worked fine - probably known by Gordon, but just in case anyone else has trouble! Puck.accelOn and event worked fine though.

    Just thought I'd share!



    Sorry! Should have posted under projects :(

    1 Attachment

    • Cat Flap.jpg
  • That's great! I'd be really interested to see how you get on!

    How exactly did Puck.accel fail on the original firmware? IIRC it may have given you a reading one behind the current one, but I thought it was still ok...

    But even so, using the accelerometer event is definitely preferable :)

  • Sorry, I should have probably been a little more descriptive than "it didn't work"! On the original firmware Puck.accel() returned the object correctly but all the values (X, Y and Z for acc and gyro) were zero. I don't think I called it directly on the device; rather I wrote it to the device each time I tried - so it's probable that it was taking the last reading and there wasn't one...

    It has been a few days now since it's been attached. The cat has always been a little tentative using the cat flap and he's a little more so now there's a Puck right in the middle of it - nonetheless has been using it. Then again, I think I'd be a little tentative too if I had to open doors with my face! I will probably offset it so he's more comfortable.

    I've had a few teething issues. It's quite a distance away sitting at the furthest point in the conservatory - something I didn't initially give much thought too. The signal was a little weak for the RPi to pick up consistently.

    I moved the Pi about a foot and a half along the unit it sits inside the house to be as close as possible to the Puck. I tweaked the Pi enclosure a bit and disabled unnecessary Wifi and Bluetooth on other devices near the Pi to reduce interference. With those tweaks it has been flawless.

    I've been monitoring battery life too and it's depleting faster than I would like, despite running accel at the lowest Hz. E.getBattery() reported 80 right from the get go though - it's now at 60 (accounting for temperature fluctuation). It might just be a bad battery. I was running the same code as the other Pucks which included code to watch the button - I've removed all unnecessary code now to see if there's any improvement.

    It is working though! We get a notification when he comes home, one when he leaves and I write it to his log in our app. I hope the Puck is resilient enough that I can repurpose the motion sensor - drives me nuts when he's just laying next to the cat flap and rolling around in the sun - to which we receive many notifications and if we're out have no idea whether he's in or out without checking camera feeds. When the day comes we're out in the evenings it will be great to know for certain whether he's in or not as the camera doesn't always pick him up.

  • That's great! Interesting about battery use though - if it continues being an issue I'll see if there's anything I can do. I know the accelometer can be configured in a bunch of different ways so potentially it could be told to sleep until moved, avoiding waking the Puck up.

    In terms of the Pi comms, I don't know quite how you're doing it, but I tend to advertise a counter value if I can:

    That means that even if a bunch of readings get lost, it only takes one to get through for the movement to update (it doesn't have to be working at exactly the point the cat goes through the door)

  • If it continues being an issue I'll see if there's anything I can do.
    I know the accelometer can be configured in a bunch of different ways
    so potentially it could be told to sleep until moved, avoiding waking
    the Puck up.

    Ah, thanks! I'll let you know how it goes. To be honest, that's kind of how I expected it to work. When setting up I was a bit shocked to see it take continuous readings. That's initially why I was trying take a single reading so I could mark roughly where I wanted the "action points". Ended up wrapping it straight into a conditional and tweaking the values.

    I do use the counter approach: action and sensor counter. I advertise the light, temperature and battery every 1000ms and update every 5 minutes - as well as the readings themselves, it's just a useful heartbeat to make sure they're working OK.

    When an action occurs: increment the action counter, set the action value, increase the interval to 20ms and set a timeout to revert back to 1000ms and set action to zero. The last point is the problem, and for the cat flap I have increased the amount of time it advertises the action at the faster interval before it resets to 0 to ensure a packet arrives!

    I use the same code across all the Pucks I have: doorbell, our cat logger (single, long and double press for meal, snack and water replacement - so he doesn't get fed twice!), dinner is ready announcement button which plays to all rooms. The other half has a button to turn the kettle on so she can get ready for her shift while it boils, another which works in tandem with a motion sensor to enable a "shower mode" so the light doesn't go off. All kinds of silly little uses really!

    I increase the interval time to account for the doorbell mainly; wanting an instant reaction. I reset the action ID because I burnt myself a few times when I was first creating the receiving script. I didn't account for the script restarting and an action being in place - the doorbell going off at silly o' clock was not great! Scared the s*** out of us!

    Also a similar silly little bug led us to think the cat was pressing the button during the night as we were getting notifications that he was feeding himself! He knows the click it makes and knows he's being fed so we genuinally thought for a bit that he was getting creative - he's more of a dog in the body of a cat...

    Anyway! Yes, I think I'm just about over the "doorbell incident", aha. I could remove that fail-safe now, and you're right it would definitely help.

  • I was just fiddling with this and, maybe you could try this:

    Puck.accelWr(0x15,0x10);  // CTRL6-C - XL_HM_MODE=1, low power accelerometer
    Puck.accelWr(0x16,0x80);  // CTRL6-C - G_HM_MODE=1, low power gyro
    Puck.accelWr(0x0D,0x00); // INT1_CTRL - disable
    Puck.accelWr(0x11,0); // CTRL2_G power down gyro
    //Puck.accelWr(0x10,0x60);  // CTRL1_XL accelerometer 416hz
    Puck.accelWr(0x10,0x20);  // CTRL1_XL accelerometer 26hz
    Puck.accelWr(0x58,0x90); // TAP_CFG - enable irq, add filter
    Puck.accelWr(0x5C,0x02); // WAKE_UP_DUR - very low duration
    Puck.accelWr(0x5B,0x02); // WAKE_UP_THS - low threshold
    Puck.accelWr(0x5E,0x20);  // MD1_CFG wakeup on INT1
    var idleTimeout;
      if (idleTimeout) clearTimeout(idleTimeout);
      else print("Motion", a);
      idleTimeout = setTimeout(function() {
        idleTimeout = undefined;
      // 32 is tilt

    It'll stop the accelerometer from waking up Espruino at all, except when there's movement when it'll give you a series of pulses (picked up by the accel event).

    Should be super low power :)

  • There's some other functionality too - like step counting. All detailed at­on_note/dm00472670-lsm6ds3trc-alwayson-3­d-accelerometer-and-3d-gyroscope-stmicro­electronics.pdf

    I wonder what the best way to expose this is - I'm actually thinking via JS modules rather than built into the firmware. Generally there's a lot of configurability and for the few lines of JS I think it's best to leave it flexible (also to avoid folks having to update firmware!).

  • Thanks for the code. I tried it and whilst it does only fire the event when there's movement, unfortunately there are side effects. In/Out fire with just a fraction of movement when the conditional is set +/- 6000. I've removed the change for now, but it's not working exactly like before. Do I need to reset the register? I'm only sending the code to Puck at the moment, no save.

    You weren't kidding about the level of configurability! I'm torn on modules vs. firmware, but I'd probably agree with you - it would likely be easier to develop and use.

  • Not quite sure I understood? You mean it's too sensitive?

    I've just added a bunch of extra modules for the new Puck.js, as well as one to improve the power usage:­-peripherals

    So you can now do:

    var idleTimeout;
    Puck.on('accel',function(a) {
      if (idleTimeout) clearTimeout(idleTimeout);
      else print("Motion", a);
      idleTimeout = setTimeout(function() {
        idleTimeout = undefined;
    // turn off with require("puckjsv2-accel-movement").off()­;

    If it's too sensitive, you can do:

      threshold : 20,
    // ...

    The default is 2, so you can just change the value to tweak the sensitivity. There's also a duration option which you can set to ignore short-lived movement.

    I've removed the change for now, but it's not working exactly like before. Do I need to reset the register?

    Using your original code? You could try just power cycling it?

  • Not quite sure I understood? You mean it's too sensitive?

    Sorry, let me clarify. My implementation is:

    • If acc.z passes -6000 and flap state is not out then he's exited the house.
    • If acc.z passes 6000 and flap state is not in then he's entered the house.
    • If acc.z is between +/- 2000 and flap state is not closed then the flap has swung close.

    This accounts for reverse swing when the flap shuts, and for us tapping the flap and realising we haven't locked it correctly (4 way locking mechanism).

    By adding your power saving example just gently tapping the puck gives excessive z axis readings. Opening the cat flap just a cm and letting it swing back shows him as: leaving, entering, leaving, entering because the z-axis readings are that extreme.

    I haven't quite got my head around all the options so I'm not quite sure the impact they're having to cause this.

  • What about the alternative of glueing a magnet on the flap and placing puck next to it? You may get more uniq readings. If you cat wears a collar, put an RFID tag or magnet on it. If it is a magnet, make it perpendicular to the earth magnet lines. You then also get the direction and not just the flap action. You still can keep track of the state of in and out and compare with the sequence of movements, of which you get going and going out. It's quite a neat problem, since just sticking the head thru the flag is not-entered or not-left yet. I now do not suggest to also glue a magnet onto the tail...

  • That's interesting - so even if you remove the setTimeout bit from Puck.on('accel',function(a) { and just have your original code, you're getting higher readings?

    I just looked at the code and it seems that the movement example sets the accelerometer to the +/1 2g scale, where Espruino sets it to +/- 4g by default.

    So basically just doubling your values should fix it for you?

  • I've only just had the chance to get back to this. The original battery died not long after my last comment - definitely need these power saving tweaks.

    I'm not using setTimeout, just the original code. Your scale suggestion has fixed the points of which the flap is considered: in, out and closed but I'm still getting consistently strange readings - especially when it shuts - it does the Hokey Cokey.

    I've set it to log: in (+12,000), out (-12,000) and closed (+/- 4,000) and the z axis of which it triggered.

    This was one motion:
    OUT: -12602 -> I moved it very slowly until it crossed the out threshold
    CLOSED: 682 -> I released it, and it dropped to the shut position, with very little rebound.
    IN: 32764 -> This would suggest it practically flew in the opposite direction which it did not.
    CLOSED: -1413 -> It's apparently shut again.


    OUT: -26331 -> Again, moved gently to the out position yet it reads more than twice the required out position.
    CLOSED: -988 -> Closed. No register of in.


    IN: 29674 -> Pulled in, very gently. Again measures very high.
    CLOSED: 3010 -> Closed.
    OUT: -31476 -> Again, you would assume it flew back some way in the opposite direction; it did not.
    CLOSED: -1246 -> Closed, again.

    @allObjects - Thanks for your thoughts! Actually he's microchipped and when we first got him I looked into reading that but never found the time to put anything into action.

    The flap actually has some kind of magnetic mechanism to attach a magnet to a collar, but it doesn't work anymore. It's difficult to get open to find out why without breaking it to pieces. I want to replace the flap entirely to be honest, but it's been so poorly fitted it's a bit of a nightmare to fit a new one. It does have magnets though, so definitely an avenue I could investigate.

    Having said that, the accelerometer seems like an elegant solution and would seem to work perfectly well if I'm able to sort the power issues. He hasn't ever stuck his head through and changed his mind - he has to go through a tunnel (through the brickwork) and it's always a committed motion - but appreciate the point you make.

    I'll put the glue away, for now ;)

  • @Nic, thanks for responding... Ic. Interesting that the cat's behavior in regard of passing the door is transactional (ACID) - and always 'committed'. Enjoyed the following youtube:­-hQ

    Obviously it is a good thing to have a dedicated mc to handle the driving and decoding and bring the interface onto a high level for the application mc. Another solution I found here:­t-door/ Your solution though is very lean!

  • Hmm - well the values are 16 bit signed, so -32768..32767. I guess it's possible that it went overrange and rather than clipping, the accelerometer rolls over?

    Maybe you could try this... It sets the accelerometer range to 4g (what is was before, not 2g)

    on = function(options) {
      options = options||{};
      // motion
      Puck.accelWr(0x15,0x10);  // CTRL6-C - XL_HM_MODE=1, low power
      Puck.accelWr(0x0D,0x00); // INT1_CTRL - disable
      Puck.accelWr(0x11,0); // CTRL2_G power down gyro
      Puck.accelWr(0x10,0x18);  // CTRL1_XL accelerometer 12.5hz, 4g
      Puck.accelWr(0x58,0x90); // TAP_CFG - enable irq, add filter
      Puck.accelWr(0x5C,options.duration||0x02­); // WAKE_UP_DUR - very low duration
      Puck.accelWr(0x5B,options.threshold||0x0­2); // WAKE_UP_THS - low threshold
      Puck.accelWr(0x5E,0x20);  // MD1_CFG wakeup on INT1

    But maybe if that helps it should actually be higher? 8 or 16 g?

  • I tried your suggestion and spent some time over the weekend reading the documentation and trying various things (tried 16g, higher Hz and various other settings that I'm certain I do not understand fully) but I always end up with the same problem as before:

    Out: -6000
    Closed: 0
    In: +6000
    Closed: 0

    Your solution pretty much follows examples they provide, and it even discusses how the first reading is effectively an anomaly and ways you can deal with it - I see the anomaly but it doesn't affect me. Even with 16g I still get some silly high numbers in the opposite direction when it closes.

    The only thing that seems to work is to set 10h back to high power Puck.accelWr(0x15,0x00), whilst leaving all the other settings more or less as you suggested. Then I get as expected:

    OUT: -6308
    CLOSED: 1575

    I thought this might be "good enough", but I'm not convinced the battery will last all that much longer... Am I wrong? Considering the issue is likely to be 'noise' on low power mode I tried normal mode:

    While High-Performance mode guarantees the best performance in terms of noise, Normal mode further reduces the current consumption.

    I don't know if it helped, but it definitely didn't solve the issue. I don't understand the configuration options (slopes/filters/etc) well enough to know if there's a combination that will further reduce the power consumption and work as it does on high performance in reaction to the flap closing.

    If all else fails I can always add a hack solution to ignore an in/out action after the flap is registered closed for a couple of seconds! :-D

    @allObjects - Thanks for sharing the links, really interesting! I'd loved to find some time to do something similar - perhaps even for a time restricted feeder.

  • Wow, that's really odd. Thanks for looking into this!

    I wonder whether in that mode the data reported is somehow the difference in acceleration between that and the last event?

    I guess what you could do is use the first motion event as a trigger to put the accelerometer back into continuous mode so you can get good readings - then after a second or two of inactivity you could drop back to the low power mode again.

    I think you just need something like:

    var idleTimeout;
    Puck.on('accel',function(a) {
      // If cat door is not level, we're not idle
      if (a.acc.x < 15000 && idleTimeout) {
        idleTimeout = undefined;
      if (!idleTimeout) { // first call
        Puck.accelWr(0x0D,3); // INT1_CTRL - Gyro/accel data ready IRQ
        idleTimeout = setTimeout(function() {
          idleTimeout = undefined;
          Puck.accelWr(0x0D,0x00); // INT1_CTRL - disable - movement only
  • I wonder whether in that mode the data reported is somehow the difference in acceleration between that and the last event?

    Ah, perhaps! How are individual events defined? The literal last movement, or a pause from a significant movement?

    I tried your code, and simply added the conditional for the z-axis and nothing more and still got similar results (so I also tried higher G, but similar results too):

    OUT: -12203
    CLOSED: 1539
    IN: 13687
    CLOSED: 3942

    Interestingly just opening the flap fractionally and letting it close produced results like:

    OUT: -32750
    CLOSED: 361
    IN: 32536
    CLOSED: -2171

    With the existing power saving configuration I tried my approach of adding a 5 second lock once an IN/OUT action has triggered. I only ever pushed the flap out and got this:

    OUT: -6809
    CLOSED: -1454
    OUT: -7337
    CLOSED: -1391
    OUT: -9631
    CLOSED: -97
    OUT: -6141
    CLOSED: -433
    OUT: -17430 -> Hugely inflated reading.
    CLOSED: -324
    IN: 7935 -> WHAT!? It has registered an OUT as IN, following the last hugely inflated reading.
    CLOSED: 548
    OUT: -7828
    CLOSED: -1584
    OUT: -7297
    CLOSED: -441
    IN: 7833 -> In registered again, normal out reading previously.
    CLOSED: -1446
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

Cat Flap

Posted by Avatar for Nic @Nic