/*
	Knihovna pro komunikaci s teplotním cidlem MAXIM DS18B20 - 9-12bit
*/
 /*********************************************************************************
Title:    DS18X20-Functions via One-Wire-Bus
Author:   Martin Thomas <eversmith@heizung-thomas.de>   
          http://www.siwawi.arubi.uni-kl.de/avr-projects
Software: avr-gcc 4.3.3 / avr-libc 1.6.7 (WinAVR 3/2010) 
Hardware: any AVR - tested with ATmega16/ATmega32/ATmega324P and 3 DS18B20

Partly based on code from Peter Dannegger and others.

changelog:
20041124 - Extended measurements for DS18(S)20 contributed by Carsten Foss (CFO)
200502xx - function DS18X20_read_meas_single
20050310 - DS18x20 EEPROM functions (can be disabled to save flash-memory)
           (DS18X20_EEPROMSUPPORT in ds18x20.h)
20100625 - removed inner returns, added static function for read scratchpad
         . replaced full-celcius and fractbit method with decicelsius
           and maxres (degreeCelsius*10e-4) functions, renamed eeprom-functions,
           delay in recall_e2 replaced by timeout-handling
10100714 - ow_command_skip_last_recovery used for parasite-powerd devices so the
           strong pull-up can be enabled in time even with longer OW recovery times
**********************************************************************************/
#include "DS18B20.h"
#include "onewire.h"		// Pomocna knihovna realizujici prenos po 1-Wire sbernici
#include "crc8.h"			// Pomocna knihovna pro vypocet 8-bit CRC


#include <string.h>
#include <stdlib.h>

int16_t Teplota_LOCAL[MAXSENSORS] = {DS18X20_INVALID_TEMP,DS18X20_INVALID_TEMP,DS18X20_INVALID_TEMP};
unsigned char Tcidla[MAXSENSORS][DS18X20_SP_SIZE];
unsigned char nSensors;
/* find DS18X20 Sensors on 1-Wire-Bus
   input/ouput: diff is the result of the last rom-search
                *diff = OW_SEARCH_FIRST for first call
   output: id is the rom-code of the sensor found 
   
 0x286F7DDA0200002D00
0x28736ADA0200002600  
   
   */
void Delayds5u(uint32_t nCount)
{
uint32_t temp;
temp=24*nCount-3;
for(; temp != 0; temp--);
}

uint8_t DS18X20_find_sensor(uint8_t *diff, uint8_t id[])
{
	uint8_t go;
	uint8_t ret;

	ret = DS18X20_OK;
	go = 1;
	do {
		*diff = ow_rom_search( *diff, &id[0] );
		if ( *diff == OW_PRESENCE_ERR || *diff == OW_DATA_ERR || *diff == OW_LAST_DEVICE )
		{ 
			go  = 0;
			ret = DS18X20_ERROR;
		}
		else
		{
			if (id[0] == DS18B20_FAMILY_CODE || id[0] == DS18S20_FAMILY_CODE ||
			    id[0] == DS1822_FAMILY_CODE)
			{ 
				go = 0;
			}
		}
//	_delay_ms(100);
	} 
	while (go);

	return ret;
}

/* get power status of DS18x20 
   input:   id = rom_code 
   returns: DS18X20_POWER_EXTERN or DS18X20_POWER_PARASITE */
uint8_t DS18X20_get_power_status(uint8_t id[])
{
	uint8_t pstat;

	ow_reset();
	ow_command(DS18X20_READ_POWER_SUPPLY, id);
	pstat = ow_bit_io(1);
	ow_reset();
	return (pstat) ? DS18X20_POWER_EXTERN : DS18X20_POWER_PARASITE;
}

/* start measurement (CONVERT_T) for all sensors if input id==NULL 
   or for single sensor where id is the rom-code */
uint8_t DS18X20_start_meas(uint8_t with_power_extern, uint8_t id[])
{
	uint8_t ret;

	ow_reset();
	if(ow_input_pin_state())  // only send if bus is "idle" = high
	{
		if (with_power_extern != DS18X20_POWER_EXTERN)
		{
			ow_command_with_parasite_enable(DS18X20_CONVERT_T, id);
			ow_parasite_enable();
		}
		else
		{
			ow_command(DS18X20_CONVERT_T, id);
		}
		ret = DS18X20_OK;
	} 
	else
	{ 
		//DS18X20_start_meas: Short Circuit!
		ret = DS18X20_START_FAIL;
	}
	return ret;
}

// returns 1 if conversion is in progress, 0 if finished
// not available when parasite powered.
uint8_t DS18X20_conversion_in_progress(void)
{
	return ow_bit_io(1) ? DS18X20_CONVERSION_DONE : DS18X20_CONVERTING;
}
/*
  inputs: id[] - ROM CODE of one sensor
  		  sp[] - adress for downloading data
		  n - number of read bytes
  outputs: resulut of operation DS18X20_OK/DS18X20_ERROR_CRC
*/
static uint8_t read_scratchpad(uint8_t id[], uint8_t sp[], uint8_t n)
{
	uint8_t i;
	uint8_t ret;

	ow_command(DS18X20_READ, id);
	for (i = 0; i < n; i++)
	{
		sp[i] = ow_byte_rd();
	}
	if (crc8(&sp[0], DS18X20_SP_SIZE))
	{
		ret = DS18X20_ERROR_CRC;
	}
	else
	{
		ret = DS18X20_OK;
	}
	return ret;
}

#if DS18X20_DECICELSIUS

/* convert scratchpad data to physical value in unit decicelsius */
static int16_t DS18X20_raw_to_decicelsius(uint8_t familycode, uint8_t sp[])
{
	uint16_t measure;
	uint8_t  negative;
	int16_t  decicelsius;
	uint16_t fract;

	measure = sp[0] | (sp[1] << 8);


	if(familycode == DS18S20_FAMILY_CODE)   // 9 -> 12 bit if 18S20
	{
		/* Extended measurements for DS18S20 contributed by Carsten Foss */
		measure &= (uint16_t)0xfffe;   // Discard LSB, needed for later extended precicion calc
		measure <<= 3;                 // Convert to 12-bit, now degrees are in 1/16 degrees units
		measure += (16 - sp[6]) - 4;   // Add the compensation and remember to subtract 0.25 degree (4/16)
	}

	// check for negative 
	if (measure & 0x8000)
	{
		negative = 1;       // mark negative
		measure ^= 0xffff;  // convert to positive => (twos complement)++
		measure++;
	}
	else
	{
		negative = 0;
	}

	// clear undefined bits for DS18B20 != 12bit resolution
	if (familycode == DS18B20_FAMILY_CODE || familycode == DS1822_FAMILY_CODE)
	{
		switch(sp[DS18B20_CONF_REG] & DS18B20_RES_MASK)
		{
			case DS18B20_9_BIT:
				measure &= ~(DS18B20_9_BIT_UNDF);
				break;
			case DS18B20_10_BIT:
				measure &= ~(DS18B20_10_BIT_UNDF);
				break;
			case DS18B20_11_BIT:
				measure &= ~(DS18B20_11_BIT_UNDF);
				break;
			default:
				// 12 bit - all bits valid
				break;
		}
	}

	decicelsius = (measure >> 4);
	decicelsius *= 10;


	fract = (measure & 0x000F) * 640;
	if (!negative)
	{
		fract += 512;
	}
	fract /= 1024;
	decicelsius += fract;

	if (negative)
	{
		decicelsius = -decicelsius;
	}

	if ( /* decicelsius == 850 || */ decicelsius < -550 || decicelsius > 1250 )
	{
		return DS18X20_INVALID_DECICELSIUS;
	}
	else
	{
		return decicelsius;
	}
}

/* format decicelsius-value into string, itoa method inspired 
   by code from Chris Takahashi for the MSP430 libc, BSD-license 
   modifications mthomas: variable-types, fixed radix 10, use div(), 
   insert decimal-point */
uint8_t DS18X20_format_from_decicelsius( int16_t decicelsius, char str[], uint8_t n)
{
	uint8_t sign = 0;
	char temp[7];
	char temp_loc = 0;
	uint8_t str_loc = 0;
	div_t dt;
	unsigned char ret;

	// range from -550:-55.0°C to 1250:+125.0°C -> min. 6+1 chars
	if (n >= (6+1) && decicelsius > -1000 && decicelsius < 10000)
	{
		if (decicelsius < 0)
		{
			sign = 1;
			decicelsius = -decicelsius;
		}

		// construct a backward string of the number.
		do
		{
			dt = div(decicelsius,10);
			temp[temp_loc++] = dt.rem + '0';
			decicelsius = dt.quot;
		}
		while (decicelsius > 0);

		if (sign)
		{
			temp[temp_loc] = '-';
		}
		else
		{
			///temp_loc--;
			temp[temp_loc] = '+';
		}

		// reverse the string.into the output
		while (temp_loc >=0)
		{
			str[str_loc++] = temp[(uint8_t)temp_loc--];
			if (temp_loc == 0)
			{
				str[str_loc++] = DS18X20_DECIMAL_CHAR;
			}
		}
		str[str_loc] = '\0';

		ret = DS18X20_OK;
	}
	else
	{
		ret = DS18X20_ERROR;
	}	
	return ret;
}

/* reads temperature (scratchpad) of sensor with rom-code id
   input: decicelsius - pointer to output variable
   		  in[] - ROM CODE of one sensor
   output: decicelsius 
   returns DS18X20_OK on success */
unsigned char DS18X20_read_decicelsius(uint8_t id[], int16_t *decicelsius)
{
	unsigned char sp[DS18X20_SP_SIZE];
	unsigned char ret;
	
	ow_reset();
	ret = read_scratchpad( id, sp, DS18X20_SP_SIZE );
	if (ret == DS18X20_OK)
	{
		*decicelsius = DS18X20_raw_to_decicelsius(id[0], sp);
	}
	return ret;
}

/* reads temperature (scratchpad) of sensor without id (single sensor)
   output: decicelsius 
   returns DS18X20_OK on success */
unsigned char DS18X20_read_decicelsius_single(uint8_t familycode, int16_t *decicelsius)
{
	unsigned char sp[DS18X20_SP_SIZE];
	unsigned char ret;
	
	ret = read_scratchpad(NULL, sp, DS18X20_SP_SIZE);
	if (ret == DS18X20_OK)
	{
		*decicelsius = DS18X20_raw_to_decicelsius(familycode, sp);
	}
	return ret;
}

#endif /* DS18X20_DECICELSIUS */

#if DS18X20_MAX_RESOLUTION

static int32_t DS18X20_raw_to_maxres( uint8_t familycode, uint8_t sp[] )
{
	uint16_t measure;
	uint8_t  negative;
	int32_t  temperaturevalue;

	measure = sp[0] | (sp[1] << 8);
	//measure = 0xFF5E; // test -10.125
	//measure = 0xFE6F; // test -25.0625

	if(familycode == DS18S20_FAMILY_CODE)    // 9 -> 12 bit if 18S20
	{
		/* Extended measurements for DS18S20 contributed by Carsten Foss */
		measure &= (uint16_t)0xfffe;   // Discard LSB, needed for later extended precicion calc
		measure <<= 3;                 // Convert to 12-bit, now degrees are in 1/16 degrees units
		measure += (16 - sp[6]) - 4; // Add the compensation and remember to subtract 0.25 degree (4/16)
	}

	// check for negative 
	if (measure & 0x8000)
	{
		negative = 1;       // mark negative
		measure ^= 0xffff;  // convert to positive => (twos complement)++
		measure++;
	}
	else 
	{
		negative = 0;
	}

	// clear undefined bits for DS18B20 != 12bit resolution
	if (familycode == DS18B20_FAMILY_CODE || familycode == DS1822_FAMILY_CODE)
	{
		switch(sp[DS18B20_CONF_REG] & DS18B20_RES_MASK)
		{
		case DS18B20_9_BIT:
			measure &= ~(DS18B20_9_BIT_UNDF);
			break;
		case DS18B20_10_BIT:
			measure &= ~(DS18B20_10_BIT_UNDF);
			break;
		case DS18B20_11_BIT:
			measure &= ~(DS18B20_11_BIT_UNDF);
			break;
		default:
			// 12 bit - all bits valid
			break;
		}
	}

	temperaturevalue  = (measure >> 4);
	temperaturevalue *= 10000;
	temperaturevalue +=(measure & 0x000F) * DS18X20_FRACCONV;

	if (negative)
	{
		temperaturevalue = -temperaturevalue;
	}

	return temperaturevalue;
}

uint8_t DS18X20_read_maxres( uint8_t id[], int32_t *temperaturevalue )
{
	uint8_t sp[DS18X20_SP_SIZE];
	uint8_t ret;
	
	ow_reset();
	ret = read_scratchpad( id, sp, DS18X20_SP_SIZE );
	if ( ret == DS18X20_OK )
	{
		*temperaturevalue = DS18X20_raw_to_maxres( id[0], sp );
	}
	return ret;
}

uint8_t DS18X20_read_maxres_single(uint8_t familycode, int32_t *temperaturevalue)
{
	uint8_t sp[DS18X20_SP_SIZE];
	uint8_t ret;
	
	ret = read_scratchpad(NULL, sp, DS18X20_SP_SIZE);
	if (ret == DS18X20_OK)
	{
		*temperaturevalue = DS18X20_raw_to_maxres(familycode, sp);
	}
	return ret;

}

uint8_t DS18X20_format_from_maxres(int32_t temperaturevalue, char str[], uint8_t n)
{
	uint8_t sign = 0;
	char temp[10];
	int8_t temp_loc = 0;
	uint8_t str_loc = 0;
	ldiv_t ldt;
	uint8_t ret;

	// range from -550000:-55.0000°C to 1250000:+125.0000°C -> min. 9+1 chars
	if (n >= (9+1) && temperaturevalue > -1000000L && temperaturevalue < 10000000L)
	{
		if (temperaturevalue < 0)
	 	{
			sign = 1;
			temperaturevalue = -temperaturevalue;
		}

		do
		{
			ldt = ldiv(temperaturevalue, 10);
			temp[temp_loc++] = ldt.rem + '0';
			temperaturevalue = ldt.quot;
		}
		while (temperaturevalue > 0);

		if (sign)
		{
			temp[temp_loc] = '-';
		}
		else
		{
			temp[temp_loc] = '+';
		}

		while (temp_loc >= 0)
		{
			str[str_loc++] = temp[(uint8_t)temp_loc--];
			if (temp_loc == 3)
			{
				str[str_loc++] = DS18X20_DECIMAL_CHAR;
			}
		}
		str[str_loc] = '\0';

		ret = DS18X20_OK;
	}
	else
	{
		ret = DS18X20_ERROR;
	}	
	return ret;
}


#endif /* DS18X20_MAX_RESOLUTION */

/* -----------------------------------------------
 Inicializace cidel
 ---------------------------------------------- */ 
uint8_t DS_18B20_Init(void)
{
	unsigned char i;
nSensors=0;
	nSensors = search_sensors();		// Vyhledani senzoru a zapsani informaci o nich do pameti
													// - vraci pocet nalezenych cidel
	if (nSensors==0)								// Nebyly nalezeny zadne senzory
	{
		return nSensors;
	}
	// Nalezeny nejake senzory - namereni prvotnich hodnot
	DS18X20_start_meas(DS18X20_POWER_EXTERN, NULL);	// zacne mereni na vsech pripojenych senzorech
Delayds5u(150000);//	_delay_ms(DS18B20_TCONV_12BIT);					// Pocka 750ms = doba mereni pri rozliseni 12-bit

	for(i=0;i<nSensors;i++)
	{
		if(DS18X20_read_decicelsius(Tcidla[i], &Teplota_LOCAL[i])==DS18X20_INVALID_DECICELSIUS)	// Precte teplotu (RAW format) z jednoho cidla podle jeho ROM CODE
		{
			Teplota_LOCAL[i] = 0xFFFF;
		}
	}
	return nSensors;						
}

uint8_t DS_18B20_InitSingle(void)
{

	unsigned char i;
nSensors=0;	
	if (ow_reset()==0)								// reset = 0 => nalezeno cidlo
	{
//		ow_byte_wr( OW_SKIP_ROM );
//		if (ow_reset()==0)								// reset = 0 => nalezeno cidlo
//		{	
			ow_byte_wr( OW_READ_ROM );					// Prikaz pro precteni ROM kodu
			for(i=0;i<OW_ROMCODE_SIZE;i++)
			{
				Tcidla[0][i] = ow_byte_rd();	// Nacteni ROM kodu do RAM
			}		
			if (DS18X20_start_meas(DS18X20_POWER_EXTERN, NULL)!=DS18X20_START_FAIL)
			{
				Delayds5u(150000);//_delay_ms(750);
				if (DS18X20_read_decicelsius_single(DS18B20_FAMILY_CODE,&Teplota_LOCAL[0])==DS18X20_INVALID_DECICELSIUS)
				{
					Teplota_LOCAL[0] = 0;
				}
			}
			nSensors = 1;
//		}
	}	
	return nSensors;
}

uint8_t search_sensors(void)
{ 
	uint8_t i;
	uint8_t id[OW_ROMCODE_SIZE];
	uint8_t diff;
	
	ow_reset();

	nSensors = 0;
	
	diff = OW_SEARCH_FIRST;

	while ( diff != OW_LAST_DEVICE && nSensors < MAXSENSORS )
	{
		DS18X20_find_sensor( &diff, &id[0] );
		
		if( diff == OW_PRESENCE_ERR )
		{

			break;
		}
		
		if( diff == OW_DATA_ERR )
		{

			break;
		}
		

		for ( i=0; i < OW_ROMCODE_SIZE; i++ )
			Tcidla[nSensors][i] = id[i];
		
		nSensors++;
	}	
	return nSensors;
}

void get_teploty(int16_t *teploty)
{
	unsigned char i;
 	for(i=0;i<nSensors;i++){
		if(DS18X20_read_decicelsius(Tcidla[i], &Teplota_LOCAL[i])==DS18X20_INVALID_DECICELSIUS)	// Precte teplotu (RAW format) z jednoho cidla podle jeho ROM CODE
		{
			Teplota_LOCAL[i] = 0xFFFF;
		}

		teploty[i]=Teplota_LOCAL[i];
	}

}


 void temptostr(int16_t in, uint8_t *out){
 uint8_t neg;
if (in<0){
 in=-in;
  neg=1;
 out[0]='-';
} else{
  neg=0;
}


out[0+neg]=(in/100);
out[1+neg]=((in-100*out[0+neg])/10);
out[2+neg]=(in-10*out[1+neg]-100*out[0+neg]);
out[3+neg]=0;
out[0+neg]+=0x30;
out[1+neg]+=0x30;
out[2+neg]+=0x30;

if(out[1+neg] == '0' && out[0+neg] == '0') out[1+neg] = ' ';
if(out[0+neg] == '0') out[0+neg] = ' ';

}
