Monday, September 21, 2020

LED Sand Clock

A fun project to build an electronic sand clock with ATtiny85 from scratch.


There are 4 direction for the "sand" to fall

 


 


Click here to see the whole post! inkl. Schematics and code.


It consists of 3 modules:
Display, Control unit and Sensor.


There are 3 wires to control the display and 1 wire to get the direction from the sensor. Every part can be replaced.

Controls:
 



Schematics:


Page logic




Display

 
You can see two 4011C (4x NAND Gate) on the top and CD4040BE (12-Stage Ripple-Carry Binary Counter/Divider) on the right side.

30 LEDs are divided in 3 pages. Pins 1-10 of the binary counter controls the group position. Combination of the pins 11 and 12 controls the page number. 00 - first page, 01 - second and 10 - third.


Sensor


Direction sensor uses 4 tilt sensors to find the top direction. Each tilt sensor sends another value to the analog pin.

Code:


Global variables:

int setPin = 0;
int resetPin = 1;
int lightOnPin = 2;
int buttonPin = 3; // pin for the change mode button
int sensorPin = A2; // pin for th edirection sensor

int sensorValue = 0;  // variable to store the value coming from the sensor
int dotSpeed = 8; // move dot every X loop
int loopCounter = 0; // count every game loop (max 1023 then reset)
int display_mode = 1; // 1-15 number of displayed dots or tests
int cur_number_of_dots = 1; 
const byte MAX_NUMBER_OF_DOTS = 30;
unsigned int dots[MAX_NUMBER_OF_DOTS] = { // positions of the dots
  1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
}; 

/**
 *     1
 *   2   4
 *     3
 */
unsigned int top_direction;
char compas[5] = "*NESW";
 
const byte NUMBER_OF_POSITIONS = 30;
unsigned int screenMap[NUMBER_OF_POSITIONS] = {
  B00000001+2048,B00000001+1024,B00000010+1024,B00000100+2048,B00000100+1024,               //  1, 2, 3, 4, 5
    B00000001,B00000010+2048,B00000010,B00000100,                                           //   6, 7, 8, 9
            B00001000+2048,B00001000,B00001000+1024,                                        //    10,11,12
                  B00010000+2048,B00010000+1024,                                            //     13,14
                      B00010000,                                                            //       15
                      B00100000,                                                            //       16
                  B00100000+2048,B00100000+1024,                                            //     17,18
            B01000000+2048,B01000000,B01000000+1024,                                        //    19,20,21 
    B00000000+512,B00000000+256+2048,B00000000+256,B10000000,                               //   22,23,24,25
  B00000000+512+2048,B00000000+512+1024,B00000000+256+1024,B10000000+2048,B10000000+1024,   //  26,27,28,29,30
};
unsigned int position; 


How to print the dots:

void sandClockMode() {
  int groupA = 0;
  int groupB = 0;
  int groupC = 0;

  for (int i = 0; i < cur_number_of_dots; i++) {
    position = dots[i];
    if (!(loopCounter % dotSpeed)) {
        position = findNewPosWithGravity(position);
    }

    dots[i] = position;
    if (screenMap[position-1] > 2048) {
      groupA = groupA | screenMap[position-1];
    } else if (screenMap[position-1] > 1024) {
      groupB = groupB | screenMap[position-1];
    } else {
      groupC = groupC | screenMap[position-1];
    }
  }
  printDot(groupA);
  printDot(groupB);
  printDot(groupC);
}


Sensor code:

int readSensor() {
  sensorValue = analogRead(sensorPin);
  //  0 - undefined, 1 - N, 2 - E, 3 - S, 4 - W
  if (sensorValue == 0 ) {
    topDirection = 0;
  } else if (sensorValue > 900) {
    topDirection = 3;
  } else if (sensorValue > 400) {
    topDirection = 4; 
  } else if  (sensorValue > 200) {
    topDirection = 1; 
  } else {
    topDirection = 2; 
  }  
  return topDirection;
}


How to move dots:

/**
 * Find the new position with gravity sensor
 * @param int pos
 * @return int
 */
int findNewPosWithGravity(int pos) {
  dir = getDirection();

  if (pos == 1) {
    if (dir == 1) {
      pos = 6;
    } else if (dir == 2) {
      if (!isPositionFree(2)) {
        pos = 6;
      } else {
        pos = 2;
      }      
    }
  } else if (pos == 2) {
    if (dir == 1) {
      if (!isPositionFree(10)) {
        if (!isPositionFree(6)) {
          pos = 7;
        } else {
          pos = 6;
        }        
      } else {
        pos = 10;
      }
    }
... and so on...


How to check if position is free
/**
 * Find out it the given position is free
 * @param int pos
 * @return bool
 */
bool isPositionFree(int pos) {
  for (int i = 0; i < cur_number_of_dots; i++) {
    if (dots[i] == pos) {
      return false;
    }
  }
  return true;
}

Labels: , ,

0 Comments:

Post a Comment

<< Home