/*===================================================
 * Clay PCB Workshop — Mesh Node
 * 
 * Change NODE_ID before flashing each board.
 * 
 * D11       — trigger input (button, sensor etc)
 * A0–A5     — outputs 1–6
 * D13       — output 7
 * D2        — network bus (shared wire between all nodes)
 * D5        - led pin for feedback
 * 
 * When trigger fires  -> sends NODE_ID to all nodes
 * When message N arrives -> activates output[ N % 7 ]
 * 
 *   node 1 -> A5 | node 2 -> A4 | node 3 -> A3
 *   node 4 -> A2 | node 5 -> A1 | node 6 -> A0 | node 7 -> D13
 *   node 8 -> A5 (wraps around)...
 * 
 * Bus wiring: one wire shared between all nodes on D9, 
 * one 4.7kΩ pullup to 5V anywhere on bus.
 * one shared ground wire.
 *===================================================*/

#define NODE_ID 2   // <- change this for each board, starts at a humanfriendly #1

/*
┌─────────────────────────────┐
|  EXAMPLE:                   |
│  YOU ARE NODE 1             │
│  Your trigger: wire on D11  │
│                             │
│  When YOU press → A5 on     │
│  every board reacts         │
│                             │
│  Your board reacts to:      │
│   A5 <- Node 1   (you)      │
│   A4 <- Node 2              │
│   A3 <- Node 3              │
│   A2 <- Node 4              │
│   A1 <- Node 5              │
│   A0 <- Node 6              │
│  D13 <- Node 7              │
└─────────────────────────────┘
*/

#define BUS_PIN 2
#define TRIGGER_PIN 11

const uint8_t OUTPUT_PINS[] = { A5, A4, A3, A2, A1, A6, 13 };
#define NUM_OUTPUTS 7
#define OUTPUT_MS 1000  // how long an output stays on (milliseconds)

#define BIT_US 1600    // bit period — long enough for 8MHz + oscillator drift
#define START_US 3200  // start pulse length

unsigned long outputOffAt[NUM_OUTPUTS];

#define LED_PIN 5    // slow blink pin to test if the sketch is running (its not broken out, but close to ground so you can hold a led to it)
static unsigned long ledChangeAt = 0; // timing for the led blink
static bool ledOn = false;

void setup() {
  Serial.begin(9600);
  Serial.print("Node ");
  Serial.println(NODE_ID);

  pinMode(TRIGGER_PIN, INPUT_PULLUP);
  pinMode(BUS_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);

  for (int i = 0; i < NUM_OUTPUTS; i++) {
    pinMode(OUTPUT_PINS[i], OUTPUT);
    digitalWrite(OUTPUT_PINS[i], LOW);
    outputOffAt[i] = 0;
  }

  // Blink own output pin and LED_PIN <NODE_ID> times on startup to confirm ID
  int myOutput = OUTPUT_PINS[NODE_ID % NUM_OUTPUTS];
  for (int i = 0; i < NODE_ID; i++) {
    digitalWrite(myOutput, HIGH);
    digitalWrite(LED_PIN, HIGH);
    delay(100);
    digitalWrite(myOutput, LOW);
    digitalWrite(LED_PIN, LOW);
    delay(400);
  }
  delay(1000); // wait a bit for clarity
}


void loop() {
  // Turn off outputs whose timer has expired
  unsigned long now = millis();
  for (int i = 0; i < NUM_OUTPUTS; i++) {
    if (outputOffAt[i] > 0 && now >= outputOffAt[i]) {
      digitalWrite(OUTPUT_PINS[i], LOW);
      outputOffAt[i] = 0;
    }
  }

  // Send NODE_ID when trigger fires (INPUT_PULLUP -> active LOW)
  if (digitalRead(TRIGGER_PIN) == LOW) {
    sendByte(NODE_ID);
    Serial.print("sent");
    delay(250);  // debounce
  }

  // Listen for incoming messages
  int msg = readByte();
  if (msg > 0) {
    int idx = (msg - 1) % NUM_OUTPUTS;
    digitalWrite(OUTPUT_PINS[idx], HIGH);
    outputOffAt[idx] = millis() + OUTPUT_MS;
    Serial.print("received ");
    Serial.println(msg);
  }

  // slow blink a pin for testing
  now = millis(); // even recycle variables ;)
  if (now >= ledChangeAt) {
    if (!ledOn) {
      digitalWrite(LED_PIN, HIGH);
      ledOn = true;
      ledChangeAt = now + 20;  // stay on 100 ms
    } else {
      digitalWrite(LED_PIN, LOW);
      ledOn = false;
      ledChangeAt = now + 5000;  // stay off 1000 ms
    }
  }
}

void sendByte(byte b) {
  pinMode(BUS_PIN, OUTPUT);

  digitalWrite(BUS_PIN, LOW);  // start pulse
  delayMicroseconds(START_US);

  for (int i = 0; i < 8; i++) {  // 8 data bits
    digitalWrite(BUS_PIN, (b >> i) & 1);
    delayMicroseconds(BIT_US);
  }

  digitalWrite(BUS_PIN, HIGH);  // stop
  delayMicroseconds(BIT_US);

  pinMode(BUS_PIN, INPUT_PULLUP);
}


int readByte() {
  pinMode(BUS_PIN, INPUT_PULLUP);

  if (digitalRead(BUS_PIN) == HIGH) return -1;  // bus idle, nothing to read

  delayMicroseconds(START_US + BIT_US / 2);  // skip start pulse, land mid bit-0?

  byte value = 0;
  for (int i = 0; i < 8; i++) {
    value |= (digitalRead(BUS_PIN) << i);
    delayMicroseconds(BIT_US);
  }

  if (value == 0) return -1;  // discard empty reads
  return value;
}
