Can someone help me with my eyeballs?

Hi, request for help. Can you please tell me what I'm doing wrong?

So I've got code that displays time on a led 16x8 LED matrix. It works fine (see code below).

What I'm trying to do is every 30 min have a set of eyeballs come on the led screen matrix (for 15 seconds let say), and then after the 15 seconds the time comes back up and runs until the next 30 min mark.

The eyeball code works fine separately on its own (see next comment).

I tried putting the eyeball code in a separate subroutine (called "eyeballs") and used a conditional statement to execute it, but it doesn't work. It seems my conditional statement is ignored.

Here's the conditional statement I tried.

void loop () {
DateTime now = rtc.now();

if(now.hour() == 23 && now.minutes() == 30){
eyeballs();

Is there something wrong with it? Will a conditional statement run a subroutine? It's reading the time correctly (e.g. instead of eyeballs, I did Serial.print "test" and it ran for a full min. But it's not executing the subroutine (or function I think is the proper term).


Here's the clock code:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#include "RTClib.h"

RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
int previousMinute = -1;
bool DEBUG = false;

Adafruit_8x16minimatrix matrix = Adafruit_8x16minimatrix();

//3x9 number digit font bitmaps
byte one[]= {B01000000,
B11000000,
B01000000,
B01000000,
B01000000,
B01000000,
B01000000,
B11100000};

byte two[]= {B11100000,
B00100000,
B00100000,
B11100000,
B10000000,
B10000000,
B10000000,
B11100000};

byte three[]= {B11100000,
B00100000,
B00100000,
B11100000,
B00100000,
B00100000,
B00100000,
B11100000};

byte four[]= {B10100000,
B10100000,
B10100000,
B11100000,
B00100000,
B00100000,
B00100000,
B00100000};

byte five[]= {B11100000,
B10000000,
B10000000,
B11100000,
B00100000,
B00100000,
B00100000,
B11100000};

byte six[]= {B11100000,
B10000000,
B10000000,
B11100000,
B10100000,
B10100000,
B10100000,
B11100000};

byte seven[]={B11100000,
B00100000,
B00100000,
B00100000,
B00100000,
B00100000,
B00100000,
B00100000};

byte eight[]={B11100000,
B10100000,
B10100000,
B11100000,
B10100000,
B10100000,
B10100000,
B10100000,
B10100000,
B11100000,
B00100000,
B00100000,
B00100000,
B00100000};

byte nine []={ B11100000,
B10100000,
B11100000,
B00100000,
B0100000,
B00100000,
B00100000,
B11100000};

byte zero[]={ B11100000,
B10100000,
B10100000,
B10100000,
B10100000,
B10100000,
B10100000,
B11100000};

char *noArray[] = {zero, one, two, three, four, five, six, seven, eight, nine};

void setup() {
Serial.begin(9600);
matrix.begin(0x70); // pass in the address
matrix.setRotation(1);
}

String convertIntTo2DigitString(int i) {
String s = String(i);
if (i < 10) {
s = '0'+s;
}
return s;
}

void loop(){

matrix.setBrightness(1);

DateTime now = rtc.now();

int currentMinute = now.minute();
int currentHour = now.hour();
int currentSecond = now.second();

if (DEBUG) {
currentHour = currentMinute;
currentMinute = now.second();
}

if (previousMinute != currentMinute) {
previousMinute = currentMinute;
String min = convertIntTo2DigitString(currentMinute);
String hour = convertIntTo2DigitString(currentHour);

matrix.clear();
matrix.drawBitmap(0, 0, (byte*)noArray[(int)hour[0]-'0'], 3, 9, LED_ON);
matrix.drawBitmap(4, 0, (byte*)noArray[(int)hour[1]-'0'], 3, 9, LED_ON);
matrix.drawBitmap(9, 0, (byte*)noArray[(int)min[0]-'0'], 3, 9, LED_ON);
matrix.drawBitmap(13,0, (byte*)noArray[(int)min[1]-'0'], 3, 9, LED_ON);
matrix.writeDisplay();
delay(200);

}
}

Here's the eyeball code (Called roboface by Philip Burgess).


// 'roboface' example sketch for Adafruit I2C 8x8 LED backpacks:
//
// Adafruit Mini 8x8 LED Matrix w/I2C Backpack - Red : ID 870 : $9.95 : Adafruit Industries, Unique & fun DIY electronics and kits Adafruit Small 1.2 8x8 LED Matrix w/I2C Backpack - Red : ID 1049 : $9.95 : Adafruit Industries, Unique & fun DIY electronics and kits
// Adafruit Mini 8x8 LED Matrix w/I2C Backpack - Yellow : ID 871 : $9.95 : Adafruit Industries, Unique & fun DIY electronics and kits Adafruit Small 1.2 8x8 LED Matrix w/I2C Backpack - Yellow : ID 1050 : $10.50 : Adafruit Industries, Unique & fun DIY electronics and kits
// Adafruit Mini 0.8 8x8 LED Matrix w/I2C Backpack - Yellow-Green : ID 872 : $9.95 : Adafruit Industries, Unique & fun DIY electronics and kits Adafruit Small 1.2 8x8 LED Matrix w/I2C Backpack - Yellow-Green : ID 1051 : $10.95 : Adafruit Industries, Unique & fun DIY electronics and kits
// Adafruit Mini 8x8 LED Matrix w/I2C Backpack - Blue : ID 959 : $11.95 : Adafruit Industries, Unique & fun DIY electronics and kits Adafruit Small 1.2 8x8 LED Matrix w/I2C Backpack - Blue : ID 1052 : $11.95 : Adafruit Industries, Unique & fun DIY electronics and kits
//
// Requires Adafruit_LEDBackpack and Adafruit_GFX libraries.
// For a simpler introduction, see the 'matrix8x8' example.
//
// This sketch demonstrates a couple of useful techniques:
// 1) Addressing multiple matrices (using the 'A0' and 'A1' solder
// pads on the back to select unique I2C addresses for each).
// 2) Displaying the same data on multiple matrices by sharing the
// same I2C address.
//
// This example uses 5 matrices at 4 addresses (two share an address)
// to animate a face:
//
// 0 0
//
// 1 2 3
//
// The 'eyes' both display the same image (always looking the same
// direction -- can't go cross-eyed) and thus share the same address
// (0x70). The three matrices forming the mouth have unique addresses
// (0x71, 0x72 and 0x73).
//
// The face animation as written is here semi-random; this neither
// generates nor responds to actual sound, it's simply a visual effect
// Consider this a stepping off point for your own project. Maybe you
// could 'puppet' the face using joysticks, or synchronize the lips to
// audio from a Wave Shield (see wavface example). Currently there are
// only six images for the mouth. This is often sufficient for simple
// animation, as explained here:
// idleworm / animation / tutorials
//
// Adafruit invests time and resources providing this open source code,
// please support Adafruit and open-source hardware by purchasing
// products from Adafruit!
//
// Written by P. Burgess for Adafruit Industries.
// BSD license, all text above must be included in any redistribution.

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"

//Adafruit_8x16minimatrix matrix = Adafruit_8x16minimatrix();

// Because the two eye matrices share the same address, only four
// matrix objects are needed for the five displays:
#define MATRIX_EYES 0

Adafruit_8x16minimatrix matrix_eyes;

// Rather than assigning matrix addresses sequentially in a loop, each
// has a spot in this array. This makes it easier if you inadvertently
// install one or more matrices in the wrong physical position --
// re-order the addresses in this table and you can still refer to
// matrices by index above, no other code or wiring needs to change.
//static const uint8_t matrixAddr[] = { 0x70, 0x71, 0x72, 0x73 };

static const uint8_t PROGMEM // Bitmaps are stored in program memory
blinkImg[][8] = { // Eye animation frames
{ B00111100, // Fully open eye
B01111110,
B11111111,
B11111111,
B11111111,
B11111111,
B01111110,
B00111100 },
{ B00000000,
B01111110,
B11111111,
B11111111,
B11111111,
B11111111,
B01111110,
B00111100 },
{ B00000000,
B00000000,
B00111100,
B11111111,
B11111111,
B11111111,
B00111100,
B00000000 },
{ B00000000,
B00000000,
B00000000,
B00111100,
B11111111,
B01111110,
B00011000,
B00000000 },
{ B00000000, // Fully closed eye
B00000000,
B00000000,
B00000000,
B10000001,
B01111110,
B00000000,
B00000000 } };

uint8_t
blinkIndex[] = { 1, 2, 3, 4, 3, 2, 1 }, // Blink bitmap sequence
blinkCountdown = 100, // Countdown to next blink (in frames)
gazeCountdown = 75, // Countdown to next eye movement
gazeFrames = 50; // Duration of eye movement (smaller = faster)

int8_t
eyeX = 3, eyeY = 3, // Current eye position
newX = 3, newY = 3, // Next eye position
dX = 0, dY = 0; // Distance from prior to new position

void setup() {

// Seed random number generator from an unused analog input:
randomSeed(analogRead(A0));

matrix_eyes.begin(0x70);

}

void loop() {

matrix_eyes.setBrightness(1);
matrix_eyes.setRotation(1);

// Draw eyeball in current state of blinkyness (no pupil). Note that
// only one eye needs to be drawn. Because the two eye matrices share
// the same address, the same data will be received by both.
matrix_eyes.clear();
// When counting down to the next blink, show the eye in the fully-
// open state. On the last few counts (during the blink), look up
// the corresponding bitmap index.

//below is the first eyeball

matrix_eyes.drawBitmap(0, 0,
blinkImg[
(blinkCountdown < sizeof(blinkIndex)) ? // Currently blinking?
blinkIndex[blinkCountdown] : // Yes, look up bitmap #
0 // No, show bitmap 0
], 8, 8, LED_ON);

//below is the 2nd eyeball

matrix_eyes.drawBitmap(8, 0,
blinkImg[
(blinkCountdown < sizeof(blinkIndex)) ? // Currently blinking?
blinkIndex[blinkCountdown] : // Yes, look up bitmap #
0 // No, show bitmap 0
], 8, 8, LED_ON);

// Decrement blink counter. At end, set random time for next blink.
if(--blinkCountdown == 0) blinkCountdown = random(5, 180);

// Add a pupil (2x2 black square) atop the blinky eyeball bitmap.
// Periodically, the pupil moves to a new position...
if(--gazeCountdown <= gazeFrames) {
// Eyes are in motion - draw pupil at interim position
matrix_eyes.fillRect(
newX - (dX * gazeCountdown / gazeFrames),
newY - (dY * gazeCountdown / gazeFrames),
0, 2, LED_OFF);
}

if(gazeCountdown == 0) { // Last frame?
eyeX = newX; eyeY = newY; // Yes. What's new is old, then...
do { // Pick random positions until one is within the eye circle
newX = random(7); newY = random(7);
dX = newX - 3; dY = newY - 3;
} while((dX * dX + dY * dY) >= 10); // Thank you Pythagoras
dX = newX - eyeX; // Horizontal distance to move
dY = newY - eyeY; // Vertical distance to move
gazeFrames = random(3, 15); // Duration of eye movement
gazeCountdown = random(gazeFrames, 120); // Count to end of next movement

} else {
// Not in motion yet -- draw pupil at current static position

//first pupil below
matrix_eyes.fillRect(eyeY, eyeX, 2, 2, LED_OFF);

//second pupil of second eyeball below
matrix_eyes.fillRect(eyeY+8, eyeX, 2, 2, LED_OFF);

}

// Refresh all of the matrices in one quick pass
for(uint8_t i=0; i<2; i++) matrix_eyes.writeDisplay();

delay(20); // ~50 FPS

}

So the condition gets tested and return true, and does the Serial.print(), please if you can clean your code up (remove un-needed empty space, ctrl-T indent etc. and post the current (not-)working version including the eyeball function and the Serial.print.. within code tags please.

You didn't define eyeballs() but I'm guessing that the merged version of your code just renamed loop() from the roboeyes sketch.

That function expects to loop. It draws one frame on the screen, delays 20ms and then exits, expecting to be called again immediately to draw the next frame. But your code only called it once.

So you can add a little code around this to call it repeatedly. First examine the code in the Arduino example BlinkWithoutDelay. Modify that to call eyeballs() once every 20ms. Delete the delay(20) as it's no longer necessary.

Now you need to only call the eyeballs() function when it's necessary. To do this, first identify when the minute flipped over to 30 or 0, by looking at when it is different to what you last saw. When that happens, set a global variable like showEyeballs to true. If that is true and more than 20ms has passed since you last called eyeballs(), call eyeballs().

The tricky part is setting showEyeballs back to false once the desired animation is finished. You will have to understand the code and find the place where it detects that the animation is finished.

Wouldn't you want that if statement to say

if(now.minutes == 0 || now.minutes == 30)

?

The way you have it in the OP it will only execute the code at 11.30PM. You said you wanted it to happen every half hour.