diff options
Diffstat (limited to 'moodlamp.c')
-rw-r--r-- | moodlamp.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/moodlamp.c b/moodlamp.c new file mode 100644 index 0000000..9d3f312 --- /dev/null +++ b/moodlamp.c @@ -0,0 +1,203 @@ +/* + * Copyright 2012 (C) Christian Franke <nobody at nowhere dot ws> + * + * 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 <avr/interrupt.h> +#include <avr/io.h> +#include <stdint.h> +#include <string.h> + +char output_buffer[512]; +uint16_t output_offset = 0; +uint16_t output_end = 0; + +char input_buffer[512]; +uint16_t input_end = 0; + +volatile uint16_t value_red = 3072; +volatile uint16_t value_green = 2048; +volatile uint16_t value_blue = 1024; + +uint8_t verbose = 0; + +static void send(const char *buffer, uint16_t len) +{ + cli(); + + if (sizeof(output_buffer) - output_end < len) { + memmove(output_buffer, output_buffer + output_offset, output_end - + output_offset); + output_end -= output_offset; + output_offset = 0; + } + + uint16_t size = len; + + if (size > sizeof(output_buffer) - output_end) + size = sizeof(output_buffer) - output_end; + + memcpy(&output_buffer[output_end], buffer, size); + + uint8_t send_needed = output_end == output_offset; + + output_end += size; + sei(); + + if (send_needed) { + output_offset++; + UDR1 = output_buffer[output_offset - 1]; + } +} + +ISR(USART1_TX_vect) +{ + if (output_end == output_offset) + return; + + UDR1 = output_buffer[output_offset]; + output_offset++; +} + +static void send_confusion(void) +{ + send("?\r\n", 3); +} + +static void send_success(void) +{ + if (verbose) { + send(":)\r\n", 4); + } +} + +static uint8_t parse_hex(char *buffer, uint8_t *target) +{ + if (buffer[0] >= '0' && buffer[0] <= '9') { + *target = 16 * (buffer[0] - '0'); + } else if (buffer[0] >= 'A' && buffer[0] <= 'F') { + *target = 16 * (buffer[0] - 'A' + 10); + } else if (buffer[0] >= 'a' && buffer[0] <= 'f') { + *target = 16 * (buffer[0] - 'a' + 10); + } else { + return 1; + } + + if (buffer[1] >= '0' && buffer[1] <= '9') { + *target += buffer[1] - '0'; + } else if (buffer[1] >= 'A' && buffer[1] <= 'F') { + *target += buffer[1] - 'A' + 10; + } else if (buffer[1] >= 'a' && buffer[1] <= 'f') { + *target += buffer[1] - 'a' + 10; + } else { + return 1; + } + + return 0; +} + +static void handle_immediate(void) +{ + if (input_end < sizeof("irrggbb\r") - 1) { + send_confusion(); + return; + } + + uint8_t new_red, new_blue, new_green; + + if (parse_hex(&input_buffer[1], &new_red) + || parse_hex(&input_buffer[3], &new_green) + || parse_hex(&input_buffer[5], &new_blue)) { + send_confusion(); + return; + } + + value_red = (uint16_t)new_red << 4; + value_green = (uint16_t)new_green << 4; + value_blue = (uint16_t)new_blue << 4; + send_success(); +} + +static void parse_buffer(void) +{ + switch (input_buffer[input_end - 1]) { + case '\r': + case '\n': + if (verbose) + send("\r\n", 2); + break; + default: + if (verbose) + send(input_buffer + input_end - 1, 1); + return; + } + + switch (input_buffer[0]) { + case 'i': + case 'I': + handle_immediate(); + break; + case 'q': + case 'Q': + verbose = 1 - verbose; + send_success(); + break; + default: + send_confusion(); + } + + input_end = 0; +} + +ISR(USART1_RX_vect) +{ + if (input_end == sizeof(input_buffer)) + input_end = 0; + + input_buffer[input_end++] = UDR1; + parse_buffer(); +} + +int main(void) +{ + UBRR1 = 4; /* Set baud rate to 230400 */ + UCSR1A = 0x00; + UCSR1B = (1 << RXCIE1) | (1 << TXCIE1) | (1 << RXEN1) | (1 << TXEN1); + UCSR1C = (1 << UCSZ11) | (1 << UCSZ10); + + sei(); /* Enable interrupts */ + + PORTA = 0x00; + DDRA = (1 << PA0) | (1 << PA1) | (1 << PA2); + + for (;;) { + if (value_red) + PORTA |= (1 << PA0); + if (value_green) + PORTA |= (1 << PA1); + if (value_blue) + PORTA |= (1 << PA2); + + for (uint16_t i = 0; i < 4096; i++) { + if (value_red < i) + PORTA &= ~(1 << PA0); + if (value_green < i) + PORTA &= ~(1 << PA1); + if (value_blue < i) + PORTA &= ~(1 << PA2); + } + } +} |