// =====================================================
// NEOPIXEL RING CLOCK — Arduino MEGA
// MakeMindz Summer Course · Module 4
// Pins: DIN→6 | TRIG→10 | ECHO→11 | BTN_A→2 | BTN_B→3
// =====================================================
#include <Adafruit_NeoPixel.h>
#define LED_PIN 6 // NeoPixel data pin
#define NUM_LEDS 60 // 60 pixels in the ring
#define TRIG_PIN 10 // HC-SR04 trigger
#define ECHO_PIN 11 // HC-SR04 echo
#define BTN_A 2 // Mode switch button (blue)
#define BTN_B 3 // Minute advance button (green)
Adafruit_NeoPixel ring(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
int hh = 10, mm = 9, sc = 0; // start at 10:09:00
unsigned long prevMs = 0;
uint8_t dispMode = 0; // 0=Clock 1=EQ 2=Radar 3=Rainbow
bool prevA = HIGH, prevB = HIGH;
unsigned long dbA = 0, dbB = 0;
// ── Ultrasonic ping — returns distance in cm ──
long ping() {
digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long t = pulseIn(ECHO_PIN, HIGH, 23000);
return t ? t * 17L / 1000 : 60;
}
// ── Colour wheel: maps 0-255 to smooth colour ─
uint32_t wheel(uint8_t p) {
p = 255 - p;
if (p < 85) return ring.Color(255-p*3, 0, p*3);
if (p < 170) { p-=85; return ring.Color(0, p*3, 255-p*3); }
p-=170; return ring.Color(p*3, 255-p*3, 0);
}
uint32_t scalec(uint32_t c, uint8_t f) {
return ring.Color(
((c>>16)&0xFF)*f/255,
((c>>8) &0xFF)*f/255,
( c &0xFF)*f/255);
}
// ── MODE 0: Classic Clock ─────────────────────
void modeClock() {
ring.clear();
for (int i=0;i<60;i+=5) ring.setPixelColor(i,ring.Color(14,14,14));
int hc=(int)round(((hh%12)*60.0f+mm)/720.0f*60.0f)%60;
for (int d=-2;d<=2;d++){
int px=(hc+d+60)%60;
uint8_t b=(d==0)?230:(abs(d)==1?100:35);
ring.setPixelColor(px,ring.Color(b,0,0));
}
int mc=(int)round((mm*60.0f+sc)/3600.0f*60.0f)%60;
ring.setPixelColor((mc-1+60)%60,scalec(ring.Color(0,255,80),70));
ring.setPixelColor(mc,ring.Color(0,255,80));
ring.setPixelColor((mc+1)%60,scalec(ring.Color(0,255,80),70));
ring.setPixelColor(sc%60,ring.Color(0,210,255));
ring.show();
}
// ── MODE 1: Equalizer ─────────────────────────
uint8_t eqH[8]={20,42,16,52,34,28,46,22};
int8_t eqV[8]={ 1,-1, 1,-1, 1,-1, 1,-1};
unsigned long eqMs=0;
void modeEQ() {
if(millis()-eqMs>65){
for(int i=0;i<8;i++){
eqH[i]+=eqV[i]*(int8_t)random(2,7);
if(eqH[i]>=58||eqH[i]<=4){eqV[i]=-eqV[i];eqH[i]=constrain(eqH[i],4,58);}
} eqMs=millis();
}
ring.clear();
for(int b=0;b<8;b++){
int ctr=b*7+3;
uint32_t c=wheel(map(eqH[b],4,58,0,185));
uint8_t br=map(eqH[b],4,58,40,255);
for(int d=-2;d<=2;d++) ring.setPixelColor((ctr+d+60)%60,scalec(c,(uint16_t)br*(d==0?255:abs(d)==1?130:45)/255));
}
ring.show();
}
// ── MODE 2: Radar + Proximity ─────────────────
int radarHead=0; unsigned long radarMs=0;
void modeRadar() {
if(millis()-radarMs>22){radarHead=(radarHead+1)%60;radarMs=millis();}
ring.clear();
for(int i=0;i<18;i++) ring.setPixelColor((radarHead-i+60)%60,ring.Color(0,map(i,0,17,210,4),0));
long cm=ping();
if(cm<40){
int obj=map(cm,2,40,0,59);
ring.setPixelColor(obj,ring.Color(255,80,0));
ring.setPixelColor((obj+1)%60,ring.Color(80,20,0));
ring.setPixelColor((obj-1+60)%60,ring.Color(80,20,0));
}
ring.show();
}
// ── MODE 3: Rainbow (speed = hand distance) ───
uint16_t rbPos=0; unsigned long rbMs=0;
void modeRainbow() {
long cm=constrain(ping(),2,40);
uint8_t spd=map(cm,2,40,14,1);
if(millis()-rbMs>spd){rbPos=(rbPos+1)%256;rbMs=millis();}
for(int i=0;i<NUM_LEDS;i++) ring.setPixelColor(i,wheel((i*4+rbPos)&0xFF));
ring.show();
}
void checkButtons() {
unsigned long now=millis();
bool a=digitalRead(BTN_A), b=digitalRead(BTN_B);
if(prevA==HIGH&&a==LOW&&now-dbA>200){
dispMode=(dispMode+1)%4; dbA=now;
ring.fill(ring.Color(25,25,25)); ring.show(); delay(55); ring.clear();
}
if(prevB==HIGH&&b==LOW&&now-dbB>200){ mm=(mm+5)%60; sc=0; dbB=now; }
prevA=a; prevB=b;
}
void setup() {
ring.begin(); ring.setBrightness(75); ring.show();
pinMode(TRIG_PIN,OUTPUT); pinMode(ECHO_PIN,INPUT);
pinMode(BTN_A,INPUT_PULLUP); pinMode(BTN_B,INPUT_PULLUP);
// Boot spiral animation
for(int i=0;i<NUM_LEDS;i++){ring.setPixelColor(i,wheel(i*4));ring.show();delay(16);}
delay(250);
for(int i=NUM_LEDS-1;i>=0;i--){ring.setPixelColor(i,0);ring.show();delay(9);}
prevMs=millis(); randomSeed(42);
}
void loop() {
checkButtons();
if(millis()-prevMs>=1000){
prevMs+=1000;
if(++sc>=60){sc=0;if(++mm>=60){mm=0;if(++hh>=24)hh=0;}}
}
switch(dispMode){
case 0: modeClock(); break;
case 1: modeEQ(); break;
case 2: modeRadar(); break;
case 3: modeRainbow(); break;
}
delay(8);
}