欢迎光临散文网 会员登陆 & 注册

赛博沙漏源码

2023-08-20 16:59 作者:呆毛呆毛快显灵  | 我要投稿

原文链接:

Hourglass Using Arduino : 5 Steps (with Pictures) - Instructables

ARDUINO HOURGLASS.INO · GitHub

注意:该链接可能需要科学上网


//code credits Fabrizio Branca


//EDISON SCIENCE CORNER


#include "Arduino.h"

#include "LedControl.h"

#include "Delay.h"


#define  MATRIX_A  1

#define MATRIX_B  0


// Values are 260/330/400

#define ACC_THRESHOLD_LOW 300

#define ACC_THRESHOLD_HIGH 360


// Matrix

#define PIN_DATAIN 5

#define PIN_CLK 4

#define PIN_LOAD 6


// Accelerometer

#define PIN_X A1

#define PIN_Y A2


// Rotary Encoder

#define PIN_ENC_1 3

#define PIN_ENC_2 2

#define PIN_ENC_BUTTON 7


#define PIN_BUZZER 14


// This takes into account how the matrixes are mounted

#define ROTATION_OFFSET 90


// in milliseconds

#define DEBOUNCE_THRESHOLD 500


#define DELAY_FRAME 100


#define DEBUG_OUTPUT 1


#define MODE_HOURGLASS 0

#define MODE_SETMINUTES 1

#define MODE_SETHOURS 2


byte delayHours = 0;

byte delayMinutes = 1;

int mode = MODE_HOURGLASS;

int gravity;

LedControl lc = LedControl(PIN_DATAIN, PIN_CLK, PIN_LOAD, 2);

NonBlockDelay d;

int resetCounter = 0;

bool alarmWentOff = false;



/**

 * Get delay between particle drops (in seconds)

 */

long getDelayDrop() {

  // since we have exactly 60 particles we don't have to multiply by 60 and then divide by the number of particles again :)

  return delayMinutes + delayHours * 60;

}



#if DEBUG_OUTPUT

void printmatrix() {

  Serial.println(" 0123-4567 ");

  for (int y = 0; y<8; y++) {

    if (y == 4) {

      Serial.println("|----|----|");

    }

    Serial.print(y);

    for (int x = 0; x<8; x++) {

      if (x == 4) {

        Serial.print("|");

      }

      Serial.print(lc.getXY(0,x,y) ? "X" :" ");

    }

    Serial.println("|");

  }

  Serial.println("-----------");

}

#endif




coord getDown(int x, int y) {

  coord xy;

  xy.x = x-1;

  xy.y = y+1;

  return xy;

}

coord getLeft(int x, int y) {

  coord xy;

  xy.x = x-1;

  xy.y = y;

  return xy;

}

coord getRight(int x, int y) {

  coord xy;

  xy.x = x;

  xy.y = y+1;

  return xy;

}




bool canGoLeft(int addr, int x, int y) {

  if (x == 0) return false; // not available

  return !lc.getXY(addr, getLeft(x, y)); // you can go there if this is empty

}

bool canGoRight(int addr, int x, int y) {

  if (y == 7) return false; // not available

  return !lc.getXY(addr, getRight(x, y)); // you can go there if this is empty

}

bool canGoDown(int addr, int x, int y) {

  if (y == 7) return false; // not available

  if (x == 0) return false; // not available

  if (!canGoLeft(addr, x, y)) return false;

  if (!canGoRight(addr, x, y)) return false;

  return !lc.getXY(addr, getDown(x, y)); // you can go there if this is empty

}




void goDown(int addr, int x, int y) {

  lc.setXY(addr, x, y, false);

  lc.setXY(addr, getDown(x,y), true);

}

void goLeft(int addr, int x, int y) {

  lc.setXY(addr, x, y, false);

  lc.setXY(addr, getLeft(x,y), true);

}

void goRight(int addr, int x, int y) {

  lc.setXY(addr, x, y, false);

  lc.setXY(addr, getRight(x,y), true);

}



int countParticles(int addr) {

  int c = 0;

  for (byte y=0; y<8; y++) {

    for (byte x=0; x<8; x++) {

      if (lc.getXY(addr, x, y)) {

        c++;

      }

    }

  }

  return c;

}



bool moveParticle(int addr, int x, int y) {

  if (!lc.getXY(addr,x,y)) {

    return false;

  }


  bool can_GoLeft = canGoLeft(addr, x, y);

  bool can_GoRight = canGoRight(addr, x, y);


  if (!can_GoLeft && !can_GoRight) {

    return false; // we're stuck

  }


  bool can_GoDown = canGoDown(addr, x, y);


  if (can_GoDown) {

    goDown(addr, x, y);

  } else if (can_GoLeft&& !can_GoRight) {

    goLeft(addr, x, y);

  } else if (can_GoRight && !can_GoLeft) {

    goRight(addr, x, y);

  } else if (random(2) == 1) { // we can go left and right, but not down

    goLeft(addr, x, y);

  } else {

    goRight(addr, x, y);

  }

  return true;

}




void fill(int addr, int maxcount) {

  int n = 8;

  byte x,y;

  int count = 0;

  for (byte slice = 0; slice < 2*n-1; ++slice) {

    byte z = slice<n ? 0 : slice-n + 1;

    for (byte j = z; j <= slice-z; ++j) {

      y = 7-j;

      x = (slice-j);

      lc.setXY(addr, x, y, (++count <= maxcount));

    }

  }

}




/**

 * Detect orientation using the accelerometer

 *

 *     | up | right | left | down |

 * --------------------------------

 * 400 |    |       | y    | x    |

 * 330 | y  | x     | x    | y    |

 * 260 | x  | y     |      |      |

 */

int getGravity() {

  int x = analogRead(PIN_X);

  int y = analogRead(PIN_Y);

  if (y < ACC_THRESHOLD_LOW)  { return 0;   }

  if (x > ACC_THRESHOLD_HIGH) { return 90;  }

  if (y > ACC_THRESHOLD_HIGH) { return 180; }

  if (x < ACC_THRESHOLD_LOW)  { return 270; }

}



int getTopMatrix() {

  return (getGravity() == 90) ? MATRIX_A : MATRIX_B;

}

int getBottomMatrix() {

  return (getGravity() != 90) ? MATRIX_A : MATRIX_B;

}




void resetTime() {

  for (byte i=0; i<2; i++) {

    lc.clearDisplay(i);

  }

  fill(getTopMatrix(), 60);

  d.Delay(getDelayDrop() * 1000);

}




/**

 * Traverse matrix and check if particles need to be moved

 */

bool updateMatrix() {

  int n = 8;

  bool somethingMoved = false;

  byte x,y;

  bool direction;

  for (byte slice = 0; slice < 2*n-1; ++slice) {

    direction = (random(2) == 1); // randomize if we scan from left to right or from right to left, so the grain doesn't always fall the same direction

    byte z = slice<n ? 0 : slice-n + 1;

    for (byte j = z; j <= slice-z; ++j) {

      y = direction ? (7-j) : (7-(slice-j));

      x = direction ? (slice-j) : j;

      // for (byte d=0; d<2; d++) { lc.invertXY(0, x, y); delay(50); }

      if (moveParticle(MATRIX_B, x, y)) {

        somethingMoved = true;

      };

      if (moveParticle(MATRIX_A, x, y)) {

        somethingMoved = true;

      }

    }

  }

  return somethingMoved;

}




/**

 * Let a particle go from one matrix to the other

 */

boolean dropParticle() {

  if (d.Timeout()) {

    d.Delay(getDelayDrop() * 1000);

    if (gravity == 0 || gravity == 180) {

      if ((lc.getRawXY(MATRIX_A, 0, 0) && !lc.getRawXY(MATRIX_B, 7, 7)) ||

          (!lc.getRawXY(MATRIX_A, 0, 0) && lc.getRawXY(MATRIX_B, 7, 7))

      ) {

        // for (byte d=0; d<8; d++) { lc.invertXY(0, 0, 7); delay(50); }

        lc.invertRawXY(MATRIX_A, 0, 0);

        lc.invertRawXY(MATRIX_B, 7, 7);

        tone(PIN_BUZZER, 440, 10);

        return true;

      }

    }

  }

  return false;

}




void alarm() {

  for (int i=0; i<5; i++) {

    tone(PIN_BUZZER, 440, 200);

    delay(1000);

  }

}




void resetCheck() {

  int z = analogRead(A3);

  if (z > ACC_THRESHOLD_HIGH || z < ACC_THRESHOLD_LOW) {

    resetCounter++;

    Serial.println(resetCounter);

  } else {

    resetCounter = 0;

  }

  if (resetCounter > 20) {

    Serial.println("RESET!");

    resetTime();

    resetCounter = 0;

  }

}




void displayLetter(char letter, int matrix) {

  // Serial.print("Letter: ");

  // Serial.println(letter);

  lc.clearDisplay(matrix);

  lc.setXY(matrix, 1,4, true);

  lc.setXY(matrix, 2,3, true);

  lc.setXY(matrix, 3,2, true);

  lc.setXY(matrix, 4,1, true);


  lc.setXY(matrix, 3,6, true);

  lc.setXY(matrix, 4,5, true);

  lc.setXY(matrix, 5,4, true);

  lc.setXY(matrix, 6,3, true);


  if (letter == 'M') {

    lc.setXY(matrix, 4,2, true);

    lc.setXY(matrix, 4,3, true);

    lc.setXY(matrix, 5,3, true);

  }

  if (letter == 'H') {

    lc.setXY(matrix, 3,3, true);

    lc.setXY(matrix, 4,4, true);

  }

}




void renderSetMinutes() {

  fill(getTopMatrix(), delayMinutes);

  displayLetter('M', getBottomMatrix());

}

void renderSetHours() {

  fill(getTopMatrix(), delayHours);

  displayLetter('H', getBottomMatrix());

}





void knobClockwise() {

  Serial.println("Clockwise");

  if (mode == MODE_SETHOURS) {

    delayHours = constrain(delayHours+1, 0, 64);

    renderSetHours();

  } else if(mode == MODE_SETMINUTES) {

    delayMinutes = constrain(delayMinutes+1, 0, 64);

    renderSetMinutes();

  }

  Serial.print("Delay: ");

  Serial.println(getDelayDrop());

}

void knobCounterClockwise() {

  Serial.println("Counterclockwise");

  if (mode == MODE_SETHOURS) {

    delayHours = constrain(delayHours-1, 0, 64);

    renderSetHours();

  } else if (mode == MODE_SETMINUTES) {

    delayMinutes = constrain(delayMinutes-1, 0, 64);

    renderSetMinutes();

  }

  Serial.print("Delay: ");

  Serial.println(getDelayDrop());

}




volatile int lastEncoded = 0;

volatile long encoderValue = 0;

long lastencoderValue = 0;

long lastValue = 0;

void updateEncoder() {

  int MSB = digitalRead(PIN_ENC_1); //MSB = most significant bit

  int LSB = digitalRead(PIN_ENC_2); //LSB = least significant bit


  int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number

  int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value


  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue--;

  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue++;


  // Serial.print("Value: ");

  // Serial.println(encoderValue);

  if ((encoderValue % 4) == 0) {

    int value = encoderValue / 4;

    if (value > lastValue) knobClockwise();

    if (value < lastValue) knobCounterClockwise();

    lastValue = value;

  }

  lastEncoded = encoded; //store this value for next time

}




/**

 * Button callback (incl. software debouncer)

 * This switches between the modes (normal, set minutes, set hours)

 */

volatile unsigned long lastButtonPushMillis;

void buttonPush() {

  if((long)(millis() - lastButtonPushMillis) >= DEBOUNCE_THRESHOLD) {

    mode = (mode+1) % 3;

    Serial.print("Switched mode to: ");

    Serial.println(mode);

    lastButtonPushMillis = millis();


    if (mode == MODE_SETMINUTES) {

      lc.backup(); // we only need to back when switching from MODE_HOURGLASS->MODE_SETMINUTES

      renderSetMinutes();

    }

    if (mode == MODE_SETHOURS) {

      renderSetHours();

    }

    if (mode == MODE_HOURGLASS) {

      lc.clearDisplay(0);

      lc.clearDisplay(1);

      lc.restore();

      resetTime();

    }

  }

}




/**

 * Setup

 */

void setup() {

  Serial.begin(9600);


  // while (!Serial) {

  //   ; // wait for serial port to connect. Needed for native USB

  // }


  // setup rotary encoder

  pinMode(PIN_ENC_1, INPUT);

  pinMode(PIN_ENC_2, INPUT);

  pinMode(PIN_ENC_BUTTON, INPUT);

  digitalWrite(PIN_ENC_1, HIGH); //turn pullup resistor on

  digitalWrite(PIN_ENC_2, HIGH); //turn pullup resistor on

  digitalWrite(PIN_ENC_BUTTON, HIGH); //turn pullup resistor on

  attachInterrupt(digitalPinToInterrupt(PIN_ENC_1), updateEncoder, CHANGE);

  attachInterrupt(digitalPinToInterrupt(PIN_ENC_2), updateEncoder, CHANGE);

  attachInterrupt(digitalPinToInterrupt(PIN_ENC_BUTTON), buttonPush, RISING);


  // Serial.println(digitalPinToInterrupt(PIN_ENC_1));

  // Serial.println(digitalPinToInterrupt(PIN_ENC_2));

  // Serial.println(digitalPinToInterrupt(PIN_ENC_BUTTON));


  randomSeed(analogRead(A0));


  // init displays

  for (byte i=0; i<2; i++) {

    lc.shutdown(i,false);

    lc.setIntensity(i,0);

  }


  resetTime();

}




/**

 * Main loop

 */

void loop() {

  delay(DELAY_FRAME);


  // update the driver's rotation setting. For the rest of the code we pretend "down" is still 0,0 and "up" is 7,7

  gravity = getGravity();

  lc.setRotation((ROTATION_OFFSET + gravity) % 360);


  // handle special modes

  if (mode == MODE_SETMINUTES) {

    renderSetMinutes(); return;

  } else if (mode == MODE_SETHOURS) {

    renderSetHours(); return;

  }


  // resetCheck(); // reset now happens when pushing a button

  bool moved = updateMatrix();

  bool dropped = dropParticle();


  // alarm when everything is in the bottom part

  if (!moved && !dropped && !alarmWentOff && (countParticles(getTopMatrix()) == 0)) {

    alarmWentOff = true;

    alarm();

  }

  // reset alarm flag next time a particle was dropped

  if (dropped) {

    alarmWentOff = false;

  }

}


赛博沙漏源码的评论 (共 条)

分享到微博请遵守国家法律