380 lines
10 KiB
C

/** @file main.c
* @brief This is the main exectution program
*/
/***************************************************************************************************
* Includes
**************************************************************************************************/
#include <ctype.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "log.h"
#include "drivers/leddriver/rpi_leddriver.h"
#include "artnet.h"
#include "cava.h"
#include "selector.h"
#include "websocket.h"
#include "rpi_midi_controller.h"
#include "rpi_param.h"
#include "rpi_pattern.h"
/***************************************************************************************************
* Preprocessor Constants and Macros
**************************************************************************************************/
/***************************************************************************************************
* Type and Contant Definitions
**************************************************************************************************/
/***************************************************************************************************
* Persistent Variables
**************************************************************************************************/
/* Command-line parameters */
bool IsTestMode = false;
int logLevel = 2;
int previousMode = -1;
unsigned long mainLoopCycle = 0;
pthread_mutex_t logLockMutex = PTHREAD_MUTEX_INITIALIZER;
/***************************************************************************************************
* Internal Function Prototypes
**************************************************************************************************/
void parseCommandLineArgs(int argc, char const *argv[]);
void terminate(int sig);
void manage_tasks(int previousMode, int currentMode);
void execute_task(int mode);
void execute_test_mode();
void execute_artnet_mode();
void execute_autonomous_mode();
void execute_manual_mode();
void adjust_loop(struct timespec const *loopStart);
void log_lock_helper(bool lock, void *udata);
/***************************************************************************************************
* External Function Definitions
**************************************************************************************************/
int main(int argc, char const *argv[]) {
// setup
signal(SIGINT, terminate);
struct sched_param sp;
sp.sched_priority = 32;
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) {
fprintf(stderr, "WARNING: Failed to set stepper thread to real-time priority: %s\n",
strerror(errno));
}
log_set_lock(log_lock_helper, &logLockMutex);
param_setup();
midi_controller_setup();
leddriver_setup();
websocket_start();
selector_start();
parseCommandLineArgs(argc, argv);
// loop
while (1) {
struct timespec loopStart;
int mode;
clock_gettime(CLOCK_MONOTONIC, &loopStart);
if (IsTestMode) {
if (mainLoopCycle % (180 * 3) < 180) {
mode = 0;
} else if (mainLoopCycle % (180 * 3) < 180 * 2) {
mode = 1;
} else {
mode = 2;
}
} else {
mode = param_access->pixled.mode;
}
/* todo thread ? */
midi_controller_execute();
if (mode != -1) {
if (mode != previousMode) {
log_info("swtching to mode : %d", mode);
manage_tasks(previousMode, mode);
} else {
execute_task(mode);
}
previousMode = mode;
}
adjust_loop(&loopStart);
}
}
/***************************************************************************************************
* Internal Function Definitions
**************************************************************************************************/
void parseCommandLineArgs(int argc, char const *argv[]) {
int argIt = 0;
while (argc > ++argIt) // Process command-line args
{
if (argv[argIt][0] == '-' && argv[argIt][2] == '\0') {
switch (toupper(argv[argIt][1])) {
case 'N': // -N: number of LEDs per channel
if (argIt >= argc - 1) {
log_error("no numeric value");
exit(-EXIT_FAILURE);
} else {
int ledCount = atoi(argv[++argIt]);
param_access->pixled.chanLedCount = ledCount <= CHAN_MAXLEDS ? ledCount : CHAN_MAXLEDS;
}
break;
case 'D': // -D: debug level
if (argIt >= argc - 1) {
log_error("no debug level");
exit(-EXIT_FAILURE);
} else {
logLevel = atoi(argv[++argIt]);
log_set_level(logLevel);
}
break;
case 'T': // -T: test mode
IsTestMode = true;
break;
default: // Otherwise error
log_error("Unknown option '%c'\n", argv[argIt][1]);
fprintf(stderr,
"Options:\n"
" -t Test mode (flash LEDs)\n"
" -n num number of LEDs per channel (max : %d)\n"
" -d lvl debug level\n",
CHAN_MAXLEDS);
exit(-EXIT_FAILURE);
}
} else {
log_warn("Unknown option '%s'\n", argv[argIt]);
}
}
}
void terminate(int sig) {
manage_tasks(previousMode, -1);
leddriver_close();
log_info("Goodbye !");
exit(EXIT_SUCCESS);
}
void manage_tasks(int previousMode, int currentMode) {
/* stop previous bg task */
switch (previousMode) {
case 1:
artnet_stop();
break;
case 2:
case 3:
cava_stop();
break;
default:
break;
}
/* start new bg task */
switch (currentMode) {
case 1:
artnet_start();
break;
case 2:
case 3:
cava_start();
break;
default:
break;
}
}
void execute_task(int mode) {
switch (mode) {
case 0:
// mode test
execute_test_mode();
break;
case 1:
// artnet mode
execute_artnet_mode();
break;
case 2:
execute_autonomous_mode();
break;
case 3:
// manual mode
execute_manual_mode();
break;
default:
break;
}
}
// Pointer to uncached Tx data buffer
// TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data
void execute_test_mode() {
// RGB values for test mode (1 value for each of 16 channels)
uint32_t on_rgbs[] = {0x5f0000, 0x005f00, 0x00005f, 0x5f5f00, 0x5f005f, 0x005f5f, 0x5f5f5f};
uint32_t off_rgbs = 0x000000;
static int i = 0, offset = 0;
for (size_t ledIndex = 0; ledIndex < param_access->pixled.chanLedCount; ++ledIndex) {
set_color(ledIndex <= offset % param_access->pixled.chanLedCount ? on_rgbs[i] * .5 : off_rgbs,
ledIndex);
}
leddriver_refresh();
if (offset < param_access->pixled.chanLedCount) {
++offset;
} else {
offset = 0;
if (i < 7) {
++i;
} else {
i = 0;
}
}
}
// RGB data
int rgb_data[CHAN_MAXLEDS][LED_NCHANS];
void execute_artnet_mode() {
uint8_t *dmxData;
for (size_t ledBar = 0; ledBar < LED_NCHANS; ledBar++) {
if (artnet_get_dmx_data(ledBar, &dmxData) == 0) {
for (size_t i = 0; i < param_access->pixled.chanLedCount; ++i) {
uint8_t *rgb = dmxData + (i * 3);
rgb_data[i][ledBar] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
rgb_txdata(rgb_data[i], i);
}
}
}
leddriver_refresh();
}
void execute_autonomous_mode() {
int ret;
uint16_t *buffer;
if ((ret = cava_get_buffer(&buffer)) == 0) {
switch (param_access->auton.pattern) {
case 0:
bounce_led(buffer, CAVA_BAR_NUMBER);
break;
case 1:
bounce_led_and_color(buffer, CAVA_BAR_NUMBER);
break;
case 2:
bounce_led_and_travel(buffer, CAVA_BAR_NUMBER);
break;
default:
break;
}
}
}
unsigned baseLed[LED_NCHANS] = {0};
void execute_manual_mode() {
int ret;
uint16_t *buffer;
if ((ret = cava_get_buffer(&buffer)) == 0) {
for (size_t ledBarIndex = 0; ledBarIndex < LED_NCHANS; ++ledBarIndex) {
uint16_t barMax = 0;
// uint16_t hueInterval = UINT16_MAX - (param_access->ledbar[ledBarIndex].hueInterval -
// param_access->ledbar[ledBarIndex].hueBase);
for (size_t cavaBar = 0; cavaBar < CAVA_BAR_NUMBER / LED_NCHANS; ++cavaBar) {
unsigned barIndex = ledBarIndex * CAVA_BAR_NUMBER / LED_NCHANS + cavaBar;
if (barMax < buffer[barIndex]) {
barMax = buffer[barIndex];
}
}
barMax *= param_access->auton.sensitivity * param_access->ledbar[ledBarIndex].sensitivity;
unsigned ledToLight = barMax * param_access->pixled.chanLedCount / 2 / UINT16_MAX;
unsigned long hueShift =
(long)barMax * (long)param_access->ledbar[ledBarIndex].hueInterval / UINT16_MAX;
uint16_t hue = param_access->ledbar[ledBarIndex].hueBase - hueShift;
uint32_t color = ColorHSV(hue, 255, param_access->ledbar[ledBarIndex].luminosity);
for (size_t i = 0; i < ledToLight; ++i) {
rgb_data[i][ledBarIndex] = color;
rgb_txdata(rgb_data[i], i);
}
for (size_t i = ledToLight; i < param_access->pixled.chanLedCount; ++i) {
rgb_data[i][ledBarIndex] = 0x000000;
rgb_txdata(rgb_data[i], i);
}
}
leddriver_refresh();
}
}
void adjust_loop(struct timespec const *loopStart) {
struct timespec loopEnd;
long elapsedTimeUs, remainingTimeUs;
clock_gettime(CLOCK_MONOTONIC, &loopEnd);
elapsedTimeUs = (loopEnd.tv_nsec - loopStart->tv_nsec) / 1E3 +
((unsigned)(loopEnd.tv_sec - loopStart->tv_sec)) * 1E6;
remainingTimeUs = 16000 - elapsedTimeUs;
if (remainingTimeUs >= 0) {
log_trace("cycle %lu, loop remaining time %ld", mainLoopCycle, remainingTimeUs);
usleep(remainingTimeUs);
} else {
log_warn("loop overlap by %06ldus", -remainingTimeUs);
log_info("loop start %ld.%09lds - loop end %ld.%09lds", loopStart->tv_sec, loopStart->tv_nsec,
loopEnd.tv_sec, loopEnd.tv_nsec);
}
++mainLoopCycle;
}
void log_lock_helper(bool lock, void *udata) {
pthread_mutex_t *pLogLockMutex = (pthread_mutex_t *)(udata);
if (lock) {
pthread_mutex_lock(pLogLockMutex);
} else {
pthread_mutex_unlock(pLogLockMutex);
}
}