src/hello-dmx.c
author Tero Marttila <terom@paivola.fi>
Fri, 11 Apr 2014 15:57:48 +0300
changeset 66 0cf14786b909
parent 65 625f34328820
child 67 53743ecc9150
permissions -rw-r--r--
dmx: implement output
/*
 * 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.out[i] = console.argv[i];
    }

    dmx.count = i;

    return 0;
}

/*
 * Set output at offset.
 */
int cmd_set ()
{
    if (console.argc < 1) {
        return '!';
    }

    byte i = console.argv[0];

    for (byte a = 1; a < console.argc; a++) {
        dmx.out[i++] = console.argv[a];
    }

    if (i > dmx.count)
        dmx.count = i;

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

            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)) {
            //c = ' ';
            led_toggle();
            c = 0;

        } else if ((c = serial_read())) {
            // got serial data
            c = input(c);

        } else {
            // unknown interrupt
            c = '?';
        }
        //led_off();
        
        if (c)
            // respond
            serial_write(c);
        
        // output
        update();
    }
}