/* 
 * AUV Battery Controller
 *
 *
 *              ++
 *              ||
 *              ||
 *   --    .----++-------------+----------O---------+-----------------+---.
 *    )\  /                    |                    |                 |    \
 *    >-<|                     |        MBARI       |                 |    |
 *   ( /  \                    |                    |                 |    /
 *   --    `-------------------+--------------------+-----------------+---'
 *
 * 
 *
 *
 * Copyright MBARI 2016 
 */

#include "board.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#include "emac_support.h"

/*i2c bus stuff*/
#define I2C_POLLING (1)
#define I2C_INTERRUPT (0)

static int mode_poll;   /* Poll/Interrupt mode flag */

/*smbus battery application specific stuff*/
#define INSTR_PWR_ENABLE_PIN   (4)
#define CHG_ENABLE_PIN   (5)
#define DISCH_ENABLE_PIN (6)
#define AUX_ENABLE_PIN   (7)
#define SMBUS_RESET_PIN  (8)

/* Battery smbus address */
#define BATTERY_ADDR	(0x16>>1)

/** Max buffer length */
#define BUFFER_SIZE			0x10
typedef enum {
	BATT_DISCH,
	BATT_OFF,
	BATT_CHG 
} BATTERY_STATE;

typedef struct {
	uint16_t serialNo;
	uint8_t  mux, channel;
	uint16_t status;
	uint16_t cellVolts[8];
	uint16_t stackVolt;
	uint16_t capacity_mAh;
	int16_t current;
	uint16_t cycleCnt;
	uint8_t state;
} BATTERY_DATA;

/** These global variables below used in POLLING mode - Slave device ----------------*/
uint8_t Master_Buf[BUFFER_SIZE];
BATTERY_DATA batteryData[55];
char strData[2100];
BATTERY_STATE battState;
uint16_t totalSMBUSErrors = 0;
uint16_t batteryTemp;
uint32_t humidity_temp;
uint16_t humidity;
float fval1, fval2, fval3;
bool batteryDataReady = false;
int batteryCount = 0;

xSemaphoreHandle batterySemphr;

/*
 * network config
 *
 * Some parameters like IP addr, etc. are set in external c-files,
 * so they can't be local defines.
 */

#define  TCP_BUFSIZE	256

const uint8_t mac_addr[] 		= { 0x0c, 0x1d, 0x12, 0xe0, 0x1f, 0x10 };
const char tcp_srv_ip[]  		= "134.89.13.201"; 	/* IP address */
const char tcp_srv_gw[]  		= "134.89.12.1";	/* gateway */
const char tcp_srv_nm[] 		= "255.255.254.0"; 	/* netmask */
const uint16_t tcp_srv_port = 23;					/* listening port */

#define WATCHDOG_ON		0


/* global variables */
TASK *to_ts_comm;
xTaskHandle to_ts_battery = NULL;
char *srv_inbuf = NULL;		/* TCP receive buffer in AHB RAM */

/* functions in embTCP stack library */
extern void uart_init(int);
extern int32_t nichestack_init(uint32_t);
extern void embTCP_tick_hook(void);

/* functions in emac_support */
extern void msleep(uint32_t);
extern int get_mac_address(int iface, uint8_t *mac_addr);

/* local functions */
static void commTask(void *parm);
int tcp_srv_init(void);
void tcp_srv_recv(void);
int tcp_srv_respond_term(SOCKTYPE sock, char *terminated_str);
int tcp_srv_respond(SOCKTYPE sock, char *bufp, int bytestosend);

/*
 * The embTCP stack requires a write_leds(int) function to display
 * internal status. We don't have enough LEDs for this so we just
 * do nothing.
 */ 
void write_leds(int leds) {
}

/*********************************************************************//**
 * @brief		Initialize buffer
 * @param[in]	type:
 * 				- 0: Initialize Master_Buf with increment value from 0
 * 					Fill all member in Slave_Buf with 0
 * 				- 1: Initialize Slave_Buf with increment value from 0
 * 					Fill all member in Master_Buf with 0
 * @return 		None
 **********************************************************************/
void Buffer_Init(uint8_t type)
{
	uint8_t i;

	if (type)
	{
		for (i = 0; i < BUFFER_SIZE; i++) {
			Master_Buf[i] = i;
		}
	}
	else
	{
		for (i = 0; i < BUFFER_SIZE; i++) {
			Master_Buf[i] = 0;
		}
	}
}

/* Set I2C mode to polling/interrupt */
static void i2c_set_mode(I2C_ID_T id, int polling)
{
	if(!polling) {
		mode_poll &= ~(1 << id);
		Chip_I2C_SetMasterEventHandler(id, Chip_I2C_EventHandler);
		NVIC_EnableIRQ(id == I2C0 ? I2C0_IRQn : I2C1_IRQn);
	} else {
		DEBUGOUT("setting I2C%d to polling mode\n", (int)id);
		mode_poll |= 1 << id;
		NVIC_DisableIRQ(id == I2C0 ? I2C0_IRQn : I2C1_IRQn);
		Chip_I2C_SetMasterEventHandler(id, Chip_I2C_EventHandlerPolling);
	}
}

static void i2c_probe_slaves(I2C_ID_T i2c)
{
	int i;
	uint8_t ch[2];

	DEBUGOUT("\r\nProbing available I2C devices...\r\n");
	DEBUGOUT("\r\n     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
	DEBUGOUT("\r\n====================================================");
	for (i = 0; i <= 0x7F; i++) {
		if (!(i & 0x0F)) DEBUGOUT("\r\n%02X  ", i >> 4);
		if (i <= 7 || i > 0x78) {
			DEBUGOUT("   ");
			continue;
		}
		/* Address 0x48 points to LM75AIM device which needs 2 bytes be read */
		if(Chip_I2C_MasterRead(i2c, i, ch, 1 + (i == 0x48)) > 0)
			DEBUGOUT(" %02X", i);
		else
			DEBUGOUT(" --");
	}
	DEBUGOUT("\r\n");
}

/* State machine handler for I2C0 and I2C1 */
static void i2c_state_handling(I2C_ID_T id)
{
	if (Chip_I2C_IsMasterActive(id)) {
		Chip_I2C_MasterStateHandler(id);
	} else {
		Chip_I2C_SlaveStateHandler(id);
	}
}

/**
 * @brief	I2C0 Interrupt handler
 * @return	None
 */
void I2C0_IRQHandler(void)
{
	i2c_state_handling(I2C0);
}

/* wrapper for msDelay */
void msDelay(int x)
{
	msleep(x);
}


void smbusReset()
{
	Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, 8);
	msDelay(50);
	Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, 8);
	msDelay(50);
	Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, 8);
	msDelay(50);
}


static int setSmbusMux(int mux, uint8_t channel)
{
	int status;
	
	  Master_Buf[0] = channel;
		status = Chip_I2C_MasterSend(I2C0, (uint8_t)((0xE0+ (mux<<1))>>1),
		  Master_Buf, 1);
		vTaskDelay(5);
	  return status;
}

void allPacks(uint8_t state)
{
uint8_t battery, mux;
int batNum;
	
	//set all batteries to "on" state, so that they will respond to charge current request with non-zero
	for (mux = 0,batNum=0; mux <= 7; mux++) {
		for (battery = 1; battery <=8; battery++) {
			if (mux == 5 && battery > 1) continue;
      if (mux == 6 && battery >6) continue;
			
			//select the battery
			if (!setSmbusMux(mux, 1<<(battery-1))) {
				DEBUGOUT("smbus error in mux setup\r\n");
				totalSMBUSErrors++;
			}
			
			Master_Buf[0] = 0x3f;
			Master_Buf[1] = 0x00;
			Master_Buf[2] = state;
			if (Chip_I2C_MasterSend(I2C0, BATTERY_ADDR, Master_Buf, 3) != 3) {
				totalSMBUSErrors++;
			}
			batteryData[batNum++].state = state;
		}
    
		msDelay(2);

		//clear the mux channels
		if (!setSmbusMux(mux, 0)) {
			DEBUGOUT("error clearing mux\r\n");
			totalSMBUSErrors++;
		}
	}
}


static void
batteryTask(void *parm) {
	uint16_t batteryStatus;
	uint16_t batteryVoltage;
	uint16_t designCapacity;
	uint16_t remainingCapacity;
	uint16_t enableReg, batteryMode;
	int16_t maxCurrent = INT16_MIN;
	uint16_t maxVoltage = 0;
	uint16_t minVoltage = UINT16_MAX;
	int16_t minCurrent = INT16_MAX;
	char out_string[80];
	uint16_t cellVoltage[8], my_serialNo;
	uint8_t battery, mux;
	uint16_t requestedChargeCurrent;
	uint8_t totalPacksOn;
	int batNum;

	smbusReset();

	/* Initialize buffer */
	Buffer_Init(1);
	memset(batteryData, 0, sizeof(BATTERY_DATA)*55);

  /* reset the mux's */
	for (mux = 0; mux <= 7; mux++) {
		(void)setSmbusMux(mux, 0);
	}

  /* battery query loop */
	for (;;) {
		maxCurrent = INT16_MIN;
		maxVoltage = 0;
		minCurrent = INT16_MAX;
		minVoltage = UINT16_MAX;

		Board_LED_Set(0, false);
		
		if (xSemaphoreTake(batterySemphr, 500 / portTICK_RATE_MS) == pdFALSE) {			
			continue;
		}

		for (mux = 0,batNum=0,totalPacksOn = 0; mux <= 7; mux++) {
			for (battery = 1; battery <=8; battery++) {
				batteryData[batNum].mux = mux;
				batteryData[batNum].channel = battery;
			
				//select the battery
				if (!setSmbusMux(mux, 1<<(battery-1))) {
//				DEBUGOUT("smbus error in mux setup\r\n");
					totalSMBUSErrors++;
				}

				if (mux == 5 && battery == 2) {
					if (Chip_I2C_MasterCmdRead(I2C0, (0x90>>1), 0x00, (uint8_t *)&batteryTemp, 2) != 2) {
//						sprintf(out_string, "error with T sensor\r\n");
//						DEBUGSTR(out_string);
						totalSMBUSErrors++;
					}
					batteryTemp = ((batteryTemp&0x00ff)<<8) | ((batteryTemp&0xff00)>>8);
					batteryTemp >>= 4;
					continue;
				}

				if (mux == 5 && battery == 8) {
					Chip_I2C_MasterSend(I2C0, (0x4e>>1), (uint8_t *)&humidity_temp, 1);
					msDelay(50);
					if (Chip_I2C_MasterRead(I2C0, (0x4e>>1), (uint8_t *)&humidity_temp, 4) != 4) {
//						sprintf(out_string, "error reading humidity sensor\r\n");
//						DEBUGSTR(out_string);
						totalSMBUSErrors++;
					}
					humidity = humidity_temp & 0x0000ffff;
					humidity = ((humidity&0x00ff)<<8) | ((humidity&0xff00)>>8);
					humidity_temp = humidity_temp>>16;
					humidity_temp = ((humidity_temp&0x00ff)<<8) | ((humidity_temp&0xff00)>>8);
					humidity_temp >>= 2;
					continue;
				}
			
				if (mux == 5 && battery > 2) continue;
				if (mux == 6 && battery >6) continue;
			
				if (Chip_I2C_MasterCmdRead(I2C0, BATTERY_ADDR, 0x03, (uint8_t *)&batteryMode, 2) != 2) {			
//					sprintf(out_string, "error\r\n");
//					DEBUGSTR(out_string);
					totalSMBUSErrors++;
				}

				batteryMode |= (1<<13)|(1<<14);
				Master_Buf[0] = 0x03; //request BatteryStatus()
				Master_Buf[1] = batteryMode & 0xff;
				Master_Buf[2] = (batteryMode & 0xff00) >> 8;
				(void)Chip_I2C_MasterSend(I2C0, BATTERY_ADDR, Master_Buf, 3);
				
				(void)Chip_I2C_MasterCmdRead(I2C0, BATTERY_ADDR, 0x16, (uint8_t *)&batteryData[batNum].status, 2);			
				batteryStatus = batteryData[batNum].status;
//				sprintf(out_string,"0x%x\r\n", batteryStatus);
//				DEBUGSTR(out_string);
			
				(void)Chip_I2C_MasterCmdRead(I2C0, BATTERY_ADDR, 0x09, (uint8_t *)&batteryData[batNum].stackVolt, 2);
				(void)Chip_I2C_MasterCmdRead(I2C0, BATTERY_ADDR, 0x0f, (uint8_t *)&batteryData[batNum].capacity_mAh, 2);
				(void)Chip_I2C_MasterCmdRead(I2C0, BATTERY_ADDR, 0x17, (uint8_t *)&batteryData[batNum].cycleCnt, 2);

				//request Serial Number
				if (Chip_I2C_MasterCmdRead(I2C0, BATTERY_ADDR, 0x1c, (uint8_t *)&batteryData[batNum].serialNo, 2) != 2) {
//					sprintf(out_string, "error requesting serial number\r\n");
//					DEBUGSTR(out_string);
					totalSMBUSErrors++;
				}
				
				my_serialNo = batteryData[batNum].serialNo;
//				sprintf(out_string,"serial no: %d\r\n", my_serialNo);
//				DEBUGSTR(out_string);

				//request Current
				if (Chip_I2C_MasterCmdRead(I2C0, BATTERY_ADDR, 0x0a, (uint8_t *)&batteryData[batNum].current, 2) != 2) {
//					sprintf(out_string, "error requesting current\r\n");
//					DEBUGSTR(out_string);
					totalSMBUSErrors++;
				}
		  
				sprintf(out_string, "current:%d\r\n", batteryData[batNum].current);
	//			DEBUGSTR(out_string);				
			
				if (batteryData[batNum].current > maxCurrent) maxCurrent = batteryData[batNum].current;
				if (batteryData[batNum].stackVolt > maxVoltage) maxVoltage = batteryData[batNum].stackVolt;
				if (batteryData[batNum].current <  minCurrent) minCurrent = batteryData[batNum].current;
				if (batteryData[batNum].stackVolt < minVoltage) minVoltage = batteryData[batNum].stackVolt;

				//request requestedChargingCurrent
				if (Chip_I2C_MasterCmdRead(I2C0, BATTERY_ADDR, 0x14, (uint8_t *)&requestedChargeCurrent, 2) != 2) {
//					sprintf(out_string, "error requesting charge current\r\n");
//					DEBUGSTR(out_string);
					totalSMBUSErrors++;
				}
			
				//check individual pack status, turn off if needed
				if ((batteryData[batNum].status & (0x0800/*|0x4000*/)) || (batteryData[batNum].state == 0)) {
					Master_Buf[0] = 0x3f; Master_Buf[1] = 0x00; Master_Buf[2] = 0x00;
					if (Chip_I2C_MasterSend(I2C0, BATTERY_ADDR, Master_Buf, 3) != 3) {
//						sprintf(out_string, "error setting battery state\r\n");
//						DEBUGSTR(out_string);
						totalSMBUSErrors++;
					}
					batteryData[batNum].state = 0;
				} else {
					totalPacksOn++;
				}
				batNum++;
			}

			//clear the mux channels
			if (!setSmbusMux(mux, 0x00)) {
//				DEBUGOUT("error clearing mux\r\n");
				totalSMBUSErrors++;
			}
		}

		if (totalPacksOn <= 25 && battState == BATT_DISCH) {
			printf("turning off pack because on count is %d\n", totalPacksOn);
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, CHG_ENABLE_PIN);
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, DISCH_ENABLE_PIN);
			allPacks(0);
			battState = BATT_OFF;
		}

//		sprintf(out_string, "max current %d\r\n", maxCurrent);
//		DEBUGSTR(out_string);
//		sprintf(out_string, "max voltage %d\r\n", maxVoltage);
//		DEBUGSTR(out_string);
//		sprintf(out_string, "min current %d\r\n", minCurrent);
//		DEBUGSTR(out_string);
//		sprintf(out_string, "min voltage %d\r\n", minVoltage);
//		DEBUGSTR(out_string);
//  	sprintf(out_string, "smbus errors %d\r\n", totalSMBUSErrors);
//		DEBUGSTR(out_string);	
		
		/*
		 * We have queried the batteries at least once, so we can report back
		 * on any network queries.
		 */
		xSemaphoreGive(batterySemphr);	
		memcpy(batteryData, batteryData, sizeof(batteryData));
		batteryCount = batNum;
		batteryDataReady = true;

		if (WATCHDOG_ON) Chip_WWDT_Feed(LPC_WWDT);

		/* sleep for 5s */
		Board_LED_Set(0, true);
		vTaskDelay( 5000 / portTICK_RATE_MS);
	}
}


void
init_wdt(uint32_t timeout) {

	uint32_t wdtFreq;
	/*	Initialize WWDT and event router */
	Chip_WWDT_Init(LPC_WWDT);
	Chip_WWDT_SelClockSource(LPC_WWDT, WWDT_CLKSRC_WATCHDOG_PCLK);
	wdtFreq = Chip_Clock_GetPeripheralClockRate(SYSCTL_PCLK_WDT) / 4;

	/* Set watchdog feed time constant to 0.1s */
	Chip_WWDT_SetTimeOut(LPC_WWDT, wdtFreq * timeout);

	/* Configure WWDT to reset on timeout */
	Chip_WWDT_SetOption(LPC_WWDT, WWDT_WDMOD_WDRESET);

	/* Clear watchdog warning and timeout interrupts */
	Chip_WWDT_ClearStatusFlag(LPC_WWDT, WWDT_WDMOD_WDTOF | WWDT_WDMOD_WDINT);

	/* Start watchdog */
	Chip_WWDT_Start(LPC_WWDT);

	/* Enable watchdog interrupt */
	NVIC_ClearPendingIRQ(WDT_IRQn);
	NVIC_EnableIRQ(WDT_IRQn);
}


int parse_msg(SOCKTYPE sock) {

  int len, battery;

	len = t_recv(sock, srv_inbuf, TCP_BUFSIZE, 0);
	if (len <= 0) {
		/* Read error (-1) or EOF (0) */
		return (-1);
	}
	srv_inbuf[len] = 0;
	
	/*
	 * There seems to be an issue when we send an EOF command in a cygwin
	 * telnet session in that it floods the server with EOT messages.
	 * So we just close the socket in this case since this is what has been
	 * requested by the client anyway.
	 */
	if (len >= 2 && (uint8_t)srv_inbuf[0] == 0xff &&
	  (uint8_t)srv_inbuf[1] == 0xec) {
		return (-1);
	}

	/* we've got a customer, check if we're ready to return battery stats */
	if (len == 7) {
		if (!batteryDataReady) {
			tcp_srv_respond_term(sock, "Battery data not ready!\n");
			return (0);

		} else if (!strncmp(srv_inbuf,"@99AC", 5)) {
			tcp_srv_respond_term(sock, "Setting batteries to charge ...");
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, CHG_ENABLE_PIN);
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, DISCH_ENABLE_PIN);
			allPacks(1);
			msDelay(250);
			Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, CHG_ENABLE_PIN);
			battState = BATT_CHG;
			tcp_srv_respond_term(sock, " done\r\n");

		} else if (!strncmp(srv_inbuf,"@99AD", 5)) {
			tcp_srv_respond_term(sock, "Setting batteries to discharge ...");
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, CHG_ENABLE_PIN);
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, DISCH_ENABLE_PIN);
			allPacks(1);
			msDelay(250);
			Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, DISCH_ENABLE_PIN);
			battState = BATT_DISCH;
			tcp_srv_respond_term(sock, " done\r\n");

		} else if (!strncmp(srv_inbuf,"@99BD", 5)) {
			tcp_srv_respond_term(sock, "Enabling AUX ...");
			Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, AUX_ENABLE_PIN);
			battState = BATT_DISCH;
			tcp_srv_respond_term(sock, " done\r\n");

		} else if (!strncmp(srv_inbuf,"@99BF", 5)) {
			tcp_srv_respond_term(sock, "Disabling AUX ...");
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, AUX_ENABLE_PIN);
			battState = BATT_DISCH;
			tcp_srv_respond_term(sock, " done\r\n");

		} else if (!strncmp(srv_inbuf,"@99AF", 5)) {
			tcp_srv_respond_term(sock, "Turning all batteries off ...");
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, CHG_ENABLE_PIN);
			Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, DISCH_ENABLE_PIN);
			allPacks(0);
			battState = BATT_OFF;
			tcp_srv_respond_term(sock, " done\r\n");

		} else if (!strncmp(srv_inbuf+3,"PF", 2)) {
			int pack;
			char out[32];
			sscanf(srv_inbuf, "@%2dPF", &pack);
			printf("turning off pack %d\n", pack);
			batteryData[pack].state = 0;
			sprintf(out, "Pack %d turned off\r\n", pack);
			tcp_srv_respond_term(sock, out);

		} else if (!strncmp(srv_inbuf,"@99RQ", 5)) {
			char *strPtr = strData;
			tcp_srv_respond_term(sock, "querying battery status ... \r\n");
			if (xSemaphoreTake(batterySemphr,
			  6000 / portTICK_RATE_MS) == pdFALSE) {
				tcp_srv_respond_term(sock, "No access to battery status!\r\n");
				return (0);
			}
			for (battery = 0; battery < batteryCount; battery++) {
				batteryData[battery].state? (*strPtr++ = '*') : (*strPtr++ = '#');
				strPtr += sprintf(strPtr,"%02d:%04d,%04d,%05d,%04d,%03d,%04X\r\n", battery, batteryData[battery].serialNo, batteryData[battery].current, batteryData[battery].stackVolt,
					batteryData[battery].capacity_mAh, batteryData[battery].cycleCnt, batteryData[battery].status);
			}
			xSemaphoreGive(batterySemphr);
			fval1 = (float)batteryTemp * .0625F;
			fval2 = (float)humidity_temp*.010072F - 40;
			fval3 = (float)humidity*.006104F;

			strPtr += sprintf(strPtr,"Env:%3dC,%3dC,%3d%%RH\r\n",
			  (int)(fval1*10.0), (int)(fval2*10.0),(int)(fval3*10.0));
			strPtr += sprintf(strPtr,"CHG: %d, ",
			  Chip_GPIO_ReadValue(LPC_GPIO, 0) & 1<<CHG_ENABLE_PIN ? 1 : 0);
			strPtr += sprintf(strPtr,"DISCH: %d, ",
			  Chip_GPIO_ReadValue(LPC_GPIO, 0) & 1<<DISCH_ENABLE_PIN ? 1 : 0);
			strPtr += sprintf(strPtr,"AUX: %d\r\n",
			  Chip_GPIO_ReadValue(LPC_GPIO, 0) & 1<<AUX_ENABLE_PIN ? 1 : 0);
			strPtr += sprintf(strPtr,"SMBUSerr:%d\r\n", totalSMBUSErrors);

			tcp_srv_respond(sock, strData, strPtr-strData);
		}
	}
	return (0);
}

static void
commTask(void *parm) {

	fd_set active_fd_set, read_fd_set;
	struct sockaddr_in client;
	int len, i, j, n;
	const int sock_count = 1 + 5; /* 1 master, 5 clients */
	SOCKTYPE socks[sock_count];
	uint32_t physts;
	
	memset(socks, 0, sizeof(socks));
	
	/* wait for tcp stack to be ready */
	while (!iniche_net_ready) TK_SLEEP(4);
	
	/* Create the master socket and set it up to accept connections. */
	socks[0] = tcp_srv_init();
	
	/* Initialize the set of active sockets. */
	FD_ZERO (&active_fd_set);
	FD_SET (socks[0], &active_fd_set);

	while (1) {
		read_fd_set = active_fd_set;
		if ((n = t_select(&read_fd_set, NULL, NULL, 10)) < 0) {
			panic ("select");
		}
		if (n < 1) {
			continue;
		}

		/* Service all the sockets with input pending. */
		for (i = 0; i < sock_count; i++) {
			if (!socks[i] || !FD_ISSET(socks[i], &read_fd_set)) continue;

			if (i == 0) {
				/* Connection request on original socket. */
				SOCKTYPE new;
				len = sizeof (client);
				new = t_accept (socks[0], (struct sockaddr *) &client,
				  &len);
				if (new < 0) {
					perror ("accept");
					exit (EXIT_FAILURE);
				}
				tcp_srv_respond_term(new,
				  "\r\n\n  >([[[[ AUV ]]]) Battery Controller\r\n\n");
				/* find empty socket slot */
				for (j = 1; j < sock_count + 1; j++) {
					if (!socks[j]) {
						FD_SET (new, &active_fd_set);
						socks[j] = new;
						break;
					}
					if (j >= sock_count) {
						tcp_srv_respond_term(new,
						  "Too many active connections, closing!\r\n");
						t_socketclose(new);
					}
				}
			} else {
				/* Data arriving on an already-connected socket. */
				if (parse_msg(socks[i]) < 0) {
					t_socketclose(socks[i]);
					FD_CLR (socks[i], &active_fd_set);
					socks[i] = 0;
				}
			}
			TK_SLEEP(2);
		}

		/* check for link status changes and update EMAC if necessary */
		physts = lpcPHYStsPoll();
		
		/* Only check for connection state when the PHY status has changed */
		if (physts & PHY_LINK_CHANGED) {
			int speed = 10;
			int fdx = false;
			
			if (physts & PHY_LINK_CONNECTED) {
				/* Set interface speed and duplex */
				if (physts & PHY_LINK_SPEED100) {
					Chip_ENET_Set100Mbps(LPC_ETHERNET);
					speed = 100;
				}
				else {
					Chip_ENET_Set10Mbps(LPC_ETHERNET);
					speed = 10;
				}
				if (physts & PHY_LINK_FULLDUPLX) {
					Chip_ENET_SetFullDuplex(LPC_ETHERNET);
					fdx = true;
				}
				else {
					Chip_ENET_SetHalfDuplex(LPC_ETHERNET);
					fdx = false;
				}
				eth_setlink(EMAC_PHY_ADDR, speed, fdx);
			}
			/* Delay for link detection (250mS) */
			TK_SLEEP(250 / portTICK_RATE_MS);
		}
		TK_SLEEP(50 / portTICK_RATE_MS);;
	}
}


/* setup tcp socket for server */
int 
tcp_srv_init(void)
{
	struct sockaddr_in me;
	
	SOCKTYPE elisten_sock;

	/* open TCP socket */
	me.sin_family = AF_INET;
	me.sin_addr.s_addr = INADDR_ANY;
	me.sin_port = htons(tcp_srv_port);

	if (((elisten_sock = t_socket(AF_INET, SOCK_STREAM, 0)) ==
	  INVALID_SOCKET) || 
	  (t_bind(elisten_sock, (struct sockaddr *)&me, sizeof(me)) != 0) ||
	  (t_listen(elisten_sock, 3) != 0)) {
		return (-1);
	}
	/* put listen socket into blocking mode */
//   t_setsockopt(elisten_sock, SOL_SOCKET, SO_NBIO, NULL, 0); 
	t_setsockopt(elisten_sock, 0, SO_NBIO, NULL, 0); 
	if ((srv_inbuf = (char *)pvPortMalloc(TCP_BUFSIZE)) == NULL) {
		return (-1);
	}
	memset(srv_inbuf, 0, TCP_BUFSIZE);
	return (elisten_sock);
}


int
tcp_srv_respond_term(SOCKTYPE sock, char *terminated_str)
{
	return tcp_srv_respond(sock, terminated_str, strlen(terminated_str));
}


/* send response to client */
int
tcp_srv_respond(SOCKTYPE sock, char *bufp, int bytestosend)
{   
   int bytessent;
   int err;

   while (bytestosend) {
      bytessent = t_send(sock, bufp, bytestosend, 0);
      if (bytessent < 0) {
         err = t_errno(sock);
         if (err == EWOULDBLOCK) {
            taskYIELD();
            continue;
         }
         printf("TCP echo server, err=%d while sending reply\n", err);
         return (err);
      } else if (bytessent < bytestosend) {
         /* Only part of the data was written */
         bufp += bytessent;
         bytestosend -= bytessent;
         taskYIELD();       /* Let other tasks run before we try again */
         continue;
      } else {
         break;     /* All bytes sent  */
	  }
   }
   return (0);
}


/* Sets up system hardware */
static void prvSetupHardware(void)
{
	SystemCoreClockUpdate();
	Board_Init();

	/* set up digital outputs*/
	Chip_GPIO_SetPinDIROutput(LPC_GPIO, 0, 4); /* GPIO - 12V off */
	Chip_GPIO_SetPinDIROutput(LPC_GPIO, 0, 5); /* GPIO - CHG_ENABLE */
	Chip_GPIO_SetPinDIROutput(LPC_GPIO, 0, 6); /* GPIO - DISCH_ENABLE */
	Chip_GPIO_SetPinDIROutput(LPC_GPIO, 0, 7); /* GPIO - AUX_DISCHG_EN */
	Chip_GPIO_SetPinDIROutput(LPC_GPIO, 0, 8); /* GPIO - Battery Reset */
	
	/* LED0 is used for the link status, on = PHY cable detected */
	/* Initial LED state is off to show an unconnected cable state */
	Board_LED_Set(0, false);

  /*initialize on-chip smbus hardware*/	

  //to drive the high side switches, use PINSEL_PINMODE_NORMAL, PINSEL_PINMODE_PULLUP. SetValue turns switch on, ClearValue turns switch off.
	//toggle discharge enable line to off
	
	Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, INSTR_PWR_ENABLE_PIN);
	Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, CHG_ENABLE_PIN);
	Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, DISCH_ENABLE_PIN);
	Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, AUX_ENABLE_PIN);
	battState = BATT_OFF;
	
	// Initialize Slave I2C peripheral
	Board_I2C_Init(I2C0);
	Chip_I2C_Init(I2C0);
	Chip_I2C_SetClockRate(I2C0 , 100000);
	i2c_set_mode(I2C0, I2C_INTERRUPT);
	
	/*
	 * Initialize embTCP debug UART.
	 * I have no idea what this does (internal embTCP library function) or
	 * if it clobbers our own UART setup. Would have to try with a serial
	 * cable attached. Disabled for now.
	 */
	//uart_init(3);
}


int
main(void)
{
	int32_t  err;
	char  buf[32];

	prvSetupHardware();
	
	vSemaphoreCreateBinary(batterySemphr);

	err = nichestack_init(0);
	if (err < 0)
	{
		sprintf(&buf[0], "nichestack_init error %d", err);
		panic(buf);
	}

	/* battery query task */
	xTaskCreate( batteryTask, (signed char *)"ts_battery",
//	  configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &to_ts_battery);
	  configMINIMAL_STACK_SIZE, NULL, 2, &to_ts_battery);

	/* TCP comm task */
	TK_CREATE(&commTask, "ts_comm", 1024, 0, 2, &to_ts_comm);

	
	/* enable watchdog */
	if (WATCHDOG_ON) init_wdt(60);
	
	TK_START_OS();
	
	/* should never get here */
	panic("exit");
	return (1);
}



/*----------------- FreeRTOS Application Hooks ----------------------------*/

/* FUNCTION: vApplicationTickHook()
 *
 * Called by the FreeRTOS timer interrupt function.
 */
void
vApplicationTickHook(void)
{
   /* DO NOT remove this function call if you are using embTCP. */
   embTCP_tick_hook();        /* increment the embTCP 'cticks' counter */
}


#if (configUSE_IDLE_HOOK == 1)

/* FUNCTION: vApplicationIdleHook()
 *
 * Called from the "idle" task.
 *
 * PARAMS: none
 *
 * RETURN: none
 */
void
vApplicationIdleHook(void)
{
   const unsigned long ulMSToSleep = 5;

   /* Sleep to reduce CPU load, but don't sleep indefinitely in case
    * there are tasks waiting to be terminated by the idle task.
    */
   Sleep(ulMSToSleep);
}

#endif

#if (configUSE_MALLOC_FAILED_HOOK == 1)

/* FUNCITON: vApplicationMallocFailedHook()
 *
 * Called by FreeRTOS when a memory allocation request fails.
 *
 * PARAMS: none
 *
 * RETURN: none
 */
void
vApplicationMallocFailedHook(void)
{
   panic("malloc");
}

#endif

#if (configCHECK_FOR_STACK_OVERFLOW > 0)

/* FUNCTION: vApplicationStackOverflowHook()
 *
 * Called if the OS detects a task stack overflow.
 *
 * PARAM1: xTaskHandle        current FreeRTOS task
 * PARAM2: char *             name of current task
 *
 * RETURN: none
 */
void
vApplicationStackOverflowHook(xTaskHandle curTCB, char *name)
{
	panic("stack overflow");
}

#endif

/* FUNCTION: vAssertCalled()
 *
 * Called if "configAssert(x)" is defined in FreeRTOSConfig.h.
 *
 * PARAMS: none
 *
 * RETURN: none
 */
void
vAssertCalled(void)
{
	panic("assert");
}

