/*
 * temper.c
 *
 *  Created on: 22.04.2016
 *      Author: pavel
 */
#include "common_config.h"
#include "onewire.h"
#include "gpio.h"
#include "temper.h"

#include <stdint.h>
#include <stdlib.h>

#ifdef PRINTF_STDLIB
#include <stdio.h>
#endif
#ifdef PRINTF_CUSTOM
#include "tinystdio.h"
#endif

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#define ERRORS_LIMIT    5

#undef DBG
#define DBG if(0)


const unsigned char crc_table[] = {
    0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
    157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
    35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
    190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
    70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
    219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
    101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
    248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
    140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
    17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
    175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
    50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
    202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
    87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
    233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
    116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53
};

static uint8_t  tBufExt[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
static uint16_t tempInt = 0;

/**
  * @brief Cтруктура датчиков температуры (измерения, состояния, счетчики ошибок)
  */
TEMPERATURE_SENSORS_t sTemperSensors[MAX_T_SENSORS];

/**
  * @brief Временные флаги инициализации данных
  */
static bool fReady[MAX_T_SENSORS];

/**
  * @brief Глобальный флаг инициализации данных температурных датчиков
  */
bool fTemperDataReady = false;


uint8_t CheckCRC(uint8_t *data, uint8_t len)
{
    uint8_t crc = 0;

    while (len--) crc = crc_table[crc ^ *data++];
    return crc;
}

/**
  * @brief  Чтение температуры из датчика
  */
bool GetTemperature(uint8_t sensor)
{
    float temperature = 85.0;
    static uint8_t  state;
    bool dataReady = false;


    /* Запускаем преобразование температуры */
#ifdef DALLAS_TWO_LINE_SWITCH
    DALLAS_OutSetAsTX(sensor);
#endif
    state = DALLAS_Send(sensor, DALLAS_SEND_RESET, (uint8_t *)"\xcc\x44", 2, NULL, 0, DALLAS_NO_READ);

    if ((state == DALLAS_ERROR) || (state == DALLAS_NO_DEVICE)) {
      sTemperSensors[sensor].ErrorCounter++;
    }
#ifdef DALLAS_TWO_LINE_SWITCH
    DALLAS_OutSetAsPower(sensor);
#endif
    /* Ждем пока датчики закончат преобразование */
    vTaskDelay(800);

    /* Clear CRC byte */
    tBufExt[8] = 0;

    /* Чтение температуры */
#ifdef DALLAS_TWO_LINE_SWITCH
    DALLAS_OutSetAsTX(sensor);
#endif
    state = DALLAS_Send(sensor, DALLAS_SEND_RESET,
            (uint8_t *)"\xcc\xbe\xff\xff\xff\xff\xff\xff\xff\xff\xff", 11, tBufExt, 9, 2);

    if ((state == DALLAS_ERROR) || (state == DALLAS_NO_DEVICE)) {
      sTemperSensors[sensor].ErrorCounter++;
    }

    /* Check CRC */
    if (state == DALLAS_OK) {

        /* Sometimes we get DALLAS_OK, and empty tBufExt,
         * while sensor surely not connected. For zero filled tBufExt
         * CRC is always ok. To detect this, furst check if buffer is empty */
        uint8_t sum = 0;
        for  (uint8_t i = 0; i < sizeof(tBufExt); i++) {
            sum |= tBufExt[i];
        }

        uint8_t crc = CheckCRC(tBufExt, 9);
        if ((crc != 0) || (sum == 0)) {
            sTemperSensors[sensor].ErrorCounter++;
        }
        DBG { printf("Sensor %d: ", sensor);
            for (uint8_t i = 0; i < 9; i++) printf("0x%X ", tBufExt[i]);
            crc ? printf("CRC Fail\r\n") : printf("CRC OK\r\n"); }
    }

    /* ---------------------------------------------------------------------- */
    /* Расчет температуры датчика */
    if (!sTemperSensors[sensor].ErrorCounter)
    {
      tempInt = tBufExt[0];
      tempInt |= tBufExt[1] << 8;

      /* Если число в дополнительном коде */
      if ((tempInt & 0x8000))
      {
        tempInt = ~tempInt + 1;
        temperature = -1*(tempInt/16.0);
      }
      else {
        temperature = tempInt/16.0;
      }

      DBG printf("temperature: %0.1f\r\n", temperature);

      if ((temperature != 85.0) && (temperature != -0.0625)) {
           sTemperSensors[sensor].Temperature = temperature;
           sTemperSensors[sensor].SensorState = 0;
           dataReady = true;
      }
      sTemperSensors[sensor].ErrorCycle = 0;
    }
    /* Если были ошибки, то показания тепературы не обновляем */
    else
    {
      sTemperSensors[sensor].ErrorCycle++;
      sTemperSensors[sensor].ErrorCounter = 0;

      /* Произошло несколько ошибочных циклов общения с датчиком */
      /* Выставляем признак отказа датчика */
      if (sTemperSensors[sensor].ErrorCycle == ERRORS_LIMIT)
      {
        sTemperSensors[sensor].SensorState = 1;  // Неисправность!
        sTemperSensors[sensor].ErrorCycle = 0;
        dataReady = true;
      }
    }

    DBG printf("Temperature sensor %d: %0.1f State = %d\r\n\r\n", sensor,
            sTemperSensors[sensor].Temperature, sTemperSensors[sensor].SensorState);

    return dataReady;
}

/**
  * @brief  Опрос датчиков температуры
  */
void RequestSensors(void) {
    bool ready = true;

    for (uint8_t i = 0; i < MAX_T_SENSORS; i++) {
      ready &= GetTemperature(i);
    }

    /* Установка глобального флага готовности данных */
    if (ready) {
      fTemperDataReady = true;
    }
}

/**
  * @brief  Задача чтения температуры 1-wire
  */
void vTaskOwire(void *pvParameters)
{
  (void)pvParameters;
  uint8_t i;

  for (i = 0; i < MAX_T_SENSORS; i++) {
    sTemperSensors[i].Temperature  = 0.0;
    sTemperSensors[i].ErrorCounter = 0;
    sTemperSensors[i].ErrorCycle   = 0;
    sTemperSensors[i].SensorState  = 1;  // По умолчанию устанавливаем признак неисправности
  }

  DALLAS_Init();

  for (;;)
  {
      RequestSensors();
      vTaskDelay(1000);
  }
}

void OW_Init(void) {
    xTaskCreate(vTaskOwire, "vTaskOwire", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
}
