/*
A Circle of Communicating Lights
*/
//==========================================================
#define SIZE 5
//==========================================================
class Button {
// a push button connected to a GPI pin
private:
int pin; // the GPIO pin# of the button
int state; // the current state (LOW=pushed, HIGH=released)
int debounce = 30;
bool pushed; // true immediately after the button changes to LOW
bool released; // true immediately after the button changes to HIGH
public:
Button(int pin); // constructor
bool update(); // get the latest value from the hardware
bool wasPushed(); // true if a chnage to LOW was detected
bool wasReleased(); // true if a change to HIGH was detected
String toString(); // show object properties as a string (for debugging)
};
Button::Button(int pin) {
this->pin = pin;
pinMode(pin,INPUT_PULLUP); // use built-in pullup resistor
this->state = HIGH; // initial state = released = HIGH
this->pushed = false;
this->released = false;
}
bool Button::update() {
// update the state and return tur eif we detect a push event
int state= digitalRead(this->pin);
// detect change to LOW state
this->pushed = (state==LOW && this->state==HIGH);
// detect change to HIGH state
this->released = (state==HIGH && this->state==LOW );
// store current state, necessary for change detection
this->state=state;
return this->wasPushed();
}
bool Button::wasPushed() {
return this->pushed;
}
bool Button::wasReleased() {
return this->released;
}
String Button::toString() {
String text = "Button: { "+String(this->pin)+"="+String(this->pushed)+" }";
return text;
}
//==========================================================
#include <Adafruit_NeoPixel.h>
class LEDStrip {
private:
uint32_t colorOn,colorOff; // colors for ON / OFF
Adafruit_NeoPixel *pixels;
public:
LEDStrip(Adafruit_NeoPixel * pixels); // constructor
void setColorOnOff(uint32_t on, uint32_t off); // default color for ON
void on(); // set all pixels to ON color
void on(int pixelNr); // set pixelNr to ON color
void off(); // set all pixels to OFF color
void off(int pixelNr); // set pixelNr to OFF color
};
LEDStrip::LEDStrip(Adafruit_NeoPixel *pixels) {
this->pixels = pixels;
this->pixels->begin();
this->setColorOnOff(Adafruit_NeoPixel::Color(20,0,0),Adafruit_NeoPixel::Color(0,0,1));
this->pixels->fill(0);
}
void LEDStrip::setColorOnOff(uint32_t on, uint32_t off) {
this->colorOn = on;
this->colorOff = off;
}
void LEDStrip::on() {
this->pixels->fill(this->colorOn);
this->pixels->show(); // update strip
}
void LEDStrip::on(int pix) {
this->pixels->setPixelColor(2*pix,this->colorOn);
this->pixels->show(); // update strip
}
void LEDStrip::off() {
this->pixels->fill(this->colorOff);
this->pixels->show(); // update strip
}
void LEDStrip::off(int pix) {
this->pixels->setPixelColor(2*pix,this->colorOff);
this->pixels->show(); // update strip
}
//==========================================================
class LED {
private:
int nr; // sequence number, starting from 0
int pin; // the GPIO pin of the LED
int state; // the current state (0=off, 1=on)
LEDStrip *strip; // a pointer to the LED strip
public:
LED(int nr,int pin,LEDStrip *strip);// constructor
void toggle(); // change between ON and OFF
int getState(); // returns 0=OFF or 1=ON
String toString(); // show object properties as a string (for debugging)
};
LED::LED(int nr, int pin, LEDStrip *strip) {
// the LED is connected to +5V (via a resistor), so HIGH == OFF
this->nr = nr;
this->strip=strip;
this->pin = pin;
pinMode(pin,OUTPUT);
this->state = HIGH; // initialie with HIGH = OFF
digitalWrite(pin,this->state);
}
int LED::getState() {
return this->state==LOW ? 1 : 0;
}
void LED::toggle() {
if (this->state == HIGH) {
this->state=LOW;
this->strip->on(this->nr);
}
else {
this->state=HIGH;
this->strip->off(this->nr);
}
digitalWrite(this->pin,this->state);
}
String LED::toString() {
String text = "LED: { "+String(this->pin)+"="+String(this->state)+" }";
return text;
}
//==========================================================
class ComLight {
// a combination of Button and a group of LEDs
private:
LED* leds[SIZE]; // an array of pointers to the LEDs
Button* button; // a pointer to the Button
int size; // the currentnumber of connected LEDs
public:
ComLight(Button *button); // constructor, takes a Button pointer
void addLED(LED *led); // add the LED to the group
bool update(); // check the Button state and toggle LEDs if pushed
void pushButton(); // simulate a push button event
void toggle(); // apply the switching rule
String toString(); // show object properties as a string (for debugging)
};
ComLight::ComLight(Button *button) {
this->size=0;
this->button = button;
}
void ComLight::addLED(LED *led) {
this->leds[this->size] = led;
this->size++;
}
bool ComLight::update() {
if (this->button->update()) {
this->toggle();
return true;
}
return false;
}
void ComLight::pushButton() {
this->toggle();
}
void ComLight::toggle() {
for(int l=0;l<this->size;l++) {
this->leds[l]->toggle();
}
}
String ComLight::toString() {
String text = "ComLight: { "+this->button->toString()+", [";
for(int l=0;l<this->size;l++) {
text+=" "+this->leds[l]->toString();
}
return text+" ] }";
}
//==========================================================
class ComLightCircle {
private:
LED* leds[SIZE]; // the list of all LEDs
Button* buttons[SIZE]; // the list of all Buttons
ComLight* comLights[SIZE]; // the list of all ComLights
public:
ComLightCircle(); // default constructor
ComLightCircle(LEDStrip *strip);// constructor
bool update(); // check the button and apply switching rule if button was pushed
void pushButton(int nr); // simulate pushing the ComLight´s button
bool checkSuccess(); // check if all LEDs are ON and BLINK if yes
String toString(); // show object properties as a string (for debugging)
};
ComLightCircle::ComLightCircle() {}
ComLightCircle::ComLightCircle(LEDStrip *strip) {
// create Buttons, pins start at #2
for(int b=0;b<SIZE;b++) this->buttons[b]= new Button(2+b);
// create LEDs, pins start at #8
for(int l=0;l<SIZE;l++) this->leds[l]= new LED(l,8+l,strip);
// compose ComLights using a Button and a group of connnected LEDs
for(int c=0;c<SIZE;c++) {
ComLight* cl = new ComLight(this->buttons[c]);
cl->addLED(this->leds[(c+SIZE-1)%SIZE]);
cl->addLED(this->leds[c]);
cl->addLED(this->leds[(c+1)%SIZE]);
this->comLights[c]= cl;
Serial.println(cl->toString());
}
}
bool ComLightCircle::update() {
// check button and apply switching rule if pushed
// return true if the button was pushed
bool changed=false;
for(int c=0;c<SIZE;c++) {
if (this->comLights[c]->update()) changed=true;
}
return changed;
}
void ComLightCircle::pushButton(int nr) {
// simulate a button push event
this->comLights[nr]->pushButton();
}
bool ComLightCircle::checkSuccess() {
// check if all LEDs are ON; if yes blink three times
for (int l=0;l<SIZE;l++) {
if (!this->leds[l]->getState()) return false;
}
delay(500);
for (int n=0;n<10;n++) {
for (int l=0;l<SIZE;l++) {
this->leds[l]->toggle();
}
delay(100);
}
}
String ComLightCircle::toString() {
String text = "ComLightCircle: { [";
for(int l=0;l<SIZE;l++) {
text+=" "+String(this->leds[l]->getState());
}
return text+" ] }";
}
//==========================================================
ComLightCircle comLightCircle; // the ComLightCircle object
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(2*SIZE, 7, NEO_GRB + NEO_KHZ800);
LEDStrip theLEDStrip = LEDStrip(&pixels); // the LED strip
void setup() {
// setup serial connection for debugging / monitoring
Serial.begin(115200);
Serial.println("ComLightCircle starting ..");
// create the ComLightCircle
comLightCircle = ComLightCircle(&theLEDStrip);
// and show its initial state
Serial.println(comLightCircle.toString());
}
int n=-1;
#define TEST 1
#define TINKERCAD 1
void loop() {
n++; // loop counter
// update the ComLightCircle and show it if sonething has changed
if (comLightCircle.update()) {
Serial.println(comLightCircle.toString());
comLightCircle.checkSuccess();
}
#ifdef TEST
for (int c=0;c<SIZE;c++) {
if (n%500==c*100) {
comLightCircle.pushButton(c);
comLightCircle.checkSuccess();
}
if (n==500) n=0; // avoid arithemtic overflow
}
#endif
// a small pause, helpful for TinkerCad simulation
delay(10);
}