Qwiic Twist - RGB Rotary Encoder Breakout code function not working correctly

I know no warranty is implied using these encoders as they are very cool. I am having an issue with the Arduino library for these encoders. Specifically the twist.isClicked() function. It appears to be exhibiting switch bounce issues. At times it will return two isClicked events when the switch was pressed once. This seems to maybe be code within the ATTINY84 causing this. These encoders are attached to a Teensy 3.5 programmed with Arduino 1.8.8 and teensydruino 1.45 libs. I know I could maybe use the provided INT pin on the encoder board and maybe bypass this issue. I’m not sure yet if the INT output on the board is correct either as it could, or might be having the same issue where it would output multiple interrupts for just a single press of the encoder’s switch due to noise or bounce. If it is an issue with the ATTINY84 code can it be updated somehow to correct the issue debounce code maybe? Probably I think a new chip would need to be programmed and swapped with the one currently on the board. Unless we could get access to the ATTINY84’s source. Currently this is just a hobby project and would be fine the way it is cause I can put up with it. But if I wanted to build this project for another person I would like it to work correctly.

Thanks for any help it would be greatly appreciated.

The documentation states that:

The INT pin is active low and is open drain output. The interrupt pin will go low when any of the follow events happen:

The user presses the button is pressed or released.

The user has turned the knob and no movement has been detected for a certain amount of time. This amount of time is called the turnInterruptTimeout and is 250ms by default. This means that once the user has stopped turning the knob for 250ms the interrupt will fire. This is helpful when the user is doing lots of knob changes. The Qwiic Twist won’t fire the interrupt until the user has stopped fidgeting. The turnInterruptTimeout is software configurable from 1ms to 65000ms (65 seconds).

What I’m seeing after the user turns the knob and stops is a constant repeating of the interrupt and reset. After turning the knob even just one click and stopping for whatever length of time, it keeps firing off interrupts, and resetting, and never stops unless power is removed and reapplied. I don’t have a digital scope to capture this to display here. I do have a 100mhz analog scope that can see it though. If sparkfun can watch the INT connection on the board with a scope they will see what I mean. After 250ms the interrupt keeps firing and resetting and never stops. This does not seem right to me how it should be working, not to mention an issue with the pressing of the encoder’s switch as well. I’m thinking this is affecting the operation of the pressing of the encoder’s switch. I haven’t tried using the RST connection to reset the device after a interrupt is triggered yet only just removing 3.3v power.

The source for the tiny84 is in the linked GITHUB repository. Looking quickly at it shows physical buttons connected to interrupt pins, and minimal, if any, debouncing…

/mike

Here are some captured images showing the issues I was describing.

The first 2 images are of the pushbutton switch of the encoder in the color blue, and the Interrupt output connection on the breakout board in Red. This shows switch bounce and how it affects the interrupt.

The next 2 images is again same connections as mentioned above but prior to the testing I turned the encoder a click only. The pressed the encoder switch 2 times once for each of the 2 images. These is where I am seeing an almost continuous stream of interrupts after I had turned the encoder one click. If you notice the blue switch waveform, the interrupts for the switch making and breaking contacts do not show up any more when the switch is actuated. Could this stream of interrupts be because of I2C get communications occurring possibly? The last image is in the next following post as I can’t get no more then 3 images as attachments in this reply.

n1ist:
The source for the tiny84 is in the linked GITHUB repository. Looking quickly at it shows physical buttons connected to interrupt pins, and minimal, if any, debouncing…

/mike

Thanks for pointing out the location, I'm briefly looked them over but haven't come up with why I am seeing this behaviour. There is no debouncing for the switch on the encoders from what I saw. The only explanation for the nearly constant steam of interrupts after the encoder is rotated just one click. Documentation mentions that an interrupt is fired from "get" commands from the master device. Possibly from the "getCount" command in a loop.

Just do know why it doesn’t do this prior to the rotating the encoder one click as the “getCount” is always being called in the loop even before I actually rotate it anyways.

Thought I’d do some more testing. Again the Qwiic Twist is connected to a teensy 3.5. I have loaded the Example 1 Basic Readings sketch into the teensy. I commented out Serial.print("Count: ") and Serial.print(twist.getCount()) in the main loop.

The delay in the loop is left at 10ms. I’m triggering my scope on the leading edge from a encoder button press at pin 10 of the ATTINY84. This is my first channel colored in blue. Second scope channel is connected to Int pin 13 of the ATTINY. Result is what is expected in my first uploaded image showing one button press and release.

Second test is same setup as above but now I rotate encoder 1 click and stop and then press the encoder switch and release it. Result is my second uploaded image. The twist.getCount was still commented out. I should not, I believe, be seeing all these interrupts being fired off. I would expect that according to the code in the ATTINY that It should be going into a sleep condition after the encoder switch was being actuated (Rising and falling edge).

Third test is same as the second test except I increased the delay in the loop to 100 ms. Result is the time between interrupts is now at 100ms in the third image I uploaded.

Again, I am not understanding what is causing this constant stream of interrupts being set off. Maybe someone can confirm that this is the expected behavior of this device. The device does work basically just not what I expected. If it’s a code issue from the ATTINY84 I’m not able at this time to reprogram these chips. I currently do not have a programmer for them. I am thinking about making one of my teensy 3.2 into an AVR ISP programmer if I have too.

Sorry for the delay - I wasn’t aware of your posts until this morning. Because of all the ways customers can contact us (chat, email, twitter, forum, github, product comments, product reviews) this one slipped through the cracks.

This is best addressed via an issue creation on github, but I don’t mean to insinuate you should have known that, it (github) is merely the place where I do most of my hardware troubleshooting and bug tracking (and get email notifications ;)). Just an FYI in case you have other issues.

To address some of your issues:

  • There are debounce filters on both the encoder lines. You shouldn’t see jitter on count readings even with many full rotations.

  • There is not hardware debounce on the switch, that’s done in software. Ok, it should be done in software. Looking at my code, there’s no debounce time.

  • Interrupts: The interrupt pin should go low on a state change of either a twist or a button change. Something is going wrong.

To isolate the conundrum, you should be able to disable the twist interrupt by setting bit 0 in 0x04 to zero (https://learn.sparkfun.com/tutorials/qw … gister-map). This will tell us if the button is the primary source of int triggering.

I’ll see if I can replicate what you describe.

Cheers,

-Nathan

Hmm, I’m getting suspicious of the sleep_mode(); call in the main loop (https://github.com/sparkfun/Qwiic_Twist … t.ino#L256). We originally tried to put the ATtiny84 to sleep to save ~1mA but I’m guessing when the ATtiny goes to sleep it may be releasing the INT pin to float back up. When you query the device (I2C event) it triggers an incoming, waking interrupt at which point the ATtiny drives the INT pin low again.

Every time you query the device the interruptIndicated state is reset (https://github.com/sparkfun/Qwiic_Twist … s.ino#L151). I think this is responsible for your third image - ie, the interrupt is re-firing each time you read the device every 100ms.

I haven’t tested it yet, but the solution may be to avoid sleeping the ATtiny.

tldr; Twist is working as expected, though my design is a bit lacking. Use clearInterrupts() to get rid of your INT toggling.

More detail:

When master queries the Twist, inside the I2C ISR the INT pin is released to high impedance (https://github.com/sparkfun/Qwiic_Twist … s.ino#L151) and pin goes high via pull-up. However, the interrupt bits are not cleared in the I2C ISR. We leave this up to the user to do (not ideal, but let’s proceed with how the firmware is designed, for now).

In the main loop, Twist checks the encoder interrupt bit, if it’s set, then drive the INT pin low (https://github.com/sparkfun/Qwiic_Twist … t.ino#L211).

So I believe what you are seeing is the Twist being read by the master, Twist releases its INT pin, then a few cycles later Twist re-assert (pulls down) the INT pin because the encoderInterrupt bit isn’t cleared.

One solution: Call myTwist.clearInterrupts(); after you detect an interrupt.

    void loop() {
      Serial.print("Count: ");
      Serial.print(twist.getCount());
      twist.clearInterrupts();

      if(twist.isPressed()) Serial.print(" Pressed!");

      Serial.println();

      delay(1000);
    }

Another solution: Inside the SparkFun Twist Arduino Library we could clear the encoder interrupt any time a user does a getCount(). This would make things a bit more invisible, tho I worry about confusing users even more than I’ve confused you! :confused:

To further obfuscate what you are experiencing: The interrupt bit is set anytime the encoder is moved. The INT pin is only pulled low after the turnInterruptTimeout has passed. So, there is a situation where if you are polling the Twist faster than turnInterruptTimeout the INT pull will not go low because you are clearing the bit before the main() loop has a chance to drive the INT pin low.

I see the documentation is not clear: https://cdn.sparkfun.com/assets/learn_t … terMap.jpg

I’ll update the reg map, hook up guide, and examples so that it’s more obvious that the user must clear the interrupts.

The Twist firmware could use improvement so if you’ve got suggestions I’m all ears!

Your exactly right, that what I’m seeing is when the encoder is moved it fires an interrupt and will continue until it is cleared. I have other code running as well in the loop for the AdaFruit Trellis 16 button keypad which also uses I2C, and a parallel LCD display. I have a minimum amount of delays in my code too keep it as fast as I can. I already tried the twist.clearInterrupts() before you even mentioned it here. It helps but not entirely. If you clear your interrupts at the beginning of your loop and you perform a getcount, then other code continues to run before the loop ends and restarts you can miss counts if the encoder is moved again. So maybe clear interrupts should be inserted right after you request a getCount? Also, I tried

if (twist.isMoved()){ 
(twist.getCount());}

That seems to keep the interrupts from firing off repeatedly, but It doesn’t give you correct updated counts (misses many counts). I am not doing it that way now. Again, depending on the amount of code in your loop and how fast the encoder is rotating you will miss counts. I have it running pretty good right now with only a few missed counts when the encoder is rotated from 0 to a count of 127.

I have similar issues with twist.isClicked() also it doesn’t always work right. This is more of a bounce issue and can probably be cleaned up with a simple RC filter on the PCB I think or maybe an offboard Schmitt trigger circuit if I can cut traces and bring it offboard and then back in. Just to mention I am using 4 of these encoders in my design. Thank you for your help and assistance it is greatly appreciated.

Noticed that if I clear interrupts right after a getCount() you can miss button presses, or clicks if you have a following isClicked() after it. IsClicked() does not return a result. So you have to be careful where you clear interrupts.

twist.getCount();
twist.clearInterrupts();

//other code
//LCD display and
//usbMIDI.sendControlChange


twist.isClicked(); // no result because it was cleared?

Now, if you put twist.clearInterrupts() at the start of your loop then you would clear any interrupts that may have occurred in the previous loop. You wind up missing counts or button clicks that haven’t been processed. A bit of a conundrum.

Maybe separate clearing encoder interrupts and button Interrupts. Maybe do a twist.ClrEncInterrupts() and twist.ClrButtonInterrupts() by themselves?

Anyway if your going to use the interrupts out connections on the board then probably code changes need to be made. Otherwise it is working pretty good now seeing I don’t make use of that connection.

Do you have a repo where I can see the bulk of your code? I’m confused by:

That seems to keep the interrupts from firing off repeatedly, but It doesn’t give you correct updated counts (misses many counts).

.getCount() is cumulative. You shouldn’t be missing anything regardless of when you read it. You could poll the twist once a second and it shouldn’t miss anything; the ATtiny will just sit there and happily track the encoder.

Button presses are different. If the user presses the button once, and you polled 1s later, you’d see it. If the user presses the button twice and you polled 1s later, you’d see only the indication of the one press.

Maybe separate clearing encoder interrupts and button Interrupts. Maybe do a twist.ClrEncInterrupts() and twist.ClrButtonInterrupts() by themselves?

Yep. I can do that. I’m also planning on implementing a proper state machine so there’s less toggling of the INT pin.

Glad to hear you got it (mostly) working! Let me know if anything else pops up.

Sometimes I have a different way of explaining things that confuses people. What I mean when I say “misses counts” is that a single click of the encoder is not updating the counter. It never skips a number such a 1,2,4,5,6,8. It misses detecting a single click at times. I usually turn the encoder about 1 click every second while watching the count increase from 0 - 127 and then back to 0. At times it would miss and the count would not increase. It wouldn’t be a big deal for me if I’m using it myself, but If I wanted to use these in a consumer product I would be much more picky. I’ve been programming PIC microcontrollers for about 20 years now. I am just getting acquainted with Adruino and also ARM processors.

Yes, It is functioning good right now. I have to add more code to my project to complete it. I’m am pretty satisfied with it. I’ll be interested in any upcoming updates. Thanks for you help.