diff --git a/RpiLedBars/Makefile b/RpiLedBars/Makefile index 15824dd..f5b09f3 100644 --- a/RpiLedBars/Makefile +++ b/RpiLedBars/Makefile @@ -1,28 +1,34 @@ CC := gcc -CFLAGS := -Wall -g -D_DEBUG -LDFLAGS := -lwiringPi -lasound -lfftw3 -lpthread -lm +CFLAGS := -Wall -g -Ilibs/log.c/src +LDFLAGS := -lwiringPi -lasound -lfftw3 -lpthread -lm -L.libs SRC := src OBJ := obj BIN := bin/pixled -SOURCES := $(shell find $(SRC) -type f -name "*.c") +SOURCES := $(shell find $(SRC) -type f -name "*.c") OBJECTS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES)) - +.PHONY: all clean debug all: $(BIN) +libs/log.c/obj/log.o: libs/log.c/src/log.c + if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi + $(CC) -c $< -o $@ -DLOG_USE_COLOR + +.libs/log.la: libs/log.c/obj/log.o + if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi + ar r $@ $^ + ranlib $@ + $(OBJ)/%.o: $(SRC)/%.c if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi - $(CC) -I$(SRC) -c $< -o $@ $(CFLAGS) + $(CC) -c $< -o $@ $(CFLAGS) -$(BIN) : $(OBJECTS) - if [ ! -d "$(dir $(BIN))" ]; then mkdir -p "$(dir $(BIN))"; fi +$(BIN): $(OBJECTS) .libs/log.la + if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi $(CC) -o $@ $^ $(LDFLAGS) -liblog.a: log.o - ar $(ARFLAGS) $@ $^ - clean: - rm -rf $(OBJ) \ No newline at end of file + rm -rf $(OBJ) .libs libs/*/obj \ No newline at end of file diff --git a/RpiLedBars/res/install_i2s_mems_mic b/RpiLedBars/res/install_i2s_mems_mic index 7b9e69e..f1b2658 100644 --- a/RpiLedBars/res/install_i2s_mems_mic +++ b/RpiLedBars/res/install_i2s_mems_mic @@ -1,6 +1,7 @@ https://makersportal.com/blog/recording-stereo-audio-on-a-raspberry-pi https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/raspberry-pi-wiring-test +sudo apt install python3-pip sudo pip3 install --upgrade adafruit-python-shell cd /tmp sudo wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/i2smic.py @@ -15,4 +16,14 @@ arecord -D plughw:1 -c1 -r 48000 -f S32_LE -t wav -V mono -v file.wav cp res/.asoundrc ~/.asoundrc # alsa API -http://www.equalarea.com/paul/alsa-audio.html \ No newline at end of file +http://www.equalarea.com/paul/alsa-audio.html + +# for project +sudo apt install libasound2-dev wiringpi + +# for cava +sudo apt install libfftw3-dev libasound2-dev libtool automake +./autogen.sh +./configure +make +sudo make install \ No newline at end of file diff --git a/RpiLedBars/src/drivers/common.c b/RpiLedBars/src/drivers/common.c index 32611f6..612095f 100644 --- a/RpiLedBars/src/drivers/common.c +++ b/RpiLedBars/src/drivers/common.c @@ -3,9 +3,12 @@ #include #include #include +#include #include #include +#include "log.h" + // Use mmap to obtain virtual address, given physical void *map_periph(MEM_MAP *mp, void *phys, int size) { mp->phys = phys; @@ -30,20 +33,24 @@ void *map_segment(void *addr, int size) { void *mem; size = PAGE_ROUNDUP(size); - if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) - fprintf(stderr, "Error: can't open /dev/mem, run using sudo\n"); + if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) { + log_fatal("can't open /dev/mem, run using sudo"); + exit(-EXIT_FAILURE); + } + mem = mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, (uint32_t)addr); close(fd); -#if DEBUG - printf("Map %p -> %p\n", (void *)addr, mem); -#endif - if (mem == MAP_FAILED) - fprintf(stderr, "Error: can't map memory\n"); + log_info("Map %p -> %p", (void *)addr, mem); + if (mem == MAP_FAILED) { + log_fatal("can't map memory"); + exit(-EXIT_FAILURE); + } return (mem); } // Free mapped memory void unmap_segment(void *mem, int size) { - if (mem) + if (mem) { munmap(mem, PAGE_ROUNDUP(size)); + } } \ No newline at end of file diff --git a/RpiLedBars/src/drivers/dma/rpi_videocore.c b/RpiLedBars/src/drivers/dma/rpi_videocore.c index 1cd66a0..81fc9ed 100644 --- a/RpiLedBars/src/drivers/dma/rpi_videocore.c +++ b/RpiLedBars/src/drivers/dma/rpi_videocore.c @@ -5,6 +5,8 @@ #include #include +#include "log.h" + // Mailbox command/response structure typedef struct { uint32_t len, // Overall length (bytes) @@ -42,7 +44,7 @@ void *map_uncached_mem(MEM_MAP *mp, int size) { (mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0 ? mp->virt : 0; - printf("VC mem handle %u, phys %p, virt %p\n", mp->h, mp->bus, mp->virt); + log_info("VC mem handle %u, phys %p, virt %p", mp->h, mp->bus, mp->virt); return (ret); } @@ -93,7 +95,7 @@ int open_mbox(void) { int fd; if ((fd = open("/dev/vcio", 0)) < 0) - fprintf(stderr, "Error: can't open VC mailbox\n"); + log_error("can't open VC mailbox"); return (fd); } // Close mailbox interface @@ -110,14 +112,15 @@ uint32_t msg_mbox(int fd, VC_MSG *msgp) { msgp->uints[i++] = 0; msgp->len = (msgp->blen + 6) * 4; msgp->req = 0; - if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0) - printf("VC IOCTL failed\n"); - else if ((msgp->req & 0x80000000) == 0) - printf("VC IOCTL error\n"); - else if (msgp->req == 0x80000001) - printf("VC IOCTL partial error\n"); - else + if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0) { + log_error("VC IOCTL failed"); + } else if ((msgp->req & 0x80000000) == 0) { + log_error("VC IOCTL error"); + } else if (msgp->req == 0x80000001) { + log_error("VC IOCTL partial error"); + } else { ret = msgp->uints[0]; + } #if DEBUG disp_vc_msg(msgp); #endif diff --git a/RpiLedBars/src/drivers/leddriver/rpi_leddriver.c b/RpiLedBars/src/drivers/leddriver/rpi_leddriver.c index 1ad781e..6b4743e 100644 --- a/RpiLedBars/src/drivers/leddriver/rpi_leddriver.c +++ b/RpiLedBars/src/drivers/leddriver/rpi_leddriver.c @@ -4,6 +4,7 @@ #include #include +#include "../../rpi_param.h" #include "../common.h" #include "../dma/rpi_dma.h" @@ -49,13 +50,12 @@ TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; void swap_bytes(); void leddriver_setup() { - gpio_setup(); videocore_setup(&vc_mem, VC_MEM_SIZE); + gpio_setup(); smi_setup(LED_NCHANS, SMI_TIMING, &vc_mem, TX_BUFF_LEN(CHAN_MAXLEDS), &txdata); } void leddriver_close() { - printf("Closing\n"); videocore_close(&vc_mem); smi_close(LED_NCHANS); gpio_close(); @@ -129,4 +129,82 @@ void swap_bytes() { *wp = __builtin_bswap16(*wp); wp++; } -} \ No newline at end of file +} + +/* source : + * https://github.com/adafruit/Adafruit_NeoPixel/blob/216ccdbff399750f5b02d4cc804c598399e39713/Adafruit_NeoPixel.cpp#L2414 + */ +uint32_t ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { + + uint8_t r, g, b; + + // Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover; + // 0 is not the start of pure red, but the midpoint...a few values above + // zero and a few below 65536 all yield pure red (similarly, 32768 is the + // midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values + // each for red, green, blue) really only allows for 1530 distinct hues + // (not 1536, more on that below), but the full unsigned 16-bit type was + // chosen for hue so that one's code can easily handle a contiguous color + // wheel by allowing hue to roll over in either direction. + hue = (hue * 1530L + 32768) / 65536; + // Because red is centered on the rollover point (the +32768 above, + // essentially a fixed-point +0.5), the above actually yields 0 to 1530, + // where 0 and 1530 would yield the same thing. Rather than apply a + // costly modulo operator, 1530 is handled as a special case below. + + // So you'd think that the color "hexcone" (the thing that ramps from + // pure red, to pure yellow, to pure green and so forth back to red, + // yielding six slices), and with each color component having 256 + // possible values (0-255), might have 1536 possible items (6*256), + // but in reality there's 1530. This is because the last element in + // each 256-element slice is equal to the first element of the next + // slice, and keeping those in there this would create small + // discontinuities in the color wheel. So the last element of each + // slice is dropped...we regard only elements 0-254, with item 255 + // being picked up as element 0 of the next slice. Like this: + // Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0 + // Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0 + // Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254 + // and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why + // the constants below are not the multiples of 256 you might expect. + + // Convert hue to R,G,B (nested ifs faster than divide+mod+switch): + if (hue < 510) { // Red to Green-1 + b = 0; + if (hue < 255) { // Red to Yellow-1 + r = 255; + g = hue; // g = 0 to 254 + } else { // Yellow to Green-1 + r = 510 - hue; // r = 255 to 1 + g = 255; + } + } else if (hue < 1020) { // Green to Blue-1 + r = 0; + if (hue < 765) { // Green to Cyan-1 + g = 255; + b = hue - 510; // b = 0 to 254 + } else { // Cyan to Blue-1 + g = 1020 - hue; // g = 255 to 1 + b = 255; + } + } else if (hue < 1530) { // Blue to Red-1 + g = 0; + if (hue < 1275) { // Blue to Magenta-1 + r = hue - 1020; // r = 0 to 254 + b = 255; + } else { // Magenta to Red-1 + r = 255; + b = 1530 - hue; // b = 255 to 1 + } + } else { // Last 0.5 Red (quicker than % operator) + r = 255; + g = b = 0; + } + + // Apply saturation and value to R,G,B, pack into 32-bit result: + uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255 + uint16_t s1 = 1 + sat; // 1 to 256; same reason + uint8_t s2 = 255 - sat; // 255 to 0 + return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) | + (((((g * s1) >> 8) + s2) * v1) & 0xff00) | (((((b * s1) >> 8) + s2) * v1) >> 8); +} diff --git a/RpiLedBars/src/drivers/leddriver/rpi_leddriver.h b/RpiLedBars/src/drivers/leddriver/rpi_leddriver.h index 68e7a19..62dc410 100644 --- a/RpiLedBars/src/drivers/leddriver/rpi_leddriver.h +++ b/RpiLedBars/src/drivers/leddriver/rpi_leddriver.h @@ -3,9 +3,6 @@ #include -#define CHAN_MAXLEDS 6 * 60 // Maximum number of LEDs per channel -#define LED_NCHANS 8 // Number of LED channels (8 or 16) - void leddriver_setup(); void leddriver_close(); diff --git a/RpiLedBars/src/drivers/selector/rpi_selector.c b/RpiLedBars/src/drivers/selector/rpi_selector.c index d2ff7a4..24f9378 100644 --- a/RpiLedBars/src/drivers/selector/rpi_selector.c +++ b/RpiLedBars/src/drivers/selector/rpi_selector.c @@ -5,6 +5,8 @@ /*************************************************************************************************** * Includes **************************************************************************************************/ +#include "rpi_selector.h" + #include #include #include @@ -19,6 +21,7 @@ unsigned const selectorPinNumber = 4; /* TODO use GPIO function from ../gpio/gipo.h (same pin number) */ int selectorPins[4] = {5, 6, 26, 27}; +char modeStr[] = "0 | 0 | 0 | 0 \n"; /*************************************************************************************************** * Persistent Variables @@ -49,12 +52,11 @@ int selector_get_position() { return -1; } -void selector_print() { - char modeStr[] = "0 | 0 | 0 | 0 \n"; +char *selector_tostr() { for (size_t i = 0; i < selectorPinNumber; ++i) { modeStr[i * 4] = digitalRead(selectorPins[i]) ? '1' : '0'; } - fprintf(stderr, modeStr); + return modeStr; } /*************************************************************************************************** diff --git a/RpiLedBars/src/drivers/selector/rpi_selector.h b/RpiLedBars/src/drivers/selector/rpi_selector.h index a2718ec..5dd0150 100644 --- a/RpiLedBars/src/drivers/selector/rpi_selector.h +++ b/RpiLedBars/src/drivers/selector/rpi_selector.h @@ -33,8 +33,8 @@ void selector_setup(); int selector_get_position(); /** - * \brief debug: print the selector position + * \brief Get string with selector debug info **/ -void selector_print(); +char *selector_tostr(); #endif /* __RPI_SELECTOR_H__ */ \ No newline at end of file diff --git a/RpiLedBars/src/main.c b/RpiLedBars/src/main.c index d49b82c..8c33bf9 100644 --- a/RpiLedBars/src/main.c +++ b/RpiLedBars/src/main.c @@ -1,4 +1,12 @@ +/** \file main.c + * \brief This is the main exectution program + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ #include +#include #include #include #include @@ -8,23 +16,51 @@ #include #include +#include "log.h" + #include "drivers/leddriver/rpi_leddriver.h" #include "drivers/selector/rpi_selector.h" -#include "rpi_midi_controller.h" #include "tasks/artnet/rpi_artnet.h" #include "tasks/cava/rpi_cava.h" +#include "rpi_midi_controller.h" +#include "rpi_param.h" + +/*************************************************************************************************** + * Preprocessor Constants and Macros + **************************************************************************************************/ + +/*************************************************************************************************** + * Type and Contant Definitions + **************************************************************************************************/ + +/*************************************************************************************************** + * Persistent Variables + **************************************************************************************************/ /* Command-line parameters */ bool IsTestMode = false; int chanLedCount = 0; +int logLevel = 2; -pthread_t *bgTasks[4] = {NULL}; +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(); @@ -35,86 +71,59 @@ void execute_manual_mode(); void adjust_loop(struct timespec const *loopStart); -int main(int argc, char const *argv[]) { - int previousMode = -1; +void log_lock_helper(bool lock, void *udata); +/*************************************************************************************************** + * External Function Definitions + **************************************************************************************************/ + +int main(int argc, char const *argv[]) { // setup parseCommandLineArgs(argc, argv); + log_set_level(logLevel); 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\n"); - } + // 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(); - artnet_init(); - setup_cava(); + // setup_cava(); // loop while (1) { struct timespec loopStart; + int mode; + clock_gettime(CLOCK_MONOTONIC, &loopStart); - int mode = selector_get_position(); + if (IsTestMode) { + if (mainLoopCycle % 300 < 150) { + mode = 2; + } else { + mode = 1; + } + } else { + mode = selector_get_position(); + } + /* todo thread ? */ midi_controller_execute(); if (mode != -1) { if (mode != previousMode) { -#if defined(_DEBUG) - printf("swtching to mode : %d\n", mode); -#endif - /* stop previous bg task */ - switch (previousMode) { - case 1: - artnet_stop(); - break; - case 2: - stop_cava_bg_worker(); - break; - default: - break; - } - - /* start new bg task */ - switch (mode) { - case 1: - artnet_start(); - break; - case 2: - start_cava_bg_worker(); - break; - default: - break; - } + log_info("swtching to mode : %d", mode); + manage_tasks(previousMode, mode); } else { - 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: - if (mode != previousMode) { - fprintf(stderr, "Error in selector\n"); - } - break; - } + execute_task(mode); } previousMode = mode; } @@ -122,6 +131,10 @@ int main(int argc, char const *argv[]) { } } +/*************************************************************************************************** + * Internal Function Definitions + **************************************************************************************************/ + void parseCommandLineArgs(int argc, char const *argv[]) { int args = 0; @@ -130,28 +143,90 @@ void parseCommandLineArgs(int argc, char const *argv[]) { if (argv[args][0] == '-') { switch (toupper(argv[args][1])) { case 'N': // -N: number of LEDs per channel - if (args >= argc - 1) - fprintf(stderr, "Error: no numeric value\n"); - else + if (args >= argc - 1) { + log_error("no numeric value"); + exit(-EXIT_FAILURE); + } else { 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]); + } break; case 'T': // -T: test mode IsTestMode = true; break; default: // Otherwise error - printf("Unrecognised option '%c'\n", argv[args][1]); - printf("Options:\n" - " -n num number of LEDs per channel\n" - " -t Test mode (flash LEDs)\n"); - exit(1); + 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(); - exit(0); + 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: + cava_stop(); + break; + default: + break; + } + + /* start new bg task */ + switch (currentMode) { + case 1: + artnet_start(); + break; + case 2: + 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 @@ -159,13 +234,13 @@ void terminate(int sig) { void execute_test_mode() { // RGB values for test mode (1 value for each of 16 channels) - uint32_t on_rgbs[] = {0xef0000, 0x00ef00, 0x0000ef, 0xefef00, 0xef00ef, 0x00efef, 0xefefef}; + 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 < chanLedCount; ++ledIndex) { - set_color(ledIndex <= offset % chanLedCount ? on_rgbs[i] : off_rgbs, ledIndex); + set_color(ledIndex <= offset % chanLedCount ? on_rgbs[i] * .5 : off_rgbs, ledIndex); } leddriver_refresh(); @@ -205,7 +280,7 @@ void execute_autonomous_mode() { int ret; uint16_t *buffer; - if ((ret = get_cava_buffer(&buffer)) == 0) { + if ((ret = cava_get_buffer(&buffer)) == 0) { for (size_t ledBarIndex = 0; ledBarIndex < 4; ++ledBarIndex) { uint16_t barMax = 0; for (size_t bar = 0; bar < 20 / 4; ++bar) { @@ -230,7 +305,15 @@ void execute_autonomous_mode() { } } -void execute_manual_mode() {} +void execute_manual_mode() { + for (size_t ledBarIndex = 0; ledBarIndex < LED_NCHANS; ++ledBarIndex) { + for (size_t i = 0; i < 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; @@ -242,11 +325,21 @@ void adjust_loop(struct timespec const *loopStart) { remainingTimeUs = 16000 - elapsedTimeUs; if (remainingTimeUs >= 0) { - // printf("loop remaining time %ld\n", remainingTimeUs); + log_trace("cycle %lu, loop remaining time %ld", mainLoopCycle, remainingTimeUs); usleep(remainingTimeUs); } else { - printf("loop overlap by %06ldus\n", -remainingTimeUs); - printf("loop start %ld.%09lds\n", loopStart->tv_sec, loopStart->tv_nsec); - printf("loop end %ld.%09lds\n", loopEnd.tv_sec, loopEnd.tv_nsec); + 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); } -} \ No newline at end of file + ++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); + } +} diff --git a/RpiLedBars/src/rpi_midi_controller.c b/RpiLedBars/src/rpi_midi_controller.c index e112b59..7fa2fe3 100644 --- a/RpiLedBars/src/rpi_midi_controller.c +++ b/RpiLedBars/src/rpi_midi_controller.c @@ -1,19 +1,85 @@ +/** \file .c + * \brief This module + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + #include "rpi_midi_controller.h" #include +#include "log.h" + +#include "rpi_param.h" + +/*************************************************************************************************** + * Preprocessor Constants and Macros + **************************************************************************************************/ + +#define C3 0x30 + +/*************************************************************************************************** + * Type and Contant Definitions + **************************************************************************************************/ + +/*************************************************************************************************** + * Persistent Variables + **************************************************************************************************/ + +/** + * Boolean for interrupting the main thread loop + */ static snd_seq_t *seq_handle; + +/** + * Boolean for interrupting the main thread loop + */ static int sys_port; + +/** + * Boolean for interrupting the main thread loop + */ static int in_port; +/* state machine */ +int destChannel = 0xf; + +/*************************************************************************************************** + * Internal Function Prototypes + **************************************************************************************************/ + +/** + * \brief + */ void subscribe_system_port(); +/** + * \brief + * + * \param controller + */ void subscribe_midi_controller(snd_seq_addr_t controller); +/** + * \brief + * + * \param ev + */ void handle_system_port_events(snd_seq_event_t *ev); +/** + * \brief + * + * \param ev + */ void handle_in_port_events(snd_seq_event_t *ev); +/*************************************************************************************************** + * External Function Definitions + **************************************************************************************************/ + void midi_controller_setup() { if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK) != 0) { SNDERR("snd_seq_open"); @@ -23,8 +89,8 @@ void midi_controller_setup() { SNDERR("snd_seq_set_client_name"); } - sys_port = snd_seq_create_simple_port(seq_handle, "listen:in", - SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT, + sys_port = snd_seq_create_simple_port(seq_handle, "sys:in", + SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION); if (sys_port < 0) { @@ -52,10 +118,9 @@ void midi_controller_execute() { if (ev->dest.port == sys_port) { handle_system_port_events(ev); } else if (ev->dest.port == in_port) { - printf("ev %02d : %#010x - %#010x - %#010x\n", ev->type, ev->data.raw32.d[0], - ev->data.raw32.d[1], ev->data.raw32.d[2]); + handle_in_port_events(ev); } else { - fprintf(stderr, "unkonwn midi dest port\n"); + log_warn("Unkonwn midi dest port %d", ev->dest.port); } } if (ret < 0 && ret != -EAGAIN) { @@ -65,6 +130,10 @@ void midi_controller_execute() { void midi_controller_close() {} +/*************************************************************************************************** + * Internal Function Definitions + **************************************************************************************************/ + void subscribe_system_port() { snd_seq_addr_t sender = {.client = 0, .port = SND_SEQ_PORT_SYSTEM_ANNOUNCE}; snd_seq_connect_from(seq_handle, sys_port, sender.client, sender.port); @@ -78,17 +147,60 @@ void handle_system_port_events(snd_seq_event_t *ev) { if (ev->type == SND_SEQ_EVENT_PORT_START) { snd_seq_addr_t *newport = (snd_seq_addr_t *)&ev->data; if (newport->client != snd_seq_client_id(seq_handle)) { - fprintf(stdout, "New port %d:%d\n", newport->client, newport->port); + log_info("New port %d:%d", newport->client, newport->port); subscribe_midi_controller(*newport); } } } void handle_in_port_events(snd_seq_event_t *ev) { + switch (ev->type) { + case SND_SEQ_EVENT_PGMCHANGE: + if (ev->data.control.value < LED_NCHANS) { + if (ev->data.control.value != destChannel) { + destChannel = ev->data.control.value; + log_info("Control param channel %d", ev->data.control.value); + } + } else { + log_debug("PGMCHANGE : ch %#04x - param %u - value %d", ev->data.control.channel, + ev->data.control.param, ev->data.control.value); + } + break; + + case SND_SEQ_EVENT_NOTEON: + if (ev->data.note.note == C3) { + if (destChannel != 0xf) { + destChannel = 0xf; + log_info("Control global param"); + } + } else { + log_debug("NOTEON : ch %#04x - note %d - vel %d", ev->data.note.channel, ev->data.note.note, + ev->data.note.velocity); + } + break; + + case SND_SEQ_EVENT_CONTROLLER: + switch (ev->data.control.param) { + case 1: + + break; + + default: + break; + } + log_debug("CONTROLLER : ch %#04x - param %u - value %d", ev->data.control.channel, + ev->data.control.param, ev->data.control.value); + break; + + default: + log_debug("ev %02d : %#010x - %#010x - %#010x", ev->type, ev->data.raw32.d[0], + ev->data.raw32.d[1], ev->data.raw32.d[2]); + break; + } if (ev->type == SND_SEQ_EVENT_PORT_START) { snd_seq_addr_t *newport = (snd_seq_addr_t *)&ev->data; if (newport->client != snd_seq_client_id(seq_handle)) { - fprintf(stdout, "New port %d:%d\n", newport->client, newport->port); + log_debug("New port %d:%d", newport->client, newport->port); subscribe_midi_controller(*newport); } } diff --git a/RpiLedBars/src/rpi_midi_controller.h b/RpiLedBars/src/rpi_midi_controller.h index 60db392..f4d673c 100644 --- a/RpiLedBars/src/rpi_midi_controller.h +++ b/RpiLedBars/src/rpi_midi_controller.h @@ -1,10 +1,42 @@ +/** \file .h + * \brief This module + */ #if !defined(__RPI_MIDI_CONTROLLER_H__) #define __RPI_MIDI_CONTROLLER_H__ +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +/*************************************************************************************************** + * Preprocessor Constants and Macros + **************************************************************************************************/ + +/*************************************************************************************************** + * Type and Contant Definitions + **************************************************************************************************/ + +/*************************************************************************************************** + * Global Variables + **************************************************************************************************/ + +/*************************************************************************************************** + * External Function Prototypes + **************************************************************************************************/ + +/** + * \brief + */ void midi_controller_setup(); +/** + * \brief + */ void midi_controller_execute(); +/** + * \brief + */ void midi_controller_close(); #endif /* __RPI_MIDI_CONTROLLER_H__ */ \ No newline at end of file diff --git a/RpiLedBars/src/rpi_param.c b/RpiLedBars/src/rpi_param.c index 30f0937..9b3a5a0 100644 --- a/RpiLedBars/src/rpi_param.c +++ b/RpiLedBars/src/rpi_param.c @@ -1 +1,58 @@ +/** \file .c + * \brief This module + */ + +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + #include "rpi_param.h" + +#include + +/*************************************************************************************************** + * Preprocessor Constants and Macros + **************************************************************************************************/ + +/*************************************************************************************************** + * Type and Contant Definitions + **************************************************************************************************/ + +pixled_param_t const dummyPixledParam = {.intensity = .5, .hue = 0}; +auton_param_t const dummyAutonParam = {.sensitivity = 1}; +ledbar_param_t const dummyLedbarParam = {.sensitivity = 1}; + +/*************************************************************************************************** + * Persistent Variables + **************************************************************************************************/ + +param_t param; + +/*************************************************************************************************** + * Internal Function Prototypes + **************************************************************************************************/ + +/*************************************************************************************************** + * External Function Definitions + **************************************************************************************************/ + +void param_setup() { + param.pixled = dummyPixledParam; + param.auton = dummyAutonParam; + for (size_t i = 0; i < 10; ++i) { + param.ledbar[i] = dummyLedbarParam; + } + param_access = ¶m; +} + +void set_sensitivity(int channel, unsigned int sensitivity) { + if (channel >= 10) { + param.auton.sensitivity = sensitivity; + } else { + param.ledbar[channel].sensitivity = sensitivity; + } +} + +/*************************************************************************************************** + * Internal Function Definitions + **************************************************************************************************/ diff --git a/RpiLedBars/src/rpi_param.h b/RpiLedBars/src/rpi_param.h index 8b0cc72..b202a4c 100644 --- a/RpiLedBars/src/rpi_param.h +++ b/RpiLedBars/src/rpi_param.h @@ -1,4 +1,80 @@ +/** \file .h + * \brief This module + */ + #if !defined(__RPI_PARAM_H__) #define __RPI_PARAM_H__ +/*************************************************************************************************** + * Includes + **************************************************************************************************/ + +#include + +/*************************************************************************************************** + * Preprocessor Constants and Macros + **************************************************************************************************/ + +#define CHAN_MAXLEDS 6 * 60 // Maximum number of LEDs per channel +#define LED_NCHANS 8 // Number of LED channels (8 or 16) + +/*************************************************************************************************** + * Type and Contant Definitions + **************************************************************************************************/ + +/** + * + */ +typedef struct { + uint8_t intensity; + int hue; +} pixled_param_t; + +/** + * + */ +typedef struct { + unsigned int sensitivity; +} auton_param_t; + +/** + * + */ +typedef struct { + unsigned int sensitivity; +} ledbar_param_t; + +/** + * + */ +typedef struct { + pixled_param_t pixled; + auton_param_t auton; + ledbar_param_t ledbar[10]; +} param_t; + +/*************************************************************************************************** + * Global Variables + **************************************************************************************************/ + +static param_t *param_access; + +/*************************************************************************************************** + * External Function Prototypes + **************************************************************************************************/ + +/** + * \brief + */ +void param_setup(); + +/** + * \brief + * + * \param[in] channel + * + * \param[in] sensitivity + */ +void set_sensitivity(int channel, unsigned int sensitivity); + #endif /* __RPI_PARAM_H__ */ \ No newline at end of file diff --git a/RpiLedBars/src/tasks/artnet/rpi_artnet.c b/RpiLedBars/src/tasks/artnet/rpi_artnet.c index 531eac8..6d71639 100644 --- a/RpiLedBars/src/tasks/artnet/rpi_artnet.c +++ b/RpiLedBars/src/tasks/artnet/rpi_artnet.c @@ -20,6 +20,7 @@ #include #include +#include "log.h" #include "rpi_artnet_utils.h" /*************************************************************************************************** @@ -33,6 +34,7 @@ /*************************************************************************************************** * Persistent Variables **************************************************************************************************/ + /** * Boolean for interrupting the main thread loop */ @@ -76,7 +78,7 @@ void artnet_start() { isUdpListenerRunning = true; if (pthread_create(&udpListener, NULL, artnet_udp_handler, NULL) < 0) { - perror("pthread_create"); + log_error("pthread_create: %s", strerror(errno)); } } @@ -84,23 +86,25 @@ void artnet_stop() { isUdpListenerRunning = false; if (pthread_join(udpListener, NULL) != 0) { - perror("pthread_join"); + log_error("pthread_join: %s", strerror(errno)); } } int artnet_get_dmx_data(unsigned int univerve, uint8_t *dmxData[]) { if (univerve > 8) { - fprintf(stderr, "Universe %d out of bounds %d\n", univerve, 16); + log_error("Universe %d out of bounds %d\n", univerve, 16); *dmxData = NULL; return -1; } *dmxData = artDmxBufferArray[univerve]; + log_trace("%d;%d;%d;%d", (*dmxData)[0], (*dmxData)[1], (*dmxData)[2], (*dmxData)[3]); return 0; } /*************************************************************************************************** * Internal Function Definitions **************************************************************************************************/ + static void *artnet_udp_handler(void *arg) { int fdUdpSocket = -1; struct pollfd pollFdArray[1]; @@ -113,7 +117,7 @@ static void *artnet_udp_handler(void *arg) { /* Create UDP socket */ fdUdpSocket = socket(PF_INET, SOCK_DGRAM, 0); if (fdUdpSocket < 0) { - perror("Opening socket failed"); + log_error("Opening socket failed: %s", strerror(errno)); } /* Set non-blocking socket */ @@ -170,7 +174,7 @@ static void *artnet_udp_handler(void *arg) { } } else if (pollReturn < 0) { - fprintf(stderr, "error polling %d: %s\n", fdUdpSocket, strerror(errno)); + log_error("error polling %d: %s", fdUdpSocket, strerror(errno)); } } @@ -187,7 +191,7 @@ static void artnet_send_poll_reply(int fdUdpSocket, struct sockaddr_in senderAdd if (sendto(fdUdpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0, (struct sockaddr *)&senderAddr, sizeof(senderAddr)) < 0) { - fprintf(stderr, "Error sending poll reply to %s:%d: %s\n", inet_ntoa(senderAddr.sin_addr), - strerror(errno)); + log_error("%s: sending poll reply to \"%s:%d\": %s", inet_ntoa(senderAddr.sin_addr), + senderAddr.sin_port, strerror(errno)); } } \ No newline at end of file diff --git a/RpiLedBars/src/tasks/artnet/rpi_artnet_packets.h b/RpiLedBars/src/tasks/artnet/rpi_artnet_packets.h index d4828f6..30fb25f 100644 --- a/RpiLedBars/src/tasks/artnet/rpi_artnet_packets.h +++ b/RpiLedBars/src/tasks/artnet/rpi_artnet_packets.h @@ -139,7 +139,7 @@ typedef struct { uint8_t Filler[26]; // Filler bytes, currently zero. } artPollReply_t; -static artPollReply_t artPollReply = { +static artPollReply_t const artPollReply = { .ID = "Art-Net", .OpCode = OpPollReply, .IPAddr = {0}, diff --git a/RpiLedBars/src/tasks/cava/rpi_cava.c b/RpiLedBars/src/tasks/cava/rpi_cava.c index 141f4d4..f33d107 100644 --- a/RpiLedBars/src/tasks/cava/rpi_cava.c +++ b/RpiLedBars/src/tasks/cava/rpi_cava.c @@ -17,6 +17,9 @@ #include #include #include +#include + +#include "log.h" /*************************************************************************************************** * Preprocessor Constants and Macros @@ -65,48 +68,29 @@ uint16_t buffer[128 + 2]; */ static void *fifo_to_buffer(void *arg); +static int start_cava_process(); + +static void stop_cava_process(); + /*************************************************************************************************** * External Function Definitions **************************************************************************************************/ -void setup_cava() { - if ((cavaPid = fork()) == -1) { - perror("fork"); - exit(1); - } - - if (cavaPid == 0) { - /* Child process*/ - char *args[] = {"/usr/local/bin/cava", "-p", "/home/pi/LedBars/RpiLedBars/cava_config", NULL}; - - if (execv(args[0], args) != 0) { - perror("execv"); - } - - } else { - sleep(1); - } -} - -void close_cava() { - stop_cava_bg_worker(); - kill(cavaPid, SIGTERM); -} - -void start_cava_bg_worker() { +void cava_start() { isFifoReaderRunning = true; pthread_create(&fifoReader, NULL, fifo_to_buffer, NULL); } -void stop_cava_bg_worker() { +void cava_stop() { isFifoReaderRunning = false; if (pthread_join(fifoReader, NULL) != 0) { - perror("pthread_join"); + log_error("pthread_join: %s", strerror(errno)); } } -int get_cava_buffer(uint16_t **buffer_dst) { +int cava_get_buffer(uint16_t **buffer_dst) { *buffer_dst = buffer; + log_trace("%d;%d;%d;%d", buffer[0], buffer[1], buffer[2], buffer[3]); return 0; } @@ -122,12 +106,8 @@ static void *fifo_to_buffer(void *arg) { char strValue[6] = "0\0"; bool hasToBeDiscarded = true; - if start_cava_process () - ; - if ((fdCavaInput = open("/tmp/cava_output", O_RDONLY | O_NONBLOCK)) < 0) { - perror("open"); - close_cava(); - exit(1); + if ((fdCavaInput = start_cava_process()) < 0) { + log_error("y'a un truc qui a pas marché"); } memset(fds, 0, sizeof(fds)); @@ -165,26 +145,27 @@ static void *fifo_to_buffer(void *arg) { valueIndex = 0; if (valueIndex > 129) { - fprintf(stderr, "Buffer overflow, \\n missed, discarding next\n"); + log_warn("Buffer overflow, \\n missed, discarding next"); hasToBeDiscarded = true; } } } else { - fprintf(stderr, "Unexpected char %d [%c]\n", current, current); + log_warn("Unexpected char %d [%c]\n", current, current); } } } } else { if (errno != EAGAIN) { - perror("read"); + log_error("read: %s", strerror(errno)); } } } else if (ret < 0) { - fprintf(stderr, "error polling %d: %s\n", fdCavaInput, strerror(errno)); + log_error("polling %d: %s\n", fdCavaInput, strerror(errno)); } } + stop_cava_process(); close(fdCavaInput); return NULL; @@ -194,17 +175,18 @@ static int start_cava_process() { int fdCavaPipe[2]; if (pipe(fdCavaPipe) < 0) { - fprintf(stderr, "Cava pipe failure: %s\n", strerror(errno)); + log_error("Cava pipe failure: %s", strerror(errno)); return -EXIT_FAILURE; } if ((cavaPid = fork()) < 0) { - fprintf(stderr, "Cava fork failure: %s\n", strerror(errno)); - exit(EXIT_FAILURE); + log_error("Cava fork failure: %s", strerror(errno)); + exit(-EXIT_FAILURE); } if (cavaPid == 0) { /* Child process*/ + int fdLogOut; char *args[] = {"cava", "-p", "/home/pi/LedBars/RpiLedBars/cava_config", NULL}; /* Close reading end of the pipe */ @@ -214,10 +196,16 @@ static int start_cava_process() { /* Close writing end of the pipe */ close(fdCavaPipe[1]); + /* Open / create a log file for cava */ + fdLogOut = open("/dev/null", O_WRONLY, NULL); + /* Dup file in place of stderr */ + dup2(fdLogOut, STDERR_FILENO); + execvp(args[0], args); - fprintf(stderr, "Cava execvp failure or return: %s\n", strerror(errno)); - exit(EXIT_FAILURE); + log_error("Cava execvp failure or return: %s", strerror(errno)); + exit(-EXIT_FAILURE); } else { + int flags; /* Close writing end of the pipe */ close(fdCavaPipe[1]); /* Set reading end of the pipe non-blocking */ @@ -231,4 +219,7 @@ static int start_cava_process() { return -1; } -static void stop_cava_process() { kill(cavaPid, SIGTERM); } +static void stop_cava_process() { + kill(cavaPid, SIGTERM); + waitpid(0, NULL, WNOHANG); +} diff --git a/RpiLedBars/src/tasks/cava/rpi_cava.h b/RpiLedBars/src/tasks/cava/rpi_cava.h index 4eacffb..fd15c5c 100644 --- a/RpiLedBars/src/tasks/cava/rpi_cava.h +++ b/RpiLedBars/src/tasks/cava/rpi_cava.h @@ -30,12 +30,12 @@ **/ void setup_cava(); -int get_cava_buffer(uint16_t **buffer_dst); +int cava_get_buffer(uint16_t **buffer_dst); void close_cava(); -void start_cava_bg_worker(); +void cava_start(); -void stop_cava_bg_worker(); +void cava_stop(); #endif /* __RPI_CAVA_H__ */ \ No newline at end of file