Electronics fundamentals
Work through KVL, KCL, Ohm's Law, power math, and practical motor control wiring with visuals and worked examples.
Open guideMove from DC fundamentals to AC intuition and hands-on builds with these focused guides.
Work through KVL, KCL, Ohm's Law, power math, and practical motor control wiring with visuals and worked examples.
Open guideBuild intuition for impedance, RMS, power factor, and three-phase geometry with annotated diagrams and callouts.
Read chapterFollow lab-style walkthroughs with BOMs, wiring tables, and debug checkpoints to recreate and remix the builds.
Browse labsBreak the virtual beam to flip alternating LEDs with mask ^= 0b101010101010. The register stays high for whatever hold time you dial in, emulating a halo installation triggered by an IR detector.
Direct register hits on ATmega-class MCUs, efficient branching, and mask-flip patterns that mirror the UI demo. Nothing here drags in frameworks—just <avr/io.h>, <util/delay.h>, and discipline.
XOR the even LEDs into a ring mask, map straight into PORTB/PORTA, and keep JTAG off our pins.
/* Ledblink v.2 — bare metal */
#include <avr/io.h>
#include <util/delay.h>
#define HALO_PATTERN 0b101010101010
static inline void drive_ring(uint16_t mask) {
PORTB = mask & 0xFF; // PB0..PB7
PORTA = (mask >> 8) & 0x0F; // PA0..PA3
}
int main(void) {
MCUSR |= (1 << JTD);
MCUSR |= (1 << JTD); // disable JTAG reliably
DDRB = 0xFF;
DDRA = 0x0F;
DDRD = 0xFF;
uint16_t ring_mask = 0;
for (;;) {
ring_mask ^= HALO_PATTERN;
drive_ring(ring_mask);
_delay_ms(300);
}
}
A beam break stores the XOR pattern, holds it for N cycles, then drops. No interrupts, no Arduino core.
#include <avr/io.h>
#include <util/delay.h>
#define HALO_PATTERN 0b101010101010
#define HOLD_TICKS 12
static inline void setup_ports(void) {
MCUSR |= (1 << JTD);
MCUSR |= (1 << JTD);
DDRB = 0xFF;
DDRA = 0x0F;
DDRC &= ~(1 << PC0); // IR input on PC0
PORTC |= (1 << PC0); // enable pull-up
}
static inline uint8_t ir_triggered(void) {
return !(PINC & (1 << PC0));
}
static inline void pulse(uint16_t *mask) {
*mask ^= HALO_PATTERN;
PORTB = *mask & 0xFF;
PORTA = (*mask >> 8) & 0x0F;
}
int main(void) {
uint16_t ring_mask = 0;
uint8_t hold = 0;
setup_ports();
for (;;) {
if (ir_triggered()) {
pulse(&ring_mask);
hold = HOLD_TICKS;
}
if (hold) {
_delay_ms(50);
if (--hold == 0) {
pulse(&ring_mask); // drop pattern back to zeroed state
}
}
}
}
Strip everything to a single toggled bit—this is the portion many “examples” comment out.
int main(void) {
DDRB = 0xFF;
uint8_t idx = 0;
for (;;) {
PORTB ^= (1 << idx);
_delay_ms(200);
if (++idx > 7) idx = 0;
}
}
Same XOR mask, but in the friendlier setup()/loop() world so workshops can follow along. Still lean—no dynamic allocation, no mystery libraries.
Map 12 GPIOs, flip the alternating bits with XOR, and blast them out with plain digitalWrite().
const uint8_t ringPins[] = {2,3,4,5,6,7,8,9,10,11,12,13};
const uint8_t RING_COUNT = sizeof(ringPins) / sizeof(ringPins[0]);
const uint16_t HALO_PATTERN = 0b101010101010;
uint16_t ringMask = 0;
void setup() {
for (uint8_t i = 0; i < RING_COUNT; ++i) {
pinMode(ringPins[i], OUTPUT);
digitalWrite(ringPins[i], LOW);
}
}
void writeRing(uint16_t mask) {
for (uint8_t i = 0; i < RING_COUNT; ++i) {
digitalWrite(ringPins[i], (mask >> i) & 0x01);
}
}
void loop() {
ringMask ^= HALO_PATTERN;
writeRing(ringMask);
delay(300);
}
Debounce the IR detector, latch the pattern for a fixed dwell, and drop back when time expires.
const uint8_t irPin = A0;
const uint16_t HALO_PATTERN = 0b101010101010;
const uint16_t HOLD_MS = 600;
uint16_t ringMask = 0;
unsigned long holdUntil = 0;
void setup() {
pinMode(irPin, INPUT_PULLUP);
for (uint8_t pin : ringPins) {
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
}
bool irTriggered() {
return digitalRead(irPin) == LOW;
}
void pulse() {
ringMask ^= HALO_PATTERN;
writeRing(ringMask);
}
void loop() {
const unsigned long now = millis();
if (irTriggered()) {
pulse();
holdUntil = now + HOLD_MS;
delay(30); // tiny debounce so we do not re-trigger instantly
}
if (holdUntil && now > holdUntil) {
pulse(); // clear pattern
holdUntil = 0;
}
}
If they only need a single LED proving ground, this is the entire story.
const uint8_t ledPin = LED_BUILTIN;
bool state = false;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
state = !state; // XOR with 1
digitalWrite(ledPin, state);
delay(200);
}