Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
RGB LED Matrix + Raspberry Pi = Analog Clock
// -*- 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