Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 397 lines (345 sloc) 13.7 KB
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <time.h>
#include "clk.h"
#include "gpio.h"
#include "dma.h"
#include "pwm.h"
#include "version.h"
#include "ws2811.h"
/* Macros and defines */
#define ARRAY_SIZE(item) (sizeof(item) / sizeof(item[0]))
#define max(x, y) (((x) > (y)) ? (x) : (y))
#define min(x, y) (((x) < (y)) ? (x) : (y))
#define LED_COUNT 5 // HackPack v4 has 5 LEDs
#define TARGET_FREQ WS2811_TARGET_FREQ // 800 kHz
#define GPIO_PIN 18 // GPIO18 = Physical Pin 12
#define DMA 10 // DMA Channel
#define STRIP_TYPE WS2811_STRIP_GRB // WS2812/SK6812RGB integrated chip+leds
/* Path to the light UNIX socket watched by this server. */
char socket_path[1024] = "/dev/lightsocket";
/* Set up LED structure for DMA */
ws2811_t ledstring =
{
.freq = TARGET_FREQ,
.dmanum = DMA,
.channel =
{
[0] =
{
.gpionum = GPIO_PIN,
.count = LED_COUNT,
.invert = 0,
.brightness = 255,
.strip_type = STRIP_TYPE,
},
[1] =
{
.gpionum = 0,
.count = 0,
.invert = 0,
.brightness = 0,
},
},
};
/* Endless loop and verbose mode. */
static uint8_t running = 1;
static bool verbose_mode = false;
/* One instruction, 5 lights of color and a delay. */
typedef struct light_instruction
{
uint32_t led1[3];
uint32_t led2[3];
uint32_t led3[3];
uint32_t led4[3];
uint32_t led5[3];
int32_t delay;
struct light_instruction* next;
} light_t;
/* Pointer to head instruction */
light_t* head;
/* Sleep for x milliseconds. */
void msleep(int pmilliseconds)
{
struct timespec ts_sleep =
{
pmilliseconds / 1000,
(pmilliseconds % 1000) * 1000000L
};
nanosleep(&ts_sleep, NULL);
}
/*
* Add a new instruction to our light list.
*/
void push_instruction(light_t *new_inst)
{
light_t *current = head;
if (!current) {
head = new_inst;
return;
}
while (current->next != NULL) {
current = current->next;
}
current->next = new_inst;
}
/*
* Clear all light instructions.
*/
void clear_instructions()
{
light_t* current = head;
light_t* next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
head = NULL;
}
/*
* Print out instructions in current show.
*/
void print_instructions()
{
light_t *current = head;
if (!current) {
return;
}
int i = 1;
while(running) {
printf("Inst %d || ", i++);
printf("LED1: %d, %d, %d | ", current->led1[0], current->led1[1], current->led1[2]);
printf("LED2: %d, %d, %d | ", current->led2[0], current->led2[1], current->led2[2]);
printf("LED3: %d, %d, %d | ", current->led3[0], current->led3[1], current->led3[2]);
printf("LED4: %d, %d, %d | ", current->led4[0], current->led4[1], current->led4[2]);
printf("LED5: %d, %d, %d | ", current->led5[0], current->led5[1], current->led5[2]);
printf("DELAY: %d\n", current->delay);
if (current->next) {
current = current->next;
} else {
break;
}
}
}
/*
* If we get a signal from Raspbian stop the main loop and clear lights.
*/
static void ctrl_c_handler(int signum)
{
(void)(signum);
running = 0;
}
/*
* In case of signals.
*/
static void setup_handlers(void)
{
struct sigaction sa = {
.sa_handler = ctrl_c_handler,
};
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
}
/*
* Do a light show.
*/
void show_lights()
{
light_t *current = head;
if (!current) {
return;
}
while(running) {
ledstring.channel[0].leds[0] = current->led1[0] << 16;
ledstring.channel[0].leds[0] += current->led1[1] << 8;
ledstring.channel[0].leds[0] += current->led1[2];
ledstring.channel[0].leds[1] = current->led2[0] << 16;
ledstring.channel[0].leds[1] += current->led2[1] << 8;
ledstring.channel[0].leds[1] += current->led2[2];
ledstring.channel[0].leds[2] = current->led3[0] << 16;
ledstring.channel[0].leds[2] += current->led3[1] << 8;
ledstring.channel[0].leds[2] += current->led3[2];
ledstring.channel[0].leds[3] = current->led4[0] << 16;
ledstring.channel[0].leds[3] += current->led4[1] << 8;
ledstring.channel[0].leds[3] += current->led4[2];
ledstring.channel[0].leds[4] = current->led5[0] << 16;
ledstring.channel[0].leds[4] += current->led5[1] << 8;
ledstring.channel[0].leds[4] += current->led5[2];
if (verbose_mode) {
printf("LED1: %ul | ", ledstring.channel[0].leds[0]);
printf("LED2: %ul | ", ledstring.channel[0].leds[1]);
printf("LED3: %ul | ", ledstring.channel[0].leds[2]);
printf("LED4: %ul | ", ledstring.channel[0].leds[3]);
printf("LED5: %ul \n", ledstring.channel[0].leds[4]);
}
ws2811_return_t ret;
if ((ret = ws2811_render(&ledstring)) != WS2811_SUCCESS) {
fprintf(stderr, "ws2811_render failed: %s\n", ws2811_get_return_t_str(ret));
break;
}
msleep((int)current->delay);
if (current->next) {
current = current->next;
} else {
break;
}
}
}
/*
* main opens our socket then runs in a loop forever
*
* Inputs:
* - argc, argv: command link input (generally nothing, but can set socket path)
*
* Socket Inputs:
* NOTE: Pad packets to 100 characters with the last character of padding '|'
* - SHW: Show current light show
* - PRT: Print out the currently loaded light show
* - CLR: Clear the currently loaded light show
* - R1,G1,B1,R2,G2,B2,R3,G3,B3,R4,G4,B4,R5,G5,B5,DELAY: Add one instruction to the light show
* : Use UNSIGNED INTEGERS
* :
* : R/G/B: Max 255 (But dimmer if possible)
* : Delay: Length to stay in milliseconds
*
* Output:
* - Return code
*/
int main(int argc, char *argv[]) {
bool next_path = false;
struct sockaddr_un addr;
char buf[100];
int fd, cl, rc;
ws2811_return_t ret;
for (int i = 0; i < argc; ++i) {
if (strcmp("-v", argv[i]) == 0) {
verbose_mode = true;
} else if (strcmp("-p", argv[i]) == 0) {
next_path = true;
} else if (strcmp("-h", argv[i]) == 0) {
printf("Light socket server for HackPackv4.\n");
printf(" -v : Verbose Mode\n");
printf(" -p <path> : Change path of light socket.\n");
printf(" : DEFAULT: /dev/lightsocket\n");
} else if (next_path) {
socket_path[0] = '\0';
size_t len = min(1024, strlen(argv[i]));
memcpy(socket_path, argv[i], len);
socket_path[len] = '\0';
next_path = false;
}
if (verbose_mode) {
printf("Got: %s\n", argv[i]);
}
}
if (verbose_mode) {
printf("Socket path is: %s\n", socket_path);
}
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
/* Unlink socket if it exists then create it. */
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
if (*socket_path == '\0') {
*addr.sun_path = '\0';
strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2);
} else {
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
unlink(socket_path);
}
/* Trap socket errors. */
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("Couldn't bind socket");
exit(-1);
}
chmod(addr.sun_path, 0777);
if (listen(fd, 5) == -1) {
perror("Couldn't listen on socket");
exit(-1);
}
/* Deal with signals then initialize lights. */
setup_handlers();
if ((ret = ws2811_init(&ledstring)) != WS2811_SUCCESS) {
fprintf(stderr, "ws2811_init failed: %s\n", ws2811_get_return_t_str(ret));
return ret;
}
/* Sit and wait on socket connections forever. Parse anything that comes in. */
while (running) {
if ( (cl = accept(fd, NULL, NULL)) == -1) {
perror("Couldn't accept from socket");
continue;
}
/* Packet is: R1,G1,B1,R2,G2,B2,R3,G3,B3,R4,G4,B4,R5,G5,B5,DELAY */
while ( (rc=read(cl,buf,sizeof(buf))) > 0) {
int i = 0;
unsigned long led_array[16];
/* Strip padding */
char *command = strtok(buf, "|");
command = strtok(NULL, "|");
char *token = strtok(command, ",");
if (strstr(token,"PRT") != NULL) {
print_instructions();
} else if (strstr(token,"CLR") != NULL) {
clear_instructions();
} else if (strstr(token,"SHW") != NULL) {
show_lights();
} else {
do {
led_array[i] = strtoul(token, NULL, 10);
token = strtok(NULL, ",");
++i;
} while (token != NULL);
if (verbose_mode) {
printf("%d elements. Read %u bytes: %.*s\n", i, rc, rc, buf);
}
/* Populate a light instruction if we have the proper number of elements */
if (i == 16) {
light_t *instruction = malloc(sizeof(light_t));
instruction->next = NULL;
/* Populate the 5 light arrays */
instruction->led1[0] = (uint32_t)led_array[0];
instruction->led1[1] = (uint32_t)led_array[1];
instruction->led1[2] = (uint32_t)led_array[2];
instruction->led2[0] = (uint32_t)led_array[3];
instruction->led2[1] = (uint32_t)led_array[4];
instruction->led2[2] = (uint32_t)led_array[5];
instruction->led3[0] = (uint32_t)led_array[6];
instruction->led3[1] = (uint32_t)led_array[7];
instruction->led3[2] = (uint32_t)led_array[8];
instruction->led4[0] = (uint32_t)led_array[9];
instruction->led4[1] = (uint32_t)led_array[10];
instruction->led4[2] = (uint32_t)led_array[11];
instruction->led5[0] = (uint32_t)led_array[12];
instruction->led5[1] = (uint32_t)led_array[13];
instruction->led5[2] = (uint32_t)led_array[14];
/* Populate the delay */
instruction->delay = (int32_t)led_array[15];
/* Add it to the list */
push_instruction(instruction);
}
}
if (rc == -1) {
exit(-1);
} else if (rc == 0) {
close(cl);
}
}
}
ledstring.channel[0].leds[0] = 0x00000000;
ledstring.channel[0].leds[1] = 0x00000000;
ledstring.channel[0].leds[2] = 0x00000000;
ledstring.channel[0].leds[3] = 0x00000000;
ledstring.channel[0].leds[4] = 0x00000000;
ws2811_render(&ledstring);
return 0;
}