372 lines
9.9 KiB
C
372 lines
9.9 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 "drivers/selector/rpi_selector.h"
|
|
|
|
#include "tasks/artnet/rpi_artnet.h"
|
|
#include "tasks/cava/rpi_cava.h"
|
|
#include "tasks/websocket/rpi_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();
|
|
selector_setup();
|
|
leddriver_setup();
|
|
websocket_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 = selector_get_position();
|
|
}
|
|
/* 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 args = 0;
|
|
|
|
while (argc > ++args) // Process command-line args
|
|
{
|
|
if (argv[args][0] == '-') {
|
|
switch (toupper(argv[args][1])) {
|
|
case 'N': // -N: number of LEDs per channel
|
|
if (args >= argc - 1) {
|
|
log_error("no numeric value");
|
|
exit(-EXIT_FAILURE);
|
|
} else {
|
|
param_access->pixled.chanLedCount = atoi(argv[++args]);
|
|
}
|
|
break;
|
|
case 'D': // -D: debug level
|
|
if (args >= argc - 1) {
|
|
log_error("no debug level");
|
|
exit(-EXIT_FAILURE);
|
|
} else {
|
|
logLevel = atoi(argv[++args]);
|
|
log_set_level(logLevel);
|
|
}
|
|
break;
|
|
case 'T': // -T: test mode
|
|
IsTestMode = true;
|
|
break;
|
|
default: // Otherwise error
|
|
log_error("Unrecognised option '%c'\n", argv[args][1]);
|
|
fprintf(stderr, "Options:\n"
|
|
" -t Test mode (flash LEDs)\n"
|
|
" -n num number of LEDs per channel\n"
|
|
" -d lvl debug level\n");
|
|
exit(-EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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_and_color(buffer, CAVA_BAR_NUMBER);
|
|
break;
|
|
case 1:
|
|
bounce_led(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);
|
|
}
|
|
}
|