Hi all,
I am making an hourglass (exactly like this https://www.instructables.com/Arduino-Hourglass/). I modified
the code for ADXL345 accelerometer and its seems to work when connected to PC. But when i connect it to external power, everything works except accelerometer so it is not working properly.
#include <TM1637.h>
#include <Adafruit_ADXL345_U.h>
#include "Arduino.h"
#include "LedControl.h"
#include "Delay.h"
#include <Wire.h>
#define MATRIX_A 0
#define MATRIX_B 1
// Values are 260/330/400
//#define ACC_THRESHOLD_LOW 260
//#define ACC_THRESHOLD_HIGH 400
#define ADXL_THRESHOLD_Z 0
#define ADXL_THRESHOLD_X 0
// Matrix
#define PIN_DATAIN 6
#define PIN_CLK 8
#define PIN_LOAD 7
// Accelerometer
#define PIN_X A5
#define PIN_Y A4
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified();
// Pins Buttons
#define PIN_BUTTON 9
#define PIN_BUTTON2 10
#define PIN_BUZZER 99
// This takes into account how the matrixes are mounted
#define ROTATION_OFFSET 0
// in milliseconds
#define DEBOUNCE_THRESHOLD 200
#define DELAY_FRAME 100
#define MODE_HOURGLASS 0
#define MODE_SETMINUTES 1
#define MODE_SETSECONDS 2
int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float X_out, Y_out, Z_out; // Outputs
float delayMinutes = 0;
float delaySeconds = 40;
float duracion = 30;
float delayMilisecs = (duracion/60)*1000;
int mode = MODE_HOURGLASS;
int gravity;
LedControl lc = LedControl(PIN_DATAIN, PIN_CLK, PIN_LOAD, 2);
NonBlockDelay d;
int resetCounter = 0;
bool alarmWentOff = false;
int CLK = 12;
int DIO = 11;
unsigned long pulsacion;
TM1637 tm(CLK,DIO);
/**
Get delay between particle drops (in seconds)
*/
float 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 + delaySeconds/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));
}
}
}
/**
Detect orientation using the accelerometer
| up | right | left | down |
--------------------------------
400 | | | y | x |
330 | y | x | x | y |
260 | x | y | | |
*/
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_out = ( Wire.read()| Wire.read() << 8); // X-axis value
X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
Y_out = Y_out/256;
Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
Z_out = Z_out/256;
//int x = analogRead(PIN_X);
//int y = analogRead(PIN_Y);
//Serial.println(y);
//Serial.print(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;
//}
if (Z_out < ADXL_THRESHOLD_Z) {
return 0;
}
if (Z_out > ADXL_THRESHOLD_Z) {
return 180;
}
if (X_out < ADXL_THRESHOLD_X) {
return 270;
}
if (X_out > ADXL_THRESHOLD_X) {
return 90;
}
}
int getTopMatrix() {
return (getGravity() == 180) ? MATRIX_A : MATRIX_B;
}
int getBottomMatrix() {
return (getGravity() != 180) ? MATRIX_A : MATRIX_B;
}
void resetTime() {
for (byte i = 0; i < 2; i++) {
lc.clearDisplay(i);
}
fill(getTopMatrix(), 60);
d.Delay(getDelayDrop()*1000);
}
void displayTime(int seconds, int minutes){
tm.point(1);
tm.display(3, seconds % 10);
tm.display(2, seconds / 10 % 10);
tm.display(1, minutes % 10);
tm.display(0, minutes / 10 % 10);
}
/**
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, 7, 0) && !lc.getRawXY(MATRIX_B, 0, 7)) ||
(!lc.getRawXY(MATRIX_A, 7, 0) && lc.getRawXY(MATRIX_B, 0, 7))
) {
// for (byte d=0; d<8; d++) { lc.invertXY(0, 0, 7); delay(50); }
lc.invertRawXY(MATRIX_A, 7, 0);
lc.invertRawXY(MATRIX_B, 0, 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);
if (letter == 'M') {
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);
lc.setXY(matrix, 4, 2, true);
lc.setXY(matrix, 4, 3, true);
lc.setXY(matrix, 5, 3, true);
}
if (letter == 'S') {
lc.setXY(matrix, 1, 5, true);
lc.setXY(matrix, 2, 6, true);
lc.setXY(matrix, 3, 6, true);
lc.setXY(matrix, 4, 5, true);
lc.setXY(matrix, 3, 3, true);
lc.setXY(matrix, 4, 4, true);
lc.setXY(matrix, 3, 2, true);
lc.setXY(matrix, 4, 1, true);
lc.setXY(matrix, 5, 1, true);
lc.setXY(matrix, 6, 2, true);
}
}
void renderSetMinutes() {
fill(getTopMatrix(), delayMinutes);
displayTime(delaySeconds,delayMinutes);
displayLetter('M', getBottomMatrix());
}
void renderSetSeconds() {
fill(getTopMatrix(), delaySeconds);
displayTime(delaySeconds,delayMinutes);
displayLetter('S', getBottomMatrix());
}
void knobClockwise() {
Serial.println("Clockwise");
if (mode == MODE_SETSECONDS) {
delaySeconds = constrain(delaySeconds + 1, 0, 59);
renderSetSeconds();
} else if (mode == MODE_SETMINUTES) {
delayMinutes = constrain(delayMinutes + 1, 0, 60);
renderSetMinutes();
}
Serial.print("Delay: ");
Serial.println(getDelayDrop());
}
void knobCounterClockwise() {
Serial.println("Counterclockwise");
if (mode == MODE_SETSECONDS) {
delaySeconds = constrain(delaySeconds - 1, 0, 59);
renderSetSeconds();
} else if (mode == MODE_SETMINUTES) {
delayMinutes = constrain(delayMinutes - 1, 0, 60);
renderSetMinutes();
}
Serial.print("Delay: ");
Serial.println(getDelayDrop());
}
void boton1(unsigned long pulsacion){
if(pulsacion>50000){
mode = (mode + 1) % 3;
Serial.print("Switched mode to: ");
Serial.println(mode);
if (mode == MODE_SETMINUTES) {
lc.backup(); // we only need to back when switching from MODE_HOURGLASS->MODE_SETMINUTES
renderSetMinutes();
}
if (mode == MODE_SETSECONDS) {
renderSetSeconds();
}
if (mode == MODE_HOURGLASS) {
lc.clearDisplay(0);
lc.clearDisplay(1);
lc.restore();
resetTime();
}
}else{
if (pulsacion>=500){
if (mode != MODE_HOURGLASS){
knobCounterClockwise();
}
}
}
//}
}
void tiempo1(){
pulsacion = 0;
while(!digitalRead(PIN_BUTTON)){
pulsacion++;
}
boton1(pulsacion);
}
/**
Button callback (incl. software debouncer)
This switches between the modes (normal, set minutes, set hours)
*/
void buttonPush() {
tiempo1();
}
volatile unsigned long lastButton2PushMillis;
void button2Push() {
if ((long)(millis() - lastButton2PushMillis) >= DEBOUNCE_THRESHOLD) {
if (mode != MODE_HOURGLASS){
knobClockwise();
}
}
}
/**
Setup
*/
void setup() {
Serial.begin(9600);
// while (!Serial) {
// ; // wait for serial port to connect. Needed for native USB
// }
// setup rotary encoder
pinMode(PIN_BUTTON, INPUT);
pinMode(PIN_BUTTON2, INPUT);
attachInterrupt(digitalPinToInterrupt(PIN_BUTTON), buttonPush, LOW);
attachInterrupt(digitalPinToInterrupt(PIN_BUTTON2), button2Push, RISING);
tm.init();
// set brightness; 0-7
tm.set(2);
displayTime(delaySeconds,delayMinutes);
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();
//Serial.print(gravity);
lc.setRotation((ROTATION_OFFSET + gravity) % 360);
// handle special modes
if (mode == MODE_SETMINUTES) {
renderSetMinutes(); return;
} else if (mode == MODE_SETSECONDS) {
renderSetSeconds(); 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;
}
}