<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: EffessDev</title>
    <description>The latest articles on DEV Community by EffessDev (@effessdev).</description>
    <link>https://dev.to/effessdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3932962%2Ff8063266-7ea2-44d6-8ff0-7fbce9748ba7.png</url>
      <title>DEV Community: EffessDev</title>
      <link>https://dev.to/effessdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/effessdev"/>
    <language>en</language>
    <item>
      <title>Can I remove the AI-generated tag from my AI-generated course?</title>
      <dc:creator>EffessDev</dc:creator>
      <pubDate>Tue, 08 Sep 2026 15:03:01 +0000</pubDate>
      <link>https://dev.to/effessdev/can-i-remove-the-ai-generated-tag-from-my-ai-generated-course-5b22</link>
      <guid>https://dev.to/effessdev/can-i-remove-the-ai-generated-tag-from-my-ai-generated-course-5b22</guid>
      <description>&lt;p&gt;I recently used DeepSeek v4 Pro to generate an embedded systems course for app, game, or web developers who want to transition into low-level programming and embedded C. I wrote a hyper-specific prompt and asked the AI to first list the chapters and their content. Then I generated each chapter individually. Right now the course has 10 chapters.&lt;/p&gt;

&lt;p&gt;Originally, I created this course for myself, and I've been actively using it. Before studying a new chapter, I paste the content back into an AI to check for factual errors. When something feels unclear or incomplete, I dig deeper by asking questions. Then integrate what I learn back into the course. Once I finish, I plan to have one of my college professors review it for any remaining issues.&lt;/p&gt;

&lt;p&gt;To stay transparent, I added an "AI-generated" badge at the top of every chapter. But at this point, I'm questioning whether that label still fits. I've put a lot of personal effort into reviewing, correcting, and expanding the material. The phrase "AI-generated" often implies low effort or minimal human involvement, and I don't think that describes my course anymore.&lt;/p&gt;

&lt;p&gt;So here's my question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is it fair to change or remove the AI-generated tag after significant human effort?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'd love to hear how other people handle this, so I haven't asked this to an AI yet.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Getting Started with ESP-IDF in VS Code for ESP32 (Setup Guide • Development Environment Tutorial)</title>
      <dc:creator>EffessDev</dc:creator>
      <pubDate>Sat, 29 Aug 2026 03:25:58 +0000</pubDate>
      <link>https://dev.to/effessdev/beyond-arduino-getting-started-with-esp-idf-in-vs-code-for-esp32-3ed4</link>
      <guid>https://dev.to/effessdev/beyond-arduino-getting-started-with-esp-idf-in-vs-code-for-esp32-3ed4</guid>
      <description>&lt;p&gt;This is a step-by-step tutorial that explains how you can set up your development environment for working with ESP-IDF projects in &lt;strong&gt;VS Code&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install ESP-IDF
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install EIM
&lt;/h3&gt;

&lt;p&gt;Espressif Systems provides a graphical tool called &lt;strong&gt;EIM (ESP-IDF Installation Manager)&lt;/strong&gt; to install ESP-IDF. Click the link below to go to the official page to download EIM:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dl.espressif.com/dl/eim/" rel="noopener noreferrer"&gt;https://dl.espressif.com/dl/eim/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure you are in the "Online Installer" tab. The exact file to download depends on your system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows: Download &lt;code&gt;eim-gui-windows-x64.exe&lt;/code&gt;. Run this installer to install EIM.&lt;/li&gt;
&lt;li&gt;Linux x64 (Ubuntu): Download and install the &lt;code&gt;.deb&lt;/code&gt; package (&lt;code&gt;eim-gui-linux-x64.deb&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Install ESP-IDF using EIM
&lt;/h3&gt;

&lt;p&gt;Now that we have installed EIM, let's install ESP-IDF using it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open EIM.&lt;/li&gt;
&lt;li&gt;Under "New Installation" click "Start Installation".&lt;/li&gt;
&lt;li&gt;Under "Easy Installation", click "Start Easy Installation" to install the latest stable version of ESP-IDF with default settings.&lt;/li&gt;
&lt;li&gt;If there are no problems, you will see the "Ready to Install" page. Click "Start Installation".&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Install ESP-IDF VS Code Extension
&lt;/h2&gt;

&lt;p&gt;We use this extension as a high-level wrapper for ESP-IDF. Most times, we do not use ESP-IDF directly. For example, if we need to compile our source code, we ask the extension to do it, which uses the ESP-IDF we just installed internally to to compile the source code.&lt;/p&gt;

&lt;p&gt;Install the extension named "ESP-IDF" by "Espressif Systems" in VS Code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify installation
&lt;/h3&gt;

&lt;p&gt;After installing, restart VS Code. Use the shortcut &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; to open the &lt;strong&gt;command palette&lt;/strong&gt; (remember this shortcut, we are going to use it a lot). Inside the command palette, search &lt;code&gt;ESP-IDF&lt;/code&gt;. You will see many entries which start with &lt;code&gt;ESP-IDF:&lt;/code&gt;. Those commands are provided my the ESP-IDF extension. These commands are what we use for almost everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Note
&lt;/h3&gt;

&lt;p&gt;If you are not in an ESP-IDF project, you might see this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No standard ESP-IDF project was found in this workspace. Do you want to activate the ESP-IDF extension anyway?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to use the extension (in this case we do), you should click "Activate Anyway". Then only we can use the extension. If you mistakenly clicked "X", restart VS Code, wait for that to pop up again, and click "Activate Anyway".&lt;/p&gt;

&lt;p&gt;Keep this in mind since you might need this in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a New Project
&lt;/h2&gt;

&lt;p&gt;Like I said before, we are going to use the &lt;code&gt;ESP-IDF: New Project&lt;/code&gt; command from the command palette to create the project. Press &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; to open the command palette.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the command palette, search and click on &lt;code&gt;ESP-IDF: New Project&lt;/code&gt;, and wait.&lt;/li&gt;
&lt;li&gt;Select your ESP-IDF version (you might see only one version since we only installed one), and wait.&lt;/li&gt;
&lt;li&gt;A new tab will pop up. Inside that tab, under &lt;code&gt;ESP-IDF Templates&lt;/code&gt; select the &lt;code&gt;sample_project&lt;/code&gt; template and click the "Create Project" button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tab will refresh, and you will see a form to fill in your project details. Fill in the details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project name: Your project name.&lt;/li&gt;
&lt;li&gt;Project directory: Your project directory.&lt;/li&gt;
&lt;li&gt;ESP-IDF target: esp32.&lt;/li&gt;
&lt;li&gt;ESP-IDF board: Custom board.&lt;/li&gt;
&lt;li&gt;Serial port: Detect.&lt;/li&gt;
&lt;li&gt;OpenOCD configuration files: Keep the default value.&lt;/li&gt;
&lt;li&gt;ESP-IDF component directory: Keep the input empty.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click "Create Project" and wait again. After creation, in the new tab which pops up, click "Open Project". This will open your brand new ESP-IDF project in a fresh VS Code window.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are prompted to generate &lt;code&gt;compile_commands.json&lt;/code&gt;, accept it. If not, or you mistakenly clicked "Decline", do &lt;code&gt;Ctrl + Shift + P -&amp;gt; ESP-IDF: Run idf.py reconfigure Task&lt;/code&gt;, which does the same thing. &lt;code&gt;compile_commands.json&lt;/code&gt; is essential for Intellisense to work correctly. Otherwise, you will encounter error squiggles everywhere.&lt;/p&gt;

&lt;p&gt;If you are using Clangd instead of the Microsoft C/C++ extension, run &lt;code&gt;ESP-IDF: Configure project for ESP-Clang&lt;/code&gt; from the VS Code command palette to make sure Clangd IntelliSense works correctly. Also, make sure both of them aren't activated at the same time, as they can interfere with each other.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configure your project
&lt;/h2&gt;

&lt;p&gt;You need to configure your project whenever you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start a new project (like we did now)&lt;/li&gt;
&lt;li&gt;Adding, removing, or renaming source files&lt;/li&gt;
&lt;li&gt;Adding or changing component dependencies&lt;/li&gt;
&lt;li&gt;Fixing corrupted build files&lt;/li&gt;
&lt;li&gt;Etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it's safe to do again and again, even if you haven't done anything above or have any problems. Since we just created a new project, let's run it.&lt;/p&gt;

&lt;p&gt;Like we did before, we are again going to use the command palette. Open the command palette (do you remember how?), and click &lt;code&gt;Ctrl + Shift + P -&amp;gt; ESP-IDF: Run idf.py reconfigure Task&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This will take some time. While it's working, let's learn what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates (or regenerates) the build files&lt;/li&gt;
&lt;li&gt;Generates &lt;code&gt;compile_commands.json&lt;/code&gt;, which is essential for Microsoft C/C++ Extension or Clangd to provide Intellisense&lt;/li&gt;
&lt;li&gt;And a lot more (just know these two for now)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's write some code
&lt;/h2&gt;

&lt;p&gt;If you look at &lt;code&gt;main.c&lt;/code&gt; right now, you will see and empty main function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's log "Hello world!" inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"esp_log.h"&lt;/span&gt;&lt;span class="c1"&gt; // Add this header for logging&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// Define a tag for your log messages&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MAIN"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Log "Hello world!"&lt;/span&gt;
  &lt;span class="n"&gt;ESP_LOGI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello world!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building (compiling) the project
&lt;/h2&gt;

&lt;p&gt;Like we always do, open the command palette and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ESP-IDF: Build Your Project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will also take some time. Be patient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flashing your project
&lt;/h2&gt;

&lt;p&gt;Flashing is like uploading the compiled code to your ESP32. You need to physically connect the ESP32 to your computer. After doing that, run the following command from the command palette:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ESP-IDF: Flash (UART) Your Project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will probably won't work out of the box. The fix differ dipending on your platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you are using Windows
&lt;/h3&gt;

&lt;p&gt;If you are on Windows, flashing might show this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A fatal error occurred: Could not connect to an Espressif device on any of the 1 available serial ports.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that happens, the next time you try, press and hold the BOOT button on your board as soon as&lt;br&gt;
you see &lt;code&gt;Connecting...&lt;/code&gt;. After some time, the &lt;code&gt;Connecting.....&lt;/code&gt; will stop and you see several outputs. Stop holding the BOOT button when you see these somewhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uploading stub flasher...
Running stub flasher...
Stub flasher running.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might require some trial and error to find out how long you will have to hold the BOOT button.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you are using Linux (e.g., Ubuntu)
&lt;/h3&gt;

&lt;p&gt;You might get this error while flashing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A fatal error occurred: Could not open /dev/ttyUSB0, the port is busy or doesn't exist.
([Errno 13] could not open port /dev/ttyUSB0: [Errno 13] Permission denied: '/dev/ttyUSB0')

Hint: Try to add user into dialout or uucp group.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that happens, run this command, log out, log back in (or reboot), and try flashing again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; dialout &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to do it only once per system. Subsequent flashing will work without running this comamnd.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring the device
&lt;/h2&gt;

&lt;p&gt;You should keep your ESP32 connected to your computer so that it recieves power and be running. Let's check whether the "Hello world!" got logged.&lt;/p&gt;

&lt;p&gt;On the bottom of VS Code, you will see several icons. Find the icon which looks like a monitor and hover over it. If you see "Monitor Device" when you hover, you found the correct icon.&lt;/p&gt;

&lt;p&gt;Click on that icon. You will see several logs popping up. These are send internally. But among those log messages you will find our Hello world:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I (258) main_task: Started on CPU0
I (258) main_task: Calling app_main()
I (258) MAIN: Hello world!   &amp;lt;--------------- this
I (258) main_task: Returned from app_main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might see different numbers than 258. That's not a problem.&lt;/p&gt;

&lt;p&gt;So, congratulations! You just set up your computer for ESP-IDF, created a new project, wrote some code, compiled, flashed, and monitored the output! Next, you can check out the &lt;a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/index.html#" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; to learn more about ESP-IDF. I also have some great tutorials which you can &lt;a href="https://effessdev.github.io/posts/" rel="noopener noreferrer"&gt;read on my website&lt;/a&gt; or on &lt;a href="https://dev.to/effessdev"&gt;DEV.to&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This section is not completed yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Red squiggly lines under &lt;code&gt;#include "something"&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Try both. At least one of them will probably work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Option 1: &lt;code&gt;Ctrl + Shift + P -&amp;gt; ESP-IDF: Run idf.py reconfigure Task&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Option 2: &lt;code&gt;Ctrl + Shift + P -&amp;gt; Add VS Code Configuration Folder&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reconfigure or build failure
&lt;/h3&gt;

&lt;p&gt;Delete the &lt;code&gt;build&lt;/code&gt; directory and try again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;idf.py&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Ctrl + Shift + P -&amp;gt; Open ESP-IDF Terminal&lt;/code&gt;. You can use &lt;code&gt;idf.py&lt;/code&gt; in this terminal (e.g., &lt;code&gt;idf.py reconfigure&lt;/code&gt;).&lt;/p&gt;

</description>
      <category>esp32</category>
      <category>arduino</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>ESP-IDF Bluetooth LE Beginner Tutorial (BLE)</title>
      <dc:creator>EffessDev</dc:creator>
      <pubDate>Fri, 28 Aug 2026 13:30:10 +0000</pubDate>
      <link>https://dev.to/effessdev/esp-idf-bluetooth-le-tutorial-53c</link>
      <guid>https://dev.to/effessdev/esp-idf-bluetooth-le-tutorial-53c</guid>
      <description>&lt;p&gt;This tutorial covers the basics of how to use Bluetooth Low Energy (BLE) in your ESP-IDF project. It's based on the &lt;a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/ble/get-started/ble-introduction.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; by Espressif Systems.&lt;/p&gt;

&lt;p&gt;This is a practical, step-by-step tutorial, focusing on building a conceptual understanding of BLE and its basic code implementation, rather than every single feature in detail. You can follow along from the beginning to the end. After completing this, you will be able to understand the &lt;a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/ble/get-started/ble-introduction.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; and the &lt;a href="https://github.com/espressif/esp-idf/tree/e37a7ae137c0ea6ebca90c0dd67f4a528d73b727/examples/bluetooth/ble_get_started/nimble/NimBLE_GATT_Server" rel="noopener noreferrer"&gt;official example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can download the full final code &lt;a href="https://github.com/effessdev/esp-idf-examples/blob/main/examples/ble_led/main/main.c" rel="noopener noreferrer"&gt;here&lt;/a&gt; and the full project &lt;a href="https://github.com/effessdev/esp-idf-examples/tree/main/examples/ble_led" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but I recommend you to write the code yourself by following this tutorial instead.&lt;/p&gt;

&lt;p&gt;Let's start!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;I am assuming you have created an empty ESP-IDF project using the &lt;code&gt;sample_project&lt;/code&gt; template and verified building and flashing is working properly. If you haven't already, you can check out my tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/effessdev/beyond-arduino-getting-started-with-esp-idf-in-vs-code-for-esp32-3ed4"&gt;Read on DEV.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://effessdev.github.io/posts/esp-idf-vscode-setup-guide/" rel="noopener noreferrer"&gt;Read on my website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enabling BLE in ESP-IDF
&lt;/h2&gt;

&lt;p&gt;Before you can use BLE, you have to enable it using Menuconfig. There are two ways to do this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Using the VS Code Command Palette
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Only use this option if you are developing using the ESP-IDF VS Code extension.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; to open the command palette.&lt;/li&gt;
&lt;li&gt;Search for and select &lt;code&gt;ESP-IDF: SDK Configuration Editor (Menuconfig)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;Component Config -&amp;gt; Bluetooth&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check the Bluetooth box and select &lt;code&gt;NimBLE - BLE only&lt;/code&gt; from the Host selector.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Option 2: Using the ESP-IDF terminal
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the project configuration menu by running &lt;code&gt;idf.py menuconfig&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;Component config -&amp;gt; Bluetooth&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enable Bluetooth by setting &lt;code&gt;Bluetooth&lt;/code&gt; to &lt;code&gt;[x]&lt;/code&gt; (enabled).&lt;/li&gt;
&lt;li&gt;Select the host stack by navigating to &lt;code&gt;Host&lt;/code&gt; and choosing &lt;code&gt;NimBLE - BLE only&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;S&lt;/code&gt; to save, then &lt;code&gt;Q&lt;/code&gt; to quit.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Updating &lt;code&gt;CMakeLists.txt&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You should add &lt;code&gt;PRIV_REQUIRES bt nvs_flash esp_driver_gpio&lt;/code&gt; in your &lt;code&gt;CMakeLists.txt&lt;/code&gt; inside &lt;code&gt;idf_component_register()&lt;/code&gt;, since our program requires them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Including necessary headers
&lt;/h2&gt;

&lt;p&gt;This program requires a lot of headers to be included. Update your &lt;code&gt;main.c&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Includes */&lt;/span&gt;
&lt;span class="cm"&gt;/* STD APIs */&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;assert.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* ESP APIs */&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"driver/gpio.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"esp_log.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"nvs_flash.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"sdkconfig.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* FreeRTOS APIs */&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;freertos/FreeRTOS.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;freertos/task.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* NimBLE stack APIs */&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"host/ble_hs.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"host/ble_uuid.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"host/util/util.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"nimble/ble.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"nimble/nimble_port.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"nimble/nimble_port_freertos.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"services/gap/ble_svc_gap.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"services/gatt/ble_svc_gatt.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* Defines */&lt;/span&gt;
&lt;span class="cp"&gt;#define TAG "NimBLE_LED"
#define DEVICE_NAME "NimBLE_LED"
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Empty for now&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can change the &lt;code&gt;TAG&lt;/code&gt; and &lt;code&gt;DEVICE_NAME&lt;/code&gt; if required. &lt;code&gt;TAG&lt;/code&gt; is just a label which we use for logging (we will use it later in the tutorial). &lt;code&gt;DEVICE_NAME&lt;/code&gt; is how our ESP32 appears to the other devices (like our phone). Later, we are going to connect your phone to your ESP32. In your phone's "available devices" list, you will see this &lt;code&gt;DEVICE_NAME&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tips for beginners
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;#define&lt;/code&gt; to define macros. Macros are of two types - &lt;strong&gt;object-like macros&lt;/strong&gt; and &lt;strong&gt;function-like macros&lt;/strong&gt;. Here, &lt;code&gt;TAG&lt;/code&gt; and &lt;code&gt;DEVICE_NAME&lt;/code&gt; are object like macros. For now, let's only consider object-like macros (the ones we are using here).&lt;/p&gt;

&lt;p&gt;How it works is that, we define &lt;code&gt;DEVICE_NAME&lt;/code&gt; to be &lt;code&gt;"NimBLE_LED"&lt;/code&gt; and use &lt;code&gt;DEVICE_NAME&lt;/code&gt; like a variable in our code (e.g., we can call a function like this: &lt;code&gt;setDeviceName(DEVICE_NAME)&lt;/code&gt;). When we compile our code, before the compilation step begins, the preprocessor &lt;strong&gt;literally replaces all instances of &lt;code&gt;DEVICE_NAME&lt;/code&gt; with &lt;code&gt;"NimBLE_LED"&lt;/code&gt;&lt;/strong&gt;, so the previous function call becomes &lt;code&gt;setDeviceName("NimBLE_LED")&lt;/code&gt;. So, it is functionally identical to we directly using the string everywhere.&lt;/p&gt;

&lt;p&gt;The main reason why we use it is because the exact same string &lt;code&gt;"NimBLE_LED"&lt;/code&gt; will be used in multiple places in our code. If we use &lt;code&gt;#define&lt;/code&gt;, we can be 100% sure that it will be identical everywhere. Also, if we want to change the name of our device, we just have to update &lt;code&gt;DEVICE_NAME&lt;/code&gt; instead of eveywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize NVS Flash (Non-Volatile Storage)
&lt;/h2&gt;

&lt;p&gt;The BLE stack requires NVS flash to store configurations, so it should be initialized using &lt;code&gt;nvs_flash_init()&lt;/code&gt;. You don't need to overthink about it at this point. Just call it in the main function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;nvs_flash_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function returns a return code. If the return code is &lt;code&gt;0&lt;/code&gt;, it means no errors. Any non-zero value means an error, and each value corresponds to a particular error. To make our code bulletproof, let's add some error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;esp_err_t&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// To store the return code&lt;/span&gt;

  &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nvs_flash_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ESP_ERR_NVS_NO_FREE_PAGES&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ESP_ERR_NVS_NEW_VERSION_FOUND&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ESP_ERROR_CHECK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nvs_flash_erase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nvs_flash_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;ESP_OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ESP_LOGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to initialize nvs flash, error code: %d "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tips for beginners
&lt;/h3&gt;

&lt;p&gt;Here, &lt;code&gt;esp_err_t&lt;/code&gt; is just an &lt;code&gt;int&lt;/code&gt;, not some mysterious type. It's provided by &lt;code&gt;esp_err.h&lt;/code&gt;, which we included in &lt;code&gt;common.h&lt;/code&gt;. &lt;code&gt;esp_err_t&lt;/code&gt; stands for "ESP Error Type". That alias was defined for code readability.&lt;/p&gt;

&lt;p&gt;Similarly, &lt;code&gt;ESP_ERR_NVS_NO_FREE_PAGES&lt;/code&gt;, &lt;code&gt;ESP_ERR_NVS_NEW_VERSION_FOUND&lt;/code&gt;, etc. are also &lt;code&gt;int&lt;/code&gt;s, which are defined in &lt;code&gt;nvs.h&lt;/code&gt;. They are &lt;strong&gt;object-like macros&lt;/strong&gt; defined using &lt;code&gt;#define&lt;/code&gt; (just like how defined &lt;code&gt;TAG&lt;/code&gt; and &lt;code&gt;DEVICE_NAME&lt;/code&gt;), which means they will be replaced with their actual values (&lt;code&gt;4365&lt;/code&gt; and &lt;code&gt;4368&lt;/code&gt; in this case) in the preprocessing step.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ESP_LOGE&lt;/code&gt; ("E" stands for "Error") is a function-like macro used to log errors. They are similar to object-like macros, but instead of replacing with an object, it replace with a function. &lt;code&gt;ESP_LOGI&lt;/code&gt; is used for logging "Info".&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize GPIO2 output pin
&lt;/h2&gt;

&lt;p&gt;Here, we are using GPIO2 because most ESP32s have an inbuilt LED for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;gpio_reset_pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GPIO_NUM_2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;gpio_set_direction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GPIO_NUM_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GPIO_MODE_OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initialize NimBLE stack
&lt;/h2&gt;

&lt;p&gt;Call &lt;code&gt;nimble_port_init()&lt;/code&gt; to initialize NimBLE, the software stack we are using to manage BLE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nimble_port_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;ESP_OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ESP_LOGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to initialize nimble stack, error code: %d "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are reusing the &lt;code&gt;ret&lt;/code&gt; variable we created earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize GAP
&lt;/h2&gt;

&lt;p&gt;GAP stands for Generic Access Profile. Initializing it makes our ESP32 discoverable to other devices (e.g., our phone).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="n"&gt;BaseType_t&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Still just an int; "rc" stands for "return code"&lt;/span&gt;

  &lt;span class="n"&gt;ble_svc_gap_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ble_svc_gap_device_name_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ESP_LOGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to set device name to %s, error code: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;DEVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;DEVICE_NAME&lt;/code&gt; is defined in &lt;code&gt;common.h&lt;/code&gt;. You can change it if you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize the GATT profile
&lt;/h2&gt;

&lt;p&gt;This is the hard part. GATT stands for Generic Attribute. It defines how two connected devices package, format, and send data using a structured hierarchy of services and characteristics.&lt;/p&gt;

&lt;p&gt;A GATT profile has one or more services, which have one or more characteristics within them, which hold data and descriptors. Each service and characteristic has a UUID. Here is a diagram showing the GATT hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------------------+
|          PROFILE          |
+---------------------------+
|                           |
|  +---------------------+  |
|  | SERVICE             |  |
|  +---------------------+  |
|  |                     |  |
|  | CHARACTERISTIC      |  |
|  |  - PROPERTIES       |  |
|  |  - VALUE            |  |
|  |  - DESCRIPTOR       |  |
|  |                     |  |
|  +---------------------+  |
|  |                     |  |
|  |  CHARACTERISTIC     |  |
|  |  - PROPERTIES       |  |
|  |  - VALUE            |  |
|  |  - DESCRIPTOR       |  |
|  |                     |  |
|  +---------------------+  |
|                           |
|  +---------------------+  |
|  | SERVICE             |  |
|  +---------------------+  |
|  |                     |  |
|  | CHARACTERISTIC      |  |  
|  |  - PROPERTIES       |  |
|  |  - VALUE            |  |
|  |  - DESCRIPTOR       |  |
|  |                     |  |
|  +---------------------+  |
|                           |
+---------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, you can create a "Heart Rate" service that has a "Heart Rate Measurement" characteristic, which holds the value.&lt;/p&gt;

&lt;p&gt;Before defining our services and characteristics, let's initialize GATT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="n"&gt;ble_svc_gatt_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Define GATT services table
&lt;/h3&gt;

&lt;p&gt;Here is where we define which services and characteristics our ESP32 have which can be tweaked from our phone. Define these before (outside) the &lt;code&gt;app_main&lt;/code&gt; function, but after the &lt;code&gt;#include&lt;/code&gt;s:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ble_uuid16_t&lt;/span&gt; &lt;span class="n"&gt;auto_io_svc_uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_UUID16_INIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x1815&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ble_uuid16_t&lt;/span&gt; &lt;span class="n"&gt;led_chr_uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_UUID16_INIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x2A56&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;led_chr_val_handle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;led_chr_access&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;conn_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;attr_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ble_gatt_access_ctxt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctxt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="cm"&gt;/* GATT services table */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ble_gatt_svc_def&lt;/span&gt; &lt;span class="n"&gt;gatt_svr_svcs&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* Automation IO service */&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_GATT_SVC_TYPE_PRIMARY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;auto_io_svc_uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;characteristics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ble_gatt_chr_def&lt;/span&gt;&lt;span class="p"&gt;[]){&lt;/span&gt;
             &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* LED characteristic */&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;led_chr_uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_cb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;led_chr_access&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_GATT_CHR_F_WRITE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val_handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;led_chr_val_handle&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
             &lt;span class="p"&gt;{&lt;/span&gt;
                 &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* No more characteristics in this service. */&lt;/span&gt;
             &lt;span class="p"&gt;}}},&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* No more services. */&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might wonder why we used &lt;code&gt;0x1815&lt;/code&gt; as the UUID of our Automation IO service and &lt;code&gt;0x2A56&lt;/code&gt; as the UUID of our LED characteristic. These short UUIDs are standardized by the &lt;strong&gt;Bluetooth SIG (Special Interest Group)&lt;/strong&gt;. Just like how the UNICODE value &lt;code&gt;U+1F60A&lt;/code&gt; corresponds to the smiling face emoji (😊), &lt;code&gt;0x1815&lt;/code&gt; corresponds to "Automation IO" and &lt;code&gt;0x2A56&lt;/code&gt; corresponds to "Digital", meaning that any device that connects to our ESP32 will decide that this service is for pin control and I/O automation when it sees the UUID &lt;code&gt;0x1815&lt;/code&gt;. There are even more such standardized UUIDs with their own specific meaning. You can check the Bluetooth SIG Official List for all assigned 16-bit and 32-bit UUIDs.&lt;/p&gt;

&lt;p&gt;Later in this tutorial, we are going to connect your phone to your ESP32, and you will see our service listed as "Automation IO". So keep this in mind.&lt;/p&gt;

&lt;p&gt;You can also use your own UUIDs instead of these standardized UUIDs, but make sure to use a 128bit UUID instead to make sure you don't accidentally use a standardized UUID, which can be misleading.&lt;/p&gt;

&lt;p&gt;Here, &lt;code&gt;{0}&lt;/code&gt; acts as the null terminator. It is required to denote the end of the array.&lt;/p&gt;

&lt;p&gt;Now we have defined the services tables. Let's define the &lt;code&gt;led_chr_access&lt;/code&gt; callback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the Access Callback
&lt;/h3&gt;

&lt;p&gt;The access callback function handles incoming requests from connected clients. Since we configured our LED characteristic with &lt;code&gt;BLE_GATT_CHR_F_WRITE&lt;/code&gt;, NimBLE will invoke this function whenever a client (such as your phone) sends a write command.&lt;/p&gt;

&lt;p&gt;Copy the following code below the GATT services table, but above the main function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;led_chr_access&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;conn_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;attr_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ble_gatt_access_ctxt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctxt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctxt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;BLE_GATT_ACCESS_OP_WRITE_CHR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Extract the incoming byte payload&lt;/span&gt;
        &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctxt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;om&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;om_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;gpio_set_level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GPIO_NUM_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;ESP_LOGI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"LED turned ON"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;gpio_set_level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GPIO_NUM_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;ESP_LOGI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"LED turned OFF"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ESP_LOGW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Invalid payload value received: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;BLE_ATT_ERR_UNLIKELY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;ctxt-&amp;gt;op&lt;/code&gt; indicates what operation triggered the callback. We check if it equals &lt;code&gt;BLE_GATT_ACCESS_OP_WRITE_CHR&lt;/code&gt;. &lt;code&gt;ctxt-&amp;gt;om&lt;/code&gt; points to an &lt;code&gt;os_mbuf&lt;/code&gt; buffer structure containing the received payload data. &lt;code&gt;ctxt-&amp;gt;om-&amp;gt;om_data[0]&lt;/code&gt; reads the first byte. &lt;code&gt;gpio_set_level()&lt;/code&gt; sets the voltage of GPIO pin 2, which corresponds to the built-in LED in most ESP32s. If you don't have it, you can change the pin or connect an external LED.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registering Services and Characteristics
&lt;/h2&gt;

&lt;p&gt;Now that our GATT table and access callback are defined, we must register them with the NimBLE stack. Paste the following code in &lt;code&gt;app_main()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ble_gatts_count_cfg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gatt_svr_svcs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ESP_LOGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to count gatt services, error code: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ble_gatts_add_svcs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gatt_svr_svcs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ESP_LOGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to add gatt services, error code: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advertising
&lt;/h2&gt;

&lt;p&gt;To enable smartphones or central devices to discover your ESP32, you must start advertising. Advertising is like sending the signals so that other devices (like your phone) can find your ESP32.&lt;/p&gt;

&lt;p&gt;Define an &lt;code&gt;on_sync&lt;/code&gt; callback function that handles advertising. You don't really need to worry about this function right now, just copy it into your file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;gatt_svr_on_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ble_hs_adv_fields&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Make device discoverable and broadcast the name&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_HS_ADV_F_DISC_GEN&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BLE_HS_ADV_F_BREDR_UNSUP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;DEVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_is_complete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ble_gap_adv_set_fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ESP_LOGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to set advertising data, error code: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Configure advertising behavior&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ble_gap_adv_params&lt;/span&gt; &lt;span class="n"&gt;adv_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;adv_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;adv_params&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;adv_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_GAP_CONN_MODE_UND&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Undirected connectable&lt;/span&gt;
    &lt;span class="n"&gt;adv_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disc_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_GAP_DISC_MODE_GEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// General discoverable&lt;/span&gt;

    &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ble_gap_adv_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BLE_OWN_ADDR_PUBLIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BLE_HS_FOREVER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;adv_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ESP_LOGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed to start advertising, error code: %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;ESP_LOGI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Advertising started successfully"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, assign the sync callback in the main function so that it runs at the correct time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;ble_hs_cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync_cb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gatt_svr_on_sync&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the NimBLE Task
&lt;/h2&gt;

&lt;p&gt;NimBLE operates asynchronously within its own FreeRTOS task context. Define a dedicated host task function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;nimble_host_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;nimble_port_run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Runs continuously to handle stack events&lt;/span&gt;
    &lt;span class="n"&gt;nimble_port_freertos_deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the task from the main function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;nimble_port_freertos_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nimble_host_task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have done the coding part. Let's test your device!&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Your ESP32 BLE Server
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Build and flash the code to your ESP32.&lt;/li&gt;
&lt;li&gt;Install nRF Connect on your mobile phone.&lt;/li&gt;
&lt;li&gt;Enable Bluetooth on your phone.&lt;/li&gt;
&lt;li&gt;Open nRF Connect and scan for nearby devices.&lt;/li&gt;
&lt;li&gt;Select your ESP32 device using the provided name (&lt;code&gt;DEVICE_NAME&lt;/code&gt;) and tap &lt;strong&gt;Connect&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Locate the &lt;strong&gt;Automation IO&lt;/strong&gt; service (&lt;code&gt;0x1815&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Digital&lt;/strong&gt; characteristic (&lt;code&gt;0x2A56&lt;/code&gt;) and tap the Write action.&lt;/li&gt;
&lt;li&gt;Send a payload of &lt;code&gt;01&lt;/code&gt; (type = BYTE) to turn GPIO2 High (LED ON), or send &lt;code&gt;00&lt;/code&gt; to switch it Low (LED OFF).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your LED responded, congrats! You just successfuly created a bluetooth-controlled IoT device and controlled it using your phone!&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Now you have built the foundations of BLE. To learn more about BLE, check out the &lt;a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/ble/get-started/ble-introduction.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; and the &lt;a href="https://github.com/espressif/esp-idf/tree/e37a7ae137c0ea6ebca90c0dd67f4a528d73b727/examples/bluetooth/ble_get_started/nimble/NimBLE_GATT_Server" rel="noopener noreferrer"&gt;official example&lt;/a&gt;. Since you now know the basic concepts, the docs will feel much easier to digest.&lt;/p&gt;

&lt;p&gt;If you found this tutorial helpful, please let me know!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>iot</category>
      <category>esp32</category>
      <category>education</category>
    </item>
    <item>
      <title>I Built an AI Memory App That Lets You See, Edit, and Control Everything It Remembers</title>
      <dc:creator>EffessDev</dc:creator>
      <pubDate>Fri, 21 Aug 2026 16:02:42 +0000</pubDate>
      <link>https://dev.to/effessdev/i-built-an-ai-memory-app-that-lets-you-see-edit-and-control-everything-it-remembers-404d</link>
      <guid>https://dev.to/effessdev/i-built-an-ai-memory-app-that-lets-you-see-edit-and-control-everything-it-remembers-404d</guid>
      <description>&lt;p&gt;Ever forgotten when you last had a tetanus vaccine? Or what your blood test results were from two years ago?&lt;/p&gt;

&lt;p&gt;I ran into this problem myself, so I built &lt;a href="https://mmorganized.vercel.app" rel="noopener noreferrer"&gt;MmOrganized&lt;/a&gt; - an AI-powered memory app that gives you full visibility and control over what the AI remembers about you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Default AI Memory
&lt;/h2&gt;

&lt;p&gt;ChatGPT and Gemini both have memory features now, which is great. But they have a specific limitation: you cannot (easily) see what they remember, edit mistakes, or delete incorrect information. You are essentially trusting a black box to store your personal data accurately.&lt;/p&gt;

&lt;p&gt;For something as critical as health information, this was a dealbreaker for me. I needed to know exactly what was being stored and be able to fix it if something was wrong (I'm not saying the ChatGPT/Gemini memory is bad; they are just meant for a different purpose).&lt;/p&gt;

&lt;h2&gt;
  
  
  How MmOrganized Works
&lt;/h2&gt;

&lt;p&gt;The app uses a familiar chat interface. You sign up with your Google or GitHub account, and you are ready to go. The concept is simple:&lt;/p&gt;

&lt;p&gt;You type anything you want the AI to remember. For example, you can say "I took a tetanus vaccine today" or "My blood pressure was 120/80 yesterday". The AI processes your input and saves it to your personal memory store.&lt;/p&gt;

&lt;p&gt;When you need to retrieve that information later, you switch to Recall mode with a single tap and ask naturally: "When did I last get a tetanus shot?" The AI searches your memory and gives you an immediate answer. You can even continue the conversation with follow-up questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Control Over Your Memories
&lt;/h2&gt;

&lt;p&gt;This is where MmOrganized differs from other AI memory solutions. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;View everything&lt;/strong&gt; - Every single memory the AI has saved about you is displayed exactly when it gets inserted, along with a handy delete button. Nothing passes in without you knowing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual control&lt;/strong&gt; - Add, edit, search, and reorder memories completely manually if you want.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Delete your data&lt;/strong&gt; - Permanently delete your entire account and data whenever you want.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Full Data Portability with Text Files
&lt;/h2&gt;

&lt;p&gt;One of the most important features is the import and export functionality. You can download your entire memory as a simple text file. This is not some proprietary format that locks you in. It is plain text that you can open in Notepad, edit with any text editor, or even copy and paste into another chatbot.&lt;/p&gt;

&lt;p&gt;You can then upload it back to MmOrganized with options to either replace your existing memory or append the new content. This gives you complete ownership of your data. You are never locked into the platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technology Behind It
&lt;/h2&gt;

&lt;p&gt;It's a full-stack Next.js app, using Shadcn UI, Drizzle ORM, Motion, Better Auth, Pusher, etc. I have also built a few custom components and animations to give some character to the site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notifications
&lt;/h2&gt;

&lt;p&gt;I also gave the AI the ability to send you notifications. Not particularly useful, but cool. But notifications in general have other purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;This currently does everything I want it to do, so I am unsure what to add next. What would make this more useful for you? What features are currently missing? Please try it out and let me know what you think below. &lt;/p&gt;

&lt;p&gt;Also, my first video edit, just go easy on me 🥺&lt;/p&gt;

&lt;p&gt;Link: &lt;a href="https://mmorganized.vercel.app" rel="noopener noreferrer"&gt;https://mmorganized.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: This is an older project that I personally still use heavily. It's not under active development because it already checks all my personal boxes. But if you have a killer feature idea, I will definitely consider it!)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>showdev</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Change touchpad scroll speed in Ubuntu (no terminal, easiest &amp; fastest method)</title>
      <dc:creator>EffessDev</dc:creator>
      <pubDate>Sat, 08 Aug 2026 12:33:12 +0000</pubDate>
      <link>https://dev.to/effessdev/change-touchpad-scroll-speed-in-ubuntu-no-terminal-easiest-fastest-method-5aj5</link>
      <guid>https://dev.to/effessdev/change-touchpad-scroll-speed-in-ubuntu-no-terminal-easiest-fastest-method-5aj5</guid>
      <description>&lt;p&gt;Here is how you can reduce the two-finger touchpad scroll speed in Ubuntu without using the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install Wayland Scroll Factor
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to the official release page: &lt;a href="https://github.com/daniel-g-carrasco/wayland-scroll-factor/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;https://github.com/daniel-g-carrasco/wayland-scroll-factor/releases/tag/v1.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Scroll down to the "Assets" section and click &lt;code&gt;wayland-scroll-factor_1.0.0-1_amd64.deb&lt;/code&gt; to download it.&lt;/li&gt;
&lt;li&gt;Double-click the downloaded file. A new "App Center" window will open.&lt;/li&gt;
&lt;li&gt;Click "Install" to install the app.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Launch the app
&lt;/h2&gt;

&lt;p&gt;Open the Wayland Scroll Factor app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fim4bh2m5xubp3t5bh13o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fim4bh2m5xubp3t5bh13o.png" alt="Opening Wayland Scroll Factor" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dhapx6xgrb435ixm08p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dhapx6xgrb435ixm08p.png" alt="Wayland Scroll Factor Window" width="799" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Adjust
&lt;/h2&gt;

&lt;p&gt;Adjust the speed using the sliders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing in other operating systems
&lt;/h2&gt;

&lt;p&gt;Visit &lt;a href="https://github.com/daniel-g-carrasco/wayland-scroll-factor" rel="noopener noreferrer"&gt;https://github.com/daniel-g-carrasco/wayland-scroll-factor&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ubuntu</category>
      <category>tooling</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I made an app to let your chatbot see your codebase</title>
      <dc:creator>EffessDev</dc:creator>
      <pubDate>Thu, 16 Jul 2026 16:10:23 +0000</pubDate>
      <link>https://dev.to/effessdev/i-made-an-app-to-let-your-chatbot-see-your-codebase-4ngn</link>
      <guid>https://dev.to/effessdev/i-made-an-app-to-let-your-chatbot-see-your-codebase-4ngn</guid>
      <description>&lt;p&gt;A coding agent that can read and edit files in your repository directly is out of reach for me. Since regular chatbots generally have generous free tiers, that is what I use regularly for coding.&lt;/p&gt;

&lt;p&gt;Sometimes I want to give the chatbot some context about my existing code. What I generally do is copy and paste the relevant files, put them in code blocks, and type in the paths to those files.&lt;/p&gt;

&lt;p&gt;However, giving the chatbot an idea of how the overall project is structured (files and folders) is crucial for optimal outputs. Doing this manually is really hard.&lt;/p&gt;

&lt;p&gt;What I used to do was run the &lt;code&gt;tree&lt;/code&gt; command and copy its output. But this includes files that are not included in version control (such as &lt;code&gt;node_modules&lt;/code&gt;), which can get high in number and clutter the output. I found out that I can use the &lt;code&gt;--gitignore&lt;/code&gt; flag to only include tracked files, but the overall workflow was still slow.&lt;/p&gt;

&lt;p&gt;I found some good options online (such as &lt;a href="https://repomix.com/" rel="noopener noreferrer"&gt;RepoMix&lt;/a&gt;), but they seemed to be much more complex than what I needed or optimized for slightly different things.&lt;/p&gt;

&lt;p&gt;So, with the help of AI, I quickly created a Python app optimized for my specific workflow, and named it &lt;a href="https://github.com/effessdev/reptclip" rel="noopener noreferrer"&gt;ReptClip&lt;/a&gt;. Here is how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  App overview
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/effessdev/reptclip#install" rel="noopener noreferrer"&gt;installation&lt;/a&gt; is super simple. You can run the command &lt;code&gt;pip install reptclip&lt;/code&gt; (or &lt;code&gt;pipx&lt;/code&gt; in Ubuntu) to install and use the app.&lt;/p&gt;

&lt;p&gt;When you run the app from your terminal, relevant parts of your codebase, such as the project structure and the files you specify, are &lt;strong&gt;copied straight to your clipboard&lt;/strong&gt;! No output files, no manual copying. You can paste it into the chat box and start typing your prompt right away.&lt;/p&gt;

&lt;p&gt;For example, an output might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project structure&lt;/span&gt;

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;AGENTS.md
README.md
docs/README.md
src/main.py
src/functions.py
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gh"&gt;# AGENTS.md&lt;/span&gt;

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;Contents of AGENTS.md
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gh"&gt;# src/main.py&lt;/span&gt;

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;Contents of main.py
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gh"&gt;# Prompt&lt;/span&gt;

&amp;lt;- cursor will be here,
   you can quickly start typing your prompt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it only uses a few tokens too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;You can use the &lt;code&gt;-i&lt;/code&gt; or &lt;code&gt;--include&lt;/code&gt; and &lt;code&gt;-e&lt;/code&gt; or &lt;code&gt;--exclude&lt;/code&gt; flags to specify which files to include. Glob patterns (&lt;code&gt;**&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt;) as well as relative paths are supported. Simply running the command without any arguments (&lt;code&gt;reptclip&lt;/code&gt; or &lt;code&gt;rrcc&lt;/code&gt;) will only copy the project structure. Even that only saves a ton of time for a lot of people. Click &lt;a href="https://github.com/effessdev/reptclip#basic-usage" rel="noopener noreferrer"&gt;here&lt;/a&gt; for basic usage instructions.&lt;/p&gt;

&lt;p&gt;I also added an alias &lt;code&gt;rrcc&lt;/code&gt; so that you can launch the app either using the full name &lt;code&gt;reptclip&lt;/code&gt; or the quicker &lt;code&gt;rrcc&lt;/code&gt; command from the terminal. &lt;code&gt;rrcc&lt;/code&gt; can be typed using only your left hand.&lt;/p&gt;

&lt;p&gt;Moreover, you can create a &lt;code&gt;reptclip-config.toml&lt;/code&gt; file (use the &lt;code&gt;config&lt;/code&gt; command to create a default one) in the root of your repository for default include and exclude patterns for a specific project. The config file also supports presets, which you can define in the config file and select one or more of them using &lt;code&gt;-p&lt;/code&gt; or &lt;code&gt;--preset&lt;/code&gt; flag (see &lt;a href="https://github.com/effessdev/reptclip#advanced-usage" rel="noopener noreferrer"&gt;Advanced Usage&lt;/a&gt; for more details). The content of files over 1MB and binary files is not pasted, and a placeholder mentioning the reason is included instead. Binary files are identified using their extensions as well as their content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Thank you for reading till the end. If you found this app useful, please consider starring the &lt;a href="https://github.com/effessdev/reptclip" rel="noopener noreferrer"&gt;repo&lt;/a&gt; and liking this post. If you have any suggestions, please let me know by writing a comment. Pull requests are also welcome if you are interested!&lt;/p&gt;

&lt;p&gt;If you think I am overstating a simple project, I wouldn't blame you. I am just an 18-year-old kid (about to start college), anyway 🙄&lt;/p&gt;

&lt;p&gt;Thanks for your support!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/effessdev/reptclip" rel="noopener noreferrer"&gt;View the project on GitHub&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/effessdev" rel="noopener noreferrer"&gt;Follow me on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
