==> main.h <== constexpr byte nLEDs = 5*32; // Five meter strip. constexpr int TICKS_PER_SEC = 50; constexpr int DELAY = (int)(1000.0 / TICKS_PER_SEC); typedef uint32_t u32; #ifdef __AVR_ATtiny85__ // FOR ATtiny85: constexpr byte IR = 1; constexpr byte LED = 4; constexpr int SPI_DATA = 3; // ATtiny85 using P3 (pin 2) constexpr int SPI_CLOCK = 4; // ATtiny85 P4 (pin 3) #define V(X) /* no verbosity on ATtiny, for now */ #define SerialReady true #define Begin(X) #define Println(X) #define Println2(X,Y) #define Print(X) #else // FOR Arduino UNO: constexpr byte IR = 2; constexpr byte LED = 13; constexpr int SPI_DATA = 3;// 11; // Same as SPI MOSI constexpr int SPI_CLOCK = 4;// 13; // Same as SPI CLK #define V(X) X /* UNO can be verbose. */ #define SerialReady Serial #define Begin(X) Serial.begin(X) #define Println(X) Serial.println(X) #define Println2(X,Y) Serial.println(X, Y) #define Print(X) Serial.print(X) #endif // LED STRIP #include LPD8806 strip = LPD8806(nLEDs, SPI_DATA, SPI_CLOCK); // INFRA-RED REMOTE #include IRrecv irrecv(IR); decode_results results; /* *INDENT-OFF* */ char DecodeIRHash(unsigned hash) { switch (hash) { case 0x6897: return '0'; case 0x30CF: return '1'; case 0x18E7: return '2'; case 0x7A85: return '3'; case 0x10EF: return '4'; case 0x38C7: return '5'; case 0x5AA5: return '6'; case 0x42BD: return '7'; case 0x4AB5: return '8'; case 0x52AD: return '9'; case 0xA25D: return 'P'; // Power ON/OFF case 0xE21D: return 'F'; // Func/Stop case 0x22DD: return '<'; // LEFT (<<) case 0x02FD: return '|'; // MIDDLE (>||) case 0xC23D: return '>'; // RIGHT (>>) case 0x629D: return '+'; // (VOL+) case 0xA857: return '-'; // (VOL-) case 0xE01F: return 'v'; // (v) left side: down triangle case 0x906F: return '^'; // (^) right side: up triangle } // default: return '?'; } struct KeyCode { byte key; unsigned code1, code2; } Keys[] = { // AdaFruit Mini Remote Control /389 {0, 0x30CF, 0}, {1, 0x22C1, 0x08F7}, {2, 0x8877, 0x74BD}, {3, 0x48B7, 0}, {4, 0x28D7, 0xCCFD}, {5, 0xA857, 0x2501}, {6, 0x6897, 0x9D61}, {7, 0x18E7, 0}, {8, 0x9867, 0}, {9, 0x58A7, 0xF4E5}, {0x11, 0x13BD, 0xA05F}, {0x12, 0x10EF, 0}, {0x13, 0x5B5D, 0x906F}, {0x14, 0x6F7D, 0x50AF}, {0x15, 0xB04F, 0}, {0x21, 0xDF3D, 0}, {0x22, 0x3741, 0x807F}, {0x23, 0x7261, 0x40BF}, {0, 0, 0} }; byte LookupKey(unsigned a) { if (a) { for (struct KeyCode *p= Keys; p->code1; p++) { if (p->code1 == a || p->code2 == a) return p->key; } } return 255; } constexpr u32 RGB(byte r, byte g, byte b, byte dim=0) { return ((u32)((g>>dim) | 0x80) << 16) | ((u32)((r>>dim) | 0x80) << 8) | ((u32)((b>>dim) | 0x80)); } constexpr byte DimLimit = 7; // 0 to 6 are valid. enum Crayon { Black, Red, Yellow, Green, Cyan, Blue, Violet, White, CrayonLimit }; byte Crayons[CrayonLimit][3] = { { 0, 0, 0}, // Black {127, 0, 0}, // Red {127, 127, 0}, // Yellow { 0, 127, 0}, // Green { 0, 127, 127}, // Cyan { 0, 0, 127}, // Blue {127, 0, 127}, // Violet {127, 127, 127}, // White }; u32 Colors[CrayonLimit][DimLimit]; void ColorInit() { for (byte cray=(byte)Black; cray < (byte)CrayonLimit; cray++) { for (byte dim = 0; dim < DimLimit; dim++) { Colors[cray][dim] = RGB(Crayons[cray][0], Crayons[cray][1], Crayons[cray][2], dim); } } } /* *INDENT-ON* */ //Input a value 0 to 384 to get a color value. //The colours are a transition r - g -b - back to r constexpr uint16_t WheelLimit = 384; u32 Wheel(uint16_t WheelPos, byte dim) { byte r, g, b; switch (WheelPos / 128) { case 0: r = 127 - WheelPos % 128; //Red down g = WheelPos % 128; // Green up b = 0; //blue off break; case 1: g = 127 - WheelPos % 128; //green down b = WheelPos % 128; //blue up r = 0; //red off break; case 2: b = 127 - WheelPos % 128; //blue down r = WheelPos % 128; //red up g = 0; //green off break; } return RGB(r >> dim, g >> dim, b >> dim); } ////////////////////////////////////////////////// ////////////////////////////////////////////////// byte TheDim = 5; byte TheWidth = 32; byte TheShift = 0; /* *INDENT-OFF* */ class App { public: App() {} virtual ~App() {} virtual void Reset() = 0; virtual void Step(char c) = 0; // LEDS: static void Clear(); static void Led(byte pos, byte r, byte g, byte b, byte dim=255); static void Led(byte pos, enum Crayon cray, byte dim=255); static void Show(); // unsigned ticks; }; /* *INDENT-ON* */ void App::Clear() { strip.begin(); for (byte i = 0; i < nLEDs; i++) strip.setPixelColor(i, RGB(0, 0, 0, 0)); } void App::Led(byte pos, byte r, byte g, byte b, byte dim=255) { if (pos > TheWidth) return; if (dim==255) dim=TheDim; strip.setPixelColor(TheShift+pos, RGB(r, g, b, dim)); } void App::Led(byte pos, enum Crayon cray, byte dim=255) { if (pos > TheWidth) return; if (dim==255) dim=TheDim; strip.setPixelColor(TheShift+pos, Colors[cray][dim]); } void App::Show() { strip.show(); } #define App_Clear App::Clear #define App_Led App::Led #define App_Show App::Show App* RegisteredApps[10]; byte CurrentApp; #include "pong.h" Pong ThePong; ////////////////////////////////////////////////// volatile int _junk_for_Delay = 3; void Delay(unsigned n) { for (unsigned i=0; i>= 1; j--; if ((i&3)==3) j--; } for (byte i = 0; i < 6; i++) { if (y&1) { App_Led(j, Red); } else { App_Led(j, Green); } y >>= 1; j--; if ((i&3)==3) j--; } App_Show(); } void setup() { pinMode(SPI_DATA, OUTPUT); pinMode(SPI_CLOCK, OUTPUT); pinMode(LED, OUTPUT); pinMode(IR, INPUT); //Initialize serial and wait for port to open: Begin(9600); ColorInit(); while (!SerialReady) { // wait for serial port to connect. Needed for native USB port only } // prints title with ending line break cli(); Println("HELO."); sei(); irrecv.enableIRIn(); // Start the IR receiver cli(); Println("IR."); sei(); RegisteredApps[1] = &ThePong; CurrentApp = 0; Splash(0xAA55); } unsigned Hash; // most recent hash of button; never cleared. void loop() { char c = '\0'; if (irrecv.decode(&results)) { unsigned h = 0xFFFF & results.value; Println2((unsigned)(0xFFFF & h), HEX); if (h == 0xFFFF) { c = DecodeIRHash(Hash); } else if (h) { Hash = h; // Remember h as Hash, if not repeating. } irrecv.resume(); // Receive the next value } if (Hash) Splash(Hash, LookupKey(Hash)); #if 0 if ('0' <= c && c <= '9') { int i = (c - '0'); cli(); Print("i="), Println(i); sei(); Splash(i); CurrentApp = i; App *app = RegisteredApps[CurrentApp]; if (app) { app->Reset(); } else { Splash(i); } } App *app = RegisteredApps[CurrentApp]; if (app) app->Step(c); #endif Delay(10*DELAY); } #if 0 Red vol- : DF3D Red Mid: 3741 or 807F Red vol+ : 7261 or 40BF up: 13BD or A05F left: 10EF save: 5B5D or 906F right: 6F7D or 50AF down: B04F 0: 30CF 1: 22C1 or 8F7 2: 8877 or 74BD 3: 48B7 4: 28D7 or CCFD 5: A857 or 2501 6: 6897 or 9D61 7: 18E7 8: 9867 9: 58A7 or F4E5 #endif ==> pong.h <== ////////////////////////////////////// // pong.h enum PongState { PongIdle, PongServe, PongMoving, PongHit, PongMiss }; enum PongState pongState; bool pongLeft; int left, ball, right; unsigned ticks; /* *INDENT-OFF* */ class Pong : public App { public: Pong() {} ~Pong() override {} void Reset() override; void Step(char c) override; private: void PongShow(); }; /* *INDENT-ON* */ void Pong::PongShow() { this->Clear(); if (0 <= left && left < nLEDs) this->Led(left, Green); if (0 <= right && right < nLEDs) this->Led(right, Red); if (0 <= ball && ball < nLEDs) this->Led(ball, White); this->Show(); } void Pong::Reset() { left = 0; right = nLEDs - 1; ball = 1; ticks = 0; pongLeft = false; pongState = PongIdle; PongShow(); } void Pong::Step(char c) { if (ticks < 32000) ticks++; switch (pongState) { case PongIdle: if (c) { pongLeft = false; ball = 1; ticks = 0; pongState = PongMoving; } break; case PongServe: break; case PongMoving: if (pongLeft) { if (ball == 0) pongState=PongIdle; else ball--; if (ticks > 10 && c) { pongLeft = false; ticks = 0; } } else { if (ball >= nLEDs) pongState=PongIdle; else ball++; if (ticks > 10 && c) { pongLeft = true; ticks = 0; } } break; case PongHit: break; case PongMiss: break; default: break; } PongShow(); }