

//******************************************************************************************************************
// Спидометр с функцией передачи текущих координат на сервер 
// спидометр сделан на базе модуля ARDUINO NANO с использованием модулей GPS и GSM
//
//******************************************************************************************************************
//
//
//******************************************************************************************************************
//
// Управление:
// Кнопка 1 - переключает режимы по кругу, при нажатии более 3 секунд выключает спидометр
// Кнопка 2 - функционал зависит от режима
//
// Режимы:
// 1) Отображение скорости
//    на экране отображается скорость, километраж и время в пути
//    кнопка 2 переключает километраж и время в пути
//    при нажатии более 3 секунд сбрасывает километраж, время в пути и сохраняет результаты в памяти
// 2) Отображение времени
//    на экране отображается время и дата
//    кнопка 2 при нажатии более 3 секунд начинает переключать часовой пояс по кругу, переиод 500мс
// 3) Отображение координат
//    на экране отображаются текущие координаты и общее пройденное растояние
//    кнопка 2 при нажатии более 5 секунд сбрасывает общее пройденное растояние
// 4) Отображение сохраненных результатов 
//    на экране отображается дата и время сохранения, пройденое растояние и время в пути, максимальная скорость
//    кнопка 2 переключает сохранения по кругу
// 5) Отображения состояния GSM модуля
//    на экране отображается период передачи данных на сайт и IMEI модуля
//    возможный период передачи 5, 10, 15, 30, 45, 60 секунд
//    если модуль выключен IMEI не отображается
//    кнопка 2 отсылает USSD запрос для получения баланса
//    при нажатии более 3 секунд меняется период передачи или отключается GSM модуль  
//
//******************************************************************************************************************


#include "CyberLib.h"
#include <TinyGPSPlus.h>
#include <SoftwareSerial.h>
#include <EEPROM.h>
//#include <TimerOne.h>
#include "gfxfont.h"
#include "glcdfont.c"
#include "FreeSerifBold24pt7bNumbers.h"
#include "FreeSansBold9pt7bNumbers.h"
#include <avr/pgmspace.h>

//#include <avr/sleep.h>
//#include <avr/power.h>

#define SP_GSM
#define SP_VERSION "SW v1.1b - HW v1.0"

// hardware SPI
// MOSI (D1) - 11, CLK (D0) - 13, DC - 6, CS - 7, RESET - 8
#define OLED_SPI    1

// урл сервиса на который отсылаются данные
//  #define SP_URL "http://repnikov.ru/g.php?a="
#define SP_URL "http://shareobj.com/imei/"

#define VIN_OFF 3.2
#define VIN_MIN 3.4
#define VIN_MAX 4.0
#define SPEED_LEFT_POS 14
#define CLOCK_LEFT_POS 6
#define WAIT_MS 200

#define MINIMUM_ACCUMULATE_DISTANCE 10

#define AM2320_I2C_ADDRESS (0xB8 >> 1) 

// TX=9, RX=10 SoftwareSerial для GPS модуля
#define SSTX 2
#define SSRX 3

// TX=A1, RX=A0 SoftwareSerial для GSM модуля
#define SGTX A1
#define SGRX A0

// Many (but maybe not all) non-AVR board installs define macros
// for compatibility with existing PROGMEM-reading AVR code.
// Do our own checks and defines here for good measure...
#ifndef pgm_read_byte
 #define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
#ifndef pgm_read_word
 #define pgm_read_word(addr) (*(const unsigned short *)(addr))
#endif
#ifndef pgm_read_dword
 #define pgm_read_dword(addr) (*(const unsigned long *)(addr))
#endif

// Pointers are a peculiar case...typically 16-bit on AVR boards,
// 32 bits elsewhere.  Try to accommodate both...
#if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF)
 #define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr))
#else
 #define pgm_read_pointer(addr) ((void *)pgm_read_word(addr))
#endif

#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif

#ifndef _swap_int16_t
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
#endif

// данные для установки GPS модуля, частота обновления 5гц
const byte InitModem1[] PROGMEM = {0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xC8, 0x00, 0x01, 0x00, 0x01, 0x00, 0xDE, 0x6A, 0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30}; //5Hz (5 outputs per second)
// данные для установки GPS модуля, скорость обмена 38400кб/с
const byte InitModem2[] PROGMEM = {0xb5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x08, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xa8}; //28 - 38400                 
// данные для инициализации дисплея
const byte InitDisp[] PROGMEM = {0xAE, 0xD5, 0x80, 0xA8, 0x3F, 0xD3, 0x0, 0x40 | 0x0, 0x8D, 0x14, 0x20, 0x00, 0xA0 | 0x1, 0xC8, 0xDA, 0x12, 0x81, 0xCF, 0xd9, 0xF1, 0xDB, 0x40, 0xA4, 0xA6, 0x2E, 0xAF};

// данные для инициализации и передачи данных GPRS
const char gsm_string_0[] PROGMEM = "AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"";  //Установка настроек подключения
const char gsm_string_1[] PROGMEM = "AT+SAPBR=3,1,\"APN\",\"mts\"";
const char gsm_string_2[] PROGMEM = "AT+SAPBR=3,1,\"USER\",\"mts\"";
const char gsm_string_3[] PROGMEM = "AT+SAPBR=3,1,\"PWD\",\"mts\"";
const char gsm_string_4[] PROGMEM = "AT+SAPBR=1,1";  //Устанавливаем GPRS соединение
const char gsm_string_5[] PROGMEM = "AT+SAPBR=2,1";
const char gsm_string_6[] PROGMEM = "AT+HTTPINIT";  //Инициализация http сервиса
const char gsm_string_7[] PROGMEM = "AT+HTTPPARA=\"CID\",1";  //Установка CID параметра для http сессии
const char gsm_string_8[] PROGMEM = "";
const char gsm_string_9[] PROGMEM = "AT+HTTPACTION=0";
const char gsm_string_a[] PROGMEM = "AT+HTTPTERM";
const char gsm_string_b[] PROGMEM = "AT+SAPBR=0,1";
const char gsm_string_c[] PROGMEM = "AT+COPS?";
const char* const ATss[] PROGMEM = {gsm_string_0, gsm_string_1, gsm_string_2, gsm_string_3, gsm_string_4, gsm_string_5, gsm_string_6, gsm_string_7, gsm_string_8, gsm_string_9, gsm_string_a, gsm_string_b, gsm_string_c};
// тайминги для инициализации и передачи данных GPRS
int ATsDelays[] = {0, 0, 0, 0, 1, 30, 5, 1, 5, 20, 5, 5};
//#define ATsDelay 50
// текущий шаг передачи данных GPRS
static uint8_t init_gprs = 0;

const char at_csq[] PROGMEM = "AT+CSQ";

const char strKM[] PROGMEM = " KM";
const char strKMH[] PROGMEM = " KM/H";
const char strMEMO[] PROGMEM = "MEMO ";
const char strLAT[] PROGMEM = " Lat";
const char strLNG[] PROGMEM = " Lng";
//const char strS[] PROGMEM = " S";
const char strS[] PROGMEM = "s ";
//const char strM[] PROGMEM = " M";
const char strO[] PROGMEM = "<";
const char strC[] PROGMEM = ";";
const char strP[] PROGMEM = "%";
const char strCLR[] PROGMEM = " ";
const char strPS[] PROGMEM = {0x1E};
const char strPF[] PROGMEM = {0xDB};
const char strPH[] PROGMEM = {0xF4};
const char strPC[] PROGMEM = {0xF5};
const char strPE[] PROGMEM = {0xF3};
const char strPN[] PROGMEM = {0xF2};
const char strPU[] PROGMEM = {0xFE};
const char strPY[] PROGMEM = {0xF9};
const char strWAIT[] PROGMEM = "WAIT";
//const char strGSMON[] PROGMEM = "GSM ON ";
const char strGSMON[] PROGMEM = "GSM ";
const char strGSMOFF[] PROGMEM = "GSM OFF";
const char strPOWEROFF[] PROGMEM = "POWER OFF";
const char strIMEI[] PROGMEM = "IMEI: -";
const char strVERSION[] PROGMEM = SP_VERSION;

static signed long resultVcc;
static uint8_t bat, bat_o;
static uint16_t speed = 0;
static uint16_t speed_max = 0;
static double distance = 0;
static unsigned long distance_all = 0;
static double lat1 = 0;
static double lng1 = 0;
static unsigned long time_start = 0;
//int temperature; 
//int humidity;

static uint8_t buffer[26];
GFXfont *gfxFont;
int16_t
    _width, _height, // Display w/h as modified by current rotation
    cursor_x, cursor_y;

//struct DistanceDataObject
//{
//  double distance;
//  unsigned long time_start;
//  unsigned long time_off;
//};

struct DistanceSaveObject
{
  double distance;
  unsigned long time;
  uint16_t speed_max;
  char date[17];
};

byte m_pos1 = 0;
byte m_pos1t = 0;
byte m_pos2 = 0;
//byte m_pos2t = 0;
byte m_stat = 0;   //0 - button 1 triger, 1 - button 2 triger, 2 - blink bat, 3 - blink time, 4 - show time/km, 5 - Save triger, 6 - back speed triger, 7 - USSD triger
byte m_statt = 0;  // 0 - GSM ON/OFF triger, 1 - USSD triger lock, 3 - charge, 4 - old show time/km, 5 - GSM ready
//byte m_seting = 0; // 0 - GSM Power ON/OFF

bool is_clr_display = 0; // признак очистки экрана
bool is_triger_1 = 0; // ипользуетя для одиночного выполнения функций

long m_delay = 0;
long r_delay = 0;
uint8_t m_timezone = 3;

char strOut[30] = "";

uint8_t _second;
uint8_t _minute;
uint8_t _hour;
uint8_t _day;
uint8_t _month;
uint16_t _year;
  
SoftwareSerial ss(SSTX, SSRX);
SoftwareSerial sg(SGTX, SGRX);
uint8_t gsm_count = 0;
uint8_t gsm_count_c = 0;
uint8_t gsm_bat = 0;
uint8_t gsm_level = 0;
String gsm_operator;
char gsm_IMEI[16];
String ds;

TinyGPSPlus gps;

void(* resetFunc) (void) = 0; //объявляем функцию reset с адресом 0

void setup() {
  D4_In; // D4 на вход, кнопка 1
  D5_In; // D5 на вход, кнопка 2
  D9_Out; // D9 на выход, управление питанием спидометра
  D16_Out; // A2 на выход, управление питанием GSM модуля
  D17_Out; // A3 на выход, управление питанием GPS модуля
  
  D4_High; // D4 подтягиваем внутренним резистором на плюс птания
  D5_High; // D5 подтягиваем внутренним резистором на плюс птания
  D9_High; // D9 включаем спидометр
  D17_High; // A3 включаем GPS модуль


//  analogReference(INTERNAL);

// нициализация SPI
  StartSPI(0, 8, 1);//MODE0, DIV8, MSBFIRST инициализация SPI
  
// инициализация дисплея  MOSI (D1) - 11, CLK (D0) - 13, DC - 6, CS - 7, RESET - 8
  init_OLED(); // инициализация дисплея

//  Serial.begin(19200);

  ss.begin(9600); // запускаем сериал порт для GPS на 9600
  // загружаем в модуль GPS команду на частоту обновления 5 раз в секунду
  for (int k = 0; k < 22; k++)
  {
    ss.write(pgm_read_byte_near(InitModem1 + k));
  }
  delay(10); // ждем 10мс
  // загружаем в модуль GPS команду на скорость обмена 38400
  for (int k = 0; k < 28; k++)
  {
    ss.write(pgm_read_byte_near(InitModem2 + k));
  }
  delay(100); // ждем 100мс
  ss.begin(38400); // запускаем сериал порт для GPS на 38400

//  EEPROM.get(14, m_seting);
//  if  (bitRead(m_seting, 0) == 0) { 
  EEPROM.get(14, gsm_count_c); // считываем из EEPROM частоту обновления GPRS данных
  if  (gsm_count_c == 0) {
    D16_Low; // A2 -> Low, // если частота обновления 0 выключаем GSM модуль
  } else {
    D16_High; // A2 -> High, // если частота обновления больше 0 включаем GSM модуль
  }
  bitSet(m_stat, 2); // включаем отображение батареи
  bitSet(m_stat, 3); // включаем отображение разделителя часов
  
  if (D4_Read == LOW) { // проверяем нажатие кнопки 1
    bitClear(m_statt, 3); // если нажата то отключаем режим зарядки
  } else {
    bitSet(m_statt, 3); // если не нажата включаем режим зарядки
  }
  
  EEPROM.get(15, m_timezone); // считываем из EEPROM временную зону
  EEPROM.get(1000, distance_all); // считываем из EEPROM количество пройденных километров
  
//  Timer1.initialize(1000000);
//  Timer1.attachInterrupt(InterruptS);
}

void loop() {
  if (bitRead(m_statt, 3) == 1) { // если включен режим зарядки
    D16_Low; // A2 -> Low, выключаем GSM модуль
    D17_Low; // A3 -> Low, выключаем GPS модуль
    D9_Low; // D9 -> Low, выключаем спидометр
    if (D4_Read == LOW) { // если нажата кнопка 1 запускаем спидометр
      resetFunc(); // запускаем инициализацию спидометра
    }
    delay(250); // ждем 250мс
  } else { // если не включен режим зарядки выполняем основной код
    
    unsigned long wait_start = millis(); // инициализируем таймер обработки данных от GPRS модуля
    do // обработки данных от GPRS модуля и нажатий кнопок
    {
      while (ss.available()) {
        gps.encode(ss.read());
      }

      // обрабатываем нажатие кнопки 1
      if (D4_Read == LOW) {         // если кнопка нажата 
        if (bitRead(m_stat, 0) == 0) {  // проверяем не была ли она нажата ранее
          m_delay = millis(); // инициализируем таймер долгого нажатия
  //        if (speed > 300) {
  //          if (m_pos1 == 1) {
  //            m_pos2 = 0;
  //          }
  //        } else {
            m_pos1++; // переключаем режим
            if (m_pos1 == 2) m_pos1 = 3; // пропускаем 2 режим, датчика температуры и влажности нет
  //          clear_display();
  //        }
          if (m_pos1 > 5) { // если больше чем последний режим переходим в начало
            m_pos1 = 0;
          }
          if (m_pos1 == 4) { // сбрасываем в ноль 2 позицию если перешли в режим просмотра сохраненных растояний
            m_pos2 = 0;
          }
//          is_clr_display = 1; // очищаем экран
        }
        if ((millis() - m_delay) > 5000) { // если нажатие длится более 5 сек выключаем устройство
          PowerOFF(); // функция выключения устройства
        }
        bitSet(m_stat, 0); // устанавливаему признак нажатия кнопки 1
        bitClear(m_stat, 6); // сбрасываем таймер автоматического переключения в режим отображения скорости/времени 
      } else { // если кнопка не нажата сбрасываем признак нажатия кнопки 1 
        bitClear(m_stat, 0);
      }
      // обрабатываем нажатие кнопки 2
      if (D5_Read == LOW) { // если кнопка нажата
        if (bitRead(m_stat, 1) == 0) { // проверяем не была ли она нажата ранее
          m_delay = millis(); // инициализируем таймер долгого нажатия
        }
        if (m_pos1 == 0) { // если находимся в режиме отображения скорости
          if ((millis() - m_delay) > 3000) { // если нажата более 3 секунд сбрасываем километраж и время, сохраняем данные в памяти
            if  (bitRead(m_stat, 5) == 0) { // если сохранения небыло то сохраняем 
              SaveTrack(); // соранение и сброс
              bitSet(m_stat, 5); // устанавливаем признак что сохранение было
            }
          }
          if  (bitRead(m_stat, 1) == 0) { // если кнопка нажата меняем режим отображения километы/время
            if (bitRead(m_stat, 4) == 0) {
              bitSet(m_stat, 4); // включаем режим отображения километров
            } else {
              bitClear(m_stat, 4); // включаем режим отображения времени
            }
            is_clr_display = 1;
          }
        }

        if (m_pos1 == 1) { // если находимся в режиме отображения часов
          if ((millis() - m_delay) > 3000) { // если кнопка нажата более 3 секунд меняем часовой пояс
            if (m_timezone > 11) { 
              m_timezone = 0; // если больше 12 обнуляем
            } else {
              m_timezone = m_timezone + 1; // если меньше 12 увеличиваем
            }
            m_delay = millis() - 2500; // перезапускаем таймер длительного нажатия для задержки изменения часового пояса (500мс)
            bitSet(m_stat, 5); // устанвливаес статус было изменение
          }
        } 
        
        if (m_pos1 == 3) { // если находимся в режиме отображения координат
          if ((millis() - m_delay) > 5000) { // если нажата более 5 секунд сбрасываем счетчик километров
            if  (bitRead(m_stat, 5) == 0) { 
              distance_all = 0;
//              EEPROM.put(1000, distance_all);
              bitSet(m_stat, 5);
              is_clr_display = 1; // очищаем экран
            }
          }
        } 
        if (m_pos1 == 4) { // если находимся в режиме отображения сохраненных результатов
    //      if ((millis() - m_delay) > 3000) {
    //        
    //      }
          if  (bitRead(m_stat, 1) == 0) { // если кнопка нажата переключаемся на отображение следующий результат
            m_pos2--;
            if  (m_pos2 > 19) { // позиция не может быть больше 19
              m_pos2 = 19;
            }
            is_clr_display = 1; // очищаем экран
          }
        }
        if (m_pos1 == 5) { // если находимся в режиме отображения статуса GSM модуля
          if ((millis() - m_delay) > 3000) { // если кнопка нажата более 3 секунд меняем время отправки координат на сайт или отключаем модуль
            // 3 примерно равно 1 секуде
            if (gsm_count_c > 44) { 
              gsm_count_c = gsm_count_c + 45; // если больше 15 секунд увеличиваем на 15 секунд
            } else {
              gsm_count_c = gsm_count_c + 15; // если меньше 15 секунд увеличеваем на 5 секунд
            }
            if (gsm_count_c > 180) gsm_count_c = 0; // если больше 60 секунд отключаем GSM модуль
            bitSet(m_stat, 5); // устанвливаес статус было изменение
            bitSet(m_statt, 0); // ??? вероятно отключение GSM модуля
            bitSet(m_statt, 1); // блокировать USSD запрос
            m_delay = millis() - 1000;
          }
        } 
        bitSet(m_stat, 1); // устанавливаем признак нажатия кнопки 2
        bitClear(m_stat, 6); // сбрасываем таймер автоматического переключения в режим отображения скорости/времени
      } else {
        if (m_pos1 == 5) { // если находимся в режиме отображения статуса GSM модуля
          if  (bitRead(m_stat, 1) == 1) { // если была нажата а потом отжата кнопка 2
            if (bitRead(m_stat, 5) == 1) { // если изменения небыло то изменяем
              EEPROM.put(14, gsm_count_c); // сохраняем настройку
              is_clr_display = 1; // очищаем экран
            } else {
              if  (bitRead(m_statt, 1) == 0) { // если не блокирован USSD запрос
                bitSet(m_stat, 7); // установить статус отправки USSD запроса 
              }
              bitClear(m_statt, 1); // сбросить блокировку USSD запроса
            }
          }
        }
        if (m_pos1 == 1) { // если находимся в режиме отображения часов
          if (bitRead(m_stat, 1) == 1) { // если была нажата а потом отжата кнопка 2
            if (bitRead(m_stat, 5) == 1) { // если изменения небыло то изменяем
              EEPROM.put(15, m_timezone); // сохраняем настройку
            } else {
              m_pos1 = 0;
            }
          }
        }
        bitClear(m_stat, 1); // сбросить признак нажатия кнопки 2
        bitClear(m_stat, 5); // сбросить признак изменения
      }
      delay(10); // ждем 10 милисекунд
    } while (millis() - wait_start < WAIT_MS); // выполняем до тех пор пока не прошло WAIT_MS милисекунд 
  
//    readAM2320temp(&temperature, &humidity);
  
  //  if (time_start == 0 && gps.time.isValid()) {
  //    DistanceDataObject DistanceData;
  //    EEPROM.get(0, DistanceData);
  //    if (DistanceData.time_start != 0 && DistanceData.time_off != 0) {
  //      time_start = DistanceData.time_start + (GetSecond() - DistanceData.time_off);
  //    } else {
  //      time_start = GetSecond();
  //    }
  //    distance = DistanceData.distance;
  //  }
  
    if (time_start == 0 && gps.date.isValid() && gps.date.day()) { // если время в пути равно 0 и GPS дата определена
     time_start = GetSecond(); // сбрасываем время в пути
     distance = 0; // сбрасываем дистанцию
    }
  
    if (gps.speed.age() < 1500) { // если данные GPS о скорости не устарели
      speed = int(_GPS_KMPH_PER_KNOT * gps.speed.value()); // вычисляем скорость
      if (speed_max < speed) speed_max = speed; // вычисляем максимальную скорость
    } else {
      speed = 0; // если данные GPS о скорости устарели скорость равна 0
    }
  
    double lat2 = gps.location.lat(); // сохраняем координаты 2
    double lng2 = gps.location.lng();
    if (lat1 != 0 && lng1 != 0) { // если координаты 1 не пустые
      double distance_t = gps.distanceBetween(lat1, lng1, lat2, lng2); // вычисляем растояние между координатами
      if (distance_t >= MINIMUM_ACCUMULATE_DISTANCE) { // если высисленное растояние больше MINIMUM_ACCUMULATE_DISTANCE прибавляем растояние 
        if (distance_t < 250) { // если растояние меньше 250 
          distance += distance_t; // прибавляем растояние
          lat1 = lat2; // сохраняем координаты 2 в координаты 1
          lng1 = lng2;
        } else {
          lat1 = 0; // сбрасываем координаты 1
          lng1 = 0; 
        }
      }
    } else {
      lat1 = lat2; // если координаты 1 пустые сохраняем координаты 2 в координаты 1
      lng1 = lng2;
    }
        
    gfxFont = (GFXfont *)&FreeSerifBold24pt7bNumbers; // устанавливаем шрифт
  //  m_pos1 = 4; //----------------------------------------------------------------------------------------
    if (m_pos1 != m_pos1t || is_clr_display) { // если произошли изменения очищаем экран
      m_pos1t = m_pos1;
      clear_display();
      is_clr_display = 0;
    }
    switch (m_pos1) {
      case 1: // режим отображения времени
        GetTimeDate();
        GetFormat2d(_hour);
        drawChar(0, 0 + CLOCK_LEFT_POS, strOut[0]);
        drawChar(0, 25 + CLOCK_LEFT_POS, strOut[1]);
        if (bitRead(m_stat, 3)) {
          drawChar(1, 50 + CLOCK_LEFT_POS, ':');
        } else {
          drawChar(0, 50 + CLOCK_LEFT_POS, 0xFF);
        }
        GetFormat2d(_minute);
        drawChar(0, 67 + CLOCK_LEFT_POS, strOut[0]);
        drawChar(0, 92 + CLOCK_LEFT_POS, strOut[1]);
  
        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;

//        dtostrf(gps.time.value(), 1, 0, strOut);
        
        cursor_x = 5;
        cursor_y = 0;
        sprintf(strOut, "%02d/%02d/%04d", _day, _month, _year);
        writeString(strOut, 10);
        
  //      GetFormat2d(_day);
  //      strOut[2] = '/';
  //      writeString(strOut, 3);
  //      GetFormat2d(_month);
  //      strOut[2] = '/';
  //      writeString(strOut, 3);
  //      sprintf(strOut, "%04d", _year);
  //      writeString(strOut, 4);
        
        break;
//      case 2: // режим отображения температуры и влажности | отключен
//        cursor_x = 0;
//        cursor_y = 0;
//        dtostrf(temperature, 1, 0, strOut);
//        writeString(strOut, 10);
//        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
//        writeStringM(strO, 1); // вывод знака градуса
//        cursor_x = 1; 
//        writeStringM(strC, 1); // вывод C
//        cursor_x = 4;
//        cursor_y -= 10;
//        dtostrf(humidity, 1, 0, strOut);
//        writeString(strOut, 10);
//        gfxFont = 0;
//        cursor_y += 2;
//        writeStringM(strP, 1); // вывод %
//        break;
      case 3: // вывод координат
        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
        cursor_x = 0;
        cursor_y = 0;
        dtostrf(gps.location.lat(), 1, 6, strOut);
        writeString(strOut, 10);
        gfxFont = 0;
        writeStringM(strLAT, 4);
        
        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
        cursor_x = 2;
        cursor_y = 0;
        dtostrf(gps.location.lng(), 1, 6, strOut);
        writeString(strOut, 10);
        gfxFont = 0;
        writeStringM(strLNG, 4);
        // вывод растояния
        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
        cursor_x = 5;
        cursor_y = 0;
        dtostrf(float(distance_all + long(distance))/1000, 1, 3, strOut);
        writeString(strOut, 17);
        gfxFont = 0;
        writeStringM(strKM, 3);
  
//        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
//        cursor_x = 4;
//        cursor_y = 0;
//        sprintf(strOut, "%01d", gps.satellites.value());
//        writeString(strOut, 10);
//        gfxFont = 0;
//        writeStringM(strS, 2);
//  
//        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
//        cursor_x = 4;
//        cursor_y = 60;
//        sprintf(strOut, "%01d", gps.altitude.meters());
//        writeString(strOut, 10);
//        gfxFont = 0;
//        writeStringM(strM, 2);
        break;
      case 4: //вывод сохраненных результатов, 20 ячеек переключаются по кругу
        uint16_t addr;
        EEPROM.get(12, addr);
        if (addr == 0 || addr > 556) {
          addr = 16;
        }
        addr += m_pos2*27;
        if (addr > 556) {
          addr = addr - 540;
        }
        DistanceSaveObject DistanceData;
        EEPROM.get(addr, DistanceData);
  
        gfxFont = 0;
        cursor_x = 0;
        cursor_y = 0;
        writeStringM(strMEMO, 5);
        writeString(DistanceData.date, 17);
  
        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
        cursor_x = 1;
        cursor_y = 0;
        dtostrf(DistanceData.distance/1000, 1, 3, strOut);
        writeString(strOut, 10);
        gfxFont = 0;
        writeStringM(strKM, 3);
  
        GetTime(DistanceData.time);
        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
        cursor_x = 3;
        cursor_y = 0;
        writeString(strOut, 8);
  
        gfxFont = 0;
        dtostrf((float)DistanceData.speed_max/100, 1, 1, strOut);
        cursor_x = 5;
        cursor_y = 0;
        writeString(strOut, 10);
        writeStringM(strKMH, 5);
        break;
      case 5: // вывод информации GSM
        gfxFont = 0;
        cursor_x = 0;
        cursor_y = 45;
        if  (gsm_count_c == 0) { // если время обновления равно 0 то GSM модуль выключен
          writeStringM(strGSMOFF, 7);
        } else {
          // вывод времени обновления данных GPRS
          writeStringM(strGSMON, 4);
          dtostrf(gsm_count_c/3, 1, 0, strOut);
          writeString(strOut, 2);
          writeStringM(strS, 2);
          // вывод IMEI кода
          cursor_x = 2;
          cursor_y = 0;
          writeStringM(strIMEI, 7);
          cursor_y = 36;
          writeString(gsm_IMEI, 16);
        }
        break;
      default: // вывод скорости
  //      OutDisplaySpeed();
        sprintf(strOut, "%04d" , speed/10);
        if (strOut[0] != '0') {
          drawChar(0, 0 + SPEED_LEFT_POS, strOut[0]);
        } else {
          drawChar(0, 0 + SPEED_LEFT_POS, 0xFF);
        }
        if (strOut[1] != '0') {
          drawChar(0, 25 + SPEED_LEFT_POS, strOut[1]);
        } else {
          drawChar(0, 25 + SPEED_LEFT_POS, 0xFF);
        }
        drawChar(0, 50 + SPEED_LEFT_POS, strOut[2]);
        drawChar(3, 73 + SPEED_LEFT_POS, '.');
        drawChar(0, 85 + SPEED_LEFT_POS, strOut[3]);
  
        cursor_x = 5;
        cursor_y = 0;
        gfxFont = (GFXfont *)&FreeSansBold9pt7bNumbers;
        if (bitRead(m_stat, 4) == 0) {
          dtostrf(distance/1000, 1, 3, strOut);
          writeString(strOut, 10);
          gfxFont = 0;
          writeStringM(strKM, 3);
        } else {
          GetTime(GetSecond() - time_start);
          writeString(strOut, 8);
        }
    }

    // мигание часов
    if (bitRead(m_stat, 3)) {
      bitClear(m_stat, 3);
    } else {
      bitSet(m_stat, 3);
    }
    
    // если скорость мненьше 3км/ч показывать часы иначе показывать скорость
    if (bitRead(m_stat, 0) == 0 && bitRead(m_stat, 1) == 0 && m_pos1 < 4) {
      if (bitRead(m_stat, 6) == 0) {
        r_delay = millis();
      }
      bitSet(m_stat, 6);
      if ((millis() - r_delay) > 3000) {
        if (speed > 300) {
          m_pos1 = 0; 
        } else {
          m_pos1 = 1;
        }
        bitClear(m_stat, 6);
      }
    } else {
      bitClear(m_stat, 6);
    }
  
  
  //-----------------------------------------------------------------------------------------------------------------------GPRS
  #ifdef SP_GSM
    if  (bitRead(m_statt, 0) == 1) { 
      if  (gsm_count_c == 0) {
        sg.println(F("AT+CPOWD=1"));
        delay(250);
        ReadGSM();
        D16_Low; // A2 -> Low
        init_gprs = 12;
      } 
      bitClear(m_statt, 0);
      bitClear(m_stat, 1);
    }
    if  (gsm_count_c > 0) {
      if  (bitRead(m_statt, 5) == 0) {
        if (gsm_count == 10) {
          gsm_count = 0;
          bitSet(m_statt, 5);
        }
        gsm_count++;
      } else {
        ss.end();
        sg.begin(57600);
                    
        // Проверяем состояние модуля, если равно "+CPAS: 0" то готов к работе
        sg.println(F("AT+CPAS"));
        delay(10);
        ds = ReadGSM();
        if (ds.indexOf("0") == 17) {
        
          // получение баланса по USSD запросу
          if (bitRead(m_stat, 7) == 1) {
            clear_display();
            cursor_x = 3;
            cursor_y = 50;
            writeStringM(strWAIT, 4);
            sg.println(F("AT+CMGF=1"));
            delay(50);
            sg.println(F("AT+CSCS= \"GSM\""));
            delay(50);
            wait_start = millis();
            sg.println(F("AT+CUSD=1,\"*100#\""));
            delay(50);
            ReadGSM();
            do {
              delay(10);
              ds = ReadGSM();
              if (ds != "") {
                ds = ds.substring(12, ds.length());
//                cursor_x = 2;
                cursor_y = 0;
                writeString((char*)ds.c_str(), 50);
                ds = "";
                break;
              }
            } while (millis() - wait_start < 50000);
            bitClear(m_stat, 7);
          } else {
            // если оператор не определен запускаем цикл определения
            if (gsm_operator == "") {
              init_gprs = 12;
            }
            // запускаем цикл отправки данных на сервер, если есть что отправлять или получаем оператора и силу сигнала
            if ((lat1 != 0 && lng1 != 0) || init_gprs == 12) {
//            if (true || init_gprs == 12) {
              if (init_gprs < 13) {
                 strcpy_P(strOut, (char*)pgm_read_word(&(ATss[init_gprs])));
              }
              if (init_gprs == 5) {
          //      Serial.println(ATs[init_gprs]);  //посылаем в монитор порта
//                strcpy_P(strOut, (char*)pgm_read_word(&(ATss[init_gprs])));
                sg.println(strOut);  //посылаем в GSM модуль
//                sg.println(F("AT+SAPBR=2,1"));  //посылаем в GSM модуль
                delay(50);
                ds = ReadGSM();
//                Serial.println(ds);
//                Serial.println(ds.indexOf("1,1"));
//                if (ds.indexOf("+SAPBR: 1,1") > -1) {
                if (ds.indexOf("1,1") == 23) {
                  init_gprs++;   
                } else {
                  countGSM(ATsDelays[init_gprs]);
                }
      //          Serial.println(ds);  //показываем ответ от GSM модуля
              } else {
                if (init_gprs == 8) {
                  if (gsm_count == 0) {
                    ds = F("AT+HTTPPARA=\"URL\",\""SP_URL);
                    GetTimeDate();
                    
      //              sprintf(strOut, "%02d/%02d/%04d|%02d:%02d:%02d", _day, _month, _year, _hour, _minute, gps.time.second());
      //              ds.concat(strOut);
          
//                    sprintf(strOut, "%02d/%02d/%04d", _day, _month, _year);
//                    ds.concat(strOut);
//          
//                    sprintf(strOut, "%02d:%02d:%02d", _hour, _minute, gps.time.second());
//                    ds.concat(',');
//                    ds.concat(strOut);
//                    
//                    dtostrf(gps.location.lat(), 1, 6, strOut);
//                    ds.concat(';');
//                    ds.concat(strOut);
//                    dtostrf(gps.location.lng(), 1, 6, strOut);
//                    ds.concat(';');
//                    ds.concat(strOut);
//                    dtostrf(speed, 1, 0, strOut);
//                    ds.concat(';');
//                    ds.concat(strOut);
//                    dtostrf(resultVcc/1000.0, 1, 2, strOut);
//                    ds.concat(';');
//                    ds.concat(strOut);


//                    dtostrf(temperature, 1, 0, strOut);
//                    ds.concat(';');
//                    ds.concat(strOut);
//                    dtostrf(humidity, 1, 0, strOut);
//                    ds.concat(';');
//                    ds.concat(strOut);

//                    ds.concat(F(";"SP_MD5"\""));

//                    ds.concat(F(SP_MD5));
                    ds.concat(gsm_IMEI);
                    ds.concat('/');
                    dtostrf(gps.location.lat(), 1, 6, strOut);
                    ds.concat(strOut);
                    dtostrf(gps.location.lng(), 1, 6, strOut);
                    ds.concat('/');
                    ds.concat(strOut);
                    ds.concat('"');
                    sg.println( ds );
                    ds = "";
                  }
                  countGSM(ATsDelays[init_gprs]);
                } else {
                  if (init_gprs < 12) {
                    if (gsm_count == 0) {
                      ReadGSM();
//                      strcpy_P(strOut, (char*)pgm_read_word(&(ATss[init_gprs])));
                      sg.println(strOut);  //посылаем в GSM модуль
                    }
                    countGSM(ATsDelays[init_gprs]);
                  } else {
                    if (init_gprs == 12) {
//                      commGSM(ATs[init_gprs], "\"", "\"");
//                      strcpy_P(strOut, (char*)pgm_read_word(&(ATss[init_gprs])));
                      commGSMn(strOut, 23, '"');
                      gsm_operator = strOut;
                      sg.println(F("AT+GSN"));
                      delay(10);
                      ds = ReadGSM();
//                      ds = ds.substring(9, 24);
//                      ds.toCharArray(gsm_IMEI, 16);
                      ds.substring(9, 24).toCharArray(gsm_IMEI, 16);
                      init_gprs++;
                    } 
                    if (gsm_count == gsm_count_c) {
                       gsm_count = 0;
                       init_gprs = 0;
                    } else {
                      gsm_count++;
                    }
//                    gsm_level = commGSM("AT+CSQ", ": ", ",");
                    strcpy_P(strOut, at_csq);
                    gsm_level = commGSMn(strOut, 15, ',');
//                    if  (bitRead(m_statt, 0) == 1) { 
//                      if  (bitRead(m_seting, 0) == 0) { 
//                        sg.println(F("AT+CPOWD=1"));
//                        delay(250);
//                        ReadGSM();
//                        digitalWrite(A2, LOW);
//                      } 
//      //                else {
//      //                  digitalWrite(A2, HIGH);
//      //                }
//                      bitClear(m_statt, 0);
//                      bitClear(m_stat, 1);
//                    }
                  }
                }
              }
            }
          }
    //      if (gsm_operator != "") {
    //        cursor_x = 7;
    //        cursor_y = 0;
    //        if (gsm_level < 99) {
    //          int pr = ceil(5 / ( 35 / float(gsm_level) ) );
    //          for(int8_t i = 0; i < 5; i++ ) {
    //            if (i < pr) {
    //              writeStringM(strPU, 1);
    //            } else {
    //              writeStringM(strPY, 1);
    //            }
    //          }
    //          writeStringM(strCLR, 1);
    //          writeString((char*)gsm_operator.c_str(), 20);
    //          writeStringM(strCLR, 1);
    //          if (init_gprs < 12) {
    //            writeStringM(strPS, 1);
    //          } else {
    //            writeStringM(strCLR, 1);
    //          }
    //        }  
    //      } 
        } 
        else {
//          if  (bitRead(m_seting, 0) == 1) { 
          if  (gsm_count_c > 0) {
//            digitalWrite(A2, HIGH);
            D16_High; // A2 -> High
          }
        }
        //AT+CSQ уровень сигнала
        //AT+CCLK? считать часы
        //AT+CCLK="16/07/08,10:13:00+02" установить часы
        //AT+CBC информация о заряде батареи
      
      
      //    commGSM("AT+COPS?", ",\"", "\"");
      
          
      //    int pr = commGSM("AT+CBC", ",", ",");
      //    if (pr) gsm_bat = pr;
      //    cursor_x = 7;
      //    cursor_y = 90;
      //    writeString(strOut, 4);
      
      
//      sg.println(F("AT+CBC"));
//      delay(10);
//      ds = ReadGSM();
//      Serial.println(ds);
//      int ds1 = ds.substring(17, 19).toInt();
//      Serial.println(ds1);
//      long ds2 = ds.substring(20, 24).toInt();
//      Serial.println(ds2);
//                      ds.toCharArray(gsm_IMEI, 16);
                      
      
        sg.end();
        ss.begin(38400);
      }
    }
  #endif  
  }
  
  // вычисление индикатора батарейки
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(10);                           // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);                 // Convert
  while (bit_is_set(ADCSRA,ADSC));
  resultVcc = ADCL;
  resultVcc |= ADCH<<8;
  // disable ADC
//  ADCSRA = 0;
  resultVcc = 1126400L / resultVcc;    // Back-calculate AVcc in mV
  
  gfxFont = 0;

  // вывод напряжения батареи
  cursor_x = 7;
  cursor_y = 70;
  dtostrf(resultVcc/1000.0, 1, 2, strOut);
  writeString(strOut, 10);
  writeString("V", 1);

  
  // вывод символа батарейки
  if (resultVcc < (VIN_MIN * 1000)) {
    bat = 0;
    if (resultVcc < (VIN_OFF * 1000)) { // если напряжение меньше VIN_OFF сохраняем все и выключаем устройство
      PowerOFF();
    }
  } else {
    bat = int(8/(((VIN_MAX - VIN_MIN) * 1000)/(resultVcc - (VIN_MIN * 1000))));
    if (bat > 8) {
      bat = 8;
    }
  }
//  cursor_x = 6;
//  cursor_y = 100;
//  dtostrf(bat, 1, 0, strOut);
//  writeString(strOut, 2);

   // мигание батарейки
  if (bat == 0 && bitRead(m_stat, 2)) {
    bitClear(m_stat, 2);
  } else {
    bitSet(m_stat, 2);
  }
  byte batt = bat/2;
  cursor_x = 7;
  cursor_y = 105;
  for(int8_t i = 0; i < 4; i++ ) {
    if (bitRead(m_stat, 2)) {
      if (i < batt) {
        writeStringM(strPF, 1);
      } else {
        if (i == 0 && bat == 0) { 
          writeStringM(strPN, 1);
        } else {
          if (i == batt && bat % 2) {
            writeStringM(strPH, 1);
          } else {
            writeStringM(strPC, 1);
          }
        }
      }
    } else {
      writeStringM(strCLR, 1);
    }
  }
  if (bitRead(m_stat, 2)) {
    writeStringM(strPE, 1);
  }


  if (gsm_count_c > 0 && gsm_operator != "") {
    cursor_x = 7;
    cursor_y = 0;
    if (gsm_level < 99) {
      int pr = ceil(5 / ( 35 / float(gsm_level) ) );
      for(int8_t i = 0; i < 5; i++ ) {
        if (i < pr) {
          writeStringM(strPU, 1);
        } else {
          writeStringM(strPY, 1);
        }
      }
      writeStringM(strCLR, 1);
      writeString((char*)gsm_operator.c_str(), 20);
      writeStringM(strCLR, 1);
      if (init_gprs < 12) {
        writeStringM(strPS, 1);
      } else {
        writeStringM(strCLR, 1);
      }
    }  
  }
}

//void readAM2320temp(int *temperature, int *humidity)
//{
//  byte temp;
//  Wire.begin();
//  Wire.beginTransmission(AM2320_I2C_ADDRESS);
//  Wire.endTransmission();
//  delayMicroseconds(100); 
//  
//  Wire.beginTransmission(AM2320_I2C_ADDRESS);
//  Wire.write(0x03);//AM2320_read_sensor_data
//  Wire.write(0);
//  Wire.write(4);
//  Wire.endTransmission();
////  delayMicroseconds(1600);
//  Wire.requestFrom(AM2320_I2C_ADDRESS, 8);
//  
//  temp = Wire.read();
//  temp = Wire.read();
//  
//  *humidity = Wire.read() << 8;
//  *humidity += Wire.read();
//  *humidity = *humidity / 10;
//
//  *temperature = Wire.read() << 8;
//  *temperature += Wire.read();
//  *temperature = *temperature / 10;
//
//  temp = Wire.read();
//  temp = Wire.read();
//}


unsigned long GetSecond()
{
//  GetTimeDate();
//  return long( (long(_day) * 86400) + (long(_hour) * 3600) + (long(_minute) * 60) + gps.time.second() );
//  return long( (long(gps.date.day()) * 86400) + (long(gps.time.hour()) * 3600) + (long(gps.time.minute()) * 60) + long(gps.time.second()) );
  return long( (long(gps.date.day()) * 86400) + (long(gps.time.hour()) * 3600) + (long(gps.time.minute()) * 60) + long(gps.time.second()) );
}

void GetTime(uint16_t _time)
{
  _hour = byte( _time / 60 / 60 );
  _minute = byte( ( _time - (long(_hour) * 3600) ) / 60 );
  _second = byte( _time - (long(_hour) * 3600) - (long(_minute) * 60) );
  sprintf(strOut, "%02d:%02d:%02d", _hour, _minute, _second);
}

void GetFormat2d(uint8_t _d)
{
  sprintf(strOut, "%02d", _d);
} 

void GetTimeDate()
{ 
  uint8_t _mday;
  _minute = gps.time.minute();
  _hour = gps.time.hour();
  _day = gps.date.day();
  _month = gps.date.month();
  _year = gps.date.year();
  _hour = _hour + m_timezone;
  if (_hour >= 24) {
    _hour = _hour - 24;
    _day = _day + 1;  
    if (_month % 2 == 0) {
      if (_month == 2) {
        if (_year % 4 == 0 && _year % 100 != 0 || _year % 400 == 0) {
          _mday = 29; 
        } else {
          _mday = 28;
        } 
      } else {
        _mday = 30;
      }
    } else {
      _mday = 31;
    }
    if (_day > _mday) {
      _day = _day - _mday;
      _month = _month + 1;
      if (_month > 12) {
        _month = _month - 12;
        _year = _year + 1;
      }
    } 
  }
}
  
void SaveTrack()
{
//  distance_all = distance_all + long(distance/1000);
//  EEPROM.put(1000, distance_all);
  SaveDistance();
  uint16_t addr;
  EEPROM.get(12, addr);
  if (addr == 0 || addr > 556) {
    addr = 16;
  } else {
    addr += 27;
  }
  EEPROM.put(12, addr);
  GetTimeDate();
  unsigned long __second = GetSecond();
  DistanceSaveObject SaveData;
  SaveData.distance = distance;
  SaveData.time = __second - time_start;
//  SaveData.time = time_p;
  SaveData.speed_max = speed_max;
  sprintf(SaveData.date, "%02d/%02d/%04d %02d:%02d", _day, _month, _year, _hour, _minute);
  EEPROM.put(addr, SaveData);
  distance = 0;
  speed_max = 0;
  time_start = __second;
//  time_p = 0;
}

void PowerOFF()
{
//  distance_all = distance_all + long(distance/1000);
//  EEPROM.put(1000, distance_all);
  SaveDistance();
  clear_display();
  gfxFont = 0;
  cursor_x = 3;
  cursor_y = 40;
  writeStringM(strPOWEROFF, 9);
  cursor_x = 6;
  cursor_y = 10;
  writeStringM(strVERSION, 18);
  if (init_gprs < 12) {
    delay(20000);
  } else {
    delay(5000);
  }
  resetFunc();
//  while (true) digitalWrite(9, LOW);
}

void SaveDistance() 
{
  distance_all = distance_all + long(distance);
  EEPROM.put(1000, distance_all);
}  
//void InterruptS(void)
//{
////  time_p = time_p + 1;
//}


//==========================================================//
// Display
//==========================================================//
// Inits oled and draws logo at startup
void init_OLED(void)
{
  // инициализация дисплея  MOSI (D1) - 11, CLK (D0) - 13, DC - 6, CS - 7, RESET - 8
  D6_Out; // D6 на выход, управление DC
  D7_Out; // D7 на выход, управление CS
  D8_Out; // D8 на выход, Управление RESET
  // сбрасываем дисплей
  D8_High; // RESET высокий уровень
  delay(1); // ждем 1мс
  D8_Low; // RESET ниский уровень
  delay(10); // ждем сброса дисплея 10мс
  D8_High; // RESET высокий уровень
  for (int k = 0; k < 26; k++)
  {
//    byte myByte = pgm_read_byte_near(InitDisp + k);
//    sendcommand(myByte);
    sendcommand(pgm_read_byte_near(InitDisp + k));
  }
  
//  sendcommand(0xAE);            //DISPLAYOFF
//  sendcommand(0xD5);            //SETDISPLAYCLOCKDIV
//  sendcommand(0x80);            // the suggested ratio 0x80
//  sendcommand(0xA8);            //SSD1306_SETMULTIPLEX
//  sendcommand(0x3F);
//  sendcommand(0xD3);            //SETDISPLAYOFFSET
//  sendcommand(0x0);             //no offset
//  sendcommand(0x40 | 0x0);      //SETSTARTLINE
//  sendcommand(0x8D);            //CHARGEPUMP
//  sendcommand(0x14);            // or 0x10
//  sendcommand(0x20);            //MEMORYMODE
//  sendcommand(0x00);            //0x0 act like ks0108
//  
//  sendcommand(0xA0 | 0x1);      //SEGREMAP   //Rotate screen 180 deg
//  sendcommand(0xC8);            //COMSCANDEC  Rotate screen 180 Deg
//  
//  sendcommand(0xDA);            //0xDA
//  sendcommand(0x12);            //COMSCANDEC
//  sendcommand(0x81);            //SETCONTRAS
//  sendcommand(0xCF);            // or 0x9F
//  sendcommand(0xd9);            //SETPRECHARGE 
//  sendcommand(0xF1);            // or 0x22
//  sendcommand(0xDB);            //SETVCOMDETECT                
//  sendcommand(0x40);
//  sendcommand(0xA4);            //DISPLAYALLON_RESUME        
//  sendcommand(0xA6);            //NORMALDISPLAY             
//  sendcommand(0x2E);            // stop scroll
//  sendcommand(0xAF);            //display on
  
  clear_display();
//  setXYp(0,0);
}

//==========================================================//
// Used to send commands to the display.
void sendcommand(unsigned char com)
{
#ifdef OLED_SPI
  D7_High; // D7 -> High
  D6_Low; // D6 -> Low
  D7_Low; // D7 -> Low
  fastSPIwrite(com);
  D7_High; // D7 -> High
#else
  TWBR = 6;
  Wire.beginTransmission(OLED_address);     //begin transmitting
  Wire.write(0x80);                          //command mode
  Wire.write(com);
  Wire.endTransmission();                    // stop transmitting
#endif
}

void fastSPIwrite(uint8_t d) {
//  (void)SPI.transfer(d);
  SendSPI(d);
}

//==========================================================//
// Actually this sends a byte, not a char to draw in the display. 
// Display's chars uses 8 byte font the small ones and 96 bytes
// for the big number font.
//void SendChar(unsigned char data) 
//{ 
//#ifdef OLED_SPI
//  digitalWrite(7, HIGH);
//  digitalWrite(6, HIGH);
//  digitalWrite(7, LOW);
//  fastSPIwrite(data);
//  digitalWrite(7, HIGH);
//#else
//  Wire.beginTransmission(OLED_address); // begin transmitting
//  Wire.write(0x40);//data mode
//  Wire.write(data);
//  Wire.endTransmission();    // stop transmitting
//#endif
//}

//==========================================================//
// Resets display depending on the actual mode.
//void reset_display(void)
//{
//  displayOff();
//  clear_display();
//  displayOn();
//}

//==========================================================//
// Turns display on.
//void displayOn(void)
//{
//  sendcommand(0xaf);        //display on
//}

//==========================================================//
// Turns display off.
//void displayOff(void)
//{
//  sendcommand(0xae);    //display off
//}

//==========================================================//
// Clears the display by sendind 0 to all the screen map.
void clear_display(void)
{
  bat_o = 0;
  D7_High; // D7 -> High
  D6_High; // D6 -> High
  D7_Low; // D7 -> Low
  for( int i = 0; i < 128*8; i++ ) {
    fastSPIwrite(0);
  }
  D7_High; // D7 -> High
  setXYp(0,0);
}


// Draw a character
void drawChar(int16_t x, int16_t y, unsigned char c) {

  if(!gfxFont) { // 'Classic' built-in font

//    if((x >= _width)            || // Clip right
//       (y >= _height)           || // Clip bottom
//       ((x + 6 * size - 1) < 0) || // Clip left
//       ((y + 8 * size - 1) < 0))   // Clip top
//      return;
//
//    if(!_cp437 && (c >= 176)) c++; // Handle 'classic' charset behavior
//

    byte w = 6;
//    if (c == 219 || c == 243 || c == 244 || c == 245) w = 3;
    if (c == 243) w = 3;
    for(int8_t i = 0; i < w; i++ ) {
      uint8_t line;
      if (i < 5) {
        line = pgm_read_byte(font+(c*5)+i);
      } else {
        line = 0x0;
      }
      for(int8_t j=0; j<8; j++, line >>= 1) {
        if(line & 0x1) {
          buffer[i] |=  (1 << (j&7));
        } else {
          buffer[i] &= ~(1 << (j&7));
        }
      }
    }
    drawBuffer(x++, y, w);
  } else { // Custom font

//    setXYp(x,y);
    int8_t b = 1;
    if (c == 0xFF) {
      c = '0';
      b = 0;
    }
    if (c == 0x2D) {
      x += 1;
    }
//    if (c == 0x2E) {
//      x += 1;
//    }
    c -= pgm_read_byte(&gfxFont->first);
    GFXglyph *glyph  = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]);
    uint8_t  *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap);
    
    uint16_t bo = pgm_read_word(&glyph->bitmapOffset);
    uint8_t  w  = pgm_read_byte(&glyph->width),
             h  = pgm_read_byte(&glyph->height),
             xa = pgm_read_byte(&glyph->xAdvance);
    int8_t   xo = pgm_read_byte(&glyph->xOffset),
             yo = pgm_read_byte(&glyph->yOffset);
    uint8_t  xx, yy, bits, bit = 0, bity = 0;
    int16_t  xo16, yo16;

    for (uint16_t i = 0; i < 24; i++) {
      buffer[i] = 0;
    }
    for (yy = 0; yy < h; yy++) {
      if (b) {
        for (xx = 0; xx < w; xx++) {
          if(!(bit++ & 7)) {
            bits = pgm_read_byte(&bitmap[bo++]);
          }
          if(bits & 0x80) {
            buffer[xo+xx] |=  (1 << ((yy)&7));
          }
          bits <<= 1;
        }
      }
      if((bity == 7)) {
        drawBuffer(x++, y, (xa+xo));
        bity = 0;
      } else {
        bity++;
      }
    }
    drawBuffer(x++, y, (xa+xo));
  } // End classic vs custom font
}

void drawBuffer(int16_t x, int16_t y, uint16_t w) {
  setXYp(x,y);
  D7_High; // D7 -> High
  D6_High; // D6 -> High
  D7_Low; // D7 -> Low
  for (uint16_t i = 0; i < w; i++) {
    fastSPIwrite(buffer[i]);
    buffer[i] = 0;
  }
  D7_High; // D7 -> High
}

void setXYp(unsigned char row,unsigned char col)
{
  sendcommand(0xb0+row);                //set page address
  sendcommand(0x00+(col&0x0f));       //set low col address
  sendcommand(0x10+((col>>4)&0x0f));  //set high col address
}

void writeChar(uint8_t c) {
  if(!gfxFont) { // 'Classic' built-in font

    if(c == '\n') {
      cursor_y = 0;
      cursor_x  += 1;
    } else if(c != '\r') {
      bool b = (c == 219 || c == 242 || c == 243 || c == 244 || c == 245);
      if (!b && (cursor_y + 6) >= 127) { // Heading off edge?
        cursor_x += 1;            // Reset x to zero
        cursor_y = 0; // Advance y one line
      }
      drawChar(cursor_x, cursor_y, c);
      if (b) {
        cursor_y += 5;
      } else {
        cursor_y += 6;
      }
    }

  } else { // Custom font

    if(c == '\n') {
//      cursor_x  = 0;
//      cursor_y += (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
    } else if(c != '\r') {
      uint8_t first = pgm_read_byte(&gfxFont->first);
      if((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) {
        uint8_t   c2    = c - pgm_read_byte(&gfxFont->first);
        GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c2]);
        uint8_t   w     = pgm_read_byte(&glyph->width),
                  h     = pgm_read_byte(&glyph->height);
        if((w > 0) && (h > 0)) { // Is there an associated bitmap?
          int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); // sic
//          if(wrap && ((cursor_x + (xo + w)) >= _width)) {
//            // Drawing character would go off right edge; wrap to new line
//            cursor_x  = 0;
//            cursor_y += (uint8_t)pgm_read_byte(&gfxFont->yAdvance);
//          }
          drawChar(cursor_x, cursor_y, c);
        }
        cursor_y += pgm_read_byte(&glyph->xAdvance);
      }
    }

  }
}

void writeString(char s[], uint8_t n) {
  for (uint8_t i=0; i<n; i++) {
    if (s[i] == 0) {
       break;
    }
    writeChar(s[i]);
  }
}

void writeStringM(const char* s, uint8_t n) {
  for (uint8_t i=0; i<n; i++) {
    char c = pgm_read_byte_near(s + i);
    if (c == 0) {
       break;
    }
    writeChar(c);
  }
}

//-GSM-------------------------------------------------------------------------------//


String ReadGSM() {  //функция чтения данных от GSM модуля
  int c;
  String v;
  while (sg.available()) {  //сохраняем входную строку в переменную v
    c = sg.read();
    v += char(c);
    delay(1);
  }
//  Serial.println(v);
  return v;
}

void countGSM(int count) { 
  if (gsm_count == count) {
    gsm_count = 0;
    init_gprs++;
  } else {
    gsm_count++;
//    Serial.println(String(gsm_count));
  }
}

//int commGSM(String com, String str_start, String str_end) {
//  #define commGSM_Serial
//  sg.println(com);
//  delay(5);
//  String ans = ReadGSM();
//  #ifdef commGSM_Serial
//    Serial.println(ans);
//  #endif
//  int p1 = ans.indexOf(str_start);
//  #ifdef commGSM_Serial
//    Serial.println("p1 " + String(p1));
//  #endif
//  if (p1 > -1) {
//    int p2 = ans.lastIndexOf(str_end);
//    #ifdef commGSM_Serial
//      Serial.println("p2 " + String(p2));
//    #endif
//    ans = ans.substring(p1 + str_start.length(), p2);
//    #ifdef commGSM_Serial
//      Serial.println(ans);
//      Serial.println("ans.length" + String(ans.length()));
//    #endif
//    ans.toCharArray(strOut, ans.length() + 1);
//    return ans.toInt();
//  }
//}

int commGSMn(String com, int int_start, char str_end) {
//  #define commGSM_Serial
  sg.println(com);
  delay(5);
  String ans = ReadGSM();
  #ifdef commGSM_Serial
    Serial.println(ans);
  #endif
  int p2 = ans.lastIndexOf(str_end);
  ans = ans.substring(int_start, p2);
  ans.toCharArray(strOut, ans.length() + 1);
  return ans.toInt();
}
