1 - Halo mask with pins
Map 12 GPIOs, flip alternating bits with XOR, and write the mask 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);
}
2 - IR hold with millis()
Debounce the detector, latch the pattern for a fixed dwell, and drop back when time expires.
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 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 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);
}
}
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);
}
if (holdUntil && now > holdUntil) {
pulse();
holdUntil = 0;
}
}
3 - Basic blink
A single LED proving ground for the smallest possible toggle loop.
const uint8_t ledPin = LED_BUILTIN;
bool state = false;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
state = !state;
digitalWrite(ledPin, state);
delay(200);
}