/*
 * VERSION 1.0 Januar 7th 2010 LATE  File: 'one_wire_temperature.c'
 * Author: Linda Fouedjio  based on Michael Traxler based and Giacomo Ortona's hadtempsens
 * modified (heavily rather rebuild): Peter Zumbruch
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/iocan128.h>

#include "api.h"
#include "api_define.h"
#include "api_global.h"
#include "can.h"
#include "mem-check.h"

#include "one_wire.h"
#include "one_wire_adc.h"
#include "one_wire_dualSwitch.h"
#include "read_write_register.h"
#include "one_wire_simpleSwitch.h"
#include "one_wire_temperature.h"

#include "OWIDeviceSpecific.h"
#include "OWIHighLevelFunctions.h"
#include "OWIBitFunctions.h"

#define DS1820_START_CONVERSION         0x44
#define DS1820_READ_SCRATCHPAD          0xbe
#define DS1820_WRITE_SCRATCHPAD         0x4E
#define DS1820_CONFIG_REGISTER          0x7f
#define DS1820_READ_POWER               0xb4

#ifndef OWI_TEMPERATURE_MAX_CONVERSION_TIME_MILLISECONDS
#define OWI_TEMPERATURE_MAX_CONVERSION_TIME_MILLISECONDS 750
#endif

#ifndef OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS
#define OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS 10
#endif

#warning TODO: make it changeable through API, by changing the resolution for DS18B20
#ifndef OWI_TEMPERATURE_CONVERSION_TIME_RESOLUTION_SPLIT_FACTOR
#define OWI_TEMPERATURE_CONVERSION_TIME_RESOLUTION_SHIFT_FACTOR 0 /*12 bit resolution, division by 1*/
//#define OWI_TEMPERATURE_CONVERSION_TIME_RESOLUTION_SHIFT_FACTOR 1 /*11 bit resolution, division by 2*/
//#define OWI_TEMPERATURE_CONVERSION_TIME_RESOLUTION_SHIFT_FACTOR 2 /*10 bit resolution, division by 4*/
//#define OWI_TEMPERATURE_CONVERSION_TIME_RESOLUTION_SHIFT_FACTOR 3 /*9 bit resolution, division by 8*/
#endif

uint16_t owiTemperatureMask = 0;
uint16_t owiTemperatureMask_DS18B20 = 0;
uint16_t* p_owiTemperatureMask_DS18B20 = &owiTemperatureMask_DS18B20;
uint16_t owiTemperatureMask_DS18S20 = 0;
uint16_t* p_owiTemperatureMask_DS18S20 = &owiTemperatureMask_DS18S20;
uint16_t owiTemperatureTimeoutMask = 0xFFFF; /*bit mask of non converted channels/pins 1:conversion timeout*/
uint8_t owiUseCommonTemperatureConversion_flag = TRUE;

/* max length defined by MAX_LENGTH_PARAMETER */
static const char owiTemperatureCommandKeyword00[] PROGMEM = "convert";

const char* owiApiCommandKeywords[] PROGMEM = {
        owiTemperatureCommandKeyword00
        };



/*
 *this function contains all the functions that are necessary to
 *read the temperature of sensor
 * as input parameters the function needs only a keyword
 * the function has no return parameter
 */

void owiReadTemperatureSensors( struct uartStruct *ptr_uartStruct )
{
   /* check command syntax */
   if ( 0 != owiReadTemperatureSensorsCheckCommandSyntax(ptr_uartStruct))
   {
      return;
   }

   NumDevicesFound = owiReadDevicesID(BUSES);

   if ( 0 != NumDevicesFound )
   {
      /* reset mask of busses with sensors*/
      owiTemperatureMask = 0;

      if ( 0 < ( owiScanIDS(FAMILY_DS18B20_TEMP, p_owiTemperatureMask_DS18B20) +
                 owiScanIDS(FAMILY_DS18S20_TEMP, p_owiTemperatureMask_DS18S20) ) )
      {
         owiTemperatureMask |= owiTemperatureMask_DS18B20;
         owiTemperatureMask |= owiTemperatureMask_DS18S20;

         //#warning ADC in comparison has here an init - is the Initialization always needed here, or is it just needed once at the beginning?

         /* conversions */

         switch (ptr_uartStruct->number_of_arguments)
         {
            case 0:
               /* read all */
               owiMakeTemperatureConversions(BUSES);
               break;
            case 2:
               /* read single ID w/ temperature conversion of all buses*/
               /* during the filling it couldn't decide weather the 2nd argument
                * is a flag or a value ... no we can, its a flag */
               ptr_owiStruct->conv_flag = ( 0 != ptr_owiStruct->value);
               if (TRUE == ptr_owiStruct->conv_flag) { owiMakeTemperatureConversions(BUSES); }
               break;
         }

         /*
          * access values
          *
          *   - read DS18B20
          *   - read DS18S20
          */

         int8_t familyCode[] = { FAMILY_DS18B20_TEMP, FAMILY_DS18S20_TEMP };
         uint8_t foundDevices = 0;

         for (uint8_t index = 0; index < sizeof(familyCode)/sizeof(int8_t); index++)
         {
            if ( eventDebug <= debug && ( debugMask >> debugOWIADC ) & 0x1 )
            {
               snprintf_P(uart_message_string, BUFFER_SIZE - 1,
                          PSTR("DEBUG (%4i, %s) fcn:readTempSensors call: FindFamilyDevicesAndGetValues family code: %x"),
                          __LINE__, __FILE__, familyCode[index]);
               UART0_Send_Message_String(NULL, 0);
            }

            foundDevices += owiFindFamilyDevicesAndAccessValues(BUSES, NumDevicesFound, familyCode[index], NULL );
         }


         if ( TRUE == ptr_owiStruct->idSelect_flag && 0 == foundDevices)
         {
            general_errorCode = CommunicationError(ERRG, -1, TRUE, PSTR("no matching ID was found"), 4000);
         }
      }
   }
   else
   {
      general_errorCode = CommunicationError(ERRG, GENERAL_ERROR_no_device_is_connected_to_the_bus, 0, NULL, 0);
   }
}

uint8_t owiReadTemperatureSensorsCheckCommandSyntax(struct uartStruct *ptr_uartStruct)
{
   /* check for syntax:
    *    allowed arguments are:
    *       - (empty)
    *       - ID
    *       - ID conversion_flag
    *       
    *       return 0 if ok
    *       else != 0
    */

   int status = 0;
   switch (ptr_uartStruct->number_of_arguments)
   {
      case 0:
         break;
      case 1:
      case 2:
         /* read single ID w/o temperature conversion
          * or
          * read single ID w/ temperature conversion of all buses*/
         if ( FALSE == ptr_owiStruct->idSelect_flag)
         {
            snprintf_P(message, BUFFER_SIZE, PSTR("invalid arguments"));
            CommunicationError(ERRA, -1, TRUE, message, COMMUNICATION_ERROR_USE_GLOBAL_MESSAGE_STRING_INDEX_THRESHOLD -1 );
            status = -1;
            break;
         }
         break;
      default:
         snprintf_P(message, BUFFER_SIZE, PSTR("write argument: too many arguments"));
         CommunicationError(ERRA, -1, TRUE, message, COMMUNICATION_ERROR_USE_GLOBAL_MESSAGE_STRING_INDEX_THRESHOLD -1 );
         status = -1;
         break;
   }
   return status;
}

/*
 *this function
 */

int owiMakeTemperatureConversions( uint8_t *pins )
{
   if ( eventDebugVerbose <= debug )
   {
      snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) "), __LINE__, __FILE__);
      UART0_Send_Message_String(NULL,0);
   }

   //#warning which meaning does this access of PORT A have, while OWI is connected to C?
   //   PORTA |= 0x8;

   uint16_t currentTimeoutMask = 0;
   uint16_t maxConversionTime = OWI_TEMPERATURE_MAX_CONVERSION_TIME_MILLISECONDS;
   static unsigned char timeout_flag;
   static uint32_t count;
   static uint32_t maxcount;
   uint8_t commonPins = 0x0;
   uint8_t currentPins = 0x0;
   uint8_t busPatternIndexMax = 0;

   /* first loop checking busPattern against masks and creating common bus mask*/
   if ( TRUE == owiUseCommonTemperatureConversion_flag)
   {
      for ( int8_t busPatternIndex = 0 ; busPatternIndex < PIN_BUS ; busPatternIndex++ )
      {
         if ( 0 != checkBusAndDeviceActivityMasks(pins[busPatternIndex], busPatternIndex, owiBusMask, owiTemperatureMask, FALSE ))
         {
            continue;
         }

         if ( eventDebug <= debug && ((debugMask >> debugOWITemperatures) & 1))
         {
            snprintf_P(uart_message_string, BUFFER_SIZE - 1,
                       PSTR("DEBUG (%4i, %s) fcn:MakeTempConversionOfAll bus: %i combining 0x%x to common set of pins 0x%x)"),
                       __LINE__, __FILE__,
                       busPatternIndex, pins[busPatternIndex],commonPins);
            UART0_Send_Message_String(NULL,0);
         }

         commonPins |= pins[busPatternIndex];
      }

      if ( eventDebug <= debug && ((debugMask >> debugOWITemperatures) & 1))
      {
         snprintf_P(uart_message_string, BUFFER_SIZE - 1,
                    PSTR("DEBUG (%4i, %s) fcn:MakeTempConversionOfAll final common pins: 0x%x)"),
                    __LINE__, __FILE__,
                    commonPins);
         UART0_Send_Message_String(NULL,0);

      }

      busPatternIndexMax = 1; /* run next loop only once */
   }
   else
   {
      busPatternIndexMax = PIN_BUS;
   }

   for ( int8_t busPatternIndex = 0 ; busPatternIndex < busPatternIndexMax ; busPatternIndex++ )
   {
      if ( TRUE == owiUseCommonTemperatureConversion_flag)
      {
         currentPins = commonPins;
      }
      else
      {
         currentPins = pins[busPatternIndex];

         if ( 0 != checkBusAndDeviceActivityMasks(currentPins, busPatternIndex, owiBusMask, owiTemperatureMask, TRUE ))
         {
            continue;
         }
      }

      maxConversionTime = OWI_TEMPERATURE_MAX_CONVERSION_TIME_MILLISECONDS;
       if ( 0 == ((owiTemperatureMask_DS18S20 & currentPins) & 0xFF ) )
      {
         if ( eventDebug <= debug && ((debugMask >> debugOWITemperatures) & 1))
         {
            snprintf_P(uart_message_string, BUFFER_SIZE - 1,
                       PSTR("DEBUG (%4i, %s) fcn:MakeTempConversionOfAll bus: only DS18B20, adopting max. t_conv to: %i (pins 0x%x)"),
                       __LINE__, __FILE__, maxConversionTime, currentPins);
            UART0_Send_Message_String(NULL,0);
         }
         maxConversionTime = maxConversionTime >> OWI_TEMPERATURE_CONVERSION_TIME_RESOLUTION_SHIFT_FACTOR;
      }
      else
      {
         if ( eventDebug <= debug && ((debugMask >> debugOWITemperatures) & 1))
         {
            snprintf_P(uart_message_string, BUFFER_SIZE - 1,
                       PSTR("DEBUG (%4i, %s) fcn:MakeTempConversionOfAll bus: mixed sensors, max. t_conv: %f (pins 0x%x)"),
                       __LINE__, __FILE__, maxConversionTime, currentPins);
            UART0_Send_Message_String(NULL,0);
         }
      }
       if ( 0 == OWI_DetectPresence(currentPins) )
      {
         if ( eventDebug <= debug && ((debugMask >> debugOWITemperatures) & 1))
         {
            snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) fcn:MakeTempConversionOfAll no Device present (pin pattern 0x%x)"), __LINE__, __FILE__,
                       currentPins);
            UART0_Send_Message_String(NULL,0);
         }
      }
      else
      {
         if ( eventDebug <= debug && ((debugMask >> debugOWITemperatures) & 1))
         {
            snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) fcn:MakeTempConversionOfAll some devices present (pin pattern 0x%x)"), __LINE__, __FILE__,
                       currentPins);
            UART0_Send_Message_String(NULL,0);
            snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) fcn:MakeTempConversionOfAll starting conversion sequence"), __LINE__, __FILE__);
            UART0_Send_Message_String(NULL,0);
         }
      }

#warning TODO implement owiTemperatureConversionPresetValues();

       maxcount = ( OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS > 0 ) ? maxConversionTime / OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS : 1;
       count = maxcount;
       timeout_flag = FALSE;
       if ( eventDebug <= debug && ( debugMask >> debugOWITemperatures ) & 0x1 )
       {
          snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) waiting for conversion"), __LINE__, __FILE__);
          UART0_Send_Message_String(NULL,0);
       }
       OWI_SendByte(OWI_ROM_SKIP, currentPins);
       OWI_SendByte(DS1820_START_CONVERSION, currentPins);
       while ( OWI_ReadBit(currentPins) == 0 )
       {
          _delay_ms(OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS);

          /* timeout check */
          if ( 0 == --count)
          {
             timeout_flag = TRUE;
             break;
          }
       }

       owiTemperatureConversionAfterMath(timeout_flag, currentPins, maxcount, count, &currentTimeoutMask, maxConversionTime);

   }//end of for ( int8_t busPatternIndex = 0 ; busPatternIndex < PIN_BUS ; busPatternIndex++ )

   if ( eventDebug <= debug && ( debugMask >> debugOWITemperatures ) & 0x1 )
   {
      snprintf_P(uart_message_string, BUFFER_SIZE - 1,
                 PSTR("DEBUG (%4i, %s) fcn:MakeTempConversionOfAll finished (Timeout: current = 0x%x, all = 0x%x)"),
                 __LINE__, __FILE__, currentTimeoutMask, owiTemperatureTimeoutMask);
      UART0_Send_Message_String(NULL,0);
   }

   return 1; //1=ok
}

/*
 * owiTemperatureConversionAfterMath(const unsigned char timeout_flag, const uint8_t currentPins, const uint32_t maxcount, const uint32_t count, uint16_t *currentTimeoutMask, const uint16_t maxConversionTime)
 *
 * this function checks or a given timeout flag
 *     - if not set
 *        - clears currentPins from
 *              global owiTemperatureTimeoutMask
 *        - it waits the remaining time:
 *              count x OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS
 *     - if set
 *        - currentPins are added to
 *              global owiTemperatureTimeoutMask
 *                  and
 *              currentTimeOutMask
 *        - error messages are issued
 *
 * no return value
 */

void owiTemperatureConversionAfterMath(const unsigned char timeout_flag, const uint8_t currentPins, const uint32_t maxcount, uint32_t count, uint16_t *currentTimeoutMask, const uint16_t maxConversionTime)
{
    if ( FALSE == timeout_flag )
      {
         owiTemperatureTimeoutMask &= ~(currentPins);

         if ( eventDebug <= debug && ( debugMask >> debugOWITemperatures ) & 0x1 )
         {
            snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) owiTemperatureConversionAfterMath waited %i times a delay of"),
                       __LINE__, __FILE__, maxcount - count);
            snprintf(uart_message_string, BUFFER_SIZE - 1, "%s %i ms", uart_message_string, OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS);
            UART0_Send_Message_String(NULL,0);
         }

         if ( count > 0 )
         {
            /*wait the remaining time*/
            if ( eventDebug <= debug && ( debugMask >> debugOWITemperatures ) & 0x1 )
            {
               snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) owiTemperatureConversionAfterMath waiting the remaining %i times"), __LINE__, __FILE__, count);
               snprintf(uart_message_string, BUFFER_SIZE - 1, "%s %i ms", uart_message_string, OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS);
               UART0_Send_Message_String(NULL,0);
            }

            while ( 0 < --count ) { _delay_ms(OWI_TEMPERATURE_CONVERSION_DELAY_MILLISECONDS); }
         }
         if ( eventDebug <= debug && ( debugMask >> debugOWITemperatures ) & 0x1 )
         {
            snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) owiTemperatureConversionAfterMath conversion done"), __LINE__, __FILE__);
            UART0_Send_Message_String(NULL,0);
         }
      }
      else
      {
         owiTemperatureTimeoutMask |= currentPins;
         *currentTimeoutMask |= currentPins;
         CommunicationError(ERRG, -1, 0, PSTR("OWI Temperature Conversion timeout"), 200);

         if ( eventDebug <= debug && ( debugMask >> debugOWITemperatures ) & 0x1 )
         {
            snprintf_P(uart_message_string, BUFFER_SIZE - 1,
                       PSTR("DEBUG (%4i, %s) OWI Temperature Conversion timeout (>%i ms) on bus_mask (%i)"),
                       __LINE__, __FILE__,  maxConversionTime, currentPins);
         }
      }
}

/*
 * owiReadSingleTemperature( unsigned char bus_pattern, unsigned char * id )
 *
 * this function will read the data contained in the scratchpad of the device
 * identified by the id ID number
 *
 * it will return the temperature value stored in the scratchpad as an 32bit unsigned int
 *   - where the temperature value is only maximum 16 lower bit
 *   - the remaining top 16 bit are used to signal errors
 *   - if an error occurs, this function will return values larger than 0xFFFF
 */

uint32_t owiReadSingleTemperature( unsigned char bus_pattern, unsigned char * id )
{
   uint32_t temperature = 0x0;

   /*checks*/
   if ( eventDebug <= debug && (debugMask >> debugOWITemperatures) & 0x1)
   {
      snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) fcn:ReadTemperatures begin"), __LINE__, __FILE__);
      UART0_Send_Message_String(NULL,0);
   }
   if ( 0 == ((owiBusMask & bus_pattern) & 0xFF) )
   {
      if ( eventDebug <= debug && (debugMask >> debugOWITemperatures) & 0x1)
      {
         snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) passive (bus pattern 0x%x owiBusMask 0x%x)"), __LINE__, __FILE__,
                    bus_pattern,owiBusMask);
         UART0_Send_Message_String(NULL,0);
      }
      return (0x0L | owiReadStatus_owi_bus_mismatch) << 16;
   }

   if ( 0 != ((owiTemperatureTimeoutMask & bus_pattern) & 0xFF) )
   {
      /*conversion went into timeout*/
      snprintf_P(message, BUFFER_SIZE - 1, PSTR("OWI Temperature Conversion timeout (>%i ms, %s, bus_pattern 0x%x vs. mask: 0x%x)"),
                 OWI_TEMPERATURE_MAX_CONVERSION_TIME_MILLISECONDS, owi_id_string, bus_pattern, owiTemperatureTimeoutMask);
      CommunicationError(ERRG, -1, 0, message, -1001);

      if ( eventDebug <= debug && (debugMask >> debugOWITemperatures) & 0x1)
      {
         snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) %s"), __LINE__, __FILE__, message);
         UART0_Send_Message_String(NULL,0);
      }

      clearString(message, BUFFER_SIZE);
      return (0x0L | owiReadStatus_conversion_timeout) << 16;
   }
#warning TODO: consider the case that bus_pattern has more than one bit active, but the conversion failed/succeeded not on all the same way

   /* Reset, presence */

   if ( 0 == OWI_DetectPresence(bus_pattern) )
   {
      return ( 0x0L | owiReadStatus_no_device_presence ) << 16;
   }

   /* Match id found earlier*/
   OWI_MatchRom(id, bus_pattern);

   /* Send READ SCRATCHPAD command.
    *
    * READ SCRATCHPAD [BEh]
    * This command allows the master to read the contents of the scratchpad. The data transfer starts with the
    * least significant bit of byte 0 and continues through the scratchpad until the 9th byte (byte 8 – CRC) is
    * read. The master may issue a reset to terminate reading at any time if only part of the scratchpad data is needed.
    *
    * (http://datasheets.maxim-ic.com/en/ds/DS18B20.pdf and http://datasheets.maxim-ic.com/en/ds/DS18S20.pdf)
    */

#warning no additional reset (~1ms) or detect presence (2ms) command for termination of reading is issued, why? maybe add global bus_mask to be reset?

   OWI_SendByte(DS1820_READ_SCRATCHPAD, bus_pattern);

   /* Read only two first bytes (temperature low, temperature high)*/
   /* and place them in the 16 bit temperature variable.*/
   temperature = OWI_ReceiveByte(bus_pattern);
   temperature |= ( OWI_ReceiveByte(bus_pattern) << 8 );
   temperature &= 0xFFFF;

   if ( eventDebug <= debug && (debugMask >> debugOWITemperatures) & 0x1)
   {
      snprintf_P(uart_message_string, BUFFER_SIZE - 1, PSTR("DEBUG (%4i, %s) fcn:ReadTemperatures end"), __LINE__, __FILE__);
      UART0_Send_Message_String(NULL,0);
   }

   return temperature | ((0x0L | owiReadWriteStatus_OK) << 16);

}// END of owiReadSingleTemperature function
