Herkese merhabalar dostlar, bu yazıda dot matrix kullanarak elektronik kum saati yapımının detaylarına göz atacağız. Bu örnek Arduino projesi Tubitak 4006 , Teknofest gibi yarışmalar için örnek bir proje de olabilir. Tubitak 4006 örnek projesi olarak kullanırsanız referans gösteriniz.

Proje Fabrizio Branca’ya ait olan kum saati projesinden esinlenilmiştir. Orjinal Proje videosuna aşağıdaki link üstünden ulaşabilirsiniz.
Https://www.Youtube.Com/watch?V=U-vmEOuy348

Malzemeler

Projenin ana malzemesi kum saati görüntüsünü oluşturacak dot matrixler. Dot matrix modülleri üstündeki LEDleri kullanarak kum saati görüntüsünü elde etmiş olacağız. Bir öteki mühim parça ise ADXL345 İvme ölçer.

ADXL345 Sensörü Özellikleri:

  • Giriş Voltajı: 3-5V
  • Düşük güç tüketimi(40uA)
  • Serbest düşüş algılama
  • SPI ve I²C arayüzü

Sensörün üç eksende verebildiği açısal ivme değerleri ile saatin eksen hareketlerini tespit ederek LED matrixleri test etmiş olacağız.

Projenin ana malzemeleri:

Bağlantı Şeması

Sensör ve dot matrixlerin Arduino ile olan bağlantılarını aşağıdaki şema üzerinde görebilirsiniz.

Tüm linkleri yaparak projeyi kod aşamasına hazır hale getiriyoruz.

3D Dosyalar

Projeyi kum saati görünümüne çeviren tasarımın tüm parçalarına buraya tıklayarak ulaşabilirsiniz..

Proje Kodu

Fabrizio Branca isimli arkadaşının projesinden faydalandığımız bu projede en uygun fiyatlı ve en kolay sensörleri kullanmaya çalıştık. Kod kısmında işe kendi kodunda kullandığı kütüphaneleri indirerek başlıyoruz. Ardından Kütüphaneler indikten sonra bunları Arduino’ya programına eklememiz gerekiyor. İndireceğimiz kütüphanenin linki aşağıdadır;

Kütüphaneler: https://github.Com/fbrnc/Arduino_Hourglass/tree/master/lib
(Delay ve LedControl kütüphanelerini indiriyoruz)

Projenin ana parçalarından olan ADXL345 sensörünün kullanım öncesi kalibre edilmesi projemizin daha sıhhatli çalışmasını elde edecektir. ADXL sensörünü otomatik olarak kalibre etmek için Adafruit’in ADXL345 kütüphanesini (Eğer yüklü değilse Adafruit’in unified sensor kütüphanesini de kurmanız gerekiyor.) kurabilirsiniz. Ardından aşağıdaki linkteki aşamaları takip ederek sensörü otomatik bir biçimde kalibre edebilirsiniz.
Https://learn.Adafruit.Com/adxl345-digital-accelerometer/programming

ADXL345 sensörü ile alakalı detaylı bilgi için bu videoya da göz atabilirsiniz: https://www.Youtube.Com/watch?V=KMhbV1p3MWk

Ön hazırlıklar tamamlandıysa kod yüklenmeye hazır. Kod içerisindeki THRESHOLD değerleri ile oynayarak kum saatinin sensöre nazaran dönüş hassasiyetini ayarlayabilirsiniz.

Koddaki delayHours ve delayMinutes değişkenlerinin de kum saatinin boşalım süresini temsil ettiğini eklemek istiyorum.

#include "Arduino.h"
#include "LedControl.h"
#include "Delay.h"
#include <Wire.h>

int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float x, y;

#define  MATRIX_A  1
#define MATRIX_B  0

#define ACC_THRESHOLD_LOW -0.5
#define ACC_THRESHOLD_HIGH 0

// Matrix
#define PIN_DATAIN 5
#define PIN_CLK 4
#define PIN_LOAD 6

// This takes into account how the matrixes are mounted
#define ROTATION_OFFSET 90

// in milliseconds
#define DEBOUNCE_THRESHOLD 500
#define DELAY_FRAME 80


byte delayHours = 0;
byte delayMinutes = 1;

int gravity;
LedControl lc = LedControl(PIN_DATAIN, PIN_CLK, PIN_LOAD, 2);
NonBlockDelay d;
int resetCounter = 0;

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;
}

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));
    }
  }
}

int getGravity() {
  Wire.beginTransmission(ADXL345);
  Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  x = ( Wire.read()| Wire.read() << 8); // X-axis value
  x = x/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  y = ( Wire.read()| Wire.read() << 8); // Y-axis value
  y = y/256;
  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);
        return true;
      }
    }
  }
  return false;
}


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;
  }
}

/**
 * Setup
 */
void setup() {
  Serial.begin(9600);

  Wire.begin(); // Initiate the Wire library
  // Set ADXL345 in measuring mode
  Wire.beginTransmission(ADXL345); // Start communicating with the device 
  Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  // Enable measurement
  Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable 
  Wire.endTransmission();
  delay(10);

  // init displays
  for (byte i=0; i<2; i++) {
    lc.shutdown(i,false);
    lc.setIntensity(i,2);
  }

  resetTime();
}

/**
 * Main loop
 */
void loop() {
  delay(DELAY_FRAME);

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

  bool moved = updateMatrix();
  bool dropped = dropParticle();
  }

Umarım keyifli bir proje olmuştur. Gelecek videolar için proje fikirlerinizi yorum kısmında paylaşmayı unutmayın. Bir başka projede görüşünceye dek, hoşça kalın ?

Leave A Comment

All fields marked with an asterisk (*) are required