Not Safely Sending Signals

During Global Game Jam 2020, I was asked about the easiest way to have a state propagate a cluster of Blinks and I recalled the first algorithm I wrote to do this. Since then @danking222 has created and outlined a much safer way to do this, and it is such a nice solution. It takes a moment to wrap your head around why it is so important to do what he outlines, but it will save us all headaches down the road. That said, I believe there is definitely more than one way to pet a kitten, so try out the sketch below, and use it with caution. Also, I would love to hear your thoughts on why this is less safe :construction_worker_man:

/*
 * Spreading a message in the most precarious way possible
 * 
 * Send the message we heard, 
 * then don't listen for a period of time
 * 
 * This is how I used to propogate messages until Dan King created
 * a much more stable solution. This sketch does do what you'd expect
 * but it feels a bit more precarious than the validation that occurs with
 * the Safe Communication tutorials
 * 
 * by Jonathan Bobrow
 * 02.02.2020
 */

#define BROADCAST_DURATION 100
#define DEAF_DURATION 200

uint32_t timeOfSwitch = 0;

Color colors[] = {WHITE,CYAN,MAGENTA,BLUE,ORANGE,GREEN,RED}; 

byte myState = 1;
byte numStates = 6;

void setup() {
  // nothing needed in setup
}

void loop() {

  // increment our state each time the button is pressed
  if(buttonPressed()) {
    setState(myState+1);
  }


  // determine if we should listen to our neighbors
  if(timeOfSwitch < millis() - (BROADCAST_DURATION + DEAF_DURATION) ) {
    
    // we have broadcasted and ignored feedback, so we are good to listen again
    FOREACH_FACE(f) {

      // is there a Blink on this face?
      if(!isValueReceivedOnFaceExpired(f)) {

        // get their value
        byte neighbor = getLastValueReceivedOnFace(f);
        
        // is our neighbor different (since Blinks broadcast 0 by default, we don't want to respond to 0)
        if(neighbor != 0 && neighbor != myState) {

          // let's match our neighbor :)
          setState(neighbor);
        }
      }
    }
  }

  // is the time passed our broadcast duration?
  if(timeOfSwitch < millis() - BROADCAST_DURATION ) {

    // return to sending 0
    setValueSentOnAllFaces(0);  // stop sending state
  }

  // display a color that matches our state
  setColor(colors[myState]);
}


/*
 * setState
 *  - Set the state of the Blink to this value
 *  - Broadcast the value of the Blink on all faces
 *  - Set the time that we started broadcasting
 */
void setState(byte _state) {
  if(_state >= numStates) {
    myState = 1;
  }
  else {
    myState = _state;
  }

  timeOfSwitch = millis();

  setValueSentOnAllFaces(myState);  // start sending state
}

Using timers is, generally speaking brittle. Other than that, it is difficult to figure out the right balance between ignoring and processing signals.

Did you see my approach to this?

I also implemented this as a library to simplify using face value signals: