{"id":18382431,"url":"https://github.com/ntd/ardecoder","last_synced_at":"2026-04-24T12:31:59.686Z","repository":{"id":136637817,"uuid":"233673149","full_name":"ntd/ardecoder","owner":"ntd","description":"Decoding up to 3 incremental encoders with Arduino Uno or Nano","archived":false,"fork":false,"pushed_at":"2025-09-11T09:36:17.000Z","size":138,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-11T12:40:37.075Z","etag":null,"topics":["arduino","encoder","incremental-encoder","nano","quadrature","rotary-encoder","uno"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ntd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-13T19:13:46.000Z","updated_at":"2025-09-11T09:36:21.000Z","dependencies_parsed_at":"2023-07-26T13:15:39.524Z","dependency_job_id":null,"html_url":"https://github.com/ntd/ardecoder","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ntd/ardecoder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntd%2Fardecoder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntd%2Fardecoder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntd%2Fardecoder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntd%2Fardecoder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ntd","download_url":"https://codeload.github.com/ntd/ardecoder/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntd%2Fardecoder/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32223871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T10:26:35.452Z","status":"ssl_error","status_checked_at":"2026-04-24T10:25:27.643Z","response_time":64,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","encoder","incremental-encoder","nano","quadrature","rotary-encoder","uno"],"created_at":"2024-11-06T01:05:38.741Z","updated_at":"2026-04-24T12:31:59.681Z","avatar_url":"https://github.com/ntd.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"Ardecoder: decoding up to 3 encoder with Arduino Uno or Nano.\n\n\nHOW TO USE\n----------\n\nYou need to have `arduino-cli` and `GNU make` installed on your system.\n\n```sh\n# For new Arduino NANO:\nmake\n# For older Arduino NANO (prior than 2018):\nmake MODEL=nano:cpu=atmega328old\n# For Arduino UNO:\nmake MODEL=uno\n```\nThis pulls in the required dependencies (e.g. `arduino:avr` platform\nsupport) and creates the hex file to upload (`build/ardecoder.ino.hex`).\n\nThen connect your device and upload. By default the makefile expects\nthe board to be connected to `/dev/ttyUSB0` but you can change that by\nusing the `DEVICE` argument.\n\n```sh\n# Upload to the default device (/dev/ttyUSB0)\nmake upload\n# Upload to a specific device\nmake upload DEVICE=/dev/ttyACM1\n```\n\n\nELECTRICAL CONNECTIONS\n----------------------\n\n|     |               |\n| --- | ------------- |\n| D2  | Encoder 1 (A) |\n| D3  | Encoder 1 (B) |\n| D4  | Encoder 2 (A) |\n| D5  | Encoder 2 (B) |\n| D6  | Encoder 3 (A) |\n| D7  | Encoder 3 (B) |\n|     |               |\n| D9  | Encoder 1 (Z) |\n| D10 | Encoder 2 (Z) |\n| D11 | Encoder 3 (Z) |\n\nIf fewer encoders are needed, do not connect them. This also works with\nincremental encoders without the Z index: in this case the counter will\nbe set to 0 at startup and never reset.\n\nYou can skip any encoder, e.g. you are allowed to connect only encoder 2\nand 3 without using encoder 1.\n\n![Only encoder 1 connected](./photo.jpeg)\n\nTo improve reliability, you can put photocouplers between encoder and\narduino, as shown by the above example where only encoder 1 is connected\n(see [the schematic](./ardecoder.pdf) for details). In that case,\nremember to consider the maximum speed of the photocoupler too.\n\nA [public video](https://www.youtube.com/watch?v=IgxP2A8nXgc) shows\nanother example on Arduino UNO without photocouplers.\n\n\nHOW DECODING WORKS\n------------------\n\nThe code keeps the last state of A B phase (`old`) and compares it to\nthe new state (`now`). Any variation will increase or decrease the\ncounter (`raw`) depending on a lookup table (`lut`).\n\nIf `OVERFLOW` is defined, it is checked if a phase is skipped (e.g. when\nthe encoder is turning too quickly) by leveraging another lookup table\n(`skp`). If that is the case, the counter is incremented or decremented\nby 2, depending on the last adjustment.\n\n```\n                 _______         _______\n        A ______|       |_______|       |______\nCCW \u003c--      _______         _______         __  --\u003e CW\n        B __|       |_______|       |_______|\n```\n\n| now\u003cbr\u003eA B | old\u003cbr\u003eA B | lut\u003cbr\u003e\u0026nbsp; | skp\u003cbr\u003e\u0026nbsp;  | |\n| --- | --- | ---:|:-----:| ---\n| 0 0 | 0 0 |   0 | false |  No movement\n| 0 0 | 0 1 |  +1 | false |\n| 0 0 | 1 0 |  -1 | false |\n| 0 0 | 1 1 |   0 |  true |  One step has been skipped\n| 0 1 | 0 0 |  -1 | false |\n| 0 1 | 0 1 |   0 | false |  No movement\n| 0 1 | 1 0 |   0 |  true |  One step has been skipped\n| 0 1 | 1 1 |  +1 | false |\n| 1 0 | 0 0 |  +1 | false |\n| 1 0 | 0 1 |  -2 |  true |  One step has been skipped\n| 1 0 | 1 0 |   0 | false |  No movement\n| 1 0 | 1 1 |  -1 | false |\n| 1 1 | 0 0 |  +2 |  true |  One step has been skipped\n| 1 1 | 0 1 |  -1 | false |\n| 1 1 | 1 0 |  +1 | false |\n| 1 1 | 1 1 |   0 | false |  No movement\n\nAll 3 counters are adjusted according to the above decoding algorithm by\nthe PCINT2 interrupt handler, called whenever any of the PIND bits (i.e.\nany D0..D7 digital input) changes.\n\n\nPERFORMANCES\n------------\n\nNo serious attempts at counting CPU cycles has been performed. I am not\neven sure how much the serial communication impacts the overall\nperformance but I am pretty confident this thing can read more than 20K\nsteps per second with 3 encoders enabled and `OVERFLOW` disabled.\n\nOn real test cases I have seen counting more than 30K steps per seconds\n(more than 3K steps in 0.1 seconds, to be exact) but I think I am just\nhitting the speed limit of the photocouplers I used.\n\n\nPROTOCOL\n--------\n\nThe communication protocol on the USB bus is text based, so it can be\nused interactively with a serial console. Requests and responses are\nexpected to be newline terminated, and any newline format is accepted:\n`\"\\n\"`, `\"\\r\"` or `\"\\r\\n\"`. Anything between `\"#\"` and a newline is\nconsidered a comment and it is ignored.\n\n### GET ENCODER DATA\n\nTo query an encoder, just send its number (`1`, `2` or `3`) followed by\na newline.\n\n- Example request: `\"1\\n\"`\n- Example response: `\"1 -581 0 1\\r\\n\"`\n\nThe returned fields are respectively:\n\n1. the number of the encoder (`1`, `2` or `3`, the same as the request);\n2. the raw counter value;\n3. `1` if homed (it passed through the Z phase), `0` otherwise;\n4. the number of times a phase was skipped (if `OVERFLOW` is enabled).\n\n### SLAVE OR PUSH MODE\n\nIn slave mode (the default), you must manually get the encoder data via\nspecific queries. For monitoring you must implement by yourself a\npolling loop.\n\nIn push mode, additionally the encoder data is sent without request\nwhenever its position changes. The maximum rate of changes (i.e. the\nminimum time between two unsolicited responses) is customizable\n\nTo enable push mode, use `\"S\"` followed by the rate time (in\nmilliseconds) and a newline. To disable push mode and turn back in slave\nmode, specify a rate time of 0.\n\n- Request to enable push mode every 0,2 s: `\"S200\\n\"`\n- Request to disable push mode: `\"S0\\n\"`\n\n\nLICENSE\n-------\n\nArdecoder is [MIT licensed](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntd%2Fardecoder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fntd%2Fardecoder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntd%2Fardecoder/lists"}