{"id":17948594,"url":"https://github.com/markdapiggy/arduino-pseudotasks","last_synced_at":"2026-04-10T23:52:54.266Z","repository":{"id":259851525,"uuid":"879625660","full_name":"MarkDaPiggy/Arduino-PseudoTasks","owner":"MarkDaPiggy","description":"PseudoTasks is an extremely lightweight co-operative multitasking mechanism for the Arduino platform","archived":false,"fork":false,"pushed_at":"2024-10-28T11:08:39.000Z","size":25,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-09T04:16:56.062Z","etag":null,"topics":["arduino","arduino-library","c","cpp"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MarkDaPiggy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-10-28T09:03:36.000Z","updated_at":"2024-10-28T11:08:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"80f5e450-b4db-400d-92d3-037128457db7","html_url":"https://github.com/MarkDaPiggy/Arduino-PseudoTasks","commit_stats":null,"previous_names":["markdapiggy/arduino-pseudotasks"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarkDaPiggy%2FArduino-PseudoTasks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarkDaPiggy%2FArduino-PseudoTasks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarkDaPiggy%2FArduino-PseudoTasks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarkDaPiggy%2FArduino-PseudoTasks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MarkDaPiggy","download_url":"https://codeload.github.com/MarkDaPiggy/Arduino-PseudoTasks/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247026139,"owners_count":20871325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","arduino-library","c","cpp"],"created_at":"2024-10-29T09:07:19.409Z","updated_at":"2026-04-10T23:52:54.203Z","avatar_url":"https://github.com/MarkDaPiggy.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# The Why, What and How of PseudoTasks\n\n**[Jump to Installation Instructions](#installation-instructions)**\n\n## 1. Why?\n\nWhen programs grow in complexity and a level of concurrency is required, preemptive multitasking kernels (such as RTOS) are often seen as a solution but Preemptive Multitasking is fraught with issues.\n  - code needs to be re-entrant\n  - resources need to be protected from contention\n  - race conditions need to be avoided\n  - priorities need to be managed\n  - debugging becomes very difficult\n  - there is an addition time and space (and financial) cost\n\nIf you don't understand the implications of all the above, for your own sake,\nplease don't use preemptive multitasking!\n \nArduino code is simple enough (compared to (say) MS Windows) that we can avoid\nusing preemptive multitasking in most cases through sensible program structure\nand co-routines.\n\n\n## 2. What\n\nPseudoTasks are implemented as a set of #defined 'gate' macros in PseudoTasks.h.\n(PseudoTasks.cpp just includes PseudoTasks.h). Gate macros are placed at the\nstart of a task and determine when to run the task.\n\n**Gate Macros:**\n\n**RUN_ALWAYS**                                      \n\u003ethe task is always run\n\n**RUN_NEVER**                                       \n\u003ethe task is never run (useful for disabling a task while leaving the code in place\n\n**RUN_IF(Expr)**                                    \n\u003ethe task is run if the expression is true\n\n**RUN_EVERY_MILLISECONDS(MilliSeconds)**            \n\u003ethe task is run if at least MilliSeconds has elapsed. NOTE: elapsed time since \nthe last time the task was started, not time is was completed.\n \n**RUN_EVERY_MICROSECONDS(MicroSeconds)**            \n\u003esame as for RUN_EVERY_MILLISECONDS, except the parameters is microseconds \nrather then milliseconds.\n \n**RUN_ON_PIN(InputPin, DebounceDelay, Inverted)**\n\u003ethe task is run depending on the state of InputPin. High if inverted is false, \nLow if inverted is true. For a simple check of the pin, set\nDebounceDelay to 0. If the DebounceDelay is greater then 0, the pin is\nread a twice and the task only run if the value is unchanged. This filters\nout simple switch bouncing but might not be adequate in all cases.\n\n\n## 3A. How to do short tasks\n\n1. Write each of your 'tasks' as a function.\n2. Call each of your functions from loop() like this:\n\n```C++\n    void loop()\n    {\n        task1();\n        task2();\n        task3();\n        task4();\n        task5();\n    }\n``` \n\n3. For each task, use the gate macros to determine if the task is ready to run.\n\n```C++\nvoid task1()\n{\n  RUN_ALWAYS();\n  Serial.println(\"Task 1 always runs\");\n}\n\nvoid task2()\n{\n  RUN_NEVER();\n  Serial.println(\"Task 2 never runs\");\n}\n\nvoid task3()\n{\n  RUN_EVERY_MILLISECONDS(500);\n  Serial.println(\"Task 3 runs every half a second\");\n}\n\nvoid task4()\n{\n  RUN_EVERY_MICROSECONDS(50);\n  Serial.println(\"Task 4 runs lots but there is a delay \"\n    \"of 50 microseconds between runs\");\n}\n\nvoid task5()\n{\n  RUN_ON_PIN(D5, 0, true)   \n  Serial.println(\"Task 5 runs if Pin D5 is low\");\n}\n```\n\n## 3B. How to do long tasks\n\nLong tasks need to be structured so that they don't block the other tasks. Long\ntasks are usually long because they have embedded delays. One way to manage \nthis situation is to use co-routines.\n\n\nFrom google:\n\n\u003eA coroutine is a special type of function that can suspend and resume its \nexecution at specific points, allowing it to yield control to other \ncoroutines or the main program. This cooperative multitasking approach \nenables efficient and lightweight concurrency, making coroutines a popular \nchoice for implementing asynchronous programming, event-driven systems, and \nconcurrent computations.\n\nCo-routines can be implemented as a state transition engine using a switch \nstatement. This is easier to do than to describe but here goes...\n\nConsider the following function:\n\n```C++\nvoid\nMultiStep(void)\n{\n  Serial.println(\"Step 0\");\n  delay(100);\n  Serial.println(\"Step 1\"); \n  delay(1000);     \n  Serial.println(\"Step 2\");\n  delay(5000);     \n  Serial.println(\"Step 3\");\n  delay(50);     \n}\n```\n\nThis function will take at least 6.6 seconds to run and will block all other \ntasks for this duration. To implement this as a co-routine:\n\n1. Identify each delay and think of the code between delays as a transition \n   between states.\n2. Create and set a state variable to 0. We change this value as we change \n   states.\n3. Create and set a delay vairalbe to 0. We delay by this amount each time we \n   re-enter the function.\n3. Create a switch statement on the state variable with a case for each state.\n4. Put the transition code in each case block. Set the next step appropriately. \n   Calls to delay() are replaced by code to set the desired delay and a return.\n   This allows other tasks to run other tasks while we are delayed.\n\nNew code:\n\n```C++\nvoid\nMultiStep(void)\n{\n  static int Step = 0;\n  static int Delay = 0;\n\n  RUN_EVERY(Delay);\n\n  switch (Step) {\n    case 0:\n        Serial.println(\"Step 0\");\n        CHANGE_STATE(1, 100);\n    case 1:\n        Serial.println(\"Step 1\"); \n        CHANGE_STATE(2, 1000);        \n    case 2:\n        Serial.println(\"Step 2\");\n        CHANGE_STATE(3, 5000);                \n    case 3:\n        Serial.println(\"Step 3\");\n        CHANGE_STATE(1, 50);                        \n  }\n}\n```\n\n## Installation Instructions\n\n1. Download the file PseudoTasks.ZIP\n1. Open Arduino IDE\n1. Sketch|Include Library|Add ZIP library\n1. Select previously saved attachment\n\nTo run the sample...\n\u003eFile|Examples|PseudoTasks|TaskSample\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkdapiggy%2Farduino-pseudotasks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarkdapiggy%2Farduino-pseudotasks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkdapiggy%2Farduino-pseudotasks/lists"}