Skip to content
Snippets Groups Projects
mensactrl.c 4.01 KiB
Newer Older
daniel's avatar
daniel committed
/*
 * Control WS281X LEDs connected to the LCDIF
 *
 * Code taken from the proof-of-concept tool by
 * (C) 2013 Jeroen Domburg (jeroen AT spritesmods.com)

 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <math.h>

#include <zmq.h>

struct pixel {
	uint16_t x, y;
	uint8_t r, g, b;
} __attribute__((packed));

struct ws281x_fb {
	int x_res, y_res;
	int fd;
	uint8_t *inputfb;
	uint16_t *fbmem;
};

//This will encode a framebuffer with per-led RGB values into the weird framebuffer
//data values we need to form the correct WS2811 driving waveform.
void encodeToFb(struct ws281x_fb *wsfb) {
	int pix, col, bit, bitmask, strip, n;
	int p=0;
	for (pix=0; pix<wsfb->x_res; pix++) { //STRIPLEN points...
		for (col=0; col<3; col++) { //...of 3 bytes (r, g. b)
			bitmask=0x80;
			for (bit=0; bit<8; bit++) { //...of 8 bits each.
				/* At 4MHz, a bit has 5 cycles. For an 1, it's high 4, low one. For an 0, it's high one, low 4. */
				p++; /* First cycle is always 1 */
				n=0;
				//Iterate through every LED-strip to fetch the bit it needs to send out. Combine those
				//in n.
				for (strip=0; strip<wsfb->y_res; strip++) {
					if (wsfb->inputfb[(wsfb->x_res*strip*3)+(pix*3)+col]&bitmask) n|=1<<strip;
				}
				wsfb->fbmem[p++]=n;
				wsfb->fbmem[p++]=n;	//Middle 3 are dependent on bit value
				wsfb->fbmem[p++]=n;
				p++; /* Last cycle is always 0 */
				bitmask>>=1; //next bit in byte
			}
		}
	}
}


//Helper function to set the value of a single pixel in the 'framebuffer'
//of LED pixel values.
void setPixel(struct ws281x_fb *wsfb, int pixel, int strip, int r, int g, int b) {
	int pos=(strip*wsfb->x_res*3)+pixel*3;
	if (strip<0 || strip>=wsfb->x_res) return;
	if (pixel<0 || pixel>=wsfb->y_res) return;
	wsfb->inputfb[pos++]=r; //My strips have R and G switched.
	wsfb->inputfb[pos++]=g;
	wsfb->inputfb[pos++]=b;
}

static struct ws281x_fb *setup_fb(const char *devname, int x, int y)
{
	struct ws281x_fb *wsfb = malloc(sizeof(struct ws281x_fb));
	int i;

	wsfb->fd = open(devname, O_RDWR);
	if (wsfb->fd < 0) {
		perror("opening fb");
		free(wsfb);
		exit(1);
	}
	wsfb->inputfb = malloc(x*y*3);
	if (!wsfb->inputfb) {
		free(wsfb);
		exit(1);
	}
	memset(wsfb->inputfb, 0, x*y*3);

	wsfb->fbmem=mmap(NULL, x*5*24*2, PROT_READ|PROT_WRITE, MAP_SHARED, wsfb->fd, 0);
	if (wsfb->fbmem==NULL) {
		perror("mmap'ing fb");
		free(wsfb->inputfb);
		free(wsfb);
		exit(1);
	}
	for (i = 0; i < x*y*5*24; i++) {
		/* Init frame buffer bit clock. */
		if (i % 5 == 0)
			wsfb->fbmem[i] = 0xffff;
		if (i % 5 == 4)
			wsfb->fbmem[i] = 0x00;
	}

	wsfb->x_res = x;
	wsfb->y_res = y;

	return wsfb;
}

int main(int argc, char *argv[]) {
	struct ws281x_fb *wsfb;
	void *context = zmq_ctx_new ();
	void *subscriber = zmq_socket (context, ZMQ_SUB);
	int rc;

	if (argc != 2)
		exit(1);

	rc = zmq_connect (subscriber, "tcp://10.0.0.1:5556");
	if (rc < 0)
		perror("zmq_connect");

	rc = zmq_setsockopt (subscriber, ZMQ_SUBSCRIBE,
			NULL, 0);
	if (rc < 0)
		perror("zmq_connect");

	wsfb = setup_fb(argv[1], 10, 2);

	while (1) {
		struct pixel pix;
		zmq_recv(subscriber, &pix, sizeof(pix), 0);
		printf("(%u, %u): r=%02x g=%02x b=%02x\n", pix.x, pix.y,  pix.r, pix.g, pix.b);
		setPixel(wsfb, pix.x, pix.y, pix.r, pix.g, pix.b);
		encodeToFb(wsfb);
	}

	return 0;
}