{"id":259,"date":"2020-11-18T13:38:15","date_gmt":"2020-11-18T12:38:15","guid":{"rendered":"https:\/\/followthescore.org\/schueler-labor\/?p=259"},"modified":"2020-11-18T13:38:15","modified_gmt":"2020-11-18T12:38:15","slug":"comlightscircle","status":"publish","type":"post","link":"https:\/\/followthescore.org\/schueler-labor\/comlightscircle\/","title":{"rendered":"ComLightsCircle"},"content":{"rendered":"\n<pre class=\"wp-block-code\"><code>\r\n\/*\r\n  A Circle of Communicating Lights\r\n*\/\r\n\r\n\/\/==========================================================\r\n\r\n#define SIZE 5\r\n\r\n\/\/==========================================================\r\n\r\nclass Button {\r\n    \/\/ a push button connected to a GPI pin\r\nprivate:\r\n    int pin;        \/\/ the GPIO pin# of the button\r\n    int state;      \/\/ the current state (LOW=pushed, HIGH=released)\r\n    int debounce = 30;\r\n    bool pushed;    \/\/ true immediately after the button changes to LOW\r\n    bool released;  \/\/ true immediately after the button changes to HIGH\r\n \r\npublic:\r\n    Button(int pin);        \/\/ constructor\r\n    bool update();          \/\/ get the latest value from the hardware\r\n    bool wasPushed();       \/\/ true if a chnage to LOW was detected\r\n    bool wasReleased();     \/\/ true if a change to HIGH was detected\r\n    String toString();      \/\/ show object properties as a string (for debugging)\r\n};\r\n\r\nButton::Button(int pin) {\r\n    this->pin = pin;\r\n    pinMode(pin,INPUT_PULLUP);  \/\/ use built-in pullup resistor\r\n    this->state = HIGH;         \/\/ initial state = released = HIGH \r\n    this->pushed = false;\r\n    this->released = false;\r\n}\r\n\r\nbool Button::update() {\r\n    \/\/ update the state and return tur eif we detect a push event\r\n    \r\n    int state= digitalRead(this->pin);\r\n    \/\/ detect change to LOW state\r\n    this->pushed   = (state==LOW  &amp;&amp; this->state==HIGH);\r\n    \/\/ detect change to HIGH state\r\n    this->released = (state==HIGH &amp;&amp; this->state==LOW );\r\n    \/\/ store current state, necessary for change detection\r\n    this->state=state;\r\n    return this->wasPushed();\r\n}\r\nbool Button::wasPushed() {\r\n    return this->pushed;\r\n}\r\nbool Button::wasReleased() {\r\n    return this->released;\r\n}\r\nString Button::toString() {\r\n    String text = \"Button: { \"+String(this->pin)+\"=\"+String(this->pushed)+\" }\";\r\n    return text;\r\n}\r\n\r\n\/\/==========================================================\r\n\r\n#include &lt;Adafruit_NeoPixel.h>\r\n\r\nclass LEDStrip {\r\nprivate:\r\n    uint32_t colorOn,colorOff;  \/\/ colors for ON \/ OFF\r\n    Adafruit_NeoPixel *pixels;\r\npublic:\r\n    LEDStrip(Adafruit_NeoPixel * pixels);           \/\/ constructor\r\n    void setColorOnOff(uint32_t on, uint32_t off);  \/\/ default color for ON\r\n    void on();                                      \/\/ set all pixels to ON color\r\n    void on(int pixelNr);                           \/\/ set pixelNr to ON color\r\n    void off();                                     \/\/ set all pixels to OFF color\r\n    void off(int pixelNr);                          \/\/ set pixelNr to OFF color\r\n};\r\n\r\nLEDStrip::LEDStrip(Adafruit_NeoPixel *pixels) {\r\n    this->pixels = pixels;\r\n    this->pixels->begin();\r\n    this->setColorOnOff(Adafruit_NeoPixel::Color(20,0,0),Adafruit_NeoPixel::Color(0,0,1));\r\n    this->pixels->fill(0);\r\n}\r\n\r\nvoid LEDStrip::setColorOnOff(uint32_t on, uint32_t off) {\r\n    this->colorOn = on;\r\n    this->colorOff = off;\r\n}\r\n\r\nvoid LEDStrip::on() {\r\n    this->pixels->fill(this->colorOn);\r\n    this->pixels->show();      \/\/ update strip\r\n}\r\nvoid LEDStrip::on(int pix) {\r\n    this->pixels->setPixelColor(2*pix,this->colorOn);\r\n    this->pixels->show();      \/\/ update strip\r\n}\r\nvoid LEDStrip::off() {\r\n    this->pixels->fill(this->colorOff);\r\n    this->pixels->show();      \/\/ update strip\r\n}\r\nvoid LEDStrip::off(int pix) {\r\n    this->pixels->setPixelColor(2*pix,this->colorOff);\r\n    this->pixels->show();      \/\/ update strip\r\n}\r\n\r\n\/\/==========================================================\r\n\r\nclass LED {\r\nprivate:\r\n    int nr;             \/\/ sequence number, starting from 0\r\n    int pin;            \/\/ the GPIO pin of the LED\r\n    int state;          \/\/ the current state (0=off, 1=on)\r\n    LEDStrip *strip;    \/\/ a pointer to the LED strip \r\npublic:\r\n    LED(int nr,int pin,LEDStrip *strip);\/\/ constructor\r\n    void toggle();                      \/\/ change between ON and OFF\r\n    int getState();                     \/\/ returns 0=OFF or 1=ON\r\n    String toString();                  \/\/ show object properties as a string (for debugging)\r\n};\r\n\r\nLED::LED(int nr, int pin, LEDStrip *strip) {\r\n    \/\/ the LED is connected to +5V (via a resistor), so HIGH == OFF\r\n    this->nr = nr;\r\n    this->strip=strip;\r\n    this->pin = pin;\r\n    pinMode(pin,OUTPUT);\r\n    this->state = HIGH;             \/\/ initialie with HIGH = OFF\r\n    digitalWrite(pin,this->state);\r\n}\r\nint LED::getState() {\r\n    return this->state==LOW ? 1 : 0;\r\n}\r\nvoid LED::toggle() {\r\n    if (this->state == HIGH) {\r\n        this->state=LOW;\r\n        this->strip->on(this->nr);\r\n    }\r\n    else {\r\n        this->state=HIGH;\r\n        this->strip->off(this->nr);\r\n    }\r\n    digitalWrite(this->pin,this->state);\r\n}\r\nString LED::toString() {\r\n    String text = \"LED: { \"+String(this->pin)+\"=\"+String(this->state)+\" }\";\r\n    return text;\r\n}\r\n\r\n\/\/==========================================================\r\n\r\nclass ComLight {\r\n    \/\/ a combination of Button and a group of LEDs\r\nprivate:\r\n    LED* leds&#91;SIZE];    \/\/ an array of pointers to the LEDs\r\n    Button* button;     \/\/ a pointer to the Button\r\n    int size;           \/\/ the currentnumber of connected LEDs\r\n \r\npublic:\r\n    ComLight(Button *button);   \/\/ constructor, takes a Button pointer\r\n    void addLED(LED *led);      \/\/ add the LED to the group\r\n    bool update();              \/\/ check the Button state and toggle LEDs if pushed\r\n    void pushButton();          \/\/ simulate a push button event\r\n    void toggle();              \/\/ apply the switching rule\r\n    String toString();          \/\/ show object properties as a string (for debugging)\r\n};\r\n\r\nComLight::ComLight(Button *button) {\r\n    this->size=0;\r\n    this->button = button;\r\n}\r\nvoid ComLight::addLED(LED *led) {\r\n    this->leds&#91;this->size] = led;\r\n    this->size++;\r\n}\r\nbool ComLight::update() {\r\n    if (this->button->update()) {\r\n        this->toggle();\r\n        return true;\r\n    }\r\n    return false;\r\n}\r\nvoid ComLight::pushButton() {\r\n    this->toggle();\r\n}\r\nvoid ComLight::toggle() {\r\n    for(int l=0;l&lt;this->size;l++) {\r\n        this->leds&#91;l]->toggle();\r\n    }\r\n}\r\nString ComLight::toString() {\r\n    String text = \"ComLight: { \"+this->button->toString()+\", &#91;\";\r\n    for(int l=0;l&lt;this->size;l++) {\r\n        text+=\" \"+this->leds&#91;l]->toString();\r\n    }\r\n    return text+\" ] }\";\r\n}\r\n\r\n\/\/==========================================================\r\n\r\nclass ComLightCircle {\r\nprivate:\r\n    LED*        leds&#91;SIZE];         \/\/ the list of all LEDs\r\n    Button*     buttons&#91;SIZE];      \/\/ the list of all Buttons\r\n    ComLight*   comLights&#91;SIZE];    \/\/ the list of all ComLights\r\n\r\npublic:\r\n    ComLightCircle();               \/\/ default constructor\r\n    ComLightCircle(LEDStrip *strip);\/\/ constructor\r\n    bool    update();               \/\/ check the button and apply switching rule if button was pushed\r\n    void    pushButton(int nr);     \/\/ simulate pushing the ComLight\u00b4s button\r\n    bool    checkSuccess();         \/\/ check if all LEDs are ON and BLINK if yes\r\n    String  toString();             \/\/ show object properties as a string (for debugging)\r\n};\r\n\r\nComLightCircle::ComLightCircle() {}\r\nComLightCircle::ComLightCircle(LEDStrip *strip) {\r\n    \r\n    \/\/ create Buttons, pins start at #2\r\n    for(int b=0;b&lt;SIZE;b++) this->buttons&#91;b]= new Button(2+b);\r\n\r\n    \/\/ create LEDs, pins start at #8\r\n    for(int l=0;l&lt;SIZE;l++) this->leds&#91;l]= new LED(l,8+l,strip);\r\n\r\n    \/\/ compose ComLights using a Button and a group of connnected LEDs\r\n    for(int c=0;c&lt;SIZE;c++) {\r\n        ComLight* cl = new ComLight(this->buttons&#91;c]);\r\n        cl->addLED(this->leds&#91;(c+SIZE-1)%SIZE]);\r\n        cl->addLED(this->leds&#91;c]);\r\n        cl->addLED(this->leds&#91;(c+1)%SIZE]);\r\n        this->comLights&#91;c]= cl;\r\n        Serial.println(cl->toString());\r\n    }\r\n}\r\n\r\nbool ComLightCircle::update() {\r\n    \/\/ check button and apply switching rule if pushed\r\n    \/\/ return true if the button was pushed\r\n    bool changed=false;\r\n    for(int c=0;c&lt;SIZE;c++) {\r\n        if (this->comLights&#91;c]->update()) changed=true;\r\n    }\r\n    return changed;\r\n}\r\n\r\nvoid ComLightCircle::pushButton(int nr) {\r\n    \/\/ simulate a button push event\r\n    this->comLights&#91;nr]->pushButton();\r\n}\r\n\r\nbool ComLightCircle::checkSuccess() {\r\n    \/\/ check if all LEDs are ON; if yes blink three times\r\n    for (int l=0;l&lt;SIZE;l++) {\r\n        if (!this->leds&#91;l]->getState()) return false;\r\n    }\r\n    delay(500);\r\n    for (int n=0;n&lt;10;n++) {\r\n        for (int l=0;l&lt;SIZE;l++) {\r\n            this->leds&#91;l]->toggle();\r\n        }\r\n        delay(100);\r\n    }    \r\n}\r\n\r\nString ComLightCircle::toString() {\r\n    String text = \"ComLightCircle: { &#91;\";\r\n\r\n    for(int l=0;l&lt;SIZE;l++) {\r\n        text+=\" \"+String(this->leds&#91;l]->getState());\r\n    }\r\n    return text+\" ] }\";\r\n}\r\n\r\n\/\/==========================================================\r\n\r\nComLightCircle  comLightCircle;     \/\/ the ComLightCircle object\r\nAdafruit_NeoPixel pixels = Adafruit_NeoPixel(2*SIZE, 7, NEO_GRB + NEO_KHZ800);\r\nLEDStrip theLEDStrip = LEDStrip(&amp;pixels);        \/\/ the LED strip\r\n\r\nvoid setup() {\r\n    \/\/ setup serial connection for debugging \/ monitoring\r\n    Serial.begin(115200);\r\n    Serial.println(\"ComLightCircle starting ..\");\r\n\r\n    \/\/ create the ComLightCircle\r\n    comLightCircle = ComLightCircle(&amp;theLEDStrip);\r\n\r\n    \/\/ and show its initial state\r\n    Serial.println(comLightCircle.toString());\r\n}\r\n\r\nint n=-1;\r\n\r\n#define TEST 1\r\n#define TINKERCAD 1\r\n\r\nvoid loop() {\r\n    n++;    \/\/ loop counter\r\n    \r\n    \/\/ update the ComLightCircle and show it if sonething has changed\r\n    if (comLightCircle.update()) {\r\n        Serial.println(comLightCircle.toString());\r\n        comLightCircle.checkSuccess();\r\n    }\r\n\r\n#ifdef TEST\r\n    for (int c=0;c&lt;SIZE;c++) {\r\n        if (n%500==c*100) {\r\n            comLightCircle.pushButton(c);\r\n            comLightCircle.checkSuccess();\r\n        }\r\n        if (n==500) n=0;   \/\/ avoid arithemtic overflow\r\n    }\r\n#endif\r\n\r\n    \/\/ a small pause, helpful for TinkerCad simulation\r\n    delay(10);\r\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/posts\/259"}],"collection":[{"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/comments?post=259"}],"version-history":[{"count":2,"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/posts\/259\/revisions"}],"predecessor-version":[{"id":261,"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/posts\/259\/revisions\/261"}],"wp:attachment":[{"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/media?parent=259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/categories?post=259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/followthescore.org\/schueler-labor\/wp-json\/wp\/v2\/tags?post=259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}