using cmake to build project and submodules
This commit is contained in:
		
							
								
								
									
										14
									
								
								RpiLedBars/backend/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								RpiLedBars/backend/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
 | 
			
		||||
add_subdirectory(tasks)
 | 
			
		||||
add_subdirectory(drivers)
 | 
			
		||||
 | 
			
		||||
# add the executable
 | 
			
		||||
add_executable(${PROJECT_NAME}
 | 
			
		||||
    rpi_midi_controller.c
 | 
			
		||||
    rpi_param.c
 | 
			
		||||
    rpi_pattern.c
 | 
			
		||||
    main.c)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(${PROJECT_NAME} PRIVATE m asound)
 | 
			
		||||
target_link_libraries(${PROJECT_NAME} PRIVATE logc)
 | 
			
		||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_tasks ${PROJECT_NAME}_drivers)
 | 
			
		||||
							
								
								
									
										14
									
								
								RpiLedBars/backend/src/drivers/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								RpiLedBars/backend/src/drivers/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
# add the executable
 | 
			
		||||
add_library(${PROJECT_NAME}_drivers
 | 
			
		||||
    common.c
 | 
			
		||||
    dma/rpi_dma.c
 | 
			
		||||
    dma/rpi_videocore.c
 | 
			
		||||
    gpio/rpi_gpio.c
 | 
			
		||||
    leddriver/rpi_leddriver.c
 | 
			
		||||
    selector/rpi_selector.c
 | 
			
		||||
    smi/rpi_smi.c)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(${PROJECT_NAME}_drivers PRIVATE wiringPi)
 | 
			
		||||
target_link_libraries(${PROJECT_NAME}_drivers PRIVATE logc)
 | 
			
		||||
 | 
			
		||||
target_include_directories(${PROJECT_NAME}_drivers PUBLIC dma gpio leddriver selector smi)
 | 
			
		||||
							
								
								
									
										56
									
								
								RpiLedBars/backend/src/drivers/common.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								RpiLedBars/backend/src/drivers/common.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
// Use mmap to obtain virtual address, given physical
 | 
			
		||||
void *map_periph(MEM_MAP *mp, void *phys, int size) {
 | 
			
		||||
  mp->phys = phys;
 | 
			
		||||
  mp->size = PAGE_ROUNDUP(size);
 | 
			
		||||
  mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE);
 | 
			
		||||
  mp->virt = map_segment(phys, mp->size);
 | 
			
		||||
  return (mp->virt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Free mapped peripheral or memory
 | 
			
		||||
void unmap_periph_mem(MEM_MAP *mp) {
 | 
			
		||||
  if (mp) {
 | 
			
		||||
    unmap_segment(mp->virt, mp->size);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----- VIRTUAL MEMORY -----
 | 
			
		||||
 | 
			
		||||
// Get virtual memory segment for peripheral regs or physical mem
 | 
			
		||||
void *map_segment(void *addr, int size) {
 | 
			
		||||
  int fd;
 | 
			
		||||
  void *mem;
 | 
			
		||||
 | 
			
		||||
  size = PAGE_ROUNDUP(size);
 | 
			
		||||
  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);
 | 
			
		||||
  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) {
 | 
			
		||||
    munmap(mem, PAGE_ROUNDUP(size));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								RpiLedBars/backend/src/drivers/common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								RpiLedBars/backend/src/drivers/common.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
#if !defined(__COMMON_H__)
 | 
			
		||||
#define __COMMON_H__
 | 
			
		||||
 | 
			
		||||
// Location of peripheral registers in physical memory
 | 
			
		||||
#define PHYS_REG_BASE PI_23_REG_BASE
 | 
			
		||||
#define PI_01_REG_BASE 0x20000000 // Pi Zero or 1
 | 
			
		||||
#define PI_23_REG_BASE 0x3F000000 // Pi 2 or 3
 | 
			
		||||
#define PI_4_REG_BASE 0xFE000000  // Pi 4
 | 
			
		||||
 | 
			
		||||
#define CLOCK_HZ 250000000 // Pi 2 - 4
 | 
			
		||||
//#define CLOCK_HZ        400000000   // Pi Zero
 | 
			
		||||
 | 
			
		||||
// Location of peripheral registers in bus memory
 | 
			
		||||
#define BUS_REG_BASE 0x7E000000
 | 
			
		||||
 | 
			
		||||
// Get virtual 8 and 32-bit pointers to register
 | 
			
		||||
#define REG8(m, x) ((volatile uint8_t *)((uint32_t)(m.virt) + (uint32_t)(x)))
 | 
			
		||||
#define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt) + (uint32_t)(x)))
 | 
			
		||||
// Get bus address of register
 | 
			
		||||
#define REG_BUS_ADDR(m, x) ((uint32_t)(m.bus) + (uint32_t)(x))
 | 
			
		||||
// Convert uncached memory virtual address to bus address
 | 
			
		||||
#define MEM_BUS_ADDR(mp, a) ((uint32_t)a - (uint32_t)mp->virt + (uint32_t)mp->bus)
 | 
			
		||||
// Convert bus address to physical address (for mmap)
 | 
			
		||||
#define BUS_PHYS_ADDR(a) ((void *)((uint32_t)(a) & ~0xC0000000))
 | 
			
		||||
 | 
			
		||||
// Size of memory page
 | 
			
		||||
#define PAGE_SIZE 0x1000
 | 
			
		||||
// Round up to nearest page
 | 
			
		||||
#define PAGE_ROUNDUP(n) ((n) % PAGE_SIZE == 0 ? (n) : ((n) + PAGE_SIZE) & ~(PAGE_SIZE - 1))
 | 
			
		||||
 | 
			
		||||
// Structure for mapped peripheral or memory
 | 
			
		||||
typedef struct {
 | 
			
		||||
  int fd,    // File descriptor
 | 
			
		||||
      h,     // Memory handle
 | 
			
		||||
      size;  // Memory size
 | 
			
		||||
  void *bus, // Bus address
 | 
			
		||||
      *virt, // Virtual address
 | 
			
		||||
      *phys; // Physical address
 | 
			
		||||
} MEM_MAP;
 | 
			
		||||
 | 
			
		||||
// Use mmap to obtain virtual address, given physical
 | 
			
		||||
void *map_periph(MEM_MAP *mp, void *phys, int size);
 | 
			
		||||
 | 
			
		||||
// Free mapped peripheral or memory
 | 
			
		||||
void unmap_periph_mem(MEM_MAP *mp);
 | 
			
		||||
 | 
			
		||||
void *map_segment(void *addr, int size);
 | 
			
		||||
void unmap_segment(void *addr, int size);
 | 
			
		||||
 | 
			
		||||
#endif // __COMMON_H__
 | 
			
		||||
							
								
								
									
										90
									
								
								RpiLedBars/backend/src/drivers/dma/rpi_dma.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								RpiLedBars/backend/src/drivers/dma/rpi_dma.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
#include "rpi_dma.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include "rpi_videocore.h"
 | 
			
		||||
 | 
			
		||||
// DMA channels and data requests
 | 
			
		||||
#define DMA_SMI_DREQ 4
 | 
			
		||||
#define DMA_PWM_DREQ 5
 | 
			
		||||
#define DMA_SPI_TX_DREQ 6
 | 
			
		||||
#define DMA_SPI_RX_DREQ 7
 | 
			
		||||
#define DMA_BASE (PHYS_REG_BASE + 0x007000)
 | 
			
		||||
// DMA register addresses offset by 0x100 * chan_num
 | 
			
		||||
#define DMA_CS 0x00
 | 
			
		||||
#define DMA_CONBLK_AD 0x04
 | 
			
		||||
#define DMA_TI 0x08
 | 
			
		||||
#define DMA_SRCE_AD 0x0c
 | 
			
		||||
#define DMA_DEST_AD 0x10
 | 
			
		||||
#define DMA_TXFR_LEN 0x14
 | 
			
		||||
#define DMA_STRIDE 0x18
 | 
			
		||||
#define DMA_NEXTCONBK 0x1c
 | 
			
		||||
#define DMA_DEBUG 0x20
 | 
			
		||||
#define DMA_REG(ch, r) ((r) == DMA_ENABLE ? DMA_ENABLE : (ch)*0x100 + (r))
 | 
			
		||||
#define DMA_ENABLE 0xff0
 | 
			
		||||
// DMA register values
 | 
			
		||||
#define DMA_WAIT_RESP (1 << 3)
 | 
			
		||||
#define DMA_CB_DEST_INC (1 << 4)
 | 
			
		||||
#define DMA_DEST_DREQ (1 << 6)
 | 
			
		||||
#define DMA_CB_SRCE_INC (1 << 8)
 | 
			
		||||
#define DMA_SRCE_DREQ (1 << 10)
 | 
			
		||||
#define DMA_PRIORITY(n) ((n) << 16)
 | 
			
		||||
 | 
			
		||||
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
 | 
			
		||||
MEM_MAP dma_regs;
 | 
			
		||||
 | 
			
		||||
char *dma_regstrs[] = {"DMA CS",  "CB_AD",  "TI",      "SRCE_AD", "DEST_AD",
 | 
			
		||||
                       "TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG",   ""};
 | 
			
		||||
 | 
			
		||||
void dma_setup(MEM_MAP *mp, int chan, int nsamp, uint8_t **txdata, int offset, uint32_t dest_ad) {
 | 
			
		||||
  map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE);
 | 
			
		||||
 | 
			
		||||
  DMA_CB *cbs = mp->virt;
 | 
			
		||||
 | 
			
		||||
  *txdata = (uint8_t *)(cbs + offset);
 | 
			
		||||
  enable_dma(chan);
 | 
			
		||||
  cbs[0].ti = DMA_DEST_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_SRCE_INC | DMA_WAIT_RESP;
 | 
			
		||||
  cbs[0].tfr_len = nsamp;
 | 
			
		||||
  cbs[0].srce_ad = MEM_BUS_ADDR(mp, *txdata);
 | 
			
		||||
  cbs[0].dest_ad = dest_ad;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dma_close() { unmap_periph_mem(&dma_regs); }
 | 
			
		||||
 | 
			
		||||
// Enable and reset DMA
 | 
			
		||||
void enable_dma(int chan) {
 | 
			
		||||
  *REG32(dma_regs, DMA_ENABLE) |= (1 << chan);
 | 
			
		||||
  *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start DMA, given first control block
 | 
			
		||||
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval) {
 | 
			
		||||
  *REG32(dma_regs, DMA_REG(chan, DMA_CONBLK_AD)) = MEM_BUS_ADDR(mp, cbp);
 | 
			
		||||
  *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 2;         // Clear 'end' flag
 | 
			
		||||
  *REG32(dma_regs, DMA_REG(chan, DMA_DEBUG)) = 7;      // Clear error bits
 | 
			
		||||
  *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 | csval; // Start DMA
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return remaining transfer length
 | 
			
		||||
uint32_t dma_transfer_len(int chan) { return (*REG32(dma_regs, DMA_REG(chan, DMA_TXFR_LEN))); }
 | 
			
		||||
 | 
			
		||||
// Check if DMA is active
 | 
			
		||||
uint32_t dma_active(int chan) { return ((*REG32(dma_regs, DMA_REG(chan, DMA_CS))) & 1); }
 | 
			
		||||
 | 
			
		||||
// Halt current DMA operation by resetting controller
 | 
			
		||||
void stop_dma(int chan) {
 | 
			
		||||
  if (dma_regs.virt)
 | 
			
		||||
    *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Display DMA registers
 | 
			
		||||
void disp_dma(int chan) {
 | 
			
		||||
  volatile uint32_t *p = REG32(dma_regs, DMA_REG(chan, DMA_CS));
 | 
			
		||||
  int i = 0;
 | 
			
		||||
 | 
			
		||||
  while (dma_regstrs[i][0]) {
 | 
			
		||||
    printf("%-7s %08X ", dma_regstrs[i++], *p++);
 | 
			
		||||
    if (i % 5 == 0 || dma_regstrs[i][0] == 0)
 | 
			
		||||
      printf("\n");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								RpiLedBars/backend/src/drivers/dma/rpi_dma.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								RpiLedBars/backend/src/drivers/dma/rpi_dma.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
#if !defined(__RPI_DMA_H__)
 | 
			
		||||
#define __RPI_DMA_H__
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include "../common.h"
 | 
			
		||||
 | 
			
		||||
#define DMA_CHAN_A 10
 | 
			
		||||
#define DMA_CHAN_B 11
 | 
			
		||||
 | 
			
		||||
// DMA control block (must be 32-byte aligned)
 | 
			
		||||
typedef struct {
 | 
			
		||||
  uint32_t ti, // Transfer info
 | 
			
		||||
      srce_ad, // Source address
 | 
			
		||||
      dest_ad, // Destination address
 | 
			
		||||
      tfr_len, // Transfer length
 | 
			
		||||
      stride,  // Transfer stride
 | 
			
		||||
      next_cb, // Next control block
 | 
			
		||||
      debug,   // Debug register, zero in control block
 | 
			
		||||
      unused;
 | 
			
		||||
} DMA_CB __attribute__((aligned(32)));
 | 
			
		||||
 | 
			
		||||
void dma_setup(MEM_MAP *mp, int chan, int nsamp, uint8_t **txdata, int offset, uint32_t dest_ad);
 | 
			
		||||
void dma_close();
 | 
			
		||||
 | 
			
		||||
void enable_dma(int chan);
 | 
			
		||||
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval);
 | 
			
		||||
uint32_t dma_transfer_len(int chan);
 | 
			
		||||
uint32_t dma_active(int chan);
 | 
			
		||||
void stop_dma(int chan);
 | 
			
		||||
void disp_dma(int chan);
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_DMA_H__
 | 
			
		||||
							
								
								
									
										128
									
								
								RpiLedBars/backend/src/drivers/dma/rpi_videocore.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								RpiLedBars/backend/src/drivers/dma/rpi_videocore.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
			
		||||
#include "rpi_videocore.h"
 | 
			
		||||
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
// Mailbox command/response structure
 | 
			
		||||
typedef struct {
 | 
			
		||||
  uint32_t len,           // Overall length (bytes)
 | 
			
		||||
      req,                // Zero for request, 1<<31 for response
 | 
			
		||||
      tag,                // Command number
 | 
			
		||||
      blen,               // Buffer length (bytes)
 | 
			
		||||
      dlen;               // Data length (bytes)
 | 
			
		||||
  uint32_t uints[32 - 5]; // Data (108 bytes maximum)
 | 
			
		||||
} VC_MSG __attribute__((aligned(16)));
 | 
			
		||||
 | 
			
		||||
void disp_vc_msg(VC_MSG *msgp);
 | 
			
		||||
 | 
			
		||||
int open_mbox(void);
 | 
			
		||||
void close_mbox(int fd);
 | 
			
		||||
uint32_t msg_mbox(int fd, VC_MSG *msgp);
 | 
			
		||||
void *map_uncached_mem(MEM_MAP *mp, int size);
 | 
			
		||||
 | 
			
		||||
void videocore_setup(MEM_MAP *mp, int size) { map_uncached_mem(mp, size); }
 | 
			
		||||
void videocore_close(MEM_MAP *mp) {
 | 
			
		||||
  unmap_periph_mem(mp);
 | 
			
		||||
  if (mp->fd) {
 | 
			
		||||
    unlock_vc_mem(mp->fd, mp->h);
 | 
			
		||||
    free_vc_mem(mp->fd, mp->h);
 | 
			
		||||
    close_mbox(mp->fd);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Allocate uncached memory, get bus & phys addresses
 | 
			
		||||
void *map_uncached_mem(MEM_MAP *mp, int size) {
 | 
			
		||||
  void *ret;
 | 
			
		||||
  mp->size = PAGE_ROUNDUP(size);
 | 
			
		||||
  mp->fd = open_mbox();
 | 
			
		||||
  ret = (mp->h = alloc_vc_mem(mp->fd, mp->size, DMA_MEM_FLAGS)) > 0 &&
 | 
			
		||||
                (mp->bus = lock_vc_mem(mp->fd, mp->h)) != 0 &&
 | 
			
		||||
                (mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0
 | 
			
		||||
            ? mp->virt
 | 
			
		||||
            : 0;
 | 
			
		||||
  log_info("VC mem handle %u, phys %p, virt %p", mp->h, mp->bus, mp->virt);
 | 
			
		||||
  return (ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Allocate memory on PAGE_SIZE boundary, return handle
 | 
			
		||||
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags) {
 | 
			
		||||
  VC_MSG msg = {
 | 
			
		||||
      .tag = 0x3000c, .blen = 12, .dlen = 12, .uints = {PAGE_ROUNDUP(size), PAGE_SIZE, flags}};
 | 
			
		||||
  return (msg_mbox(fd, &msg));
 | 
			
		||||
}
 | 
			
		||||
// Lock allocated memory, return bus address
 | 
			
		||||
void *lock_vc_mem(int fd, int h) {
 | 
			
		||||
  VC_MSG msg = {.tag = 0x3000d, .blen = 4, .dlen = 4, .uints = {h}};
 | 
			
		||||
  return (h ? (void *)msg_mbox(fd, &msg) : 0);
 | 
			
		||||
}
 | 
			
		||||
// Unlock allocated memory
 | 
			
		||||
uint32_t unlock_vc_mem(int fd, int h) {
 | 
			
		||||
  VC_MSG msg = {.tag = 0x3000e, .blen = 4, .dlen = 4, .uints = {h}};
 | 
			
		||||
  return (h ? msg_mbox(fd, &msg) : 0);
 | 
			
		||||
}
 | 
			
		||||
// Free memory
 | 
			
		||||
uint32_t free_vc_mem(int fd, int h) {
 | 
			
		||||
  VC_MSG msg = {.tag = 0x3000f, .blen = 4, .dlen = 4, .uints = {h}};
 | 
			
		||||
  return (h ? msg_mbox(fd, &msg) : 0);
 | 
			
		||||
}
 | 
			
		||||
uint32_t set_vc_clock(int fd, int id, uint32_t freq) {
 | 
			
		||||
  VC_MSG msg1 = {.tag = 0x38001, .blen = 8, .dlen = 8, .uints = {id, 1}};
 | 
			
		||||
  VC_MSG msg2 = {.tag = 0x38002, .blen = 12, .dlen = 12, .uints = {id, freq, 0}};
 | 
			
		||||
  msg_mbox(fd, &msg1);
 | 
			
		||||
  disp_vc_msg(&msg1);
 | 
			
		||||
  msg_mbox(fd, &msg2);
 | 
			
		||||
  disp_vc_msg(&msg2);
 | 
			
		||||
  return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Display mailbox message
 | 
			
		||||
void disp_vc_msg(VC_MSG *msgp) {
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  printf("VC msg len=%X, req=%X, tag=%X, blen=%x, dlen=%x, data ", msgp->len, msgp->req, msgp->tag,
 | 
			
		||||
         msgp->blen, msgp->dlen);
 | 
			
		||||
  for (i = 0; i < msgp->blen / 4; i++)
 | 
			
		||||
    printf("%08X ", msgp->uints[i]);
 | 
			
		||||
  printf("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Open mailbox interface, return file descriptor
 | 
			
		||||
int open_mbox(void) {
 | 
			
		||||
  int fd;
 | 
			
		||||
 | 
			
		||||
  if ((fd = open("/dev/vcio", 0)) < 0)
 | 
			
		||||
    log_error("can't open VC mailbox");
 | 
			
		||||
  return (fd);
 | 
			
		||||
}
 | 
			
		||||
// Close mailbox interface
 | 
			
		||||
void close_mbox(int fd) {
 | 
			
		||||
  if (fd >= 0)
 | 
			
		||||
    close(fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Send message to mailbox, return first response int, 0 if error
 | 
			
		||||
uint32_t msg_mbox(int fd, VC_MSG *msgp) {
 | 
			
		||||
  uint32_t ret = 0, i;
 | 
			
		||||
 | 
			
		||||
  for (i = msgp->dlen / 4; i <= msgp->blen / 4; i += 4)
 | 
			
		||||
    msgp->uints[i++] = 0;
 | 
			
		||||
  msgp->len = (msgp->blen + 6) * 4;
 | 
			
		||||
  msgp->req = 0;
 | 
			
		||||
  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
 | 
			
		||||
  return (ret);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								RpiLedBars/backend/src/drivers/dma/rpi_videocore.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								RpiLedBars/backend/src/drivers/dma/rpi_videocore.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
#if !defined(__RPI_VIDEOCORE_H__)
 | 
			
		||||
#define __RPI_VIDEOCORE_H__
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include "../common.h"
 | 
			
		||||
 | 
			
		||||
// Videocore mailbox memory allocation flags, see:
 | 
			
		||||
//     https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
 | 
			
		||||
typedef enum {
 | 
			
		||||
  MEM_FLAG_DISCARDABLE = 1 << 0,    // can be resized to 0 at any time. Use for cached data
 | 
			
		||||
  MEM_FLAG_NORMAL = 0 << 2,         // normal allocating alias. Don't use from ARM
 | 
			
		||||
  MEM_FLAG_DIRECT = 1 << 2,         // 0xC alias uncached
 | 
			
		||||
  MEM_FLAG_COHERENT = 2 << 2,       // 0x8 alias. Non-allocating in L2 but coherent
 | 
			
		||||
  MEM_FLAG_ZERO = 1 << 4,           // initialise buffer to all zeros
 | 
			
		||||
  MEM_FLAG_NO_INIT = 1 << 5,        // don't initialise (default is initialise to all ones)
 | 
			
		||||
  MEM_FLAG_HINT_PERMALOCK = 1 << 6, // Likely to be locked for long periods of time
 | 
			
		||||
  MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2
 | 
			
		||||
} VC_ALLOC_FLAGS;
 | 
			
		||||
 | 
			
		||||
// VC flags for unchached DMA memory
 | 
			
		||||
#define DMA_MEM_FLAGS (MEM_FLAG_DIRECT | MEM_FLAG_ZERO)
 | 
			
		||||
 | 
			
		||||
void videocore_setup(MEM_MAP *mp, int size);
 | 
			
		||||
void videocore_close(MEM_MAP *mp);
 | 
			
		||||
 | 
			
		||||
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags);
 | 
			
		||||
void *lock_vc_mem(int fd, int h);
 | 
			
		||||
uint32_t unlock_vc_mem(int fd, int h);
 | 
			
		||||
uint32_t free_vc_mem(int fd, int h);
 | 
			
		||||
uint32_t set_vc_clock(int fd, int id, uint32_t freq);
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_VIDEOCORE_H__
 | 
			
		||||
							
								
								
									
										88
									
								
								RpiLedBars/backend/src/drivers/gpio/rpi_gpio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								RpiLedBars/backend/src/drivers/gpio/rpi_gpio.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
#include "rpi_gpio.h"
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "../common.h"
 | 
			
		||||
 | 
			
		||||
// GPIO register definitions
 | 
			
		||||
#define GPIO_BASE (PHYS_REG_BASE + 0x200000)
 | 
			
		||||
#define GPIO_MODE0 0x00
 | 
			
		||||
#define GPIO_SET0 0x1c
 | 
			
		||||
#define GPIO_CLR0 0x28
 | 
			
		||||
#define GPIO_LEV0 0x34
 | 
			
		||||
#define GPIO_GPPUD 0x94
 | 
			
		||||
#define GPIO_GPPUDCLK0 0x98
 | 
			
		||||
 | 
			
		||||
#define GPIO_MODE_STRS "IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"
 | 
			
		||||
 | 
			
		||||
bool isInitialized = false;
 | 
			
		||||
 | 
			
		||||
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
 | 
			
		||||
MEM_MAP gpio_regs;
 | 
			
		||||
 | 
			
		||||
char *gpio_mode_strs[] = {GPIO_MODE_STRS};
 | 
			
		||||
 | 
			
		||||
// definitions
 | 
			
		||||
 | 
			
		||||
void gpio_setup() {
 | 
			
		||||
  if (!isInitialized) {
 | 
			
		||||
    map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
 | 
			
		||||
    isInitialized = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gpio_close() {
 | 
			
		||||
  if (isInitialized) {
 | 
			
		||||
    unmap_periph_mem(&gpio_regs);
 | 
			
		||||
    isInitialized = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set input or output with pullups
 | 
			
		||||
void gpio_set(int pin, int mode, int pull) {
 | 
			
		||||
  gpio_mode(pin, mode);
 | 
			
		||||
  gpio_pull(pin, pull);
 | 
			
		||||
}
 | 
			
		||||
// Set I/P pullup or pulldown
 | 
			
		||||
void gpio_pull(int pin, int pull) {
 | 
			
		||||
  volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32;
 | 
			
		||||
 | 
			
		||||
  *REG32(gpio_regs, GPIO_GPPUD) = pull;
 | 
			
		||||
  usleep(2);
 | 
			
		||||
  *reg = pin << (pin % 32);
 | 
			
		||||
  usleep(2);
 | 
			
		||||
  *REG32(gpio_regs, GPIO_GPPUD) = 0;
 | 
			
		||||
  *reg = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set input or output
 | 
			
		||||
void gpio_mode(int pin, int mode) {
 | 
			
		||||
  if (gpio_regs.virt) {
 | 
			
		||||
    volatile uint32_t *reg = REG32(gpio_regs, GPIO_MODE0) + pin / 10, shift = (pin % 10) * 3;
 | 
			
		||||
    *reg = (*reg & ~(7 << shift)) | (mode << shift);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set an O/P pin
 | 
			
		||||
void gpio_out(int pin, int val) {
 | 
			
		||||
  volatile uint32_t *reg = REG32(gpio_regs, val ? GPIO_SET0 : GPIO_CLR0) + pin / 32;
 | 
			
		||||
  *reg = 1 << (pin % 32);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get an I/P pin value
 | 
			
		||||
uint8_t gpio_in(int pin) {
 | 
			
		||||
  volatile uint32_t *reg = REG32(gpio_regs, GPIO_LEV0) + pin / 32;
 | 
			
		||||
  return (((*reg) >> (pin % 32)) & 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Display the values in a GPIO mode register
 | 
			
		||||
void disp_mode_vals(uint32_t mode) {
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < 10; i++)
 | 
			
		||||
    printf("%u:%-4s ", i, gpio_mode_strs[(mode >> (i * 3)) & 7]);
 | 
			
		||||
  printf("\n");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								RpiLedBars/backend/src/drivers/gpio/rpi_gpio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								RpiLedBars/backend/src/drivers/gpio/rpi_gpio.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#if !defined(__RPI_GPIO_H__)
 | 
			
		||||
#define __RPI_GPIO_H__
 | 
			
		||||
 | 
			
		||||
// GPIO I/O definitions
 | 
			
		||||
#define GPIO_IN 0
 | 
			
		||||
#define GPIO_OUT 1
 | 
			
		||||
#define GPIO_ALT0 4
 | 
			
		||||
#define GPIO_ALT1 5
 | 
			
		||||
#define GPIO_ALT2 6
 | 
			
		||||
#define GPIO_ALT3 7
 | 
			
		||||
#define GPIO_ALT4 3
 | 
			
		||||
#define GPIO_ALT5 2
 | 
			
		||||
 | 
			
		||||
#define GPIO_NOPULL 0
 | 
			
		||||
#define GPIO_PULLDN 1
 | 
			
		||||
#define GPIO_PULLUP 2
 | 
			
		||||
 | 
			
		||||
void gpio_setup();
 | 
			
		||||
 | 
			
		||||
void gpio_close();
 | 
			
		||||
 | 
			
		||||
void gpio_pull(int pin, int pull);
 | 
			
		||||
 | 
			
		||||
void gpio_mode(int pin, int mode);
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_GPIO_H__
 | 
			
		||||
							
								
								
									
										210
									
								
								RpiLedBars/backend/src/drivers/leddriver/rpi_leddriver.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								RpiLedBars/backend/src/drivers/leddriver/rpi_leddriver.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,210 @@
 | 
			
		||||
#include "rpi_leddriver.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "../../rpi_param.h"
 | 
			
		||||
#include "../common.h"
 | 
			
		||||
 | 
			
		||||
#include "../dma/rpi_dma.h"
 | 
			
		||||
#include "../dma/rpi_videocore.h"
 | 
			
		||||
#include "../gpio/rpi_gpio.h"
 | 
			
		||||
#include "../smi/rpi_smi.h"
 | 
			
		||||
 | 
			
		||||
#if PHYS_REG_BASE == PI_4_REG_BASE // Timings for RPi v4 (1.5 GHz)
 | 
			
		||||
#define SMI_TIMING 10, 15, 30, 15  // 400 ns cycle time
 | 
			
		||||
#else                              // Timings for RPi v0-3 (1 GHz)
 | 
			
		||||
#define SMI_TIMING 10, 10, 20, 10  // 400 ns cycle time
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define TX_TEST 0      // If non-zero, use dummy Tx data
 | 
			
		||||
#define LED_NBITS 24   // Number of data bits per LED
 | 
			
		||||
#define LED_PREBITS 4  // Number of zero bits before LED data
 | 
			
		||||
#define LED_POSTBITS 4 // Number of zero bits after LED data
 | 
			
		||||
#define BIT_NPULSES 3  // Number of O/P pulses per LED bit
 | 
			
		||||
 | 
			
		||||
// Length of data for 1 row (1 LED on each channel)
 | 
			
		||||
#define LED_DLEN (LED_NBITS * BIT_NPULSES)
 | 
			
		||||
 | 
			
		||||
// Transmit data type, 8 or 16 bits
 | 
			
		||||
#if LED_NCHANS > 8
 | 
			
		||||
#define TXDATA_T uint16_t
 | 
			
		||||
#else
 | 
			
		||||
#define TXDATA_T uint8_t
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Ofset into Tx data buffer, given LED number in chan
 | 
			
		||||
#define LED_TX_OSET(n) (LED_PREBITS + (LED_DLEN * (n)))
 | 
			
		||||
 | 
			
		||||
// Size of data buffers & NV memory, given number of LEDs per chan
 | 
			
		||||
#define TX_BUFF_LEN(n) (LED_TX_OSET(n) + LED_POSTBITS)
 | 
			
		||||
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
 | 
			
		||||
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
 | 
			
		||||
 | 
			
		||||
/* Global */
 | 
			
		||||
MEM_MAP vc_mem;
 | 
			
		||||
TXDATA_T *txdata;
 | 
			
		||||
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)];
 | 
			
		||||
 | 
			
		||||
void swap_bytes();
 | 
			
		||||
 | 
			
		||||
void leddriver_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() {
 | 
			
		||||
  videocore_close(&vc_mem);
 | 
			
		||||
  smi_close(LED_NCHANS);
 | 
			
		||||
  gpio_close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_color(uint32_t rgb, int index) {
 | 
			
		||||
  int msk;
 | 
			
		||||
  TXDATA_T *txd = &(tx_buffer[LED_TX_OSET(index)]);
 | 
			
		||||
 | 
			
		||||
  // For each bit of the 24-bit RGB values..
 | 
			
		||||
  for (size_t n = 0; n < LED_NBITS; n++) {
 | 
			
		||||
    // Mask to convert RGB to GRB, M.S bit first
 | 
			
		||||
    msk = n == 0 ? 0x800000 : n == 8 ? 0x8000 : n == 16 ? 0x80 : msk >> 1;
 | 
			
		||||
    // 1st byte or word is a high pulse on all lines
 | 
			
		||||
    txd[0] = (TXDATA_T)0xffff;
 | 
			
		||||
    // 2nd has high or low bits from data
 | 
			
		||||
    // 3rd is a low pulse
 | 
			
		||||
    txd[1] = txd[2] = 0;
 | 
			
		||||
    if (rgb & msk) {
 | 
			
		||||
      txd[1] = (TXDATA_T)0xffff;
 | 
			
		||||
    }
 | 
			
		||||
    txd += BIT_NPULSES;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set Tx data for 8 or 16 chans, 1 LED per chan, given 1 RGB val per chan
 | 
			
		||||
// Logic 1 is 0.8us high, 0.4 us low, logic 0 is 0.4us high, 0.8us low
 | 
			
		||||
void rgb_txdata(int *rgbs, int index) {
 | 
			
		||||
  int i, n, msk;
 | 
			
		||||
  TXDATA_T *txd = &(tx_buffer[LED_TX_OSET(index)]);
 | 
			
		||||
 | 
			
		||||
  // For each bit of the 24-bit RGB values..
 | 
			
		||||
  for (n = 0; n < LED_NBITS; n++) {
 | 
			
		||||
    // Mask to convert RGB to GRB, M.S bit first
 | 
			
		||||
    msk = n == 0 ? 0x800000 : n == 8 ? 0x8000 : n == 16 ? 0x80 : msk >> 1;
 | 
			
		||||
    // 1st byte or word is a high pulse on all lines
 | 
			
		||||
    txd[0] = (TXDATA_T)0xffff;
 | 
			
		||||
    // 2nd has high or low bits from data
 | 
			
		||||
    // 3rd is a low pulse
 | 
			
		||||
    txd[1] = txd[2] = 0;
 | 
			
		||||
    for (i = 0; i < LED_NCHANS; i++) {
 | 
			
		||||
      if (rgbs[i] & msk)
 | 
			
		||||
        txd[1] |= (1 << i);
 | 
			
		||||
    }
 | 
			
		||||
    txd += BIT_NPULSES;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void leddriver_refresh() {
 | 
			
		||||
#if LED_NCHANS <= 8
 | 
			
		||||
  swap_bytes();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  while (dma_active(DMA_CHAN_A)) {
 | 
			
		||||
    usleep(10);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  memcpy(txdata, tx_buffer, TX_BUFF_SIZE(CHAN_MAXLEDS));
 | 
			
		||||
  enable_dma(DMA_CHAN_A);
 | 
			
		||||
  start_smi(&vc_mem);
 | 
			
		||||
  usleep(10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Swap adjacent bytes in transmit data
 | 
			
		||||
void swap_bytes() {
 | 
			
		||||
  uint16_t *wp = (uint16_t *)tx_buffer;
 | 
			
		||||
  int len = TX_BUFF_SIZE(CHAN_MAXLEDS);
 | 
			
		||||
 | 
			
		||||
  len = (len + 1) / 2;
 | 
			
		||||
  while (len-- > 0) {
 | 
			
		||||
    *wp = __builtin_bswap16(*wp);
 | 
			
		||||
    wp++;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								RpiLedBars/backend/src/drivers/leddriver/rpi_leddriver.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								RpiLedBars/backend/src/drivers/leddriver/rpi_leddriver.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#if !defined(__RPI_LEDDRIVER_H__)
 | 
			
		||||
#define __RPI_LEDDRIVER_H__
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
void leddriver_setup();
 | 
			
		||||
 | 
			
		||||
void leddriver_close();
 | 
			
		||||
 | 
			
		||||
void set_color(uint32_t rgb, int index);
 | 
			
		||||
 | 
			
		||||
void rgb_txdata(int *rgbs, int index);
 | 
			
		||||
 | 
			
		||||
void leddriver_refresh();
 | 
			
		||||
 | 
			
		||||
uint32_t ColorHSV(uint16_t hue, uint8_t sat, uint8_t val);
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_LEDDRIVER_H__
 | 
			
		||||
							
								
								
									
										65
									
								
								RpiLedBars/backend/src/drivers/selector/rpi_selector.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								RpiLedBars/backend/src/drivers/selector/rpi_selector.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
/** @file rpi_selector.c
 | 
			
		||||
 *  @brief This module is used to manage selector on gpio
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
#include "rpi_selector.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <wiringPi.h>
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
unsigned const selectorPinNumber = 4;
 | 
			
		||||
/* TODO use GPIO function from ../gpio/gipo.h (same pin number) */
 | 
			
		||||
int selectorPins[4] = {26, 27, 6, 5};
 | 
			
		||||
char modeStr[] = "0 | 0 | 0 | 0 \n";
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Persistent Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
void selector_setup() {
 | 
			
		||||
  wiringPiSetupGpio();
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < selectorPinNumber; ++i) {
 | 
			
		||||
    pinMode(selectorPins[i], INPUT);
 | 
			
		||||
    pullUpDnControl(selectorPins[i], PUD_DOWN);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int selector_get_position() {
 | 
			
		||||
  int newPosition = -1;
 | 
			
		||||
  for (size_t i = 0; i < selectorPinNumber; ++i) {
 | 
			
		||||
    if (digitalRead(selectorPins[i])) {
 | 
			
		||||
      newPosition = i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return newPosition;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *selector_tostr() {
 | 
			
		||||
  for (size_t i = 0; i < selectorPinNumber; ++i) {
 | 
			
		||||
    modeStr[i * 4] = digitalRead(selectorPins[i]) ? '1' : '0';
 | 
			
		||||
  }
 | 
			
		||||
  return modeStr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
							
								
								
									
										42
									
								
								RpiLedBars/backend/src/drivers/selector/rpi_selector.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								RpiLedBars/backend/src/drivers/selector/rpi_selector.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
/** @file rpi_selector.h
 | 
			
		||||
 *  @brief This module is used to manage selector on gpio
 | 
			
		||||
 */
 | 
			
		||||
#if !defined(__RPI_SELECTOR_H__)
 | 
			
		||||
#define __RPI_SELECTOR_H__
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Global Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
/**
 | 
			
		||||
 * @brief setup the selector module
 | 
			
		||||
 **/
 | 
			
		||||
void selector_setup();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief get the selector position
 | 
			
		||||
 **/
 | 
			
		||||
int selector_get_position();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Get string with selector debug info
 | 
			
		||||
 **/
 | 
			
		||||
char *selector_tostr();
 | 
			
		||||
 | 
			
		||||
#endif /* __RPI_SELECTOR_H__ */
 | 
			
		||||
							
								
								
									
										223
									
								
								RpiLedBars/backend/src/drivers/smi/rpi_smi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								RpiLedBars/backend/src/drivers/smi/rpi_smi.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,223 @@
 | 
			
		||||
#include "rpi_smi.h"
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "../dma/rpi_dma.h"
 | 
			
		||||
#include "../gpio/rpi_gpio.h"
 | 
			
		||||
 | 
			
		||||
// GPIO first pin
 | 
			
		||||
#define SMI_SD0_PIN 8
 | 
			
		||||
 | 
			
		||||
// Data widths
 | 
			
		||||
#define SMI_8_BITS 0
 | 
			
		||||
#define SMI_16_BITS 1
 | 
			
		||||
#define SMI_18_BITS 2
 | 
			
		||||
#define SMI_9_BITS 3
 | 
			
		||||
 | 
			
		||||
// Clock registers and values
 | 
			
		||||
#define CLK_BASE (PHYS_REG_BASE + 0x101000)
 | 
			
		||||
// #define CLK_PWM_CTL 0xa0
 | 
			
		||||
// #define CLK_PWM_DIV 0xa4
 | 
			
		||||
#define CLK_SMI_CTL 0xb0
 | 
			
		||||
#define CLK_SMI_DIV 0xb4
 | 
			
		||||
#define CLK_PASSWD 0x5a000000
 | 
			
		||||
#define PWM_CLOCK_ID 0xa
 | 
			
		||||
 | 
			
		||||
// DMA request threshold
 | 
			
		||||
#define REQUEST_THRESH 2
 | 
			
		||||
 | 
			
		||||
// Register definitions
 | 
			
		||||
#define SMI_BASE (PHYS_REG_BASE + 0x600000)
 | 
			
		||||
#define SMI_CS 0x00   // Control & status
 | 
			
		||||
#define SMI_L 0x04    // Transfer length
 | 
			
		||||
#define SMI_A 0x08    // Address
 | 
			
		||||
#define SMI_D 0x0c    // Data
 | 
			
		||||
#define SMI_DSR0 0x10 // Read settings device 0
 | 
			
		||||
#define SMI_DSW0 0x14 // Write settings device 0
 | 
			
		||||
#define SMI_DSR1 0x18 // Read settings device 1
 | 
			
		||||
#define SMI_DSW1 0x1c // Write settings device 1
 | 
			
		||||
#define SMI_DSR2 0x20 // Read settings device 2
 | 
			
		||||
#define SMI_DSW2 0x24 // Write settings device 2
 | 
			
		||||
#define SMI_DSR3 0x28 // Read settings device 3
 | 
			
		||||
#define SMI_DSW3 0x2c // Write settings device 3
 | 
			
		||||
#define SMI_DMC 0x30  // DMA control
 | 
			
		||||
#define SMI_DCS 0x34  // Direct control/status
 | 
			
		||||
#define SMI_DCA 0x38  // Direct address
 | 
			
		||||
#define SMI_DCD 0x3c  // Direct data
 | 
			
		||||
#define SMI_FD 0x40   // FIFO debug
 | 
			
		||||
#define SMI_REGLEN (SMI_FD * 4)
 | 
			
		||||
 | 
			
		||||
// Union of 32-bit value with register bitfields
 | 
			
		||||
#define REG_DEF(name, fields)                                                                      \
 | 
			
		||||
  typedef union {                                                                                  \
 | 
			
		||||
    struct {                                                                                       \
 | 
			
		||||
      volatile uint32_t fields;                                                                    \
 | 
			
		||||
    };                                                                                             \
 | 
			
		||||
    volatile uint32_t value;                                                                       \
 | 
			
		||||
  } name
 | 
			
		||||
 | 
			
		||||
// Control and status register
 | 
			
		||||
#define SMI_CS_FIELDS                                                                              \
 | 
			
		||||
  enable:                                                                                          \
 | 
			
		||||
  1, done : 1, active : 1, start : 1, clear : 1, write : 1, _x1 : 2, teen : 1, intd : 1, intt : 1, \
 | 
			
		||||
      intr : 1, pvmode : 1, seterr : 1, pxldat : 1, edreq : 1, _x2 : 8, _x3 : 1, aferr : 1,        \
 | 
			
		||||
      txw : 1, rxr : 1, txd : 1, rxd : 1, txe : 1, rxf : 1
 | 
			
		||||
REG_DEF(SMI_CS_REG, SMI_CS_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Data length register
 | 
			
		||||
#define SMI_L_FIELDS                                                                               \
 | 
			
		||||
  len:                                                                                             \
 | 
			
		||||
  32
 | 
			
		||||
REG_DEF(SMI_L_REG, SMI_L_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Address & device number
 | 
			
		||||
#define SMI_A_FIELDS                                                                               \
 | 
			
		||||
  addr:                                                                                            \
 | 
			
		||||
  6, _x1 : 2, dev : 2
 | 
			
		||||
REG_DEF(SMI_A_REG, SMI_A_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Data FIFO
 | 
			
		||||
#define SMI_D_FIELDS                                                                               \
 | 
			
		||||
  data:                                                                                            \
 | 
			
		||||
  32
 | 
			
		||||
REG_DEF(SMI_D_REG, SMI_D_FIELDS);
 | 
			
		||||
 | 
			
		||||
// DMA control register
 | 
			
		||||
#define SMI_DMC_FIELDS                                                                             \
 | 
			
		||||
  reqw:                                                                                            \
 | 
			
		||||
  6, reqr : 6, panicw : 6, panicr : 6, dmap : 1, _x1 : 3, dmaen : 1
 | 
			
		||||
REG_DEF(SMI_DMC_REG, SMI_DMC_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Device settings: read (1 of 4)
 | 
			
		||||
#define SMI_DSR_FIELDS                                                                             \
 | 
			
		||||
  rstrobe:                                                                                         \
 | 
			
		||||
  7, rdreq : 1, rpace : 7, rpaceall : 1, rhold : 6, fsetup : 1, mode68 : 1, rsetup : 6, rwidth : 2
 | 
			
		||||
REG_DEF(SMI_DSR_REG, SMI_DSR_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Device settings: write (1 of 4)
 | 
			
		||||
#define SMI_DSW_FIELDS                                                                             \
 | 
			
		||||
  wstrobe:                                                                                         \
 | 
			
		||||
  7, wdreq : 1, wpace : 7, wpaceall : 1, whold : 6, wswap : 1, wformat : 1, wsetup : 6, wwidth : 2
 | 
			
		||||
REG_DEF(SMI_DSW_REG, SMI_DSW_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Direct control register
 | 
			
		||||
#define SMI_DCS_FIELDS                                                                             \
 | 
			
		||||
  enable:                                                                                          \
 | 
			
		||||
  1, start : 1, done : 1, write : 1
 | 
			
		||||
REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Direct control address & device number
 | 
			
		||||
#define SMI_DCA_FIELDS                                                                             \
 | 
			
		||||
  addr:                                                                                            \
 | 
			
		||||
  6, _x1 : 2, dev : 2
 | 
			
		||||
REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Direct control data
 | 
			
		||||
#define SMI_DCD_FIELDS                                                                             \
 | 
			
		||||
  data:                                                                                            \
 | 
			
		||||
  32
 | 
			
		||||
REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Debug register
 | 
			
		||||
#define SMI_FLVL_FIELDS                                                                            \
 | 
			
		||||
  fcnt:                                                                                            \
 | 
			
		||||
  6, _x1 : 2, flvl : 6
 | 
			
		||||
REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Pointers to SMI registers
 | 
			
		||||
volatile SMI_CS_REG *smi_cs;
 | 
			
		||||
volatile SMI_L_REG *smi_l;
 | 
			
		||||
volatile SMI_A_REG *smi_a;
 | 
			
		||||
volatile SMI_D_REG *smi_d;
 | 
			
		||||
volatile SMI_DMC_REG *smi_dmc;
 | 
			
		||||
volatile SMI_DSR_REG *smi_dsr;
 | 
			
		||||
volatile SMI_DSW_REG *smi_dsw;
 | 
			
		||||
volatile SMI_DCS_REG *smi_dcs;
 | 
			
		||||
volatile SMI_DCA_REG *smi_dca;
 | 
			
		||||
volatile SMI_DCD_REG *smi_dcd;
 | 
			
		||||
 | 
			
		||||
MEM_MAP smi_regs, clk_regs;
 | 
			
		||||
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp, uint8_t **txdata, int len);
 | 
			
		||||
 | 
			
		||||
void smi_setup(int channels, int ns, int setup, int strobe, int hold, MEM_MAP *mp, int nsamp,
 | 
			
		||||
               uint8_t **txdata) {
 | 
			
		||||
  map_periph(&smi_regs, (void *)SMI_BASE, PAGE_SIZE);
 | 
			
		||||
  map_periph(&clk_regs, (void *)CLK_BASE, PAGE_SIZE);
 | 
			
		||||
 | 
			
		||||
  int width = channels > 8 ? SMI_16_BITS : SMI_8_BITS;
 | 
			
		||||
  int i, divi = ns / 2;
 | 
			
		||||
 | 
			
		||||
  smi_cs = (SMI_CS_REG *)REG32(smi_regs, SMI_CS);
 | 
			
		||||
  smi_l = (SMI_L_REG *)REG32(smi_regs, SMI_L);
 | 
			
		||||
  smi_a = (SMI_A_REG *)REG32(smi_regs, SMI_A);
 | 
			
		||||
  smi_d = (SMI_D_REG *)REG32(smi_regs, SMI_D);
 | 
			
		||||
  smi_dmc = (SMI_DMC_REG *)REG32(smi_regs, SMI_DMC);
 | 
			
		||||
  smi_dsr = (SMI_DSR_REG *)REG32(smi_regs, SMI_DSR0);
 | 
			
		||||
  smi_dsw = (SMI_DSW_REG *)REG32(smi_regs, SMI_DSW0);
 | 
			
		||||
  smi_dcs = (SMI_DCS_REG *)REG32(smi_regs, SMI_DCS);
 | 
			
		||||
  smi_dca = (SMI_DCA_REG *)REG32(smi_regs, SMI_DCA);
 | 
			
		||||
  smi_dcd = (SMI_DCD_REG *)REG32(smi_regs, SMI_DCD);
 | 
			
		||||
  smi_cs->value = smi_l->value = smi_a->value = 0;
 | 
			
		||||
  smi_dsr->value = smi_dsw->value = smi_dcs->value = smi_dca->value = 0;
 | 
			
		||||
  if (*REG32(clk_regs, CLK_SMI_DIV) != divi << 12) {
 | 
			
		||||
    *REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | (1 << 5);
 | 
			
		||||
    usleep(10);
 | 
			
		||||
    while (*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7))
 | 
			
		||||
      ;
 | 
			
		||||
    usleep(10);
 | 
			
		||||
    *REG32(clk_regs, CLK_SMI_DIV) = CLK_PASSWD | (divi << 12);
 | 
			
		||||
    usleep(10);
 | 
			
		||||
    *REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | 6 | (1 << 4);
 | 
			
		||||
    usleep(10);
 | 
			
		||||
    while ((*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7)) == 0)
 | 
			
		||||
      ;
 | 
			
		||||
    usleep(100);
 | 
			
		||||
  }
 | 
			
		||||
  if (smi_cs->seterr)
 | 
			
		||||
    smi_cs->seterr = 1;
 | 
			
		||||
  smi_dsr->rsetup = smi_dsw->wsetup = setup;
 | 
			
		||||
  smi_dsr->rstrobe = smi_dsw->wstrobe = strobe;
 | 
			
		||||
  smi_dsr->rhold = smi_dsw->whold = hold;
 | 
			
		||||
  smi_dmc->panicr = smi_dmc->panicw = 8;
 | 
			
		||||
  smi_dmc->reqr = smi_dmc->reqw = REQUEST_THRESH;
 | 
			
		||||
  smi_dsr->rwidth = smi_dsw->wwidth = width;
 | 
			
		||||
  for (i = 0; i < channels; i++)
 | 
			
		||||
    gpio_mode(SMI_SD0_PIN + i, GPIO_ALT1);
 | 
			
		||||
 | 
			
		||||
  setup_smi_dma(mp, nsamp, txdata, width + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void smi_close(int channels) {
 | 
			
		||||
  for (size_t i = 0; i < channels; ++i)
 | 
			
		||||
    gpio_mode(SMI_SD0_PIN + i, GPIO_IN);
 | 
			
		||||
  if (smi_regs.virt) {
 | 
			
		||||
    *REG32(smi_regs, SMI_CS) = 0;
 | 
			
		||||
  }
 | 
			
		||||
  stop_dma(DMA_CHAN_A);
 | 
			
		||||
  unmap_periph_mem(&clk_regs);
 | 
			
		||||
  unmap_periph_mem(&smi_regs);
 | 
			
		||||
  dma_close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start SMI DMA transfers
 | 
			
		||||
void start_smi(MEM_MAP *mp) {
 | 
			
		||||
  DMA_CB *cbs = mp->virt;
 | 
			
		||||
 | 
			
		||||
  start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
 | 
			
		||||
  smi_cs->start = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// private
 | 
			
		||||
 | 
			
		||||
// Set up SMI transfers using DMA
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp, uint8_t **txdata, int len) {
 | 
			
		||||
  smi_dmc->dmaen = 1;
 | 
			
		||||
  smi_cs->enable = 1;
 | 
			
		||||
  smi_cs->clear = 1;
 | 
			
		||||
  smi_cs->pxldat = 1;
 | 
			
		||||
  smi_l->len = nsamp * len;
 | 
			
		||||
  smi_cs->write = 1;
 | 
			
		||||
 | 
			
		||||
  dma_setup(mp, DMA_CHAN_A, nsamp, txdata, len, REG_BUS_ADDR(smi_regs, SMI_D));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								RpiLedBars/backend/src/drivers/smi/rpi_smi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								RpiLedBars/backend/src/drivers/smi/rpi_smi.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#if !defined(__RPI_SMI_H__)
 | 
			
		||||
#define __RPI_SMI_H__
 | 
			
		||||
 | 
			
		||||
#include "../common.h"
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
void smi_setup(int channels, int ns, int setup, int strobe, int hold, MEM_MAP *mp, int nsamp,
 | 
			
		||||
               uint8_t **txdata);
 | 
			
		||||
 | 
			
		||||
void smi_close(int channels);
 | 
			
		||||
 | 
			
		||||
void start_smi(MEM_MAP *mp);
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_SMI_H__
 | 
			
		||||
							
								
								
									
										371
									
								
								RpiLedBars/backend/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										371
									
								
								RpiLedBars/backend/src/main.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,371 @@
 | 
			
		||||
/** @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 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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										240
									
								
								RpiLedBars/backend/src/rpi_midi_controller.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								RpiLedBars/backend/src/rpi_midi_controller.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,240 @@
 | 
			
		||||
/** @file .c
 | 
			
		||||
 *  @brief This module
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "rpi_midi_controller.h"
 | 
			
		||||
 | 
			
		||||
#include <alsa/asoundlib.h>
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
#include "rpi_param.h"
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define C3 48
 | 
			
		||||
#define Gd3 56
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * 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");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (snd_seq_set_client_name(seq_handle, "Midi Listener") != 0) {
 | 
			
		||||
    SNDERR("snd_seq_set_client_name");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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) {
 | 
			
		||||
    SNDERR("snd_seq_create_simple_port");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  subscribe_system_port();
 | 
			
		||||
 | 
			
		||||
  in_port = snd_seq_create_simple_port(seq_handle, "listen:in",
 | 
			
		||||
                                       SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
 | 
			
		||||
                                       SND_SEQ_PORT_TYPE_APPLICATION);
 | 
			
		||||
 | 
			
		||||
  if (in_port < 0) {
 | 
			
		||||
    SNDERR("snd_seq_create_simple_port");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  snd_seq_addr_t controller = {.client = 24, .port = 0};
 | 
			
		||||
  subscribe_midi_controller(controller);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void midi_controller_execute() {
 | 
			
		||||
  snd_seq_event_t *ev = NULL;
 | 
			
		||||
  int ret;
 | 
			
		||||
  while ((ret = snd_seq_event_input(seq_handle, &ev)) > 0) {
 | 
			
		||||
    if (ev->dest.port == sys_port) {
 | 
			
		||||
      handle_system_port_events(ev);
 | 
			
		||||
    } else if (ev->dest.port == in_port) {
 | 
			
		||||
      handle_in_port_events(ev);
 | 
			
		||||
    } else {
 | 
			
		||||
      log_warn("Unkonwn midi dest port %d", ev->dest.port);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (ret < 0 && ret != -EAGAIN) {
 | 
			
		||||
    SNDERR("snd_seq_event_input");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void subscribe_midi_controller(snd_seq_addr_t controller) {
 | 
			
		||||
  snd_seq_connect_from(seq_handle, in_port, controller.client, controller.port);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)) {
 | 
			
		||||
      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 if (destChannel != GLOBAL_CHANNEL) {
 | 
			
		||||
      log_info("Control global param");
 | 
			
		||||
      destChannel = GLOBAL_CHANNEL;
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case SND_SEQ_EVENT_NOTEON:
 | 
			
		||||
    if (ev->data.note.note == C3) {
 | 
			
		||||
      if (destChannel != GLOBAL_CHANNEL) {
 | 
			
		||||
        destChannel = GLOBAL_CHANNEL;
 | 
			
		||||
        log_info("Control global param");
 | 
			
		||||
      }
 | 
			
		||||
    } else if (ev->data.note.note >= Gd3) {
 | 
			
		||||
      set_pattern(ev->data.note.note - Gd3);
 | 
			
		||||
    } 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_NOTEOFF:
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case SND_SEQ_EVENT_CONTROLLER:
 | 
			
		||||
    switch (ev->data.control.param) {
 | 
			
		||||
    /* Pots */
 | 
			
		||||
    case 1:
 | 
			
		||||
      set_sensitivity(destChannel, ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
    case 2:
 | 
			
		||||
      set_luminosity(destChannel, ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
    case 3:
 | 
			
		||||
      set_hue_base(destChannel, ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
    case 4:
 | 
			
		||||
      set_hue_interval(destChannel, ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
    case 5:
 | 
			
		||||
      set_gravity(ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
    case 6:
 | 
			
		||||
      set_saturation(destChannel, ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
    case 7:
 | 
			
		||||
      set_hue_auto_shift_interval(destChannel, ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
    case 8:
 | 
			
		||||
      set_hue_auto_shift_speed(destChannel, ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    /* Pads in CC */
 | 
			
		||||
    case 26:
 | 
			
		||||
      set_hue_auto_shift(ev->data.control.value != 0);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      log_debug("CONTROLLER : ch %#04x - param %u - value %d", ev->data.control.channel,
 | 
			
		||||
                ev->data.control.param, ev->data.control.value);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    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)) {
 | 
			
		||||
      log_debug("New port %d:%d", newport->client, newport->port);
 | 
			
		||||
      subscribe_midi_controller(*newport);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								RpiLedBars/backend/src/rpi_midi_controller.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								RpiLedBars/backend/src/rpi_midi_controller.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +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__ */
 | 
			
		||||
							
								
								
									
										202
									
								
								RpiLedBars/backend/src/rpi_param.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								RpiLedBars/backend/src/rpi_param.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
/** @file .c
 | 
			
		||||
 *  @brief This module
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "rpi_param.h"
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
#include "drivers/selector/rpi_selector.h"
 | 
			
		||||
#include "websocket.h"
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
pixled_param_t const dummyPixledParam = {.mode = 0, .chanLedCount = 0};
 | 
			
		||||
auton_param_t const dummyAutonParam = {
 | 
			
		||||
    .sensitivity = 10, .gravity = 80, .pattern = 0, .isHueAutoShiftEnabled = true};
 | 
			
		||||
ledbar_param_t const dummyLedbarParam = {.sensitivity = 1.,
 | 
			
		||||
                                         .hueBase = 121,
 | 
			
		||||
                                         .hueInterval = -100,
 | 
			
		||||
                                         .hueAutoShift = 0,
 | 
			
		||||
                                         .hueAutoShiftInterval = 0,
 | 
			
		||||
                                         .hueAutoShiftSpeed = 1.,
 | 
			
		||||
                                         .isIncreasing = false,
 | 
			
		||||
                                         .luminosity = 20,
 | 
			
		||||
                                         .saturation = 180};
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Persistent Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
param_t param;
 | 
			
		||||
 | 
			
		||||
pthread_mutex_t paramLockMutex = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
void param_setup() {
 | 
			
		||||
  param.pixled = dummyPixledParam;
 | 
			
		||||
  param.auton = dummyAutonParam;
 | 
			
		||||
  for (size_t i = 0; i < LED_NCHANS; ++i) {
 | 
			
		||||
    param.ledbar[i] = dummyLedbarParam;
 | 
			
		||||
  }
 | 
			
		||||
  param_access = ¶m;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_mode(unsigned int mode) {
 | 
			
		||||
  if (mode != param.pixled.mode) {
 | 
			
		||||
    pthread_mutex_lock(¶mLockMutex);
 | 
			
		||||
    param.pixled.mode = mode;
 | 
			
		||||
    log_debug("Mode : %d", mode);
 | 
			
		||||
    websocket_send_mode(mode);
 | 
			
		||||
    pthread_mutex_unlock(¶mLockMutex);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_pattern(unsigned int pattern) {
 | 
			
		||||
  if (pattern != param.auton.pattern) {
 | 
			
		||||
    pthread_mutex_lock(¶mLockMutex);
 | 
			
		||||
    param.auton.pattern = pattern;
 | 
			
		||||
    log_debug("Pattern : %d", pattern);
 | 
			
		||||
    pthread_mutex_unlock(¶mLockMutex);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_gravity(int8_t gravity8) {
 | 
			
		||||
  param.auton.gravity = gravity8 * 100 / INT8_MAX;
 | 
			
		||||
  log_debug("Gravity : %d", param.auton.gravity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_hue_auto_shift(bool HasToBeEnabled) {
 | 
			
		||||
  if (HasToBeEnabled != param.auton.isHueAutoShiftEnabled) {
 | 
			
		||||
    param.auton.isHueAutoShiftEnabled = HasToBeEnabled;
 | 
			
		||||
    log_debug("Hue auto shift : %s", HasToBeEnabled ? "Enabled" : "Disabled");
 | 
			
		||||
    if (!HasToBeEnabled) {
 | 
			
		||||
      for (size_t i = 0; i < LED_NCHANS; ++i) {
 | 
			
		||||
        param.ledbar[i].hueAutoShift = 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_sensitivity(int channel, int8_t sensitivity8) {
 | 
			
		||||
  float sensitivity = pow(1.065, sensitivity8 - INT8_MAX / 2);
 | 
			
		||||
  if (channel >= LED_NCHANS) {
 | 
			
		||||
    param.auton.sensitivity = sensitivity;
 | 
			
		||||
  } else {
 | 
			
		||||
    param.ledbar[channel].sensitivity = sensitivity;
 | 
			
		||||
  }
 | 
			
		||||
  log_debug("Sensitivity : %f", sensitivity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_hue_base(int channel, int8_t hueBase8) {
 | 
			
		||||
  uint16_t hueBase = hueBase8 * HUEBASE_MAX / INT8_MAX;
 | 
			
		||||
  if (channel < LED_NCHANS) {
 | 
			
		||||
    param.ledbar[channel].hueBase = hueBase;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (size_t i = 0; i < LED_NCHANS; ++i) {
 | 
			
		||||
      param.ledbar[i].hueBase = hueBase;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  log_debug("Hue base : %u", hueBase);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_hue_interval(int channel, int8_t hueInterval8) {
 | 
			
		||||
  int16_t hueInterval = hueInterval8 - ((INT8_MAX + 1) / 2);
 | 
			
		||||
  if (-30 <= hueInterval && hueInterval <= 30) {
 | 
			
		||||
    hueInterval = hueInterval * (HUEINTERVAL_MAX + 1) / 30;
 | 
			
		||||
  } else if (hueInterval <= -30) {
 | 
			
		||||
    hueInterval = 22 * (hueInterval + 30) - HUEINTERVAL_MAX;
 | 
			
		||||
  } else {
 | 
			
		||||
    hueInterval = 22 * (hueInterval - 30) + HUEINTERVAL_MAX;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (channel < LED_NCHANS) {
 | 
			
		||||
    param.ledbar[channel].hueInterval = hueInterval;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (size_t i = 0; i < LED_NCHANS; ++i) {
 | 
			
		||||
      param.ledbar[i].hueInterval = hueInterval;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  log_debug("Hue interval [%2d] : %d", channel, hueInterval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_hue_auto_shift_interval(int channel, int8_t hueInterval8) {
 | 
			
		||||
  int16_t hueInterval = hueInterval8 - ((INT8_MAX + 1) / 2);
 | 
			
		||||
  if (-30 <= hueInterval && hueInterval <= 30) {
 | 
			
		||||
    hueInterval = hueInterval * (HUEINTERVAL_MAX + 1) / 30;
 | 
			
		||||
  } else if (hueInterval <= -30) {
 | 
			
		||||
    hueInterval = 22 * (hueInterval + 30) - HUEINTERVAL_MAX;
 | 
			
		||||
  } else {
 | 
			
		||||
    hueInterval = 22 * (hueInterval - 30) + HUEINTERVAL_MAX;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (channel < LED_NCHANS) {
 | 
			
		||||
    param.ledbar[channel].hueAutoShiftInterval = hueInterval;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (size_t i = 0; i < LED_NCHANS; ++i) {
 | 
			
		||||
      param.ledbar[i].hueAutoShiftInterval = hueInterval;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  log_debug("Hue auto shift interval [%2d] : %d", channel, hueInterval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_hue_auto_shift_speed(int channel, int8_t hueSpeed8) {
 | 
			
		||||
  float hueSpeed = pow(1.065, hueSpeed8 - INT8_MAX / 2);
 | 
			
		||||
  if (channel < LED_NCHANS) {
 | 
			
		||||
    param.ledbar[channel].hueAutoShiftSpeed = hueSpeed;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (size_t i = 0; i < LED_NCHANS; ++i) {
 | 
			
		||||
      param.ledbar[i].hueAutoShiftSpeed = hueSpeed;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  log_debug("Hue auto shift speed [%2d] : %d", channel, hueSpeed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_luminosity(int channel, int8_t luminosity8) {
 | 
			
		||||
  uint8_t luminosity = luminosity8 * UINT8_MAX / INT8_MAX;
 | 
			
		||||
  if (channel < LED_NCHANS) {
 | 
			
		||||
    param.ledbar[channel].luminosity = luminosity;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (size_t i = 0; i < LED_NCHANS; ++i) {
 | 
			
		||||
      param.ledbar[i].luminosity = luminosity;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  log_debug("Luminosity : %u", luminosity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_saturation(int channel, int8_t saturation8) {
 | 
			
		||||
  uint8_t saturation = saturation8 * UINT8_MAX / INT8_MAX;
 | 
			
		||||
  if (channel < LED_NCHANS) {
 | 
			
		||||
    param.ledbar[channel].saturation = saturation;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (size_t i = 0; i < LED_NCHANS; ++i) {
 | 
			
		||||
      param.ledbar[i].saturation = saturation;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  log_debug("Saturation : %u", saturation);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
							
								
								
									
										144
									
								
								RpiLedBars/backend/src/rpi_param.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								RpiLedBars/backend/src/rpi_param.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
/** @file .h
 | 
			
		||||
 *  @brief This module
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if !defined(__RPI_PARAM_H__)
 | 
			
		||||
#define __RPI_PARAM_H__
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define CHAN_MAXLEDS 60 // Maximum number of LEDs per channel
 | 
			
		||||
#define LED_NCHANS 8    // Number of LED channels (8 or 16)
 | 
			
		||||
 | 
			
		||||
#define GLOBAL_CHANNEL 15
 | 
			
		||||
 | 
			
		||||
#define HUE_MAX 359
 | 
			
		||||
#define HUEBASE_MIN 0
 | 
			
		||||
#define HUEBASE_MAX HUE_MAX
 | 
			
		||||
#define HUEINTERVAL_MIN -HUE_MAX
 | 
			
		||||
#define HUEINTERVAL_MAX HUE_MAX
 | 
			
		||||
#define LUMINOSITY_MIN 0
 | 
			
		||||
#define LUMINOSITY_MAX 255
 | 
			
		||||
#define SATURATION_MIN 0
 | 
			
		||||
#define SATURATION_MAX 255
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
typedef struct {
 | 
			
		||||
  int mode;
 | 
			
		||||
  unsigned int chanLedCount;
 | 
			
		||||
} pixled_param_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
typedef struct {
 | 
			
		||||
  float sensitivity;
 | 
			
		||||
  unsigned int gravity;
 | 
			
		||||
  unsigned int pattern;
 | 
			
		||||
  bool isHueAutoShiftEnabled;
 | 
			
		||||
} auton_param_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
typedef struct {
 | 
			
		||||
  float sensitivity;
 | 
			
		||||
  unsigned int hueBase;
 | 
			
		||||
  int hueInterval;
 | 
			
		||||
  unsigned int hueAutoShift;
 | 
			
		||||
  int hueAutoShiftInterval;
 | 
			
		||||
  float hueAutoShiftSpeed;
 | 
			
		||||
  bool isIncreasing;
 | 
			
		||||
  unsigned int luminosity;
 | 
			
		||||
  unsigned int saturation;
 | 
			
		||||
} ledbar_param_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
typedef struct {
 | 
			
		||||
  pixled_param_t pixled;
 | 
			
		||||
  auton_param_t auton;
 | 
			
		||||
  ledbar_param_t ledbar[LED_NCHANS];
 | 
			
		||||
} param_t;
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Global Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
param_t *param_access;
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 */
 | 
			
		||||
void param_setup();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] mode
 | 
			
		||||
 */
 | 
			
		||||
void set_mode(unsigned int mode);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] pattern
 | 
			
		||||
 */
 | 
			
		||||
void set_pattern(unsigned int pattern);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] HasToBeEnabled
 | 
			
		||||
 */
 | 
			
		||||
void set_hue_auto_shift(bool HasToBeEnabled);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] gravity8
 | 
			
		||||
 */
 | 
			
		||||
void set_gravity(int8_t gravity8);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] channel
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] sensitivity8
 | 
			
		||||
 */
 | 
			
		||||
void set_sensitivity(int channel, int8_t sensitivity8);
 | 
			
		||||
 | 
			
		||||
void set_hue_base(int channel, int8_t hueBase8);
 | 
			
		||||
 | 
			
		||||
void set_hue_interval(int channel, int8_t hueInterval8);
 | 
			
		||||
 | 
			
		||||
void set_hue_auto_shift_interval(int channel, int8_t hueInterval8);
 | 
			
		||||
 | 
			
		||||
void set_hue_auto_shift_speed(int channel, int8_t hueSpeed8);
 | 
			
		||||
 | 
			
		||||
void set_luminosity(int channel, int8_t luminosity8);
 | 
			
		||||
 | 
			
		||||
void set_saturation(int channel, int8_t saturation8);
 | 
			
		||||
 | 
			
		||||
#endif /* __RPI_PARAM_H__ */
 | 
			
		||||
							
								
								
									
										259
									
								
								RpiLedBars/backend/src/rpi_pattern.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								RpiLedBars/backend/src/rpi_pattern.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,259 @@
 | 
			
		||||
/** @file .c
 | 
			
		||||
 *  @brief This module
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "rpi_pattern.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
#include "drivers/leddriver/rpi_leddriver.h"
 | 
			
		||||
#include "rpi_param.h"
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Persistent Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Index to channel translattion
 | 
			
		||||
 */
 | 
			
		||||
int channelTranslation[LED_NCHANS] = {0, 1, 2, 3, 4, 6, 7, 5};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * RGB data buffer. Persistency is not needed but, as of it size, it must be declared in heap
 | 
			
		||||
 */
 | 
			
		||||
int rgbData[CHAN_MAXLEDS][LED_NCHANS];
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Number of led previously lighted per led bar
 | 
			
		||||
 */
 | 
			
		||||
int lightedLedCountArray[LED_NCHANS];
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Index of first previously lighted per led bar
 | 
			
		||||
 */
 | 
			
		||||
int baseLedIndexArray[LED_NCHANS];
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
uint16_t get_max_on_interval(uint16_t *array, unsigned int length, size_t intervalMin,
 | 
			
		||||
                             size_t intervalSize);
 | 
			
		||||
 | 
			
		||||
uint16_t apply_sensitivity(uint16_t value, unsigned int valueIndex);
 | 
			
		||||
 | 
			
		||||
unsigned int get_led_to_light_count(uint16_t barValue, unsigned int barIndex);
 | 
			
		||||
 | 
			
		||||
unsigned int get_first_led_to_light(unsigned int barIndex);
 | 
			
		||||
 | 
			
		||||
uint32_t get_hsv_color(unsigned int barIndex, float shiftRatio);
 | 
			
		||||
 | 
			
		||||
void increment_autoshift(unsigned int barIndex);
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
void bounce_led_and_color(uint16_t *spectrumArray, unsigned int spectrumLength) {
 | 
			
		||||
  for (size_t ledBarIndex = 0; ledBarIndex < LED_NCHANS; ++ledBarIndex) {
 | 
			
		||||
    size_t intervalSize = spectrumLength / LED_NCHANS;
 | 
			
		||||
    uint16_t barMax = 0;
 | 
			
		||||
    unsigned int ledToLightCount = 0;
 | 
			
		||||
 | 
			
		||||
    barMax = get_max_on_interval(spectrumArray, spectrumLength, ledBarIndex * intervalSize,
 | 
			
		||||
                                 intervalSize);
 | 
			
		||||
    barMax = apply_sensitivity(barMax, ledBarIndex);
 | 
			
		||||
    ledToLightCount = get_led_to_light_count(barMax, ledBarIndex);
 | 
			
		||||
 | 
			
		||||
    uint32_t color = get_hsv_color(ledBarIndex, (ledToLightCount - 1) /
 | 
			
		||||
                                                    (float)param_access->pixled.chanLedCount);
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < ledToLightCount; ++i) {
 | 
			
		||||
      rgbData[i][ledBarIndex] = color;
 | 
			
		||||
      rgb_txdata(rgbData[i], i);
 | 
			
		||||
    }
 | 
			
		||||
    for (size_t i = ledToLightCount; i < param_access->pixled.chanLedCount; ++i) {
 | 
			
		||||
      rgbData[i][ledBarIndex] = 0x000000;
 | 
			
		||||
      rgb_txdata(rgbData[i], i);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    increment_autoshift(ledBarIndex);
 | 
			
		||||
  }
 | 
			
		||||
  leddriver_refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bounce_led(uint16_t *spectrumArray, unsigned int spectrumLength) {
 | 
			
		||||
  for (size_t ledBarIndex = 0; ledBarIndex < LED_NCHANS; ++ledBarIndex) {
 | 
			
		||||
    size_t intervalSize = spectrumLength / LED_NCHANS;
 | 
			
		||||
    uint16_t barMax = 0;
 | 
			
		||||
    unsigned int ledToLightCount = 0;
 | 
			
		||||
 | 
			
		||||
    barMax = get_max_on_interval(spectrumArray, spectrumLength, ledBarIndex * intervalSize,
 | 
			
		||||
                                 intervalSize);
 | 
			
		||||
    barMax = apply_sensitivity(barMax, ledBarIndex);
 | 
			
		||||
    ledToLightCount = get_led_to_light_count(barMax, ledBarIndex);
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < ledToLightCount; ++i) {
 | 
			
		||||
 | 
			
		||||
      uint32_t color = get_hsv_color(ledBarIndex, i / (float)param_access->pixled.chanLedCount);
 | 
			
		||||
      rgbData[i][ledBarIndex] = color;
 | 
			
		||||
      rgb_txdata(rgbData[i], i);
 | 
			
		||||
    }
 | 
			
		||||
    for (size_t i = ledToLightCount; i < param_access->pixled.chanLedCount; ++i) {
 | 
			
		||||
      rgbData[i][ledBarIndex] = 0x000000;
 | 
			
		||||
      rgb_txdata(rgbData[i], i);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    increment_autoshift(ledBarIndex);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  leddriver_refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bounce_led_and_travel(uint16_t *spectrumArray, unsigned int spectrumLength) {
 | 
			
		||||
  for (size_t ledBarIndex = 0; ledBarIndex < LED_NCHANS; ++ledBarIndex) {
 | 
			
		||||
    size_t intervalSize = spectrumLength / LED_NCHANS;
 | 
			
		||||
    uint16_t barMax = 0;
 | 
			
		||||
    unsigned int ledToLightCount = 0, firstLedToLight = get_first_led_to_light(ledBarIndex);
 | 
			
		||||
    uint32_t color = 0;
 | 
			
		||||
 | 
			
		||||
    barMax = get_max_on_interval(spectrumArray, spectrumLength, ledBarIndex * intervalSize,
 | 
			
		||||
                                 intervalSize);
 | 
			
		||||
    barMax = apply_sensitivity(barMax, ledBarIndex) / 4;
 | 
			
		||||
    ledToLightCount = get_led_to_light_count(barMax, ledBarIndex) + 1;
 | 
			
		||||
 | 
			
		||||
    if (!param_access->auton.isHueAutoShiftEnabled) {
 | 
			
		||||
      color = get_hsv_color(ledBarIndex,
 | 
			
		||||
                            (ledToLightCount - 1) * 4L / (float)param_access->pixled.chanLedCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < ledToLightCount; ++i) {
 | 
			
		||||
      unsigned int ledIndex = firstLedToLight + i;
 | 
			
		||||
      ledIndex = ledIndex < param_access->pixled.chanLedCount
 | 
			
		||||
                     ? ledIndex
 | 
			
		||||
                     : ledIndex - param_access->pixled.chanLedCount;
 | 
			
		||||
      if (param_access->auton.isHueAutoShiftEnabled) {
 | 
			
		||||
        color = get_hsv_color(ledBarIndex, ledIndex / (float)param_access->pixled.chanLedCount);
 | 
			
		||||
      }
 | 
			
		||||
      rgbData[ledIndex][ledBarIndex] = color;
 | 
			
		||||
      rgb_txdata(rgbData[ledIndex], ledIndex);
 | 
			
		||||
    }
 | 
			
		||||
    for (size_t i = ledToLightCount; i < param_access->pixled.chanLedCount; ++i) {
 | 
			
		||||
      unsigned int ledIndex = firstLedToLight + i;
 | 
			
		||||
      ledIndex = ledIndex < param_access->pixled.chanLedCount
 | 
			
		||||
                     ? ledIndex
 | 
			
		||||
                     : ledIndex - param_access->pixled.chanLedCount;
 | 
			
		||||
      rgbData[ledIndex][ledBarIndex] = 0x000000;
 | 
			
		||||
      rgb_txdata(rgbData[ledIndex], ledIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    increment_autoshift(ledBarIndex);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  leddriver_refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
uint16_t get_max_on_interval(uint16_t *array, unsigned int length, size_t intervalMin,
 | 
			
		||||
                             size_t intervalSize) {
 | 
			
		||||
  uint16_t barMax = 0;
 | 
			
		||||
  for (size_t i = 0; i < intervalSize; ++i) {
 | 
			
		||||
    unsigned barIndex = intervalMin + i;
 | 
			
		||||
    if (barMax < array[barIndex]) {
 | 
			
		||||
      barMax = array[barIndex];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return barMax;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t apply_sensitivity(uint16_t value, unsigned int valueIndex) {
 | 
			
		||||
  return value * param_access->auton.sensitivity * param_access->ledbar[valueIndex].sensitivity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int get_led_to_light_count(uint16_t barValue, unsigned int barIndex) {
 | 
			
		||||
  unsigned int nbLightedLed = lightedLedCountArray[barIndex];
 | 
			
		||||
  unsigned int ledToLightCount = barValue * param_access->pixled.chanLedCount / UINT16_MAX;
 | 
			
		||||
 | 
			
		||||
  if (ledToLightCount < nbLightedLed) {
 | 
			
		||||
    /* apply gravity */
 | 
			
		||||
    ledToLightCount = ((ledToLightCount * (100 - param_access->auton.gravity)) +
 | 
			
		||||
                       (nbLightedLed * param_access->auton.gravity)) /
 | 
			
		||||
                      100;
 | 
			
		||||
  }
 | 
			
		||||
  lightedLedCountArray[barIndex] = ledToLightCount;
 | 
			
		||||
  return ledToLightCount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int get_first_led_to_light(unsigned int barIndex) {
 | 
			
		||||
  unsigned int firstLed = baseLedIndexArray[barIndex];
 | 
			
		||||
  if (lightedLedCountArray[barIndex] != 0) {
 | 
			
		||||
    firstLed += lightedLedCountArray[barIndex] / 4 + 1;
 | 
			
		||||
    firstLed = firstLed < param_access->pixled.chanLedCount
 | 
			
		||||
                   ? firstLed
 | 
			
		||||
                   : firstLed - param_access->pixled.chanLedCount;
 | 
			
		||||
  }
 | 
			
		||||
  baseLedIndexArray[barIndex] = firstLed;
 | 
			
		||||
  return firstLed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t get_hsv_color(unsigned int barIndex, float shiftRatio) {
 | 
			
		||||
  int hueShift = shiftRatio * param_access->ledbar[barIndex].hueInterval;
 | 
			
		||||
  uint16_t hue = param_access->ledbar[barIndex].hueBase +
 | 
			
		||||
                 (param_access->ledbar[barIndex].hueAutoShift) + hueShift;
 | 
			
		||||
  // log_trace("hueAutoShift : %010x, hueShift : %d, hue %u",
 | 
			
		||||
  //           param_access->ledbar[barIndex].hueAutoShift, hueShift, hue);
 | 
			
		||||
  uint32_t color =
 | 
			
		||||
      ColorHSV(hue * UINT16_MAX / HUEBASE_MAX, param_access->ledbar[barIndex].saturation,
 | 
			
		||||
               param_access->ledbar[barIndex].luminosity);
 | 
			
		||||
  return color;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void increment_autoshift(unsigned int barIndex) {
 | 
			
		||||
  static float increment[LED_NCHANS];
 | 
			
		||||
  if (param_access->auton.isHueAutoShiftEnabled) {
 | 
			
		||||
    int const hueInterval = param_access->ledbar[barIndex].hueAutoShiftInterval;
 | 
			
		||||
    int hueAutoShift = param_access->ledbar[barIndex].hueAutoShift;
 | 
			
		||||
    if ((hueInterval > 0 && param_access->ledbar[barIndex].isIncreasing) ||
 | 
			
		||||
        (hueInterval < 0 && !param_access->ledbar[barIndex].isIncreasing)) {
 | 
			
		||||
      increment[barIndex] += param_access->ledbar[barIndex].hueAutoShiftSpeed;
 | 
			
		||||
    } else if (hueInterval != 0) {
 | 
			
		||||
      increment[barIndex] -= param_access->ledbar[barIndex].hueAutoShiftSpeed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (abs(increment[barIndex]) > 1) {
 | 
			
		||||
      hueAutoShift += increment[barIndex];
 | 
			
		||||
      increment[barIndex] = increment[barIndex] - (int)increment[barIndex];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((hueInterval > 0 && hueAutoShift < 0) || (hueInterval < 0 && hueAutoShift > 0)) {
 | 
			
		||||
      hueAutoShift = -hueAutoShift;
 | 
			
		||||
      param_access->ledbar[barIndex].isIncreasing = !param_access->ledbar[barIndex].isIncreasing;
 | 
			
		||||
    } else if ((hueInterval > 0 && hueAutoShift > hueInterval) ||
 | 
			
		||||
               (hueInterval < 0 && hueAutoShift < hueInterval)) {
 | 
			
		||||
      hueAutoShift = hueInterval;
 | 
			
		||||
      param_access->ledbar[barIndex].isIncreasing = !param_access->ledbar[barIndex].isIncreasing;
 | 
			
		||||
    }
 | 
			
		||||
    if (barIndex == 0) {
 | 
			
		||||
      log_trace("hueInterval : %d, hueAutoShift : %d, increase : %s", hueInterval, hueAutoShift,
 | 
			
		||||
                param_access->ledbar[barIndex].isIncreasing ? "yes" : "no");
 | 
			
		||||
    }
 | 
			
		||||
    param_access->ledbar[barIndex].hueAutoShift = hueAutoShift;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								RpiLedBars/backend/src/rpi_pattern.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								RpiLedBars/backend/src/rpi_pattern.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
/** @file .h
 | 
			
		||||
 *  @brief This module
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if !defined(__RPI_PATTERN_H__)
 | 
			
		||||
#define __RPI_PATTERN_H__
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Global Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] spectrumArray array of value from spectral analysis
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] spectrumLength length of spectrum array
 | 
			
		||||
 */
 | 
			
		||||
void bounce_led_and_color(uint16_t *spectrumArray, unsigned int spectrumLength);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] spectrumArray array of value from spectral analysis
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] spectrumLength length of spectrum array
 | 
			
		||||
 */
 | 
			
		||||
void bounce_led(uint16_t *spectrumArray, unsigned int spectrumLength);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] spectrumArray array of value from spectral analysis
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] spectrumLength length of spectrum array
 | 
			
		||||
 */
 | 
			
		||||
void bounce_led_and_travel(uint16_t *spectrumArray, unsigned int spectrumLength);
 | 
			
		||||
 | 
			
		||||
#endif /* __RPI_PATTERN_H__ */
 | 
			
		||||
							
								
								
									
										11
									
								
								RpiLedBars/backend/src/tasks/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								RpiLedBars/backend/src/tasks/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
# add the executable
 | 
			
		||||
add_library(${PROJECT_NAME}_tasks
 | 
			
		||||
    artnet/artnet.c
 | 
			
		||||
    cava/cava.c
 | 
			
		||||
    selector/selector.c
 | 
			
		||||
    websocket/websocket.c)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(${PROJECT_NAME}_tasks PRIVATE pthread)
 | 
			
		||||
target_link_libraries(${PROJECT_NAME}_tasks PRIVATE logc ws)
 | 
			
		||||
 | 
			
		||||
target_include_directories(${PROJECT_NAME}_tasks PUBLIC includes)
 | 
			
		||||
							
								
								
									
										198
									
								
								RpiLedBars/backend/src/tasks/artnet/artnet.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								RpiLedBars/backend/src/tasks/artnet/artnet.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
/** @file rpi_artnet.c
 | 
			
		||||
 *  @brief This module contains continuous tasks for artnet node communications.
 | 
			
		||||
 *
 | 
			
		||||
 * This is the implementation file.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
#include "artnet.h"
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "artnet_packets.h"
 | 
			
		||||
#include "artnet_utils.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Persistent Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Boolean for interrupting the main thread loop
 | 
			
		||||
 */
 | 
			
		||||
bool isUdpListenerRunning = false;
 | 
			
		||||
/**
 | 
			
		||||
 * Task thread identifier
 | 
			
		||||
 */
 | 
			
		||||
pthread_t udpListener;
 | 
			
		||||
/**
 | 
			
		||||
 * Buffer for storing artnet dmx data received
 | 
			
		||||
 */
 | 
			
		||||
uint8_t artDmxBufferArray[16][512];
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief This function is used to thread main execution function
 | 
			
		||||
 *
 | 
			
		||||
 * @param   arg not used.
 | 
			
		||||
 *
 | 
			
		||||
 * @return  NULL.
 | 
			
		||||
 */
 | 
			
		||||
static void *artnet_udp_handler(void *arg);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief This function is used to send an artnet poll reply packet
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in]   fdUdpSocket The UDP socket file descriptor.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in]   senderAddr  The adress of poll emiter.
 | 
			
		||||
 */
 | 
			
		||||
static void artnet_send_poll_reply(int fdUdpSocket, struct sockaddr_in senderAddr);
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
void artnet_start() {
 | 
			
		||||
  isUdpListenerRunning = true;
 | 
			
		||||
 | 
			
		||||
  if (pthread_create(&udpListener, NULL, artnet_udp_handler, NULL) < 0) {
 | 
			
		||||
    log_error("pthread_create: %s", strerror(errno));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void artnet_stop() {
 | 
			
		||||
  isUdpListenerRunning = false;
 | 
			
		||||
 | 
			
		||||
  if (pthread_join(udpListener, NULL) != 0) {
 | 
			
		||||
    log_error("pthread_join: %s", strerror(errno));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int artnet_get_dmx_data(unsigned int univerve, uint8_t *dmxData[]) {
 | 
			
		||||
  if (univerve > 8) {
 | 
			
		||||
    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];
 | 
			
		||||
  int timeoutMs;
 | 
			
		||||
  int flags;
 | 
			
		||||
  char buffer[ARTNET_MAX_BUFFER];
 | 
			
		||||
  struct sockaddr_in serverAddr, srcAddr;
 | 
			
		||||
  socklen_t srcLen = sizeof(struct sockaddr_in);
 | 
			
		||||
 | 
			
		||||
  /* Create UDP socket */
 | 
			
		||||
  fdUdpSocket = socket(PF_INET, SOCK_DGRAM, 0);
 | 
			
		||||
  if (fdUdpSocket < 0) {
 | 
			
		||||
    log_error("Opening socket failed: %s", strerror(errno));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Set non-blocking socket */
 | 
			
		||||
  flags = fcntl(fdUdpSocket, F_GETFL, 0);
 | 
			
		||||
  fcntl(fdUdpSocket, F_SETFL, flags | O_NONBLOCK);
 | 
			
		||||
 | 
			
		||||
  /* pollfd structure and timeout */
 | 
			
		||||
  memset(pollFdArray, 0, sizeof(pollFdArray));
 | 
			
		||||
  pollFdArray[0].fd = fdUdpSocket;
 | 
			
		||||
  pollFdArray[0].events = POLLIN;
 | 
			
		||||
  timeoutMs = 10;
 | 
			
		||||
 | 
			
		||||
  /* Configure settings in address struct */
 | 
			
		||||
  serverAddr.sin_family = AF_INET;
 | 
			
		||||
  serverAddr.sin_port = htons(ARTNET_PORT);
 | 
			
		||||
  serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
 | 
			
		||||
  memset(serverAddr.sin_zero, '\0', sizeof(serverAddr.sin_zero));
 | 
			
		||||
 | 
			
		||||
  /* Bind socket with address struct */
 | 
			
		||||
  bind(fdUdpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
 | 
			
		||||
 | 
			
		||||
  while (isUdpListenerRunning) {
 | 
			
		||||
 | 
			
		||||
    int pollReturn;
 | 
			
		||||
    if ((pollReturn = poll(pollFdArray, 1, timeoutMs)) == 1) {
 | 
			
		||||
      ssize_t bufferLen =
 | 
			
		||||
          recvfrom(fdUdpSocket, buffer, ARTNET_MAX_BUFFER, 0, (struct sockaddr *)&srcAddr, &srcLen);
 | 
			
		||||
 | 
			
		||||
      if (bufferLen <= ARTNET_MAX_BUFFER && bufferLen > sizeof(artnetHeader_t)) {
 | 
			
		||||
        artnetHeader_t *artnetHeader = (artnetHeader_t *)buffer;
 | 
			
		||||
 | 
			
		||||
        if (memcmp(artnetHeader->id, ARTNET_ID, sizeof(ARTNET_ID)) == 0) {
 | 
			
		||||
          switch (artnetHeader->opCode) {
 | 
			
		||||
          case OpDmx:
 | 
			
		||||
            if (bufferLen >= 20) {
 | 
			
		||||
              artDmx_t *artDmx = (artDmx_t *)buffer;
 | 
			
		||||
              uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
 | 
			
		||||
              uint8_t *artDmxBuffer = artDmxBufferArray[artDmx->subUni & 0x00ff];
 | 
			
		||||
              if (dmxLength <= 512) {
 | 
			
		||||
                // store for later use
 | 
			
		||||
                memcpy(artDmxBuffer, artDmx->data, dmxLength);
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
          case OpPoll:
 | 
			
		||||
            artnet_send_poll_reply(fdUdpSocket, srcAddr);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
          default:
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    } else if (pollReturn < 0) {
 | 
			
		||||
      log_error("error polling %d: %s", fdUdpSocket, strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  close(fdUdpSocket);
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void artnet_send_poll_reply(int fdUdpSocket, struct sockaddr_in senderAddr) {
 | 
			
		||||
  /* Configure settings in address struct */
 | 
			
		||||
  senderAddr.sin_family = AF_INET;
 | 
			
		||||
  senderAddr.sin_port = htons(ARTNET_PORT);
 | 
			
		||||
  memset(senderAddr.sin_zero, '\0', sizeof(senderAddr.sin_zero));
 | 
			
		||||
 | 
			
		||||
  if (sendto(fdUdpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0,
 | 
			
		||||
             (struct sockaddr *)&senderAddr, sizeof(senderAddr)) < 0) {
 | 
			
		||||
    log_error("%s: sending poll reply to \"%s:%d\": %s", inet_ntoa(senderAddr.sin_addr),
 | 
			
		||||
              senderAddr.sin_port, strerror(errno));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										128
									
								
								RpiLedBars/backend/src/tasks/artnet/artnet_op_codes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								RpiLedBars/backend/src/tasks/artnet/artnet_op_codes.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
			
		||||
#if !defined(__RPI_ARTNET_OP_CODES_H__)
 | 
			
		||||
#define __RPI_ARTNET_OP_CODES_H__
 | 
			
		||||
 | 
			
		||||
/* List of hex values and discriptions of Opcodes */
 | 
			
		||||
 | 
			
		||||
/* This is an ArtPoll packet, no other data is contained in this UDP packet */
 | 
			
		||||
#define OpPoll 0x2000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtPollReply Packet. It contains device status information. */
 | 
			
		||||
#define OpPollReply 0x2100
 | 
			
		||||
 | 
			
		||||
/* Diagnostics and data logging packet. */
 | 
			
		||||
#define OpDiagData 0x2300
 | 
			
		||||
 | 
			
		||||
/* Used to send text based parameter commands. */
 | 
			
		||||
#define OpCommand 0x2400
 | 
			
		||||
 | 
			
		||||
/* This is an ArtDmx data packet. It contains zero start code DMX512 information for a single
 | 
			
		||||
 * Universe. */
 | 
			
		||||
#define OpOutput 0x5000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtDmx data packet. It contains zero start code DMX512 information for a single
 | 
			
		||||
 * Universe. */
 | 
			
		||||
#define OpDmx 0x5000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtNzs data packet. It contains non-zero start code (except RDM) DMX512 information
 | 
			
		||||
 * for a single Universe. */
 | 
			
		||||
#define OpNzs 0x5100
 | 
			
		||||
 | 
			
		||||
/* This is an ArtAddress packet. It contains remote programming information for a Node. */
 | 
			
		||||
#define OpAddress 0x6000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtInput packet. It contains enable – disable data for DMX inputs. */
 | 
			
		||||
#define OpInput 0x7000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtTodRequest packet. It is used to request a Table of Devices (ToD) for RDM
 | 
			
		||||
 * discovery. */
 | 
			
		||||
#define OpTodRequest 0x8000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtTodData packet. It is used to send a Table of Devices (ToD) for RDM discovery. */
 | 
			
		||||
#define OpTodData 0x8100
 | 
			
		||||
 | 
			
		||||
/* This is an ArtTodControl packet. It is used to send RDM discovery control messages. */
 | 
			
		||||
#define OpTodControl 0x8200
 | 
			
		||||
 | 
			
		||||
/* This is an ArtRdm packet. It is used to send all non discovery RDM messages. */
 | 
			
		||||
#define OpRdm 0x8300
 | 
			
		||||
 | 
			
		||||
/* This is an ArtRdmSub packet. It is used to send compressed, RDM Sub-Device data. */
 | 
			
		||||
#define OpRdmSub 0x8400
 | 
			
		||||
 | 
			
		||||
/* This is an ArtVideoSetup packet. It contains video screen setup information for nodes that
 | 
			
		||||
 * implement the extended video features. */
 | 
			
		||||
#define OpVideoSetup 0xa010
 | 
			
		||||
 | 
			
		||||
/* This is an ArtVideoPalette packet. It contains colour palette setup information for nodes that
 | 
			
		||||
 * implement the extended video features. */
 | 
			
		||||
#define OpVideoPalette 0xa020
 | 
			
		||||
 | 
			
		||||
/* This is an ArtVideoData packet. It contains display data for nodes that implement the extended
 | 
			
		||||
 * video features. */
 | 
			
		||||
#define OpVideoData 0xa040
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMacMaster packet. It is used to program the Node’s MAC address, Oem device type and
 | 
			
		||||
 * ESTA manufacturer code. This is for factory initialisation of a Node. It is not to be used by
 | 
			
		||||
 * applications. */
 | 
			
		||||
#define OpMacMaster 0xf000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMacSlave packet. It is returned by the node to acknowledge receipt of an
 | 
			
		||||
 * ArtMacMaster packet. */
 | 
			
		||||
#define OpMacSlave 0xf100
 | 
			
		||||
 | 
			
		||||
/* This is an ArtFirmwareMaster packet. It is used to upload new firmware or firmware extensions to
 | 
			
		||||
 * the Node. */
 | 
			
		||||
#define OpFirmwareMaster 0xf200
 | 
			
		||||
 | 
			
		||||
/* This is an ArtFirmwareReply packet. It is returned by the node to acknowledge receipt of an
 | 
			
		||||
 * ArtFirmwareMaster packet or ArtFileTnMaster packet. */
 | 
			
		||||
#define OpFirmwareReply 0xf300
 | 
			
		||||
 | 
			
		||||
/* Uploads user file to node. */
 | 
			
		||||
#define OpFileTnMaster 0xf400
 | 
			
		||||
 | 
			
		||||
/* Downloads user file from node. */
 | 
			
		||||
#define OpFileFnMaster 0xf500
 | 
			
		||||
 | 
			
		||||
/* Node acknowledge for downloads. */
 | 
			
		||||
#define OpFileFnReply 0xf600
 | 
			
		||||
 | 
			
		||||
/* This is an ArtIpProg packet. It is used to reprogramme the IP, Mask and Port address of the Node.
 | 
			
		||||
 */
 | 
			
		||||
#define OpIpProg 0xf800
 | 
			
		||||
 | 
			
		||||
/* This is an ArtIpProgReply packet. It is returned by the node to acknowledge receipt of an
 | 
			
		||||
 * ArtIpProg packet. */
 | 
			
		||||
#define OpIpProgReply 0xf900
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMedia packet. It is Unicast by a Media Server and acted upon by a Controller. */
 | 
			
		||||
#define OpMedia 0x9000
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMediaPatch packet. It is Unicast by a Controller and acted upon by a Media Server.
 | 
			
		||||
 */
 | 
			
		||||
#define OpMediaPatch 0x9100
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMediaControl packet. It is Unicast by a Controller and acted upon by a Media
 | 
			
		||||
 * Server. */
 | 
			
		||||
#define OpMediaControl 0x9200
 | 
			
		||||
 | 
			
		||||
/* This is an ArtMediaControlReply packet. It is Unicast by a Media Server and acted upon by a
 | 
			
		||||
 * Controller. */
 | 
			
		||||
#define OpMediaContrlReply 0x9300
 | 
			
		||||
 | 
			
		||||
/* This is an ArtTimeCode packet. It is used to transport time code over the network. */
 | 
			
		||||
#define OpTimeCode 0x9700
 | 
			
		||||
 | 
			
		||||
/* Used to synchronise real time date and clock */
 | 
			
		||||
#define OpTimeSync 0x9800
 | 
			
		||||
 | 
			
		||||
/* Used to send trigger macros */
 | 
			
		||||
#define OpTrigger 0x9900
 | 
			
		||||
 | 
			
		||||
/* Requests a node's file list */
 | 
			
		||||
#define OpDirectory 0x9a00
 | 
			
		||||
 | 
			
		||||
/* Replies to OpDirectory with file list */
 | 
			
		||||
#define OpDirectoryReply 0x9b00
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_ARTNET_OP_CODES_H__
 | 
			
		||||
							
								
								
									
										178
									
								
								RpiLedBars/backend/src/tasks/artnet/artnet_packets.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								RpiLedBars/backend/src/tasks/artnet/artnet_packets.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
#if !defined(__RPI_ARTNET_PACKETS_H__)
 | 
			
		||||
#define __RPI_ARTNET_PACKETS_H__
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include "artnet_op_codes.h"
 | 
			
		||||
#include "artnet_utils.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  char id[8];
 | 
			
		||||
  uint16_t opCode;
 | 
			
		||||
} __attribute__((__packed__)) artnetHeader_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  artnetHeader_t artnetHeader;
 | 
			
		||||
  uint16_t protVer;
 | 
			
		||||
  uint8_t sequence;
 | 
			
		||||
  uint8_t physical;
 | 
			
		||||
  uint8_t subUni;
 | 
			
		||||
  uint8_t net;
 | 
			
		||||
  uint8_t lengthHi;
 | 
			
		||||
  uint8_t lengthLo;
 | 
			
		||||
  uint8_t data[512];
 | 
			
		||||
} __attribute__((__packed__)) artDmx_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  char ID[8];
 | 
			
		||||
  uint16_t OpCode;
 | 
			
		||||
  uint8_t IPAddr[4];
 | 
			
		||||
  uint16_t Port;
 | 
			
		||||
  uint16_t VersionInfo; // The node's current FIRMWARE VERS
 | 
			
		||||
  // Bits 14-8 of the 15 bit universe number are encoded into the bottom 7 bits of this field.
 | 
			
		||||
  uint8_t NetSwitch;
 | 
			
		||||
  // This is used in combination with SubSwitch and Swin[] or Swout[] to produce the full universe
 | 
			
		||||
  // address. Bits 7-4 of the 15 bit universe number are encoded into the bottom 4 bits of this
 | 
			
		||||
  // field.
 | 
			
		||||
  uint8_t SubSwitch;
 | 
			
		||||
  uint16_t Oem; // Manufacturer code, bit 15 set if
 | 
			
		||||
  // extended features avail
 | 
			
		||||
  uint8_t UbeaVersion; // Firmware version of UBEA
 | 
			
		||||
  uint8_t Status;
 | 
			
		||||
  // bit 0 = 0 UBEA not present
 | 
			
		||||
  // bit 0 = 1 UBEA present
 | 
			
		||||
  // bit 1 = 0 Not capable of RDM (Uni-directional DMX)
 | 
			
		||||
  // bit 1 = 1 Capable of RDM (Bi-directional DMX)
 | 
			
		||||
  // bit 2 = 0 Booted from flash (normal boot)
 | 
			
		||||
  // bit 2 = 1 Booted from ROM (possible error condition)
 | 
			
		||||
  // bit 3 = Not used
 | 
			
		||||
  // bit 54 = 00 Universe programming authority unknown
 | 
			
		||||
  // bit 54 = 01 Universe programming authority set by front panel controls
 | 
			
		||||
  // bit 54 = 10 Universe programming authority set by network
 | 
			
		||||
  // bit 76 = 00 Indicators Normal
 | 
			
		||||
  // bit 76 = 01 Indicators Locate
 | 
			
		||||
  // bit 76 = 10 Indicators Mute
 | 
			
		||||
  uint8_t EstaMan[2]; // ESTA manufacturer id, lo byte
 | 
			
		||||
  char ShortName[18]; // short name defaults to IP
 | 
			
		||||
  char LongName[64];
 | 
			
		||||
  // Text feedback of Node status or errors  also used for debug info
 | 
			
		||||
  char NodeReport[64];
 | 
			
		||||
 | 
			
		||||
  uint8_t NumPortsHi; // 0
 | 
			
		||||
  uint8_t NumPortsLo; // 4 If num i/p ports is dif to output ports, return biggest
 | 
			
		||||
 | 
			
		||||
  uint8_t PortTypes[4];
 | 
			
		||||
  // bit 7 is output
 | 
			
		||||
  // bit 6 is input
 | 
			
		||||
  // bits 0-5 are protocol number (0= DMX, 1=MIDI)
 | 
			
		||||
  // for DMX-Hub ={0xc0,0xc0,0xc0,0xc0};
 | 
			
		||||
 | 
			
		||||
  uint8_t GoodInput[4];
 | 
			
		||||
  // bit 7 is data received
 | 
			
		||||
  // bit 6 is data includes test packets
 | 
			
		||||
  // bit 5 is data includes SIP's
 | 
			
		||||
  // bit 4 is data includes text
 | 
			
		||||
  // bit 3 set is input is disabled
 | 
			
		||||
  // bit 2 is receive errors
 | 
			
		||||
  // bit 1-0 not used, transmitted as zero.
 | 
			
		||||
  // Don't test for zero!
 | 
			
		||||
 | 
			
		||||
  uint8_t GoodOutput[4];
 | 
			
		||||
  // bit 7 is data is transmitting
 | 
			
		||||
  // bit 6 is data includes test packets
 | 
			
		||||
  // bit 5 is data includes SIP's
 | 
			
		||||
  // bit 4 is data includes text
 | 
			
		||||
  // bit 3 output is merging data.
 | 
			
		||||
  // bit 2 set if DMX output short detected on power up
 | 
			
		||||
  // bit 1 set if DMX output merge mode is LTP
 | 
			
		||||
  // bit 0 not used, transmitted as zero.
 | 
			
		||||
 | 
			
		||||
  uint8_t SwIn[4];
 | 
			
		||||
  // Bits 3-0 of the 15 bit universe number are encoded into the low nibble
 | 
			
		||||
  // This is used in combination with SubSwitch and NetSwitch to produce the full universe address.
 | 
			
		||||
  // THIS IS FOR INPUT - ART-NET or DMX
 | 
			
		||||
  // NB ON ART-NET II THESE 4 UNIVERSES WILL BE UNICAST TO.
 | 
			
		||||
 | 
			
		||||
  uint8_t SwOut[4];
 | 
			
		||||
  // Bits 3-0 of the 15 bit universe number are encoded into the low nibble
 | 
			
		||||
  // This is used in combination with SubSwitch and NetSwitch to produce the full universe address.
 | 
			
		||||
  // data belongs
 | 
			
		||||
  // THIS IS FOR OUTPUT - ART-NET or DMX.
 | 
			
		||||
  // NB ON ART-NET II THESE 4 UNIVERSES WILL BE UNICAST TO.
 | 
			
		||||
 | 
			
		||||
  uint8_t SwVideo;
 | 
			
		||||
  // Low nibble is the value of the video
 | 
			
		||||
  // output channel
 | 
			
		||||
 | 
			
		||||
  uint8_t SwMacro;
 | 
			
		||||
  // Bit 0 is Macro input 1
 | 
			
		||||
  // Bit 7 is Macro input 8
 | 
			
		||||
 | 
			
		||||
  uint8_t SwRemote;
 | 
			
		||||
  // Bit 0 is Remote input 1
 | 
			
		||||
  // Bit 7 is Remote input 8
 | 
			
		||||
 | 
			
		||||
  uint8_t Spare1; // Spare, currently zero
 | 
			
		||||
  uint8_t Spare2; // Spare, currently zero
 | 
			
		||||
  uint8_t Spare3; // Spare, currently zero
 | 
			
		||||
  uint8_t Style;  // Set to Style code to describe type of equipment
 | 
			
		||||
 | 
			
		||||
  uint8_t Mac[6]; // Mac Address, zero if info not available
 | 
			
		||||
 | 
			
		||||
  uint8_t BindIp[4]; // If this unit is part of a larger or modular product, this is the IP of the
 | 
			
		||||
                     // root device.
 | 
			
		||||
  uint8_t BindIndex; // Set to zero if no binding, otherwise this number represents the order of
 | 
			
		||||
                     // bound devices. A lower number means closer to root device.
 | 
			
		||||
 | 
			
		||||
  uint8_t Status2;
 | 
			
		||||
  // bit 0 = 0 Node does not support web browser
 | 
			
		||||
  // bit 0 = 1 Node supports web browser configuration
 | 
			
		||||
 | 
			
		||||
  // bit 1 = 0 Node's IP address is manually configured
 | 
			
		||||
  // bit 1 = 1 Node's IP address is DHCP configured
 | 
			
		||||
 | 
			
		||||
  // bit 2 = 0 Node is not DHCP capable
 | 
			
		||||
  // bit 2 = 1 Node is DHCP capable
 | 
			
		||||
 | 
			
		||||
  // bit 2-7 not implemented, transmit as zero
 | 
			
		||||
 | 
			
		||||
  uint8_t Filler[26]; // Filler bytes, currently zero.
 | 
			
		||||
} artPollReply_t;
 | 
			
		||||
 | 
			
		||||
static artPollReply_t const artPollReply = {
 | 
			
		||||
    .ID = "Art-Net",
 | 
			
		||||
    .OpCode = OpPollReply,
 | 
			
		||||
    .IPAddr = {0},
 | 
			
		||||
    .Port = ARTNET_PORT,
 | 
			
		||||
    .VersionInfo = 1,
 | 
			
		||||
    .NetSwitch = 0,
 | 
			
		||||
    .SubSwitch = 0,
 | 
			
		||||
    .Oem = 0x0190,
 | 
			
		||||
    .UbeaVersion = 0,
 | 
			
		||||
    .Status = 0,
 | 
			
		||||
    .EstaMan = {0, 0},
 | 
			
		||||
    .ShortName = "Tropicananass",
 | 
			
		||||
    .LongName = "Tropicananass Artnetnode",
 | 
			
		||||
    .NodeReport = {0},
 | 
			
		||||
    .NumPortsHi = 0,
 | 
			
		||||
    .NumPortsLo = 1,
 | 
			
		||||
    .PortTypes = {0x80, 0, 0, 0},
 | 
			
		||||
    .GoodInput = {0},
 | 
			
		||||
    .GoodOutput = {0},
 | 
			
		||||
    .SwIn = {0},
 | 
			
		||||
    .SwOut = {0},
 | 
			
		||||
    .SwVideo = 0,
 | 
			
		||||
    .SwMacro = 0,
 | 
			
		||||
    .SwRemote = 0,
 | 
			
		||||
    .Spare1 = 0,
 | 
			
		||||
    .Spare2 = 0,
 | 
			
		||||
    .Spare3 = 0,
 | 
			
		||||
    .Style = 0,
 | 
			
		||||
    .Mac = {0},
 | 
			
		||||
    .BindIp = {0},
 | 
			
		||||
    .BindIndex = 0,
 | 
			
		||||
    .Status2 = 0b00000000,
 | 
			
		||||
    .Filler = {0},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_ARTNET_PACKETS_H__
 | 
			
		||||
							
								
								
									
										25
									
								
								RpiLedBars/backend/src/tasks/artnet/artnet_utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								RpiLedBars/backend/src/tasks/artnet/artnet_utils.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
#if !defined(__RPI_ARTNET_UTILS_H__)
 | 
			
		||||
#define __RPI_ARTNET_UTILS_H__
 | 
			
		||||
 | 
			
		||||
#define ARTNET_PORT 0x1936
 | 
			
		||||
 | 
			
		||||
// Buffers
 | 
			
		||||
#define ARTNET_MAX_BUFFER 530
 | 
			
		||||
#define DMX_MAX_BUFFER 512
 | 
			
		||||
 | 
			
		||||
// Packet constants
 | 
			
		||||
#define ARTNET_ID "Art-Net"
 | 
			
		||||
#define ARTNET_DMX_START_LOC 18
 | 
			
		||||
 | 
			
		||||
// Packet confines
 | 
			
		||||
#define ARTNET_SHORT_NAME_MAX_LENGTH 17
 | 
			
		||||
#define ARTNET_LONG_NAME_MAX_LENGTH 63
 | 
			
		||||
 | 
			
		||||
// DMX settings
 | 
			
		||||
#define DMX_MAX_OUTPUTS 4
 | 
			
		||||
#define DMX_MS_BETWEEN_TICKS 25
 | 
			
		||||
 | 
			
		||||
// RDM
 | 
			
		||||
#define DMX_RDM_STARTCODE 0xCC
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_ARTNET_UTILS_H__
 | 
			
		||||
							
								
								
									
										225
									
								
								RpiLedBars/backend/src/tasks/cava/cava.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								RpiLedBars/backend/src/tasks/cava/cava.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,225 @@
 | 
			
		||||
/** @file rpi_cava.h
 | 
			
		||||
 *  @brief This module contains continuous tasks for cava (spectrum analyzer) comunication
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
#include "cava.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <wait.h>
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Constant for Maximum char in a line (128 number of (5 char + 1 delim) + \n )
 | 
			
		||||
 */
 | 
			
		||||
#define MAXLINECHAR CAVA_BAR_NUMBER *(5 + 1) + 1
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Persistent Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Cava process id
 | 
			
		||||
 */
 | 
			
		||||
pid_t cavaPid;
 | 
			
		||||
/**
 | 
			
		||||
 * Boolean for interrupting the main thread loop
 | 
			
		||||
 */
 | 
			
		||||
bool isFifoReaderRunning = false;
 | 
			
		||||
/**
 | 
			
		||||
 * Task thread identifier
 | 
			
		||||
 */
 | 
			
		||||
pthread_t fifoReader;
 | 
			
		||||
 | 
			
		||||
int fdCavaInput;
 | 
			
		||||
char lineBuffer[MAXLINECHAR];
 | 
			
		||||
uint16_t buffer[128 + 2];
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief This function is used to thread main execution function
 | 
			
		||||
 *
 | 
			
		||||
 * @param   arg not used.
 | 
			
		||||
 *
 | 
			
		||||
 * @return  NULL.
 | 
			
		||||
 */
 | 
			
		||||
static void *fifo_to_buffer(void *arg);
 | 
			
		||||
 | 
			
		||||
static int start_cava_process();
 | 
			
		||||
 | 
			
		||||
static void stop_cava_process();
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
void cava_start() {
 | 
			
		||||
  isFifoReaderRunning = true;
 | 
			
		||||
  pthread_create(&fifoReader, NULL, fifo_to_buffer, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cava_stop() {
 | 
			
		||||
  isFifoReaderRunning = false;
 | 
			
		||||
  if (pthread_join(fifoReader, NULL) != 0) {
 | 
			
		||||
    log_error("pthread_join: %s", strerror(errno));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
static void *fifo_to_buffer(void *arg) {
 | 
			
		||||
  struct pollfd fds[1];
 | 
			
		||||
  int timeoutMs;
 | 
			
		||||
  int ret;
 | 
			
		||||
  size_t valueIndex = 0, charOffset = 0;
 | 
			
		||||
  char strValue[6] = "0\0";
 | 
			
		||||
  bool hasToBeDiscarded = true;
 | 
			
		||||
 | 
			
		||||
  if ((fdCavaInput = start_cava_process()) < 0) {
 | 
			
		||||
    log_error("y'a un truc qui a pas marché");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  memset(fds, 0, sizeof(fds));
 | 
			
		||||
  fds[0].fd = fdCavaInput;
 | 
			
		||||
  fds[0].events = POLLIN;
 | 
			
		||||
  timeoutMs = 10;
 | 
			
		||||
 | 
			
		||||
  while (isFifoReaderRunning) {
 | 
			
		||||
 | 
			
		||||
    if ((ret = poll(fds, 1, timeoutMs)) == 1) {
 | 
			
		||||
      int nread;
 | 
			
		||||
      nread = read(fdCavaInput, lineBuffer, 128 + 1);
 | 
			
		||||
 | 
			
		||||
      if (nread >= 0) {
 | 
			
		||||
        for (size_t i = 0; i < nread; ++i) {
 | 
			
		||||
          char current = lineBuffer[i];
 | 
			
		||||
 | 
			
		||||
          if (hasToBeDiscarded) {
 | 
			
		||||
            if (current == '\n') {
 | 
			
		||||
              charOffset = 0;
 | 
			
		||||
              strValue[charOffset] = '\0';
 | 
			
		||||
              valueIndex = 0;
 | 
			
		||||
              hasToBeDiscarded = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
          } else {
 | 
			
		||||
            if ('0' <= current && current <= '9') {
 | 
			
		||||
              strValue[charOffset++] = current;
 | 
			
		||||
            } else if (current == '\n' || current == ';') {
 | 
			
		||||
              strValue[charOffset] = '\0';
 | 
			
		||||
              charOffset = 0;
 | 
			
		||||
              buffer[valueIndex++] = atoi(strValue);
 | 
			
		||||
 | 
			
		||||
              if (current == '\n' || valueIndex > 129) {
 | 
			
		||||
                valueIndex = 0;
 | 
			
		||||
 | 
			
		||||
                if (valueIndex > 129) {
 | 
			
		||||
                  log_warn("Buffer overflow, \\n missed, discarding next");
 | 
			
		||||
                  hasToBeDiscarded = true;
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
              log_warn("Unexpected char %d [%c]\n", current, current);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        if (errno != EAGAIN) {
 | 
			
		||||
          log_error("read: %s", strerror(errno));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else if (ret < 0) {
 | 
			
		||||
      log_error("polling %d: %s\n", fdCavaInput, strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stop_cava_process();
 | 
			
		||||
  close(fdCavaInput);
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int start_cava_process() {
 | 
			
		||||
  int fdCavaPipe[2];
 | 
			
		||||
 | 
			
		||||
  if (pipe(fdCavaPipe) < 0) {
 | 
			
		||||
    log_error("Cava pipe failure: %s", strerror(errno));
 | 
			
		||||
    return -EXIT_FAILURE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((cavaPid = fork()) < 0) {
 | 
			
		||||
    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 */
 | 
			
		||||
    close(fdCavaPipe[0]);
 | 
			
		||||
    /* Dup writing end of the pipe in place of stdout */
 | 
			
		||||
    dup2(fdCavaPipe[1], STDOUT_FILENO);
 | 
			
		||||
    /* 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);
 | 
			
		||||
    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 */
 | 
			
		||||
    flags = fcntl(fdCavaPipe[0], F_GETFL, 0);
 | 
			
		||||
    fcntl(fdCavaPipe[0], F_SETFL, flags | O_NONBLOCK);
 | 
			
		||||
    /* Return reading end of the pipe */
 | 
			
		||||
    return fdCavaPipe[0];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Unreachable  */
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void stop_cava_process() {
 | 
			
		||||
  kill(cavaPid, SIGTERM);
 | 
			
		||||
  waitpid(0, NULL, WNOHANG);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								RpiLedBars/backend/src/tasks/includes/artnet.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								RpiLedBars/backend/src/tasks/includes/artnet.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
/** @file artnet.h
 | 
			
		||||
 *  @brief This module contains continuous tasks for artnet node communications.
 | 
			
		||||
 *
 | 
			
		||||
 * This is the header file for the definition of services to allow managing thread acting as artnet
 | 
			
		||||
 * node.
 | 
			
		||||
 */
 | 
			
		||||
#if !defined(__ARTNET_H__)
 | 
			
		||||
#define __ARTNET_H__
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Global Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Start artnet node module
 | 
			
		||||
 **/
 | 
			
		||||
void artnet_start();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Stop artnet node module
 | 
			
		||||
 */
 | 
			
		||||
void artnet_stop();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Get last DMX data received for the specified universe
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in]   univerve The universe to get data from
 | 
			
		||||
 *
 | 
			
		||||
 * @param[out]  dmxData  The pointer to the DMX data array
 | 
			
		||||
 */
 | 
			
		||||
int artnet_get_dmx_data(unsigned int univerve, uint8_t *dmxData[]);
 | 
			
		||||
 | 
			
		||||
#endif /* __ARTNET_H__ */
 | 
			
		||||
							
								
								
									
										37
									
								
								RpiLedBars/backend/src/tasks/includes/cava.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								RpiLedBars/backend/src/tasks/includes/cava.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
/** @file cava.h
 | 
			
		||||
 *  @brief This module contains continuous tasks for cava (spectrum analyzer) comunication
 | 
			
		||||
 */
 | 
			
		||||
#if !defined(__CAVA_H__)
 | 
			
		||||
#define __CAVA_H__
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define CAVA_BAR_NUMBER 128
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Global Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
int cava_get_buffer(uint16_t **buffer_dst);
 | 
			
		||||
 | 
			
		||||
void cava_start();
 | 
			
		||||
 | 
			
		||||
void cava_stop();
 | 
			
		||||
 | 
			
		||||
#endif /* __CAVA_H__ */
 | 
			
		||||
							
								
								
									
										34
									
								
								RpiLedBars/backend/src/tasks/includes/selector.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								RpiLedBars/backend/src/tasks/includes/selector.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
/** @file selector.h
 | 
			
		||||
 *  @brief This module contains continuous tasks for cava (spectrum analyzer) comunication
 | 
			
		||||
 */
 | 
			
		||||
#if !defined(__SELECTOR_TASK_H__)
 | 
			
		||||
#define __SELECTOR_TASK_H__
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Global Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Start selector listener module
 | 
			
		||||
 **/
 | 
			
		||||
void selector_start();
 | 
			
		||||
 | 
			
		||||
void selector_stop();
 | 
			
		||||
 | 
			
		||||
#endif /* __SELECTOR_TASK_H__ */
 | 
			
		||||
							
								
								
									
										43
									
								
								RpiLedBars/backend/src/tasks/includes/websocket.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								RpiLedBars/backend/src/tasks/includes/websocket.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/** @file websocket.h
 | 
			
		||||
 *  @brief This module
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if !defined(__WEBSOCKET_H__)
 | 
			
		||||
#define __WEBSOCKET_H__
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Global Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Start websocket module
 | 
			
		||||
 **/
 | 
			
		||||
void websocket_start();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Start websocket module
 | 
			
		||||
 **/
 | 
			
		||||
void websocket_stop();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Send mode to websocket client
 | 
			
		||||
 **/
 | 
			
		||||
void websocket_send_mode(int mode);
 | 
			
		||||
 | 
			
		||||
#endif /* __WEBSOCKET_H__ */
 | 
			
		||||
							
								
								
									
										87
									
								
								RpiLedBars/backend/src/tasks/selector/selector.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								RpiLedBars/backend/src/tasks/selector/selector.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
/** @file selector.c
 | 
			
		||||
 *  @brief This module contains continuous tasks for cava (spectrum analyzer) comunication
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
#include "selector.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "../../drivers/selector/rpi_selector.h"
 | 
			
		||||
#include "../../rpi_param.h"
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define LISTENER_INTERVAL 500000
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Persistent Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
pthread_t selectorListener;
 | 
			
		||||
/**
 | 
			
		||||
 * Boolean for interrupting the main thread loop
 | 
			
		||||
 */
 | 
			
		||||
bool isSelectorListenerRunning = false;
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief This function is used to thread main execution function
 | 
			
		||||
 *
 | 
			
		||||
 * @param   arg not used.
 | 
			
		||||
 *
 | 
			
		||||
 * @return  NULL.
 | 
			
		||||
 */
 | 
			
		||||
static void *listen_to_selector(void *arg);
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
void selector_start() {
 | 
			
		||||
  isSelectorListenerRunning = true;
 | 
			
		||||
  pthread_create(&selectorListener, NULL, listen_to_selector, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void selector_stop() {
 | 
			
		||||
  isSelectorListenerRunning = false;
 | 
			
		||||
  if (pthread_join(selectorListener, NULL) != 0) {
 | 
			
		||||
    log_error("pthread_join: %s", strerror(errno));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
static void *listen_to_selector(void *arg) {
 | 
			
		||||
  int previousPosition;
 | 
			
		||||
  selector_setup();
 | 
			
		||||
 | 
			
		||||
  while (isSelectorListenerRunning) {
 | 
			
		||||
    int newPosition = selector_get_position();
 | 
			
		||||
    if (newPosition != -1 && newPosition != previousPosition) {
 | 
			
		||||
      set_mode(newPosition);
 | 
			
		||||
      previousPosition = newPosition;
 | 
			
		||||
    }
 | 
			
		||||
    usleep(LISTENER_INTERVAL);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										194
									
								
								RpiLedBars/backend/src/tasks/websocket/websocket.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								RpiLedBars/backend/src/tasks/websocket/websocket.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
			
		||||
/** @file websocket.c
 | 
			
		||||
 *  @brief This module
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Includes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "websocket.h"
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "ws.h"
 | 
			
		||||
 | 
			
		||||
#include "../../rpi_param.h"
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Preprocessor Constants and Macros
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define MAX_TOKEN 10
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Type and Contant Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Persistent Variables
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
int client_fd = -1;
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Prototypes
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Called when a client connects to the server.
 | 
			
		||||
 *
 | 
			
		||||
 * @param fd File Descriptor belonging to the client. The @p fd parameter
 | 
			
		||||
 * is used in order to send messages and retrieve informations
 | 
			
		||||
 * about the client.
 | 
			
		||||
 */
 | 
			
		||||
void onopen(int fd);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Called when a client disconnects to the server.
 | 
			
		||||
 *
 | 
			
		||||
 * @param fd File Descriptor belonging to the client. The @p fd parameter
 | 
			
		||||
 * is used in order to send messages and retrieve informations
 | 
			
		||||
 * about the client.
 | 
			
		||||
 */
 | 
			
		||||
void onclose(int fd);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Called when a client connects to the server.
 | 
			
		||||
 *
 | 
			
		||||
 * @param fd File Descriptor belonging to the client. The
 | 
			
		||||
 * @p fd parameter is used in order to send messages and
 | 
			
		||||
 * retrieve informations about the client.
 | 
			
		||||
 *
 | 
			
		||||
 * @param msg Received message, this message can be a text
 | 
			
		||||
 * or binary message.
 | 
			
		||||
 *
 | 
			
		||||
 * @param size Message size (in bytes).
 | 
			
		||||
 *
 | 
			
		||||
 * @param type Message type.
 | 
			
		||||
 */
 | 
			
		||||
void onmessage(int fd, const unsigned char *msg, uint64_t size, int type);
 | 
			
		||||
 | 
			
		||||
void mode_command_handler(char *payload);
 | 
			
		||||
 | 
			
		||||
void pattern_command_handler(char *payload);
 | 
			
		||||
 | 
			
		||||
void color_command_handler(char *payload);
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * External Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
void websocket_start() {
 | 
			
		||||
  struct ws_events evs;
 | 
			
		||||
  evs.onopen = &onopen;
 | 
			
		||||
  evs.onclose = &onclose;
 | 
			
		||||
  evs.onmessage = &onmessage;
 | 
			
		||||
  ws_socket(&evs, 8080, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void websocket_stop() {}
 | 
			
		||||
 | 
			
		||||
void websocket_send_mode(int mode) {
 | 
			
		||||
  char msg[] = "m:0";
 | 
			
		||||
  msg[2] = '0' + mode;
 | 
			
		||||
  if (client_fd != -1) {
 | 
			
		||||
    log_debug("Sending mode \"%s\"", msg);
 | 
			
		||||
    ws_sendframe_txt(client_fd, msg, true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * Internal Function Definitions
 | 
			
		||||
 **************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
void onopen(int fd) {
 | 
			
		||||
  char *cli;
 | 
			
		||||
  cli = ws_getaddress(fd);
 | 
			
		||||
  log_debug("Connection opened, client: %d | addr: %s", fd, cli);
 | 
			
		||||
  free(cli);
 | 
			
		||||
  client_fd = fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void onclose(int fd) {
 | 
			
		||||
  char *cli;
 | 
			
		||||
  cli = ws_getaddress(fd);
 | 
			
		||||
  log_debug("Connection closed, client: %d | addr: %s", fd, cli);
 | 
			
		||||
  free(cli);
 | 
			
		||||
  client_fd = -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void onmessage(int fd, const unsigned char *msg, uint64_t size, int type) {
 | 
			
		||||
  char msgStr[size + 1];
 | 
			
		||||
  char *command, *payload;
 | 
			
		||||
  char *cli = ws_getaddress(fd);
 | 
			
		||||
  log_trace("Received message: %s (size: %" PRId64 ", type: %d), from: %s/%d", msg, size, type, cli,
 | 
			
		||||
            fd);
 | 
			
		||||
 | 
			
		||||
  strcpy(msgStr, (char *)msg);
 | 
			
		||||
  command = strtok(msgStr, ":");
 | 
			
		||||
  if (command != NULL) {
 | 
			
		||||
    payload = strtok(NULL, ":");
 | 
			
		||||
    if (payload != NULL && strlen(command) == 1) {
 | 
			
		||||
      switch (command[0]) {
 | 
			
		||||
      case 'm':
 | 
			
		||||
        mode_command_handler(payload);
 | 
			
		||||
        break;
 | 
			
		||||
      case 'p':
 | 
			
		||||
        pattern_command_handler(payload);
 | 
			
		||||
        break;
 | 
			
		||||
      case 'c':
 | 
			
		||||
        color_command_handler(payload);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
        log_warn("Unkown command in \"%s\"", msgStr);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      log_warn("Empty payload in \"%s\"", msgStr);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    log_warn("No command found in \"%s\"", msgStr);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  free(cli);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mode_command_handler(char *payload) {
 | 
			
		||||
  int newMode = atoi(payload);
 | 
			
		||||
  if (0 <= newMode && newMode <= 3) {
 | 
			
		||||
    set_mode(newMode);
 | 
			
		||||
  } else {
 | 
			
		||||
    log_warn("Unknown mode : %s", payload);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pattern_command_handler(char *payload) {
 | 
			
		||||
  int newPattern = atoi(payload);
 | 
			
		||||
  if (0 <= newPattern && newPattern <= 3) {
 | 
			
		||||
    set_pattern(newPattern);
 | 
			
		||||
  } else {
 | 
			
		||||
    log_warn("Unknown pattern : %s", payload);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void color_command_handler(char *payload) {
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  char *tokenArray[MAX_TOKEN] = {NULL};
 | 
			
		||||
  tokenArray[i] = strtok(payload, ",");
 | 
			
		||||
  while (tokenArray[i] && i < MAX_TOKEN - 1) {
 | 
			
		||||
    tokenArray[++i] = strtok(NULL, ",");
 | 
			
		||||
  }
 | 
			
		||||
  log_debug("tokens[%d]", i);
 | 
			
		||||
  for (size_t n = 0; n < i; ++n) {
 | 
			
		||||
    log_debug(" - %s", tokenArray[n]);
 | 
			
		||||
  }
 | 
			
		||||
  if (atoi(tokenArray[0]) == 0) {
 | 
			
		||||
    set_hue_base(LED_NCHANS, atoi(tokenArray[1]) * INT8_MAX / HUE_MAX);
 | 
			
		||||
    set_saturation(LED_NCHANS, atoi(tokenArray[2]) * INT8_MAX / 100);
 | 
			
		||||
    set_luminosity(LED_NCHANS, atoi(tokenArray[3]) * INT8_MAX / 100);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user