<?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: Keerthinath</title>
    <description>The latest articles on DEV Community by Keerthinath (@keerthinath_2decdd1426ef2).</description>
    <link>https://dev.to/keerthinath_2decdd1426ef2</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%2F3841531%2F70316927-18c5-44f0-bcf6-0774283eb9d3.png</url>
      <title>DEV Community: Keerthinath</title>
      <link>https://dev.to/keerthinath_2decdd1426ef2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/keerthinath_2decdd1426ef2"/>
    <language>en</language>
    <item>
      <title>Getting Started with Embedded C: Blinking an LED on Raspberry Pi</title>
      <dc:creator>Keerthinath</dc:creator>
      <pubDate>Tue, 24 Mar 2026 11:10:03 +0000</pubDate>
      <link>https://dev.to/keerthinath_2decdd1426ef2/getting-started-with-embedded-c-blinking-an-led-on-raspberry-pi-1ipk</link>
      <guid>https://dev.to/keerthinath_2decdd1426ef2/getting-started-with-embedded-c-blinking-an-led-on-raspberry-pi-1ipk</guid>
      <description>&lt;p&gt;Blinking an LED is the "Hello, World!" of embedded systems. It sounds simple, but it teaches you the most fundamental skill in embedded programming — controlling hardware directly from software. In this tutorial, we'll blink an LED on a Raspberry Pi using pure C, without any Python libraries or high-level frameworks. Just C, Linux, and hardware.&lt;br&gt;
By the end, you'll understand GPIO control, memory-mapped I/O, and how to compile and run embedded-style C code on a Linux system.&lt;/p&gt;

&lt;p&gt;What You'll Need&lt;/p&gt;

&lt;p&gt;Raspberry Pi (any model: 3B, 4, or Zero W)&lt;br&gt;
1× LED (any color)&lt;br&gt;
1× 330Ω resistor&lt;br&gt;
Breadboard and jumper wires&lt;br&gt;
SSH access or a connected keyboard/monitor&lt;/p&gt;

&lt;p&gt;Step 1: Understanding GPIO on Raspberry Pi&lt;br&gt;
GPIO stands for General Purpose Input/Output. The Raspberry Pi exposes 40 pins on its header, many of which can be configured as digital inputs or outputs. When you set a GPIO pin HIGH, it outputs 3.3V. When you set it LOW, it outputs 0V.&lt;br&gt;
We'll use GPIO 17 (physical pin 11) to drive our LED.&lt;br&gt;
The Circuit&lt;br&gt;
Connect your components like this:&lt;br&gt;
Raspberry Pi Pin 11 (GPIO 17)&lt;br&gt;
        |&lt;br&gt;
       [330Ω Resistor]&lt;br&gt;
        |&lt;br&gt;
       [LED Anode (+)]&lt;br&gt;
       [LED Cathode (-)]&lt;br&gt;
        |&lt;br&gt;
Raspberry Pi Pin 6 (GND)&lt;br&gt;
The resistor is critical — it limits current through the LED so it doesn't burn out.&lt;/p&gt;

&lt;p&gt;Step 2: Two Approaches to GPIO in C&lt;br&gt;
There are two main ways to control GPIO from C on a Raspberry Pi:&lt;br&gt;
ApproachMethodBest Forsysfs (filesystem interface)Write to /sys/class/gpio/Beginners, simple scriptsMemory-mapped I/ODirect register access via /dev/memAdvanced, bare-metal style&lt;br&gt;
We'll start with sysfs — it's beginner-friendly and doesn't require root privileges for GPIO access (after setup). Then we'll peek at the memory-mapped approach for the embedded purists.&lt;/p&gt;

&lt;p&gt;Step 3: Blink Using sysfs (Beginner Approach)&lt;br&gt;
The Linux sysfs interface exposes GPIO pins as virtual files. You control them by reading and writing these files — perfect for learning the concept cleanly.&lt;br&gt;
3.1 Enable GPIO Access Without Root (Optional)&lt;br&gt;
By default, GPIO access requires root. To avoid running everything as root, add your user to the gpio group:&lt;br&gt;
bashsudo usermod -aG gpio $USER&lt;/p&gt;

&lt;h1&gt;
  
  
  Log out and back in for this to take effect
&lt;/h1&gt;

&lt;p&gt;3.2 Write the C Program&lt;br&gt;
Create a file called blink_sysfs.c:&lt;br&gt;
c#include &lt;/p&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define GPIO_PIN        "17"
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define GPIO_EXPORT     "/sys/class/gpio/export"
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define GPIO_DIRECTION  "/sys/class/gpio/gpio17/direction"
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define GPIO_VALUE      "/sys/class/gpio/gpio17/value"
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define BLINK_COUNT     10
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define DELAY_US        500000   // 500ms
&lt;/h1&gt;

&lt;p&gt;/* Write a string to a sysfs file */&lt;br&gt;
static int gpio_write(const char *path, const char *value) {&lt;br&gt;
    int fd = open(path, O_WRONLY);&lt;br&gt;
    if (fd &amp;lt; 0) {&lt;br&gt;
        perror("Failed to open GPIO file");&lt;br&gt;
        return -1;&lt;br&gt;
    }&lt;br&gt;
    write(fd, value, strlen(value));&lt;br&gt;
    close(fd);&lt;br&gt;
    return 0;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;int main(void) {&lt;br&gt;
    int i;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Step 1: Export GPIO pin to userspace */
printf("Exporting GPIO %s...\n", GPIO_PIN);
if (gpio_write(GPIO_EXPORT, GPIO_PIN) &amp;lt; 0) {
    fprintf(stderr, "Export failed. Is the pin already exported?\n");
    /* Continue anyway — pin might already be exported */
}

/* Small delay to let sysfs create the GPIO directory */
usleep(100000);

/* Step 2: Set GPIO direction to output */
printf("Setting GPIO direction to output...\n");
if (gpio_write(GPIO_DIRECTION, "out") &amp;lt; 0) {
    fprintf(stderr, "Failed to set direction.\n");
    return EXIT_FAILURE;
}

/* Step 3: Blink the LED */
printf("Blinking LED %d times...\n", BLINK_COUNT);
for (i = 0; i &amp;lt; BLINK_COUNT; i++) {
    gpio_write(GPIO_VALUE, "1");    /* LED ON */
    printf("LED ON\n");
    usleep(DELAY_US);

    gpio_write(GPIO_VALUE, "0");    /* LED OFF */
    printf("LED OFF\n");
    usleep(DELAY_US);
}

/* Step 4: Cleanup — unexport the GPIO pin */
printf("Cleaning up...\n");
gpio_write("/sys/class/gpio/unexport", GPIO_PIN);

printf("Done!\n");
return EXIT_SUCCESS;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
3.3 Compile and Run&lt;br&gt;
bash# Compile&lt;br&gt;
gcc -o blink_sysfs blink_sysfs.c&lt;/p&gt;

&lt;h1&gt;
  
  
  Run (may need sudo if not in gpio group)
&lt;/h1&gt;

&lt;p&gt;./blink_sysfs&lt;br&gt;
Expected output:&lt;br&gt;
Exporting GPIO 17...&lt;br&gt;
Setting GPIO direction to output...&lt;br&gt;
Blinking LED 10 times...&lt;br&gt;
LED ON&lt;br&gt;
LED OFF&lt;br&gt;
LED ON&lt;br&gt;
LED OFF&lt;br&gt;
...&lt;br&gt;
Done!&lt;br&gt;
Your LED should blink 10 times at 0.5-second intervals. 🎉&lt;/p&gt;

&lt;p&gt;Step 4: Understanding What Just Happened&lt;br&gt;
Let's break down the key concepts:&lt;br&gt;
GPIO Export&lt;br&gt;
cgpio_write(GPIO_EXPORT, GPIO_PIN);&lt;br&gt;
This tells the Linux kernel to expose GPIO 17 to userspace by creating the directory /sys/class/gpio/gpio17/. Before this, the pin is managed exclusively by the kernel.&lt;br&gt;
Direction Control&lt;br&gt;
cgpio_write(GPIO_DIRECTION, "out");&lt;br&gt;
GPIO pins are bidirectional by default. Writing "out" configures pin 17 as a digital output. You could write "in" to read a button or sensor instead.&lt;br&gt;
Value Control&lt;br&gt;
cgpio_write(GPIO_VALUE, "1");  // HIGH — LED on&lt;br&gt;
gpio_write(GPIO_VALUE, "0");  // LOW  — LED off&lt;br&gt;
This is the actual toggle. Writing "1" drives the pin to 3.3V; writing "0" pulls it to 0V.&lt;/p&gt;

&lt;p&gt;Step 5: Advanced — Memory-Mapped GPIO (Embedded Style)&lt;br&gt;
If you want to understand how embedded systems really work — directly accessing hardware registers — here's a peek at the memory-mapped approach. This bypasses the Linux filesystem entirely and writes directly to the BCM2835 peripheral registers.&lt;/p&gt;

&lt;p&gt;⚠️ Warning: This requires root (sudo) and is hardware-specific to Raspberry Pi's BCM2835/BCM2837 SoC. Incorrect memory access can cause system instability.&lt;/p&gt;

&lt;p&gt;c#include &lt;/p&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;h1&gt;
  
  
  include 
&lt;/h1&gt;

&lt;p&gt;/* BCM2835 peripheral base address (Pi 3) */&lt;/p&gt;

&lt;h1&gt;
  
  
  define BCM2835_PERI_BASE   0x3F000000
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define GPIO_BASE           (BCM2835_PERI_BASE + 0x200000)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define PAGE_SIZE           (4 * 1024)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define BLOCK_SIZE          (4 * 1024)
&lt;/h1&gt;

&lt;p&gt;/* GPIO register offsets */&lt;/p&gt;

&lt;h1&gt;
  
  
  define GPFSEL1             1   /* GPIO Function Select 1 (pins 10-19) */
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define GPSET0              7   /* GPIO Pin Output Set 0 */
&lt;/h1&gt;

&lt;h1&gt;
  
  
  define GPCLR0              10  /* GPIO Pin Output Clear 0 */
&lt;/h1&gt;

&lt;p&gt;volatile uint32_t *gpio_map;&lt;/p&gt;

&lt;p&gt;void gpio_setup(void) {&lt;br&gt;
    int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);&lt;br&gt;
    if (mem_fd &amp;lt; 0) {&lt;br&gt;
        perror("Cannot open /dev/mem");&lt;br&gt;
        exit(EXIT_FAILURE);&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpio_map = (volatile uint32_t *)mmap(
    NULL,
    BLOCK_SIZE,
    PROT_READ | PROT_WRITE,
    MAP_SHARED,
    mem_fd,
    GPIO_BASE
);

close(mem_fd);

if (gpio_map == MAP_FAILED) {
    perror("mmap failed");
    exit(EXIT_FAILURE);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;void gpio17_output(void) {&lt;br&gt;
    /* GPFSEL1 controls GPIO 10-19. GPIO 17 uses bits 21-23 &lt;em&gt;/&lt;br&gt;
    gpio_map[GPFSEL1] &amp;amp;= ~(7 &amp;lt;&amp;lt; 21);   /&lt;/em&gt; Clear bits 21-23 &lt;em&gt;/&lt;br&gt;
    gpio_map[GPFSEL1] |=  (1 &amp;lt;&amp;lt; 21);   /&lt;/em&gt; Set as output (001) */&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;void gpio17_set(int value) {&lt;br&gt;
    if (value)&lt;br&gt;
        gpio_map[GPSET0] = (1 &amp;lt;&amp;lt; 17);  /* Set GPIO 17 HIGH &lt;em&gt;/&lt;br&gt;
    else&lt;br&gt;
        gpio_map[GPCLR0] = (1 &amp;lt;&amp;lt; 17);  /&lt;/em&gt; Set GPIO 17 LOW */&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;int main(void) {&lt;br&gt;
    gpio_setup();&lt;br&gt;
    gpio17_output();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (int i = 0; i &amp;lt; 10; i++) {
    gpio17_set(1);
    printf("LED ON\n");
    usleep(500000);

    gpio17_set(0);
    printf("LED OFF\n");
    usleep(500000);
}

munmap((void *)gpio_map, BLOCK_SIZE);
return EXIT_SUCCESS;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
bash# Compile&lt;br&gt;
gcc -o blink_mmap blink_mmap.c&lt;/p&gt;

&lt;h1&gt;
  
  
  Run as root
&lt;/h1&gt;

&lt;p&gt;sudo ./blink_mmap&lt;br&gt;
This is the same pattern used in bare-metal microcontroller programming — writing directly to memory-mapped peripheral registers. If you've worked with STM32 or AVR, this will look very familiar.&lt;/p&gt;

&lt;p&gt;Step 6: Troubleshooting&lt;br&gt;
ProblemSolutionPermission denied on sysfsRun with sudo or add user to gpio groupLED doesn't light upCheck resistor value and LED polaritymmap failedEnsure you're running as root for memory-mapped approachWrong GPIO numberDouble-check BCM vs physical pin numbering&lt;/p&gt;

&lt;p&gt;Note on Pin Numbering: Raspberry Pi has two numbering schemes. We use BCM numbering (GPIO 17 = physical pin 11). Always verify using pinout command on your Pi.&lt;/p&gt;

&lt;p&gt;What's Next?&lt;br&gt;
Now that you can blink an LED, you're ready to explore:&lt;/p&gt;

&lt;p&gt;Reading GPIO inputs — connect a button and read its state in C&lt;br&gt;
PWM control — vary LED brightness using software PWM&lt;br&gt;
I2C/SPI in C — communicate with sensors using Linux's i2c-dev interface&lt;br&gt;
Writing a Linux kernel module — go truly bare-metal with a custom driver&lt;/p&gt;

&lt;p&gt;Embedded C on Linux is a powerful combination. The same skills you've learned here — file descriptors, memory mapping, register manipulation — are the foundations of professional embedded Linux development.&lt;/p&gt;

&lt;p&gt;Have questions or ran into issues? Drop them in the comments — I read every one.&lt;/p&gt;

</description>
      <category>embedded</category>
      <category>raspberrypi</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Why C Still Dominates Embedded Systems in 2026</title>
      <dc:creator>Keerthinath</dc:creator>
      <pubDate>Tue, 24 Mar 2026 11:06:23 +0000</pubDate>
      <link>https://dev.to/keerthinath_2decdd1426ef2/why-c-still-dominates-embedded-systems-in-2026-3cm1</link>
      <guid>https://dev.to/keerthinath_2decdd1426ef2/why-c-still-dominates-embedded-systems-in-2026-3cm1</guid>
      <description>&lt;p&gt;Every few years, a new programming language arrives with promises of revolutionizing embedded development. Rust offers memory safety. MicroPython offers rapid prototyping. Zig promises modern tooling with low-level control. Yet, decade after decade, C remains the undisputed king of embedded systems. In 2026, with more languages than ever competing for the embedded space, C's dominance is not just surviving — it's thriving. Here's why.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hardware Speaks C
When a microcontroller boots up, it doesn't care about your abstractions. It cares about registers, memory addresses, and clock cycles. C was designed from the ground up to map almost one-to-one with hardware concepts. A pointer in C isn't an abstraction — it is a memory address. A struct layout in C is predictable, controllable, and consistent.
Consider this simple example of setting a GPIO pin on an ARM Cortex-M microcontroller:
c#define GPIOA_ODR  (*((volatile uint32_t *)0x40020014))&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;void set_pin_high(void) {&lt;br&gt;
    GPIOA_ODR |= (1 &amp;lt;&amp;lt; 5);  // Set pin 5 high&lt;br&gt;
}&lt;br&gt;
This is not abstraction — this is direct hardware manipulation. No runtime, no garbage collector, no virtual machine sitting between you and the silicon. This kind of control is why C is irreplaceable in embedded contexts where every microsecond and every byte of RAM matters.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Ecosystem is Irreplaceable
C has over 50 years of tooling, libraries, and community knowledge behind it. Every major microcontroller vendor — STMicroelectronics, NXP, Texas Instruments, Microchip — ships their SDKs, HALs, and reference implementations in C. The CMSIS (Cortex Microcontroller Software Interface Standard) is C. FreeRTOS, the world's most deployed RTOS, is written in C. AUTOSAR, the automotive software standard, is C.
Switching away from C doesn't just mean learning a new syntax. It means leaving behind:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Decades of battle-tested drivers and middleware&lt;br&gt;
Vendor-supported toolchains (Keil, IAR, MPLAB)&lt;br&gt;
A global talent pool of embedded engineers&lt;br&gt;
Millions of lines of production-proven code&lt;/p&gt;

&lt;p&gt;No language can replicate this overnight.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Predictability Over Abstraction&lt;br&gt;
In embedded systems, unpredictability is a safety hazard. Medical devices, automotive ECUs, and industrial controllers cannot afford unexpected pauses for garbage collection or runtime type resolution. C gives engineers deterministic execution — what you write is what runs, with no hidden overhead.&lt;br&gt;
Languages with rich runtimes introduce what embedded engineers call "jitter" — non-deterministic timing variations. In a hard real-time system controlling a motor or sampling an ADC at precise intervals, jitter can cause system failure. C avoids this entirely.&lt;br&gt;
c// Precise, deterministic ISR — timing is everything&lt;br&gt;
void TIM2_IRQHandler(void) {&lt;br&gt;
if (TIM2-&amp;gt;SR &amp;amp; TIM_SR_UIF) {&lt;br&gt;
    sample_adc();           // Execute in &amp;lt; 1µs&lt;br&gt;
    TIM2-&amp;gt;SR &amp;amp;= ~TIM_SR_UIF;&lt;br&gt;
}&lt;br&gt;
}&lt;br&gt;
Every embedded engineer reading this knows exactly how long that interrupt will take. That's the point.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;C is Not "Unsafe" — Undisciplined C Is&lt;br&gt;
Critics of C often argue that memory safety issues make it unsuitable for modern development. This argument, while valid in application-level software, misses the embedded reality. In embedded systems:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Memory is statically allocated at compile time in most safety-critical designs&lt;br&gt;
MISRA-C and CERT-C coding standards enforce disciplined, safe C usage&lt;br&gt;
Static analysis tools like PC-lint, Polyspace, and Coverity catch memory issues before runtime&lt;br&gt;
Hardware MPUs (Memory Protection Units) add an additional safety layer&lt;/p&gt;

&lt;p&gt;A well-disciplined embedded C codebase following MISRA-C guidelines is extraordinarily robust. Blaming C for unsafe embedded code is like blaming a scalpel for a poor surgery — the tool isn't the problem.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What About Rust?
Rust deserves a serious mention. Its ownership model eliminates entire classes of memory bugs at compile time, and it has made genuine inroads in embedded — particularly in the Linux kernel and some aerospace projects. The embedded-hal ecosystem is growing, and Rust on ARM Cortex-M is viable today.
But here's the honest reality in 2026:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most embedded teams cannot afford to retrain overnight&lt;br&gt;
Rust's compile times and learning curve are still significant&lt;br&gt;
Vendor SDK support for Rust is still fragmented&lt;br&gt;
Legacy codebases in C aren't going anywhere&lt;/p&gt;

&lt;p&gt;Rust may well be the future of embedded systems. But the future isn't now — not at scale.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Developer Pool
Hiring matters. Whether you're a startup building an IoT product or a Tier-1 automotive supplier, you need engineers. The global pool of embedded C developers dwarfs that of any embedded Rust, Zig, or MicroPython developer. Universities still teach C. Certifications like CES (Certified Embedded System) are C-based. Entry-level embedded engineers are trained in C.
For any organization building real products, this is a practical constraint that no amount of language idealism can ignore.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Conclusion: C Isn't Staying Because of Inertia&lt;br&gt;
There's a common misconception that C persists only because the industry is slow to change. That's wrong. C persists because it is genuinely the right tool for the job in the majority of embedded use cases. It maps to hardware, it's deterministic, its ecosystem is unmatched, and its developer pool is global.&lt;br&gt;
Will C eventually be replaced? Perhaps — in some domains. But in 2026, if you're serious about embedded systems, you're serious about C. Everything else is a complement, not a replacement.&lt;/p&gt;

&lt;p&gt;Are you an embedded engineer who's made the switch to Rust or another language? I'd love to hear your experience in the comments below.&lt;/p&gt;

</description>
      <category>embedded</category>
      <category>iot</category>
      <category>c</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
