Код:
// http://compcar.ru
#include <IRremote.h>
#include <EEPROM.h>
//
//#define DEBUG 1
//
#define p_delay(VAR,MS) mills_poll_##VAR = millis()+MS
#define POLL(NAME) if (mills_poll_##NAME<millis()) poll_##NAME()
#define POLL_BUT(SW) if (SW.press_cnt>0&&SW.next_poll<millis()) poll_button(&SW)
//------------------------------
// назначение пинов ардуино
#define RECV_PIN 10 //вход ИК приемника
#define REVL_PIN 12 //вход ЛАМПОЧКИ заднего хода
#define JOYSTICK_PIN 5 // Вход для рулевых Кнопок
#define AVSW_PIN 8 //выход на переключатель A/V для заднего хода
#define AMP_PIN 7 // Выход для включения усилителя
#define LIGHT_SENS 0 // Вход датчика освещености
#define VOLT_SENS 1 // Вход датчика напряжения
#define FUEL_SENS 2 // Вход датчика уровня топлива
#define encoder0PinA 2 //Вывод А энкодера
#define encoder0PinB 3 //Вывод В энкодера
#define encoder_botton 4 //Вход кнопки энкодера
//-------------------------------
// Энкодер
#define ENC_DELAY0 50 // интервал для повторяющихся срабатываний
#define ENC_DELAY 1000 // интервал опроса
int X2400=0,X1500=0; // сколько раз срабатывал триггер левый и правый
// -----------------------------
// AUTO_LIGHT
#define AUTO_LIGHT_DELAY 1000 // 10 секунд цикл поверки датчика освещения
#define MAX_ULK 6 // сдвиг 3 вверх/3 вниз по градации яркости по "половинке"
byte ulk=MAX_ULK/2; // коэффициент для ручной правки яркости
// JOYSTICK
#define JOYSTICK_DELAY 250
#define JOYSTICK_DELAY0 100
// VOLTMETER
#define VOLTMETER_DELAY 1000 // опрос 1 раз в секунду
// IR
#define IR_DELAY_R 120
#define IR_DELAY_P 250
// Rear Lamp
// Количество нажатий кнопки для перехода : (задний ход)
#define TO_AV 1 // VGA->AV
#define TO_VGA 2 // AV->VGA
// задержка на переключение на камеру заднего вида при включении задней передачи
#define REVL_DELAY 500
byte revl_state=0; // в задержке ли находится триггер заднего хода
#define REVL_STATE_OFFSET 10 // адрес eeprom для хранения состояния
// структура для кнопки
typedef struct bbb{ // есть баг в компиляторе - при вызове функции не работает typedef тип
byte pin;
byte press_cnt;
byte state;
unsigned long next_poll;
} BUTTON;
// кнопка аудио-видео
BUTTON av;
// длительность нажатия и между нажатиями (МС)
#define BUT_DELAY 100 // пауза между нажатиями
#define BUT_MS 100 // время нажатия
// через сколько после старта включить усь
#define AMP_DELAY 15000 // 15 секунд задержка до включения усилителя
IRrecv irrecv(RECV_PIN);
decode_results results;
unsigned long old_ir,old_al,res_dt;
// циклы опроса процедур
unsigned long mills_poll_ENC=0,
mills_poll_REVL=0,
mills_poll_VOLTMETER=0,
mills_poll_JOYSTICK=0,
mills_poll_IR=0,
mills_poll_AUTO_LIGHT=0,
mills_poll_AMP=AMP_DELAY,
mills_poll_BUT=0;
// флаги
boolean need_ir_resume=false; // продолжение чтения для ИК
boolean send_data=false; // готовы данные для отправки
// структура в 13 байт для отправки в ALL_IN_ONE
struct all_in_one{
unsigned long ir_dt;
byte light,
volt,
fuel,
reserved[5];
};
struct all_in_one aino;
void setup(){
int cnt=0;
int cnt1;
Serial.begin(115200);
irrecv.enableIRIn(); // включить приемник
revl_state=EEPROM.read(REVL_STATE_OFFSET);
pinMode(AVSW_PIN, OUTPUT);
pinMode(REVL_PIN, INPUT);
digitalWrite(REVL_PIN, HIGH); //Подключить внутренний подтягивающий резистор
digitalWrite(AVSW_PIN, LOW);
digitalWrite(AMP_PIN, LOW);
pinMode(AMP_PIN, OUTPUT);
//----------------
pinMode(encoder0PinA, INPUT);
digitalWrite(encoder0PinA, HIGH); // подключить подтягивающий резистор
pinMode(encoder0PinB, INPUT);
digitalWrite(encoder0PinB, HIGH); // подключить подтягивающий резистор
pinMode(encoder_botton, INPUT);
digitalWrite(encoder_botton, HIGH); // подключить подтягивающий резистор
attachInterrupt(0, doEncoderA, FALLING); // настроить прерывание interrupt 0 на pin 2
attachInterrupt(1, doEncoderB, FALLING); // настроить прерывание interrupt 0 на pin 3
//-----------------
aino.ir_dt=0;
aino.light=0;
aino.volt=0;
aino.fuel=0;
av.pin=AVSW_PIN;
av.press_cnt=0;
}
void loop(){
// если текущие миллисекунды больше интервала поллинга
POLL(IR); // пущаю ИК
POLL(AUTO_LIGHT);
POLL(AMP);
POLL(VOLTMETER);
POLL(REVL);
POLL(JOYSTICK);
POLL(ENC);
POLL_BUT(av);
if (send_data) virt_keys(); // если есть данные для отправки - проверяем на свои действия
if (send_data) {
#ifndef DEBUG
Serial.write( (byte*)&aino,sizeof(struct all_in_one));
#else
Serial.print(millis());
Serial.print("\tKey:");
Serial.print(aino.ir_dt);
Serial.print("\tLight:");
Serial.print(int(aino.light));
Serial.print("\tVolt:");
Serial.print(int(aino.volt));
Serial.print("\tFuel:");
Serial.println(int(aino.fuel));
#endif
send_data=false;
aino.ir_dt=0; // устанавливаем состояние ик приемника в неопределенное
}
}
void press_button(struct bbb* b,byte count){ // установка задачи на нажимание
b->press_cnt+=count;
}
void poll_button(struct bbb* b){ // нажатие кнопки
if (b->state==1) { //отпускаем нажатую кнопку
digitalWrite(b->pin, LOW);
b->press_cnt--;
b->state=0;
if (b->press_cnt>0){ //еще надо нажимать, устанавливаю промежуток между нажатиями
b->next_poll=BUT_DELAY+millis();
}
} else { // нажимаем кнопку
digitalWrite(b->pin, HIGH);
b->next_poll=BUT_MS+millis(); // сколько ms удерживать кнопку
b->state=1;
}
}
// ---------------------------------
// обработчик "виртуальных кнопок" - нужен для назначения действий на кнопки
// пульта внутри ардуино (например сменить режим монитора, увеличить/уменьшить яркость и т.д.
void virt_keys(){
switch (aino.ir_dt){
case (83585175): // source button
press_button(&av,1);
send_data=false;
aino.ir_dt=0;
break;
case (83599965): // view button - увеличиваем яркость
if (ulk<=MAX_ULK) ulk++;
mills_poll_AUTO_LIGHT=0;
send_data=false;
aino.ir_dt=0;
break;
case (83602005): // back music - уменьшаем яркость
if (ulk>0) ulk--;
mills_poll_AUTO_LIGHT=0;
send_data=false;
aino.ir_dt=0;
break;
}
}
//------------------------------------------
//****************резистивные кнопки**************************
void poll_JOYSTICK()
{
unsigned long temp;
temp = analogRead(JOYSTICK_PIN); // прочитать данные АЦП
if( temp < 1023 && aino.ir_dt==0) { // Приоритет ИК выше джойстика
if (temp==res_dt) {// нету дребезга
aino.ir_dt=temp;//+0x110000;
p_delay(JOYSTICK,JOYSTICK_DELAY); // задержка между повторами
send_data=true;
} else {
p_delay(JOYSTICK,JOYSTICK_DELAY0); // задержка на дребезг
}
}
res_dt=temp;
}
//------------------------------------------
// вольтметр
void poll_VOLTMETER(){
int sens = analogRead(VOLT_SENS)>>2; // /4 :)
if (sens!=aino.volt) {
aino.volt=sens;
send_data=true;
}
p_delay(VOLTMETER,VOLTMETER_DELAY); // задержка для следующего опроса.
}
//------------------------------------------
// Включение усилителя
void poll_AMP(){
digitalWrite(AMP_PIN, HIGH);
p_delay(AMP,3600*24*1000); // задержка для следующего опроса -сутки
}
//--------------------------------
// Опрос автоматической корретировки света
// алгоритм работы взят у ув. CHIP-а (all-in-one project)
// изменения: сравнения с кратностью 30 заменены на сдвиг вправо на 5 битов (деление на 32)
// введен сдвиговый коэфициент для корректировки яркости с пульта
// свет=(свет>>4 + коэф_свет - МАКС_СВЕТ_КОЭФФ/2)
// для простоты показываю изменение яркости в винде от датчика и кнопок на пульте
// думаю, что будет удобно. если не настраивать кнопок работать будет по алгоритму CHIP
// \датчик| 0 32 64 128 160 192 224
// коэф \ |-----------------------------------------------------
// 0 | 2 2 2 64 96 128 160
// 1 | 2 2 64 96 128 160 192
// 2 | 2 2 64 96 128 160 192
// 3 | 2 64 96 128 160 192 255
// 4 | 2 64 96 128 160 192 255
// 5 | 64 96 128 160 192 255 255
// 6 | 64 96 128 160 192 255 255
void poll_AUTO_LIGHT(){ // управление яркостью
byte light;
int sens = analogRead(LIGHT_SENS)>>2; // /4 :) // интенсивность
sens=((sens>>4)+ulk-MAX_ULK/2)>>1; // обработка коэффициента
light=sens<=0?2:(sens>=6?255:(sens+1)<<5);
if (light!=aino.light) {
aino.light=light;
send_data=true;
}
p_delay(AUTO_LIGHT,AUTO_LIGHT_DELAY); // задержка для следующего опроса.
}
//--------------------------------
// обработка переключения моника на камеру заднего вида
// задача не требуется обработки в реальном времени,
// поэтому упрощаем алгоритм до срабатывания 1 раз в REVL_DELAY мс
void poll_REVL(){
byte temp;
temp=revl_state;
if (digitalRead(REVL_PIN) != HIGH){
switch (revl_state) {
case 0:
revl_state=1;
break;
case 1:
revl_state=2;
press_button(&av,TO_AV);
}
} else {
if (revl_state==2) {
press_button(&av,TO_VGA);
}
revl_state=0;
}
p_delay(REVL,REVL_DELAY); // задержка для следующего опроса.
if (temp!=revl_state){
EEPROM.write(REVL_STATE_OFFSET,revl_state);
}
}
//--------------------------------
// обработчик ИК пульта
// простая защита от ложных повторов от ИК-пульта
void poll_IR() {
if (need_ir_resume) { // восстанавливаем прием на IR
need_ir_resume=false;
irrecv.resume();
return;
}
if (irrecv.decode(&results)) {
need_ir_resume=true; // устанавливаю флаг восстановление приема
if (results.value > 0 && results.value < 0xFFFFFFFF) {
aino.ir_dt = results.value;
p_delay(IR,IR_DELAY_P); // Пауза после первого нажатия и перед удержанием (перед повторами)
old_ir = aino.ir_dt;
}
if (results.value == 0xFFFFFFFF )
{
aino.ir_dt=old_ir;
p_delay(IR,IR_DELAY_R); //пауза между повторами
}
send_data=true;
}
}
//----------------------------------
// обработчик энкодера
void poll_ENC()
{
if( aino.ir_dt==0 ) { // Не занят отправкой
if (X2400>0){
aino.ir_dt=0x2400;
X2400--;
send_data=true;
} else
if (X1500>0) {
aino.ir_dt=0x1500;
X1500--;
send_data=true;
}
}
p_delay(ENC,X2400>0||X1500>0?ENC_DELAY0:ENC_DELAY); // если есть еще срабатывания - короткая задержка
}
//*******************обработка прерывания***********************
void doEncoderA()
{
if(digitalRead(encoder0PinB)==HIGH){X2400++;}else{X1500++;}
}
//*******************обработка прерывания***********************
void doEncoderB()
{
if(digitalRead(encoder0PinA)==HIGH){X1500++;}else{X2400++;}
}