{"id":174275,"date":"2025-10-09T13:44:21","date_gmt":"2025-10-09T13:44:21","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=174275"},"modified":"2025-11-20T14:30:34","modified_gmt":"2025-11-20T14:30:34","slug":"esp32-freertos-semaphores-arduino","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-freertos-semaphores-arduino\/","title":{"rendered":"ESP32 with FreeRTOS: Getting Started with Semaphores (Arduino IDE)"},"content":{"rendered":"\n<p>In this guide, we&#8217;ll introduce you to and explain how to use FreeRTOS semaphores with the ESP32, using the Arduino IDE. Semaphores are like signals (or flags) that allow you to synchronize tasks and manage events. They can be used to indicate that an event has occurred or that a resource is available. Unlike queues, semaphores don&#8217;t carry data.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-FreeRTOS-semaphores.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 FreeRTOS Semaphores Arduino IDE Getting Started Guide\" class=\"wp-image-174593\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-FreeRTOS-semaphores.jpg?w=1920&amp;quality=100&amp;strip=all&amp;ssl=1 1920w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-FreeRTOS-semaphores.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-FreeRTOS-semaphores.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-FreeRTOS-semaphores.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-FreeRTOS-semaphores.jpg?resize=1536%2C864&amp;quality=100&amp;strip=all&amp;ssl=1 1536w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>There are two types of semaphores: <strong>binary semaphores <\/strong>and <strong>counting semaphores<\/strong>. In this tutorial, we&#8217;ll create and explore two different examples to show you how these two types of semaphores work.<\/p>\n\n\n\n<p class=\"rntbox rntclgreen\"><strong>New to FreeRTOS?&nbsp;<\/strong>Start with this tutorial: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-freertos-arduino-tasks\/\">ESP32 with FreeRTOS (Arduino IDE)\u2014Getting Started Guide: Creating Tasks<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introducing Semaphores<\/h2>\n\n\n\n<p>Semaphores are signaling tools in FreeRTOS used to coordinate tasks. They are commonly used to indicate that an event has occurred or that a resource is available. Unlike queues, semaphores don&#8217;t carry data, only a &#8220;count&#8221; , or &#8220;flag&#8221;, or &#8220;signal&#8221; (or whatever you want to call it) used to trigger actions when something happens.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"457\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/binary-semaphore-how-it-works.png?resize=750%2C457&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Simple example of how a binary semaphore works\" class=\"wp-image-174597\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/binary-semaphore-how-it-works.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/binary-semaphore-how-it-works.png?resize=300%2C183&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><figcaption class=\"wp-element-caption\">Simple example of how a binary semaphore works<\/figcaption><\/figure><\/div>\n\n\n<p>They allow tasks to wait for events, such as a button press or motion detection, or to signal that an event has occurred. This makes them especially useful in scenarios involving interrupts and task synchronization.<\/p>\n\n\n\n<p>Since semaphores don\u2019t store data, they consume less memory than queues and are ideal for lightweight event signaling between tasks.<\/p>\n\n\n\n<p>There are two types of semaphores:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Binary semaphore<\/strong>: signals a single event. It is a synchronization tool that can be either empty (0) or full (1). It&#8217;s like a signal that a task is waiting for before it can proceed.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Counting semaphore<\/strong>: tracks multiple events (it can be the same event several times). It&#8217;s like a queue of events up to a maximum count that you define. Contrary to FreeRTOS queues, these don&#8217;t carry data. Only a synchronization signal. You&#8217;ll better understand how this works later in the example.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Semaphores Basic Functions<\/h2>\n\n\n\n<p>Here are some basic functions of binary and counting semaphores<strong> <\/strong>when using the ESP32 with Arduino IDE. We&#8217;ll explore these functions in practical examples next.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating a Binary Semaphore<\/h3>\n\n\n\n<p>To create a binary semaphore, use the <span class=\"rnthl rntliteral\">xSemaphoreCreateBinary()<\/span> function. It returns a <span class=\"rnthl rntliteral\">SemaphoreHandle_t<\/span> handle if successful, or <span class=\"rnthl rntliteral\">NULL<\/span> if the creation fails.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating a Counting Semaphore<\/h3>\n\n\n\n<p>To create a counting semaphore, use the <span class=\"rnthl rntliteral\">xSemaphoreCreateCounting()<\/span> function. It returns a <span class=\"rnthl rntliteral\">SemaphoreHandle_t<\/span> handle if successful, or <span class=\"rnthl rntliteral\">NULL<\/span> if the creation fails. Pass as an argument the maximum count.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Taking a Semaphore (Getting from the Semaphore)<\/h3>\n\n\n\n<p>Use the <span class=\"rnthl rntliteral\">xSemaphoreTake(semaphore, timeout)<\/span> function in a task to wait for or take a semaphore. For a binary semaphore, it blocks until the semaphore is available (state 1), setting it to 0 when taken.<\/p>\n\n\n\n<p>For a counting semaphore, it decrements the count if greater than 0, or blocks if the count is 0. The timeout parameter specifies how long to wait (in ticks); <span class=\"rnthl rntliteral\">portMAX_DELAY<\/span> means wait indefinitely. This means the task will be blocked until there&#8217;s a semaphore value to take.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Giving a Semaphore<\/h3>\n\n\n\n<p>To give a semaphore use the <span class=\"rnthl rntliteral\">xSemaphoreGive()<\/span> function if inside a task, or <span class=\"rnthl rntliteral\">xSemaphoreGiveFromISR()<\/span> if used in ISRs (interrupt service routine functions).<\/p>\n\n\n\n<p>For a binary semaphore, it sets the state to 1, unblocking a waiting task (or ignored if the semaphore is already 1). For a counting semaphore, it increments the count up to <span class=\"rnthl rntliteral\">maxCount<\/span>, unblocking a waiting task (ignored if at <span class=\"rnthl rntliteral\">maxCount<\/span>).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example 1: Binary Semaphore &#8211; Toggling an LED with a Button Press<\/h2>\n\n\n\n<p>In this section, we&#8217;ll build a simple example to demonstrate how a binary semaphore works and how to implement it in a practical application. This example will toggle an LED once when the pushbutton is pressed. To signal the button press, we&#8217;ll use a semaphore. Simultaneously, we&#8217;ll have another task blinking an LED to demonstrate that we can have multiple tasks running simultaneously.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-one-pushbutton-two-LEDs.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 with one pushbutton and two LEDs - example to demonstrate binary semaphores\" class=\"wp-image-174598\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-one-pushbutton-two-LEDs.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-one-pushbutton-two-LEDs.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>So, here&#8217;s an overview of the example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We&#8217;ll add an interrupt to a pushbutton. When the pushbutton is pressed, the corresponding ISR will give the semaphore (will set it to 1).<\/li>\n\n\n\n<li>There&#8217;s another task, called <span class=\"rnthl rntliteral\">LEDToggleTask()<\/span>, that will be waiting for the semaphore to toggle the state of the LED. When the semaphore is given by the ISR, this task will run and the semaphore will be reset to 0. Only when the semaphore is set to 1, when the button is pressed, this task will run again.<\/li>\n\n\n\n<li>Simultaneously, we have another task called <span class=\"rnthl rntliteral\">LEDBlinkTask()<\/span> that will increase and decrease the brightness of another LED.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Parts Required<\/h3>\n\n\n\n<p>Here&#8217;s a list of the parts required for this example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32 Board<\/a>&nbsp;of your choice&nbsp;\u2013 read&nbsp;<a href=\"https:\/\/makeradvisor.com\/esp32-development-boards-review-comparison\/\" target=\"_blank\" rel=\"noreferrer noopener\">Best ESP32 Development Boards<\/a><\/li>\n\n\n\n<li>2x <a href=\"https:\/\/makeradvisor.com\/tools\/3mm-5mm-leds-kit-storage-box\/\" target=\"_blank\" rel=\"noreferrer noopener\">LED<\/a><\/li>\n\n\n\n<li>2x <a href=\"https:\/\/makeradvisor.com\/tools\/resistors-kits\/\" target=\"_blank\" rel=\"noreferrer noopener\">220 Ohm resistor<\/a>&nbsp;or similar value<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/pushbuttons-kit\/\" target=\"_blank\" rel=\"noopener\" title=\"\">Pushbutton<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/mb-102-solderless-breadboard-830-points\/\" target=\"_blank\" rel=\"noreferrer noopener\">Breadboard<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/jumper-wires-kit-120-pieces\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jumper wires<\/a><\/li>\n<\/ul>\n\n\n<p>You can use the preceding links or go directly to <a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\">MakerAdvisor.com\/tools<\/a> to find all the parts for your projects at the best price!<\/p><p style=\"text-align:center;\"><a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/10\/header-200.png?w=1200&#038;quality=100&#038;strip=all&#038;ssl=1\"><\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wiring the Circuit<\/h3>\n\n\n\n<p>Wire the following circuit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Red LED connected to <span class=\"rnthl rntcred\">GPIO 2<\/span><\/li>\n\n\n\n<li>Blue LED connected to <span class=\"rnthl rntcblue\">GPIO 4<\/span><\/li>\n\n\n\n<li>Pushbutton connected to <span class=\"rnthl rntcgreen\">GPIO 23<\/span><\/li>\n<\/ul>\n\n\n\n<p>You can follow the next schematic diagram.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"899\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-Two-LEDs-One-Pushbutton_bb.png?resize=786%2C899&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 with two LEDs and one pushbutton schematic diagram\" class=\"wp-image-174599\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-Two-LEDs-One-Pushbutton_bb.png?w=786&amp;quality=100&amp;strip=all&amp;ssl=1 786w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-Two-LEDs-One-Pushbutton_bb.png?resize=262%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 262w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-Two-LEDs-One-Pushbutton_bb.png?resize=768%2C878&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 786px) 100vw, 786px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">Code<\/h3>\n\n\n\n<p>Upload the following code to the Arduino IDE.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-freertos-semaphores-arduino\/\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n#define BUTTON_PIN 23\n#define LED1_PIN 2   \/\/ Toggled LED\n#define LED2_PIN 4   \/\/ Blinking LED\n\n#define DEBOUNCE_DELAY 200\n\nSemaphoreHandle_t buttonSemaphore = NULL;\n\nvolatile uint32_t lastInterruptTime = 0;\n\nvoid IRAM_ATTR buttonISR() {\n  uint32_t currentTime = millis();\n  if (currentTime - lastInterruptTime &gt; DEBOUNCE_DELAY) {\n    BaseType_t higherPriorityTaskWoken = pdFALSE;\n    xSemaphoreGiveFromISR(buttonSemaphore, &amp;higherPriorityTaskWoken);\n    lastInterruptTime = currentTime;\n    if (higherPriorityTaskWoken) {\n      portYIELD_FROM_ISR();\n    }\n  }\n}\n\nvoid LEDToggleTask(void *parameter) {\n  pinMode(LED1_PIN, OUTPUT);\n  bool ledState = false;\n  for (;;) {\n    if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {\n      ledState = !ledState;\n      digitalWrite(LED1_PIN, ledState ? HIGH : LOW);\n      Serial.print(&quot;LEDToggleTask: LED1 &quot;);\n      Serial.println(ledState ? &quot;ON&quot; : &quot;OFF&quot;);\n    }\n  }\n}\n\nvoid LEDBlinkTask(void *parameter) {\n  pinMode(LED2_PIN, OUTPUT);\n  for (;;) {\n    digitalWrite(LED2_PIN, HIGH);\n    Serial.println(&quot;LEDBlinkTask: LED2 ON&quot;);\n    vTaskDelay(250 \/ portTICK_PERIOD_MS);\n    digitalWrite(LED2_PIN, LOW);\n    Serial.println(&quot;LEDBlinkTask: LED2 OFF&quot;);\n    vTaskDelay(250 \/ portTICK_PERIOD_MS);\n  }\n}\n\nvoid setup() {\n  Serial.begin(115200);\n\n  \/\/ Defining the button as an interrupt\n  pinMode(BUTTON_PIN, INPUT_PULLUP);\n  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);\n\n  buttonSemaphore = xSemaphoreCreateBinary();\n  if (buttonSemaphore == NULL) {\n    Serial.println(&quot;Failed to create semaphore!&quot;);\n    while (1);\n  }\n\n  xTaskCreatePinnedToCore(\n    LEDToggleTask,          \/\/ Task function\n    &quot;LEDToggleTask&quot;,        \/\/ Task name\n    3000,                   \/\/ Stack size\n    NULL,                   \/\/ Task parameters\n    2,                      \/\/ Higher priority\n    NULL,                   \/\/ Task handle\n    1                       \/\/ Core ID\n  );\n\n  xTaskCreatePinnedToCore(\n    LEDBlinkTask,           \/\/ Task function\n    &quot;LEDBlinkTask&quot;,         \/\/ Task name\n    3000,                   \/\/ Stack size\n    NULL,                   \/\/ Task parameters\n    1,                      \/\/ Medium priority\n    NULL,                   \/\/ Task handle\n    1                       \/\/ Core ID\n  );\n}\n\nvoid loop() {\n  \n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/FreeRTOS\/Binary_Semaphore_LED.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How Does the Code Work?<\/h3>\n\n\n\n<p>Start by defining the pins for the pushbutton, for the toggled LED, and for the blinking LED.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#define BUTTON_PIN 23\n#define LED1_PIN 2    \/\/ Toggled LED\n#define LED2_PIN 4    \/\/ Blinking LED<\/code><\/pre>\n\n\n\n<p>Define the debounce delay for the pushbutton in milliseconds.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#define DEBOUNCE_DELAY 200<\/code><\/pre>\n\n\n\n<p>Create an handle for the semaphore called <span class=\"rnthl rntliteral\">buttonSemaphore<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>SemaphoreHandle_t buttonSemaphore = NULL;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">setup()<\/h4>\n\n\n\n<p>Let&#8217;s explain the <span class=\"rnthl rntliteral\">setup()<\/span> first, and then analyse the tasks.<\/p>\n\n\n\n<p>First, set the pushbutton as an interrupt and set its callback function (ISR). In this case, it&#8217;s called <span class=\"rnthl rntliteral\">buttonISR<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>pinMode(BUTTON_PIN, INPUT_PULLUP);\nattachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);<\/code><\/pre>\n\n\n\n<p>We create a binary semaphore using the <span class=\"rnthl rntliteral\">xSemaphoreCreateBinary()<\/span> function on the <span class=\"rnthl rntliteral\">buttonSemaphore<\/span> handle we&#8217;ve created previously.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>buttonSemaphore = xSemaphoreCreateBinary();\nif (buttonSemaphore == NULL) {\n  Serial.println(\"Failed to create semaphore!\");\n  while (1);\n}<\/code><\/pre>\n\n\n\n<p>Then, we create the <span class=\"rnthl rntliteral\">LedToggleTask<\/span> and the <span class=\"rnthl rntliteral\">LEDBlinkTask<\/span> with different priorities.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>xTaskCreatePinnedToCore(\n  LEDToggleTask,          \/\/ Task function\n  \"LEDToggleTask\",        \/\/ Task name\n  3000,                   \/\/ Stack size\n  NULL,                   \/\/ Task parameters\n  2,                      \/\/ Higher priority\n  NULL,                   \/\/ Task handle\n  1                       \/\/ Core ID\n);\n\nxTaskCreatePinnedToCore(\n  LEDBlinkTask,           \/\/ Task function\n  \"LEDBlinkTask\",         \/\/ Task name\n  3000,                   \/\/ Stack size\n  NULL,                   \/\/ Task parameters\n  1,                      \/\/ Medium priority\n  NULL,                   \/\/ Task handle\n  1                       \/\/ Core ID\n);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">buttonISR()<\/h4>\n\n\n\n<p>The following lines create the <span class=\"rnthl rntliteral\">buttonISR()<\/span> function.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void IRAM_ATTR buttonISR() {\n  uint32_t currentTime = millis();\n  if (currentTime - lastInterruptTime &gt; DEBOUNCE_DELAY) {\n    BaseType_t higherPriorityTaskWoken = pdFALSE;\n    xSemaphoreGiveFromISR(buttonSemaphore, &amp;higherPriorityTaskWoken);\n    lastInterruptTime = currentTime;\n    if (higherPriorityTaskWoken) {\n      portYIELD_FROM_ISR();\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>When the button is pressed, the <span class=\"rnthl rntliteral\">buttonISR()<\/span> function will run. If we have a valid press, it uses FreeRTOS semaphore functions to signal another task that the button event has occurred.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\"><strong>Giving the Semaphore<\/strong><\/h5>\n\n\n\n<p>The following line is the key semaphore action. It \u201cgives\u201d the semaphore. This semaphore is like a signal that tells another part of the program (usually a FreeRTOS task\u2014in our case, it&#8217;s the <span class=\"rnthl rntliteral\">LEDToggleTask<\/span>) that the button was pressed.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>xSemaphoreGiveFromISR(buttonSemaphore, &amp;higherPriorityTaskWoken);<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\"><strong>higherPriorityTaskWoken and portYIELD_FROM_ISR<\/strong><\/h5>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">higherPriorityTaskWoken<\/span> variable is used to check if giving the semaphore will wake up a task that has a higher priority than the currently running task. If it does, we call <span class=\"rnthl rntliteral\">portYIELD_FROM_ISR()<\/span> to let the system immediately switch to that higher-priority task right after the interrupt finishes. In our case, we want to switch immediately to the <span class=\"rnthl rntliteral\">LEDToggleTask<\/span>.<\/p>\n\n\n\n<p>In other words:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Basically, <span class=\"rnthl rntliteral\">higherPriorityTaskWoke<\/span>n is used to check if giving the semaphore unblocked a more important task.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We pass it to <span class=\"rnthl rntliteral\">xSemaphoreGiveFromISR()<\/span> so it can update the value.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If it&#8217;s <span class=\"rnthl rntliteral\">pdTRUE<\/span>, we call <span class=\"rnthl rntliteral\">portYIELD_FROM_ISR()<\/span> to let FreeRTOS switch to that task immediately.<\/li>\n<\/ul>\n\n\n\n<p>This is how FreeRTOS lets interrupts safely trigger high-priority tasks without causing problems in task scheduling.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">LEDToggleTask<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">LEDToggleTask()<\/span> will toggle the state of LED1 when there&#8217;s a value on the semaphore.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void LEDToggleTask(void *parameter) {\n  pinMode(LED1_PIN, OUTPUT);\n  bool ledState = false;\n  for (;;) {\n    if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {\n      ledState = !ledState;\n      digitalWrite(LED1_PIN, ledState ? HIGH : LOW);\n      Serial.print(\"LEDToggleTask: LED1 \");\n      Serial.println(ledState ? \"ON\" : \"OFF\");\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>When the task <span class=\"rnthl rntliteral\">LEDToggleTask()<\/span> runs, it sets up the LED pin as an output and starts an infinite loop. Inside the loop, it waits for the semaphore using <span class=\"rnthl rntliteral\">xSemaphoreTake(buttonSemaphore, portMAX_DELAY)<\/span>. The <span class=\"rnthl rntliteral\">portMAX_DELAY<\/span> means that the task will wait indefinitely until there&#8217;s a value on the semaphore (until the button is pressed).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {<\/code><\/pre>\n\n\n\n<p>When the semaphore is received, the task will toggle the state of the LED and print it to the Serial Monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {\n  ledState = !ledState;\n  digitalWrite(LED1_PIN, ledState ? HIGH : LOW);\n  Serial.print(\"LEDToggleTask: LED1 \");\n  Serial.println(ledState ? \"ON\" : \"OFF\");<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">LEDBlinkTask<\/h4>\n\n\n\n<p>Besides the other task, we have the <span class=\"rnthl rntliteral\">LEDBlinkTask<\/span> that runs independently and simultaneously, blinking an LED indefinitely.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void LEDBlinkTask(void *parameter) {\n  pinMode(LED2_PIN, OUTPUT);\n  for (;;) {\n    digitalWrite(LED2_PIN, HIGH);\n    Serial.println(\"LEDBlinkTask: LED2 ON\");\n    vTaskDelay(250 \/ portTICK_PERIOD_MS);\n    digitalWrite(LED2_PIN, LOW);\n    Serial.println(\"LEDBlinkTask: LED2 OFF\");\n    vTaskDelay(250 \/ portTICK_PERIOD_MS);\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Demonstration<\/h3>\n\n\n\n<p>Upload the code to your board. After uploading, open the Serial Monitor at a baud rate of 115200. Press the ESP32 RST button so that it starts running the code.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-pushbutton.two-LEDs-pressing-button.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 with two LEDs and a pushbutton - pushbutton being pressed to toggle the state of an LED\" class=\"wp-image-174601\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-pushbutton.two-LEDs-pressing-button.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-pushbutton.two-LEDs-pressing-button.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>The LED connected to <span class=\"rnthl rntcblue\">GPIO 4<\/span> will be blinking every 250 milliseconds. Press the pushbutton to toggle the state of the LED connected to <span class=\"rnthl rntcred\">GPIO 2<\/span>. You can see a little demonstration in the short video below.<\/p>\n\n\n<div style=\"text-align:center\"><iframe src=\"https:\/\/player.vimeo.com\/video\/1125868051?color=ff9933&title=1&byline=0&portrait=0\" width=\"720\" height=\"405\" frameborder=\"0\" webkitallowfullscreen mozallowfullscreen allowfullscreen><\/iframe><\/div><\/br>\n\n\n\n<p>In the Serial Monitor, you should get something similar.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"707\" height=\"373\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-binary-semaphores-Serial-Monitor-Demonstration.jpg?resize=707%2C373&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Binary Semaphore Example - Serial Monitor Demonstration\" class=\"wp-image-174473\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-binary-semaphores-Serial-Monitor-Demonstration.jpg?w=707&amp;quality=100&amp;strip=all&amp;ssl=1 707w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-binary-semaphores-Serial-Monitor-Demonstration.jpg?resize=300%2C158&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 707px) 100vw, 707px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Example 2: Counting Semaphore<\/h2>\n\n\n\n<p>In this section, we&#8217;ll build a simple example to demonstrate how counting semaphores work. We&#8217;ll create a counting semaphore with a maximum count of 5. That semaphore will take up to 5 button presses. There is another task that will consume that semaphore to blink an LED as many times as the values in the semaphore. When a value is consumed from the semaphore, another value can be added.<\/p>\n\n\n\n<p>In summary, here&#8217;s an overview of how the project works:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We&#8217;ll attach an interrupt to a pushbutton. When the pushbutton is pressed, the interrupt service routine (ISR) will give a semaphore\u2014up to a maximum count of 5.<\/li>\n\n\n\n<li>The <span class=\"rnthl rntliteral\">LEDBlinkTask<\/span> will wait for the semaphore. Each time it receives one, it will blink the LED. The LED will blink once for each count currently available in the semaphore.<\/li>\n\n\n\n<li>When the <span class=\"rnthl rntliteral\">LEDBlinkTask<\/span> consumes a value from the semaphore, there is &#8220;space&#8221; for a new count added by the press of a pushbutton.<\/li>\n\n\n\n<li>Simultaneously, we&#8217;ll have another task called <span class=\"rnthl rntliteral\">LEDFadeTask<\/span> that will fade another LED. This task is used to demonstrate the power of FreeRTOS to handle multitasking.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Parts Required and Wiring Diagram<\/h3>\n\n\n\n<p>The same as the previous example.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Code<\/h3>\n\n\n\n<p>Copy the following code to the Arduino IDE.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-freertos-semaphores-arduino\/\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n#define BUTTON_PIN 23\n#define LED1_PIN 2    \/\/ Blinking LED\n#define LED2_PIN 4    \/\/ Fading LED\n\n#define DEBOUNCE_DELAY 200 \/\/ debounce for the pushbutton in milliseconds\n\n#define SEMAPHORE_MAX_COUNT 5\n\nSemaphoreHandle_t buttonSemaphore = NULL;\nvolatile uint32_t lastInterruptTime = 0;\n\nvoid IRAM_ATTR buttonISR() {\n  uint32_t currentTime = millis();\n  if (currentTime - lastInterruptTime &gt; DEBOUNCE_DELAY) {\n    BaseType_t higherPriorityTaskWoken = pdFALSE;\n    if (xSemaphoreGiveFromISR(buttonSemaphore, &amp;higherPriorityTaskWoken) == pdTRUE) {\n      Serial.println(&quot;buttonISR: Gave semaphore token&quot;);\n    } else {\n      Serial.println(&quot;buttonISR: Semaphore full&quot;);\n    }\n    lastInterruptTime = currentTime;\n    if (higherPriorityTaskWoken) {\n      portYIELD_FROM_ISR();\n    }\n  }\n}\n\nvoid LEDBlinkTask(void *parameter) {\n  pinMode(LED1_PIN, OUTPUT);\n  for (;;) {\n    \n    \/\/ Get and print the current semaphore count\n    UBaseType_t count = uxSemaphoreGetCount(buttonSemaphore);\n    Serial.print(&quot;LEDBlinkTask: Current semaphore count = &quot;);\n    Serial.println(count);\n\n    if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {\n      Serial.println(&quot;LEDBlinkTask: Blinking LED1 for button press&quot;);\n      vTaskDelay(500 \/ portTICK_PERIOD_MS);\n      digitalWrite(LED1_PIN, HIGH);\n      vTaskDelay(1000 \/ portTICK_PERIOD_MS);\n      digitalWrite(LED1_PIN, LOW);\n      vTaskDelay(500 \/ portTICK_PERIOD_MS);\n    }\n  }\n}\n\nvoid LEDFadeTask(void *parameter) {\n  pinMode(LED2_PIN, OUTPUT);\n  for (;;) {\n    \/\/ Fade up (0 to 255)\n    for (int duty = 0; duty &lt;= 255; duty += 5) {\n      analogWrite(LED2_PIN, duty);\n      if (duty % 50 == 0) {  \/\/ Print every 10th step\n        Serial.print(&quot;LEDFadeTask: Fading LED2, duty=&quot;);\n        Serial.println(duty);\n      }\n      vTaskDelay(50 \/ portTICK_PERIOD_MS);\n    }\n    \/\/ Fade down (255 to 0)\n    for (int duty = 255; duty &gt;= 0; duty -= 5) {\n      analogWrite(LED2_PIN, duty);\n      if (duty % 50 == 0 || duty == 255) {\n        Serial.print(&quot;LEDFadeTask: Fading LED2, duty=&quot;);\n        Serial.println(duty);\n      }\n      vTaskDelay(50 \/ portTICK_PERIOD_MS);\n    }\n  }\n}\n\nvoid setup() {\n  Serial.begin(115200);  \/\/ Higher baud rate\n  delay(1000);\n  Serial.println(&quot;Starting FreeRTOS: Counting Semaphore&quot;);\n\n  pinMode(BUTTON_PIN, INPUT_PULLUP);\n  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);\n\n  buttonSemaphore = xSemaphoreCreateCounting(SEMAPHORE_MAX_COUNT, 0);\n  if (buttonSemaphore == NULL) {\n    Serial.println(&quot;Failed to create semaphore!&quot;);\n    while (1);\n  }\n\n  xTaskCreatePinnedToCore(\n    LEDBlinkTask,           \/\/ Task function\n    &quot;LEDBlinkTask&quot;,         \/\/ Task name\n    3000,                   \/\/ Stack size\n    NULL,                   \/\/ Task parameters\n    2,                      \/\/ Higher priority\n    NULL,                   \/\/ Task handle\n    1                       \/\/ Core ID\n  );\n\n  xTaskCreatePinnedToCore(\n    LEDFadeTask,            \/\/ Task function\n    &quot;LEDFadeTask&quot;,          \/\/ Task name\n    3000,                   \/\/ Stack size\n    NULL,                   \/\/ Task parameters\n    1,                      \/\/ Lower priority\n    NULL,                   \/\/ Task handle\n    1                       \/\/ Core ID\n  );\n}\nvoid loop() {}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/FreeRTOS\/Counting_Semaphore_Pushbutton.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How Does the Code Work?<\/h3>\n\n\n\n<p>This code is quite similar to the previous one. We&#8217;ll just take a look at the important sections related to the counting semaphore.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Creating the Counting Semaphore<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, we create a counting semaphore with a maximum count of 5 (<span class=\"rnthl rntliteral\">SEMAPHORE_MAX_COUNT<\/span>) starting at 0. We do that using the <span class=\"rnthl rntliteral\">xSemaphoreCreateCounting()<\/span> function.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>buttonSemaphore = xSemaphoreCreateCounting(SEMAPHORE_MAX_COUNT, 0);\nif (buttonSemaphore == NULL) {\n  Serial.println(\"Failed to create semaphore!\");\n  while (1);\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Creating Tasks<\/h4>\n\n\n\n<p>Still in the <span class=\"rnthl rntliteral\">setup()<\/span>, we create our tasks and assign them to a core. The <span class=\"rnthl rntliteral\">LEDBlinkTask<\/span> has a higher priority than the <span class=\"rnthl rntliteral\">LEDFadeTask<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>xTaskCreatePinnedToCore(\n  LEDBlinkTask,           \/\/ Task function\n  \"LEDBlinkTask\",         \/\/ Task name\n  3000,                   \/\/ Stack size\n  NULL,                   \/\/ Task parameters\n  2,                      \/\/ Higher priority\n  NULL,                   \/\/ Task handle\n  1                       \/\/ Core ID\n);\n\nxTaskCreatePinnedToCore(\n  LEDFadeTask,            \/\/ Task function\n  \"LEDFadeTask\",          \/\/ Task name\n  3000,                   \/\/ Stack size\n  NULL,                   \/\/ Task parameters\n  1,                      \/\/ Lower priority\n  NULL,                   \/\/ Task handle\n  1                       \/\/ Core ID\n);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Button ISR and the Counting Semaphore<\/h4>\n\n\n\n<p>When you press the pushbutton, the <span class=\"rnthl rntliteral\">buttonISR()<\/span> function will run. If we have a valid button press, we give it to the counting semaphore. The semaphore will take up to five counts. We use the same function we used in the previous example <span class=\"rnthl rntliteral\">xSemaphoreGiveFromISR()<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (xSemaphoreGiveFromISR(buttonSemaphore, &amp;higherPriorityTaskWoken) == pdTRUE) {\n  Serial.println(\"buttonISR: Gave semaphore token\");\n} else {\n  Serial.println(\"buttonISR: Semaphore full\");\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">LEDBlinkTask and Taking the Semaphore<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">LEDBlinkTask<\/span> waits indefinitely until we have a count on the semaphore. When there&#8217;s a count on the semaphore, we take it and blink the LED. <\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {\n  Serial.println(\"LEDBlinkTask: Blinking LED1 for button press\");\n  vTaskDelay(500 \/ portTICK_PERIOD_MS);\n  digitalWrite(LED1_PIN, HIGH);\n  vTaskDelay(1000 \/ portTICK_PERIOD_MS);\n  digitalWrite(LED1_PIN, LOW);\n  vTaskDelay(500 \/ portTICK_PERIOD_MS);\n}<\/code><\/pre>\n\n\n\n<p>Since we have a counting semaphore, it will blink the LED as many times as the counts currently available on the semaphore.<\/p>\n\n\n\n<p>Each time it takes from the semaphore, there&#8217;s a new space available to add a new count (via a pushbutton press).<\/p>\n\n\n\n<p>Inside this task, we also print the current semaphore count by calling the <span class=\"rnthl rntliteral\">uxSemaphoreGetCount()<\/span> function and passing the semaphore handle as an argument.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Get and print the current semaphore count\nUBaseType_t count = uxSemaphoreGetCount(buttonSemaphore);\nSerial.print(\"LEDBlinkTask: Current semaphore count = \");\nSerial.println(count);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">LEDFadeTask<\/h4>\n\n\n\n<p>Simultaneously, we have another independent task called <span class=\"rnthl rntliteral\">LEDFadeTask<\/span> that simply fades the other LED.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void LEDFadeTask(void *parameter) {\n  pinMode(LED2_PIN, OUTPUT);\n  for (;;) {\n    \/\/ Fade up (0 to 255)\n    for (int duty = 0; duty &lt;= 255; duty += 5) {\n      analogWrite(LED2_PIN, duty);\n      if (duty % 50 == 0) {  \/\/ Print every 10th step\n        Serial.print(\"LEDFadeTask: Fading LED2, duty=\");\n        Serial.println(duty);\n      }\n      vTaskDelay(50 \/ portTICK_PERIOD_MS);\n    }\n    \/\/ Fade down (255 to 0)\n    for (int duty = 255; duty &gt;= 0; duty -= 5) {\n      analogWrite(LED2_PIN, duty);\n      if (duty % 50 == 0 || duty == 255) {\n        Serial.print(\"LEDFadeTask: Fading LED2, duty=\");\n        Serial.println(duty);\n      }\n      vTaskDelay(50 \/ portTICK_PERIOD_MS);\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Demonstration<\/h3>\n\n\n\n<p>Upload the code to your board. After uploading, open the Serial Monitor at a baud rate of 115200. Press the ESP32 RST button so that it starts running the code.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-pushbutton.two-LEDs-pressing-button.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 with two LEDs and a pushbutton - pushbutton being pressed to toggle the state of an LED\" class=\"wp-image-174601\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-pushbutton.two-LEDs-pressing-button.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-pushbutton.two-LEDs-pressing-button.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>The LED connected to <span class=\"rnthl rntcblue\">GPIO 4<\/span> will be constantly fading.<\/p>\n\n\n\n<p>Press the pushbutton several times to blink the LED connected to <span class=\"rnthl rntcred\">GPIO 2<\/span> as many times as many button presses on the semaphore queue.<\/p>\n\n\n\n<p>You can see a little demonstration in the short video below.<\/p>\n\n\n<div style=\"text-align:center\"><iframe src=\"https:\/\/player.vimeo.com\/video\/1125868354?color=ff9933&title=1&byline=0&portrait=0\" width=\"720\" height=\"405\" frameborder=\"0\" webkitallowfullscreen mozallowfullscreen allowfullscreen><\/iframe><\/div><\/br>\n\n\n\n<p>In the Serial Monitor, you should get something similar. The semaphore counting will decrease as the LED blinks (if you don&#8217;t continue pressing the pushbutton).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"666\" height=\"502\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-Counting-Semaphore-Serial-Monitor-Demonstration.jpg?resize=666%2C502&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 counting semaphore - Serial Monitor Example Demonstration\" class=\"wp-image-174480\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-Counting-Semaphore-Serial-Monitor-Demonstration.jpg?w=666&amp;quality=100&amp;strip=all&amp;ssl=1 666w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-Counting-Semaphore-Serial-Monitor-Demonstration.jpg?resize=300%2C226&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 666px) 100vw, 666px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this guide, you learned about FreeRTOS binary and counting semaphores and how to implement them with the ESP32 programmed with Arduino IDE.<\/p>\n\n\n\n<p>Semaphores allow us to synchronize tasks to signal when a resource is available, when an event occurred, or a point in a task where the other should run.<\/p>\n\n\n\n<p>We&#8217;ve shown you two simple examples to demonstrate how semaphores work. This can be applied to much more complex applications with multiple tasks giving and taking from the semaphore.<\/p>\n\n\n\n<p>We hope you&#8217;ve found this tutorial useful to start implementing FreeRTOS programming on your ESP32 sketches. We have more tutorials on this FreeRTOS series that you may like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-freertos-arduino-tasks\/\">ESP32 with FreeRTOS (Arduino IDE)\u2014Getting Started Guide: <strong>Creating Tasks<\/strong><\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-freertos-queues-inter-task-arduino\/\">ESP32 with FreeRTOS <strong>Queues<\/strong>: Inter-Task Communication (Arduino IDE)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-freertos-software-timers-interrupts\/\" title=\"\">ESP32 with FreeRTOS: <strong>Software Timers\/Timer Interrupts<\/strong> (Arduino IDE)<\/a><\/li>\n<\/ul>\n\n\n\n<p>To learn more about the ESP32, make sure to check out our resources:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\">Learn ESP32 with Arduino IDE (eBook)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32\/\">All our ESP32 Projects and Guides<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this guide, we&#8217;ll introduce you to and explain how to use FreeRTOS semaphores with the ESP32, using the Arduino IDE. Semaphores are like signals (or flags) that allow you &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 with FreeRTOS: Getting Started with Semaphores (Arduino IDE)\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-freertos-semaphores-arduino\/#more-174275\" aria-label=\"Read more about ESP32 with FreeRTOS: Getting Started with Semaphores (Arduino IDE)\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":174593,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_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":[276,281,277,299,264],"tags":[],"class_list":["post-174275","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32","category-esp32-project","category-esp32-arduino-ide","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/07\/ESP32-FreeRTOS-semaphores.jpg?fit=1920%2C1080&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/174275","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=174275"}],"version-history":[{"count":16,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/174275\/revisions"}],"predecessor-version":[{"id":183532,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/174275\/revisions\/183532"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/174593"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=174275"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=174275"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=174275"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}