led-controller/firmware/main.c

713 lines
23 KiB
C

/* 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/>.
*
*/
/* 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 <stdint.h> /* Standard Integer Types */
#include <stdio.h> /* Standard IO facilities */
#include <stdlib.h> /* General utilities */
#include <stdbool.h> /* Boolean */
#include <string.h> /* Strings */
#include <avr/io.h> /* AVR device-specific IO definitions */
#include <util/delay.h> /* Convenience functions for busy-wait delay loops */
#include <avr/interrupt.h> /* Interrupts */
#include <avr/wdt.h> /* Watchdog timer handling */
#include <avr/pgmspace.h> /* 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 = "\ttemp show temperature\n";
const char help_08[] PROGMEM = "\tmode show current mode\n";
const char help_09[] PROGMEM = "\tmode N select mode\n";
const char help_10[] PROGMEM = "\tch X Y show channel [1-2].[1-5] brightness\n";
const char help_11[] PROGMEM = "\tch X Y Z set channel [1-2].[1-5] brightness [0-255]\n";
const char help_12[] PROGMEM = "\tir learn power learn the IR command to power on/off\n";
const char help_13[] PROGMEM = "\tir learn mode learn the IR command to change between modes\n";
const char help_14[] PROGMEM = "\tir learn brightness up learn the IR command to increase brightness\n";
const char help_15[] PROGMEM = "\tir learn brightness down learn the IR command to decrease brightness\n";
const char help_16[] PROGMEM = "\tir learn channel next learn the IR command to select next channel\n";
const char help_17[] 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,
help_17
};
//volatile uint8_t* PORTS[CHANNELS_1+CHANNELS_2] = {&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTD,&PORTD,&PORTD,&PORTD,&PORTD};
volatile uint8_t* PORTS[CHANNELS_1+CHANNELS_2] = {&PORTC,&PORTC,&PORTC,&PORTD,&PORTD,&PORTD};
//volatile uint8_t* DDRS[CHANNELS_1+CHANNELS_2] = {&DDRC,&DDRC,&DDRC,&DDRC,&DDRC,&DDRD,&DDRD,&DDRD,&DDRD,&DDRD};
volatile uint8_t* DDRS[CHANNELS_1+CHANNELS_2] = {&DDRC,&DDRC,&DDRC,&DDRD,&DDRD,&DDRD};
//const uint8_t BITS[CHANNELS_1+CHANNELS_2] = {PC0,PC1,PC2,PC3,PC4,PD2,PD3,PD4,PD5,PD7};
const uint8_t BITS[CHANNELS_1+CHANNELS_2] = {PC0,PC1,PC2,PD2,PD3,PD4};
/* 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 through the tachometer 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 */
/* save the UART input into a string */
ISR(USART_RX_vect) { /* UART receive interrupt */
input[input_i] = getchar(); /* save input */
input[input_i+1] = 0; /* always end the string */
if (input_i<INPUT_MAX) { /* next character, if space is available */
input_i++;
}
uart_flag = true; /* set flag */
}
/* power ok and IR interrupt
* store the new power start
* save the pulse time
*/
ISR(PCINT0_vect) { /* PCI0 Interrupt Vector for PCINT[7:0] */
if (pwr_ok!=(PINB&(1<<PWR_OK))) { /* did the PWR_OK pin state changed */
pwr_ok = PINB&(1<<PWR_OK); /* save new state */
power_flag = true;
} else if (ir!=(PINB&(1<<IR))) { /* did the IR pin state changed */
ir = PINB&(1<<IR); /* save new state */
if (pulse>0) { /* save pulse, except the first */
burst[pulse-1] = (TCNT1*1000UL)/ir_tick; /* pulse time in µs */
burst[pulse] = 0; /* end the burst with 0 */
}
if (pulse<PULSE_MAX-1) { /* prepare to save next pulse */
pulse++;
}
TCNT1 = 0; /* clear timer 1 */
}
}
/* fan tachometer interrupt */
ISR(PCINT1_vect) { /* PCI0 Interrupt Vector for PCINT[14:8] */
if (fan!=(PINC&(1<<FAN))) { /* did the FAN pin state changed */
fan = PINC&(1<<FAN); /* save new state */
if (fan) { /* only react to rising edge */
tachometer = (uint16_t)(timer2_ovf<<8)+TCNT2; /* save time */
TCNT2 = 0; /* reset timer 2 */
timer2_ovf = 0; /* reset timer 2 */
}
}
}
/* timer 2 interrupt used to measure fan speed based on tachometer */
ISR(TIMER2_OVF_vect) { /* timer 2 overflow interrupt vector */
if (timer2_ovf<0xff) { /* prevent overflow */
timer2_ovf++; /* increase tachometer counter */
} else { /* timeout for tachometer (the tachometer counter should be reseted before this timeout if the fan is on) */
tachometer = 0; /* indicate no speed can be measured */
if (pwr_ok) { /* warn the fan is dead while the power in on */
power_flag = true;
}
timer2_ovf = 0; /* reset the tachometer counter */
}
}
/* end of IR burst (using a timeout) */
ISR(TIMER1_COMPA_vect) { /* timer 1 OCR1A match interrupt vector */
if (pulse>0 && !ir_flag) { /* warm a burst is ready to be processed */
ir_flag = true;
}
}
/* generate a PWM for the channel outputs
* this is a bit long for an interrupt, but it's time critical
*/
ISR(TIMER0_COMPA_vect) { /* timer 0 OCR0A match interrupt vector */
ch_tick++;
if (pwr_ok) { /* only generate PWM if power is on */
for (int i=0; i<CHANNELS_1+CHANNELS_2; i++) { /* generate PWM for every channel */
if (on[i]==ch_tick) { /* time to switch on */
if (on[i]!=off[i]) { /* switch on */
*(PORTS[i]) |= (1<<BITS[i]);
} else if (brightness[mode][i]==0) { /* switch off if it's also the off time and the brightness is 0 */
*(PORTS[i]) &= ~(1<<BITS[i]);
} else { /* switch on if it's also the off time and the brightness is full */
*(PORTS[i]) |= (1<<BITS[i]);
}
} else if (off[i]==ch_tick && brightness[mode][i]!=0xff) { /* time to switch off */
*(PORTS[i]) &= ~(1<<BITS[i]);
}
}
}
}
/* disable watchdog when booting */
void wdt_init(void) __attribute__((naked)) __attribute__((section(".init3")));
void wdt_init(void)
{
MCUSR = 0;
wdt_disable();
return;
}
void ioinit(void)
{
/* configure power */
DDRB |= (1<<nPS_ON); /* nPS_ON is output */
PORTB |= (1<<nPS_ON); /* switch off power supply */
DDRB &= ~(1<<PWR_OK); /* PWR_ON (PB1/PCINT1) is input */
pwr_ok = PINB&(1<<PWR_OK); /* save state */
PCIFR &= ~(1<<PCIF0); /* clear interrupt flag */
PCICR |= (1<<PCIE0); /* enable interrupt for PCINT[7:0] */
PCMSK0 |= (1<<PCINT1); /* enable interrupt for PCINT1 */
/* configure LED (on PD6/OC0A) */
DDRD |= (1<<LED); /* LED is output */
PORTD &= ~(1<<LED); /* switch LED on */
/* configure FAN */
DDRC &= ~(1<<FAN); /* FAN (PC5/PCINT13) is input */
fan = PINC&(1<<FAN); /* save state */
PCIFR &= ~(1<<PCIF1); /* clear interrupt flag */
PCICR |= (1<<PCIE1); /* enable interrupt for PCINT[14:8] */
PCMSK1 |= (1<<PCINT13); /* enable interrupt for PCINT1 */
/* use timer 2 to measure the tachometer */
/* use normal mode */
TCCR2A &= ~((1<<WGM20)|(1<<WGM21));
TCCR2B &= ~(1<<WGM22);
/* clock/64 prescaler */
TCCR2B |= (1<<CS22);
TCCR2B &= ~((1<<CS21)|(1<<CS20));
TIFR2 = (1<<TOV2); /* clear timer 2 overflow interrupt flag */
TIMSK2 |= (1<<TOIE2); /* enable timer 2 overflow interrupt */
/* configure IR receiver */
DDRB &= ~(1<<IR); /* IR (PB0/PCINT0) receiver is input */
ir = PINB&(1<<IR); /* save state */
PCIFR = (1<<PCIF0); /* clear interrupt flag */
PCICR |= (1<<PCIE0); /* enable interrupt for PCINT[7:0] */
PCMSK0 |= (1<<PCINT0); /* enable interrupt for PCINT0 */
/* use timer 1 to measure IR pulse */
/* use CTC mode */
TCCR1A &= ~((1<<WGM10)|(1<<WGM11));
TCCR1B |= (1<<WGM12);
TCCR1B &= ~(1<<WGM13);
/* clock/8 prescaler, offers most precision for 15ms (up to 28.5ms) */
TCCR1B |= (1<<CS11);
TCCR1B &= ~((1<<CS12)|(1<<CS10));
uint16_t prescale = TIMER1_PRESCALE[(TCCR1B&((1<<CS12)|(1<<CS11)|(1<<CS10)))>>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<<OCF1A); /* clear timer 1 compare interrupt flag */
TIMSK1 |= (1<<OCIE1A); /* enable timer 1 compare interrupt */
/* configure channels (used for powering LEDs using an nMOS) */
for (uint8_t i=0; i<CHANNELS_1+CHANNELS_2; i++) {
*(DDRS[i]) |= (1<<BITS[i]);
*(PORTS[i]) &= ~(1<<BITS[i]);
}
/* use timer 0 as source for the channel PWM */
/* use CTC mode */
TCCR0A |= (1<<WGM01);
TCCR0A &= ~(1<<WGM00);
TCCR0B &= ~(1<<WGM02);
/* /8 prescale timer */
TCCR0B &= ~(1<<CS02);
TCCR0B |= (1<<CS01);
TCCR0B &= ~(1<<CS00);
OCR0A = 0x40; /* set PWM frequency (with prescale=8, 0x00=2304kHz-0xff=9kHz) */
TIFR0 = (1<<OCF0A); /* clear timer 0 compare interrupt flag */
TIMSK0 |= (1<<OCIE0A); /* enable timer 0 compare interrupt */
/* configure internal temperature sensor */
ADMUX |= (1<<REFS1)|(1<<REFS0); /* select internal 1.1V voltage reference */
/* select ADC8 internal temperature sensor */
ADMUX |= (1<<MUX3);
ADMUX &= ~((1<<MUX2)|(1<<MUX1)|(1<<MUX0));
ADCSRA |= (1<<ADEN); /* enable ADC */
ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); /* prescaler at 128 */
/* continuously do the conversion (then don't wait for ADCS to clear) */
//ADCSRB &= ~((1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0)); /* free-running mode */
//ADCSRA |= (1<<ADATE); /* conversion can be triggered */
//ADCSRA |= (1<<ADSC); /* start analogue to digital conversion */
/* use UART as terminal */
uart_init();
stdout = &uart_output;
stdin = &uart_input;
sei(); /* enable interrupts */
}
void help(void)
{
char* str;
for (uint8_t i=0; i<sizeof(help_table)/sizeof(PGM_P); i++) { /* display all help lines */
str = malloc(strlen_PF((uint_farptr_t)pgm_read_word(&(help_table[i]))));
strcpy_PF(str, (uint_farptr_t)pgm_read_word(&(help_table[i])));
printf(str);
free(str);
}
}
int main(void)
{
ioinit(); /* initialize IOs */
uint8_t command_i = 0; /* command index */
/* last IR data */
struct nec ir_data;
ir_data.valid = false;
ir_data.repeat = false;
ir_data.address = 0;
ir_data.command = 0;
uint8_t ir_repeat = 0; /* number of times the IR data has been repeated */
puts("LED dimmer up & running");
/* load (or initialize) settings */
if (!verify_settings()) {
initialize_settings();
save_settings();
if (!verify_settings()) {
puts("can't store setting");
return 0;
}
puts("settings created");
} else {
load_settings();
puts("settings loaded");
}
channel_flag = true; /* calculate channel values later */
/* switch power supply as saved */
if (power) {
PORTB &= ~(1<<nPS_ON);
} else {
PORTB |= (1<<nPS_ON);
}
PORTD &= ~(1<<LED); /* switch on LED */
while (true) {
/* calculated PWM values (on/off times) */
while (channel_flag) {
/* the next channel goes on/starts when the previous goes off, so to distribute the power over the whole range, instead of only using a lot of it in the beginning */
uint8_t start = 0;
for (uint8_t i=0; i<CHANNELS_1+CHANNELS_2; i++) {
on[i] = start;
start += brightness[mode][i];
off[i] = start;
}
channel_flag = false;
}
/* handle UART input */
while (uart_flag) {
PORTD |= (1<<LED); /* switch off LED */
/* echo back */
char c = 0;
while (command_i<input_i) {
c = input[command_i++];
putchar(c);
}
/* detect end of line */
if ('\n'==c || '\r'==c) {
if ('\r'==c) { /* display new line */
puts("");
}
/* process user command */
if (command_i>1) {
input[command_i-1] = '\0';
uart_action(input);
save_settings();
}
input_i = command_i = 0; /* reset input buffer */
}
uart_flag = false;
PORTD &= ~(1<<LED); /* switch on LED */
}
/* 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<<nPS_ON);
power = 0;
}
} else {
puts("power off");
for (uint8_t i=0; i<CHANNELS_1+CHANNELS_2; i++) { /* switch off output */
*(PORTS[i]) &= ~(1<<BITS[i]);
}
power = 0;
}
save_settings();
power_flag = false;
}
/* handle IR input */
while (ir_flag) {
PORTD |= (1<<LED); /* switch off LED */
time2nec(burst,pulse-1); /* convert raw time burst in NEC format */
struct nec ir_tmp = nec2data(burst,pulse-1); /* decode NEC burst */
if (ir_tmp.valid) {
if (ir_tmp.repeat) {
if (ir_repeat<0xff) {
ir_repeat++;
}
} else {
ir_data = ir_tmp;
ir_repeat = 0;
}
if (ir_repeat==0 || ir_repeat>3) { /* process command if new or repeated */
if (learn_flag) { /* learn command */
if (to_learn<IR_ACTION_END) {
ir_keys[to_learn][0] = ir_data.address;
ir_keys[to_learn][1] = ir_data.command;
}
puts("IR code learned");
to_learn = IR_ACTION_END;
learn_flag = false;
} else { /* trigger action */
ir_action(ir_data.address,ir_data.command);
}
save_settings();
}
}
pulse = 0; /* reset burst */
ir_flag = false;
PORTD &= ~(1<<LED); /* switch on LED */
}
}
return 0;
}
void uart_action(char* str)
{
/* split command */
const char* delimiter = " ";
char* word = strtok(str,delimiter);
if (!word) {
goto error;
}
/* parse command */
if (0==strcmp(word,"help")) {
help();
} else if (0==strcmp(word,"reset")) {
reset_settings();
/* reset using watchdog */
do {
wdt_enable(WDTO_15MS);
for(;;) {}
} while(0);
} else if (0==strcmp(word,"power")) {
word = strtok(NULL,delimiter);
if (!word) {
if (PINB&(1<<nPS_ON)) {
puts("power is off");
} else {
puts("power is on");
}
} else if (0==strcmp(word,"on")) {
PORTB &= ~(1<<nPS_ON);
} else if (0==strcmp(word,"off")) {
PORTB |= (1<<nPS_ON);
} else {
goto error;
}
} else if (0==strcmp(word,"fan")) {
if (tachometer) {
uint16_t prescale = TIMER2_PRESCALE[TCCR2B&((1<<CS22)|(1<<CS21)|(1<<CS20))];
if (prescale) {
if (timer2_ovf<0xff) {
uint32_t speed = ((60*F_CPU)/(prescale*(uint32_t)tachometer))/2; /* calculate speed. 2 pulses per revolution */
printf("fan speed: %lurpm\n",speed);
} else {
printf("fan is off (or not detected)\n");
}
} else {
printf("fan speed measurement not started\n");
}
} else {
printf("fan is off (or not detected)\n");
}
} else if (0==strcmp(word,"temp")) {
ADCSRA |= (1<<ADSC); /* start analogue to digital conversion */
loop_until_bit_is_clear(ADCSRA,ADSC); /* wait until conversion is finished */
printf("temperature: %u°C\n",(uint16_t)((ADCW-T_OFFSET)*T_FACTOR));
} else if (0==strcmp(word,"mode")) {
word = strtok(NULL,delimiter);
if (!word) {
printf("%u/%u\n",mode+1,MODES);
} else {
/* expecting one digit mode N */
if (strlen(word)!=1) {
goto error;
}
/* expecting mode 1-MODES */
if (word[0]<'1') {
goto error;
}
uint8_t mode_temp = word[0]-'1';
if (mode_temp>MODES) {
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;
static uint8_t channel = CHANNELS_1+CHANNELS_2;
uint8_t step = 0xff/LEVELS; /* the brightness increase/decrease steps */
for (ir_code=0; ir_code<IR_ACTION_END; ir_code++) { /* find the action for the current command */
if (ir_keys[ir_code][0]==address && ir_keys[ir_code][1]==command) {
break;
}
}
if (ir_code<IR_ACTION_END) { /* process action */
switch (ir_code) {
case POWER:
printf("switching power supply ");
if (PINB&(1<<nPS_ON)) {
puts("on");
} else {
puts("off");
}
PINB |= (1<<nPS_ON);
break;
case MODE:
mode = (mode+1)%MODES;
printf("selecting mode %u/%u\n",mode+1,MODES);
channel_flag = true;
break;
case BRIGHTNESS_UP:
if (channel<CHANNELS_1+CHANNELS_2) {
if (brightness[mode][channel]<0xff-step) {
brightness[mode][channel] += step;
} else {
brightness[mode][channel] = 0xff;
}
printf("increasing brightness ch %u %u: %u\n",(channel/CHANNELS_1)+1,(channel%CHANNELS_1)+1,brightness[mode][channel]);
} else if (channel==CHANNELS_1+CHANNELS_2) { /* increase all brightness if no channel is selected */
for (uint8_t i=0; i<CHANNELS_1+CHANNELS_2; i++) {
if (brightness[mode][i]<0xff-step) {
brightness[mode][i] += step;
} else {
brightness[mode][i] = 0xff;
}
}
printf("increasing brightness all channels\n");
}
channel_flag = true;
break;
case BRIGHTNESS_DOWN:
if (channel<CHANNELS_1+CHANNELS_2) {
if (brightness[mode][channel]>step) {
brightness[mode][channel] -= step;
} else {
brightness[mode][channel] = 0x00;
}
printf("decreasing brightness ch %u %u: %u\n",(channel/CHANNELS_1)+1,(channel%CHANNELS_1)+1,brightness[mode][channel]);
} else if (channel==CHANNELS_1+CHANNELS_2) { /* decrease all brightness if no channel is selected */
for (uint8_t i=0; i<CHANNELS_1+CHANNELS_2; i++) {
if (brightness[mode][i]>step) {
brightness[mode][i] -= step;
} else {
brightness[mode][i] = 0x00;
}
}
printf("decreasing brightness all channels\n");
}
channel_flag = true;
break;
case CHANNEL_NEXT:
if (channel<=CHANNELS_1+CHANNELS_2) {
channel = (channel+1)%(CHANNELS_1+CHANNELS_2+1);
} else {
channel = 0x00;
}
printf("selecting channel ch %u %u (%u)\n",(channel/CHANNELS_1)+1,(channel%CHANNELS_1)+1,brightness[mode][channel]);
break;
case CHANNEL_PREVIOUS:
if (0==channel) {
channel = CHANNELS_1+CHANNELS_2;
} else {
channel--;
}
printf("selecting channel ch %u %u (%u)\n",(channel/CHANNELS_1)+1,(channel%CHANNELS_1)+1,brightness[mode][channel]);
break;
default:
printf("unhandled IR action: %u\n", ir_code);
break;
}
} else {
puts("unknown IR command");
}
}