long time, I wanted to learn about the Encoder, didn't get the explanation that fit my knowledge in electronics and programming. So, this wonderful video makes much easy to understand and to apply it. I really thank you infinitely.
THANK YOU! I was having so much trouble with the Encoder library causing bad reads and trying to convert an 'absolute count' into just direction (all I needed was forward and back). This simple explanation of how to use interrupts to do this allowed me to build a bulletproof direction-only encoder read with less than 20 lines of code.
Thanks, Ron, glad you liked the video. I'm not so sure about the "vast experience" but kind of you to say so anyway! Thanks for the feedback, nice to hear from you.
Thanks great explanation on these little gems. I recently had a 4 month nightmare because one of these did not function properly when installed in a mini oscilloscope. I was posting on forums everywhere I saw one of these built, and I guess I solved it myself, when I came upon one of my posts and read exactly what I posted. So to test my theory, I purchased a few of these, and replaced the one in the scope, and suddenly my nightmare was over, despite I had already purchased another model that was a bit larger and a lot more expensive. So now I have a pocket model and a desktop model and that is actually a good thing.
Can you have too much of a good thing, Jerry? Not when it comes to electronics and Arduino stuff! As you know I have a "proper" bench scope, but my JYE mini scope (video #92) at 1/20th of the price often does the job, especially at the Arduino level we usually are dealing with. I'm glad you got yours sorted out, and as you say, big model + small model =win-win! Thanks for that anecdote Jerry, glad you liked the explanation of Rotary Encoders, and good to hear from you again.
Nice, I always enjoy these classes. I always thought that if a man learns one new thing per day, it will keep his brain active and perhaps, slow the aging process. So thanks for the added time for me on earth, and I assure you I do use most of what you teach in one way or another. Now that money is short, travel is restricted and it takes MONTHS to receive anything from China now, I must work with what I have. Thankfully I have stocked up over the years by using the policy, if I need one item, I order 5. When you get your crap from China it is useful since a lot of it arrives dead or dying. Again, thank you for the wonderful videos it is almost like a visit from a good friend and now that we are locked down in our homes, thanks to our friends in China, such visits are always so very welcome.
Yes, agreed, it is taking forever to get stuff from China now. And no free shipping (to the UK at least). I always buy "a few" of the components, at that price it is worth having backups.
Good day, Ralph. GREAT video and mostly, excellent explanation on just how this rotary encoder works. This is exactly what I was needing to set the temperature on my brother in law's future med cooler. I'll write in private for more details. Meanwhile, have a great day and take care. God bless !!
Oh dear. "Mostly excellent". Damned by faint praise. A case of the curate's egg, I feel. "Good in parts". I await your email with some nervousness, Daniel!
Assuming you ARE actually getting contact bounce, it seems like you'd want to move the "lastInterruptTime" down a few lines (outside the if-clause). Otherwise, you'd still get contact bounce if the dial was not being moved except for vibrations/optical noise that causes the bounce. By moving it outside the if-clause, you remove all contact bounce that occurs within 5ms of the last contact bounce. The way you have it now, only the first bounce starts the timer to disregard an incoming 5ms stream of bounces. If a stream of bounces is 6ms, then you count the last bounce as a pulse. This may not matter depending on the optics/velocity of the encoder. Great video!
An interesting proposed change to the logic, RainHarvester. Let's see. My logic is thus: 1. Grab the current time. 2. Compare time against previously stored time. 3. Have I been here in the last 5 milliseconds since running the ISR logic? Yes - exit. 4. Do the rotary value stuff. 5. Update the stored time with the current time. This means that as long as 5ms has elapsed since last run it would indeed run the ISR logic (eg after 6ms). Your change forces the behaviour to change so that at least 5ms has to elapse since last CALLED (not just run). Now I like this but it MAY lead to unintended behaviour, for which I will use my car's radio as an example. My car's volume control is also a rotary encoder. If I spin it fast it refuses to acknowledge the pulses and just adjusts the volume slightly (as though only one or two pulses got through). So rapidly adjusting the volume to zero by turning the volume control knob rapidly anti-clockwise doesn't work. You have to turn it in a more gently manner for it to respond. This can be annoying as a user, as I know I've turned the volume control 10 clicks (very rapidly) but the volume has only changed from 20 to 18 in that time. This is the behaviour that may be the result of your logic change. Perhaps 5ms is a good value for your logic to work just fine and my car's design might use 20ms which is much slower (and thus less responsive). The long and short of it is that I'm intrigued whether your design would actually work better in Real Life or not. I shall add it to my list of things to investigate when I have a spare nanosecond or two. Thanks very much for posting, it certainly got me thinking long and hard about this, appreciated.
Your suggested fix worked for me. I was getting number skipping when I turned the knob...52,54,56,etc. Per your suggestion, I moved that lastInterruptTime down outside of the loop and now I'm getting exact sequencing of the up and down numbers in the monitor window. I also have added 10k resistors to my pins 3 and 4. BTW I am using a Teensy 3.5 board.
There is a library for rotary encoders but it actually makes more sense to do it this way as you learn a lot more about the functionality of the devices you are attaching to your Arduino. My question is can you expand the interrupt routine to detect more than one rotary encoder as I am working on a project that needs two of them?
+Nick B And you have understood the purpose of my channel perfectly! Of course there are times when I use libraries as a building block of a bigger project but at least if you have gone through the hows and whys then you be a better developer. Regarding your question, NIck, remember the Arduino Uno / Nano has TWO interrupt pins (2,3) (Mega has SIX!) so why not attach each device to its own interrupt pin? That will ensure "separation of concerns" and each interrupt method has a "single responsibility" - easier to code, maintain and understand. Golden rule: an interrupt routine should be lean and mean, no fancy logic and absolutely no Serial.print statements (unless you are debugging, of course). Update a (volatile) variable and get out. Job done. Enjoy my channel and other videos, I just wish I had more time to do more!
Thanks for your response. I was thinking along the pins 2 and 3 line myself. I rarely use Serial.print anyway as most of my Arduino code outputs to a display device of some kind, LCD, OLED etc. I enjoy coding so the challenge of coming up with code to drive other devices isn't really a challenge to me, it is more of a quest for more knowledge and the satisfaction when it works as you intended.
Really nice and clear video, thanks Ralph. I've been wondering what to use my encoder for, you've now given me some ideas. One question i have is why did you attach the interrupt in the LOW rather then FALLING mode, as it would seem from the diagrams that FALLING was the way to do this ? Incidentally I tried it with FALLING, but it gave erratic readings, so clearly LOW is the better choice - would be interested in your thoughts as to why this is. Thanks
Hey, Steve! I had problems getting FALLING working reliably, possibly because of switch bounce. LOW, however, worked reliably as you found out. And when you look at the square waveform LOW / HIGH is what we are really detecting and comparing against. FALLING and RISING (not that you can do the latter except on a few Arduino boards) is not bringing anything to this party. Great question, nice to hear from you.
This is a great piece of code Ralph. Thanks for the instructions and describing the function of it all. I have a question about it in regards to the switch on the encoder. How would we use the switch if we wanted to set a specific input to a stepper motor function? ie; When we hook up 2 different stepper motors to our arduino and want to run a manual set up to perform at set parameters in the loop instead of having to code it each time. Like say we want to set motor A with a start point and an end point then save those setting by clicking in on the switch of the encoder. Then set motor B up with different start and end points with its own encoder(unless there is a way to do it with just one encoder). Afterward when we push a button to run the code it will move each motor to it's individual start and stop points until the user pushes stop.
Hi there, er, Yep That Is Me! Stepper Motors are notoriously difficult to "set" unless you have some sort of end-stop as a reference point. This might be a mechanical switch like a microswitch, or a magnetic Hall Effect sensor, just to name a couple. That is why the question you're asking is difficult. The stepper motor has NO IDEA where it is currently positioned (unlike a servo) so "setting" it is just a matter of counting the individual pulses that you send to the motor to (hopefully) move it to the correct place. And then reverse that by sending a number of "reverse" pulses to move it back again. But it still does not know where it is set to. I recommend you read up the differences between servos and stepper motors and you will find lots of information. Good luck!
Hi Ralph, this video was just what I needed - I am building a shed (my shed in sunny Reading) environment controller with one of the MEGA / ESP8266 boards and am using a rotary encode to scroll and select from a menu on a 3.2tft screen. There are various sensors all of which can be viewed/controlled either automatically or via WiFi (anywhere in the world. Not all the mega pins can be used for interrupts and the screen uses a few and so the pin allocations is a bit of a headache but I'll get there. Any way interesting video. Well done.
Great and thorough video, by far the most useful interrupt video I've seen yet. Unfortunately/weirdly the code doesn't run on WOKWI which is where I'm currently practicing in anticipation of receiving components. The sketch seems to get caught up when you run attachInterrupt with a mode of LOW. I guess because it's simulating a rotary encoder it isn't sure what the pulse is when first initialised... or some other reason I haven't considered. I look forward to trying this out with actual hardware though. Thanks again.
Thanks for a great video. I wanted to learn how a rotary encoder worked, watched some other videos but they didn’t really explain what was going on and how the code actually worked. Great stuff as I think having watched yours I’ve got a handle on it now. It’s a bit of a change from writing ABAP4 for big SAP systems but very interesting to get down in the weeds. Malcolm
Challenges are what makes the success even sweeter, Malcolm! Try a simple sketch to prove you understand Rotary Encoders eg light an LED on the left or right depending on which way you turn the RE. Or, have a strip of Neopixels and wind them up and down using the RE, now that is a challenge (and no peeking at a video that I did to show this). Good luck. Hang on, did you say SAP? Spawn of the devil, you mean. No offense. I'm more of a dot Net person.
That’s exactly where I’m heading. Thought I’d try a pot first as haven’t played with analog stuff yet. Spawn of the devil it may be but I could move seamlessly from client to client and work on/implement systems with 100s - 1000s of users.
Thanks for the video. I couldn't figure out why you were interrupting on Clock going LOW instead of FALLING since your diagrams didn't make it clear that both pins stay HIGH when the encoder isn't moving. Once I checked (I just hooked it up and checked the pins with an LED), it made sense. No more using potentiometers for me.
@@RalphBacon It makes more sense to do the interrupt on LOW if the pin is high unless the shaft is turned. It's probably not the encoders. The clock speed on the Arduino isn't all that high. Thinking about it, the only time it would make sense to trigger on FALLING or RISING would be if the waveform never hit HIGH or LOW (I don't know the range for LOW or HIGH detection). Sine or triangle waves would probably work better, but there would still be some slop in the timing. That probably wouldn't matter for any application the Arduino was suited for.
Hey mate, Great videos! I know this vid was from a while back, but I think I have improved on this code to make the turns 100% accurate and its super simple and easy to understand while still using interrupts. I have coded for the ESP8266 D1 mini (hence the ICACHE_RAM_ATTR in interrupt loop) but it makes for a super accurate way getting the right signal from the rotary encoder. All it takes, is some easy to understand nested while loops! They lock you into the sequence of the encoder and only kick you out of the loop if its an erroneous bounce signal. Let me know what you think. ... const int PinA = 12; const int PinB = 14; bool pinBVal; bool pinAVal; bool turn; int REPosition=0; void setup() { Serial.begin(9600); pinMode(PinA, INPUT_PULLUP); pinMode(PinB, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(PinA), interruptA, FALLING); attachInterrupt(digitalPinToInterrupt(PinB), interruptB, FALLING); } void loop() { delay(1000); Serial.print("Time on: "); Serial.println(millis()/1000); } ICACHE_RAM_ATTR void interruptA(){ turn=0; pinBVal=digitalRead(PinB); pinAVal=digitalRead(PinA); //clockwise while((pinAVal==LOW)&&(pinBVal==HIGH)&&(turn==0)){ //step1 pinBVal=digitalRead(PinB); pinAVal=digitalRead(PinA); while((pinAVal==LOW)&&(pinBVal==LOW)&&(turn==0)){ //step2 pinBVal=digitalRead(PinB); pinAVal=digitalRead(PinA); while((pinAVal==HIGH)&&(pinBVal==LOW)&&(turn==0)){ //step3 pinBVal=digitalRead(PinB); pinAVal=digitalRead(PinA); if ((pinAVal==HIGH)&&(pinBVal==HIGH)&&(turn==0)){ //step4 turn=1; REPosition=REPosition+1; //adding the position Serial.println(REPosition); } } } } } ICACHE_RAM_ATTR void interruptB(){ turn=0; pinBVal=digitalRead(PinB); pinAVal=digitalRead(PinA); //counter-clockwise while((pinBVal==LOW)&&(pinAVal==HIGH)&&(turn==0)){ //step5 pinBVal=digitalRead(PinB); pinAVal=digitalRead(PinA); while((pinBVal==LOW)&&(pinAVal==LOW)&&(turn==0)){ //step6 pinBVal=digitalRead(PinB); pinAVal=digitalRead(PinA); while((pinBVal==HIGH)&&(pinAVal==LOW)&&(turn==0)){ //step7 pinBVal=digitalRead(PinB); pinAVal=digitalRead(PinA); if ((pinBVal==HIGH)&&(pinAVal==HIGH)&&(turn==0)){ //step8 turn=1; REPosition=REPosition-1; //subtracting the position Serial.println(REPosition); } } } } }
That's certainly a unique way of determining rotational direction, Graham. I can't try it right now (I'm editing this week's video) but I'll bookmark it and come back to it later. You say it works until you get a bounce and then kicks you out of the while loop. Then what, start the whole thing again?
@@RalphBacon sorry, I should have explained that better. When you turn the encoder one click, this is how the program handles it... It interrupts the program when PinA goes low. If its a bouncing signal from a terminal creating a connection within the encoder it will not enter any while loops as the next condition (pinB low) won't yet be true,and the interrupt will terminate and restart until a solid low is reached. once a stable ground is established inside the encoder the interrupt will wait until the next step is complete in the process. Once pinB goes low, the code latches into the new while loop. If there is bouncing on pinB the code will only revert back to the previous while loop until a solid pinB low is reached. The same latching while loop happens for the next step in the process which is pinA going back to high. If it bounces the program bounces back and forward between 'while' loops until a solid high is reached. As soon as pinB goes high as well (bouncing high or not), the program is complete and the code adds a +1 to the count variable. I know the code looks a bit inelegant in the RUclips comments but it's much nicer in the ide, and it works flawlessly.
Hey Ralph I hope you are feeling better after the surgery. This is a great video as usual, very clear and well explained. I wanted for a while to control the position of DC motor by means of photo/optical switch. having looked at this video seems very similar concept using 90 degrees phase shift. I wonder whether you could share with us your expertise to control a motor using optical switch. your effort and time always appreciated. Regards Sol
Hi Sol, I did some work using a mechanical switch to control a DC motor (a stepper motor) but you may get some ideas from these videos #38 and #39. Good luck and thanks for your good wishes.
Saving the state of B, by using A not only as an interrupt but also as a clkock input for a D-FlipFlop makes the whole thing much less vulnerable to the internal interrupt timings in the arduino, since the state of B, when turning the knob fast, might already be disapeared to something else than what it was when A changed e.g. "low" when the interrupt-routine tries to qantify the state of B as a reaction to the interrupt on A. --- So a little bit of additional external hardware like a 74HC74 might help a lot storing the actual true value of B when A triggered, as well as some 100nF capacitors between each A and GND as well as B and GND for debouncing the mechanical contact jitter.
Yes, adding a bit of external hardware _would_ undoubtedly help, but this was just an intro and seems to work well enough. I talked about debouncing using external hardware in video #98 which provided a rock-solid solution. Glad this got you thinking (and posting your thoughts), thanks for that.
Just took a look into #98. Nice work! I am not sure if I do like the diode stuff, since this presets the capacitor to some value I have doubt about. I think this is not just a question of the logic family used (HC, HCT, ACT, AHCT, LS. etc.) but also from the switching mechanics characteristics. My expirience is that this depends very much on the type of switch. Even between rotary encoders from different manufacturers there seem to big pretty large differences in switching characteristics (because of varying mechanical solutions used) ... at least from what my Oscilloscope tells me.
The image description at the beginning is wrong or at best miss leading. As you can use interrupt RISING and test the direction being high or low . Or interrupt on FALLING is the reverse of direction being high or low. But well done , it helped me understand the method.
Hi Ralph, Great video as always! I'm working with a filament spooler for my 3d-printer and using PWM output for the servo motor. So far it works fine but when I try to use external interrupt, for another task, the servo motor acts strange. It starts moving to wrong positions. Is it so that the interrupt affect the PWM function? The Arduino I am using is MEGA 2560. Regards Jan
The interrupt will, as the name implies, interrupt anything running, although the actual PWM signal is generated by separate hardware so should not be affected. But if you are moving or adjusting the servo signal then that will be affected by an interrupt; keep them very short and simple and do all the processing in the main loop(). Or use an ESP32 and put essential processing in a separate task (or several tasks) - I did a couple of videos on this.
Hi, I know it's been 4 years but I just started :). Thank you for the videos, they are giving me great opportunities to learn something new. Do you know that millis() does not work inside ISR? This is indirectly the reason FALLING gives erratic readings. I believe switches inside the encoder are bouncing and with millis() not working you are getting multiple falling edges as you cannot ignore them. I'll keep watching your videos. Regards.
Indeed, millis() does not work (the value is never updated) within an ISR but micros() is OK-ish! I think the rotary encoder I used in this video is giving me multiple falling signals (poor quality or dirty contacts) thus it triggers erratically. LOW works because I ignore the same state. One day I will fix this for good! Great to hear from you, Pikor, you now have #190 videos to watch!
Hi Ralph, thanks for the great video! I am currently working on a MIDI Controller for my music (I have the parts but am having trouble ACTUALLY building it🤣). This is the list of items I want to connect to my Arduino Uno: 3x Rotary Encoders (with push-switch function) 3x Linear potentiometers (audio faders) 1x Ultrasonic Sensor. 1x OLED Display (showing the selected MIDI channel and value of the potentiometers) First off - will I even be able to connect all of these parts to the standard Arduino Uno or will I need something with more pins, i.e. the Arduino mega? I got the single rotary encoder to work using your sketch but I am wondering how I would go about connecting the other two and then also including the additional fader potentiometers... Would appreciate it if you could point me in the right direction here, as I'm kind of lost at the moment! (This is my first electronics project so I'm still very new to this all as I'm sure you'll know!) FYI - this is my inspiration for the project (so you get an idea of what I am actually trying to make): nuancescontroller.fr/ Cheers, Calvin
Hey Calvin, the Arduino only has two interrupts so max two rotary encoder. Arduino Mega had many more plus more general purpose up pins. But for a first project this is ambitious; can you create a (much) simpler proof of concept project that runs on a Uno just so you get some experience of what's required?
Great explanation. Must learn more about interrupts on arduino. There is a fantastic library called Rotary. I will try to find the link. works with all encoders, full or half step. Never misses a pulse also.
Hello Arduinite! I'm currently on vacation 5,000 miles away from home so I'll have to be brief with these comments until my return, mid-July. Glad you like the video, Neville, and interrupts are a good topic to master. I've covered them from time to time during my videos. If you find those links please post here!
Ralph thank you so much for your efforts with this video, its the best explanation I have found on this subject. I hope to apply the knowledge you have shared to an Arduino based mechanised fishing system that I am working on. I need the encoder to set upper and lower fishing levels that the equipment can fish between. Thanks again. Allan
Thanks for your kind words, Allan and welcome to my channel. If you're building an Arduino-based project then there are certainly other videos here that may interest you too! Thanks for posting, good to hear from you.
Thankyou. It was the best explanation. The only thing that I am searching now is HOW can I connect a rotary encoder to an arduino. No I don't refer to ebay, chinese or adafruit encoders already mounted on pcb's. I mean encoders bought from tme.eu or digikey, farnell, without any pasive components selected using datasheetd (5v, number of steps, durability, shaft lenghts...etc) because I want variety since I am using them in comercail products. Ok now, WHAT is the use of those passive components on those ebay encoders ? They are there to prevent false readings / skips ? How and why ? Why 2 capacitors are needed ? . Anyway, with your tutorial I am using an encoder Bourns type PEC-11R-4020K-SOO24 and tried also with Alps EC11B20242. Both work fine, . Basicaly you can use whaever encoder on the market if you read the datasheet ( voltage, current, pin configuration are important). I need to mention that YOUR VIDEO here finally helped me after one night wasted - to finaly make them work. Thankyou. I "like" its the least I can do, and it's not enough. You even did it without external libraries or aditional code junk that would use my precious Atmega memory. Thanks alot !
Wow, rev3erse, your appreciation of my efforts is, er, appreciated! There's no reason that other rotary encoders would not work. I'm not sure the one I used had any additional components? Any capacitors *might* be for switch debouncing although you can do that in software too. Anyway, I'm very glad it is all working for you, thanks so much for posting, appreciated.
Very nice demonstration, I think I might borrow some parts of your code for my project. Huge thanks. However I'm confused by the usage of millis() inside the ISR, as from what i remember it can't be used inside the ISR. Pls explain this.
Hello, excellent video, it is the most effective and accurate that I have seen, it works perfect for an encoder, I did it for 6 encoders, but when I moved them, it does not show me any value on the monitor, you know why this is, greetings .
The final code used in this example can be found here: www.dropbox.com/sh/l7bzy5hj46ywa58/AABjo0yydmB5nzeAuK_nPI9da?dl=0 Any problems with accessing that code (or understanding it) please DO let me know!
+Sergio Puentes Torres Hi! A 3-bit resolution will give you only 7 positions - are you sure this is what you meant? This rotary encoder gives more resolution than that. Here's a link to how resolution is defined which may help you describe your requirements better: bit.ly/2cgLQHQ
+Sergio Puentes Torres Well, project guidance for your specific needs is best directed at the Arduino Forum: (forum.arduino.cc/index.php). I would suggest you post your question in the Project Guidance section as there are many excellent and knowledgeable people there who can help you. As you might imagine I just can't spare the time to develop specific solutions but thanks so much for your interest, others might also benefit from your query.
Great video on encoders and it was really educational. I have an Arduino Mega 2560 that I plan on using eight encoders with. I would love to hear your strategy on how to accomplish this. My current strategy is to Mux the 8 A pins and 8 B pins into 2 separate Mux's, using the same 3 address lines for both. Setting the 3 address lines and reading the 2 input lines would happen in a timer interrupt routine. I thought I would use timer zero already running at 1 ms. Instead of looking for transitions I would map the 2 bit Gray code into 2 bit binary and check to see if the number was increasing or decreasing relative to the last read. This would give me the direction. And every time there was a change in value I would either increment or decrement the virtual position. What do you think?
Glad you liked the video; but as for your encoder strategy it sounds dodgy! You have *six* interrupt pins available on a MEGA. Paralleling the encoders up like this is not something I would do. How does your interrupt routine know which rotary encoder has triggered the interrupt? Trying to multiplex various encoder signals onto a timer routine... I mean, what could possibly go wrong? (Spoiler alert: lots). If you really think this would work just try it with two onto a single interrupt routine - and let us know if you have got it to work (reliably)!
Having thought about this overnight (is that sad?) is sounds like you're using Rotary Encoders in the same manner as a keypad matrix with columns/rows. Thus you poll for particular pins (A or B) and determine which Rotary Encoder is being used. Is this the approach you want to take? Unusual but stranger things happen at C. (pun intended).
Ralph, in this example, you have the encoder CLK pin connected to Arduino pin 3, and the DT pin connected to Arduino pin 4, so am I correct in assuming that ONLY the CLK pin needs an interrupt pin? But with the De-Bounce code in video #226, BOTH the CLK and the DT pins need and interrupt pin. Is that correct? Thanks!
You need to detect when the Rotary Encoder is moved - so a single pin/interrupt is sufficient. In video #226 I believe the logic requires both pins to be detected to determine the correct state (or invalid state). These days I tend to use hardware debouncing (I did a video too) as it is 100% reliable and requires no software at all.
Hello RalphGoing through all your videos again.I am confused with a couple of lines of code in the isr of the original sketch.void isr () { 20 static unsigned long lastInterruptTime = 0; 21 unsigned long interruptTime = millis(); . 39 lastInterruptTime = interruptTime; }In line 20 why the "static", what does it do; but mostly, the last thing we do in the isr() is set "lastInterruptTime" on line 39 ready for next interrupt, but next time the interrupt runs the first thing we do is reset "lastInterruptTime" to 0, why don't these statements conflict?Thanks for your time againGreg
A _static_ variable is set to its initial value (if it has one, this one does) and is then _removed_ from the function_ , making it behave almost like a global variable except it can only be accessed by that function. Keeps things much tidier. Thus, line 39 does indeed update the variable with the current millis, but on re-entry to this function line 20 is *not* actioned; that statement was only ever actioned once, the very first time the function was invoked. Does this make sense?
I'm just wanting to know if these cheap ones are mechanical, or magnetic or optical. I'm guessing mechanical because it would be simpler? How often do the contacts wear out?
Cheap rotary encoders are mechanical. The datasheet (if you can find one, Bourns do have one for their encoders) tell you what the MTBF is. Unless you are going mad with it, it's unlikely to wear out. Optical are, of course, better but cost considerably more.
hi Ralph Got it working, sort of. I isolated an unrelated push button to update my OLED counter. Works great. Uncomment the encoder ISR, and it works. but the pushbutton doesn't update at all, or takes forever !!! Screen updates very slowly. Seems I may not be exiting the loop. So where should the ISR code code related for initialization, setup() and loop() sections? Thanks.
Ron, you said previously the switch button is pulled HIGH (to Vcc) when pressed? If so (you must be sure!) you need to change the sketch as I'm assuming the opposite in my sketch, line 56. That switch pin needs to be tied to ground via a 10K resistor and the code line changed to a simple INPUT (not INPUT_PULLUP). Then lines 71 to 73 need amending to look for a HIGH value on that pin, so line 71 and 73 need the "NOT" (exclamation mark) removing because you are looking for a HIGH and not a NOT HIGH (confusing sentence if ever I read one). Regarding "positioning" of routines, it makes no difference. The compiler will find them wherever they are declared. From a Best Practices point of view, all routines (those written by you) should come first, then SETUP then LOOP. But I think you need to get the sketch working first! Have a go with the above and *let me know* (hit REPLY to this message). If you can post your modified sketch to a central cloud storage place (Dropbox, Google, Amazon etc) where I can get hold of it I can see whether what you have done. But please don't post it here! If you have nowhere suitable I'll send you a folder link where you can copy it into.
Thank you, very helpful. I'm new to Arduino and I'm building a button box for my Racing Sim in which I use two encoders and one rotary Switch. I have used your code for one encoder but I don't know how to use it for two encoders. Would you be so kind to help me on this?
Yes, Maurizio, it's easy enough to do. If you have already built the demo circuit for one Rotary Controller you will have used ONE of the TWO interrupt pins available on the UNO. They are pins D2 and D3. So write an identical attachInterrupt statement but this time specify the other pin (so if you have used D2 this time, use D3 in the new statement, or vice versa) and specify (and write) another ISR for the new interrupt pin. Then everything else is pretty much the same. You will be duplicating some code but for a beginner that's easier than trying to be clever and getting bogged down. Have a go, let me know how you get on.
Hi Ralph. Tried again, and got this Start Reset Up :77 and no other writes despite turning. It's as if it leaves the loop and never returns. I switched the encoder pins, as mine are label VCC, L R, Z Gnd, so I assume VCC goes to 5 V, and goes to ground, Z is also marked as the switch on the PCB where the 2 pins are, and L & R are then assumed to be the A and B encoder pins. Still no luck. I noticed the Tx LED only blinks once, which matches with the single "77" value, but certainly not the set of values you get from 50 to 77 which is what I expected. This is baffling. In using a genuine Uno board. My Led goes on (LED anode to pin 11) but doesn't dim or brighten. I assume with each turn, there's an RX/TX flicker to update my computer screen. No such luck.
I've replied to your later response Ron, hope it helps. And it would help me too if you would just hit 'Reply' under the original post (rather than posting a brand new 'thread') so I can find all related responses to the original question, thanks!
Nope, won't work dudekidPL. You could use FALLING as a trigger (I found it less reliable than LOW but your mileage may vary) but you can't escape switch bounce so you would *still* have to check for that to avoid false pulses. There's a more in-depth video on switch bounce in videos #96 & #98 (#96 talks about Rotary Encoders some more, #98 shows you how you CAN remove the 5ms statement if you're willing to add in a bit more hardware). Have a gander and let me know what you think!
Thanks for the quick reply! I will have a look at #96 for more encoder info. I am currently trying to implement a row of encoders into a key matrix. It should work as long as I can get a single encoder to work correctly and then put that interrupt function inside the key matrix. Unfortunately I cant get a stable encoder output so far. I have 5V at the common pin because my key matrix cycles 5v between the columns. I use pull down resistors for the A and B pins. A is connected to an interrupt pin and state of B is then checked inside the ISR to acquire the direction of the square waves. In my head it sounds right but i get crazy outputs.
I got it to work quite well with your code and by connecting the common pin to GND and using the software pull-ups for pins A and B . Is there a way to get similar performance but connecting the common pin to 5V and using physical pull down resistors on pins A and B? my circuit requires this configuration unfortunately
Yes, you can, with a bit of fiddling about (easy). Connect the common to 5v. This means you the pins A & B will be made HIGH as you turn the shaft. The Arduino Uno doesn't have a trigger on HIGH (Arduino Due only), only on RISING so use that in your attachInterrupt statement. Also, you will need to connect 10K resistors from the pins A & B to ground so they are not left floating. The debounce routine is still required in the ISR (using the check for millis() so no change there). Should then work. Let me know if you have problems.
Thanks once again for the quick reply, you're a gentleman! I followed your instructions but used 7.8K pull down resistors instead. I didn't have 10k but that shouldn't have much difference. The debouncing function based on time since last interrupt definitely helps but in this configuration the encoder is still acting crazy. Changing the debouncing period from 5ms to 50ms helped somewhat but I still get many stray signals, not to mention the significant delay introduced. Any suggestions? Could it be down to the encoder itself? maybe the one i am using is designed to have the common pin grounded and that's it?
Thank you so much for this! I hope you don't mind my asking, would this work with multiple encoders? I'd like to add another 3 or 4 (the 32u4 based 'Pro Micro' board I'm using has 5 Digital pins usable for Interrupts). I'm very new to this, but would I define the extra pins ('const int Pin' for each A and B), define a last rotary value ('lastCount') for each encoder, define virtual positions ('volatile int virtualPosition') for each encoder, then replicate the content of the Interrupt ISR section ('if' statements etc) for each encoder, replicate the content of Setup ('pinMode's and 'attachInterrupt') for each encoder, and finally define what you want each encoder to do within the Main Loop? That's how I imagine it might work, but I could be way off! Also would you put all the encoders within the same ISR interupt section, or do you somehow have multiple interrupts? Sorry, I realize this probably isn't a straightforward question! Thanks again!
If you use a 32U4, Tobias, you do indeed have 5 hardware interrupts available on pins 0,1, 2, 3, 7 but you share pins 0 and 1 for RX/TX (sketch upload) so you may have to temporarily disconnect whatever hardware you have on pins 0 & 1 (pin A of a rotary encoder, I assume) to allow the upload to work. It may work "as is". Only one way to know! More... Everything else you say sounds sensible, just keep the ISRs slim (in code terms) and you should be fine. Easy to try this out without too much coding (eg send a Serial message for each, initially). Do ensure you use the construct: attachInterrupt (digitalPinToInterrupt(X)...) where X is the actual pin name (as I gave above). Don't specify the actual interrupt number, it will be a minefield to debug. Good luck!
@@tobiasjames9107 You have the additional pins, but only one ISR can be running at a given time. If you were moving two encoders at the same time, it could miss one or the other occasionally.
@@CrimFerret Thanks so much for pointing that out! As I'm only planning on using this for Photoshop brush size and zoom etc, that won't be a problem. I imagine if you wanted to move two encoders simultaneously (as you might for something like an audio synth or for robotics) you'd maybe need to use an interrupt-capable I2C GPIO expander, which I've read about but don't fully understand haha!
@@tobiasjames9107 The why and how are the important parts to learn. Those who just wire these projects up and go oooh....ahhh over the blinking LEDs are missing the point (I actually know a couple of people who think making LED's blink is what the Arduino is for).
Hello, I have seen your tutorial and I have tried it and works very well, but I got confused how to send that data to blynk so I can monitor the movement? Would you help me to solve my problem please?
Many people misunderstand what the Rotary Encoder is actually doing: all it does is tell you which way the spindle or knob is turning, Joni. There are no values. That is for your sketch to decide that each "step" represents 1 or 50 or 500000. If you use my test sketch and work out what is happening all will become clear - note that I add or subtract a value depending on the rotation direction; no value is being given to the sketch.
Very good no problems understanding the video but on your site you have an example where you attach an interrupt to both pins. I cannot see what the advantage is of having an interrupt on both pins? If an interrupt detects motion on one pin then there must be motion on the other. Why not just poll it to find the direction. Why use two interupts?
Hmm. Good question. I can't remember using two interrupts for a Rotary Encoder (I never have since). Was it just demoing a particular point? I would expect an interrupt for Pin A and in the interrupt a quick read of Pin B which would indicate rotational direction (as you say). Strictly speaking the interrupt should only set a flag and the main loop do the reading but for a demo it was more understandable to do some work in the ISR.
Hi its this one github.com/RalphBacon/RotaryEncoderUpdate/blob/master/RotaryEncoderDualInterrruptsOnChange.ino Its not the code that is the issue but the reasoning behind using two interrupts. I cannot see the advantage it has over your earlier code that uses one interrupt. Since the Uno only has two interupts I would want to use one for the push switch and one for the rotation or use one of the boards that can handle more than 2 interrupts. Many thanks for the excellent series of videos. Regards Dave
Ah ha! So this was the update for the stepless rotary Encoder (which I have come to dislike). I'm wondering what the comments were that "made me ponder" and do this alternative code. Perhaps because there are more steps in a stepless encoder (quarter steps, I believe) so I'm trapping both pin A and Pin B (whichever arrives first) as otherwise we would miss a pulse. As I say, it was just another type of RE and I didn't like it anyway, and now that you've reminded me of the dual interrupts that is the final nail in the coffin. Just use the single interrupt from my original sketch, that works fine. If you use a stepless encoder and you end up with less resolution (ie fewer steps in the full rotation) well, that's the price we pay for not detecting every tiny pulse.
Hi Thanks for the advice, yes a single interrupt for the rotation and another for the push button. I will also need 2 limit switches for my project and I suspect it will be best to use interrupts there as well so I will need to get one of the boards that has more than 2 interrupts. Have you ever looked at interrupt handling on the SM32 family of boards?
a quick question.. for the code that you use, do both pinA and pinB need to be interrupt pins, or is just pinA or pinB enough? and ifso... which? i'm asking as for my project i have only one interrupt pin available.
You are in luck indeed, Matthieu, as only the Pin A is put onto the interrupt. Then the Pin B is read directly from another, non-interrupt GPIO pin when the interrupt fires (not the most efficient interrupt in the world but it works well). I hope this helps!
You only have two interrupt pins on the Uno and related boards, Najeha, so that's how many servos you will be limited to if you are using interrupts to control them. Is this really what you asked?
My homemade Six Digit, 20MHz, CD74HC192 Up-Down counter circuit counts Quadrature Encoder pulses at 20Mhz speeds. And seems to be many times faster than using my PIC16F887 microcontrollers. Can you tell me why?
I suspect it is because your CD74HC192 is a hardware-based solution and your PIC controller relies on software. Hardware implemented solutions will always trump software, even when the software is written very well (no delays, for example). Of course, a very fast µcontroller could match a hardware implementation but that's just compensating for the slower execution. So that's my take on this question, good one that it is, Techis God, thanks for posting.
Not sure about this, Bharti. If you mean that you want to hang a pendulum on the Rotary Encoder so it turns the shaft as you move the object around it could work (especially if you used a stepless version) but really you should be using the device described in vide #76, an MPU6050 3-Axis Accelerometer Gyro which would work a lot better. Have a look at that video and let me know whether it's a good fit for your project.
Hi Ralph. Tried your code. Not working. I only get ST and later ART as in START. Same delay with RE... SET I get the LED to come on sold when I push the encoder switch. Turning it does nothing to the LED and only periodic updates to the screen as to the value. You made it look easy. Any ideas?
Mmm, tricky to diagnose at a distance Ron, but let's have a go. Firstly I can't work out what you mean by the sentence " I only get ST and later ART as in START. Same delay with RE... SET", can you explain that please? Secondly, if the LED comes on but does not fade (you have to turn it counter-clockwise quite a few times to get it fade all the way down) then I suspect that your A/B wiring is not correct. Recheck that. You can verify whether the rotary encoder is doing anything at all by monitoring the output on the Serial Monitor - it will print "Up" or "Down" with a value depending on which way you are turning the knob. Finally, ensure you using the EXACT sketch (no modifications or pin changes or anything) that I supplied otherwise we could be talking at cross purposes!
Hi, I'm trying to make a rotary encoder work to increment a seven segment display. I have the display part working fine, but every way that I have tried to program the rotary encoder, with or without interrupts, the counter goes from max to min as soon as the encoder is moved. Any help? Thanks.
Never mind, I just had to us the pullup resistors for both outputs A and B of the encoder. I'll leave this here in case anyone else has the same problem. Great video. :)
Hi, excellent video. I am looking forward to using a rotary encoder to build a volume control knob. My question is, what happens when the power is turned off or cut off for some reason? How do i store the last value from the rotary encoder form the Arduino board in order to maintain the last value of the knob? Should i use a SD card? Thank you in advance. I apologize for my english, not my native language.
No need for an SD card here, Daniel. Just use the Arduino's built-in EEPROM. Very easy to do, see videos #26 & #27 to see an example (stores a light level) and video #65 on how to use the EEPROM, *very* easy. Any good?
Hi Ralph, thank you very much for such a quick reply. I will look into the videos you have mentioned. Thanks again for the great help and great tutorials!
Hello Ralph, This video is pretty neat explanation of rotary encoder and your code is efficient and light but I have question regarding your 5ms time check in isr routine. Surely you didn't use delay() function because it doesn't work in ISR vector, however in your code you declared the time variables inside ISR function. This will cause them to initialize with 0 every single time isr is called and you would always end up having interrupttime - lastinterrupttime > 5 condition as true. I might not be seeing the point here but if you could explain this 5ms time check for debounce in ISR. Cheers
Many Arduinites get tripped up over this bit of code, Shuaaib, so let me see if I can explain it. The *lastInterruptTime* variable has been declared as *static* which means it is a sort of global variable but _only_ to this function. It doesn't get reset every time. The initialisation occurs just once, the first time. Would it work if this variable were a _true_ global variable? Yes. Is that the _right way_ to do it? No. But hey, life's short. Whatever works for you, works, right? I'm just writing the code to demonstrate Best Practice! I hope this helps your understanding of this code, thanks for posting that question.
Oh, Yes you are right. Totally tripped on that one. Didn't realize, variable was declared static. Normally in embedded systems people are looking for volatile keyword for variables that are used inside ISR. :) Thanks for clarification. Cheers
Good tutorials and good codes.🐹💕🦀🐙💓 Especially I was very impressed with the way to avoid bounce. I managed to understand how to use the rotary encoder.Thank you so much.🐹💕🦀🐙💓
Hi Ralph and thanks for the video. I am not that skilled in C but I am trying... How come that the varible that is static in the interrupt routine doesn't lose its value when the subrutine ends?
Hello Christer, good to hear from you. The whole point of a *static variable* is that although it is declared within a function (subroutine) the compiler pulls it out so that its scope is not constrained to that function's scope - it survives even when the function ends, in much the same way that a global variable survives, except that it can *only* be used by the function that declared it. I think it's better than a global variable as its scope is still constrained (you can't amend or read its value anywhere except in the function it was declared in) but other viewers (no names mentioned but Robert might read this) think that global variables are easier to use - which might be true but are also easier to abuse and introduce bugs! Is this clear? If not, reply to this message and I'll have another go!
How did I get your name wrong in my reply! Doh! I've changed it now, Christer, and I'm glad my response was clear enough. Keep coding, your C++ will only improve.
In principle it works the same way, although I found non-stepped/non-indented more difficult to work with. You still compute which way the rotary encoder is being turned, and how quickly and then have to decide what to do with that information for mapping or gaming. As you are using a Leonardo you could build a virtual keyboard and use the rotary encoder to either issue "Left Key" or "Right Key" signals depending on which way you were rotating the spindle.
@@RalphBacon here my short video. the only problems ks Rotary Encoder while i spin disc is to hard to select song or using as dj scratch ruclips.net/video/fT3u12VrX4Y/видео.html
Im trying to use this and uploaded the latest version unmodified to my Arduino Leonardo. However after it reset it would not become visable and on the Devices page was an "known USB device" I had to reset and try to upload a blank code to get the arduino back. Any ideas? Thanks
+John Hutton Hi John, welcome to the fun and games of Arduino World! Firstly I'd say don't use a Leonardo unless you have a need. Why? Because of the reasons you're now describing. It's a finicky module at the best of times (I've done a video on its clone) and for the first few seconds exposes itself to Windows to allow code uploads, then turns into a HID (Human Interface Device - usually a keyboard or mouse) for the rest of the time. Added to which you might have to (re-) install the drivers if you move it to another USB socket on your PC (or hub). This is the case if it states "Unknown USB device". Try uninstalling it and reinstalling it. Sometimes a reboot of your PC clears things down too but it doesn't take long before things can get out of sync again. Watch my video #30 which talks about my use of the Leonardo clone (called a Sparkfun Pro) and mentions drivers and the like (IIRC correctly). Frankly though, unless you NEED an Leonardo go and get a Nano or Uno when all these problems just disappear.
Hi Ralph, reason I was using Leonardo was to interface with a flight sim (Falcon BMS) and looking at a rotary pulse when clockwise or anti-clockwise to send a key command. any idea how to do that with an uno? Or how I can get it to do so as a joystick? any help appreciated. John
I see, you want to use the Arduino Leonardo as a keyboard/joystick then? In which case you will have to use the Leonardo (or a clone) as it presents itself as such to Windows. However, in your first post you say that AFTER an upload the Leonardo started misbehaving; are you SURE you told the Arduino IDE that you were using an Leonardo (not a UNO or something else)? Otherwise you may have to reflash the Leonardo as its bootloader will have been overwritten. There are some articles on the internet about flashing bootloaders (I've had to do it a couple of times). I can't give you precise instructions (as I don't use a true Leonardo) but the principle is the same. If you have 'bricked' your Leonardo, then this is a good article: learn.sparkfun.com/tutorials/installing-an-arduino-bootloader but it all depends on your exact circumstances in how to get new code (the bootloader) into a device that is no longer being recognised via USB. It's very do-able just take your time and think about the exact steps you need to take (the first one being sure that you have the correct bootloader for the Leonardo). You may need an Arduino UNO to do the uploading to the Leonardo via the ICSP header (for which you will need a cheap cable) but the article describes that too. Good luck with this, treat it as a learning experience. You can do it.
Additionally, there may be more information you might find useful in my video #16 Getting your Arduino connected to your PC. It describes using an FTDI programmer (far simpler than you might think). Of course, this only applies if you have, indeed, bricked your Arduino Leonardo.
Do you mean: can a DC motor turn the rotary encoder and then you do something in your sketch? Or something else? If the former, yes, but it's not designed for a fast speed, about 60 rpm is the expected use (ie one turn per second).
I love your videos from a technical viewpoint, but as a beginner, know absolutely nothing about coding. I get lost when you talk about the sketches. I've studied the Arduino code reference pages, but they're of little help. It's easy to build a project and copy someone else's sketch to make it function, but I'd like to learn how to do a project from idea to hardware to sketch. Any ideas on how to learn coding without someone "giving the answer first" and assuming we understand the code? Every tutorial I've found on the internet works this way, even the so-called "learn to program Arduino" courses. Thanks for providing so many interesting projects!
Hmm. You've got me thinking. Perhaps as part of my BB (Bacon Bytes) series I can include simple (and I mean _simple_ ) coding examples that assume nothing.
+naps1saps Do you mean an example WITHOUT an interrupt? This would only work in a sketch that has been carefully written to have no Serial.print statements, no delays and in a way so that each function (method) is only given a small time slice to do its work - otherwise you could so easily miss that rotary encoder pulse - hence the use of interrupts. If you have run out of interrupts I would seriously suggest you move onto an Arduino Mega which has many more interrupt pins (six, I believe) which would solve your problem. If you are in a part of the world that can buy from the Far East (via eBay, for example) then you can pick up a Mega clone for about $10 or so, very good value.
Hi Ralph, having issues with the serial monitor, lots of random ASCII character garbage is being spat out when turning encoder... any ideas? Great tutorial too, thanks!
+Edward Clarke Welcome to my channel! I suspect that your baud rate is not correct. Check that the Serial WIndow (bottom right hand corner) is set to the same value (eg 9600) as what is in the Serial.begin(9600) statement. You can test this just by sending a standard "Hello World" statement and ensuring that is received correct to begin with. Finally, close and re-open the Arduino IDE as sometimes it gets confused.
Hmm, sorry to hear it's still not working. You are not using digital pins 0 and/or 1 (D0 / D1) on the Arduino board are you, as they are used for the serial communication - if you use those pins for something else then garbage may appear on the serial monitor (or your uploads will fail!). Does the standard "Hello World" appear correctly on the serial monitor? And garbage characters appear only when you turn the encoder?
Actually just fixed it, no idea how... yes I was using the digital pins correctly. I did also try to use a sketch just with some print commands in a loop but that wasn't working at all. Either way, it's all good now. Am quite new to this as you can probably tell! Thanks for all your help, really appreciated :)
Thank you for this tutorial, It'll help me with my music project. As my project has a lot of timed interruptions I also had to add some cli(); and sei(); to block and restore the handling of the interruptions, inside the equivalent of your "if (virtualPosition != lastCount)" in the main loop() function. It may also fix your problem if your "isr" function is not called when intended. But some strange behavior happen: when I add those cli(); and sei(); inside the "isr" function itself the arduino resets everytime it's called. Does anybody know why ?
I'm still investigating. Summary for my investigations: context: - Arduino MEGA 2560 - I don't have the Dupont wire shield, only a plain encoder (the type you see on the pcb but without the pcb), I only use the turn detection pin, not the button for now - I 'm using the INPUT_PULLUP mode on the pins for my arduino to handle power to the encoder, way simpler, less cables - I use tone so my Arduino is constantly using time interrupts in addition to the rotary encoder handling what happens - if interrupt on pinA is in LOW mode -> it works without cli() and sei(), if I add cli and sei inside the interrupt routine it crashes - if interrupt on pinA is in FALLING mode -> i can add cli() and sei(), it doesn't crash, but when turning right I get sometimes "turn left" detection some leads: - maybe the breadboard connection is loose and we detect too much falling on pinA because it's trembling when I turn - maybe the interrupts stack in LOW mode, because of the cli(), too rapidly and that's the reason why it crashes. my workaround : - I'm in LOW mode, I don't use cli and sei, and it works, but I don't understand why, is there a protection for an interrupt function not to interrupt when it's executing itself ?
Ideally, you want to detect FALLING, as LOW will constantly retrigger whilst it is LOW (and requires logic to ignore that). Using sei and cli will not crash the program. Are you using these commands to prevent further retriggering of the interrupt routine you are in or to prevent the other timer interrupts? Are you sure your wiring is not shorting something out? Getting a LEFT signal when turning RIGHT indicates that you are getting switch bounce or turning it too fast - have you allowed for switch bounce as per my demo code? 5mS should be enough.
@@RalphBacon Yes I'm using the rebound protection. The decrease looks parasitic, but I secured the pin and I still got those, when I'm turning to increase it happen like once every 4-5 increase .... When I'm using LOW without sei and cli it's ok, no parasitic decrease. Here is the interrupt routine pastebin.com/NdmyJh4X Here is the consume code for which I detect "-1" on the counter when I switch to FALLING with cli and sei: pastebin.com/r1Cu7PAR If you are curious, full code is available here (may change, I'm currently updating this project on this branch) github.com/DelTa-B/hook-matching/blob/release/demo3/HookMatching/HookMatching.ino What I think is that the arduino MEGA compiler tells to block interrupts when processing a LOW trigger and that a cli in this state crashes. Also my connection is CHA to the left pin, Ground to the middle, CHB to the right pin. CHA is an interrupt pin, CHB is not (but I tested with an interrupt pin and got same result) CHA and CHB are in INPUT_PULLUP mode.
You may need a processor that can handle more interrupts - but if you have 5 rotary encoders you only need 5 interrupts as only ONE pin one each rotary encoder will need to trigger an interrupt.
I don't know about the RasPi when it comes to interrupts; the ESP32 can have any pin as an interrupt but it doesn't have a huge number of pins either; but it might be OK.
Glad you enjoyed the video, Sanjay! Plenty more to learn here, which you will need if you intend to start building Arduino projects (as per your stated plan to me). Enjoy and thanks for posting!
Nice instructional vid. Just a question though, so if I am planning to control 8 motors separately and have rotary encoders to report its position, do I need 8 interrupt pins for it, can I use any pins in arduino to use as interrupt? Thanks in advance.
Hey 3D Printwiz! I wish I had a 3D printer, quite frankly. Anyway, with regards to your question the short answer is "No", you cannot use just any old pin for an interrupt. On the Uno/Nano only pins 2 and 3 can be used as "external" interrupts (like you get from a rotary encoder). On the Arduino MEGA you have more: pin 2 (interrupt 0), 3 (interrupt 1), 18 (interrupt 5), 19 (interrupt 4), 20 (interrupt 3), and 21 (interrupt 2). If you need several interrupts a MEGA can cost very little when cloned from the Far East (I bought mine for less than $10). Additional reading: you might want to read up on the difference between "pin change interrupts" and "external interrupts" as we've been dealing with the latter but the former *may* be of use to you! Thanks for posting this interesting question, I'm sure others will think it useful too.
Thank you so much for your answer, it helps a lot since I am building a desktop robot with a precision of an industrial one, hehe, it feels impossible as I go through the process. As for 3d Printers, I have a Flashforge, but there are a lot of 3d Printers now that are twice as large, and as better, plus cheaper. Get one I, believe me it is worth it.
Getting one is the easy bit. Paying for it is the first challenge. Secondly where do I put it? I have zero space left in my workshop. Build another workshop! Could be an expensive 3D printer! I'd also like a 50W laser cutter so I could cut acrylic for project boxes and the like. Another dream. ;)
Robot Arm looks great just don't let it be seen in Germany (I Am Arm means I Am Poor there). And that soundtrack reminded me of a couple of 1990s raves that were better forgotten. But it's an interesting, working construction and I wish I had a man cave (well, my workshop aka shoe cupboard is my man cave) but all space has been used up. Sigh. One day, one day...
Hi Ralph. A few words from the nit-picking Norwegian here. :) The ATMega328P can indeed do interrupts on almost all its pins. The datasheet says: "The External Interrupts are triggered by the INT pins or any of the PCINT pins". If we take a look at the pinout we see that all of the Arduinos pins (0-13 and the analog pins as well) has a PCINTx label. That's the theory... I'm not sure if you can use the Arduino IDE though. Edit: And you said that in your comment of course... I wasn't paying attention. :)
+Radha Krishna Korlam Hello! Firstly thanks for posting that question. I'm sure others have wondered the same. I'm not sure that a rotary encoder of this design is really suitable for attaching to a wheel (ideally you want an optical sensor) but let's proceed as though it were OK. You simple need to count the pulses received (via the interrupt) during a 1 /10 /60 second interval (depends on how fast the wheel is going round). You can accurately detect the time interval by comparing the current millis() value with the previously stored millis() value from the last loop. When the time exceeds your interval (1 /10 /60 or whatever timeframe you have chosen) then you can calculate (extrapolate) the rpm. Personally I would not use a rotary encoder. I would use a Hall Effect transistor to count pulses from a small magnet attached (very securely!) to a wheel. I have such a video already in an advanced state of preparation but not yet published, but it won't be too long. I'll use this as an example of how a Hall Effect device can be used, so thanks for the suggestion and good luck with your project (or home) work!
Ralph S Bacon Thank you so much for your help sir. I am sure you are one of the best youtubers. And your content too is very helpful too. If I have any doubts with arduino based projects, may I contact you via mail or something else?
+Radha Krishna Korlam Thanks so much for your supporting comments. As regards help with your projects I can recommend the Arduino forum (forum.arduino.cc/) where there are many, many experts just waiting to help. You may even spot me lurking in a corner there too! This is definitely the best approach as I just don't have the time to give specific, bespoke help which I'm sure you understand.That way, I can develop more videos whilst the experts at the forum help newcomers with their projects.
IN NOT SURE BUT I THINK TO GET IT TO WORK ON A SCHEMATIC WITH DIGITAL LOGIC LIKE A 7400 SERIES TYPE LOGIC IT HAVE A PULSE WIDTH LIKE A SAID BELOW BUT I COULD BE COMPLETLY WRONG HERE. LOOKING AT THE PROGRAMMING LIKE IN LISTENING TO AT THIS SECOND UR DESCRIPTION IS PERFECT AND EASILY UNDERSTOOD . THANKS FOR THE VID AGAIN AND IM GOING TO GET A ROTARY ENCODER FOR MY PROJECT.
Yes, do get a rotary encoder and if you have an oscilloscope it would be even better to see the waveforms. As I said below it is the rising edge that determines the direction not the width of the pulse. I'm sure you will find using the actual device will make it all much clearer. Thanks for posting (although we'd all appreciated your posts not being in UPPER CASE as it makes it seem as if you are shouting at me ).
Hi! First of I want to thank you for this video. Helped me a lot with the code for my project. But I have not had the possibility to test it because: When I try to compile it I get an error "'digitalPinToInterrupt' was not declared in this scope". I try to compile your code aswell but get the same error. I'm a n00b at programming and does not know where to look first for a solution. I have IDE 1.8.5 and the forum post I have found suggests a newer IDE (because of a updated arduino.h). Sincerely
Ok. I have now realised that it has to do with the board I want to use. I have to manually fix so it can use the interrupts... I will have to dig a bit deeper before I can give a better answer. Btw the board I want to use is a Digispark ATTiny 85.
Hi Björn and thanks for the support! I'm not even sure the ATTiny85 has hardware interrupt pins the same way as the Uno but you can program it for pin change interrupts. Here's a Stack Exchange article I found that may help you: arduino.stackexchange.com/questions/3929/attiny85-interrupt-id-vs-pin-when-programming-with-arduino but read the comment (correction) by guru Nick Gammon below the answer. And note that the interrupt is triggered on both rising and falling edges.
I have now giving up the idea of using a ATTiny 85. And I have now tried to make it for the UNO. But step into problems again... The attachInterrupt(digitalPinToInterrupt(PinA, isr, LOW); line gives me a error: macro "digitalPinToInterrupt" passed 3 arguments, but takes just 1 And I'm not sure where to find a answer for my problem. Tried google and gives me only results on C++ problems with the macro Min and Max. I'm a real n00b att programming and would just love some pointers that direct me where to look for a solution... I will read up on the link you gave me in later time, when I have understood this code first so I can convert it to a ATTiny 85. Sincerely EDIT: Now I found the problem after even more googleing... It was the format of the line... Missed a ) after PinA... Feels dumb now...
Never feel dumb about making mistakes. It's actually the best way to learn. When you have to write that line again you will remember the syntax (or at least check your brackets!). On that basis I learn a huge amount every day!!! You can guess how many mistakes I make! Anyway, I think you'll find it much easier using an UNO first, at least, before moving over to the Tiny85. Thanks for the update.
I wanted to say that I have managed to get the code working on a UNO now. Works great! But needed some time to figure out how to turn it ON and OFF with the switch. I must thank you again for a very good explaining video. Think I could not managed if it was not for you video. Sincerely
No you don't need to do that Irfan, if you're using the exact same module as I am, because it already has 10K pullup resistors on-board. If you're using a different type of rotary encoder (eg a bare one) then you need to do that, OR even simpler just change the code in the sketch to make the input pins INPUT_PULLUP then it will all work just fine.
Thank you for your respond sir, really appreciate it. btw im using this type of DC motor with encoder www.cytron.io/p-spg30e-30k. this type of encoder i need to put that 1K ohm or just insert the INPUT_PULLUP code?
Looking at their suggested wiring diagram they are definitely connecting a 1K resistor from the pin to +ve (5v). But I would try with INPUT_PULLUP first (that uses a weak internal resistor, 10K to 50K) which might be enough. If it doesn't work as expected use the 1K resistors. It might be that the voltage on those lines is also being used internally in the motor electronics (unlikely but you never know)!
Robotics is not really my thing but you could ask at the Arduino forum (forum.arduino.cc/index.php) as there are certain to be people there who have had experience of this.
Very nice, and professionally presented. Unfortunately, though, the link to the code points into data-Nirvana, in other words, a 404 (page not found) at Dropbox. Could you be so kind and update the link? With best regards
Thanks for letting me know, Volker, I've updated the link in all places, video description and comments. If you experience further problems please do let me know. Thanks for your kinds words too, glad you like the video.
I'm sorry Jerry, I don't quite understand what you mean. Possibly to clear down the sketch in an Arduino Leonardo? Have you uploaded a keyboard emulator and now can't stop it splatting all over your PC?
+Max Foster I'm glad you found it useful. And once you know, it seem so straightforward, doesn't it? Thanks for posting your comment and keep watching!
Sir, actually I want to connect a servo motor with it So,I wanted to know the exact connections of the servo motor,encoder and Arduino Uno board Can you please help me out?
You must learn to walk before you start to run, Suryansh! Do the Rotary Encoder project. Then you will know how they work and what they really do. Then connect up a servo and play with that (unfortunately I don't have a video for that but you're bound to find a good example somewhere, perhaps on the Arduino site). By that time I suspect you will figure out how to connect the two together. As they say, you can teach experience - that's something you must acquire all by yourself. It's a fairly simple requirement you have so should be straightforward to do - good luck.
Sorry about that James, Dropbox has changed the way files can be shared. The new link is in the video description and here: www.dropbox.com/sh/l7bzy5hj46ywa58/AABjo0yydmB5nzeAuK_nPI9da?dl=0 Do let me know if you can't access it.
Hi Ralph, Could you maybe show us a code that would keep the position of the digital switch on power down of the arduino in the EEPROM. Thanks, Love your Videos ..... PS .... and maybe put your sketches at the bottom in the show more. Cheers
Now, Joe, remember that there is no value associated with the position of a Rotary Encoder. Not like a potentiometer, for instance. All it does, as you know, is send a anti/clockwise pulse to your code which then decides what to do. For example, in/decreasing a local variable. So now that we have this variable in your code that is being adjusted, to save it to EEPROM is really easy if you follow video #65 EEPROM basics. Whilst I don't use a Rotary Encoder in that demo, I do use an in/decreasing value from a radio transmitter and save it to EEPROM in exactly the same way as you need to do when using a Rotary Encoder. Have a look and if you have queries post them under that video and I'll respond! And, if I understand your last point correctly I'll put a link to my demo sketches in the video description (is that what you meant?).
Hi Ralph.Ver y good vídeo your my LIKE.Im wondering ir you can share a link vídeo if your already have Or any idea about hoe to do the conect and enconder that doesnt have the PCB .I have seen some vídeos with and with out resistor and with and with out capacitor.I am getting confused.I am trying to make a cockpit forma muy flightsim.Hope u can help.Luis from argentina
Hola Luis, que tal? Here's a picture of the connections required to a bare Rotary Encoder (no PCB): community.axoloti.com/uploads/default/original/2X/f/f656716442794f064bec964b8258ea771cd40360.jpg You can use the NEW method or the OLD method - the NEW method is probably more reliable.
Let's say I want to increase/decrease the value by 1 rotating the encoder, like you just showed, AND by 10 rotating while pressing the button, how I do that?
You mean you are turning it AND pressing the button simultaneously, Marco? If so, do a digital read of the pin that the switch is connected to whilst incrementing (or decrementing) the count, all in the ISR. If the cost (in time, mainly) of doing a digital read is too much, we could connect the switch to another ISR and just monitor its state (using a volatile bool, for example) and then read that variable in the original ISR - much quicker. The UNO only has two interrupts so this would use both of them. Any good?
I think the digitalRead thing is the better way to do it. So I'll put in my code IF digitalRead HIGH increase (or decrease) by 10, ELSE by 1. Thanks for your quick response!
Yes, that would work but it definitely isn't the best way to do it (using another interrupt is) but sometimes pragmatism triumphs over purity. Quite right too. Thanks for your post, let us know how you get on.
Whilst it would most certainly measure the rotational speed and direction of a motor I'm certainly not convinced that it would survive very long unless the motor was used in a very limited way (eg a robot arm). Most motor encoders use an optical method of determining speed and direction (identical concept but not using mechanical switches). So the short answer is "Probably not what you want" and the longer answer is "Find (or build) an optical encoder using an LED and a phototransistor which has not moving parts as such". I hope this helps.
Excellent video, Ralph. Thanks! You are a source for inspiration. There is room for an interesting little C-question in your code, and that made me Google around a bit. We all know that changing a variable in an ISR is not as trivial as changing it elsewhere; it needs to be 'volatile', of course. That is to prevent the compiler optimising it away or put it in a register. In your isr() you have another variable, lastInterruptTime, that should be treated volatile, but you didn't. Instead you made it static. Perfectly fine of course. So, what is the difference between static and volatile? And... Can you use static instead of volatile? Or perhaps do both; "static volatile"? Frankly, I'm not sure. Well, not sure enough to try to make an explanation, that is. ;-)
Hei Robert, hvor er du? For others reading this post, let's think about what "volatile" is trying to do. Global variables (declared outside of any function) can be changed by the ISR. But the compiler is unaware of this and can retrieve the value from its registers without actually reading the data from memory again. Moreover, the compiler optimisation may "mistakenly" remove code if it thinks a condition is always true or false because the variable it is checking has not been marked as volatile (programming error, not an optimisation error). By marking it as volatile the compiler (and optimiser) is now aware the variable's value could suddenly (and unexpectedly) change and always retrieve its true value from memory. Variables used within *and only within* the ISR itself are safe because the compiler knows that's where they are used! Volatile is not needed as they are not accessed elsewhere. As it happens, during the execution of an ISR, millis() is not incremented so that piece of code that tracks the millis() will have the same value at the start of the function as at the end of it. But it seems more logical to update the "lastInterruptTime" at the end of a function to me! So declaring a variable used within an ISR as both static and volatile is not required because the scope of that *static* variable is local (only for the ISR in the first place, ie not global) and is not *volatile* as it's not being used globally. Tricky subject you've raised here Robert! Probably requires more background reading but in broad terms that is my understanding (and my code works so I guess it is not being optimised away or corrupted). Wow! I wonder if anyone else would care to comment on your post?
Google translate did a terrible job there... You asked where I am. I might as well answer that. :) I'm in the middle part of Norway, north of Trondheim. :) The rather rhetorical English question "how are you?", does not exist in the Norwegian language. Typing in "How are you doing" in Google Translate gives a better translation by the way. Enough linguistic digression... ;-) You're absolutely right about volatile and static variables of course. I just felt a bit unconfortable when I saw the variable lastInterruptTime in the ISR and thought "this variable is lost". I didn't notice it was static. A static variable "survives" outside the scope, but is not accessable outside the scope. If if were non-static it would have been lost, and the code would behave strange. I would have made lastInterruptTime global (and volatile of course), but now I realise it's better (and more elegant) to do it your way. Being a C-developer for more than 30 years doesn't mean you've stoped learning. Just ask me! :-)
Robert, hvordan går det? Better? Ha ha! Thank goodness you're fluent in English! Regarding your post, I don't think we ever stop learning, whether that's in C++ or in life in general! It's when we *stop* learning that the rot sets in. I'm sure some beginners find the whole concept of scope, static, volatile and so on quite confusing. I try and not gloss over such things in my demos but I will henceforth take *extra* pains to try and explain such esoteric language concepts for the benefit of one and all. Once understood, the general reaction should be "Is that all it is?".
Better indeed. :) When it comes to computers and development, I think my English is ok. But if you like to discuss knitting, I think I'll pass. "Weft and warp", I have no idea what that means, at least not in a knitting context. :)
That's a shame, my next video was going to be about the weft and warp process for knitting. Knit one, purl one. Oh well, I had better stick to Arduino-related videos then :)
Thanks for posting. However, I'm quite surprised to see that you did not explain the connections from encoder to the Arduino? The tutorial being that elaborate and all? Just a suggestion maid;)
Well now, noilex, the demo sketch used in the demo also contains the pin connection details: PinA (from the encoder) goes to pin 2, PinB goes to pin 4 and pinSW goes to pin 8. We use Pin 2 because the Arduino has interrupt capability on that pin, so we can reliably detect rotary encoder movement. Hope that helps.
Hi Ralph, I have a project where I am using two encoders. I have utilised your code to run the encoders but I have one small problem. Is it possible to contact you via email or some other form so I can explain what is happening? From up north in Doncaster . Cheers Leigh
@Leigh Bucksey Firstly, let me say how great it is that my video has helped you. Makes it all so worthwhile. Now to the issue of 'support'. The BEST place for support is the Arduino forum (forum.arduino.cc/index.php) where there are many experts cleverer than me who can lend a hand, providing you can explain succinctly the issue and what you have done so far in trying to resolve it. And follow the house rules for posting code and the like. As you can imagine, if I tried to do this single-handedly I'd do nothing but answer queries. That said, I'm prepared on this occasion to let you PM me on that forum after you have posted your query so that I am alerted. I should then be able to read your original post within 24 hours, during which time others may already have posted suggestions. I'll do my best then to help, how does that sound? And BTW don't apologise for being up north in Doncaster, someone's go to live there (heh heh)!
Great explanation. My only suggestion is to minimise the isr code to just a flag and process the change in the main loop. bool is_changed = false; int pos = 0; void isr() { static unsigned long last_int_time = 0; if (millis() - last_int_time > 10) { is_changed = true; last_int_time = millis(); } } then in the loop... if (is_changed) { is_changed = false; if (digitalRead(pin_b) == LOW) { pos--; Serial.print(F("Down: ")); Serial.println(pos); } else { pos++; Serial.print(F("Up: ")); Serial.println(pos); } }
+manoftheday1 I'm intrigued as what you mean by 'menu' - and has it got anything to do with the Rotary Encoder? Explain further as I'm always after ideas for videos that could help beginners.
Hmm, interesting that you changed the pins but I'm delighted you got it working! "Simple" is a relative term, what I meant was that once the concept is understood then it becomes much easier to actually implement! Great, thanks for the update.
Learn how to do each item separately. Then think about how you would connect all three methods together. There are no short cuts to doing it properly and learning along the way.
Is there really such a problem as "bouncing"? because i think your encoder is skipping steps because of that 5ms read delay, not because it reads too slowly
Switch bounce exists for just about every switch in existence. My 5mS delay is good enough most of the time to ignore those bounces. It doesn't seem to skip any steps unless you turn the RE _really_ fast (like the volume control in my car).
long time, I wanted to learn about the Encoder, didn't get the explanation that fit my knowledge in electronics and programming. So, this wonderful video makes much easy to understand and to apply it. I really thank you infinitely.
Glad you liked it Naboulsi, thanks for letting me know, always good to get positive feedback!
THANK YOU! I was having so much trouble with the Encoder library causing bad reads and trying to convert an 'absolute count' into just direction (all I needed was forward and back). This simple explanation of how to use interrupts to do this allowed me to build a bulletproof direction-only encoder read with less than 20 lines of code.
Glad I could help you, Rob, and thanks for posting here, nice to hear from you.
Best videos I've found. Very well done, multi screen, clear, well spoken and very kind of you to share your clearly vast experience.
Thanks, Ron, glad you liked the video. I'm not so sure about the "vast experience" but kind of you to say so anyway! Thanks for the feedback, nice to hear from you.
Thanks great explanation on these little gems. I recently had a 4 month nightmare because one of these did not function properly when installed in a mini oscilloscope. I was posting on forums everywhere I saw one of these built, and I guess I solved it myself, when I came upon one of my posts and read exactly what I posted. So to test my theory, I purchased a few of these, and replaced the one in the scope, and suddenly my nightmare was over, despite I had already purchased another model that was a bit larger and a lot more expensive. So now I have a pocket model and a desktop model and that is actually a good thing.
Can you have too much of a good thing, Jerry? Not when it comes to electronics and Arduino stuff! As you know I have a "proper" bench scope, but my JYE mini scope (video #92) at 1/20th of the price often does the job, especially at the Arduino level we usually are dealing with. I'm glad you got yours sorted out, and as you say, big model + small model =win-win! Thanks for that anecdote Jerry, glad you liked the explanation of Rotary Encoders, and good to hear from you again.
Best video on the topic out of dozens i've looked through.
I'm very glad it was helpful, Steve!
Nice, I always enjoy these classes. I always thought that if a man learns one new thing per day, it will keep his brain active and perhaps, slow the aging process. So thanks for the added time for me on earth, and I assure you I do use most of what you teach in one way or another. Now that money is short, travel is restricted and it takes MONTHS to receive anything from China now, I must work with what I have. Thankfully I have stocked up over the years by using the policy, if I need one item, I order 5. When you get your crap from China it is useful since a lot of it arrives dead or dying. Again, thank you for the wonderful videos it is almost like a visit from a good friend and now that we are locked down in our homes, thanks to our friends in China, such visits are always so very welcome.
Yes, agreed, it is taking forever to get stuff from China now. And no free shipping (to the UK at least). I always buy "a few" of the components, at that price it is worth having backups.
I've tried multiple libraries and codes but none were stable! your code works great! and you provide very clear explanations! thank you so much!!
Glad I could help!
Good day, Ralph.
GREAT video and mostly, excellent explanation on just how this rotary encoder works.
This is exactly what I was needing to set the temperature on my brother in law's future med cooler. I'll write in private for more details.
Meanwhile, have a great day and take care. God bless !!
Oh dear. "Mostly excellent". Damned by faint praise. A case of the curate's egg, I feel. "Good in parts". I await your email with some nervousness, Daniel!
Assuming you ARE actually getting contact bounce, it seems like you'd want to move the "lastInterruptTime" down a few lines (outside the if-clause).
Otherwise, you'd still get contact bounce if the dial was not being moved except for vibrations/optical noise that causes the bounce.
By moving it outside the if-clause, you remove all contact bounce that occurs within 5ms of the last contact bounce. The way you have it now, only the first bounce starts the timer to disregard an incoming 5ms stream of bounces. If a stream of bounces is 6ms, then you count the last bounce as a pulse.
This may not matter depending on the optics/velocity of the encoder.
Great video!
An interesting proposed change to the logic, RainHarvester. Let's see. My logic is thus:
1. Grab the current time.
2. Compare time against previously stored time.
3. Have I been here in the last 5 milliseconds since running the ISR logic? Yes - exit.
4. Do the rotary value stuff.
5. Update the stored time with the current time.
This means that as long as 5ms has elapsed since last run it would indeed run the ISR logic (eg after 6ms). Your change forces the behaviour to change so that at least 5ms has to elapse since last CALLED (not just run). Now I like this but it MAY lead to unintended behaviour, for which I will use my car's radio as an example.
My car's volume control is also a rotary encoder. If I spin it fast it refuses to acknowledge the pulses and just adjusts the volume slightly (as though only one or two pulses got through). So rapidly adjusting the volume to zero by turning the volume control knob rapidly anti-clockwise doesn't work. You have to turn it in a more gently manner for it to respond. This can be annoying as a user, as I know I've turned the volume control 10 clicks (very rapidly) but the volume has only changed from 20 to 18 in that time. This is the behaviour that may be the result of your logic change. Perhaps 5ms is a good value for your logic to work just fine and my car's design might use 20ms which is much slower (and thus less responsive).
The long and short of it is that I'm intrigued whether your design would actually work better in Real Life or not. I shall add it to my list of things to investigate when I have a spare nanosecond or two. Thanks very much for posting, it certainly got me thinking long and hard about this, appreciated.
Your suggested fix worked for me. I was getting number skipping when I turned the knob...52,54,56,etc. Per your suggestion, I moved that lastInterruptTime down outside of the loop and now I'm getting exact sequencing of the up and down numbers in the monitor window. I also have added 10k resistors to my pins 3 and 4. BTW I am using a Teensy 3.5 board.
There is a library for rotary encoders but it actually makes more sense to do it this way as you learn a lot more about the functionality of the devices you are attaching to your Arduino. My question is can you expand the interrupt routine to detect more than one rotary encoder as I am working on a project that needs two of them?
+Nick B
And you have understood the purpose of my channel perfectly! Of course there are times when I use libraries as a building block of a bigger project but at least if you have gone through the hows and whys then you be a better developer.
Regarding your question, NIck, remember the Arduino Uno / Nano has TWO interrupt pins (2,3) (Mega has SIX!) so why not attach each device to its own interrupt pin? That will ensure "separation of concerns" and each interrupt method has a "single responsibility" - easier to code, maintain and understand. Golden rule: an interrupt routine should be lean and mean, no fancy logic and absolutely no Serial.print statements (unless you are debugging, of course). Update a (volatile) variable and get out. Job done.
Enjoy my channel and other videos, I just wish I had more time to do more!
Thanks for your response. I was thinking along the pins 2 and 3 line myself. I rarely use Serial.print anyway as most of my Arduino code outputs to a display device of some kind, LCD, OLED etc. I enjoy coding so the challenge of coming up with code to drive other devices isn't really a challenge to me, it is more of a quest for more knowledge and the satisfaction when it works as you intended.
Really nice and clear video, thanks Ralph. I've been wondering what to use my encoder for, you've now given me some ideas. One question i have is why did you attach the interrupt in the LOW rather then FALLING mode, as it would seem from the diagrams that FALLING was the way to do this ? Incidentally I tried it with FALLING, but it gave erratic readings, so clearly LOW is the better choice - would be interested in your thoughts as to why this is. Thanks
Hey, Steve! I had problems getting FALLING working reliably, possibly because of switch bounce. LOW, however, worked reliably as you found out. And when you look at the square waveform LOW / HIGH is what we are really detecting and comparing against. FALLING and RISING (not that you can do the latter except on a few Arduino boards) is not bringing anything to this party. Great question, nice to hear from you.
This is a great piece of code Ralph. Thanks for the instructions and describing the function of it all. I have a question about it in regards to the switch on the encoder. How would we use the switch if we wanted to set a specific input to a stepper motor function? ie; When we hook up 2 different stepper motors to our arduino and want to run a manual set up to perform at set parameters in the loop instead of having to code it each time. Like say we want to set motor A with a start point and an end point then save those setting by clicking in on the switch of the encoder. Then set motor B up with different start and end points with its own encoder(unless there is a way to do it with just one encoder). Afterward when we push a button to run the code it will move each motor to it's individual start and stop points until the user pushes stop.
Hi there, er, Yep That Is Me! Stepper Motors are notoriously difficult to "set" unless you have some sort of end-stop as a reference point. This might be a mechanical switch like a microswitch, or a magnetic Hall Effect sensor, just to name a couple. That is why the question you're asking is difficult. The stepper motor has NO IDEA where it is currently positioned (unlike a servo) so "setting" it is just a matter of counting the individual pulses that you send to the motor to (hopefully) move it to the correct place. And then reverse that by sending a number of "reverse" pulses to move it back again. But it still does not know where it is set to. I recommend you read up the differences between servos and stepper motors and you will find lots of information. Good luck!
Hi Ralph, this video was just what I needed - I am building a shed (my shed in sunny Reading) environment controller with one of the MEGA / ESP8266 boards and am using a rotary encode to scroll and select from a menu on a 3.2tft screen. There are various sensors all of which can be viewed/controlled either automatically or via WiFi (anywhere in the world. Not all the mega pins can be used for interrupts and the screen uses a few and so the pin allocations is a bit of a headache but I'll get there. Any way interesting video. Well done.
Very glad you found this useful, DW, once you see how it works it all makes sense, doesn't it?
Great and thorough video, by far the most useful interrupt video I've seen yet. Unfortunately/weirdly the code doesn't run on WOKWI which is where I'm currently practicing in anticipation of receiving components. The sketch seems to get caught up when you run attachInterrupt with a mode of LOW. I guess because it's simulating a rotary encoder it isn't sure what the pulse is when first initialised... or some other reason I haven't considered. I look forward to trying this out with actual hardware though. Thanks again.
Try using FALLING instead of LOW, as the interrupt will continue to trigger if set to LOW, whereas FALLING fires once then stops.
Thanks for a great video. I wanted to learn how a rotary encoder worked, watched some other videos but they didn’t really explain what was going on and how the code actually worked. Great stuff as I think having watched yours I’ve got a handle on it now. It’s a bit of a change from writing ABAP4 for big SAP systems but very interesting to get down in the weeds.
Malcolm
Challenges are what makes the success even sweeter, Malcolm! Try a simple sketch to prove you understand Rotary Encoders eg light an LED on the left or right depending on which way you turn the RE. Or, have a strip of Neopixels and wind them up and down using the RE, now that is a challenge (and no peeking at a video that I did to show this). Good luck. Hang on, did you say SAP? Spawn of the devil, you mean. No offense. I'm more of a dot Net person.
That’s exactly where I’m heading. Thought I’d try a pot first as haven’t played with analog stuff yet. Spawn of the devil it may be but I could move seamlessly from client to client and work on/implement systems with 100s - 1000s of users.
Thanks for the video. I couldn't figure out why you were interrupting on Clock going LOW instead of FALLING since your diagrams didn't make it clear that both pins stay HIGH when the encoder isn't moving. Once I checked (I just hooked it up and checked the pins with an LED), it made sense. No more using potentiometers for me.
You're welcome! You can try making it work on FALLING too, but I found it glitchy! Perhaps my rotary encoders are a bit "cheap".
@@RalphBacon It makes more sense to do the interrupt on LOW if the pin is high unless the shaft is turned. It's probably not the encoders. The clock speed on the Arduino isn't all that high. Thinking about it, the only time it would make sense to trigger on FALLING or RISING would be if the waveform never hit HIGH or LOW (I don't know the range for LOW or HIGH detection). Sine or triangle waves would probably work better, but there would still be some slop in the timing. That probably wouldn't matter for any application the Arduino was suited for.
Hey mate, Great videos! I know this vid was from a while back, but I think I have improved on this code to make the turns 100% accurate and its super simple and easy to understand while still using interrupts. I have coded for the ESP8266 D1 mini (hence the ICACHE_RAM_ATTR in interrupt loop) but it makes for a super accurate way getting the right signal from the rotary encoder. All it takes, is some easy to understand nested while loops! They lock you into the sequence of the encoder and only kick you out of the loop if its an erroneous bounce signal. Let me know what you think.
...
const int PinA = 12;
const int PinB = 14;
bool pinBVal;
bool pinAVal;
bool turn;
int REPosition=0;
void setup() {
Serial.begin(9600);
pinMode(PinA, INPUT_PULLUP);
pinMode(PinB, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PinA), interruptA, FALLING);
attachInterrupt(digitalPinToInterrupt(PinB), interruptB, FALLING);
}
void loop() {
delay(1000);
Serial.print("Time on: ");
Serial.println(millis()/1000);
}
ICACHE_RAM_ATTR void interruptA(){
turn=0;
pinBVal=digitalRead(PinB);
pinAVal=digitalRead(PinA); //clockwise
while((pinAVal==LOW)&&(pinBVal==HIGH)&&(turn==0)){ //step1
pinBVal=digitalRead(PinB);
pinAVal=digitalRead(PinA);
while((pinAVal==LOW)&&(pinBVal==LOW)&&(turn==0)){ //step2
pinBVal=digitalRead(PinB);
pinAVal=digitalRead(PinA);
while((pinAVal==HIGH)&&(pinBVal==LOW)&&(turn==0)){ //step3
pinBVal=digitalRead(PinB);
pinAVal=digitalRead(PinA);
if ((pinAVal==HIGH)&&(pinBVal==HIGH)&&(turn==0)){ //step4
turn=1;
REPosition=REPosition+1; //adding the position
Serial.println(REPosition);
}
}
}
}
}
ICACHE_RAM_ATTR void interruptB(){
turn=0;
pinBVal=digitalRead(PinB);
pinAVal=digitalRead(PinA); //counter-clockwise
while((pinBVal==LOW)&&(pinAVal==HIGH)&&(turn==0)){ //step5
pinBVal=digitalRead(PinB);
pinAVal=digitalRead(PinA);
while((pinBVal==LOW)&&(pinAVal==LOW)&&(turn==0)){ //step6
pinBVal=digitalRead(PinB);
pinAVal=digitalRead(PinA);
while((pinBVal==HIGH)&&(pinAVal==LOW)&&(turn==0)){ //step7
pinBVal=digitalRead(PinB);
pinAVal=digitalRead(PinA);
if ((pinBVal==HIGH)&&(pinAVal==HIGH)&&(turn==0)){ //step8
turn=1;
REPosition=REPosition-1; //subtracting the position
Serial.println(REPosition);
}
}
}
}
}
That's certainly a unique way of determining rotational direction, Graham. I can't try it right now (I'm editing this week's video) but I'll bookmark it and come back to it later. You say it works until you get a bounce and then kicks you out of the while loop. Then what, start the whole thing again?
@@RalphBacon sorry, I should have explained that better.
When you turn the encoder one click, this is how the program handles it...
It interrupts the program when PinA goes low. If its a bouncing signal from a terminal creating a connection within the encoder it will not enter any while loops as the next condition (pinB low) won't yet be true,and the interrupt will terminate and restart until a solid low is reached. once a stable ground is established inside the encoder the interrupt will wait until the next step is complete in the process. Once pinB goes low, the code latches into the new while loop. If there is bouncing on pinB the code will only revert back to the previous while loop until a solid pinB low is reached. The same latching while loop happens for the next step in the process which is pinA going back to high. If it bounces the program bounces back and forward between 'while' loops until a solid high is reached. As soon as pinB goes high as well (bouncing high or not), the program is complete and the code adds a +1 to the count variable. I know the code looks a bit inelegant in the RUclips comments but it's much nicer in the ide, and it works flawlessly.
Hey Ralph I hope you are feeling better after the surgery.
This is a great video as usual, very clear and well explained.
I wanted for a while to control the position of DC motor by means of photo/optical switch. having looked at this video seems very similar concept using 90 degrees phase shift. I wonder whether you could share with us your expertise to control a motor using optical switch. your effort and time always appreciated.
Regards
Sol
Hi Sol, I did some work using a mechanical switch to control a DC motor (a stepper motor) but you may get some ideas from these videos #38 and #39. Good luck and thanks for your good wishes.
Saving the state of B, by using A not only as an interrupt but also as a clkock input for a D-FlipFlop makes the whole thing much less vulnerable to the internal interrupt timings in the arduino, since the state of B, when turning the knob fast, might already be disapeared to something else than what it was when A changed e.g. "low" when the interrupt-routine tries to qantify the state of B as a reaction to the interrupt on A. --- So a little bit of additional external hardware like a 74HC74 might help a lot storing the actual true value of B when A triggered, as well as some 100nF capacitors between each A and GND as well as B and GND for debouncing the mechanical contact jitter.
Yes, adding a bit of external hardware _would_ undoubtedly help, but this was just an intro and seems to work well enough. I talked about debouncing using external hardware in video #98 which provided a rock-solid solution. Glad this got you thinking (and posting your thoughts), thanks for that.
Just took a look into #98. Nice work!
I am not sure if I do like the diode stuff, since this presets the capacitor to some value I have doubt about. I think this is not just a question of the logic family used (HC, HCT, ACT, AHCT, LS. etc.) but also from the switching mechanics characteristics. My expirience is that this depends very much on the type of switch. Even between rotary encoders from different manufacturers there seem to big pretty large differences in switching characteristics (because of varying mechanical solutions used) ... at least from what my Oscilloscope tells me.
The image description at the beginning is wrong or at best miss leading. As you can use interrupt RISING and test the direction being high or low . Or interrupt on FALLING is the reverse of direction being high or low. But well done , it helped me understand the method.
Glad it helped and sorry for any confusion!
Thanks, I got it. I tested it as it is and it worked great, then tailored to my project. Great job Ralph.
Thanks for letting me know, James, it is so annoying when Dropbox changes the location.
yes, I know what you mean. Change is one thing that never changes .
Hi Ralph, Great video as always!
I'm working with a filament spooler for my 3d-printer and using PWM output for the servo motor. So far it works fine but when I try to use external interrupt, for another task, the servo motor acts strange. It starts moving to wrong positions.
Is it so that the interrupt affect the PWM function?
The Arduino I am using is MEGA 2560.
Regards
Jan
The interrupt will, as the name implies, interrupt anything running, although the actual PWM signal is generated by separate hardware so should not be affected.
But if you are moving or adjusting the servo signal then that will be affected by an interrupt; keep them very short and simple and do all the processing in the main loop().
Or use an ESP32 and put essential processing in a separate task (or several tasks) - I did a couple of videos on this.
Hi, I know it's been 4 years but I just started :). Thank you for the videos, they are giving me great opportunities to learn something new. Do you know that millis() does not work inside ISR? This is indirectly the reason FALLING gives erratic readings. I believe switches inside the encoder are bouncing and with millis() not working you are getting multiple falling edges as you cannot ignore them.
I'll keep watching your videos. Regards.
Indeed, millis() does not work (the value is never updated) within an ISR but micros() is OK-ish! I think the rotary encoder I used in this video is giving me multiple falling signals (poor quality or dirty contacts) thus it triggers erratically. LOW works because I ignore the same state. One day I will fix this for good! Great to hear from you, Pikor, you now have #190 videos to watch!
Hi Ralph, thanks for the great video! I am currently working on a MIDI Controller for my music (I have the parts but am having trouble ACTUALLY building it🤣).
This is the list of items I want to connect to my Arduino Uno:
3x Rotary Encoders (with push-switch function)
3x Linear potentiometers (audio faders)
1x Ultrasonic Sensor.
1x OLED Display (showing the selected MIDI channel and value of the potentiometers)
First off - will I even be able to connect all of these parts to the standard Arduino Uno or will I need something with more pins, i.e. the Arduino mega?
I got the single rotary encoder to work using your sketch but I am wondering how I would go about connecting the other two and then also including the additional fader potentiometers...
Would appreciate it if you could point me in the right direction here, as I'm kind of lost at the moment! (This is my first electronics project so I'm still very new to this all as I'm sure you'll know!)
FYI - this is my inspiration for the project (so you get an idea of what I am actually trying to make):
nuancescontroller.fr/
Cheers,
Calvin
Hey Calvin, the Arduino only has two interrupts so max two rotary encoder. Arduino Mega had many more plus more general purpose up pins. But for a first project this is ambitious; can you create a (much) simpler proof of concept project that runs on a Uno just so you get some experience of what's required?
VERY VERY VERY VERY INTERESTING, GOOD TO SEE THIS AND WHEN I TESTED IT'S PERFECT without skipping steps ,simply 👌👍
Glad you liked it!
@@RalphBacon I loved, its very helpful in my project thank you very much 😊
Great explanation. Must learn more about interrupts on arduino. There is a fantastic library called Rotary. I will try to find the link. works with all encoders, full or half step. Never misses a pulse also.
Hello Arduinite! I'm currently on vacation 5,000 miles away from home so I'll have to be brief with these comments until my return, mid-July.
Glad you like the video, Neville, and interrupts are a good topic to master. I've covered them from time to time during my videos. If you find those links please post here!
Ralph thank you so much for your efforts with this video, its the best explanation I have found on this subject.
I hope to apply the knowledge you have shared to an Arduino based mechanised fishing system that I am working on. I need the encoder to set upper and lower fishing levels that the equipment can fish between.
Thanks again.
Allan
Thanks for your kind words, Allan and welcome to my channel. If you're building an Arduino-based project then there are certainly other videos here that may interest you too! Thanks for posting, good to hear from you.
Thankyou. It was the best explanation. The only thing that I am searching now is HOW can I connect a rotary encoder to an arduino. No I don't refer to ebay, chinese or adafruit encoders already mounted on pcb's. I mean encoders bought from tme.eu or digikey, farnell, without any pasive components selected using datasheetd (5v, number of steps, durability, shaft lenghts...etc) because I want variety since I am using them in comercail products. Ok now, WHAT is the use of those passive components on those ebay encoders ? They are there to prevent false readings / skips ? How and why ? Why 2 capacitors are needed ? . Anyway, with your tutorial I am using an encoder Bourns type PEC-11R-4020K-SOO24 and tried also with Alps EC11B20242. Both work fine,
. Basicaly you can use whaever encoder on the market if you read the datasheet ( voltage, current, pin configuration are important). I need to mention that YOUR VIDEO here finally helped me after one night wasted - to finaly make them work. Thankyou. I "like" its the least I can do, and it's not enough. You even did it without external libraries or aditional code junk that would use my precious Atmega memory. Thanks alot !
Wow, rev3erse, your appreciation of my efforts is, er, appreciated! There's no reason that other rotary encoders would not work. I'm not sure the one I used had any additional components? Any capacitors *might* be for switch debouncing although you can do that in software too. Anyway, I'm very glad it is all working for you, thanks so much for posting, appreciated.
Very nice demonstration, I think I might borrow some parts of your code for my project. Huge thanks. However I'm confused by the usage of millis() inside the ISR, as from what i remember it can't be used inside the ISR. Pls explain this.
You can _use_ millis inside an interrupt function; they just won't get _updated_ there. As all I'm doing is taking a snapshot, that's fine.
Thank for this Program. This works perfect. Other Programs do not because the bouncing makes troubles.
Quite so. I might do an update on this soon so keep tuned.
@@RalphBacon Thanks it works for about 99%. Sometime it loos one step, sometime it doubles the puls.
Will wait for the update.
Hello, excellent video, it is the most effective and accurate that I have seen, it works perfect for an encoder, I did it for 6 encoders, but when I moved them, it does not show me any value on the monitor, you know why this is, greetings .
Remember that each encoder requires a hardware interrupt pin - the Arduino UNO only has two, pins 2 and 3, could this be the problem?
The final code used in this example can be found here: www.dropbox.com/sh/l7bzy5hj46ywa58/AABjo0yydmB5nzeAuK_nPI9da?dl=0
Any problems with accessing that code (or understanding it) please DO let me know!
One question please..If I want to make an encoder of 3 bits of resolution, how do I do it?
+Sergio Puentes Torres
Hi! A 3-bit resolution will give you only 7 positions - are you sure this is what you meant? This rotary encoder gives more resolution than that. Here's a link to how resolution is defined which may help you describe your requirements better:
bit.ly/2cgLQHQ
Yes, it is to better understand it. I want it simple, with only 7 positions. Please
Like this video with photo sensors
ruclips.net/video/XIUrnR8bLAI/видео.html
My issue is the code using arduino
+Sergio Puentes Torres
Well, project guidance for your specific needs is best directed at the Arduino Forum: (forum.arduino.cc/index.php). I would suggest you post your question in the Project Guidance section as there are many excellent and knowledgeable people there who can help you. As you might imagine I just can't spare the time to develop specific solutions but thanks so much for your interest, others might also benefit from your query.
Great video on encoders and it was really educational. I have an Arduino Mega 2560 that I plan on using eight encoders with. I would love to hear your strategy on how to accomplish this. My current strategy is to Mux the 8 A pins and 8 B pins into 2 separate Mux's, using the same 3 address lines for both. Setting the 3 address lines and reading the 2 input lines would happen in a timer interrupt routine. I thought I would use timer zero already running at 1 ms. Instead of looking for transitions I would map the 2 bit Gray code into 2 bit binary and check to see if the number was increasing or decreasing relative to the last read. This would give me the direction. And every time there was a change in value I would either increment or decrement the virtual position. What do you think?
BTW The use case is knob twiddling on synthesizers. So the encoders are for human hands and most likely only two will be used at any given time.
Glad you liked the video; but as for your encoder strategy it sounds dodgy! You have *six* interrupt pins available on a MEGA. Paralleling the encoders up like this is not something I would do. How does your interrupt routine know which rotary encoder has triggered the interrupt? Trying to multiplex various encoder signals onto a timer routine... I mean, what could possibly go wrong? (Spoiler alert: lots). If you really think this would work just try it with two onto a single interrupt routine - and let us know if you have got it to work (reliably)!
Having thought about this overnight (is that sad?) is sounds like you're using Rotary Encoders in the same manner as a keypad matrix with columns/rows. Thus you poll for particular pins (A or B) and determine which Rotary Encoder is being used. Is this the approach you want to take? Unusual but stranger things happen at C. (pun intended).
Thanks for your help. Finally my project works as it should. Thanks again!
I'm delighted to have been of help, Pete. Thanks for posting.
this is a solid video that explains it really well
Thanks, Alan, glad you liked it.
Thank you. Planning out a MAME spinner, and I think this will suit.
Yes, it should work out nicely 👍🏻
Ralph, in this example, you have the encoder CLK pin connected to Arduino pin 3, and the DT pin connected to Arduino pin 4, so am I correct in assuming that ONLY the CLK pin needs an interrupt pin? But with the De-Bounce code in video #226, BOTH the CLK and the DT pins need and interrupt pin. Is that correct? Thanks!
You need to detect when the Rotary Encoder is moved - so a single pin/interrupt is sufficient.
In video #226 I believe the logic requires both pins to be detected to determine the correct state (or invalid state).
These days I tend to use hardware debouncing (I did a video too) as it is 100% reliable and requires no software at all.
@@RalphBacon Thanks Ralph! That clears it up for me. Regards!
Hello RalphGoing through all your videos again.I am confused with a couple of lines of code in the isr of the original sketch.void isr () {
20 static unsigned long lastInterruptTime = 0;
21 unsigned long interruptTime = millis();
.
39 lastInterruptTime = interruptTime;
}In line 20 why the "static", what does it do; but mostly, the last thing we do in the isr() is set "lastInterruptTime" on line 39 ready for next interrupt, but next time the interrupt runs the first thing we do is reset "lastInterruptTime" to 0, why don't these statements conflict?Thanks for your time againGreg
A _static_ variable is set to its initial value (if it has one, this one does) and is then _removed_ from the function_ , making it behave almost like a global variable except it can only be accessed by that function. Keeps things much tidier. Thus, line 39 does indeed update the variable with the current millis, but on re-entry to this function line 20 is *not* actioned; that statement was only ever actioned once, the very first time the function was invoked.
Does this make sense?
I'm just wanting to know if these cheap ones are mechanical, or magnetic or optical. I'm guessing mechanical because it would be simpler? How often do the contacts wear out?
Cheap rotary encoders are mechanical. The datasheet (if you can find one, Bourns do have one for their encoders) tell you what the MTBF is. Unless you are going mad with it, it's unlikely to wear out. Optical are, of course, better but cost considerably more.
Great video mate, just what I needed for my next project :-)
Good luck with your project, Fred Jones and thanks for posting.
elaborate,helpful & to the point. very nice
Thanks, Pasha, glad you enjoyed it!
hi Ralph Got it working, sort of. I isolated an unrelated push button to update my OLED counter. Works great. Uncomment the encoder ISR, and it works. but the pushbutton doesn't update at all, or takes forever !!! Screen updates very slowly. Seems I may not be exiting the loop. So where should the ISR code code related for initialization, setup() and loop() sections? Thanks.
Ron, you said previously the switch button is pulled HIGH (to Vcc) when pressed? If so (you must be sure!) you need to change the sketch as I'm assuming the opposite in my sketch, line 56. That switch pin needs to be tied to ground via a 10K resistor and the code line changed to a simple INPUT (not INPUT_PULLUP). Then lines 71 to 73 need amending to look for a HIGH value on that pin, so line 71 and 73 need the "NOT" (exclamation mark) removing because you are looking for a HIGH and not a NOT HIGH (confusing sentence if ever I read one).
Regarding "positioning" of routines, it makes no difference. The compiler will find them wherever they are declared. From a Best Practices point of view, all routines (those written by you) should come first, then SETUP then LOOP. But I think you need to get the sketch working first! Have a go with the above and *let me know* (hit REPLY to this message).
If you can post your modified sketch to a central cloud storage place (Dropbox, Google, Amazon etc) where I can get hold of it I can see whether what you have done. But please don't post it here! If you have nowhere suitable I'll send you a folder link where you can copy it into.
Thank you, very helpful. I'm new to Arduino and I'm building a button box for my Racing Sim in which I use two encoders and one rotary Switch. I have used your code for one encoder but I don't know how to use it for two encoders. Would you be so kind to help me on this?
Yes, Maurizio, it's easy enough to do. If you have already built the demo circuit for one Rotary Controller you will have used ONE of the TWO interrupt pins available on the UNO. They are pins D2 and D3. So write an identical attachInterrupt statement but this time specify the other pin (so if you have used D2 this time, use D3 in the new statement, or vice versa) and specify (and write) another ISR for the new interrupt pin. Then everything else is pretty much the same. You will be duplicating some code but for a beginner that's easier than trying to be clever and getting bogged down. Have a go, let me know how you get on.
Hi Ralph. Tried again, and got this
Start
Reset
Up :77
and no other writes despite turning. It's as if it leaves the loop and never returns. I switched the encoder pins, as mine are label VCC, L R, Z Gnd, so I assume VCC goes to 5 V, and goes to ground, Z is also marked as the switch on the PCB where the 2 pins are, and L & R are then assumed to be the A and B encoder pins. Still no luck. I noticed the Tx LED only blinks once, which matches with the single "77" value, but certainly not the set of values you get from 50 to 77 which is what I expected. This is baffling. In using a genuine Uno board. My Led goes on (LED anode to pin 11) but doesn't dim or brighten. I assume with each turn, there's an RX/TX flicker to update my computer screen. No such luck.
I've replied to your later response Ron, hope it helps. And it would help me too if you would just hit 'Reply' under the original post (rather than posting a brand new 'thread') so I can find all related responses to the original question, thanks!
instead of triggering the ISR when the pin is LOW you could trigger it with the FALLING condition and get rid of that 'less than 5ms' if statement?
Nope, won't work dudekidPL. You could use FALLING as a trigger (I found it less reliable than LOW but your mileage may vary) but you can't escape switch bounce so you would *still* have to check for that to avoid false pulses. There's a more in-depth video on switch bounce in videos #96 & #98 (#96 talks about Rotary Encoders some more, #98 shows you how you CAN remove the 5ms statement if you're willing to add in a bit more hardware). Have a gander and let me know what you think!
Thanks for the quick reply! I will have a look at #96 for more encoder info. I am currently trying to implement a row of encoders into a key matrix. It should work as long as I can get a single encoder to work correctly and then put that interrupt function inside the key matrix. Unfortunately I cant get a stable encoder output so far. I have 5V at the common pin because my key matrix cycles 5v between the columns. I use pull down resistors for the A and B pins. A is connected to an interrupt pin and state of B is then checked inside the ISR to acquire the direction of the square waves. In my head it sounds right but i get crazy outputs.
I got it to work quite well with your code and by connecting the common pin to GND and using the software pull-ups for pins A and B . Is there a way to get similar performance but connecting the common pin to 5V and using physical pull down resistors on pins A and B? my circuit requires this configuration unfortunately
Yes, you can, with a bit of fiddling about (easy). Connect the common to 5v. This means you the pins A & B will be made HIGH as you turn the shaft. The Arduino Uno doesn't have a trigger on HIGH (Arduino Due only), only on RISING so use that in your attachInterrupt statement. Also, you will need to connect 10K resistors from the pins A & B to ground so they are not left floating. The debounce routine is still required in the ISR (using the check for millis() so no change there). Should then work. Let me know if you have problems.
Thanks once again for the quick reply, you're a gentleman!
I followed your instructions but used 7.8K pull down resistors instead. I didn't have 10k but that shouldn't have much difference. The debouncing function based on time since last interrupt definitely helps but in this configuration the encoder is still acting crazy. Changing the debouncing period from 5ms to 50ms helped somewhat but I still get many stray signals, not to mention the significant delay introduced. Any suggestions? Could it be down to the encoder itself? maybe the one i am using is designed to have the common pin grounded and that's it?
Thank you so much for this!
I hope you don't mind my asking, would this work with multiple encoders? I'd like to add another 3 or 4 (the 32u4 based 'Pro Micro' board I'm using has 5 Digital pins usable for Interrupts).
I'm very new to this, but would I define the extra pins ('const int Pin' for each A and B), define a last rotary value ('lastCount') for each encoder, define virtual positions ('volatile int virtualPosition') for each encoder, then replicate the content of the Interrupt ISR section ('if' statements etc) for each encoder, replicate the content of Setup ('pinMode's and 'attachInterrupt') for each encoder, and finally define what you want each encoder to do within the Main Loop? That's how I imagine it might work, but I could be way off! Also would you put all the encoders within the same ISR interupt section, or do you somehow have multiple interrupts?
Sorry, I realize this probably isn't a straightforward question!
Thanks again!
If you use a 32U4, Tobias, you do indeed have 5 hardware interrupts available on pins 0,1, 2, 3, 7 but you share pins 0 and 1 for RX/TX (sketch upload) so you may have to temporarily disconnect whatever hardware you have on pins 0 & 1 (pin A of a rotary encoder, I assume) to allow the upload to work. It may work "as is". Only one way to know! More...
Everything else you say sounds sensible, just keep the ISRs slim (in code terms) and you should be fine. Easy to try this out without too much coding (eg send a Serial message for each, initially).
Do ensure you use the construct:
attachInterrupt (digitalPinToInterrupt(X)...) where X is the actual pin name (as I gave above). Don't specify the actual interrupt number, it will be a minefield to debug. Good luck!
@@RalphBacon Thank you so much for getting back to me, and so quickly! And thank you for the information/conformation! I'll give it a go!
@@tobiasjames9107 You have the additional pins, but only one ISR can be running at a given time. If you were moving two encoders at the same time, it could miss one or the other occasionally.
@@CrimFerret Thanks so much for pointing that out! As I'm only planning on using this for Photoshop brush size and zoom etc, that won't be a problem. I imagine if you wanted to move two encoders simultaneously (as you might for something like an audio synth or for robotics) you'd maybe need to use an interrupt-capable I2C GPIO expander, which I've read about but don't fully understand haha!
@@tobiasjames9107 The why and how are the important parts to learn. Those who just wire these projects up and go oooh....ahhh over the blinking LEDs are missing the point (I actually know a couple of people who think making LED's blink is what the Arduino is for).
Hello, I have seen your tutorial and I have tried it and works very well, but I got confused how to send that data to blynk so I can monitor the movement? Would you help me to solve my problem please?
Many people misunderstand what the Rotary Encoder is actually doing: all it does is tell you which way the spindle or knob is turning, Joni. There are no values. That is for your sketch to decide that each "step" represents 1 or 50 or 500000. If you use my test sketch and work out what is happening all will become clear - note that I add or subtract a value depending on the rotation direction; no value is being given to the sketch.
Very good no problems understanding the video but on your site you have an example where you attach an interrupt to both pins. I cannot see what the advantage is of having an interrupt on both pins? If an interrupt detects motion on one pin then there must be motion on the other. Why not just poll it to find the direction. Why use two interupts?
Hmm. Good question. I can't remember using two interrupts for a Rotary Encoder (I never have since). Was it just demoing a particular point? I would expect an interrupt for Pin A and in the interrupt a quick read of Pin B which would indicate rotational direction (as you say). Strictly speaking the interrupt should only set a flag and the main loop do the reading but for a demo it was more understandable to do some work in the ISR.
Hi its this one
github.com/RalphBacon/RotaryEncoderUpdate/blob/master/RotaryEncoderDualInterrruptsOnChange.ino
Its not the code that is the issue but the reasoning behind using two interrupts. I cannot see the advantage it has over your earlier code that uses one interrupt.
Since the Uno only has two interupts I would want to use one for the push switch and one for the rotation or use one of the boards that can handle more than 2 interrupts.
Many thanks for the excellent series of videos.
Regards
Dave
Ah ha! So this was the update for the stepless rotary Encoder (which I have come to dislike). I'm wondering what the comments were that "made me ponder" and do this alternative code. Perhaps because there are more steps in a stepless encoder (quarter steps, I believe) so I'm trapping both pin A and Pin B (whichever arrives first) as otherwise we would miss a pulse.
As I say, it was just another type of RE and I didn't like it anyway, and now that you've reminded me of the dual interrupts that is the final nail in the coffin. Just use the single interrupt from my original sketch, that works fine. If you use a stepless encoder and you end up with less resolution (ie fewer steps in the full rotation) well, that's the price we pay for not detecting every tiny pulse.
Hi Thanks for the advice, yes a single interrupt for the rotation and another for the push button. I will also need 2 limit switches for my project and I suspect it will be best to use interrupts there as well so I will need to get one of the boards that has more than 2 interrupts. Have you ever looked at interrupt handling on the SM32 family of boards?
I can't say I have, David. I would hope that it's more or less the same as any other device, but that's just me and wishful thinking!
Thank You, perfect simple explanation
Glad you liked it, Kieran, thanks for posting.
a quick question.. for the code that you use, do both pinA and pinB need to be interrupt pins, or is just pinA or pinB enough? and ifso... which? i'm asking as for my project i have only one interrupt pin available.
You are in luck indeed, Matthieu, as only the Pin A is put onto the interrupt. Then the Pin B is read directly from another, non-interrupt GPIO pin when the interrupt fires (not the most efficient interrupt in the world but it works well). I hope this helps!
sure does.. thank you very much.. :)
Hello Sir, How to operate multiple servo motors using interrupts simultaneously with servo.h library?
You only have two interrupt pins on the Uno and related boards, Najeha, so that's how many servos you will be limited to if you are using interrupts to control them. Is this really what you asked?
My homemade Six Digit, 20MHz, CD74HC192 Up-Down counter circuit counts Quadrature Encoder pulses at 20Mhz speeds. And seems to be many times faster than using my PIC16F887 microcontrollers.
Can you tell me why?
I suspect it is because your CD74HC192 is a hardware-based solution and your PIC controller relies on software. Hardware implemented solutions will always trump software, even when the software is written very well (no delays, for example). Of course, a very fast µcontroller could match a hardware implementation but that's just compensating for the slower execution.
So that's my take on this question, good one that it is, Techis God, thanks for posting.
Can we use rotary encoder for sensing the projection's xy coordinates?
As we can use Lidar for the same.
Not sure about this, Bharti. If you mean that you want to hang a pendulum on the Rotary Encoder so it turns the shaft as you move the object around it could work (especially if you used a stepless version) but really you should be using the device described in vide #76, an MPU6050 3-Axis Accelerometer Gyro which would work a lot better. Have a look at that video and let me know whether it's a good fit for your project.
Hi Ralph. Tried your code. Not working. I only get ST and later ART as in START. Same delay with RE... SET
I get the LED to come on sold when I push the encoder switch. Turning it does nothing to the LED and only periodic updates to the screen as to the value. You made it look easy. Any ideas?
Mmm, tricky to diagnose at a distance Ron, but let's have a go.
Firstly I can't work out what you mean by the sentence " I only get ST and later ART as in START. Same delay with RE... SET", can you explain that please?
Secondly, if the LED comes on but does not fade (you have to turn it counter-clockwise quite a few times to get it fade all the way down) then I suspect that your A/B wiring is not correct. Recheck that. You can verify whether the rotary encoder is doing anything at all by monitoring the output on the Serial Monitor - it will print "Up" or "Down" with a value depending on which way you are turning the knob.
Finally, ensure you using the EXACT sketch (no modifications or pin changes or anything) that I supplied otherwise we could be talking at cross purposes!
Hi, I'm trying to make a rotary encoder work to increment a seven segment display. I have the display part working fine, but every way that I have tried to program the rotary encoder, with or without interrupts, the counter goes from max to min as soon as the encoder is moved. Any help? Thanks.
Never mind, I just had to us the pullup resistors for both outputs A and B of the encoder.
I'll leave this here in case anyone else has the same problem.
Great video. :)
If you define the input pins as INPUT_PULLUP you should not need actual pull up resistors. Check the code to verify this and try it out!
@@RalphBacon That works too. Thanks!
Hi, excellent video. I am looking forward to using a rotary encoder to build a volume control knob. My question is, what happens when the power is turned off or cut off for some reason? How do i store the last value from the rotary encoder form the Arduino board in order to maintain the last value of the knob? Should i use a SD card?
Thank you in advance.
I apologize for my english, not my native language.
No need for an SD card here, Daniel. Just use the Arduino's built-in EEPROM. Very easy to do, see videos #26 & #27 to see an example (stores a light level) and video #65 on how to use the EEPROM, *very* easy. Any good?
Hi Ralph, thank you very much for such a quick reply. I will look into the videos you have mentioned. Thanks again for the great help and great tutorials!
Hello Ralph, This video is pretty neat explanation of rotary encoder and your code is efficient and light but I have question regarding your 5ms time check in isr routine. Surely you didn't use delay() function because it doesn't work in ISR vector, however in your code you declared the time variables inside ISR function. This will cause them to initialize with 0 every single time isr is called and you would always end up having interrupttime - lastinterrupttime > 5 condition as true.
I might not be seeing the point here but if you could explain this 5ms time check for debounce in ISR.
Cheers
Many Arduinites get tripped up over this bit of code, Shuaaib, so let me see if I can explain it.
The *lastInterruptTime* variable has been declared as *static* which means it is a sort of global variable but _only_ to this function. It doesn't get reset every time. The initialisation occurs just once, the first time.
Would it work if this variable were a _true_ global variable? Yes. Is that the _right way_ to do it? No.
But hey, life's short. Whatever works for you, works, right? I'm just writing the code to demonstrate Best Practice! I hope this helps your understanding of this code, thanks for posting that question.
Oh, Yes you are right. Totally tripped on that one. Didn't realize, variable was declared static. Normally in embedded systems people are looking for volatile keyword for variables that are used inside ISR. :)
Thanks for clarification.
Cheers
You are most welcome Shuaaib, I'm glad it clarified things for you. Nice to hear from you.
Nice, found the information here, that i need.
Glad it was helpful!
Good tutorials and good codes.🐹💕🦀🐙💓
Especially I was very impressed with the way to avoid bounce.
I managed to understand how to use the rotary encoder.Thank you so much.🐹💕🦀🐙💓
I'm very glad I helped you understand it, thanks for posting.
Hi Ralph and thanks for the video. I am not that skilled in C but I am trying... How come that the varible that is static in the interrupt routine doesn't lose its value when the subrutine ends?
Hello Christer, good to hear from you. The whole point of a *static variable* is that although it is declared within a function (subroutine) the compiler pulls it out so that its scope is not constrained to that function's scope - it survives even when the function ends, in much the same way that a global variable survives, except that it can *only* be used by the function that declared it. I think it's better than a global variable as its scope is still constrained (you can't amend or read its value anywhere except in the function it was declared in) but other viewers (no names mentioned but Robert might read this) think that global variables are easier to use - which might be true but are also easier to abuse and introduce bugs! Is this clear? If not, reply to this message and I'll have another go!
Thanks Ralph, crystal clear! I think I need to pull out my old C/C++ books from the dusty shelf ;-) Thanks again!
How did I get your name wrong in my reply! Doh! I've changed it now, Christer, and I'm glad my response was clear enough. Keep coding, your C++ will only improve.
i have encoder non stoppers my only concern is how to convert into input mapping for gaming.?
encoder used 600ppr
board used Arduino Leonardo
In principle it works the same way, although I found non-stepped/non-indented more difficult to work with.
You still compute which way the rotary encoder is being turned, and how quickly and then have to decide what to do with that information for mapping or gaming.
As you are using a Leonardo you could build a virtual keyboard and use the rotary encoder to either issue "Left Key" or "Right Key" signals depending on which way you were rotating the spindle.
@@RalphBacon here my short video. the only problems ks Rotary Encoder while i spin disc is to hard to select song or using as dj scratch
ruclips.net/video/fT3u12VrX4Y/видео.html
That looks like you might be overloading the processor with interrupts.
@@RalphBacon hmmm.. 🤔
what else can be done that way.?
Im trying to use this and uploaded the latest version unmodified to my Arduino Leonardo.
However after it reset it would not become visable and on the Devices page was an "known USB device"
I had to reset and try to upload a blank code to get the arduino back.
Any ideas?
Thanks
+John Hutton
Hi John, welcome to the fun and games of Arduino World!
Firstly I'd say don't use a Leonardo unless you have a need. Why? Because of the reasons you're now describing. It's a finicky module at the best of times (I've done a video on its clone) and for the first few seconds exposes itself to Windows to allow code uploads, then turns into a HID (Human Interface Device - usually a keyboard or mouse) for the rest of the time. Added to which you might have to (re-) install the drivers if you move it to another USB socket on your PC (or hub). This is the case if it states "Unknown USB device". Try uninstalling it and reinstalling it.
Sometimes a reboot of your PC clears things down too but it doesn't take long before things can get out of sync again. Watch my video #30 which talks about my use of the Leonardo clone (called a Sparkfun Pro) and mentions drivers and the like (IIRC correctly). Frankly though, unless you NEED an Leonardo go and get a Nano or Uno when all these problems just disappear.
Hi Ralph,
reason I was using Leonardo was to interface with a flight sim (Falcon BMS) and looking at a rotary pulse when clockwise or anti-clockwise to send a key command.
any idea how to do that with an uno? Or how I can get it to do so as a joystick?
any help appreciated.
John
I see, you want to use the Arduino Leonardo as a keyboard/joystick then? In which case you will have to use the Leonardo (or a clone) as it presents itself as such to Windows.
However, in your first post you say that AFTER an upload the Leonardo started misbehaving; are you SURE you told the Arduino IDE that you were using an Leonardo (not a UNO or something else)? Otherwise you may have to reflash the Leonardo as its bootloader will have been overwritten. There are some articles on the internet about flashing bootloaders (I've had to do it a couple of times). I can't give you precise instructions (as I don't use a true Leonardo) but the principle is the same.
If you have 'bricked' your Leonardo, then this is a good article: learn.sparkfun.com/tutorials/installing-an-arduino-bootloader but it all depends on your exact circumstances in how to get new code (the bootloader) into a device that is no longer being recognised via USB.
It's very do-able just take your time and think about the exact steps you need to take (the first one being sure that you have the correct bootloader for the Leonardo). You may need an Arduino UNO to do the uploading to the Leonardo via the ICSP header (for which you will need a cheap cable) but the article describes that too. Good luck with this, treat it as a learning experience. You can do it.
Additionally, there may be more information you might find useful in my video #16 Getting your Arduino connected to your PC. It describes using an FTDI programmer (far simpler than you might think). Of course, this only applies if you have, indeed, bricked your Arduino Leonardo.
Will this work for a ho dc motor?
Do you mean: can a DC motor turn the rotary encoder and then you do something in your sketch? Or something else? If the former, yes, but it's not designed for a fast speed, about 60 rpm is the expected use (ie one turn per second).
I love your videos from a technical viewpoint, but as a beginner, know absolutely nothing about coding. I get lost when you talk about the sketches. I've studied the Arduino code reference pages, but they're of little help. It's easy to build a project and copy someone else's sketch to make it function, but I'd like to learn how to do a project from idea to hardware to sketch. Any ideas on how to learn coding without someone "giving the answer first" and assuming we understand the code? Every tutorial I've found on the internet works this way, even the so-called "learn to program Arduino" courses. Thanks for providing so many interesting projects!
Hmm. You've got me thinking. Perhaps as part of my BB (Bacon Bytes) series I can include simple (and I mean _simple_ ) coding examples that assume nothing.
Can you make another example with an interrupt timer? My setup requires the interrupt pins for other tasks like safety sensors.
+naps1saps
Do you mean an example WITHOUT an interrupt? This would only work in a sketch that has been carefully written to have no Serial.print statements, no delays and in a way so that each function (method) is only given a small time slice to do its work - otherwise you could so easily miss that rotary encoder pulse - hence the use of interrupts. If you have run out of interrupts I would seriously suggest you move onto an Arduino Mega which has many more interrupt pins (six, I believe) which would solve your problem. If you are in a part of the world that can buy from the Far East (via eBay, for example) then you can pick up a Mega clone for about $10 or so, very good value.
Hi Ralph, having issues with the serial monitor, lots of random ASCII character garbage is being spat out when turning encoder... any ideas? Great tutorial too, thanks!
+Edward Clarke
Welcome to my channel!
I suspect that your baud rate is not correct. Check that the Serial WIndow (bottom right hand corner) is set to the same value (eg 9600) as what is in the Serial.begin(9600) statement.
You can test this just by sending a standard "Hello World" statement and ensuring that is received correct to begin with.
Finally, close and re-open the Arduino IDE as sometimes it gets confused.
I've tried all that with no luck :( thanks anyway!
Hmm, sorry to hear it's still not working. You are not using digital pins 0 and/or 1 (D0 / D1) on the Arduino board are you, as they are used for the serial communication - if you use those pins for something else then garbage may appear on the serial monitor (or your uploads will fail!).
Does the standard "Hello World" appear correctly on the serial monitor? And garbage characters appear only when you turn the encoder?
Actually just fixed it, no idea how... yes I was using the digital pins correctly.
I did also try to use a sketch just with some print commands in a loop but that wasn't working at all. Either way, it's all good now.
Am quite new to this as you can probably tell!
Thanks for all your help, really appreciated :)
Great to hear it's now working! Enjoy the video(s) and keep learning as you go!
Thank you for this tutorial,
It'll help me with my music project.
As my project has a lot of timed interruptions I also had to add some cli(); and sei(); to block and restore the handling of the interruptions, inside the equivalent of your "if (virtualPosition != lastCount)" in the main loop() function. It may also fix your problem if your "isr" function is not called when intended.
But some strange behavior happen: when I add those cli(); and sei(); inside the "isr" function itself the arduino resets everytime it's called.
Does anybody know why ?
I'm still investigating.
Summary for my investigations:
context:
- Arduino MEGA 2560
- I don't have the Dupont wire shield, only a plain encoder (the type you see on the pcb but without the pcb), I only use the turn detection pin, not the button for now
- I 'm using the INPUT_PULLUP mode on the pins for my arduino to handle power to the encoder, way simpler, less cables
- I use tone so my Arduino is constantly using time interrupts in addition to the rotary encoder handling
what happens
- if interrupt on pinA is in LOW mode -> it works without cli() and sei(), if I add cli and sei inside the interrupt routine it crashes
- if interrupt on pinA is in FALLING mode -> i can add cli() and sei(), it doesn't crash, but when turning right I get sometimes "turn left" detection
some leads:
- maybe the breadboard connection is loose and we detect too much falling on pinA because it's trembling when I turn
- maybe the interrupts stack in LOW mode, because of the cli(), too rapidly and that's the reason why it crashes.
my workaround :
- I'm in LOW mode, I don't use cli and sei, and it works, but I don't understand why, is there a protection for an interrupt function not to interrupt when it's executing itself ?
Ideally, you want to detect FALLING, as LOW will constantly retrigger whilst it is LOW (and requires logic to ignore that).
Using sei and cli will not crash the program. Are you using these commands to prevent further retriggering of the interrupt routine you are in or to prevent the other timer interrupts?
Are you sure your wiring is not shorting something out?
Getting a LEFT signal when turning RIGHT indicates that you are getting switch bounce or turning it too fast - have you allowed for switch bounce as per my demo code? 5mS should be enough.
@@RalphBacon
Yes I'm using the rebound protection. The decrease looks parasitic, but I secured the pin and I still got those, when I'm turning to increase it happen like once every 4-5 increase ....
When I'm using LOW without sei and cli it's ok, no parasitic decrease.
Here is the interrupt routine
pastebin.com/NdmyJh4X
Here is the consume code for which I detect "-1" on the counter when I switch to FALLING with cli and sei:
pastebin.com/r1Cu7PAR
If you are curious, full code is available here (may change, I'm currently updating this project on this branch)
github.com/DelTa-B/hook-matching/blob/release/demo3/HookMatching/HookMatching.ino
What I think is that the arduino MEGA compiler tells to block interrupts when processing a LOW trigger and that a cli in this state crashes.
Also my connection is CHA to the left pin, Ground to the middle, CHB to the right pin. CHA is an interrupt pin, CHB is not (but I tested with an interrupt pin and got same result)
CHA and CHB are in INPUT_PULLUP mode.
Check out Nick Gammon's website about interrupts - you may find the answer there: www.gammon.com.au/forum/?id=10945
I have 5 rotary encoders that require 10 interrupt pins. But Arduino Mega has only 6 interrupt pins. What should I do?
You may need a processor that can handle more interrupts - but if you have 5 rotary encoders you only need 5 interrupts as only ONE pin one each rotary encoder will need to trigger an interrupt.
@@RalphBacon it is a quadrature encoder so it requires two pins. Which microprocessor will be okay? Rasberry Pi 3 >
I don't know about the RasPi when it comes to interrupts; the ESP32 can have any pin as an interrupt but it doesn't have a huge number of pins either; but it might be OK.
Nicely explained Ralph 😊
Glad you enjoyed the video, Sanjay! Plenty more to learn here, which you will need if you intend to start building Arduino projects (as per your stated plan to me). Enjoy and thanks for posting!
Nice instructional vid. Just a question though, so if I am planning to control 8 motors separately and have rotary encoders to report its position, do I need 8 interrupt pins for it, can I use any pins in arduino to use as interrupt? Thanks in advance.
Hey 3D Printwiz! I wish I had a 3D printer, quite frankly.
Anyway, with regards to your question the short answer is "No", you cannot use just any old pin for an interrupt. On the Uno/Nano only pins 2 and 3 can be used as "external" interrupts (like you get from a rotary encoder). On the Arduino MEGA you have more: pin 2 (interrupt 0), 3 (interrupt 1), 18 (interrupt 5), 19 (interrupt 4), 20 (interrupt 3), and 21 (interrupt 2). If you need several interrupts a MEGA can cost very little when cloned from the Far East (I bought mine for less than $10).
Additional reading: you might want to read up on the difference between "pin change interrupts" and "external interrupts" as we've been dealing with the latter but the former *may* be of use to you!
Thanks for posting this interesting question, I'm sure others will think it useful too.
Thank you so much for your answer, it helps a lot since I am building a desktop robot with a precision of an industrial one, hehe, it feels impossible as I go through the process. As for 3d Printers, I have a Flashforge, but there are a lot of 3d Printers now that are twice as large, and as better, plus cheaper. Get one I, believe me it is worth it.
Getting one is the easy bit. Paying for it is the first challenge. Secondly where do I put it? I have zero space left in my workshop. Build another workshop! Could be an expensive 3D printer! I'd also like a 50W laser cutter so I could cut acrylic for project boxes and the like. Another dream. ;)
Robot Arm looks great just don't let it be seen in Germany (I Am Arm means I Am Poor there). And that soundtrack reminded me of a couple of 1990s raves that were better forgotten. But it's an interesting, working construction and I wish I had a man cave (well, my workshop aka shoe cupboard is my man cave) but all space has been used up. Sigh. One day, one day...
Hi Ralph. A few words from the nit-picking Norwegian here. :)
The ATMega328P can indeed do interrupts on almost all its pins. The datasheet says: "The External Interrupts are triggered by the INT pins or any of the PCINT pins". If we take a look at the pinout we see that all of the Arduinos pins (0-13 and the analog pins as well) has a PCINTx label.
That's the theory... I'm not sure if you can use the Arduino IDE though.
Edit: And you said that in your comment of course... I wasn't paying attention. :)
Hi Ralph, awesome video. Do you have anything with a rotary and a 7 segment display?
Not together, but there are plenty of videos on this channel that cover both topics. I'll leave you to browse!
Hi sir,
How do we write the code to calculate the rpm of a wheel connected to the encoder?
+Radha Krishna Korlam
Hello! Firstly thanks for posting that question. I'm sure others have wondered the same. I'm not sure that a rotary encoder of this design is really suitable for attaching to a wheel (ideally you want an optical sensor) but let's proceed as though it were OK.
You simple need to count the pulses received (via the interrupt) during a 1 /10 /60 second interval (depends on how fast the wheel is going round). You can accurately detect the time interval by comparing the current millis() value with the previously stored millis() value from the last loop. When the time exceeds your interval (1 /10 /60 or whatever timeframe you have chosen) then you can calculate (extrapolate) the rpm.
Personally I would not use a rotary encoder. I would use a Hall Effect transistor to count pulses from a small magnet attached (very securely!) to a wheel. I have such a video already in an advanced state of preparation but not yet published, but it won't be too long. I'll use this as an example of how a Hall Effect device can be used, so thanks for the suggestion and good luck with your project (or home) work!
Ralph S Bacon
Thank you so much for your help sir. I am sure you are one of the best youtubers. And your content too is very helpful too.
If I have any doubts with arduino based projects, may I contact you via mail or something else?
+Radha Krishna Korlam
Thanks so much for your supporting comments. As regards help with your projects I can recommend the Arduino forum (forum.arduino.cc/) where there are many, many experts just waiting to help. You may even spot me lurking in a corner there too! This is definitely the best approach as I just don't have the time to give specific, bespoke help which I'm sure you understand.That way, I can develop more videos whilst the experts at the forum help newcomers with their projects.
Ralph S Bacon
Yup sir, I understand. Thank you. :-)
Good video- Well presented, nice and clear- subbed.
Thanks for your post, Bikefarm Taiwan, good to hear from you and thanks for subscribing. 😃
IN NOT SURE BUT I THINK TO GET IT TO WORK ON A SCHEMATIC WITH DIGITAL LOGIC LIKE A 7400 SERIES TYPE LOGIC IT HAVE A PULSE WIDTH LIKE A SAID BELOW BUT I COULD BE COMPLETLY WRONG HERE. LOOKING AT THE PROGRAMMING LIKE IN LISTENING TO AT THIS SECOND UR DESCRIPTION IS PERFECT AND EASILY UNDERSTOOD . THANKS FOR THE VID AGAIN AND IM GOING TO GET A ROTARY ENCODER FOR MY PROJECT.
Yes, do get a rotary encoder and if you have an oscilloscope it would be even better to see the waveforms. As I said below it is the rising edge that determines the direction not the width of the pulse. I'm sure you will find using the actual device will make it all much clearer. Thanks for posting (although we'd all appreciated your posts not being in UPPER CASE as it makes it seem as if you are shouting at me ).
Hi!
First of I want to thank you for this video. Helped me a lot with the code for my project. But I have not had the possibility to test it because:
When I try to compile it I get an error "'digitalPinToInterrupt' was not declared in this scope".
I try to compile your code aswell but get the same error.
I'm a n00b at programming and does not know where to look first for a solution.
I have IDE 1.8.5 and the forum post I have found suggests a newer IDE (because of a updated arduino.h).
Sincerely
Ok. I have now realised that it has to do with the board I want to use. I have to manually fix so it can use the interrupts... I will have to dig a bit deeper before I can give a better answer. Btw the board I want to use is a Digispark ATTiny 85.
Hi Björn and thanks for the support! I'm not even sure the ATTiny85 has hardware interrupt pins the same way as the Uno but you can program it for pin change interrupts. Here's a Stack Exchange article I found that may help you: arduino.stackexchange.com/questions/3929/attiny85-interrupt-id-vs-pin-when-programming-with-arduino but read the comment (correction) by guru Nick Gammon below the answer. And note that the interrupt is triggered on both rising and falling edges.
I have now giving up the idea of using a ATTiny 85. And I have now tried to make it for the UNO. But step into problems again... The
attachInterrupt(digitalPinToInterrupt(PinA, isr, LOW);
line gives me a error:
macro "digitalPinToInterrupt" passed 3 arguments, but takes just 1
And I'm not sure where to find a answer for my problem. Tried google and gives me only results on C++ problems with the macro Min and Max. I'm a real n00b att programming and would just love some pointers that direct me where to look for a solution...
I will read up on the link you gave me in later time, when I have understood this code first so I can convert it to a ATTiny 85.
Sincerely
EDIT: Now I found the problem after even more googleing... It was the format of the line...
Missed a ) after PinA... Feels dumb now...
Never feel dumb about making mistakes. It's actually the best way to learn. When you have to write that line again you will remember the syntax (or at least check your brackets!). On that basis I learn a huge amount every day!!! You can guess how many mistakes I make! Anyway, I think you'll find it much easier using an UNO first, at least, before moving over to the Tiny85. Thanks for the update.
I wanted to say that I have managed to get the code working on a UNO now. Works great! But needed some time to figure out how to turn it ON and OFF with the switch.
I must thank you again for a very good explaining video. Think I could not managed if it was not for you video.
Sincerely
hello sir. This is a Good video. i want to ask is it a must to add a 1k ohm at each Pin A and Pin B when i want to connect to arduino?
No you don't need to do that Irfan, if you're using the exact same module as I am, because it already has 10K pullup resistors on-board. If you're using a different type of rotary encoder (eg a bare one) then you need to do that, OR even simpler just change the code in the sketch to make the input pins INPUT_PULLUP then it will all work just fine.
Thank you for your respond sir, really appreciate it. btw im using this type of DC motor with encoder
www.cytron.io/p-spg30e-30k.
this type of encoder i need to put that 1K ohm or just insert the INPUT_PULLUP code?
Looking at their suggested wiring diagram they are definitely connecting a 1K resistor from the pin to +ve (5v). But I would try with INPUT_PULLUP first (that uses a weak internal resistor, 10K to 50K) which might be enough. If it doesn't work as expected use the 1K resistors. It might be that the voltage on those lines is also being used internally in the motor electronics (unlikely but you never know)!
thank you for your suggestion. do you have any tutorial on the moving mobile robot using encoder like localization for example
Robotics is not really my thing but you could ask at the Arduino forum (forum.arduino.cc/index.php) as there are certain to be people there who have had experience of this.
Very nice, and professionally presented. Unfortunately, though, the link to the code points into data-Nirvana, in other words, a 404 (page not found) at Dropbox. Could you be so kind and update the link?
With best regards
Thanks for letting me know, Volker, I've updated the link in all places, video description and comments. If you experience further problems please do let me know. Thanks for your kinds words too, glad you like the video.
i need you help how to return into empty file into androuino leonardo as fresh from the pack
I'm sorry Jerry, I don't quite understand what you mean. Possibly to clear down the sketch in an Arduino Leonardo? Have you uploaded a keyboard emulator and now can't stop it splatting all over your PC?
Great explanation.
Thanks so much for uploading :)
+Max Foster
I'm glad you found it useful. And once you know, it seem so straightforward, doesn't it? Thanks for posting your comment and keep watching!
Sir, actually I want to connect a servo motor with it
So,I wanted to know the exact connections of the servo motor,encoder and Arduino Uno board
Can you please help me out?
You must learn to walk before you start to run, Suryansh! Do the Rotary Encoder project. Then you will know how they work and what they really do. Then connect up a servo and play with that (unfortunately I don't have a video for that but you're bound to find a good example somewhere, perhaps on the Arduino site). By that time I suspect you will figure out how to connect the two together. As they say, you can teach experience - that's something you must acquire all by yourself. It's a fairly simple requirement you have so should be straightforward to do - good luck.
I got a file not found message when trying to download the final code.
Sorry about that James, Dropbox has changed the way files can be shared. The new link is in the video description and here: www.dropbox.com/sh/l7bzy5hj46ywa58/AABjo0yydmB5nzeAuK_nPI9da?dl=0 Do let me know if you can't access it.
Great! I 've also learned "easy-peasy" :-)
Added value all round then, Valerio! Normally I charge extra for language lessons but this one is included! Thanks for posting.
Hi Ralph, Could you maybe show us a code that would keep the position of the digital switch on power down of the arduino in the EEPROM.
Thanks,
Love your Videos .....
PS .... and maybe put your sketches at the bottom in the show more.
Cheers
Now, Joe, remember that there is no value associated with the position of a Rotary Encoder. Not like a potentiometer, for instance.
All it does, as you know, is send a anti/clockwise pulse to your code which then decides what to do. For example, in/decreasing a local variable. So now that we have this variable in your code that is being adjusted, to save it to EEPROM is really easy if you follow video #65 EEPROM basics. Whilst I don't use a Rotary Encoder in that demo, I do use an in/decreasing value from a radio transmitter and save it to EEPROM in exactly the same way as you need to do when using a Rotary Encoder.
Have a look and if you have queries post them under that video and I'll respond! And, if I understand your last point correctly I'll put a link to my demo sketches in the video description (is that what you meant?).
I'm heading over to video #65 .......... oops ...... know i see the code here on this page;) Cheers.
Thank you very much! Thanks to you I got it to work
Excellent news, Maarten, thanks for letting me know. Nice to hear from you.
i can use this i think for my first Nokia screen menu job wel done
Glad it helped you, Jeff, good luck with your project.
Hi Ralph.Ver y good vídeo your my LIKE.Im wondering ir you can share a link vídeo if your already have Or any idea about hoe to do the conect and enconder that doesnt have the PCB .I have seen some vídeos with and with out resistor and with and with out capacitor.I am getting confused.I am trying to make a cockpit forma muy flightsim.Hope u can help.Luis from argentina
Hola Luis, que tal? Here's a picture of the connections required to a bare Rotary Encoder (no PCB): community.axoloti.com/uploads/default/original/2X/f/f656716442794f064bec964b8258ea771cd40360.jpg
You can use the NEW method or the OLD method - the NEW method is probably more reliable.
Let's say I want to increase/decrease the value by 1 rotating the encoder, like you just showed, AND by 10 rotating while pressing the button, how I do that?
You mean you are turning it AND pressing the button simultaneously, Marco? If so, do a digital read of the pin that the switch is connected to whilst incrementing (or decrementing) the count, all in the ISR. If the cost (in time, mainly) of doing a digital read is too much, we could connect the switch to another ISR and just monitor its state (using a volatile bool, for example) and then read that variable in the original ISR - much quicker. The UNO only has two interrupts so this would use both of them. Any good?
I think the digitalRead thing is the better way to do it.
So I'll put in my code IF digitalRead HIGH increase (or decrease) by 10, ELSE by 1.
Thanks for your quick response!
Yes, that would work but it definitely isn't the best way to do it (using another interrupt is) but sometimes pragmatism triumphs over purity. Quite right too. Thanks for your post, let us know how you get on.
Would this work for motor encoders???
Whilst it would most certainly measure the rotational speed and direction of a motor I'm certainly not convinced that it would survive very long unless the motor was used in a very limited way (eg a robot arm). Most motor encoders use an optical method of determining speed and direction (identical concept but not using mechanical switches). So the short answer is "Probably not what you want" and the longer answer is "Find (or build) an optical encoder using an LED and a phototransistor which has not moving parts as such". I hope this helps.
Excellent video, Ralph. Thanks! You are a source for inspiration.
There is room for an interesting little C-question in your code, and that made me Google around a bit.
We all know that changing a variable in an ISR is not as trivial as changing it elsewhere; it needs to be 'volatile', of course. That is to prevent the compiler optimising it away or put it in a register.
In your isr() you have another variable, lastInterruptTime, that should be treated volatile, but you didn't. Instead you made it static. Perfectly fine of course.
So, what is the difference between static and volatile? And... Can you use static instead of volatile? Or perhaps do both; "static volatile"? Frankly, I'm not sure. Well, not sure enough to try to make an explanation, that is. ;-)
Hei Robert, hvor er du?
For others reading this post, let's think about what "volatile" is trying to do. Global variables (declared outside of any function) can be changed by the ISR. But the compiler is unaware of this and can retrieve the value from its registers without actually reading the data from memory again. Moreover, the compiler optimisation may "mistakenly" remove code if it thinks a condition is always true or false because the variable it is checking has not been marked as volatile (programming error, not an optimisation error). By marking it as volatile the compiler (and optimiser) is now aware the variable's value could suddenly (and unexpectedly) change and always retrieve its true value from memory.
Variables used within *and only within* the ISR itself are safe because the compiler knows that's where they are used! Volatile is not needed as they are not accessed elsewhere.
As it happens, during the execution of an ISR, millis() is not incremented so that piece of code that tracks the millis() will have the same value at the start of the function as at the end of it. But it seems more logical to update the "lastInterruptTime" at the end of a function to me!
So declaring a variable used within an ISR as both static and volatile is not required because the scope of that *static* variable is local (only for the ISR in the first place, ie not global) and is not *volatile* as it's not being used globally.
Tricky subject you've raised here Robert! Probably requires more background reading but in broad terms that is my understanding (and my code works so I guess it is not being optimised away or corrupted).
Wow! I wonder if anyone else would care to comment on your post?
Google translate did a terrible job there... You asked where I am. I might as well answer that. :) I'm in the middle part of Norway, north of Trondheim. :) The rather rhetorical English question "how are you?", does not exist in the Norwegian language. Typing in "How are you doing" in Google Translate gives a better translation by the way. Enough linguistic digression... ;-)
You're absolutely right about volatile and static variables of course. I just felt a bit unconfortable when I saw the variable lastInterruptTime in the ISR and thought "this variable is lost". I didn't notice it was static.
A static variable "survives" outside the scope, but is not accessable outside the scope. If if were non-static it would have been lost, and the code would behave strange.
I would have made lastInterruptTime global (and volatile of course), but now I realise it's better (and more elegant) to do it your way.
Being a C-developer for more than 30 years doesn't mean you've stoped learning. Just ask me! :-)
Robert, hvordan går det? Better? Ha ha! Thank goodness you're fluent in English!
Regarding your post, I don't think we ever stop learning, whether that's in C++ or in life in general! It's when we *stop* learning that the rot sets in. I'm sure some beginners find the whole concept of scope, static, volatile and so on quite confusing. I try and not gloss over such things in my demos but I will henceforth take *extra* pains to try and explain such esoteric language concepts for the benefit of one and all. Once understood, the general reaction should be "Is that all it is?".
Better indeed. :) When it comes to computers and development, I think my English is ok. But if you like to discuss knitting, I think I'll pass. "Weft and warp", I have no idea what that means, at least not in a knitting context. :)
That's a shame, my next video was going to be about the weft and warp process for knitting. Knit one, purl one. Oh well, I had better stick to Arduino-related videos then :)
Thanks for posting. However, I'm quite surprised to see that you did not explain the connections from encoder to the Arduino? The tutorial being that elaborate and all? Just a suggestion maid;)
Well now, noilex, the demo sketch used in the demo also contains the pin connection details: PinA (from the encoder) goes to pin 2, PinB goes to pin 4 and pinSW goes to pin 8. We use Pin 2 because the Arduino has interrupt capability on that pin, so we can reliably detect rotary encoder movement. Hope that helps.
Thank you for clarifying ;)
Can we count from 0.1decimal places
I'm not completely sure what you mean, Paul, can you just explain a bit further, please?
Hi Ralph,
I have a project where I am using two encoders. I have utilised your code to run the encoders but I have one small problem. Is it possible to contact you via email or some other form so I can explain what is happening? From up north in Doncaster . Cheers Leigh
@Leigh Bucksey
Firstly, let me say how great it is that my video has helped you. Makes it all so worthwhile.
Now to the issue of 'support'. The BEST place for support is the Arduino forum (forum.arduino.cc/index.php) where there are many experts cleverer than me who can lend a hand, providing you can explain succinctly the issue and what you have done so far in trying to resolve it. And follow the house rules for posting code and the like.
As you can imagine, if I tried to do this single-handedly I'd do nothing but answer queries. That said, I'm prepared on this occasion to let you PM me on that forum after you have posted your query so that I am alerted. I should then be able to read your original post within 24 hours, during which time others may already have posted suggestions.
I'll do my best then to help, how does that sound?
And BTW don't apologise for being up north in Doncaster, someone's go to live there (heh heh)!
Great explanation. My only suggestion is to minimise the isr code to just a flag and process the change in the main loop.
bool is_changed = false;
int pos = 0;
void isr()
{
static unsigned long last_int_time = 0;
if (millis() - last_int_time > 10)
{
is_changed = true;
last_int_time = millis();
}
}
then in the loop...
if (is_changed)
{
is_changed = false;
if (digitalRead(pin_b) == LOW)
{
pos--;
Serial.print(F("Down: "));
Serial.println(pos);
}
else
{
pos++;
Serial.print(F("Up: "));
Serial.println(pos);
}
}
Yes, that would certainly optimise the ISR and make it shorter, which is what ISRs should be, of course.
Great video!
Glad you enjoyed it.
Thanks for this greet videos. please and please can you do a video on how to create LCD menu on common 1602 LCD thank you.
+manoftheday1
I'm intrigued as what you mean by 'menu' - and has it got anything to do with the Rotary Encoder? Explain further as I'm always after ideas for videos that could help beginners.
@Ralph S Bacon Your video helped me a lot! Thanks!
Glad you like it, beicel, once you have understood how it works it all becomes very simple, does it not?
Yeah, but not that simple! ;) Anyway, I needed to change pin 3 and 4 (revert), but I have the same rotary encoder like you!
Hmm, interesting that you changed the pins but I'm delighted you got it working! "Simple" is a relative term, what I meant was that once the concept is understood then it becomes much easier to actually implement! Great, thanks for the update.
THANK YOU SOOOOOOOOOO MUCH! You are a life saver
Well, that is high praise indeed, Vi Van Gupta, I'm happy you found the video useful. Thanks for posting.
sir,how to conect encoder+RELAY+display.
Learn how to do each item separately. Then think about how you would connect all three methods together. There are no short cuts to doing it properly and learning along the way.
Brilliant !
You are very kind! Thank you.
Is there really such a problem as "bouncing"? because i think your encoder is skipping steps because of that 5ms read delay, not because it reads too slowly
Switch bounce exists for just about every switch in existence. My 5mS delay is good enough most of the time to ignore those bounces. It doesn't seem to skip any steps unless you turn the RE _really_ fast (like the volume control in my car).