Esc to close · to navigate · to open

WI21

Embedded Systems

Microcontrollers and the buses, peripherals, and interrupts that connect a chip to the physical world — concurrency without a kernel.

Concepts learned

Tech

  • C
  • Arduino

Hero demo Solar Tracker

Walk me through this step by step
  1. A sunflower follows the sun across the sky all day. Now picture a solar panel doing the same — not because it looks pretty, but because a panel pointed straight at the sun gathers roughly 40% more energy than one bolted flat to a roof. The question is how to make a £5 microcontroller do this without GPS, without an astronomical clock, and without knowing what time it is. The answer is wonderfully cheap: bolt two tiny light sensors to the panel, one on each side, and let them argue.

  2. The sensors are LDRs — light-dependent resistors. Each one returns a number that grows when more light hits it. Mount one on the east edge of the panel and one on the west edge, and the brighter sensor tells you which way the sun is. If east reads higher, the sun is east of straight-ahead, so nudge the panel east. If west reads higher, nudge west. Repeat forever. That is the entire algorithm, and it does not care what hour it is or what latitude you are at.

  3. Pick the Morning east preset. The east LDR reads 900, the west reads 200. Compute the difference: \Delta = E - W = 900 - 200 = 700. Positive and large, so step east. The servo rotates by a small fixed amount — the demo uses 2° per step. After one step the panel sits 2° closer to the sun, both sensor readings shift slightly, and the next iteration recomputes from there.

  4. Run this loop many times and the panel walks toward the sun in tiny steps. Early on the difference is huge, the algorithm steps every iteration, and the panel sweeps quickly. As the panel aligns, the east and west readings converge — the difference shrinks toward zero. Eventually the sensors disagree so little that stepping further would overshoot. That is when the controller should stop, not because it ran out of power, but because there is nothing useful left to do.

  5. That “stop when it is close enough” rule is the deadband. Concretely: |\Delta| \le d \;\Rightarrow\; \text{hold}. With deadband d = 5, the Noon balanced preset (E=600, W=600, \Delta = 0) holds — obvious. But the Slightly east preset (E=505, W=500, \Delta = 5) also holds, because 5 is right at the threshold. Without a deadband the panel would jitter east-west all day chasing one-unit sensor noise, draining the battery and grinding the servo for no measurable energy gain.

  6. The full decision rule is just three branches:

    \text{dir} = \begin{cases} \text{hold} & |\Delta| \le d \\ \text{east} & \Delta > d \\ \text{west} & \Delta < -d \end{cases}

    Two sensor reads, a subtraction, two comparisons, and a servo write. The whole microcontroller program is maybe twenty lines of C. No PID loop, no Kalman filter, no astronomical model — and yet a real garden-lamp tracker uses exactly this.

  7. Two more knobs keep it well-behaved in the real world. The step size — 2° per iteration in the demo — sets how aggressively the panel chases the sun; smaller steps are smoother but slower, bigger steps overshoot and oscillate around the deadband. The angle is also clamped to \pm 90° so the panel cannot try to point at the ground when sunset confuses the sensors. The demo flags clamped steps in the angle history so you can see where the rule wanted to go versus where physics let it.

  8. What makes this work is that the two sensors are a gradient estimate. The difference E - W is a finite-difference approximation to the spatial derivative of brightness across the panel, and stepping in its direction is the cheapest possible gradient ascent. You are optimising without ever writing down a cost function. In control-theory language it is a bang-bang controller with a dead zone — the same family as a thermostat that clicks the boiler on at 18°C and off at 20°C.

  9. Try it yourself.

  10. So that is the tracker: two LDRs, a subtraction, a deadband, and a servo. The same bang-bang-with-deadband pattern recurs across the course — the traffic light FSM holds each phase until a timer or sensor fires, and the interrupt polling demo is the same “react when the signal crosses a threshold” idea pushed down into the CPU. Paired with the pwm visualiser, which is what actually drives the servo into motion, this demo is the front end of the embedded toolkit: turning a noisy analogue world into one of three discrete decisions.

EWE:900W:200east+90°−90°
morning-east-bright · angle=0° · deadband=5
0 °
5
0
direction: east · next=-2°

Reflection

Pre-interview preview. Akwasi’s first-person reflection on this course is pending — track at issue #51. The Solar Tracker / Traffic Crossing / RPM Display video reel lands with the v10 interview alongside the reflection.

Traffic light FSM (AIO_MQTT)

Lifted from AIO_MQTT_traffic_control; FSM with normal, pedestrian, and night modes.

\text{mode: normal}\text{NS: GREEN}\text{EW: RED}

Traffic-light FSM in normal mode. NS = GREEN, EW = RED. Cycle: green 5s, yellow 1.5s, all-red 1s.

5000 ms
1500 ms
1000 ms
30000 ms
t = 0.00 s

ADC sampling / aliasing

Tune the sample rate against the signal frequency; watch aliasing appear past Nyquist.

f = 10\,\text{Hz}f_s = 100\,\text{Hz}f_{Nyq} = 50.0\,\text{Hz}f_{alias} = 10.00\,\text{Hz}
\text{bits} = 8\text{LSB} = 0.0078\,\text{V}
10 Hz

True frequency of the continuous sine wave, in hertz.

100 Hz

ADC sample rate. Must exceed 2·f to avoid aliasing.

8

Resolution of the mid-tread uniform quantizer.

1.0 V

Peak amplitude of the sine, in volts. Sets quantizer range.

f_alias = 10.00 Hz

PWM visualizer

Tune duty cycle and frequency; watch the average-voltage approximation drive a load.

Walk me through this step by step
  1. You want to dim an LED, but your microcontroller pin can only output two voltages — fully on at 5 V or fully off at 0 V. There is no “2.5 V” setting in the silicon, no analogue dial, no in-between. So how do you make the LED look half as bright? The answer is one of the most useful tricks in embedded systems, and it shows up every time you set a servo angle, control a fan, or fade an indicator.

  2. The trick is to cheat with time. Instead of trying to hold the pin at 2.5 V, flick it between 5 V and 0 V very quickly, spending half of every tick at 5 V and half at 0 V. Your eye cannot follow individual flickers above roughly 100 per second, so it sees the time-average instead. The LED looks as if it were sitting at half-brightness — even though the actual voltage is always either fully on or fully off.

  3. Load the Motor preset: 5 V high, 0 V low, switching 1000 times a second, on for half of each cycle. The average voltage the LED feels is just 5\,\text{V} \times 0.5 = 2.5\,\text{V}. Drop the on-fraction to 0.25 and the average becomes 5\,\text{V} \times 0.25 = 1.25\,\text{V} — a dimmer LED, with no analogue circuitry anywhere. That on-fraction is the duty: the fraction of each cycle the pin is ON.

    V_\text{avg} = V_\text{low} + D \cdot (V_\text{high} - V_\text{low})
  4. The whole thing only works because the load has inertia. An LED’s brightness, a motor’s shaft speed, a fan’s airflow — none of them can change as fast as the pin can flip. They smooth out the rapid pulses into a steady average, the same way your ear hears a tone instead of individual speaker-cone wiggles. Switch faster than the load can react, and physics does the averaging for you.

  5. How many on/off pulses per second do you need? It depends on the load. An LED needs ~100 Hz or above so the eye stops seeing flicker. A small DC motor likes a few kHz, fast enough that the motor’s coil-inductance averages the current. A servo wants exactly 50 Hz — its electronics decode the on-time itself, not the average voltage. Go too slow and the load follows each pulse; go too fast and the switching transistor wastes energy in every transition.

  6. For a brushed DC motor, torque is proportional to current, and current follows the average voltage across the coils. So 50% duty at 12 V gives roughly the same torque as a constant 6 V supply — but the H-bridge driver only ever sees ON or OFF, which is what a switching transistor is actually good at. That is why every cheap cordless drill, RC car, and laptop fan controls speed by PWM rather than by an analogue dimmer.

  7. Resistive heaters break the average-voltage rule. A heater dissipates power as V^2/R, which is non-linear in V, so you have to use the RMS (root-mean-square) voltage instead. For the Heater preset (12 V, 20% duty) the average is only 2.4 V, but the RMS works out to about 5.4 V — and it is the RMS that sets the heat output. Same waveform, two different “effective” voltages, picked by what the load cares about.

    V_\text{rms} = \sqrt{D \cdot V_\text{high}^2 + (1 - D) \cdot V_\text{low}^2}
  8. Try it yourself.

  9. So that is PWM: a fake analogue output built from fast digital pulses, with the load’s own inertia doing the smoothing. The pattern recurs across the rest of the course — the solar tracker’s servo is driven by exactly the 50 Hz pulse train above, the rpm demo measures the output of a PWM-driven motor by counting encoder edges, and the interrupt vs polling demo is the same idea in reverse: reacting to a pulse train instead of generating one. Paired with the adc sampling visualiser, which turns analogue voltages back into digital numbers, PWM closes the loop between digital pin and physical world.

\text{period} = 0.0010\,\text{s}f = 1000\,\text{Hz}D = 50\%

PWM pulse train at frequency 1000 Hz with duty cycle 50%, switching between 0.0 V and 5.0 V. The DC average is 2.500 V and the RMS-equivalent voltage is 3.536 V — the levels that matter for motor speed control.

1000 Hz

Pulse rate in hertz.

0.50

Fraction of each period spent at V_high.

5.0 V
0.0 V
5

How many periods of the waveform to display.

f = 1000Hz, D = 50%

I²C protocol visualizer

Step through an I²C start / address / data / ack / stop transaction at the bit level.

Sensor read 1B · event 1 of 20
SCLSDASTARTADDR_bit_7ADDR_bit_6ADDR_bit_5ADDR_bit_4ADDR_bit_3ADDR_bit_2ADDR_bit_1ADDR_bit_0ADDR_ACKDATA_0_bit_7DATA_0_bit_6DATA_0_bit_5DATA_0_bit_4DATA_0_bit_3DATA_0_bit_2DATA_0_bit_1DATA_0_bit_0DATA_0_ACKSTOP
addr=0x68 (R/W=R) · bytes=[0xAB] · acks=2
0
0
acks: 2 · bytes: 1

Interrupt vs polling

Two side-by-side execution timelines comparing interrupt-driven and polled event handling.

sparse-events · poll=10ms · isr-lat=2ms
pollisr0ms50ms100ms150ms200ms

Polling

mean latency
2.50ms
max latency
5.00ms
CPU util
6.1%
missed
0

Interrupts

mean latency
2.00ms
max latency
2.00ms
CPU util
8.0%
missed
0
10.0 ms
2.0 ms
0
poll mean: 2.50ms · isr mean: 2.00ms

RPM measurement

Reading rotational speed from encoder pulses or magnetic Hall-effect transitions.

\text{instant} = 1708\ \text{RPM}\text{windowed} = 1800\ \text{RPM}\text{avg} = 1780\ \text{RPM}\text{PPR} = 4
1800 RPM

Ground-truth motor speed driving the simulated encoder.

4

Encoder resolution. Higher PPR → more pulses to time.

0.20 s

Pulse-counting window. Longer → smoother, slower.

5

How many recent instant samples to average.

5 %

Per-pulse Gaussian jitter as a fraction of the nominal period.

instant 1708 RPM · windowed 1800 RPM · avg 1780 RPM

Seven-segment decoder

Hex digit → segment-encoding map with a live BCD-to-seven-segment lookup.

\text{text: "5AFE"}\text{lit: 20}\text{slots: 4}\text{nibble[0]: 0x5}

Displaying "5AFE" on a 4-digit 7-segment array. 20 segments lit total.

100 %

LED segment brightness (10–100%).

segments lit: 20

MQTT IoT message flow

Publish / subscribe / broker animation with QoS levels and topic wildcards.

PublisherBrokerSubscriberPUBLISHPUBLISH
filtersensors/+
topicsensors/temp
match
effective QoS0
QoS 0 fire-forget · event 0 of 2 · effective QoS=0
0
0
events: 2 · matched: yes

Accelerometer (DeviceMotion)

Live tilt visualisation using your phone’s accelerometer (iOS gesture-unlock supported via the standard DeviceMotionEvent.requestPermission() flow).

Phone tilt

roll=0.0° pitch=0.0°g

|a| over time

limit=24.53 m/s²|a| (m/s²)sample idx

Flat still · sample 1 of 30 · roll=0.0° · pitch=0.0°

0
1.5 g
0
shakes detected: 0

What’s coming

The author’s written reflection and the video reel of the lab projects land alongside the v10 interview. The demos and featured problems above are wired and playable today.