src/hello-dmx.c
author Tero Marttila <terom@paivola.fi>
Sat, 08 Nov 2014 17:51:09 +0200
changeset 98 e743c905cbf5
parent 79 076657910c0a
child 100 d4ff49d86531
permissions -rw-r--r--
dmx: move dmx out to PORTD2, and debug to PORTB5 (integrated LED); support \n for commands
/*
 * 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   5

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;

/*
 * Tick output state
 */
void update ()
{
    led_toggle();
    dmx_packet(dmx.count, dmx.out);
}

/*
 * Console input state.
 */
enum state {
    START       = '\n',
    CMD         = ';',
    ARG         = ',',
    ERROR       = '!',
};

#define CONSOLE_ARGS 8

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 '!';
    }

    if (end >= DMX_COUNT) {
        return '!';
    }

    // apply
    // arg is 1..256
    byte c = start - 1;

    // c < end actually means inclusive to end of range, as the index is -1
    for ( ; c < end; ) {
        for (byte a = 2; a < console.argc && c < end; 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' || c == '\n') {
        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 {
            // success
            ret = '\n';

            // commit changes
            update();
        }

        // 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;
}

void main ()
{
    led_init();
    timer_init();
    serial_init();
    dmx_init();

    // mainloop
    char c = '>';
    unsigned interval = 8000; // 2Hz

    // start
    sei();
    serial_write(c);
    timer1_start(interval);

    while (true) {
        // interval sleep
        if (timer_sleep(0)) {
            // output
            update();
        }

        // input
        while ((c = serial_read())) {
            serial_write(input(c));
        }
    }
}