ComLightsCircle


/*
  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);
}
Veröffentlicht am
Kategorisiert in Allgemein