/*****************************************************************************
 * FILE: ecrobot_interface.c
 *
 * COPYRIGHT 2008 Takashi Chikamasa <takashic@cybernet.co.jp>
 *
 * <About leJOS NXJ>
 *  leJOS NXJ is a full firmware replacement of LEGO Mindstorms NXT and 
 *  designed for Java programming environment for the NXT 
 *  ( For more detailed information, please see: http://lejos.sourceforge.net/ )
 *  In the leJOS NXJ distribution, C source files for NXT platform layer is also
 *  included besides with the Java VM. The platform C source code is well
 *  structured, comprehensive, and achieved higher performance than the LEGO's
 *  one. Therefore, leJOS NXJ (platform) is also the best GCC based C/C++  
 *  development platform for NXT.
 *
 *  The contents of this file are subject to the Mozilla Public License
 *  Version 1.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *  http://www.mozilla.org/MPL/
 *
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 *  License for the specific language governing rights and limitations
 *  under the License.
 *
 *  The Original Code is TinyVM code, first released March 6, 2000,
 *  later released as leJOS on September 23, 2000.
 *
 *  The Initial Developer of the Original Code is Jose H. Solorzano.
 *
 *  Contributor(s): see leJOS NXJ ACKNOWLEDGEMENTS .
 *
 *
 * <About TOPPERS OSEK>
 *  TOPPERS OSEK is an open source OSEK kernel and developed by TOPPERS project.
 *  TOPPERS(Toyohashi OPen Platform for Embedded Real-time Systems) has been managed 
 *  by a Non Profit Organization founded in Sep. 2003 and has been led by Professor
 *  Hiroaki Takada of Nagoya University in Japan. 
 *
 *  TOPPERS OSEK program is covered by the TOPPERS License as published
 *  by the TOPPERS PROJECT (http://www.toppers.jp/en/index.html).
 *
 *****************************************************************************/

#include <stddef.h>
#include <string.h>

#include "ecrobot_base.h"
#include "ecrobot_private.h"
#include "ecrobot_interface.h"

#include "colorsensor.h"


static volatile U8 deviceStatus = DEVICE_NO_INIT;


/*==============================================================================
 * NXT Servo Motor API
 *=============================================================================*/

/**
 * set Servo Motor PWM duty ratio
 *
 * @param port_id: NXT_PORT_A/NXT_PORT_B/NXT_PORT_C
 * @param speed: PWM duty ration (-100 to 100)
 */
void ecrobot_set_motor_speed(U8 port_id, S8 speed)
{
	/* 1st arg:port id (0/1/2)        */
	/* 2nd arg:speed (-100 to 100)    */
	/* 3rd arg:mode (1:brake/0:float) */

	/* As far as we tested the controllability of motor position for 
	 * precise real-time control applications (e.g. NXTway-GS, NXT GT)
	 * brake mode seems to be better.
	 */
	nxt_motor_set_speed(port_id, speed, 1);
}

/**
 * set Servo Motor brake mode and PWM duty ratio
 *
 * @param port_id: NXT_PORT_A/NXT_PORT_B/NXT_PORT_C
 * @param mode: 0(float)/1(brake)
 * @param speed: PWM duty ratio (-100 to 100)
 */
void ecrobot_set_motor_mode_speed(U8 port_id, S32 mode, S8 speed)
{
	/* 1st arg:port id (0/1/2)        */
	/* 2nd arg:speed (-100 to 100)    */
	/* 3rd arg:mode (1:brake/0:float) */
	nxt_motor_set_speed(port_id, speed, mode);
}

/**
 * get Servo Motor revolution in degree
 *
 * @param port_id: NXT_PORT_A/NXT_PORT_B/NXT_PORT_C
 * @return: motor revolution in degree
 */
S32 ecrobot_get_motor_rev(U8 port_id)
{
	return nxt_motor_get_count(port_id);
}

/**
 * set Servo Motor revolution in degree
 *
 * @param port_id: NXT_PORT_A/NXT_PORT_B/NXT_PORT_C
 * @param rev: motor revolution in degree
 */
void ecrobot_set_motor_rev(U8 port_id, S32 rev)
{
	nxt_motor_set_count(port_id, rev);
}


/*==============================================================================
 * NXT A/D Sensors API
 *=============================================================================*/

/**
 * turn infra-red light on
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 */
void ecrobot_set_light_sensor_active(U8 port_id)
{
	set_digi0(port_id);
}

/**
 * turn infra-red light off
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 */
void ecrobot_set_light_sensor_inactive(U8 port_id)
{
	unset_digi0(port_id);
}

/**
 * get Light Sensor raw A/D data
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return: A/D raw data(0 to 1023)
 */
U16 ecrobot_get_light_sensor(U8 port_id)
{
	return (U16)sensor_adc(port_id);
}

/**
 * get Touch Sensor on/off status
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return: 1(touched)/0(not touched)
 */
U8 ecrobot_get_touch_sensor(U8 port_id)
{
	return (sensor_adc(port_id) < 512);
}

/**
 * get Sound Sensor raw A/D data
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return: A/D raw data(0 to 1023)
 */
U16 ecrobot_get_sound_sensor(U8 port_id)
{
	return (U16)sensor_adc(port_id);
}


/*==============================================================================
 * NXT I2C API
 *=============================================================================*/
/**
 * init a NXT sensor port for I2C communication
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @param type: LOWSPEED_9V/LOWSPEED
 */
void ecrobot_init_i2c(U8 port_id, U8 type)
{
	if (deviceStatus == DEVICE_NO_INIT)
	{
		nxt_avr_set_input_power(port_id, type);
		i2c_enable(port_id);
	}
}

/**
 * wait until I2C communication is ready
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @param wait: wait time out in msec
 * @return: 1(I2C is ready)/0(time out)
 */
U8 ecrobot_wait_i2c_ready(U8 port_id, U32 wait)
{
	volatile U32 time_out;

	time_out = systick_get_ms() + wait;
	while(systick_get_ms() <= time_out)
	{
		if (i2c_busy(port_id) == 0) return 1;
	}
	return 0; /* time out */
}

/**
 * send I2C data
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @param address: 0x01 to 0x7F
 *  Note that addresses are from 0x01 to 0x7F not
 *  even numbers from 0x02 to 0xFE as given in some I2C device specifications.
 *  They are 7-bit addresses not 8-bit addresses.
 * @param i2c_reg: I2C register e.g. 0x42
 * @param buf: buffer containing data to send
 * @param len: length of the data to send
 * @return: 1(success)/0(failure)
 */
SINT ecrobot_send_i2c(U8 port_id, U32 address, SINT i2c_reg, U8 *buf, U32 len)
{
	SINT ret;
	
	ecrobot_wait_i2c_ready(port_id, 50);
	ret = i2c_start_transaction(port_id, address, i2c_reg, 1, buf, len, 1);
	return !ret;
}

/**
 * read I2C data
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @param address: 0x01 to 0x7F
 *  Note that addresses are from 0x01 to 0x7F not
 *  even numbers from 0x02 to 0xFE as given in some I2C device specifications.
 *  They are 7-bit addresses not 8-bit addresses.
 * @param i2c_reg: I2C register e.g. 0x42
 * @param buf: buffer to return data
 * @param len: length of the return data
 * @return: 1(success)/0(failure)
 */
SINT ecrobot_read_i2c(U8 port_id, U32 address, SINT i2c_reg, U8 *buf, U32 len)
{
	SINT ret;

	ecrobot_wait_i2c_ready(port_id, 50);
	ret = i2c_start_transaction(port_id, address, i2c_reg, 1, buf, len, 0);
	return !ret;
}

/**
 * terminate a NXT sensor port used for I2C communication
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 */
void ecrobot_term_i2c(U8 port_id)
{
	i2c_disable(port_id);
}


/*==============================================================================
 * NXT Ultrasonic Sensor API
 *=============================================================================*/
static S32 distance_state[4] = {-1,-1,-1,-1}; /* -1: sensor is not connected */

static S32 getDistance(void)
{
	SINT i;
	
	/*
 	 * if multiple Ultrasonic Sensors are connected to a NXT,
 	 * only the senosr measurement data which is connected to the smallest port id
 	 * can be monitored in LCD and logging data.
 	 */
	for (i = 0; i < 4; i++)
	{
		if (distance_state[i] != -1)
		{
			return distance_state[i];
		}
	}
	return -1;
}

/**
 * init a NXT sensor port for Ultrasonic Sensor
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 */
void ecrobot_init_sonar_sensor(U8 port_id)
{
	ecrobot_init_i2c(port_id, LOWSPEED);
}

/**
 * get Ultrasonic Sensor measurement data in cm
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return: distance in cm (0 to 255), -1 (failure)
 *  NOTE that this API has one cycle delay between data acquisition request
 *  and actual data acquisition. 
 */
S32 ecrobot_get_sonar_sensor(U8 port_id)
{
	static U8 data[4] = {0};

	if (i2c_busy(port_id) == 0)
	{
		distance_state[port_id] = (S32)data[port_id];
	   /* i2c_start_transaction just triggers an I2C transaction,
		* actual data transaction between ARM7 and a Ultrasonic
		* Sensor is done by an ISR after this, so there is one cycle
		* delay for consistent data acquistion
		*/
		i2c_start_transaction(port_id,1,0x42,1,&data[port_id],1,0);
	}

	return distance_state[port_id];
}

/**
 * terminate I2C used for for Ultrasonic Sensor
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 */
void ecrobot_term_sonar_sensor(U8 port_id)
{
	distance_state[0] = -1;
	distance_state[1] = -1;
	distance_state[2] = -1;
	distance_state[3] = -1;
	i2c_disable(port_id);
}

/*==============================================================================
 * NXT Color Sensor API
 *=============================================================================*/
#define NUM_OF_SENSOR_DATA (4)

const U8 NXT_COLORSENSOR_PROTOCOLS[NUM_OF_NXT_COLORSENSOR_MODES] =
{
	COLORSENSOR,
	LIGHTSENSOR_RED,
	LIGHTSENSOR_GREEN,
	LIGHTSENSOR_BLUE,
	COLORSENSOR,
	LIGHTSENSOR_NONE,
	LIGHTSENSOR_NONE
};

typedef struct
{
	U16 color;
	S16 rgb[3];
	U16 light;
	volatile U8 mode;
	volatile U8 mode_state;
	volatile U8 port_in_use;
} NXTCOLORSENSOR_T;

static NXTCOLORSENSOR_T sensor_data[NUM_OF_SENSOR_DATA];

/**
 * Background process to communicate with NXT Color Sensor data
 * @param -
 * @return -
 */
void ecrobot_process_bg_nxtcolorsensor(void)
{
	for (int port_id = 0; port_id < NUM_OF_SENSOR_DATA; port_id++)
	{
		NXTCOLORSENSOR_T* sd = &(sensor_data[port_id]);
		if (sd->port_in_use)
		{
			U8 mode = sd->mode; // copy the sensor mode in atomic manner to keep data consistency
			if (mode == NXT_COLORSENSOR_DEACTIVATE)
			{
				sd->port_in_use = 0;
				colorsensor_init(port_id, LIGHTSENSOR_NONE); // turn off all LEDs
				return;
			}
			else if (mode != sd->mode_state)
			{
				sd->color = NXT_COLOR_UNKNOWN;
				sd->rgb[0] = 0;
				sd->rgb[1] = 0;
				sd->rgb[2] = 0;
				sd->light = 0;
				sd->mode_state = mode;
				colorsensor_init(port_id, LIGHTSENSOR_NONE); // it seems to need to turn off all LEDs to switch a different mode
				colorsensor_init(port_id, NXT_COLORSENSOR_PROTOCOLS[mode]);
			}

			if (mode == NXT_COLORSENSOR)
			{
				sd->color = colorsensor_color_get(port_id);
				colorsensor_rgb_get(port_id, sd->rgb);
			}
			else
			{
				sd->light = colorsensor_light_get(port_id);
				if (mode == NXT_LIGHTSENSOR_WHITE)
				{
					colorsensor_light_full(port_id); // turn on all LEDs for white lamp
				}
			}
		}
	}
}

/**
 * initializes NXT Color Sensor
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @param mode: NXT_COLORSENSOR/NXT_LIGHTSENSOR_RED/NXT_LIGHTSENSOR_GREEN/NXT_LIGHTSENSOR_BLUE/NXT_LIGHTSENSOR_WHITE/NXT_LIGHTSENSOR_NONE/NXT_COLORSENSOR_DEACTIVATE
 */
void ecrobot_init_nxtcolorsensor(U8 port_id, U8 mode)
{
	if (deviceStatus == DEVICE_NO_INIT)
	{
		ecrobot_set_nxtcolorsensor(port_id, NXT_COLORSENSOR_DEACTIVATE);
		ecrobot_set_nxtcolorsensor(port_id, mode);
	}
}

/**
 * sets sensor mode of NXT Color Sensor
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @param mode: NXT_COLORSENSOR/NXT_LIGHTSENSOR_RED/NXT_LIGHTSENSOR_GREEN/NXT_LIGHTSENSOR_BLUE/NXT_LIGHTSENSOR_WHITE/NXT_LIGHTSENSOR_NONE/NXT_COLORSENSOR_DEACTIVATE
 */
void ecrobot_set_nxtcolorsensor(U8 port_id, U8 mode)
{
	if (mode == NXT_COLORSENSOR_DEACTIVATE)
	{
		sensor_data[port_id].color = NXT_COLOR_UNKNOWN;
		sensor_data[port_id].rgb[0] = 0;
		sensor_data[port_id].rgb[1] = 0;
		sensor_data[port_id].rgb[2] = 0;
		sensor_data[port_id].light = 0;
		sensor_data[port_id].mode_state = mode;
	}
	sensor_data[port_id].mode = mode;
	sensor_data[port_id].port_in_use = 1;
}

/**
 * gets sensor mode of NXT Color Sensor
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return NXT_COLORSENSOR/NXT_LIGHTSENSOR_RED/NXT_LIGHTSENSOR_GREEN/NXT_LIGHTSENSOR_BLUE/NXT_LIGHTSENSOR_WHITE/NXT_LIGHTSENSOR_NONE/NXT_COLORSENSOR_DEACTIVATE
 */
U8 ecrobot_get_nxtcolorsensor_mode(U8 port_id)
{
	return sensor_data[port_id].mode;
}

/**
 * gets light sensor data
 * Note that this API is valid for only when one of NXT_LIGHTSENSOR_RED/GREEN/BLUE/WHITE/OFF is configured as the sensor mode.
 * Otherwise, some chunk data is returned
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return 0-1023 raw light sensor data
 */
U16 ecrobot_get_nxtcolorsensor_light(U8 port_id)
{
	return sensor_data[port_id].light;
}

/**
 * gets color sensor data by ID
 * Note that this API is valid for only when COLORSENSOR is configured as the sensor mode. 
 * Otherwise, some chunk data is returned
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return NXT_COLOR_BLACK/NXT_COLOR_BLUE/NXT_COLOR_GREEN/NXT_COLOR_YELLOW/NXT_COLOR_ORANGE/NXT_COLOR_RED/NXT_COLOR_WHITE/NXT_COLOR_UNKNOWN
 */
U16 ecrobot_get_nxtcolorsensor_id(U8 port_id)
{
	U16 color = NXT_COLOR_UNKNOWN;
	switch (sensor_data[port_id].color)
	{
		case BLACK:
			color = NXT_COLOR_BLACK;
			break;
		case LIGHT_BLUE:
		case DARK_BLUE:
			color = NXT_COLOR_BLUE;
			break;
		case GREEN:
			color = NXT_COLOR_GREEN;
			break;
		case YELLOW:
			color = NXT_COLOR_YELLOW;
			break;
		case ORANGE:
			color = NXT_COLOR_ORANGE;
			break;
		case RED:
			color = NXT_COLOR_RED;
			break;
		case WHITE:
			color = NXT_COLOR_WHITE;
			break;
		default:
			// do nothing (unknown color)
			break;
	}

	return color;
}

/**
 * gets color sensor data by RGB
 * Note that this API is valid for only NXT_COLORSENSOR mode.
 * Otherwise, some chunk data is returned
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @param rgb: RGB raw data
 */
void ecrobot_get_nxtcolorsensor_rgb(U8 port_id, S16 rgb[3])
{
	rgb[0] = sensor_data[port_id].rgb[0];
	rgb[1] = sensor_data[port_id].rgb[1];
	rgb[2] = sensor_data[port_id].rgb[2];
}

/**
 * terminates NXT Color Sensor.
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 */
void ecrobot_term_nxtcolorsensor(U8 port_id)
{
	colorsensor_term(port_id);
}

/*==============================================================================
 * RCX Sensors API
 * 
 * According to LEGO Hardware Developer Kit, RCX sensors are categorized as
 * two types
 *   ACTIVE SENSOR:  RCX Light Sensor, RCX Rotation Sensor
 *   PASSIVE SENSOR: RCX Touch Sensor
 * ACTIVE SENSOR requires additional power source to drive the sensor
 * PASSIVE SENSOR is compatible with NXT A/D sensors
 *
 * Concept of these RCX Sensors API is contributed by Maurits Kooiman(Mansk).
 * see http://forums.nxtasy.org/index.php?showtopic=1540
 *=============================================================================*/

/**
 * supply power source for ACTIVE SENSORS
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 */
void ecrobot_set_RCX_power_source(U8 port_id)
{
	if (deviceStatus == DEVICE_NO_INIT)
	{
		nxt_avr_set_input_power(port_id, LOWSPEED_9V);
	}
}

/**
 * stop power source for ACTIVE SENSORS
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 */
void ecrobot_term_RCX_power_source(U8 port_id)
{
	nxt_avr_set_input_power(port_id, 0);
}

/**
 * get RCX Sensor raw A/D data
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return: raw A/D data (0 to 1023)
 */
S16 ecrobot_get_RCX_sensor(U8 port_id)
{
	return (S16)sensor_adc(port_id);
}

/**
 * get RCX Touch Sensor on/off status
 *
 * @param port_id: NXT_PORT_S1/NXT_PORT_S2/NXT_PORT_S3/NXT_PORT_S4
 * @return: 1(touched)/0(not touched)
 */
U8 ecrobot_get_RCX_touch_sensor(U8 port_id)
{
	return (sensor_adc(port_id) < 512);
}


/*==============================================================================
 * NXT internal status API
 *=============================================================================*/
#define N_BTN_STATE 20

static nxt_inputs ecrobot_inputs;
static U8 buttons_states[N_BTN_STATE];
static SINT buttons_i;

/**
 * get battery voltage in mille volt
 *
 * @return: battery voltage in mV (e.g. 9000 = 9.000V)
 */
U16 ecrobot_get_battery_voltage(void)
{
	return (U16)ecrobot_inputs.battery_state;
}

/**
 * get System tick in mille second
 *
 * @return: time in msec.
 *  NOTE that system tick is started when the NXT was turned on.
 *  not when application was started.
 */
U32 ecrobot_get_systick_ms(void)
{
	return systick_get_ms();
}

/**
 * get ENTER buttons status 
 *  NOTE that STOP and EXIT buttons are preserved by the system,
 *  so should not be disclosed to application.
 *
 * @return: 1(pressed)/0(not pressed)
 */
U8 ecrobot_is_ENTER_button_pressed(void)
{
	return ((ecrobot_get_button_state() & ENTER_PRESSED) == ENTER_PRESSED);
}

/**
 * get RUN buttons status 
 *  NOTE that STOP and EXIT buttons are preserved by the system,
 *  so should not be disclosed to application.
 *
 * @return: 1(pressed)/0(not pressed)
 */
U8 ecrobot_is_RUN_button_pressed(void)
{
	return ((ecrobot_get_button_state() & RUN_PRESSED) == RUN_PRESSED);
}

void ecrobot_init_nxtstate(void)
{
//	ecrobot_inputs.battery_state = 0;
	ecrobot_inputs.buttons_state = 0;
	memset(buttons_states, 0, sizeof(buttons_states));
	buttons_i = 0;
}

U8 ecrobot_get_button_state(void)
{
	return ecrobot_inputs.buttons_state;
}

void ecrobot_poll_nxtstate(void)
{
	SINT i;

	ecrobot_inputs.battery_state = battery_voltage();

	/* button debouncer */
	buttons_states[buttons_i++] = (U8)buttons_get() & 0x0F;
	if (buttons_i == N_BTN_STATE) buttons_i = 0;
	for (i = 1; i < N_BTN_STATE; i++)
	{
		if (buttons_states[i-1] != buttons_states[i])
		{
	  		return;
		}
		
		if (i == N_BTN_STATE-1)
    	{
	  		ecrobot_inputs.buttons_state = buttons_states[i];
    	}
  	}
}

void ecrobot_setDeviceInitialized(void)
{
	if (deviceStatus == DEVICE_NO_INIT)
	{
		deviceStatus = DEVICE_INITIALIZED;
	}
}

void ecrobot_initDeviceStatus(void)
{
	deviceStatus = DEVICE_NO_INIT;
}

U8 get_device_status()
{
	return deviceStatus;
}

void ecrobot_restart_NXT(void)
{
	restart_NXT();
}

void ecrobot_shutdown_NXT(void)
{
	shutdown_NXT();
}

void ecrobot_exec_NXT_BIOS(void)
{
	exec_NXT_BIOS();
}

/*==============================================================================
 * NXT Data Logging API for NXT GamePad
 *=============================================================================*/
static S16 adc[4]; /* used for ecrobot_adc_data_monitor */

/**
 * data logging API used with NXT GamePad
 *
 * @param data1: user configurable data to be logged
 * @param data2: user configurable data to be logged
 */
void ecrobot_bt_data_logger(S8 data1, S8 data2)
{
	U8 data_log_buffer[32];

	*((U32 *)(&data_log_buffer[0]))  = (U32)systick_get_ms();
	*(( S8 *)(&data_log_buffer[4]))  =  (S8)data1;
	*(( S8 *)(&data_log_buffer[5]))  =  (S8)data2;
	*((U16 *)(&data_log_buffer[6]))  = (U16)ecrobot_inputs.battery_state;
	*((S32 *)(&data_log_buffer[8]))  = (S32)nxt_motor_get_count(0);
	*((S32 *)(&data_log_buffer[12])) = (S32)nxt_motor_get_count(1);
	*((S32 *)(&data_log_buffer[16])) = (S32)nxt_motor_get_count(2);
	*((S16 *)(&data_log_buffer[20])) = (S16)sensor_adc(0);
	*((S16 *)(&data_log_buffer[22])) = (S16)sensor_adc(1);
	*((S16 *)(&data_log_buffer[24])) = (S16)sensor_adc(2);
	*((S16 *)(&data_log_buffer[26])) = (S16)sensor_adc(3);
	*((S32 *)(&data_log_buffer[28])) = (S32)getDistance();
	
	ecrobot_send_bt_packet(data_log_buffer, 32);
}

/**
 * data logging API used with NXT GamePad
 *
 * @param data1: user configurable data to be logged
 * @param data2: user configurable data to be logged
 * @param adc1:  user configurable data to be logged
 * @param adc2:  user configurable data to be logged
 * @param adc3:  user configurable data to be logged
 * @param adc4:  user configurable data to be logged
 */
void ecrobot_bt_adc_data_logger(S8 data1, S8 data2, S16 adc1, S16 adc2, S16 adc3, S16 adc4)
{
	U8 data_log_buffer[32];

	*((U32 *)(&data_log_buffer[0]))  = (U32)systick_get_ms();
	*(( S8 *)(&data_log_buffer[4]))  =  (S8)data1;
	*(( S8 *)(&data_log_buffer[5]))  =  (S8)data2;
	*((U16 *)(&data_log_buffer[6]))  = (U16)ecrobot_inputs.battery_state;
	*((S32 *)(&data_log_buffer[8]))  = (S32)nxt_motor_get_count(0);
	*((S32 *)(&data_log_buffer[12])) = (S32)nxt_motor_get_count(1);
	*((S32 *)(&data_log_buffer[16])) = (S32)nxt_motor_get_count(2);
	*((S16 *)(&data_log_buffer[20])) = (S16)adc1;
	*((S16 *)(&data_log_buffer[22])) = (S16)adc2;
	*((S16 *)(&data_log_buffer[24])) = (S16)adc3;
	*((S16 *)(&data_log_buffer[26])) = (S16)adc4;
	*((S32 *)(&data_log_buffer[28])) = (S32)getDistance();
	adc[0] = adc1;
	adc[1] = adc2;
	adc[2] = adc3;
	adc[3] = adc4;
	
	ecrobot_send_bt_packet(data_log_buffer, 32);
}


/*==============================================================================
 * NXT LCD display API
 *=============================================================================*/

/**
 * convert a BMP file to an array data for LCD display
 *
 * @param file: monochrome BMP file data
 * @param lcd: data array to be drawn in LCD
 * @param width: pixel width of the BMP file (max. 100)
 * @param height: pixel height of the BMP file (max. 64)
 * @return: 1(success)/-1(failure)
 */
SINT ecrobot_bmp2lcd(const CHAR *file, U8 *lcd, S32 width, S32 height)
{
	SINT bmp_line, bmp_line_alignment;
	SINT bmp_row, bmp_col;
	SINT lcd_row;
	SINT lcd_pos;
	SINT bmp_bit_pos;
	SINT lcd_bit_pos;
	SINT bits;
	U8 bmp_data;
	BMP *bmp = (BMP *)file;
	
	/* check a BMP file header information */
	if (bmp->fileHeader.type != BM_TYPE)  /* Windows */
		return -1;
	if ((bmp->infoHeader.width > NXT_LCD_WIDTH) ||
		(bmp->infoHeader.width < 1) ||
		(bmp->infoHeader.width != width))
		return -1;
	if ((bmp->infoHeader.height > NXT_LCD_DEPTH * 8) ||
		(bmp->infoHeader.height < 1) ||
		(bmp->infoHeader.height != height))
		return -1;
	if (bmp->infoHeader.bits != 1)        /* monochrome bmp */
		return -1;
	if (bmp->infoHeader.compression != 0) /* no compression */
		return -1;
	
	/* 
	 * Specifications of a monochrome bmp file
	 *
	 * - each bit represents a pixel.
	 * - each line has 4 bytes alignment.
	 *   (100 pixels data is saved in 100/8 = 12 + 1 + 3 = 16 byte)
	 * - data order in a BMP file:
     *   (0,n)---------------->(m,n)
     *        ---------------->
     *        ---------------->
     *        ---------------->
     *   (0,0)---------------->(m,0)
	 */
	 
	/* calculate line alignment */
	bmp_line = bmp->infoHeader.width / 8;
	if (bmp->infoHeader.width % 8)
	{
		bmp_line++;
	}
	
	if ((bmp_line % 4) != 0)
	{
		bmp_line_alignment = ((bmp_line / 4) + 1) * 4;
	}
	else
	{
		bmp_line_alignment = bmp_line;
	}
	
	/* convert BMP data to LCD array data */
	for (bmp_row = 0; bmp_row < bmp->infoHeader.height; bmp_row++)
	{
		lcd_row = NXT_LCD_DEPTH - ((bmp_row / 8) + 1);
		lcd_bit_pos = bmp_row % 8;
		bits = 0;
		for(bmp_col = 0; bmp_col < bmp_line; bmp_col++)
		{
			bmp_data = file[(bmp_row * bmp_line_alignment) + bmp_col + bmp->fileHeader.offset];
			lcd_pos = (lcd_row * NXT_LCD_WIDTH) + (bmp_col * 8);
			for (bmp_bit_pos = 0; ((bmp_bit_pos < 8) && (bits < bmp->infoHeader.width)); bmp_bit_pos++, bits++)
			{
				int lcd_index = lcd_pos + bmp_bit_pos;
				/* checks render data can be stored into LCD */
				if (lcd_index < NXT_LCD_WIDTH*NXT_LCD_DEPTH)
				{
					if (!(bmp_data & (0x01 << (7 - bmp_bit_pos))))
					{
						lcd[lcd_index] |= (0x80 >> lcd_bit_pos);
					}
					else
					{
						lcd[lcd_index] &= (~0x80 >> lcd_bit_pos);
					}
				}
			}
		}
	}
	return 1;
}

void ecrobot_show_int(S32 var)
{
	display_clear(0);

	display_goto_xy(0, 7);
	display_string("VAR: ");
	display_int(var, 0);

	display_update();
}

void ecrobot_debug(UINT var)
{
	display_clear(0);

	display_goto_xy(0, 7);
	display_string("DBG: ");
	display_int(var, 0);

	display_update();
}

void ecrobot_debug1(UINT var1, UINT var2, UINT var3)
{
	display_clear(0);

	display_goto_xy(0, 1);
	display_string("VAR1: ");
	display_int(var1, 0);

	display_goto_xy(0, 2);
	display_string("VAR2: ");
	display_int(var2, 0);

	display_goto_xy(0, 3);
	display_string("VAR3: ");
	display_int(var3, 0);

	display_update();
}

void ecrobot_debug2(UINT var1, UINT var2, UINT var3)
{
	display_clear(0);

	display_goto_xy(0, 4);
	display_string("VAR4: ");
	display_int(var1, 0);

	display_goto_xy(0, 5);
	display_string("VAR5: ");
	display_int(var2, 0);

	display_goto_xy(0, 6);
	display_string("VAR6: ");
	display_int(var3, 0);

	display_update();
}

void ecrobot_status_monitor(const CHAR *target_name)
{
	display_clear(0);

	display_goto_xy(0, 0);
   	display_string(target_name);

   	display_goto_xy(0, 1);
   	display_string("TIME:");
   	display_unsigned(systick_get_ms()/1000, 0);

   	display_goto_xy(0, 2);
   	display_string("BATT:");
   	display_unsigned(ecrobot_inputs.battery_state/100, 0);
    
   	display_goto_xy(0, 3);
   	display_string("REV: ");
   	display_int(nxt_motor_get_count(0), 0);
   	display_int(nxt_motor_get_count(1), 6);

   	display_goto_xy(0, 4);
   	display_string("     ");
   	display_int(nxt_motor_get_count(2), 0);

   	display_goto_xy(0, 5);
   	display_string("ADC: ");
   	display_unsigned(sensor_adc(0), 0);
   	display_unsigned(sensor_adc(1), 5);

   	display_goto_xy(0, 6);
   	display_string("     ");
   	display_unsigned(sensor_adc(2), 0);
   	display_unsigned(sensor_adc(3), 5);

   	display_goto_xy(0, 7);
   	display_string("BT/DST: ");
	if (ecrobot_get_bt_status() == BT_STREAM)
	{
		display_unsigned(1, 0);
   	}
   	else
   	{
		display_unsigned(0, 0);
   	}
   	display_int(getDistance(), 5);

	display_update();
}

/* This function is used to display user selected sensor values */
void ecrobot_adc_data_monitor(const CHAR *target_name)
{
	display_clear(0);

	display_goto_xy(0, 0);
   	display_string(target_name);

   	display_goto_xy(0, 1);
   	display_string("TIME:");
   	display_unsigned(systick_get_ms()/1000, 0);

   	display_goto_xy(0, 2);
   	display_string("BATT:");
   	display_unsigned(ecrobot_inputs.battery_state/100, 0);
    
   	display_goto_xy(0, 3);
   	display_string("REV: ");
   	display_int(nxt_motor_get_count(0), 0);
   	display_int(nxt_motor_get_count(1), 6);

   	display_goto_xy(0, 4);
   	display_string("     ");
   	display_int(nxt_motor_get_count(2), 0);

   	display_goto_xy(0, 5);
   	display_string("ADC1/2:");
   	display_int(adc[0], 0);
   	display_int(adc[1], 5);

   	display_goto_xy(0, 6);
   	display_string("ADC3/4:");
   	display_int(adc[2], 0);
   	display_int(adc[3], 5);

   	display_goto_xy(0, 7);
   	display_string("BT/DST: ");
	if (ecrobot_get_bt_status() == BT_STREAM)
	{
		display_unsigned(1, 0);
   	}
   	else
   	{
		display_unsigned(0, 0);
   	}
   	display_int(getDistance(), 5);

	display_update();
}

/* This function is used to display user selected signed integer values */
void ecrobot_sint_var_monitor(SINT vars[16])
{
	display_clear(0);

	for (SINT i=0; i<16/2; i++)
	{
		display_goto_xy(0, i);
		display_int(vars[2*i], 8);
		display_int(vars[2*i+1], 8);
   	}

	display_update();
}

/*==============================================================================
 * NXT Sound API
 *=============================================================================*/

/**
 * play a tone(wrapper of leJOS sound tone api)
 *
 * @param freq: soundfrequency in Hz (31 to 2100)
 * @param ms: sound duration in centisecond (10 - 256)
 * @vol: sound volume (0 to 100)
 * @return 1 (always)
 */
SINT ecrobot_sound_tone(U32 freq, U32 ms, U32 vol)
{
	sound_freq_vol(freq, ms, vol);
	return 1;
}

/**
 * play a WAV file (support for only 8bit monoral PCM)
 *
 * @param file: WAV file data
 *  NOTE that supports only 8bit monoral PCM
 * @param length: length of WAV file
 * @param freq: sampling frequency -1(inherit the WAV file original)/2000 to 22050Hz
 * @param vol: sound volume(0 to 100)
 * @return: 1(success)/0(sound resource is busy)/-1(failure)
 */
SINT ecrobot_sound_wav(const CHAR *file, U32 length, S32 freq, U32 vol)
{
	WAV *wav = (WAV *)file;

	/* check sound resource is free */
	if (sound_get_time() > 0)
		return 0;
		
	/* check length of a file */
	if (length < WAV_HDR_SIZE)
		return -1;
	
	/* check a WAV file header information */
	if (wav->riff.chunkID != RIFF_CHUNK_ID)
		return -1;
	if (wav->riff.format != RIFF_FORMAT)
		return -1;
	if (wav->fmt.chunkID != FMT_CHUNK_ID)
		return -1;
	if (wav->fmt.audioFormat != 0x0001) /* PCM */
		return -1;
	if (wav->fmt.numChannels != 0x0001) /* mono channel */
		return -1;
	if (wav->fmt.bitsPerSample != 0x0008) /* 8bit */
		return -1;

	/* in case of freq < 0, freq(sample rate) is inherited from a WAV file */
	if (freq < 0)
	{
		freq = wav->fmt.sampleRate;
	}
	
	/* read wav data. Currently, supported PCM file types are 
	 * linear PCM(data chunkID is "data" and "fact") and non-linear PCM.
	 */
	if (wav -> fmt.dummy == 0x0000 && wav->data.chunkID == DATA_CHUNK_ID)
	{
		/* linear PCM */
		sound_play_sample(wav->data.data, wav->data.chunkSize, (U32)freq, vol);
	}
	else if (wav -> fmt.dummy != 0x0000)
	{
		WAV_ND *wav_nd = (WAV_ND *)file;

		if (wav_nd->data.chunkID == DATA_CHUNK_ID)
		{
			/* linear PCM w/o dummy data */
			sound_play_sample(wav_nd->data.data, wav_nd->data.chunkSize, (U32)freq, vol);
		}
	}
	else
	{
		WAV_FACT *wav_fact = (WAV_FACT *)file;
		if (wav_fact->data.chunkID == FACT_CHUNK_ID && wav_fact->data.data_chunkID == DATA_CHUNK_ID)
		{
 			/* linear PCM with "fact" chunk ID */
			sound_play_sample(wav_fact->data.data, wav_fact->data.data_chunkSize, (U32)freq, vol);
		}
		else
		{
			WAV_NL *wav_nl = (WAV_NL *)file;
			if (wav_nl->data.chunkID == DATA_CHUNK_ID)
			{
 				/* non linear PCM */
				sound_play_sample(wav_nl->data.data, wav_nl->data.chunkSize, (U32)freq, vol);
			}
			else
			{
				return -1;
			}
		}
	}
	return 1;
}

