PE1-4.2 Embedded C & I/O Programming

Standard embedded C practice and the Arduino and NodeMCU platforms as documented by the course's own resources — written September 2026

What this is and why it exists

Embedded C is C with most of the standard library absent and the hardware exposed. The language you already know still works. What changes is what it costs.

On a desktop, memory is effectively free and a wasted millisecond is invisible. On a small board memory is measured in kilobytes and timing is often the point of the program.

The vocabulary

  • Data type — the kind of value a variable holds, which here also decides how many bytes it occupies.
  • Bit operation — an operator acting on the individual bits of a value.
  • Pin mode — whether a pin is configured to read or to drive.
  • Pull-up resistor — a resistor holding an input at a known level when nothing else drives it.
  • PWM — pulse width modulation. A digital output switched rapidly, so its average is adjustable.
  • Duty cycle — the fraction of each switching period the output spends high.
  • Blocking — a call that stops everything else until it returns.
  • Non-blocking — a pattern that checks whether it is time to act and returns immediately.

The mental model

Start with cost, because that is the real change. On this hardware, choosing a smaller integer type is a genuine decision rather than a style preference. A program that would run comfortably on a desktop can fail to fit here. Running out of memory shows up as strange behaviour rather than a clear error.

Bit operations deserve more attention here than anywhere else. Hardware is controlled a bit at a time, and several unrelated settings often share one byte. Setting one bit without disturbing its neighbours is an everyday operation on this hardware and a rarity elsewhere.

Functions matter more than usual too. On a small device the alternative is one enormous loop that nobody can follow. The temptation to write it is strong, because the program starts small.

Now the pins. A pin faces one way at a time, and the direction has to be set before the pin can be used. pinMode does that, and forgetting it is the most common first bug in this topic. It also offers an input mode with an internal pull-up resistor. That option is widely missed, and people wire an external resistor for a job the chip would have done in software.

Reading and writing a single digital pin is the whole of digital input and output. digitalWrite sets a pin high or low; digitalRead reports which it is. Every piece of interfacing in the next topic is built from those two calls.

The analog pair is not symmetric, and this is the trap of the topic. analogRead genuinely measures a voltage, through the converter. analogWrite does not produce a voltage at all. It switches the pin on and off rapidly and varies the fraction of the time it is on, which is pulse width modulation.

That difference is not a detail. The average is what a motor or an LED responds to, so those work well. Anything that needs a real steady voltage does not, and expecting one is the classic first mistake. If you need a true analog output you need a converter, either on a separate chip or built from a filter.

Timing closes the topic with a habit worth forming immediately. The obvious way to wait is a delay call that pauses the program. It is quick to write and it stops everything, including reading a button, updating a display or answering the network.

The alternative is to note the current time, keep running, and each time round the loop check whether enough time has passed to act. That is the non-blocking pattern. It looks more complicated for a program that blinks one light. It is the only thing that works for a program doing two things at once, and learning it early saves rewriting every project later.

What you should now be able to explain or do

Choose data types with memory cost in mind, and use bit operations on hardware settings. Set pin direction with pinMode, including the internal pull-up option. Drive and read pins with digitalWrite and digitalRead. Say exactly what analogWrite produces and what it cannot drive. Replace a blocking delay with a non-blocking timing pattern.

Check yourself

Memory is measured in kilobytes. A larger type than you need can be the difference between a program that fits and one that misbehaves.

It sets whether a pin reads or drives. The overlooked option is an input mode with an internal pull-up resistor, which replaces an external one.

A rapidly switching digital output whose on-fraction you set. The average is adjustable, but the pin is only ever fully high or fully low.

Not directly. You would need a filter to average the switching, or a separate converter chip.

Nothing else can run while it waits. Buttons go unread, displays go unupdated, and the network goes unanswered.

Go deeper

We haven't checked most of these for screen reader use yet.

Back to Embedded C & I/O Programming: work through the checklist