/* 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 .
*
*/
/* This is the main part of the LED light controller program.
* It handles all peripherals (power, fan, channels, IR, serial)
*/
/* This program is specifically designed for hardware version A,
* with schematic revision 2, and layout revision 5.
*/
#include /* Standard Integer Types */
#include /* Standard IO facilities */
#include /* General utilities */
#include /* Boolean */
#include /* Strings */
#include /* AVR device-specific IO definitions */
#include /* Convenience functions for busy-wait delay loops */
#include /* Interrupts */
#include /* Watchdog timer handling */
#include /* Program Space Utilities */
#include "main.h"
#include "uart.h"
#include "ir_nec.h"
#include "settings.h"
/* help strings */
const char help_00[] PROGMEM = "commands:\n";
const char help_01[] PROGMEM = "\thelp display this help\n";
const char help_02[] PROGMEM = "\treset reset boad and settings\n";
const char help_03[] PROGMEM = "\tpower show power state\n";
const char help_04[] PROGMEM = "\tpower on switch power on\n";
const char help_05[] PROGMEM = "\tpower off switch power off\n";
const char help_06[] PROGMEM = "\tfan show fan speed\n";
const char help_07[] PROGMEM = "\tmode show current mode\n";
const char help_08[] PROGMEM = "\tmode N select mode\n";
const char help_09[] PROGMEM = "\tch X Y show channel [1-2].[1-5] brightness\n";
const char help_10[] PROGMEM = "\tch X Y Z set channel [1-2].[1-5] brightness [0-255]\n";
const char help_11[] PROGMEM = "\tir learn power learn the IR command to power on/off\n";
const char help_12[] PROGMEM = "\tir learn mode learn the IR command to change between modes\n";
const char help_13[] PROGMEM = "\tir learn brightness up learn the IR command to increase brightness\n";
const char help_14[] PROGMEM = "\tir learn brightness down learn the IR command to decrease brightness\n";
const char help_15[] PROGMEM = "\tir learn channel next learn the IR command to select next channel\n";
const char help_16[] PROGMEM = "\tir learn channel previous learn the IR command to select previous channel\n";
PGM_P const help_table[] PROGMEM = {
help_00,
help_01,
help_02,
help_03,
help_04,
help_05,
help_06,
help_07,
help_08,
help_09,
help_10,
help_11,
help_12,
help_13,
help_14,
help_15,
help_16
};
volatile uint8_t* PORTS[CHANNELS_1+CHANNELS_2] = {&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTD,&PORTD,&PORTD,&PORTD,&PORTD};
volatile uint8_t* DDRS[CHANNELS_1+CHANNELS_2] = {&DDRC,&DDRC,&DDRC,&DDRC,&DDRC,&DDRD,&DDRD,&DDRD,&DDRD,&DDRD};
const uint8_t BITS[CHANNELS_1+CHANNELS_2] = {PC0,PC1,PC2,PC3,PC4};
/* global variables */
#define INPUT_MAX 255 /* max length for user input string */
char input[INPUT_MAX+2]; /* user input from USART */
volatile uint8_t input_i = 0; /* user input index */
volatile uint8_t pwr_ok; /* is power ok */
volatile uint8_t fan; /* fan signal state, to measure tachometer */
volatile uint8_t timer2_ovf = 0; /* to measure fan speed using timer 2 */
const uint16_t TIMER2_PRESCALE[8] = {0,1,8,32,64,128,256,1024}; /* timer 2 CS2[2:0] values */
volatile uint16_t tachometer = 0; /* the tachometer time (from timer) */
volatile uint8_t ir; /* IR signal state, to measure IR code */
const uint16_t TIMER1_PRESCALE[8] = {0,1,8,64,256,1024,0,0}; /* timer 1 CS1[2:0] values */
volatile uint16_t ir_tick; /* number of counter ticks per millisecond */
volatile uint8_t pulse = 0; /* pulse index within the burst */
#define PULSE_MAX 128 /* maximum number of pulses to save */
uint16_t burst[PULSE_MAX]; /* pulse times forming a burst (from timer) */
/* channel variables */
#define LEVELS 10 /* the number of PWM levels */
volatile uint8_t ch_tick = 0; /* to tick for the channel PWM */
uint8_t on[CHANNELS_1+CHANNELS_2]; /* at which tick turn the channel on */
uint8_t off[CHANNELS_1+CHANNELS_2]; /* at which tick turn the channel off */
/* flags, set in the interrupts and handled in the main program */
volatile bool uart_flag = false; /* an incoming activity on the UART */
volatile bool power_flag = false; /* a change in the power or fan */
volatile bool ir_flag = false; /* to process a burst */
volatile bool channel_flag = false; /* indicate a change in the channel PWM values */
volatile bool learn_flag = false; /* learn an IR command for an action */
enum IR_ACTIONS to_learn = IR_ACTION_END; /* IR action to learn */
/* UART receive interrupt */
ISR(USART_RX_vect) {
input[input_i] = getchar(); /* save input */
input[input_i+1] = 0; /* always end the string */
if (input_i0) { /* save pulse, except the first */
burst[pulse-1] = (TCNT1*1000UL)/ir_tick;
burst[pulse] = 0;
}
if (pulse0 && !ir_flag) { /* warm an burst is ready when the timeout triggered */
ir_flag = true;
}
}
/* timer 0 interrupt used generate a PWM for the channels */
ISR(TIMER0_COMPA_vect) { /* timer 0 OCR0A match interrupt vector */
ch_tick++;
if (pwr_ok) {
for (int i=0; i>CS10]; /* timer 1 presacler */
if (0!=prescale) {
ir_tick = F_CPU/(1000*prescale); /* ticks per ms */
OCR1A = (uint32_t)(15*F_CPU)/(1000*prescale); /* set clear time to 15 ms (no IR toggle should be longer) */
}
TIFR1 &= ~(1<1) {
input[command_i-1] = '\0';
uart_action(input);
save_settings();
}
input_i = command_i = 0;
}
uart_flag = false;
}
/* handle power state */
while (power_flag) {
if (pwr_ok) {
puts("power ok");
power = 1;
/* verify if FAN is present */
_delay_ms(500);
if (0==tachometer) {
puts("FAN not on, switching power off");
PORTB |= (1<3) {
if (learn_flag) {
if (to_learnMODES) {
goto error;
}
mode = mode_temp;
channel_flag = true;
}
} else if (0==strcmp(word,"ir")) {
word = strtok(NULL,delimiter);
if (0==strcmp(word,"learn")) {
word = strtok(NULL,delimiter);
if (0==strcmp(word,"power")) {
to_learn = POWER;
learn_flag = true;
} else if (0==strcmp(word,"mode")) {
to_learn = MODE;
learn_flag = true;
} else if (0==strcmp(word,"brightness")) {
word = strtok(NULL,delimiter);
if (0==strcmp(word,"up")) {
to_learn = BRIGHTNESS_UP;
learn_flag = true;
} else if (0==strcmp(word,"down")) {
to_learn = BRIGHTNESS_DOWN;
learn_flag = true;
} else {
goto error;
}
} else if (0==strcmp(word,"channel")) {
word = strtok(NULL,delimiter);
if (0==strcmp(word,"next")) {
to_learn = CHANNEL_NEXT;
learn_flag = true;
} else if (0==strcmp(word,"previous")) {
to_learn = CHANNEL_PREVIOUS;
learn_flag = true;
} else {
goto error;
}
} else {
goto error;
}
} else {
goto error;
}
} else if (0==strcmp(word,"ch")) {
word = strtok(NULL,delimiter);
if (!word) { /* expecting channel group */
goto error;
}
if (strlen(word)!=1) { /* expecting one digit channel group X */
goto error;
}
uint8_t group = 0;
switch (word[0]) { /* expecting channel group X 1 or 2 */
case '1':
group = 0;
break;
case '2':
group = 1;
break;
default:
goto error;
}
/* get channel output */
word = strtok(NULL,delimiter);
if (!word) {
goto error;
}
/* expecting one digit channel output Y */
if (strlen(word)!=1) {
goto error;
}
/* expecting channel group Y 1-CHANNELS_X */
if (word[0]<'1') {
goto error;
}
uint8_t output = word[0]-'1';
if (group==0 && output>CHANNELS_1) {
goto error;
} else if (group==1 && output>CHANNELS_2) {
goto error;
}
uint8_t channel = group*CHANNELS_1+output;
/* brightness setting */
word = strtok(NULL,delimiter);
if (!word) {
printf("%u\n",brightness[mode][channel]);
} else {
if (strlen(word)>3) {
goto error;
}
uint16_t br = atoi(word);
if (br>0xff) {
goto error;
}
brightness[mode][channel] = (uint8_t)br;
channel_flag = true;
}
} else {
goto error;
}
if (learn_flag) {
puts("press button on remote to learn code");
}
return;
error:
puts("command not recognized");
}
void ir_action(uint8_t address, uint8_t command)
{
enum IR_ACTIONS ir_code = IR_ACTION_END;
for (ir_code=0; ir_code