/***********************************************************************************************************************
MMBasic

Onewire.c

Handles all the commands and functions related to One Wire support.

The one wire support is based on code from Dallas Semiconductor Corporation:


Copyright 2012 Gerard Sexton
This file is free software: you can redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

 Copyright (C) 1999-2006 Dallas Semiconductor Corporation,
 All Rights Reserved.

 Permission is hereby granted, free of charge,
 to any person obtaining a copy of this software and
 associated documentation files (the "Software"), to
 deal in the Software without restriction, including
 without limitation the rights to use, copy, modify,
 merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom
 the Software is furnished to do so, subject to the
 following conditions:

 The above copyright notice and this permission notice
 shall be included in all copies or substantial portions
 of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
 ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
 IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 DEALINGS IN THE SOFTWARE.

************************************************************************************************************************/
/**
* @file Onewire.c
* @author Geoff Graham, Peter Mather
* @brief Source for GPS MMBasic function
*/
/**
 * @cond
 * The following section will be excluded from the documentation.
 */




#define _SUPPRESS_PLIB_WARNING                      // required for XC1.33  Later compiler versions will need PLIB to be installed
#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
#define FALSE 0
#define TRUE 1
#define PinRead(a)  gpio_get(PinDef[a].GPno)

void owReset(unsigned char *p);
void owWrite(unsigned char *p);
void owRead(unsigned char *p);


#ifdef INCLUDE_1WIRE_SEARCH
static int LastDiscrepancy;
static int LastFamilyDiscrepancy;
static int LastDeviceFlag;
static unsigned char SerialNum[8];
#endif

#ifdef INCLUDE_CRC
static unsigned short utilcrc16;
static unsigned char utilcrc8;

static const unsigned char dscrc_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 const unsigned short oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
#endif

int mmOWvalue;                                               // value of MM.OW


void ow_pinChk(int pin);
int ow_reset(int pin);
void ow_writeByte(int pin, int data);
int ow_readByte(int pin);
int ow_verifyByte(int pin, int data);
int ow_touchByte(int pin, int data);
int ow_touchBit(int pin, int );
void ow_writeBit(int pin, int );
int ow_readBit(int pin);

void setcrc16(unsigned short reset);
unsigned short docrc16(unsigned short cdata);
void setcrc8(unsigned char reset);
unsigned char docrc8(unsigned char cdata);

int ow_first(int pin, int do_reset, int alarm_only);
int ow_next(int pin, int do_reset, int alarm_only);
int ow_verify(int pin, int alarm_only);
void ow_serialNum(unsigned char *serialnum_buf, int do_read);
void ow_familySearchSetup(int search_family);
void ow_skipFamily(void);
/*  @endcond */

// the main OneWire command
void cmd_onewire(void) {
    unsigned char *p;

    if((p = checkstring(cmdline, (unsigned char *)"RESET")) != NULL)
        owReset(p);
    else if((p = checkstring(cmdline, (unsigned char *)"WRITE")) != NULL)
        owWrite(p);
    else if((p = checkstring(cmdline, (unsigned char *)"READ")) != NULL)
        owRead(p);
#ifdef INCLUDE_1WIRE_SEARCH
//    else if((p = checkstring(cmdline, "SEARCH")) != NULL)
//        owSearch(p);
#endif
    else
        error("Unknown command");
}

/* 
 * @cond
 * The following section will be excluded from the documentation.
 */



/****************************************************************************************************************************
 The DS18B20 command and function
*****************************************************************************************************************************/


// this holds an array of 64-bit ints (one for each pin on the chip)
// each number is zero if not being used for temperature measurement
// or the timeout value if a temperature measurement is underway
long long int *ds18b20Timers;

void Init_ds18b20(int pin, int precision) {
    // set up initial pin status (open drain, output, high)
    ow_pinChk(pin);
    ExtCfg(pin, EXT_NOT_CONFIG, 0);                                 // set pin to unconfigured
    gpio_init(PinDef[pin].GPno); 
    ow_reset(pin);
	disable_interrupts_pico();
    ow_writeByte(pin, 0xcc);                                        // command skip the ROM
    ow_writeByte(pin, 0x4E);                                        // write to the scratchpad
    ow_writeByte(pin, 0x00);                                        // dummy data to TH
    ow_writeByte(pin, 0x00);                                        // dummy data to TL
    ow_writeByte(pin, precision << 5);                              // select the resolution
    ow_reset(pin);
    ow_writeByte(pin, 0xcc);                                        // skip the ROM
    ow_writeByte(pin, 0x44);                                        // command start the conversion
	enable_interrupts_pico();
    PinSetBit(pin, LATSET);
	gpio_set_dir(PinDef[pin].GPno, GPIO_OUT);
	gpio_put(PinDef[pin].GPno,GPIO_PIN_SET);
	ExtCfg(pin, EXT_DS18B20_RESERVED, 0);
}

/*  @endcond */

void cmd_ds18b20(void) {
    int pin, precision;
	getargs(&cmdline, 5,(unsigned char *)",");
    if(argc < 1) error("Argument count");
	char code;
	
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap(pin);
    precision = 1;
    if(argc >= 3 && *argv[2]) precision = getint(argv[2], 0, 3);
	int timeout=(100 << precision);
	if(argc==5)timeout=getint(argv[4],100,2000);
    Init_ds18b20(pin, precision);
    if(ds18b20Timers == NULL) ds18b20Timers = GetMemory(NBRPINS*sizeof(long long int));   // if this is the first time allocate memory for the timer array
    ds18b20Timers[pin] = ds18b20Timer + timeout;         // set the timer count to wait for the conversion
}


void fun_ds18b20(void) {
    int pin, b1, b2;
	getargs(&ep,3,(unsigned char *)",");
	if(!(argc==1 || argc==3))error("Syntax");
	char code;
	int timeout=200000;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap(pin);
	if(argc==3)timeout=getint(argv[2],100,2000)*1000;
    if(ds18b20Timers == NULL || ds18b20Timers[pin] == 0) {
        // the TIMR command has not used
        Init_ds18b20(pin, 1);                                       // the default is 10 bits
        uSec(timeout);                                               // and 200mS conversion
    } else {
        // the TIMR command has been used
        while(ds18b20Timer < ds18b20Timers[pin]);                   // wait for the conversion
        ds18b20Timers[pin] = 0;
    }

	if(!ow_readBit(pin)) {
		fret = 1000.0;
	} else {
        ow_reset(pin);
		disable_interrupts_pico();
        ow_writeByte(pin, 0xcc);                                    // skip the ROM (again)
        ow_writeByte(pin, 0xBE);                                    // command read data
        b1  = ow_readByte(pin);
        b2  = ow_readByte(pin);
		enable_interrupts_pico();
        ow_reset(pin);
        if(b1 == 255 && b2 == 255){
            fret = 1000.0;
		} else
            fret = (MMFLOAT)((short)(((unsigned short)b2 << 8) | (unsigned short)b1)) / 16.0;
    }
    ExtCfg(pin, EXT_NOT_CONFIG, 0);
    targ = T_NBR;
}

/* 
 * @cond
 * The following section will be excluded from the documentation.
 */


/****************************************************************************************************************************
 General functions
*****************************************************************************************************************************/




// send one wire reset and optionally return presence response
void owReset(unsigned char *p) {
	int pin;

	char code;
	if(!(code=codecheck(p)))p+=2;
	pin = getinteger(p);
	if(!code)pin=codemap( pin);
	ow_pinChk(pin);

// set up initial pin status (open drain, output, high)
	ExtCfg(pin, EXT_NOT_CONFIG, 0);									// set pin to unconfigured
    gpio_init(PinDef[pin].GPno); 
	PinSetBit(pin, LATSET);
	PinSetBit(pin, ODCSET);
	ow_reset(pin);
	ExtCurrentConfig[pin] = EXT_NOT_CONFIG;
}
void owWriteCore(int pin, int * buf, int len, int flag){
	disable_interrupts_pico();
	for (int i = 0; i < len; i++) {
		if (flag & 0x04) {
			if (buf[i]) {
				// Write '1' bit
				PinSetBit(pin, LATCLR);										// drive pin low
				uSec(6);
				PinSetBit(pin, LATSET);										// release the bus
				uSec(64);													// wait 64Sec
			} else {
				// Write '0' bit
				PinSetBit(pin, LATCLR);										// drive pin low
				uSec(60);													// wait 60uSec
				PinSetBit(pin, LATSET);										// release the bus
				uSec(10);
			}
		} else {
			ow_writeByte(pin, buf[i]);
		}
	}
	enable_interrupts_pico();
}


// send one wire data
void owWrite(unsigned char *p) {
	int pin, flag, len, i, buf[255];
	unsigned char *cp;

	getargs(&p, MAX_ARG_COUNT*2,(unsigned char *)",");
	if (!(argc & 0x01) || (argc < 7)) error("Argument count");
	char code;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap(pin);
	ow_pinChk(pin);

	flag = getint(argv[2], 0, 15);
	len = getint(argv[4], 1, 255);

	// check the first char for a legal variable name
	cp = argv[6];
	skipspace(cp);
	//if (argc > 7 || (len == 1 && type == 0)) {                    // numeric expressions for data
		if (len != ((argc - 5) >> 1)) error("Argument count");
		for (i = 0; i < len; i++) {
			buf[i] = getinteger(argv[i + i + 6]);
		}

// set up initial pin status (open drain, output, high)
	ExtCfg(pin, EXT_NOT_CONFIG, 0);									// set pin to unconfigured
    gpio_init(PinDef[pin].GPno); 
	PinSetBit(pin, LATSET);
	PinSetBit(pin, ODCSET);
    PinSetBit(pin, TRISCLR);                                        // this line added by JH

	if (flag & 0x01) ow_reset(pin);
	owWriteCore(pin, buf, len, flag);	
	if (flag & 0x02) ow_reset(pin);

	if (flag & 0x08) {												// strong pullup required?
		gpio_set_dir(PinDef[pin].GPno, GPIO_OUT);
		gpio_put(PinDef[pin].GPno,GPIO_PIN_SET);
	}
	ExtCurrentConfig[pin] = EXT_NOT_CONFIG;
	return;
}

void owReadCore(int pin, int * buf, int len, int flag){
    disable_interrupts_pico();
    PinSetBit(pin, TRISCLR);          // set as output *** added this line
    for (int i = 0; i < len; i++) {
        if (flag & 0x04) {
            buf[i] = ow_readBit(pin);
        } else {
            buf[i] = ow_readByte(pin);
        }
    }
    enable_interrupts_pico();
}

// read one wire data
void owRead(unsigned char *p) {
	int pin, flag, len, i, buf[255];
	void *ptr = NULL;

	getargs(&p, MAX_ARG_COUNT*2,(unsigned char *)",");
	if (!(argc & 0x01) || (argc < 7)) error("Argument count");
	char code;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap( pin);
	ow_pinChk(pin);

	flag = getint(argv[2], 0, 15);
	len = getint(argv[4], 1, 255);

    // check the validity of the argument list
    if (len != ((argc - 5) >> 1)) error("Argument count");
    for (i = 0; i < len; i++) {
        ptr = findvar(argv[i + i + 6], V_FIND);
        if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
        if (!(g_vartbl[g_VarIndex].type & (T_NBR | T_INT)) || g_vartbl[g_VarIndex].dims[0] != 0) error("Invalid variable");
    }

// set up initial pin status (open drain, output, high)
	ExtCfg(pin, EXT_NOT_CONFIG, 0);									// set pin to unconfigured
    gpio_init(PinDef[pin].GPno); 
	PinSetBit(pin, LATSET);
	PinSetBit(pin, ODCSET);

	if (flag & 0x01) ow_reset(pin);
	owReadCore(pin, buf, len, flag);
	if (flag & 0x02) ow_reset(pin);

	if (flag & 0x08) {												// strong pullup required?
		PinSetBit(pin, LATSET);
		PinSetBit(pin, TRISCLR);
	}

	for (i = 0; i < len; i++) {
			ptr = findvar(argv[i + i + 6], V_FIND);
            if(g_vartbl[g_VarIndex].type & T_NBR)
                *((MMFLOAT *)ptr) = buf[i];
            else
                *((long long int *)ptr) = buf[i];
	}
	ExtCurrentConfig[pin] = EXT_NOT_CONFIG;
	return;
}


#ifdef INCLUDE_1WIRE_SEARCH
// One wire search
// flag:  1 = reset search
//        2 = alarm set
//        4 = family search
//        8 = skip current family
//       16 = verify
//
/*  @endcond */

void fun_owSearch(void) {
	int pin, flag, alarm, i;
    union map
    {
        unsigned char serbytes[8];
        unsigned long long int ser;
    } buf,inp;
    unsigned char filter=0;

	getargs(&ep, MAX_ARG_COUNT*2,(unsigned char *)",");
	if (!(argc & 0x01) || (argc < 3)) error("Argument count");
	char code;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap( pin);
	ow_pinChk(pin);

	flag = getinteger(argv[2]);
	if (flag < 0 || flag > 31) error("Number out of bounds");
	if (((flag & 0x01) && flag > 7) || ((flag & 0x04) && flag > 7) || ((flag & 0x08) && flag > 15)) error("Invalid flag combination");

	if ((flag & 0x04) || (flag & 0x10)) {
        if(argc < 3) error("Argument count");
        inp.ser=getinteger(argv[4]);
        for (i = 0; i < 8; i++) {
			buf.serbytes[7-i] = inp.serbytes[i];
        }
        filter=buf.serbytes[0];
	}

	if (flag & 0x02) alarm = 1; else alarm = 0;

	// set up initial pin status (open drain, output, high)
	ExtCfg(pin, EXT_NOT_CONFIG, 0);									// set pin to unconfigured
    gpio_init(PinDef[pin].GPno); 
	PinSetBit(pin, LATSET);
	PinSetBit(pin, ODCSET);

	if (flag & 0x01) {
		mmOWvalue = ow_first(pin, 1, alarm);
	} else if (flag & 0x08) {
		ow_skipFamily();
		mmOWvalue = ow_next(pin, 1, alarm);
	} else if (flag & 0x10) {
		ow_serialNum(buf.serbytes, 0);
		mmOWvalue = ow_verify(pin, alarm);
	} else {
		mmOWvalue = ow_next(pin, 1, alarm);
	}
    if(flag & 0x04){
        while(SerialNum[0]!=filter && mmOWvalue){
            mmOWvalue = ow_next(pin, 1, alarm);
        }
    }
	for (i = 0; i < 8; i++) {
			buf.serbytes[7-i] = SerialNum[i];
	}
    if(!mmOWvalue)buf.ser=0;
    iret=buf.ser;
    targ = T_INT;
	return;
}
#endif
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */

#if defined(INCLUDE_CRC)
void fun_owCRC8(void){
	int len, i, x;
	unsigned char buf[255], uc = 0;

	getargs(&ep, MAX_ARG_COUNT*2,",");								// this is a macro and must be the first executable stmt in a block
	if (!(argc & 0x01) || (argc < 3)) error("Argument count");
	len = getinteger(argv[0]);
	if ((len < 1) || (len > 255)) error("Number out of bounds");

    if (len != ((argc - 1) >> 1)) error("Argument count");
    for (i = 0; i < len; i++) {
        x = getinteger(argv[i + i + 6]);
        if (x < 0 || x > 255) error("Number out of bounds");
        buf[i] = (unsigned char)x;
    }
	setcrc8(0);
	for (i = 0; i < len; i++) {
		uc = docrc8(buf[i]);
	}
	fret = (MMFLOAT)uc;
}


void fun_owCRC16(void){
	int len, i, x;
	unsigned short buf[255], us = 0;

	getargs(&ep, MAX_ARG_COUNT*2,",");								// this is a macro and must be the first executable stmt in a block
	if (!(argc & 0x01) || (argc < 3)) error("Argument count");
	len = getinteger(argv[0]);
	if ((len < 1) || (len > 255)) error("Number out of bounds");
    if (len != ((argc - 1) >> 1)) error("Argument count");
    for (i = 0; i < len; i++) {
        x = getinteger(argv[i + i + 6]);
        if (x < 0 || x > 65535) error("Number out of bounds");
        buf[i] = (unsigned short)x;
    }
	setcrc16(0);
	for (i = 0; i < len; i++) {
		us = docrc16(buf[i]);
	}
	fret = (MMFLOAT)us;
}
#endif
/*  @endcond */

void fun_mmOW(void) {
	iret = mmOWvalue;
    targ = T_INT;
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */


void ow_pinChk(int pin) {
//    if(ClockSpeed < 10000000) error("CPU speed too low");
	CheckPin(pin, CP_CHECKALL);
	return;
}


// send one wire reset and detect presence response - returns 1 if found else 0
int ow_reset(int pin) {
	PinSetBit(pin, LATSET);
	PinSetBit(pin, TRISCLR);
	PinSetBit(pin, LATCLR);											// drive pin low
	uSec(481);														// wait 481uSec
	PinSetBit(pin, TRISSET);										// set as input
	PinSetBit(pin, LATSET);											// release the bus
	uSec(70);														// wait 70uSec
	mmOWvalue = PinRead(pin) ^ 0x01;                                // read pin and invert response
	PinSetBit(pin, TRISCLR);											// set as output
	uSec(411);														// wait 411uSec
	return mmOWvalue;
}


void __not_in_flash_func(ow_writeByte)(int pin, int data) {
	int loop;

	for (loop = 0; loop < 8; loop++) {
		ow_writeBit(pin, data & 0x01);
		data >>= 1;
	}
	return;
}


int __not_in_flash_func(ow_readByte)(int pin) {
	int loop, result = 0;

	for (loop = 0; loop < 8; loop++) {
		result >>= 1;
		if (ow_readBit(pin)) result |= 0x80;
	}
	return result;
}


int ow_touchByte(int pin, int data) {
	int loop, result = 0;

	for (loop = 0; loop < 8; loop++) {
		result >>= 1;
		if (data & 0x01) {                                          // if sending a '1' then read a bit else write a '0'
			if (ow_readBit(pin)) result |= 0x80;
		} else {
			ow_writeBit(pin, 0x00);
		}
		data >>= 1;
	}
	return result;
}


int ow_touchBit(int pin, int bit) {
	int result = 0;

	if (bit & 0x01) {                                               // if sending a '1' then read a bit else write a '0'
		if (ow_readBit(pin)) result = 1;
	} else {
		ow_writeBit(pin, 0x00);
	}
	return result;
}


// note that the uSec() function will not time short delays at low clock speeds
// so we directly use the core timer for short delays
void __not_in_flash_func(ow_writeBit)(int pin, int bit) {

	if (bit) {
		// Write '1' bit
        PinSetBit(pin, LATCLR);										// drive pin low
        uSec(6);
		PinSetBit(pin, LATSET);										// release the bus
		uSec(64);													// wait 64Sec
	} else {
		// Write '0' bit
		PinSetBit(pin, LATCLR);										// drive pin low
		uSec(60);													// wait 60uSec
		PinSetBit(pin, LATSET);										// release the bus
        uSec(10);
	}
	return;
}


// note that the uSec() function will not time short delays at low clock speeds
// so we directly use the core timer for short delays
int ow_readBit(int pin) { 
    int result;
    PinSetBit(pin, TRISCLR);                                        // set as output *** JH
    PinSetBit(pin, LATCLR);                                         // drive pin low
    uSec(3);
    PinSetBit(pin, TRISSET);                                        // set as input
    PinSetBit(pin, LATSET);                                         // release the bus
    uSec(10);
    result = PinRead(pin);                                          // read pin 
    // PinSetBit(pin, TRISCLR);
    uSec(53);                                                       // wait 56uSec
    return result;
}

#ifdef INCLUDE_1WIRE_SEARCH


int ow_verifyByte(int pin, int data) {
	return (ow_touchByte(pin, data) == data) ? 1 : 0;
}

//--------------------------------------------------------------------------
// The 'ow_first' finds the first device on the 1-Wire Net
//
// When 'alarm_only' is TRUE (1) the find alarm command 0xEC is
// sent instead of the normal search command 0xF0.
// Using the find alarm command 0xEC will limit the search to only
// 1-Wire devices that are in an 'alarm' state.
//
// 'pin'        - I/O Pin.
// 'do_reset'   - TRUE (1) perform reset before search, FALSE (0) do not
//                perform reset before search.
// 'alarm_only' - TRUE (1) the find alarm command 0xEC is
//                sent instead of the normal search command 0xF0
//
// Returns:   TRUE (1) : when a 1-Wire device was found and it's
//                        Serial Number placed in the global SerialNum[portnum]
//            FALSE (0): There are no devices on the 1-Wire Net.
//
int ow_first(int pin, int do_reset, int alarm_only) {
	// reset the search state
	LastDiscrepancy = 0;
	LastDeviceFlag = FALSE;
	LastFamilyDiscrepancy = 0;

	return ow_next(pin, do_reset, alarm_only);
}


//--------------------------------------------------------------------------
// The 'ow_next' function does a general search.  This function
// continues from the previos search state. The search state
// can be reset by using the 'ow_first' function.
//
// When 'alarm_only' is TRUE (1) the find alarm command
// 0xEC is sent instead of the normal search command 0xF0.
// Using the find alarm command 0xEC will limit the search to only
// 1-Wire devices that are in an 'alarm' state.
//
// 'pin'        - I/O Pin.
// 'do_reset'   - TRUE (1) perform reset before search, FALSE (0) do not
//                perform reset before search.
// 'alarm_only' - TRUE (1) the find alarm command 0xEC is
//                sent instead of the normal search command 0xF0
//
// Returns:   TRUE (1) : when a 1-Wire device was found and it's
//                       Serial Number placed in the global SerialNum[portnum]
//            FALSE (0): when no new device was found.  Either the
//                       last search was the last device or there
//                       are no devices on the 1-Wire Net.
//
int ow_next(int pin, int do_reset, int alarm_only) {
	int bit_test, search_direction, bit_number;
	int last_zero, serial_byte_number, next_result;
	unsigned char serial_byte_mask, lastcrc8;

	// initialize for search
	bit_number = 1;
	last_zero = 0;
	serial_byte_number = 0;
	serial_byte_mask = 1;
	next_result = 0;
	lastcrc8 = 0;
	setcrc8(0);

	// if the last call was not the last one
	if (!LastDeviceFlag) {
		// check if reset first is requested
		if (do_reset)	{
			// reset the 1-wire
			// if there are no parts on 1-wire, return FALSE
			if (!ow_reset(pin)) {
				// reset the search
				LastDiscrepancy = 0;
				LastFamilyDiscrepancy = 0;
				return FALSE;
			}
		}

		// If finding alarming devices issue a different command
		if (alarm_only) {
			ow_writeByte(pin, 0xEC);                                // issue the alarming search command
		} else {
			ow_writeByte(pin, 0xF0);                                // issue the search command
		}

		// loop to do the search
		do {
			// read a bit and its compliment
			bit_test = ow_touchBit(pin, 1) << 1;
			bit_test |= ow_touchBit(pin, 1);

			// check for no devices on 1-wire
			if (bit_test == 3) break;
			else {
				// all devices coupled have 0 or 1
				if (bit_test > 0) {
				  search_direction = !(bit_test & 0x01);            // bit write value for search
				} else {
					// if this discrepancy if before the Last Discrepancy
					// on a previous next then pick the same as last time
					if (bit_number < LastDiscrepancy)
						search_direction = ((SerialNum[serial_byte_number] & serial_byte_mask) > 0);
					else
						// if equal to last pick 1, if not then pick 0
						search_direction = (bit_number == LastDiscrepancy);

					// if 0 was picked then record its position in LastZero
					if (search_direction == 0) {
						last_zero = bit_number;

						// check for Last discrepancy in family
						if (last_zero < 9) LastFamilyDiscrepancy = last_zero;
					}
				}

				// set or clear the bit in the SerialNum byte serial_byte_number
				// with mask serial_byte_mask
				if (search_direction == 1)
				  SerialNum	[serial_byte_number] |= serial_byte_mask;
				else
				  SerialNum	[serial_byte_number] &= ~serial_byte_mask;

				// serial number search direction write bit
				ow_touchBit(pin, search_direction);

				// increment the byte counter bit_number
				// and shift the mask serial_byte_mask
				bit_number++;
				serial_byte_mask <<= 1;

				// if the mask is 0 then go to new SerialNum byte serial_byte_number
				// and reset mask
				if (serial_byte_mask == 0) {
					// The below has been added to accomodate the valid CRC with the
					// possible changing serial number values of the DS28E04.
					if (((SerialNum[0] & 0x7F) == 0x1C) && (serial_byte_number == 1))
						lastcrc8 = docrc8(0x7F);
					else
						lastcrc8 = docrc8(SerialNum[serial_byte_number]);  // accumulate the CRC

					serial_byte_number++;
					serial_byte_mask = 1;
				}
			}
		} while(serial_byte_number < 8);  // loop until through all SerialNum[portnum] bytes 0-7

		// if the search was successful then
		if (!((bit_number < 65) || lastcrc8)) {
			// search successful so set LastDiscrepancy, LastDeviceFlag, next_result
			LastDiscrepancy = last_zero;
			LastDeviceFlag = (LastDiscrepancy == 0);
			next_result = TRUE;
		}
	}

	// if no device found then reset counters so next 'next' will be
	// like a first
	if (!next_result || !SerialNum[0]) {
		LastDiscrepancy = 0;
		LastDeviceFlag = FALSE;
		LastFamilyDiscrepancy = 0;
		next_result = FALSE;
	}

	return next_result;
}


//--------------------------------------------------------------------------
// The 'ow_verify' function checks that the device with the serial number
// in the global SerialNum buffer is present.
//
// When 'alarm_only' is TRUE (1) the find alarm command
// 0xEC is sent instead of the normal search command 0xF0.
// Using the find alarm command 0xEC will limit the search to only
// 1-Wire devices that are in an 'alarm' state.
//
// 'pin'        - I/O Pin.
// 'alarm_only' - TRUE (1) the find alarm command 0xEC is
//                sent instead of the normal search command 0xF0
//
// Returns:   TRUE (1) : device verified present.
//            FALSE (0): device not present.
//
int ow_verify(int pin, int alarm_only) {
	unsigned char serialNum_backup[8];
	int i, rslt, ld_backup, ldf_backup, lfd_backup;

	// keep a backup copy of the current state
	for (i = 0; i < 8; i++) serialNum_backup[i] = SerialNum[i];
	ld_backup = LastDiscrepancy;
	ldf_backup = LastDeviceFlag;
	lfd_backup = LastFamilyDiscrepancy;

	// set search to find the same device
	LastDiscrepancy = 64;
	LastDeviceFlag = FALSE;
	if (ow_next(pin, 1, alarm_only)) {
		// check if same device found
		rslt = TRUE;
		for (i = 0; i < 8; i++) {
			if (serialNum_backup[i] != SerialNum[i]) {
				rslt = FALSE;
				break;
			}
		}
	} else {
		rslt = FALSE;
	}

	// restore the search state
	for (i = 0; i < 8; i++) SerialNum[i] = serialNum_backup[i];
	LastDiscrepancy = ld_backup;
	LastDeviceFlag = ldf_backup;
	LastFamilyDiscrepancy = lfd_backup;

	// return the result of the verify
	return rslt;
}

//--------------------------------------------------------------------------
// The 'ow_serialNum' function either reads or sets the SerialNum buffer
// that is used in the search functions 'ow_first' and 'ow_next'.
// This function contains two parameters, 'serialnum_buf' is a pointer
// to a buffer provided by the caller.  'serialnum_buf' should point to
// an array of 8 unsigned chars.  The second parameter is a flag called
// 'do_read' that is TRUE (1) if the operation is to read and FALSE
// (0) if the operation is to set the internal SerialNum buffer from
// the data in the provided buffer.
//
// 'serialnum_buf' - buffer to that contains the serial number to set
//                   when do_read = FALSE (0) and buffer to get the serial
//                   number when do_read = TRUE (1).
// 'do_read'       - flag to indicate reading (1) or setting (0) the current
//                   serial number.
//
void ow_serialNum(unsigned char *serialnum_buf, int do_read)
{
	unsigned char i;

	// read the internal buffer and place in 'serialnum_buf'
	if (do_read) {
		for (i = 0; i < 8; i++) serialnum_buf[i] = SerialNum[i];
	} else { // set the internal buffer from the data in 'serialnum_buf'
		for (i = 0; i < 8; i++) SerialNum[i] = serialnum_buf[i];
	}
}


//--------------------------------------------------------------------------
// Setup the search algorithm to find a certain family of devices
// the next time a search function is called 'owNext'.
//
// 'search_family' - family code type to set the search algorithm to find
//                   next.
//
void ow_familySearchSetup(int search_family)
{
	int i;

	// set the search state to find SearchFamily type devices
	SerialNum[0] = search_family;
	for (i = 1; i < 8; i++) SerialNum[i] = 0;
	LastDiscrepancy = 64;
	LastDeviceFlag = FALSE;
}


//--------------------------------------------------------------------------
// Set the current search state to skip the current family code.
//
void ow_skipFamily(void)
{
	// set the Last discrepancy to last family discrepancy
	LastDiscrepancy = LastFamilyDiscrepancy;
	LastFamilyDiscrepancy = 0;

	// check for end of list
	if (LastDiscrepancy == 0) LastDeviceFlag = TRUE;
}


#endif

#if defined(INCLUDE_CRC)

void setcrc16(unsigned short reset) {
	utilcrc16 = reset;
	return;
}

unsigned short docrc16(unsigned short cdata) {
	cdata = (cdata ^ (utilcrc16 & 0xff)) & 0xff;
	utilcrc16 >>= 8;

	if (oddparity[cdata & 0xf] ^ oddparity[cdata >> 4])	utilcrc16 ^= 0xc001;

	cdata <<= 6;
	utilcrc16 ^= cdata;
	cdata <<= 1;
	utilcrc16 ^= cdata;

	return utilcrc16;
}


void setcrc8(unsigned char reset) {
	utilcrc8 = reset;
	return;
}

unsigned char docrc8(unsigned char cdata) {
	utilcrc8 = dscrc_table[utilcrc8 ^ cdata];
	return utilcrc8;
}
#endif 
/*  @endcond */
