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:
- Arduino Nano (Elinizde olan herhangi bir Arduino modelini de kullanabilirsiniz.)
- ADXL345 3 Eksen İvme Ölçer
- 8×8 Dot Matrix Kartı
- Jumper Kablolar
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 ?