/** @file main.c * @brief This is the main exectution program */ /*************************************************************************************************** * Includes **************************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #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); } }