Last active
May 16, 2018 16:24
-
-
Save iUltimateLP/670db6be94028b69934c142abf0f2fe3 to your computer and use it in GitHub Desktop.
RGB LED Matrix + Raspberry Pi = Analog Clock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- | |
// Small example how to use the library. | |
// For more examples, look at demo-main.cc | |
// | |
// This code is public domain | |
// (but note, that the led-matrix library this depends on is GPL v2) | |
#include "led-matrix.h" | |
#include <unistd.h> | |
#include <math.h> | |
#include <stdio.h> | |
#include <signal.h> | |
#include <getopt.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <time.h> | |
using rgb_matrix::GPIO; | |
using rgb_matrix::RGBMatrix; | |
using rgb_matrix::Canvas; | |
// IntPoint data type | |
struct IntPoint | |
{ | |
int X; | |
int Y; | |
IntPoint(int InX, int InY) | |
{ | |
X = InX; | |
Y = InY; | |
} | |
}; | |
// Color data type | |
struct Color | |
{ | |
int R; | |
int G; | |
int B; | |
Color(int InR, int InG, int InB) | |
{ | |
R = InR; | |
G = InG; | |
B = InB; | |
} | |
}; | |
volatile bool interrupt_received = false; | |
static void InterruptHandler(int signo) { | |
interrupt_received = true; | |
} | |
/* | |
static int abs(int n) | |
{ | |
if (n >= 0) | |
return n; | |
else | |
return n * -1; | |
} | |
*/ | |
// Parse a string into a color | |
static bool parseColor(Color *color, const char *str) | |
{ | |
return sscanf(str, "%hhu,%hhu,%hhu", &color->R, &color->G, &color->B) == 3; | |
} | |
// Function do draw a circle with given radius and center point | |
static void DrawCircle(Canvas *canvas, float radius, IntPoint center, Color color) | |
{ | |
/*float angleStep = 1.0 / 360; | |
for (float a = 0, r = 0; r < radius; a+= angleStep, r+= angleStep) { | |
float dotX = cos(a * 2 * M_PI) * r; | |
float dotY = sin(a * 2 * M_PI) * r; | |
canvas->SetPixel(center.X + dotX, center.Y + dotY, color.R, color.G, color.B); | |
}*/ | |
for (int y = -radius; y <= radius; y++) | |
{ | |
for(int x = -radius; x <= radius; x++) | |
{ | |
if (x*x + y*y <= radius*radius + radius * 0.8) | |
{ | |
canvas->SetPixel(center.X + x, center.Y + y, color.R, color.G, color.B); | |
} | |
} | |
} | |
} | |
// Function to fill a specific area with color | |
static void FillArea(Canvas *canvas, IntPoint topLeft, IntPoint bottomRight, Color color) | |
{ | |
for (int x = topLeft.X; x <= bottomRight.X; x++) | |
{ | |
for (int y = topLeft.Y; y <= bottomRight.Y; y++) | |
{ | |
canvas->SetPixel(x, y, color.R, color.G, color.B); | |
} | |
} | |
} | |
// Function to draw a line from point A to point B | |
static void DrawLine(Canvas *canvas, IntPoint A, IntPoint B, Color color) | |
{ | |
int deltaX = B.X - A.X; | |
signed char const ix = (deltaX > 0) - (deltaX < 0); | |
deltaX = 2 * abs(deltaX); | |
int deltaY = B.Y - A.Y; | |
signed char const iy = (deltaY > 0) - (deltaY < 0); | |
deltaY = 2 * abs(deltaY); | |
int x1 = A.X; | |
int y1 = A.Y; | |
int x2 = B.X; | |
int y2 = B.Y; | |
canvas->SetPixel(x1, y1, color.R, color.G, color.B); | |
if (deltaX >= deltaY) | |
{ | |
int error = deltaY - (deltaX / 2); | |
while (x1 != x2) | |
{ | |
if ((error > 0) || (!error && (ix > 0))) | |
{ | |
error -= deltaX; | |
y1 += iy; | |
} | |
error += deltaY; | |
x1 += ix; | |
canvas->SetPixel(x1, y1, color.R, color.G, color.B); | |
} | |
} | |
else | |
{ | |
int error = deltaX - (deltaY / 2); | |
while (y1 != y2) | |
{ | |
if ((error > 0) || (!error && (iy > 0))) | |
{ | |
error -= deltaY; | |
x1 += ix; | |
} | |
error += deltaX; | |
y1 += iy; | |
canvas->SetPixel(x1, y1, color.R, color.G, color.B); | |
} | |
} | |
} | |
static IntPoint CalculatePointerEndPoint(float progress, int pointerLength, IntPoint origin) | |
{ | |
IntPoint result = IntPoint(0,0); | |
result.X = origin.X + pointerLength * cos((progress * 2 * M_PI) - M_PI / 2); | |
result.Y = origin.Y + pointerLength * sin((progress * 2 * M_PI) - M_PI / 2); | |
//printf("Progress: %f, Angle: %f, nPi: %f, res: %d,%d\n", progress, progress * 360.0, progress * 2 * M_PI, result.X, result.Y); | |
return result; | |
} | |
static void printUsage() | |
{ | |
printf("Invalid command line. Options:\n"); | |
printf("\t-o <r,g,b> : Clock circle and center point color. Default 255,0,0.\n"); | |
printf("\t-h <r,g,b> : Hour pointer color. Default 0,255,0.\n"); | |
printf("\t-m <r,g,b> : Minute pointer color. Default 0,255,0.\n"); | |
printf("\t-s <r,g,b> : Second pointer color. Default 0,0,255.\n"); | |
printf("\t-r <radius> : Clock radius. Default 25.\n"); | |
printf("\t-d <depth> : Outer circle depth. Default 2.\n"); | |
} | |
// Main function | |
int main(int argc, char *argv[]) | |
{ | |
// Set up display | |
RGBMatrix::Options defaults; | |
defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat" | |
defaults.rows = 64; | |
defaults.chain_length = 2; | |
defaults.parallel = 1; | |
defaults.show_refresh_rate = true; | |
Canvas *canvas = rgb_matrix::CreateMatrixFromFlags(&argc, &argv, &defaults); | |
if (canvas == NULL) | |
return 1; | |
// Adjustable settings | |
Color outlineColor = Color(255,0,0); | |
Color hourPointerColor = Color(0,255,0); | |
Color minPointerColor = Color(0,255,0); | |
Color secPointerColor = Color(0,0,255); | |
int clockRadius = 25; | |
int circleDepth = 2; | |
int opt; | |
while ((opt = getopt(argc, argv, "o:h:m:s:r:d:")) != -1) | |
{ | |
switch(opt) | |
{ | |
case 'o': | |
if (!parseColor(&outlineColor, optarg)) | |
{ | |
printf("Invalid outline color: %s\n", optarg); | |
printUsage(); | |
} | |
break; | |
case 'h': | |
if (!parseColor(&hourPointerColor, optarg)) | |
{ | |
printf("Invalid hour pointer color: %s\n", optarg); | |
printUsage(); | |
} | |
break; | |
case 'm': | |
if (!parseColor(&minPointerColor, optarg)) | |
{ | |
printf("Invalid min pointer color: %s\n", optarg); | |
printUsage(); | |
} | |
break; | |
case 's': | |
if (!parseColor(&secPointerColor, optarg)) | |
{ | |
printf("Invalid sec pointer color: %s\n", optarg); | |
printUsage(); | |
} | |
break; | |
case 'r': clockRadius = atoi(optarg); break; | |
case 'd': circleDepth = atoi(optarg); break; | |
default: | |
printUsage(); | |
break; | |
} | |
} | |
// Set up interrupt handlers | |
signal(SIGTERM, InterruptHandler); | |
signal(SIGINT, InterruptHandler); | |
IntPoint centerOfScreen = IntPoint(canvas->width() / 2, canvas->height() / 2); | |
// Draw clock circle | |
DrawCircle(canvas, clockRadius, centerOfScreen, outlineColor); | |
DrawCircle(canvas, clockRadius - circleDepth, centerOfScreen, Color(0,0,0)); | |
// Draw center clock point | |
FillArea(canvas, IntPoint(centerOfScreen.X - 1, centerOfScreen.Y - 1), IntPoint(centerOfScreen.X + 1, centerOfScreen.Y + 1), Color(255,0,0)); | |
struct timespec next_time; | |
next_time.tv_sec = time(NULL); | |
next_time.tv_nsec = 0; | |
struct tm tm; | |
IntPoint lastHourPointerEnd = IntPoint(0,0); | |
while (!interrupt_received) | |
{ | |
localtime_r(&next_time.tv_sec, &tm); | |
// Remove pointers from last tick | |
DrawLine(canvas, centerOfScreen, lastHourPointerEnd, Color(0,0,0)); | |
DrawLine(canvas, centerOfScreen, CalculatePointerEndPoint((tm.tm_min - 1) / 60.0, clockRadius - 5, centerOfScreen), Color(0,0,0)); | |
DrawLine(canvas, centerOfScreen, CalculatePointerEndPoint((tm.tm_sec - 1) / 60.0, clockRadius - 8, centerOfScreen), Color(0,0,0)); | |
// Draw new pointers | |
lastHourPointerEnd = CalculatePointerEndPoint((tm.tm_hour / 12.0) + (tm.tm_min / 600.0), clockRadius - 11, centerOfScreen); | |
DrawLine(canvas, centerOfScreen, lastHourPointerEnd, hourPointerColor); | |
DrawLine(canvas, centerOfScreen, CalculatePointerEndPoint(tm.tm_min / 60.0, clockRadius - 5, centerOfScreen), minPointerColor); | |
DrawLine(canvas, centerOfScreen, CalculatePointerEndPoint(tm.tm_sec / 60.0, clockRadius - 8, centerOfScreen), secPointerColor); | |
// Draw center point on top | |
FillArea(canvas, IntPoint(centerOfScreen.X - 1, centerOfScreen.Y - 1), IntPoint(centerOfScreen.X + 1, centerOfScreen.Y + 1), outlineColor); | |
// Sleep for a second | |
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next_time, NULL); | |
next_time.tv_sec += 1; | |
} | |
canvas->Clear(); | |
delete canvas; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment