Ещё одна простая метеостанция с часами

Denifer
Offline
Зарегистрирован: 14.04.2015

Моя версия электронных часов под управлением Arduino из того, что накопилось с китайских магазинов. Часы показывают время, дату, температуру, влажность, давление в комнате и температуру на улице. Имеется таймер и будильник. Есть три способа отображения времени: мелко, крупно и в бинарном виде. Есть почасовой архив всех измеряемых параметров с возможность хранить во внутренней памяти Arduino или во внешней. Архив при желании можно весь выдать на терминал. Есть даже игрушка «Змейка». Управление с любого ИК пульта ДУ. Корпус склеен из реек сечением 10х40, задняя стенка вырезана из старого блока питания, разъёмы от сломанной клавиатуры. Экран собран из четырёх матриц 8х8 на MAX7219, соединённых последовательно. Ничего кроме матриц не спаяно, легко добавить или убрать лишнее. SPI и Rx/Tx свободны для подключения при желании flash-накопителя, сетевой карты, bluetooth или ещё чего-нибудь. Были планы сделать авторегулировку яркости, но оказалось, что на минимуме и ночью хорошо, и солнечным днём всё видно. Чисто для красоты можно закрыть экран тканью, как это сделано на акустике, но подходящей пока не увидел. При желании экран можно выключить совсем. Выдача параметров в интернет тоже была удалена за ненадобностью. Интересно сделать, а пользоваться неинтересно. Та же ситуация и с флэшками, нескольких килобайт для архива хватает. Нет настройки даты/времени, можно задать только на этапе прошивки. Но после подбора поправки или выбора более точного модуля времени отпадает нужда в его вводе, один раз ввёл и забыл. Ну и для полноценного будильника, конечно, нужен усилитель для динамика. Для проигрывания мелодии или песен удобно использовать mp3 модуль WTV020-SD-16P. Или можно сделать говорящие часы.

Компоненты:

- Arduino Nano V3;

- 4 светодиодных матрицы 8х8 с MAX7219;

- модуль времени DS1307;

- датчик давления и температуры BMP180;

- внешняя память AT24C256;

- датчик температуры DS18B20;

- датчик влажности и температуры DHT22;

- ИК приёмник TSOP4838;

- ИК пульт;

- Динамик;

- Монтажная плата;

- Рейка 10х40 для корпуса;

- Резисторы, провода, разъёмы;

- Сломанный БП и клавиатура;

Схема соединений:

Управление и описание экранов:

Фото:

Видео работы:

http://www.youtube.com/watch?v=XLUoIt8WyUk

Код:

#include <LedControl.h>
#include <SFE_BMP180.h>
#include <Wire.h>
#include <DHT22.h>
#include <RTClib.h>
#include <IRremote.h>
#include <OneWire.h>
#include <DallasTemperature.h>

//таблица символов размером 3 на 4 точки ==============================================================
byte symbols3x4[54] = {
  B1111, B1001, B1111, //0
  B1001, B1111, B0001, //1
  B1011, B1011, B1101, //2
  B1001, B1101, B1011, //3
  B1110, B0010, B1111, //4
  B1101, B1101, B1011, //5
  B1111, B0101, B0111, //6
  B1000, B1000, B1111, //7
  B0111, B1101, B1111, //8
  B1110, B1010, B1111, //9
  B1111, B1001, B1111, //O  Off 0xA
  B0000, B1111, B1010, //f
  B0000, B1111, B1010, //f
  B0000, B0000, B1111, //   On  0xD
  B1001, B1111, B0000, //O
  B0111, B0100, B0011, //n
  B1000, B1111, B1000, //T
  B1111, B1010, B1111  //A
};

//таблица символов размером 3 на 5 точек ==============================================================
byte symbols3x5[60] = {
  B11111, B10001, B11111, //0
  B10001, B11111, B00001, //1
  B10111, B10101, B11101, //2
  B10101, B10101, B11111, //3
  B11100, B00100, B11111, //4
  B11101, B10101, B10111, //5
  B11111, B10101, B10111, //6
  B10000, B10000, B11111, //7
  B11111, B10101, B11111, //8
  B11101, B10101, B11111, //9
  B00000, B00000, B00000, //   0xA
  B11111, B10000, B11111, //П  0xB
  B11111, B10101, B01111, //В
  B11111, B10001, B10001, //С
  B11100, B00100, B11111, //Ч
  B01111, B00010, B01111, //н
  B01000, B01111, B01000, //т
  B01111, B01010, B01110, //р
  B01111, B01011, B01011, //б
  B01111, B01001, B01001  //с
};

//таблица символов размером 6 на 6 точек ==============================================================
byte symbols6x6[60] = {
  B111111, B111111, B100001, B100001, B111111, B111111, //0
  B100001, B100001, B111111, B111111, B000001, B000001, //1
  B101111, B101111, B101011, B101011, B111011, B111011, //2
  B100011, B100011, B101011, B101011, B111111, B111111, //3
  B111100, B111100, B000100, B000100, B111111, B111111, //4
  B111011, B111011, B101011, B101011, B101111, B101111, //5
  B111111, B111111, B101001, B101001, B101111, B101111, //6
  B100000, B100000, B100000, B100000, B111111, B111111, //7
  B001111, B111111, B101001, B101001, B111111, B001111, //8
  B111101, B111101, B100101, B100101, B111111, B111111  //9
};

//индексы букв дней недели в symbols3x5 ===============================================================
byte arrayDayOfWeek[7] = {29, 15, 26, 37, 46, 16, 38};

//слово Go ============================================================================================
byte symbolGo[8] = {
  B01111100, B01000100, B01010100, B01011100, 
  B00000000, B00011100, B00010100, B00011100
};
//изображение трясущейся змейки
byte symbolSnake[3] = {B11110110, B11011100, B11110110};

//инициализация памяти для записи лога ================================================================
#define EEADDRESS 0x50                                         //адрес подключаемого модуля памяти
#define USEEXTERNALMEMORY 1                                    //выбор внутренней или внешней памяти
#if USEEXTERNALMEMORY                                          //длина лога и функции чтения/записи
  #define MAXSAVERECORDS 4090                                  //...для внешней памяти
  #define memoryRead extEEPROMRead
  #define memoryWrite extEEPROMWrite
#else
  #include <EEPROM.h>                                          //...для внутренней памяти
  #define MAXSAVERECORDS 123
  #define memoryRead EEPROM.read
  #define memoryWrite EEPROM.write  
#endif

//инициализация пульта и ИК приёмника =================================================================
#define BUTTON_MODE_L 0xFFA25D                                 //кнопка смены режима влево
#define BUTTON_MODE_R 0xFFE21D                                 //кнопка смены режима вправо
#define BUTTON_ENTER 0xFF629D                                  //кнопка выбора  
#define BUTTON_UP_L 0xFF22DD                                   //кнопка левый +
#define BUTTON_DOWN_L 0xFFE01F                                 //кнопка левый -
#define BUTTON_UP_R 0xFF02FD                                   //кнопка правый +
#define BUTTON_DOWN_R 0xFFA857                                 //кнопка правый -
#define BUTTON1 0xFF30CF                                       //кнопка 1
#define BUTTON2 0xFF18E7                                       //кнопка 2
#define BUTTON3 0xFF7A85                                       //кнопка 3
#define BUTTON4 0xFF10EF                                       //кнопка 4
#define BUTTON5 0xFF38C7                                       //кнопка 5
#define BUTTON6 0xFF5AA5                                       //кнопка 6
#define BUTTON7 0xFF42BD                                       //кнопка 7
#define BUTTON8 0xFF4AB5                                       //кнопка 8
#define BUTTON9 0xFF52AD                                       //кнопка 9
#define BUTTON_SAVE 0xFF906F                                   //кнопка сохранения
#define BUTTON_RESET 0xFF906F                                  //кнопка сброса
#define BUTTON_SCREENOFF 0xFFB04F                              //кнопка выключения экрана
#define IRRECVPIN 3                                            //пин ИК приёмника
IRrecv tsop(IRRECVPIN);

//инициализация датчика давления BMP180 (I2C) =========================================================
#define ALTITUDE 166.0                                         //высота над уровнем моря
SFE_BMP180 bmp180;

//инициализация модуля времени DS1307 (I2C) ===========================================================
RTC_DS1307 ds1307;

#define BEEPTIMEOUT   500                                      //интервал пиков
#define SENSORTIMEOUT 3000                                     //интервал опроса датчиков
#define LOGTIMEOUT    60000                                    //интервал проверки лога и поправки
long lastTickTime = -10000,                                    //обновление экрана
     lastSensorTime = -10000,                                  //чтение датчиков
     lastToneTime = -10000,                                    //бипер
     lastSaveTime = -60000;                                    //сохранение в лог

//инициализация экранов LED ===========================================================================
#define DINPIN 5                                               //пины LED матрицы
#define CLKPIN 6
#define CSPIN 7
#define NUMDEVICES 4                                           //количество матриц
#define LEDSIZE 8                                              //размер LED матрицы
#define SCREENSIZE 16                                          //общий размер экрана
LedControl matrix2x2 = 
  LedControl(DINPIN, CLKPIN, CSPIN, NUMDEVICES);
word screen[SCREENSIZE];                                       //массив точек экрана (16х16 бит)

//инициализация датчика температуры и влажности DHT22 =================================================
#define DHT22PIN 2                                             //пин подключения датчика
DHT22 dht22(DHT22PIN);  

//инициализация датчика температуры DS18B20 ===========================================================
#define DS18B20PIN 4                                           //пин подключения датчика
DeviceAddress ds18b20Address;                                  //адрес датчика
OneWire oneWire(DS18B20PIN);
DallasTemperature ds18b20(&oneWire);  

//кнопки управления и описание игры "змейка" ==========================================================
#define BUTTON_RIGHT 0xFF02FD                                  //поворот направо 
#define BUTTON_LEFT 0xFF22DD                                   //поворот налево
#define MAX_LENGTH_SNAKE 50                                    //максимальная длина змейки
#define DIRECT_TOP    0                                        //индексы направлений...
#define DIRECT_RIGHT  1
#define DIRECT_BOTTOM 2
#define DIRECT_LEFT   3
#define ACCELERATION  8                                        //ускорение по мере удлинения
char snakeDirect;                                              //направление движения
byte snakeXY[MAX_LENGTH_SNAKE];                                //координаты змейки (XY в полубайтах)
byte appleX, appleY;                                           //координаты яблока
byte snakeLength = 0;                                          //длина змейки 
byte snakeScore = 0;                                           //количество очков
byte appleBlink = 0;                                           //мигание яблока
char DirectToXY[4] = { 1, 16, -1, -16};                        //инкримент/декримент для полубайт

// ====================================================================================================
char lastSaveHour = -1;                                        //час последнего сохранения в лог
byte lastCalibrationDay;                                       //день последней калибровки часов
byte modeMain = 0;                                             //режим
byte modeTime = 0;                                             //режим режима
byte alarmHour = 0, alarmMinute = 0,                           //часы/минуты будильника
     timerHour = 0, timerMinute = 0;                           //часы/минуты таймера
byte beepCount = 0;                                            //количество пиков
boolean timerON = 0, alarmON = 0,                              //работа таймера, будильника
        RefreshScreen = 0,                                     //требование обновления экрана
        ScreenOnOff = 1;                                       //вкл/выкл экран
DateTime dateTime = 0;                                         //
decode_results irResultsOld;                                   //принятый код с пульта
int arcTail, arcTailTemp;                                      //номер последней запись в логе
long timerTime = 0;                                            //
word timeDelta = 500;                                          //скорость обновления экрана (мс)
long int aB[9] = {BUTTON1, BUTTON2, BUTTON3, BUTTON4, BUTTON5, //массив цифровых кнопок пульта
                  BUTTON6, BUTTON7, BUTTON8, BUTTON9};

//вывод в позицию х числа digit в бинарном виде точками 2х2 (maxDot максимальная длина) ===============
void outBinColumn(byte x, byte digit, byte maxDot) {
  x = x * 3 + 1;                                               
  for (byte i = 0; i < maxDot; i++) {                          
    word mask = B00000011 << i * 3;
    if (digit & 0x1) {
      screen[x] |= mask;
      screen[x + 1] |= mask;
    } 
    digit >>= 1;                                                          
  }
}

//вывод двухсимвольного числа offset размером wh (ширина 4 бита + высота 4 бита) в позицию x,y ========
void outTwoSymbol(byte x, byte y, byte offset, byte wh) {
  byte widthNext = wh >> 4;
  widthNext = widthNext + 1 + (~widthNext & 1);
  outOneSymbol(x,             y, offset / 10, wh);
  outOneSymbol(x + widthNext, y, offset % 10, wh);              
}

//вывод символа по смещению offset размером wh в позицию x,y ==========================================
void outOneSymbol(byte x, byte y, byte offset, byte wh) {
  word k;
  byte width = wh >> 4;
  byte height = wh & 0x0F; 
  offset *= width;
  y = 16 - height - y;
  for (byte i = 0; i < width; i++, offset++, x++) {
    if (height == 4) {
      k = symbols3x4[offset];
    } else if (height == 5) {
      k = symbols3x5[offset];
    } else {
      k = symbols6x6[offset];  
    } 
    screen[x] |= (k << y);
  }
}

//вывод режима работы (On/Off) и идикатора таймера/будильника (T/A) в позиции d1, d2 ==================
void outOnOffAndType(byte d1, byte d2) {
  for (byte i = 7, *ptrOnOff = &symbols3x4[d1]; i < 16; screen[i++] |= *ptrOnOff++);
  outOneSymbol(0, 12, d2, 52); 
}        

//вывод 4-ёх двухсимвольных значений 3х4 (второе может быть отрицательным) ============================
void outDataOfSensor(byte d1, char d2, byte d3, byte d4) {
  outTwoSymbol(0, 7, d1, 52);
  if (d2 > -1 || d2 < -9) {
    if (d2 < 0) {
      d2 = -d2;
      screen[8] |= 64;
    }  
    outTwoSymbol(9, 7, d2, 52);
  } else {
    outOneSymbol(13, 7, -d2, 52);          
    screen[11] |= 64;
  }
  outTwoSymbol(0, 12, d3, 52);
  outTwoSymbol(9, 12, d4, 52); 
}

//вывод двух двухсимволных чисел 3х5 в верхней части ==================================================
void outMainString(byte d1, byte d2) {
  outTwoSymbol(0, 0, d1, 53);
  outTwoSymbol(9, 0, d2, 53);
}  

//очистка экрана ======================================================================================
void clearScreen() {
  for (byte i = 0; i < SCREENSIZE; i++) {
    screen[i] = 0;
  }
}

//вывод мигающей точки в x,y ==========================================================================
void dotBlink(byte x, byte y) {
  if (appleBlink) {  
    bitSet(screen[x], y);
  }
}

//вывод матрицы на экран с переворачиваниями по четвертям =============================================
void printScreen() {
  char k[4][6] = {{7, -1, -1, 0, 8, 1}, {0, 8, 1, 15, 7, -1}, 
                  {15, 7, -1, 0, 8, 1}, {8, 16, 1, 15, 7, -1}};
  for (char a = 0; a < NUMDEVICES; a++) {
    for (char y = k[a][0], i = 0; y != k[a][1]; y += k[a][2], i++) {
      byte Row = 0;
      for (char x = k[a][3]; x != k[a][4]; x += k[a][5]) {
        Row <<= 1;
        if (screen[x] & (1 << y)) {
          Row++;
        }                                                               
      }
      matrix2x2.setRow(a, i, Row);
    }
  }
  RefreshScreen = 0;
}

//чтение с датчиков ===================================================================================
void readSensor(double &T1, double &T2, double &P, double &T3, double &H) {
  ds18b20.setWaitForConversion(false);                         //чтение с DS18B20
  ds18b20.requestTemperatures();                               //...в асинхронном режиме
  ds18b20.setWaitForConversion(true);
  T1 = ds18b20.getTempC(ds18b20Address);                       //...температуры

  char status = bmp180.startTemperature();                     //чтение с BMP180
  if (status != 0) {
    delay(status);
    if (bmp180.getTemperature(T2) != 0) {                      //...температуры
      status = bmp180.startPressure(3);
      if (status != 0) {
        delay(status);
        if (bmp180.getPressure(P, T2) != 0) {                  //...давления
        } 
      } 
    } 
  } 

  if (!dht22.readData()) {                                     //чтение с DHT22
    H = dht22.getHumidity();                                   //...влажности
    T3 = dht22.getTemperatureC();                              //...температуры
  } 
}

//генерируем случайное яблоко =========================================================================
void outApple() {
  while (isSnake(appleX = random(1, 15), appleY = random(1, 15)));
}

//проверка принадлежности точки x,y змейке ============================================================
boolean isSnake(int x, int y) {
  x = (x << 4) + y;
  for (int i = 0; i < snakeLength - 1; i++) {
    if (x == snakeXY[i]) {
      return true;
    }
  }
  return false; 
}

//инициализация змейки ================================================================================
void snakeInit() {
  snakeDirect = 0;                                             //направление
  snakeLength = 1;                                             //длина
  snakeXY[0] = B01110111;                                      //голова в 7,7
  for (byte i = 1; i < MAX_LENGTH_SNAKE; i++) {                //обнуляем тело
    snakeXY[i] = 0;
  }
  outApple();                                                  //генерируем яблоко
}

//запись байта data по смещению eeOffset во внешний модуль памяти =====================================
void extEEPROMWrite(unsigned int eeOffset, byte data) {
  Wire.beginTransmission(EEADDRESS);
  Wire.write((int)(eeOffset >> 8));
  Wire.write((int)(eeOffset & 0xFF));
  Wire.write(data);
  Wire.endTransmission();
  delay(4);
}

//чтение байта по смещению eeOffset из внешнего модуль памяти =========================================
byte extEEPROMRead(unsigned int eeOffset) {
  byte resultByte = 0xFF;
  Wire.beginTransmission(EEADDRESS);
  Wire.write((int)(eeOffset >> 8));
  Wire.write((int)(eeOffset & 0xFF));
  Wire.endTransmission();
  Wire.requestFrom(EEADDRESS, 1);
  if (Wire.available()) {
    resultByte = Wire.read();
  }  
  return resultByte;
}

//обработка кнопок +/- ================================================================================
boolean buttonInputArrow(unsigned long b, boolean t, byte &h, byte &m) {
  if (!t) {
    if (b == BUTTON_UP_L) {
      if (h < 23) {
        h++;
        return 1;      
      }
    } else if (b == BUTTON_DOWN_L) {
      if (h > 0) {
        h--;
        return 1;
      }
    } else if (b == BUTTON_UP_R) {
      if (m < 59) {
        m++;
        return 1;      
      }
    } else if (b == BUTTON_DOWN_R) {
      if (m > 0) {
        m--;
        return 1;
      }
    }
  }  
  return 0;  
}

//вывод в последовательный порт значения ячейки памяти x с поправкой y и строкой с ====================
void outFromEEPROM(word x, word y, char* c) {
  Serial.print(memoryRead(x) + y); 
  Serial.print(c);
}

// SETUP ==============================================================================================
void setup() {
  Serial.begin(115200);
  Wire.begin();
  bmp180.begin();
  tsop.enableIRIn();
  ds18b20.begin();
  ds18b20.getAddress(ds18b20Address, 0);
  //ds18b20.setResolution(9);
  for (byte i = 0; i < 4; i++) {
    matrix2x2.setIntensity(i, 0);
  }
  lastCalibrationDay = memoryRead(0);                          //начальные значения из памяти...
  alarmHour = memoryRead(1);
  alarmMinute = memoryRead(2);
  arcTail = (memoryRead(4) << 8) + memoryRead(3);
  //ds1307.adjust(DateTime(__DATE__, __TIME__));               //ввод времени
}

// LOOP ===============================================================================================
void loop() {
  
  double tTh, tBMP, pBMP, tDHT, hDHT;                          //значения с датчиков
  long timeD = 0;
  byte year2dig, tmp;

  if (millis() - lastToneTime > BEEPTIMEOUT) {                 //пиканье будильника или таймера
    lastToneTime = millis();    
    if (beepCount) {
      beepCount--;
      tone(8, 262, 250); //Serial.println("!");
    }
  }

  if (millis() - lastSensorTime > SENSORTIMEOUT) {             //чтение датчиков (3 сек)
    lastSensorTime = millis();
    readSensor(tTh, tBMP, pBMP, tDHT, hDHT);
    pBMP = pBMP * 0.750063755419211;                           //в мм.рт.ст.
  }

  if (millis() - lastSaveTime > LOGTIMEOUT) {                  //запись в лог (1 минута)
    lastSaveTime = millis();
    dateTime = ds1307.now();                                   //читаем время
    if (dateTime.minute() == 0 &&                              //если 0 минут нового часа, то пишем лог
        lastSaveHour != dateTime.hour()) {
      arcTail = (arcTail == MAXSAVERECORDS) ? 0 : ++arcTail;   
      word i = 16 + arcTail * 8;                               //смещение в памяти
      memoryWrite(i + 0, dateTime.year() % 100);               //год
      memoryWrite(i + 1, dateTime.month());                    //месяц
      memoryWrite(i + 2, dateTime.day());                      //день
      memoryWrite(i + 3, dateTime.hour());                     //час
      memoryWrite(i + 4, (char)tDHT + 100);                    //температура внутри
      memoryWrite(i + 5, (char)tTh + 100);                     //температура снаружи 
      memoryWrite(i + 6, (byte)hDHT);                          //влажность
      memoryWrite(i + 7, int(pBMP) % 100);                     //давление
      memoryWrite(3, arcTail & 0xFF);                          //номер последней записи (2 байта)
      memoryWrite(4, arcTail >> 8);      
      lastSaveHour = dateTime.hour();                          //сохраняем час записи
    }
    
    if (dateTime.hour() == 0 &&                                //если 0 часов нового дня
        lastCalibrationDay != dateTime.day()) {  
      ds1307.adjust(DateTime(dateTime.unixtime() - 2));        //подводим на две секунды
      lastCalibrationDay = dateTime.day();                     //запоминаем день
      memoryWrite(0, dateTime.day());
    }   
  }     

  //обработка кнопок от пульта
  decode_results irResults;
  if (tsop.decode(&irResults)) {                               //если нажата кнопка                                     
    unsigned long btn = irResultsOld.value;                    //Serial.println(irResults.value, HEX);
                                                               //обработка общих на все режимы кнопок
    if (irResults.value == 0xFFFFFFFF && (modeMain != 5) &&    //если 0xFFFFFFFF, то кнопка зажата...
       (btn == BUTTON_UP_L || btn == BUTTON_UP_R ||            //для цифровых кнопок повтор, кроме змеи
        btn == BUTTON_DOWN_L || btn == BUTTON_DOWN_R)) {
      irResults = irResultsOld;
    } 
    if (irResults.value == BUTTON_MODE_R) {                    //следующий режим
      modeMain = (modeMain < 6) ? ++modeMain : 0;
      arcTailTemp = arcTail; 
      modeTime = 0;
    } else if (irResults.value == BUTTON_MODE_L) {             //предыдущий режим
      modeMain = (modeMain > 0) ? --modeMain : 6;
      arcTailTemp = arcTail; 
      modeTime = 0;
    } else if (irResults.value == BUTTON_SCREENOFF) {          //вкл/выкл экран
      ScreenOnOff = !ScreenOnOff;
    }
    
    switch (modeMain) {                                        //порежимная обработка кнопок пульта
      case 0:                                                  //режим часов с датчиками
        if (irResults.value == BUTTON_ENTER) {                 //режим время/секунды/дата/день_год
          modeTime = (modeTime < 3) ? ++modeTime : 0;                   
        } else if (irResults.value == BUTTON_RESET && modeTime == 1) { //сброс секунд на 0 (подводка)
          timeD = dateTime.unixtime() % 60;
          if (timeD > 30) {
            timeD -= 60;
          }
          ds1307.adjust(DateTime(dateTime.unixtime() - timeD));
        }
      break;
      case 1:                                                  //режим таймера
        if (irResults.value == BUTTON_ENTER) {                 //старт/стоп таймера
          if (!timerON && (timerHour > 0 || timerMinute > 0)) {//старт
            timerON = 1;
            timerTime = dateTime.unixtime() + 3600 * timerHour + 60 * timerMinute;
          } else {                                             //стоп
            timerHour = timerMinute = timerON = 0;
          }                                                         
        } else if (buttonInputArrow(irResults.value, timerON,  //+/- часы/минуты
                   timerHour, timerMinute)) { 
        } else for (byte i = 0; i < 10; i++) {                 //быстрый ввод минут и авто запуск
          if ((irResults.value == aB[i]) && !timerON) {
            timerHour = 0;
            timerMinute = i + 1;
            timerON = 1;
            timerTime = dateTime.unixtime() + 60 * timerMinute;
          }
        }
      break;
      case 2:                                                  //режим будильника
        if (irResults.value == BUTTON_ENTER) {                 //старт/стоп будильника
          alarmON = ++alarmON & 0x1;
        } else if (buttonInputArrow(irResults.value, alarmON,  //+/- часы/минуты 
                   alarmHour, alarmMinute)) {  
        } else if (irResults.value == BUTTON_SAVE) {           //сохранить будильник в память
          memoryWrite(1, alarmHour);
          memoryWrite(2, alarmMinute);
        }
      break;
      case 3:                                                  //режим больших часов
        if (irResults.value == BUTTON_ENTER) {                 //режим время/секунды/дата/день_год
          modeTime = (modeTime < 3) ? ++modeTime : 0;
        }
      break;
      case 4:                                                  //режим бинарных часов
        if (irResults.value == BUTTON_ENTER) {                 //режим время/дата/
          modeTime = ++modeTime & 0x1;
        }
      break;
      case 5:                                                  //игра "змейка"
        if (irResults.value == BUTTON_LEFT) {                  //поворот налево
          modeTime = 1;
          if (--snakeDirect < 0) {
            snakeDirect = DIRECT_LEFT;
          }
        } else if (irResults.value == BUTTON_RIGHT) {          //поворот направо
          modeTime = 1;
          if (++snakeDirect > 3) {
            snakeDirect = DIRECT_TOP;
          }
        }
      break;
      case 6:                                                  //режим просмотра архива  
        if (irResults.value == BUTTON_LEFT) {                  //предыдущая запись
          if (--arcTailTemp < 0) {
            arcTailTemp = MAXSAVERECORDS;
          }
        } else if (irResults.value == BUTTON_RIGHT) {          //следующая запись
          arcTailTemp = (arcTailTemp == MAXSAVERECORDS) ? 0 : ++arcTailTemp;    
        } else if (irResults.value == BUTTON_ENTER) {          //режим час_день/месяц_год
          modeTime = ++modeTime & 0x01; 
        } else if (irResults.value == BUTTON_SAVE) {           //вывод архива на терминал
          for (word j = 0; j <= MAXSAVERECORDS; j++) { 
            word i = j * 8 + 16;
            outFromEEPROM(i + 0, 2000, ".");                   //год
            outFromEEPROM(i + 1, 0, ".");                      //месяц
            outFromEEPROM(i + 2, 0, " ");                      //день
            outFromEEPROM(i + 3, 0, ":00 ");                   //час
            outFromEEPROM(i + 4, -100, " C ");                 //температура внутри
            outFromEEPROM(i + 5, -100, " C ");                 //температура снаружи
            outFromEEPROM(i + 6, 0, " % ");                    //влажность 
            outFromEEPROM(i + 7, 700, " mmHg");                //давление
            Serial.println("");           
          }  
        }
      break;      
    } 

    if (modeMain != 5) {                                       //в режиме змейки обновить экран
      RefreshScreen = 1;
    }
    irResultsOld = irResults;
    tsop.resume();
  }

  if (RefreshScreen || millis() - lastTickTime > timeDelta) {  //главный цикл перерисовки экрана
    lastTickTime = millis();
    dateTime = ds1307.now();                                   //время с модуля
    year2dig = dateTime.year() % 100;  
    appleBlink = ++appleBlink & 0x1;                           //мигание
 
    if (timerON && timerTime - long(dateTime.unixtime()) < 0) {//сработал таймер
      timerHour = timerMinute = timerON = timeD = 0;
      beepCount = 3;                                           //пикнуть три раза
    } else {
      timeD = 3600 * timerHour + 60 * timerMinute;
    }

    if ((dateTime.unixtime() % 86400 == 
        (long)alarmHour * 3600 + alarmMinute * 60) && !beepCount) { //сработал будильник
      beepCount = 5;                                           //пикнуть 5 раз
    }                                                                        

    clearScreen();
    if (ScreenOnOff) 
    switch (modeMain) {
      case 0:                                                  //вывод в режиме часов с датчиками
        if (modeTime == 0) {
          outMainString(dateTime.hour(), dateTime.minute());   //часы/минуты
          dotBlink(7, 10);                                     //мигающая точка
        } else if (modeTime == 1) {
          outTwoSymbol(9, 0, dateTime.second(), 53);           //секунды
          dotBlink(7, 10);                                     //мигающая точа
        } else if (modeTime == 2) {
          outMainString(dateTime.day(), dateTime.month());     //день/месяц
        } else {                                              
          tmp = arrayDayOfWeek[dateTime.dayOfWeek()];          //день недели
          outOneSymbol(0, 0, tmp / 10 + 10, 53);
          outOneSymbol(4, 0, tmp % 10 + 10, 53);           
          outTwoSymbol(9, 0, year2dig, 53);                    //год
        }
        outDataOfSensor(tDHT, tTh, hDHT, word(pBMP) % 100);    //4 датчика
      break;
      case 1:                                                  //вывод в режиме таймера
        if (timerON) {
          timeD = timerTime - dateTime.unixtime();
          outMainString(timeD / 3600, (timeD % 3600) / 60);    //часы/минуты
          outTwoSymbol(0, 6, timeD % 60, 53);                  //секунды
          dotBlink(7, 10);                                     //мигающая точка
        } else {
          outMainString(timerHour, timerMinute);               //часы/минуты
        }
        outOnOffAndType(timerON ? 39 : 30, 16);                //T/A On/Off
      break;
      case 2:                                                  //вывод на экран в режиме будильника
        outMainString(alarmHour, alarmMinute);                 //часы/минуты                        
        outOnOffAndType(alarmON ? 39 : 30, 17);                //T/A On/Off
      break;
      case 3:                                                  //вывод в режиме больших часов
        if (modeTime == 0) {
          outTwoSymbol(1, 1, dateTime.hour(), 102);            //часы
          outTwoSymbol(1, 9, dateTime.minute(), 102);          //минуты
          dotBlink(7, 8);                                      //мигающая точка
        } else if (modeTime == 1) {
          outTwoSymbol(1, 1, dateTime.second(), 102);          //секунды
          dotBlink(7, 8);                                      //мигающая точка
        } else if (modeTime == 2) {
          outTwoSymbol(1, 9, dateTime.day(), 102);             //день
          outTwoSymbol(1, 1, dateTime.month(), 102);           //месяц
        } else {
          outTwoSymbol(1, 1, year2dig, 102);                   //год
        }
      break;
      case 4:                                                  //вывод в режиме бинарных часов
        if (modeTime == 0) {
          tmp = dateTime.hour();
          outBinColumn(0, tmp > 12 ? tmp % 12 : tmp, 4);       //часы
          outBinColumn(1, dateTime.minute() / 10, 4);          //минуты
          outBinColumn(2, dateTime.minute() % 10, 4);
          outBinColumn(3, dateTime.second() / 10, 4);          //секунды
          outBinColumn(4, dateTime.second() % 10, 4);
        } else {
          outBinColumn(1, dateTime.dayOfWeek(), 3);            //день недели
          outBinColumn(2, dateTime.day(), 5);                  //день
          outBinColumn(3, dateTime.month(), 4);                //месяц
          outBinColumn(4, year2dig, 4);                        //год
        }
      break;
      case 5:                                                  //вывод в режиме змейки
        if (modeTime == 0) {                                   //конец/начало игры
          if (snakeScore) {
            outTwoSymbol(2, 2, snakeScore, 53);                //вывод счёта
          }
          for (byte i = 1, *ptrGo = &symbolGo[0]; i < 9;       //слово Go
               screen[i++] |= *ptrGo++); 
          screen[12] = symbolSnake[appleBlink];                //трясущаяся змейка
          screen[13] = symbolSnake[appleBlink + 1]; 
          snakeInit();
        } else {                                               //ход игры
          tmp = snakeXY[0] + DirectToXY[(byte)snakeDirect];
          if (isSnake(tmp >> 4, tmp & 0x0F) ||                 //съел сам себя
             ((tmp & 0xF0) == 0) || ((tmp & 0x0F) == 0) ||     //...или забор
             ((tmp & 0xF0) == 0xF0) || ((tmp & 0x0F) == 0x0F)) {
            snakeScore = snakeLength - 1;
            modeTime = 0;                                      //конец игры
          } else {
            for (byte i = snakeLength - 1; i > 0; i--) {       //сдвигаем массив
              snakeXY[i] = snakeXY[i - 1];
            }
            snakeXY[0] = tmp;  
            if (snakeXY[0] == (appleX << 4) + appleY) {        //если съели яблоко
              if (++snakeLength < MAX_LENGTH_SNAKE) {          //длина не максимальна, то растём
                outApple();                                    //новое яблоко
              }
            }
            screen[0] = screen[15] = 0xFFFF;                   //рисуем забор
            for (byte i = 1; i < SCREENSIZE - 1; i++) {
              screen[i] |= 0x8001;
            }
            for (byte i = 0; i < snakeLength; i++) {           //перерисовываем змейку
              byte sx = snakeXY[i] >> 4;
              byte sy = snakeXY[i] & 15;
              screen[sx] |= (1 << sy);
            }
            screen[appleX] |= (appleBlink << appleY);          //рисуем яблоко
          }
        }
      break;
      case 6:                                                  //вывод в режиме просмотра архива
        word i = arcTailTemp * 8 + 16;
        if (memoryRead(i) == 0) {                              //если ячейка пуста
          outTwoSymbol(0, 0, arcTailTemp / 100, 53);           //вывод номера ячейки
          outTwoSymbol(9, 0, arcTailTemp % 100, 53);
        } else {
          if (modeTime == 0) {                                 //режим даты
            outMainString(memoryRead(i + 2), memoryRead(i + 3)); //день и час
          } else {
            outMainString(memoryRead(i), memoryRead(i + 1));   //год и месяц     
          }
          outDataOfSensor(memoryRead(i + 4) - 100,             //температура внутри
                          memoryRead(i + 5) - 100,             //температура снаружи  
                          memoryRead(i + 6),                   //влажность
                          memoryRead(i + 7));                  //давление
        }  
      break;    
    }
    printScreen();                                             //вывод матрицы на экран
    if (modeMain == 5) {                                       //если режим игры 
      timeDelta = 500 - snakeLength * ACCELERATION;            //...ускоряем обновление  
    } else {
      timeDelta = 500;
    }
  }
}

Библиотеки я немного корректировал, чтобы сэкономить немного места, поэтому тоже выкладываю:

https://yadi.sk/d/Zp1v7gKwgpjie

Denifer
Offline
Зарегистрирован: 14.04.2015

Фото:

trembo
trembo аватар
Онлайн
Зарегистрирован: 08.04.2011

Супер! Нет слов!
Есть ещё похер в похеровницах!

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

trembo пишет:

Супер! Нет слов!
Есть ещё похер в похеровницах!

мне кажеться если доску потолще взять.. ну или скажем там пень с леса притащить и в него все поместить..

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

Puhlyaviy пишет:

мне кажеться если доску потолще взять.. ну или скажем там пень с леса притащить и в него все поместить..

не всё же делать из люминия :)

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

SU-27-16 пишет:

Puhlyaviy пишет:

мне кажеться если доску потолще взять.. ну или скажем там пень с леса притащить и в него все поместить..

не всё же делать из люминия :)


И не говори, кума. У самой муж пьяница.

a5021
Offline
Зарегистрирован: 07.07.2013

Puhlyaviy пишет:

мне кажеться если доску потолще взять.. ну или скажем там пень с леса притащить и в него все поместить..

Вот набросились. А мне показалось, что выглядит весьма интересно. А в интерьере, скажем, деревянного дома это вообще будет смотреться не плохо. Единственное, как-то с индикацией получилось не совсем. Цифры неразборчивые и придется напрягаться с их считыванием.