dmx.py: --zero --start --stop --step ...
/*
* DMX output.
*
* See doc/dmx.txt for the RX-485 bus protocol details...
*/
#include <avr/io.h>
#include "stdlib.h"
#include "timer.c"
#include "serial.c"
#include "dmx.c"
// debug
#define DEBUG_DDR DDRB
#define DEBUG_PORT PORTB
#define DEBUG_LED 0
void led_init ()
{
sbi(&DEBUG_DDR, DEBUG_LED);
}
void led_on ()
{
sbi(&DEBUG_PORT, DEBUG_LED);
}
void led_off ()
{
cbi(&DEBUG_PORT, DEBUG_LED);
}
void led_toggle ()
{
xbi(&DEBUG_PORT, DEBUG_LED);
}
// dmx
#define DMX_COUNT 255
/*
* DMX state
*/
static struct dmx_state {
byte out[DMX_COUNT];
byte count;
} dmx;
enum state {
START = '\n',
CMD = ';',
ARG = ',',
ERROR = '!',
};
#define CONSOLE_ARGS 8
/*
* Console input state.
*/
static struct console {
byte state;
byte cmd;
byte argc;
byte argv[CONSOLE_ARGS];
} console;
/*
* Clear output (no state).
*/
int cmd_clear ()
{
dmx.count = 0;
return 0;
}
/*
* Set output to given sequence.
*/
int cmd_out ()
{
byte i;
for (i = 0; i < console.argc && i < DMX_COUNT; i++) {
dmx.out[i] = console.argv[i];
}
dmx.count = i;
return 0;
}
/*
* Set output at offset.
*/
int cmd_set ()
{
if (console.argc < 1) {
return '!';
}
if (console.argv[0] == 0) {
return '!';
}
byte i = console.argv[0] - 1;
for (byte a = 1; a < console.argc && i < DMX_COUNT; a++) {
dmx.out[i++] = console.argv[a];
}
if (i > dmx.count) {
dmx.count = i;
}
return 0;
}
/*
* Fill output range with values.
*
* r <start> <stop> <value> ...
*/
int cmd_fill ()
{
if (console.argc < 3) {
return '!';
}
byte start = console.argv[0];
byte end = console.argv[1];
if (!start) {
return '!';
}
if (end < start) {
return '!';
}
// apply
// arg is 1..256
byte c = start - 1;
for ( ; c < end && c < DMX_COUNT - 1; ) {
for (byte a = 2; a < console.argc && c < DMX_COUNT - 1; a++) {
dmx.out[c++] = console.argv[a];
}
}
if (c > dmx.count) {
dmx.count = c;
}
return 0;
}
/*
* Set output start-stop/step range to value.
*
* r <start> <stop> <step> <value>
*/
int cmd_range ()
{
if (console.argc != 4) {
return '!';
}
byte start = console.argv[0];
byte end = console.argv[1];
byte skip = console.argv[2];
byte value = console.argv[3];
byte c;
if (!start) {
return '!';
}
if (end < start) {
return '!';
}
if (!skip) {
return '!';
}
for (c = start; c <= end && c < DMX_COUNT; c += skip) {
dmx.out[c - 1] = value;
}
// XXX: -1
if (c > dmx.count) {
dmx.count = c;
}
return 0;
}
/*
* Set output to max. zeroes
*/
int cmd_zero ()
{
byte count;
if (console.argc == 0) {
count = DMX_COUNT;
} else if (console.argc == 1) {
count = console.argv[0];
} else {
return '!';
}
// set
byte i;
for (i = 0; i < count; i++) {
dmx.out[i] = 0;
}
dmx.count = i;
return 0;
}
/*
* Process console command.
*/
int command ()
{
switch (console.cmd) {
case 'c': return cmd_clear();
case 'o': return cmd_out();
case 'f': return cmd_fill();
case 'r': return cmd_range();
case 's': return cmd_set();
case 'z': return cmd_zero();
default: return '?';
}
}
/*
* Process console input.
*/
char input (char c)
{
// control
if (c == '\r') {
char ret = '?';
if (console.state == CMD) {
console.argc = 0;
} else if (console.state == ARG) {
console.argc++;
} else {
console.state = START;
return '\n';
}
// command
if ((ret = command(console.cmd))) {
} else {
ret = '\n';
}
// return to START with response
console.state = START;
return '\n'; // XXX: required to delimit command reponses..
} else if (c == ' ' || c == '\t' || c == ',') {
// argument
if (console.state == CMD) {
console.state = ARG;
console.argc = 0;
console.argv[0] = 0;
return ',';
} else if (console.state == ARG) {
if (console.argc++ < CONSOLE_ARGS) {
console.argv[console.argc] = 0;
return ',';
}
}
// printable
} else if (32 < c && c < 128) {
// process input char
if (console.state == START) {
console.cmd = c;
console.state = CMD;
return c;
} else if (console.state == ARG) {
if (c >= '0' && c <= '9') {
console.argv[console.argc] *= 10;
console.argv[console.argc] += (c - '0');
return c;
}
}
} else {
// ignore
return ' ';
}
// reject
console.state = ERROR;
return ERROR;
}
/*
* Tick output state
*/
void update ()
{
dmx_packet(dmx.count, dmx.out);
}
void main ()
{
led_init();
timer_init();
serial_init();
dmx_init();
// mainloop
char c = '>';
unsigned timeout = 8000; // 2Hz
// start
sei();
serial_write(c);
while (true) {
// sleep
//led_on();
if (timer_sleep(timeout)) {
led_toggle();
}
// input
while ((c = serial_read())) {
serial_write(input(c));
}
//led_off();
// output
update();
}
}