{"id":8390,"date":"2017-10-22T19:00:15","date_gmt":"2017-10-22T10:00:15","guid":{"rendered":"http:\/\/jumbleat.com\/?p=8390"},"modified":"2018-09-25T23:49:40","modified_gmt":"2018-09-25T14:49:40","slug":"encoder_3","status":"publish","type":"post","link":"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/","title":{"rendered":"Using rotary encoder <span class=\"sub_title\">part 3 : &#8220;Dual Encoder&#8221;<\/span>"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_80 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">\u76ee\u6b21 [ Contents ]<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-69de4022bb0d5\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #757575;color:#757575\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #757575;color:#757575\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-69de4022bb0d5\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#Wiring\" >Wiring<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#Sketch\" >Sketch<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#Note\" >Note<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#How_to_use_Dual_Encoder\" >How to use Dual Encoder<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#Functions_of_%E2%80%98Dual_Encoder\" >Functions of &#8216;Dual Encoder&#8217;<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#define\" >define<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#Expansion\" >Expansion<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#pin_define\" >pin define<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/#port_resister\" >port resister<\/a><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n<p><\/p>\n<div id=\"prolog\">\n<p>I have been struggled in <a href=\"https:\/\/jumbleat.com\/2016\/12\/17\/encoder_1\/\" target=\"_blank\" rel=\"noopener\">part 1<\/a> and <a href=\"https:\/\/jumbleat.com\/2017\/04\/20\/encoder_2\/\" target=\"_blank\" rel=\"noopener\">part 2<\/a> to read rotary encoder precisely. In this article, I wrote a new sketch for encoder combining those two wisdom.<\/p>\n<p class=\"mtm\">I called it &#8220;Dual Encoder&#8221;.<!--more--><\/p>\n<p><div class=\"jetpack-video-wrapper\"><iframe loading=\"lazy\" title=\"Arduino &amp; Dual Rotary Encoder (not true type)\" width=\"660\" height=\"371\" src=\"https:\/\/www.youtube.com\/embed\/-33uak20Tuo?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/div>\n<\/p>\n<p>Purpose of this sketch are,<\/p>\n<ul class=\"ls_algn\">\n<li>dual encoder reading<\/li>\n<li>using &#8216;timer interrupt&#8217; to read, not &#8216;attachinterrupt&#8217;<\/li>\n<li>works on click-type and non-click-type<\/li>\n<\/ul>\n<p>As a result of trial and error to solve the problem in the previous sketch, I think the accuracy was moderately improved. Especially, I have a satisfaction for use of non-click-type.<\/p>\n<p>Yet, there are chattering still on reading and it may disturb main task. And counting for click-type may goes wrong sometimes.<\/p>\n<p>So, the sketch of <a href=\"https:\/\/jumbleat.com\/2016\/12\/17\/encoder_1\/\" target=\"_blank\" rel=\"noopener\">part 1<\/a> maybe better for the people who don&#8217;t care to use attachinterrupt.<\/p>\n<p>I couldn&#8217;t make it as library because lack of my skill and time. Please use this sketch as your own project by rewrite or add code. Sorry.<\/p>\n<\/div>\n<div id=\"chap_1\">\n<h2 class=\"h2_HLine mtm mbs\"><span class=\"ez-toc-section\" id=\"Wiring\"><\/span>Wiring<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><a href=\"https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_Breadboard.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8392 size-full\" src=\"https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_Breadboard.png\" alt=\"\" width=\"1755\" height=\"957\" srcset=\"https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_Breadboard.png 1755w, https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_Breadboard-300x164.png 300w, https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_Breadboard-768x419.png 768w\" sizes=\"auto, (max-width: 1755px) 100vw, 1755px\" \/><\/a><\/p>\n<p>The parts necessary to try this sketch are:<\/p>\n<ul class=\"ls_algn\">\n<li>ArduinoUNO<\/li>\n<li>Rotary encoder \u00d72<\/li>\n<li>\uff08if you have\uff09128*64 OLED<\/li>\n<\/ul>\n<p>Needed encoders are normal type which has 3 pins, phase1 and phase2 and GND. I recommend you to use non-click-type as in <a href=\"https:\/\/jumbleat.com\/2017\/04\/20\/encoder_2\/\" target=\"_blank\" rel=\"noopener\">part 2<\/a>.<\/p>\n<table class=\"b_less\" style=\"width: 560px;\">\n<tbody>\n<tr>\n<td><iframe loading=\"lazy\" width=\"300\" height=\"150\" style=\"width: 120px; height: 240px;\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" frameborder=\"0\" src=\"\/\/rcm-fe.amazon-adsystem.com\/e\/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=jumbler02-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=as_ss_li_til&amp;asins=B018QJNH2Y&amp;linkId=4c1179af34d8cfc2b2722fda8df81d1e\"><\/iframe><\/td>\n<td><iframe loading=\"lazy\" width=\"300\" height=\"150\" style=\"width: 120px; height: 240px;\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" frameborder=\"0\" src=\"\/\/rcm-fe.amazon-adsystem.com\/e\/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=jumbler02-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=as_ss_li_til&amp;asins=B016W82H9A&amp;linkId=5c176bb75fb96c7bd10f3244e792f51c\"><\/iframe><\/td>\n<td><iframe loading=\"lazy\" width=\"300\" height=\"150\" style=\"width: 120px; height: 240px;\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" frameborder=\"0\" src=\"\/\/rcm-fe.amazon-adsystem.com\/e\/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=jumbler02-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=as_ss_li_til&amp;asins=B016892FGS&amp;linkId=04c23c6b862d3bf27645c810cbcd4f3c\"><\/iframe><\/td>\n<td><iframe loading=\"lazy\" width=\"300\" height=\"150\" style=\"width: 120px; height: 240px;\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" frameborder=\"0\" src=\"\/\/rcm-fe.amazon-adsystem.com\/e\/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=jumbler02-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=as_ss_li_til&amp;asins=B017M15KX6&amp;linkId=e6281759cf0fa4d187c85834b487042a\"><\/iframe><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Study about u8glib and OLED <a href=\"https:\/\/jumbleat.com\/2016\/09\/03\/how_to_use_u8glib_part1\/\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p>Or, you don&#8217;t have to prepare it if Serial Monitor is enough for you.<\/p>\n<\/div>\n<div id=\"chap_2\">\n<h2 class=\"h2_HLine mtm mbs\"><span class=\"ez-toc-section\" id=\"Sketch\"><\/span>Sketch<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Write the sketch to Arduino by download project file or copy &amp; paste from the code down below.<\/p>\n<p style=\"padding-left: 30px;\">Download &#8211; \u300c<a href=\"https:\/\/jumbleat.com\/wp-content\/uploads\/download\/Arduino_Sketches\/ppj\/dual_encoder_ver1_0.zip\" rel=\"noopener\">Dual encoder ver 1.0<\/a>\u300d<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false toolbar-delay:false scroll:false minimize:true lang:default decode:true\" title=\"Sketch \u300cDual Encoder\u300d - Click to Expand !\">\/\/   jumbleat.com                                 \/\/\r\n\/\/                                                \/\/\r\n\/\/           dual encoder version 1.0             \/\/\r\n\/\/                                   by jumbler   \/\/\r\n\/\/                                   2017.10.20   \/\/\r\n\r\n\r\n\r\n#include &lt;MsTimer2.h&gt;\r\n#define TIM MsTimer2\r\n\r\n#define OLED_DRAW 1\r\n\r\n#if(OLED_DRAW)\r\n\r\n#include &lt;U8glib.h&gt;\r\nU8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_DEV_0); \/\/ OLED \/ I2C \/ TWI\r\n\/\/ SDA 4\/ SDL 5\r\n\r\n#endif\r\n\r\n#define ENC_NUM       2\r\n#define ENC_TOLERANCE 25\r\n#define TIMER_INTVAL  2\r\n#define ENC_REPEAT    ENC_TOLERANCE * (TIMER_INTVAL \/ 2)\r\n\r\n#define ENCA_1 4\r\n#define ENCA_2 12\r\n#define ENCB_1 7\r\n#define ENCB_2 8\r\nbyte enc_pins[ENC_NUM * 2] = {ENCA_1, ENCA_2, ENCB_1, ENCB_2};\r\n\r\n\r\n#define ECUR B00000011  \/\/ current  enc position\r\n#define EHOM B00001100  \/\/ previous enc position\r\n#define ECHG B01000000  \/\/ flag that encoder has changed\r\n#define ERES B10000000  \/\/ encoder resolution. '1' is for quarter resolution\r\nvolatile int  enc_count[ENC_NUM];\r\nvolatile int  enc_old[ENC_NUM];\r\nvolatile byte enc_status[ENC_NUM];\r\n\r\n\r\n\r\nbyte ELAYER(byte layer) {\r\n  for (byte i = 0 ; i &lt; 8 ; i++) if ((layer &gt;&gt; i) &amp; B00000001) return i;\r\n}\r\nbyte EMASK(byte val, byte layer) {\r\n  byte tmp = (val &amp; layer) &gt;&gt; ELAYER(layer);\r\n  return tmp;\r\n}\r\n\r\nbool ENC_CHECK() {\r\n  bool yes_no = false;\r\n  for (byte i = 0 ; i &lt; ENC_NUM ; i++) yes_no |= ENC_CHECK(i);\r\n  return yes_no;\r\n}\r\n\r\nbool ENC_CHECK(byte pin) {\r\n  bool yes_no = enc_status[pin] &amp; ECHG;\r\n  return yes_no;\r\n}\r\n\r\nvoid SET_ENC_RES(byte pin, bool res) {\r\n  if (pin &lt; ENC_NUM)\r\n  {\r\n    enc_status[pin] = (enc_status[pin] &amp; ~ERES) | (res &lt;&lt; ELAYER(ERES));\r\n    enc_status[pin] &amp;= ~ECHG;\r\n  }\r\n}\r\n\r\nvoid ENC_RESET() {\r\n  for (byte i = 0 ; i &lt; 5 ; i++) ENC_READ();\r\n  for (byte i = 0 ; i &lt; (ENC_NUM) ; i++)\r\n  {\r\n    enc_status[i] |= ECHG;  \/\/ kick flag\r\n    enc_count[i]   = 0;\r\n    ENC_COUNT(i);\r\n    enc_status[i] |= ECHG;  \/\/ kick flag\r\n  }\r\n}\r\n\r\nint ENC_COUNT(byte pin) {\r\n\r\n  int  enc_val = 0;\r\n  char vec     = (enc_status[pin] &amp; ERES) ? 4 : 1;\r\n\r\n  if (enc_status[pin] &amp; ECHG)\r\n  {\r\n    enc_val  = (enc_count[pin] - enc_old[pin]) \/ vec;\r\n\r\n    enc_old[pin] = enc_count[pin];  \/\/update as previous value\r\n    enc_status[pin] &amp;= ~ECHG;       \/\/ reset counting flags\r\n  }\r\n  return enc_val;\r\n}\r\n\r\n\r\nvoid ENC_READ() {\r\n  static byte enc_gauge[ENC_NUM];\r\n\r\n  \/\/ read pins value\r\n  for (byte ii = 0 ; ii &lt; ENC_REPEAT ; ii++)\r\n  {\r\n    short pin_val[ENC_NUM];\r\n\r\n    \/\/ get current pins status of Encoder1\r\n    pin_val[0]  = ((PIND &amp; _BV(4)) ? 1 : 0) &lt;&lt; 1; \/\/ ENC A-1\r\n    pin_val[0] |= ((PINB &amp; _BV(4)) ? 1 : 0);      \/\/ ENC A-2\r\n\r\n#if(ENC_NUM &gt; 1)\r\n\r\n    \/\/ get current pins status of Encoder2\r\n    pin_val[1]  = ((PIND &amp; _BV(7)) ? 1 : 0) &lt;&lt; 1; \/\/ ENC B-1\r\n    pin_val[1] |= ((PINB &amp; _BV(0)) ? 1 : 0);      \/\/ ENC B-2\r\n\r\n#endif\r\n\r\n\r\n    \/\/ for each encoder pins\r\n    for (byte i = 0 ; i &lt; ENC_NUM ; i++)\r\n    {\r\n      \/\/ modify order of pins value\r\n      if (pin_val[i] &lt; 2) pin_val[i] = 1 + (pin_val[i] * -1);\r\n      enc_status[i] = (enc_status[i] &amp; ~ECUR) + pin_val[i];\r\n\r\n      short pos_old = EMASK(enc_status[i], EHOM);\r\n\r\n      if (pin_val[i] != pos_old)\r\n      {\r\n        \/\/gauging\r\n        enc_gauge[i] = min(ENC_TOLERANCE + 1, enc_gauge[i]++);\r\n\r\n        \/\/counting\r\n        if (enc_gauge[i] &gt;= ENC_TOLERANCE)\r\n        {\r\n          \/\/ increase or decrease ?\r\n          bool dir = (pin_val[i] &gt; pos_old) ? 1 : 0;\r\n          if (pin_val[i] == 0 &amp;&amp; pos_old == 3) dir = 1;\r\n          else if (pin_val[i] == 3 &amp;&amp; pos_old == 0) dir = 0;\r\n\r\n          \/\/ add count by the direction\r\n          if (dir) enc_count[i]++;\r\n          else enc_count[i]--;\r\n\r\n          boolean change_flag = false;\r\n\r\n          \/\/ forced count correction for Click-type\r\n          if (enc_status[i] &amp; ERES)\r\n          {\r\n            if (pin_val[i] == 3)\r\n            {\r\n              char rem = enc_count[i] % 4;\r\n              if (rem != 0)\r\n              {\r\n                enc_count[i] = (enc_count[i] \/ 4) * 4;\r\n                if (abs(rem) &gt; 2)\r\n                {\r\n                  char vec = (enc_count[i] &lt; 0) ? -1 : 1;\r\n                  enc_count[i] += 4 * vec;\r\n                }\r\n              }\r\n              change_flag = true;\r\n            }\r\n          } else {\r\n            change_flag = true;\r\n          }\r\n\r\n          if (enc_count[i] == enc_old[i]) change_flag = false;\r\n\r\n          \/\/ update current pos to home pos\r\n          enc_status[i] = (enc_status[i] &amp; ~EHOM) | (pin_val[i] &lt;&lt; ELAYER(EHOM));\r\n          \/\/ set count change flag\r\n          enc_status[i] |= change_flag &lt;&lt; ELAYER(ECHG);\r\n          enc_gauge[i] = 0;\r\n        }\r\n      } else {\r\n        enc_gauge[i] = max(0, enc_gauge[i]--);\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\n\r\n\r\nvoid setup() {\r\n  for (byte i = 0 ; i &lt; (ENC_NUM * 2) ; i++) pinMode(enc_pins[i], INPUT_PULLUP);\r\n\r\n  SET_ENC_RES(0, 1);   \/\/ SET_ENC_RES(encoder number, click = 1 \/ non_click = 0)\r\n  SET_ENC_RES(1, 1);   \/\/ SET_ENC_RES(encoder number, click = 1 \/ non_click = 0)\r\n\r\n  TIM::set(TIMER_INTVAL, ENC_READ);\r\n  TIM::start();\r\n\r\n  Serial.begin(38400);\r\n\r\n  char title[2][21] = {\"Dual Encoder ver 1.0\", \"by jumbleat.com\"};\r\n  Serial.println(title[0]);\r\n  Serial.println(title[1]);\r\n\r\n#if(OLED_DRAW)\r\n\r\n  u8g.setColorIndex(1);              \/\/ OLED pixel on\r\n  u8g.setFont(u8g_font_orgv01r);     \/\/719 Byte  \/ 11x11 \/*\r\n\r\n  u8g.firstPage();\r\n  do {\r\n    u8g.setPrintPos(15, 20);\r\n    u8g.print(title[0]);\r\n    u8g.setPrintPos(50, 55);\r\n    u8g.print(title[1]);\r\n  } while (u8g.nextPage());\r\n\r\n#endif\r\n\r\n  delay(3000);\r\n  \r\n  ENC_RESET();\r\n}\r\n\r\n\r\n\r\nvoid loop() {\r\n  static int val[ENC_NUM];\r\n\r\n  if (ENC_CHECK())\r\n  {\r\n    for (byte i = 0 ; i &lt; ENC_NUM ; i++)\r\n    {\r\n      val[i] += ENC_COUNT(i);\r\n      Serial.print((char)('A' + i));\r\n      Serial.print(\":\");\r\n      Serial.print(val[i]);\r\n      Serial.print(\"  \");\r\n    }\r\n    Serial.println();\r\n\r\n\r\n#if(OLED_DRAW)\r\n\r\n    \/\/ --- OLED Draw\r\n    int val_draw[ENC_NUM];\r\n    for (byte i = 0 ; i &lt; ENC_NUM ; i++) val_draw[i] = constrain(val[i], -64, 64);\r\n\r\n    u8g.firstPage();\r\n    do {\r\n      for (byte i = 0 ; i &lt; ENC_NUM ; i++)\r\n      {\r\n        byte space = 16;\r\n        u8g.drawFrame(0, i * space, 127, 4);\r\n        u8g.drawBox(min(64, 64 + val_draw[i]), i * space, abs(val_draw[i]), 4);\r\n        u8g.setPrintPos(0, 10 + i * space);\r\n        u8g.print(val[i]);\r\n      }\r\n    } while (u8g.nextPage());\r\n    \/\/ --- OLED Draw end\r\n\r\n#endif\r\n\r\n  }\r\n}<\/pre>\n<div id=\"chap_2-2\">\n<h3 class=\"h3_HLine mtm mbs\"><span class=\"ez-toc-section\" id=\"Note\"><\/span>Note<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4 class=\"fBox mtn mbs\">Timer interrupt<\/h4>\n<p>This sketch uses &#8220;MsTimer2&#8221; library to read encoder. So <a href=\"https:\/\/jumbleat.com\/2017\/04\/20\/encoder_2\/#chap2\" target=\"_blank\" rel=\"noopener\">get it in your IDE<\/a> if you haven&#8217;t imported yet.<\/p>\n<p>Or you could use another timer library if you rewrite it directly.<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false start-line:9 copy:false popup:false scroll:false lang:default mark:1-2 decode:true\" title=\"Reading Library part\">#include &lt;MsTimer2.h&gt;\r\n#define TIM MsTimer2\r\n\r\n#define OLED_DRAW 1<\/pre>\n<pre class=\"toolbar-overlay:false toolbar-hide:false start-line:185 copy:false popup:false scroll:false lang:default mark:7-8 decode:true\" title=\"Starting Timer interrupt part\">void setup() {\r\n  for (byte i = 0 ; i &lt; (ENC_NUM * 2) ; i++) pinMode(enc_pins[i], INPUT_PULLUP);\r\n\r\n  SET_ENC_RES(0, 1);   \/\/ SET_ENC_RES(encoder number, click = 1 \/ non_click = 0)\r\n  SET_ENC_RES(1, 1);   \/\/ SET_ENC_RES(encoder number, click = 1 \/ non_click = 0)\r\n\r\n  TIM::set(TIMER_INTVAL, ENC_READ);\r\n  TIM::start();<\/pre>\n<h4 class=\"fBox mtm mbs\">Port registers<\/h4>\n<p>This time, I used &#8216;Port registers&#8217; not &#8216;digitalRead&#8217; because it is more <a href=\"https:\/\/ehbtj.com\/electronics\/speedup-arduino\/\" target=\"_blank\" rel=\"noopener\">efficient<\/a>.<\/p>\n<p>So you can&#8217;t change pins easily. And it won&#8217;t work on Arduino which has different port number.<\/p>\n<p>Please make it change by yourself if you need modify.<\/p>\n<p>&#8220;PIN# &amp; _BV(#)&#8221; are the pin setting.<\/p>\n<p>Ref ) <a href=\"https:\/\/www.arduino.cc\/en\/Hacking\/Atmega168Hardware\" target=\"_blank\" rel=\"noopener\">Port of ArduinoUNO<\/a><\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false start-line:98 copy:false popup:false scroll:false lang:default mark:10-11,16-17 decode:true \" title=\"Direct read part\">void ENC_READ() {\r\n  static byte enc_gauge[ENC_NUM];\r\n\r\n  \/\/ read pins value\r\n  for (byte ii = 0 ; ii &lt; ENC_REPEAT ; ii++)\r\n  {\r\n    short pin_val[ENC_NUM];\r\n\r\n    \/\/ get current pins status of Encoder1\r\n    pin_val[0]  = ((PIND &amp; _BV(4)) ? 1 : 0) &lt;&lt; 1; \/\/ ENC A-1\r\n    pin_val[0] |= ((PINB &amp; _BV(4)) ? 1 : 0);      \/\/ ENC A-2\r\n\r\n#if(ENC_NUM &gt; 1)\r\n\r\n    \/\/ get current pins status of Encoder2\r\n    pin_val[1]  = ((PIND &amp; _BV(7)) ? 1 : 0) &lt;&lt; 1; \/\/ ENC B-1\r\n    pin_val[1] |= ((PINB &amp; _BV(0)) ? 1 : 0);      \/\/ ENC B-2\r\n\r\n#endif<\/pre>\n<p class=\"mtm\">By the way, the pins defining for pinMode needs to be changed too.<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false start-line:22 copy:false popup:false scroll:false lang:default mark:6-9 decode:true\" title=\"define pins part\">#define ENC_NUM       2\r\n#define ENC_TOLERANCE 25\r\n#define TIMER_INTVAL  2\r\n#define ENC_REPEAT    ENC_TOLERANCE * (TIMER_INTVAL \/ 2)\r\n\r\n#define ENCA_1 4\r\n#define ENCA_2 12\r\n#define ENCB_1 7\r\n#define ENCB_2 8\r\nbyte enc_pins[ENC_NUM * 2] = {ENCA_1, ENCA_2, ENCB_1, ENCB_2};<\/pre>\n<\/div>\n<\/div>\n<div id=\"chap_3\">\n<h2 class=\"h2_HLine mtm mbs\"><span class=\"ez-toc-section\" id=\"How_to_use_Dual_Encoder\"><\/span>How to use Dual Encoder<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Basically, simply use ENC_COUNT(#) function. You can use it anywhere you want after staring timer interrupt.<\/p>\n<p>Read <a href=\"https:\/\/jumbleat.com\/2016\/12\/17\/encoder_1\/\" target=\"_blank\" rel=\"noopener\">part 1<\/a> and <a href=\"https:\/\/jumbleat.com\/2017\/04\/20\/encoder_2\/\" target=\"_blank\" rel=\"noopener\">part 2<\/a> if you want to know how &#8220;Dual Encoder&#8221; works.<\/p>\n<p class=\"mtl\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8472 size-full\" src=\"https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_ins_01.jpg\" alt=\"\" width=\"825\" height=\"250\" srcset=\"https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_ins_01.jpg 825w, https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_ins_01-300x91.jpg 300w, https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_ins_01-768x233.jpg 768w\" sizes=\"auto, (max-width: 825px) 100vw, 825px\" \/><\/p>\n<p>Inefficient update on the sketch is caused of u8glib. Change the define value of &#8216;OLED_DRAW&#8217; to 0 if you want to kill it. And check the values on Serial Monitor.<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false start-line:9 copy:false popup:false scroll:false lang:default mark:4 decode:true\" title=\"u8glib part in defalut settings\">#include &lt;MsTimer2.h&gt;\r\n#define TIM MsTimer2\r\n\r\n#define OLED_DRAW 1\r\n\r\n#if(OLED_DRAW)\r\n\r\n#include &lt;U8glib.h&gt;\r\nU8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_DEV_0); \/\/ OLED \/ I2C \/ TWI\r\n\/\/ SDA 4\/ SDL 5\r\n\r\n#endif<\/pre>\n<div id=\"chap_3-2\">\n<h3 class=\"h3_HLine mtl mbs\"><span class=\"ez-toc-section\" id=\"Functions_of_%E2%80%98Dual_Encoder\"><\/span>Functions of &#8216;Dual Encoder&#8217;<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4 class=\"dot_rbox mts mbs\" style=\"font-size: 95%;\"><em>void<\/em> SET_ENC_RES (<em>byte encoder num, bool resolution<\/em>);<\/h4>\n<p>This set the resolution of the rotary encoder to use. There is no return value.<\/p>\n<p>If you set &#8216;resolution&#8217; to 1, it works as click-type.<\/p>\n<table>\n<tbody>\n<tr>\n<td style=\"width: 27%;\"><em>encoder num<\/em><\/td>\n<td style=\"font-size: 85%;\">Designation of encoder number. The first encoder is (0), the second one is (1).<\/td>\n<\/tr>\n<tr>\n<td><em>resolution<\/em><\/td>\n<td style=\"font-size: 85%;\">Specification of resolution<br \/>\n0(false)\u3000Non-click type<br \/>\n1(true)\u3000Click type<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If you want to the first encoder as click type,<\/p>\n<p style=\"padding-left: 30px;\">SET_ENC_RES(0, true);<\/p>\n<p>You don&#8217;t need to use this function if you use non-click type encoder. Default of this sketch is use of non-click type.<\/p>\n<h4 class=\"dot_rbox mtl mbs\" style=\"font-size: 95%;\"><em>boolean<\/em> ENC_CHECK (); \/\u00a0ENC_CHECK (<em>byte encoder num<\/em>);<\/h4>\n<p>This function is for check whether the encoder is counted or not. If there has change, it returns 1(true) otherwise 0(false).<\/p>\n<table>\n<tbody>\n<tr>\n<td style=\"width: 27%;\"><em>encoder num<\/em><\/td>\n<td style=\"font-size: 85%;\">Designation of encoder number. The first encoder is (0), the second one is (1).<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In case of ENC_CHECK (), it returns true if there is increase \/ decrease from any encoder.<\/p>\n<p>On the other hand, ENC_CHECK(#) returns true when the specifed one has changes.<\/p>\n<p>The difference-value of ENC_COUNT() is updated if you call it. So use this function when you don&#8217;t want to update the value.<\/p>\n<p>The return value of this function is reset after call &#8220;ENC_COUNT(#);&#8221;.<\/p>\n<h4 class=\"dot_rbox mtl mbs\" style=\"font-size: 95%;\"><em>int<\/em> ENC_COUNT (<em>byte\u00a0encoder num<\/em>);<\/h4>\n<p>This function returns difference-value of encoder count from previous call.<\/p>\n<table>\n<tbody>\n<tr>\n<td style=\"width: 27%;\"><em>encoder num<\/em><\/td>\n<td style=\"font-size: 85%;\">Designation of encoder number. The first encoder is (0), the second one is (1).<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Use this function like this. If you want count of the first encoder,<\/p>\n<p style=\"padding-left: 30px;\">val += ENC_COUNT(0);<\/p>\n<p>On this sketch, you may feel it slow because of u8glib. But the actual count of the encoder would be accurate, if it won&#8217;t catch chatterings.<\/p>\n<p>The return value of &#8220;ENC_CHECK();&#8221; function is reset after call this.<\/p>\n<h4 class=\"dot_rbox mtl mbs\" style=\"font-size: 95%;\"><em>void<\/em> ENC_RESET()<\/h4>\n<p>There are &#8216;enc_count[]&#8217; variable on the sketch to read continuous count of each encoder. This function reset the variables. This variable is set as int type. But it may overflows in case. Avoid the case by using this function.<\/p>\n<p>reference)\u3000<a href=\"https:\/\/www.arduino.cc\/en\/Reference\/Int\" target=\"_blank\" rel=\"noopener\">range of &#8216;int&#8217;<\/a>\u3000-32,768 \uff5e\u00a032,768<\/p>\n<\/div>\n<div id=\"chap_3-3\">\n<h3 class=\"h3_HLine mtm mbs\"><span class=\"ez-toc-section\" id=\"define\"><\/span>define<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4 class=\"dot_rbox mts mbs\" style=\"font-size: 95%;\">ENC_NUM<\/h4>\n<p>It specify the number of encoders.<\/p>\n<p>Reduce value if you use single encoder, because it reduces the load too. Don&#8217;t forget rewrite defines of pins.<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false start-line:22 copy:false popup:false scroll:false lang:default mark:1,10 decode:true\" title=\"example for single encoder use\">#define ENC_NUM       1\r\n#define ENC_TOLERANCE 25\r\n#define TIMER_INTVAL  2\r\n#define ENC_REPEAT    ENC_TOLERANCE * (TIMER_INTVAL \/ 2)\r\n\r\n#define ENCA_1 4\r\n#define ENCA_2 12\r\n\/\/#define ENCB_1 7\r\n\/\/#define ENCB_2 8\r\nbyte enc_pins[ENC_NUM * 2] = {ENCA_1, ENCA_2};<\/pre>\n<h4 class=\"dot_rbox mtl mbs\" style=\"font-size: 95%;\">ENC_TOLERANCE<\/h4>\n<p>As you know from <a href=\"https:\/\/jumbleat.com\/2017\/04\/20\/encoder_2\/\" target=\"_blank\" rel=\"noopener\">part 2<\/a>, this sketch use &#8220;<a href=\"https:\/\/jumbleat.com\/2016\/08\/19\/switch_without_chatter\/\" target=\"_blank\" rel=\"noopener\">gauging<\/a>&#8221; to avoid chattering. This is the value.<\/p>\n<p>You can&#8217;t set this value over 255.<\/p>\n<h4 class=\"dot_rbox mtl mbs\" style=\"font-size: 95%;\">TIMER_INTVAL<\/h4>\n<p>This value is interval time of timer interrupt (ms).<\/p>\n<p>If you reduce the value, accuracy of encoder reading is raised. But it may cause to worse response of whole sketch. Opposite, raising the value solves the problem although give up accuracy of encoder reading.<\/p>\n<h4 class=\"dot_rbox mtl mbs\" style=\"font-size: 95%;\">ENC_REPEAT<\/h4>\n<p>This is repeat times of &#8220;gauging&#8221; on one time of timer interrupt. This value also affects to the response of whole task.<\/p>\n<p>You can&#8217;t set this value over 255.<\/p>\n<p class=\"line_middle\">\u00a0<\/p>\n<p class=\"mtl\">ENC_TOLERANCE \/ TIMER_INTVAL \/ ENC_REPEAT, balance of those values affects accuracy of encoder reading and response of the sketch. Find proper value balance in your project.<\/p>\n<\/div>\n<\/div>\n<div id=\"chap_4\">\n<h2 class=\"h2_HLine mtm mbs\"><span class=\"ez-toc-section\" id=\"Expansion\"><\/span>Expansion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Although the purpose of this sketch is use two encoder, you can add more encoder by little rewrite.<\/p>\n<p>I tried use four encoder.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-8563\" src=\"https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_ins_02_re.jpg\" alt=\"\" width=\"1200\" height=\"600\" srcset=\"https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_ins_02_re.jpg 1200w, https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_ins_02_re-300x150.jpg 300w, https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/R_Encoder_3_ins_02_re-768x384.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/p>\n<p>But this affects timer interrupt duration. So be careful.<\/p>\n<p>This is example when you want to add third encoder.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"pin_define\"><\/span>pin define<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<pre class=\"start-line:22 lang:default mark:1,10-12 decode:true\" title=\"Add Rotary Encoder to A0,A1 pins\">#define ENC_NUM       3\r\n#define ENC_TOLERANCE 25\r\n#define TIMER_INTVAL  2\r\n#define ENC_REPEAT    ENC_TOLERANCE * (TIMER_INTVAL \/ 2)\r\n\r\n#define ENCA_1 4\r\n#define ENCA_2 12\r\n#define ENCB_1 7\r\n#define ENCB_2 8\r\n#define ENCC_1 A0\r\n#define ENCC_2 A1\r\nbyte enc_pins[ENC_NUM * 2] = {ENCA_1, ENCA_2, ENCB_1, ENCB_2, ENCC_1, ENCC_2};<\/pre>\n<h3><span class=\"ez-toc-section\" id=\"port_resister\"><\/span>port resister<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<pre class=\"start-line:106 lang:default mark:10-11 decode:true \" title=\"add port reading\">    \/\/ get current pins status of Encoder1\r\n    pin_val[0]  = ((PIND &amp; _BV(4)) ? 1 : 0) &lt;&lt; 1; \/\/ ENC A-1\r\n    pin_val[0] |= ((PINB &amp; _BV(4)) ? 1 : 0);      \/\/ ENC A-2\r\n\r\n#if(ENC_NUM &gt; 1)\r\n    \/\/ get current pins status of Encoder2\r\n    pin_val[1]  = ((PIND &amp; _BV(7)) ? 1 : 0) &lt;&lt; 1; \/\/ ENC B-1\r\n    pin_val[1] |= ((PINB &amp; _BV(0)) ? 1 : 0);      \/\/ ENC B-2\r\n\r\n    pin_val[2]  = ((PINC &amp; _BV(0)) ? 1 : 0) &lt;&lt; 1; \/\/ ENC C-1\r\n    pin_val[2] |= ((PINC &amp; _BV(1)) ? 1 : 0);      \/\/ ENC C-2\r\n#endif<\/pre>\n<p>The new encoder number will be (2).<\/p>\n<p style=\"padding-left: 30px;\">int val += ENC_COUNT(2);<\/p>\n<\/div>\n<div id=\"epilog\">\n<p class=\"mtm line_middle\">\u00a0<\/p>\n<p class=\"mtm\">Although there may be no such projects that handle multiple rotary encoders in many cases. But I think this sketch will work well even if a single encoder.<\/p>\n<ul class=\"mbl\" style=\"font-size: 85%; list-style: none;\">\n<li><a href=\"https:\/\/jumbleat.com\/2016\/12\/17\/encoder_1\/\">Using rotary encoder Part 1 : AttachInterrupt<\/a><\/li>\n<li><a href=\"https:\/\/jumbleat.com\/2017\/04\/20\/encoder_2\/\">Using rotary encoder Part 2 : Non-Click type<\/a><\/li>\n<li><a href=\"https:\/\/jumbleat.com\/2017\/10\/22\/encoder_3\/\">Using rotary encoder Part 3 : Dual Encoder<\/a><\/li>\n<\/ul>\n<p class=\"ref_site\">Reference Site<\/p>\n<ul class=\"ls_algn reference_links\">\n<li><a href=\"https:\/\/ehbtj.com\/\" target=\"_blank\" rel=\"noopener\">\u306a\u3093\u3067\u3082\u72ec\u308a\u8a00<\/a> &#8211; <a href=\"https:\/\/ehbtj.com\/electronics\/speedup-arduino\/\" target=\"_blank\" rel=\"noopener\">Arduino\u306e\u9ad8\u901f\u5316<\/a><\/li>\n<li><a href=\"https:\/\/www.arduino.cc\/\" target=\"_blank\" rel=\"noopener\">Arduino.CC<\/a>\u00a0 &#8211;\u00a0<a href=\"https:\/\/www.arduino.cc\/en\/Hacking\/Atmega168Hardware\" target=\"_blank\" rel=\"noopener\"> ATmega168-Arduino Pin Mapping<\/a><\/li>\n<li><a href=\"https:\/\/www.arduino.cc\/\" target=\"_blank\" rel=\"noopener\">Arduino.CC<\/a>\u00a0 &#8211;\u00a0<a href=\"https:\/\/www.arduino.cc\/en\/Reference\/Int\" target=\"_blank\" rel=\"noopener\"> int<\/a><\/li>\n<\/ul>\n<\/div>\n<p><\/p>","protected":false},"excerpt":{"rendered":"<p>Sorry, this entry is only available in \u65e5\u672c\u8a9e. For the sake of viewer convenience, the content is shown below in  &hellip; <a href=\"https:\/\/jumbleat.com\/en\/2017\/10\/22\/encoder_3\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Using rotary encoder <span class=\"sub_title\">part 3 : &#8220;Dual Encoder&#8221;<\/span><\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":8470,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[5,8],"tags":[11,30,280,277,276,378,28,31,275],"class_list":["post-8390","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-arduino","category-electric","tag-arduino","tag-chattering","tag-read-rotary-encoder","tag-rotary-encoder","tag-276","tag-378","tag-28","tag-31","tag-275"],"jetpack_featured_media_url":"https:\/\/jumbleat.com\/wp-content\/uploads\/2017\/10\/e_catch_encoder3.jpg","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p7JALB-2bk","jetpack_likes_enabled":false,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/posts\/8390","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/comments?post=8390"}],"version-history":[{"count":4,"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/posts\/8390\/revisions"}],"predecessor-version":[{"id":13629,"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/posts\/8390\/revisions\/13629"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/media\/8470"}],"wp:attachment":[{"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/media?parent=8390"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/categories?post=8390"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jumbleat.com\/en\/wp-json\/wp\/v2\/tags?post=8390"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}