/**
 * \file
 *
 * \brief Common Sensor Service Light/Proximity Sensor Wake Event Example
 *
 * \mainpage
 *
 * \section intro Introduction
 *
 * This application demonstrates the use of sensor events generated by the 
 * light and/or proximity sensor devices installed on an Atmel Sensors Xplained
 * board.  The application sets up the sensor event handler(s), then places
 * the microcontroller in a low-power sleep state.  When the specified
 * sensor event occurs, the system wakes up, reads and displays sensor data,
 * and re-enters the sleep mode.  This wake/sleep cycle repeats indefinitely.
 *
 * \section contactinfo Contact Information
 * For further information, visit
 * <A href="http://www.atmel.com/avr">Atmel AVR</A>.\n
 * Support and FAQ: http://support.atmel.no/
 *
 * Copyright (C) 2012 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \asf_license_stop
 * 
 */

#include <stdio.h>

#include <board.h>
#include <led.h>
#include <sleepmgr.h>

#include <sensors/sensor.h>


// Configuration Constants

#define LIGHT_WAKE       (true)   // true = use light level to wake up
#define LIGHT_SAMPLE_RATE (10)     // light sensor sample rate (Hz)
#define LIGHT_THRESH   	 (300)     // light threshold (lux)

#define PROX_WAKE        (true)    // true = use near proximity to wake up
#define PROX_SAMPLE_RATE (10)      // proximity sensor sample rate (Hz)
#define PROX_THRESH   	 (100)     // "near" proximity threshold (counts)

#define SET_PROX_THRESHOLD  (true)	// true = manually set proximity threshold
#define	PROX_THRESHOLD		(100)	// manual prox sensor threshold for "near"

#define SET_PROX_CURRENT    (true)	// true = manually set proximity current
#define	PROX_CURRENT_mA		(50)	// prox sensor LED current value (mA)

#define USE_PRINTF       (true)
#define SCALED_DATA      (true)

#if UC3
	// UC3 Sleep Mode to use, one of: 
	//   SLEEPMGR_ACTIVE, SLEEPMGR_IDLE, SLEEPMGR_FROZEN, SLEEPMGR_STDBY,
	//   SLEEPMGR_STOP, SLEEPMGR_DEEPSTOP, SLEEPMGR_STATIC,
	//   SLEEPMGR_SHUTDOWN (UC3L only)
#  define SLEEP_MODE     (SLEEPMGR_FROZEN)   // sleep mode to use

#elif XMEGA
	// XMega Sleep Mode to use, one of: 
	//   SLEEPMGR_ACTIVE, SLEEPMGR_IDLE, SLEEPMGR_ESTDBY, SLEEPMGR_PSAVE,
	//   SLEEPMGR_STDBY, SLEEPMGR_PDOWN
#  define SLEEP_MODE     (SLEEPMGR_IDLE)     // sleep mode to use
#endif


// LED definitions
//

#if (BOARD==UC3_L0_XPLAINED)
#  define ACTIVITY_LED  LED4      // LED to show awake/operating
#  define LIGHT_LED     LED1      // LED to indicate light sensor interrupt
#  define PROX_LED      LED0      // LED to indicate proximity sensor interrupt

#elif (BOARD==UC3_A3_XPLAINED)
#  define ACTIVITY_LED  LED0      // LED to show awake/operating
#  define LIGHT_LED     LED1      // LED to indicate light sensor interrupt
#  define PROX_LED      LED3      // LED to indicate proximity sensor interrupt

#elif (BOARD==XMEGA_A1_XPLAINED) || (BOARD==XMEGA_B1_XPLAINED) || \
      (BOARD==XMEGA_A3BU_XPLAINED)
#  define ACTIVITY_LED  LED0      // LED to show awake/operating
#  define LIGHT_LED     LED2      // LED to indicate light sensor interrupt
#  define PROX_LED      LED3      // LED to indicate proximity sensor interrupt

#endif

static const char * const prox_labels[4] = {
	// strings to display based on proximity values
	"none  ",		// PROXIMITY_NONE
	"FAR   ",		// PROXIMITY_FAR
	"MEDIUM",		// PROXIMITY_MEDIUM
	"NEAR  "		// PROXIMITY_NEAR
};

// supply missing C-library constants

#if defined(XMEGA) && defined(__GNUC__)
enum {EXIT_SUCCESS, EXIT_FAILURE};
#endif

// Set up sensor data storage and data formats.
//
#if (LIGHT_WAKE == true)
	sensor_t light_dev;         // light sensor descriptor
	volatile sensor_data_t light_data = { .scaled = SCALED_DATA };
	bool	 light_event_occurred = false;		// flag to indicate event
#endif

#if (PROX_WAKE == true)
	sensor_t prox_dev;          // proximity sensor descriptor
	volatile sensor_data_t prox_data    = { .scaled = SCALED_DATA };
	bool	 prox_event_occurred = false;		// flag to indicate event
#endif


// Forward declarations
//

#if (LIGHT_WAKE == true)

/*! \internal Light Sensor Event Callback Routine
 *
 * \return  Nothing.
 */
static void light_event(volatile void * in) 
{
	sensor_event_desc_t * const input = (sensor_event_desc_t *) in;

	*((sensor_data_t *)(input->arg)) = input->data;     // copy sensor data

	LED_Toggle(LIGHT_LED);
	light_event_occurred = true;
}
#endif

#if (PROX_WAKE == true)
/*! \internal Proximity Sensor Event Callback Routine
 *
 * \return  Nothing.
 */
static void prox_event(volatile void * in) 
{
	sensor_event_desc_t * const input = (sensor_event_desc_t *) in;

	*((sensor_data_t *)(input->arg)) = input->data;      // copy sensor data

	LED_Toggle(PROX_LED);
	prox_event_occurred = true;
}
#endif


/*! \brief Inertial sensor demo application entry
 *
 * Summarize what's demonstrated in this demonstration.
 *
 * \return  Nothing.
 */
int main(void)
{
#if (USE_PRINTF == true)
	uint32_t id;			// device ID
	uint8_t  version;		// device version
#endif

	// Initialize the Xplained (UC3 or XMEGA) platform & sensor boards.
	//
	sensor_platform_init();

	LED_On(ACTIVITY_LED);

#if (USE_PRINTF == true)
	printf("\r\n");				// new line
#endif

	// Initialize the MCU sleep manager API and specify a sleep mode.
	//
	sleepmgr_init();
	sleepmgr_lock_mode(SLEEP_MODE);


#if (LIGHT_WAKE == true)
	// *********** Light Sensor Initialization ************* //
	//
	// Attach light sensor
	//
	sensor_attach(&light_dev, SENSOR_TYPE_LIGHT, 0, 0);

	if (light_dev.err) {
		puts("\r\nLight sensor initialization error.");
		mdelay(5000);
		return EXIT_FAILURE;
	}

#  if (USE_PRINTF == true)
	sensor_device_id(&light_dev, &id, &version);
	printf("Light sensor: %s    ID = 0x%02x ver. 0x%02x\r\n",
			light_dev.drv->caps.name, (unsigned) id, (unsigned) version);
#  endif

	 // Enable high light level event
	 //
	sensor_event_desc_t light_event_desc = {
		.sensor      = &light_dev,
		.event       = SENSOR_EVENT_HIGH_LIGHT,
		.data.scaled = SCALED_DATA,
		.handler     = light_event,
		.arg         = &light_data,
		.enabled     = true
	};

	sensor_set_sample_rate(&light_dev, LIGHT_SAMPLE_RATE);

	sensor_set_threshold(&light_dev, SENSOR_THRESHOLD_HIGH_LIGHT, 
			              LIGHT_THRESH);

	sensor_add_event(&light_event_desc);

#endif	// LIGHT_WAKE


#if (PROX_WAKE == true)
	// ************* Proximity Sensor Initialization ************* //
	//
	// Attach proximity sensor
	//
	sensor_attach(&prox_dev, SENSOR_TYPE_PROXIMITY, 0, 0);

	if (prox_dev.err) {
		puts("\r\nProximity sensor initialization error.");
		mdelay(5000);
		return EXIT_FAILURE;
	}

#  if (USE_PRINTF == true)
	sensor_device_id(&prox_dev, &id, &version);
	printf("Proximity sensor: %s    ID = 0x%02x ver. 0x%02x\r\n",
			prox_dev.drv->caps.name, (unsigned) id, (unsigned) version);
#  endif

	sensor_set_sample_rate(&prox_dev, PROX_SAMPLE_RATE);

	sensor_set_channel(&prox_dev, 0);		// select all channels

#  if (SET_PROX_THRESHOLD == true)
	// Manually  set proximity threshold values for each channel
	//   Otherwise, sensor will use values previously stored in nvram.
	sensor_set_threshold(&prox_dev, SENSOR_THRESHOLD_NEAR_PROXIMITY, 
		PROX_THRESHOLD);
#  endif

#  if (SET_PROX_CURRENT == true)
	// Manually set LED current value for each channel
	//   Otherwise, sensor will use default values.
	sensor_set_current(&prox_dev, PROX_CURRENT_mA);
#  endif

	// Enable near proximity event for wakeup
	sensor_event_desc_t prox_event_desc = {
		.sensor      = &prox_dev,
		.event       = SENSOR_EVENT_NEAR_PROXIMITY,
		.data.scaled = SCALED_DATA,
		.handler     = prox_event,
		.arg         = &prox_data,
		.enabled     = true
	};

	sensor_add_event(&prox_event_desc);

#endif	// PROX_WAKE


	// ******************* Main Loop ************************ //
		
	while (true) {

		// Put device in low power sleep mode; wait for an interrupt to wake.
		//
		LED_Off(ACTIVITY_LED);            // turn off while asleep

		sleepmgr_enter_sleep();         // enter specified sleep mode

		// Device has woken up 
		//
		LED_On(ACTIVITY_LED);             // turn on while awake


#if (USE_PRINTF == true)

#  if (LIGHT_WAKE == true)
		if (light_event_occurred) {

			light_event_occurred = false;
			printf ("light level = %5d\r\n", (int16_t) light_data.light.value);
		}
#  endif

#  if (PROX_WAKE == true)
		if (prox_event_occurred) {

			prox_event_occurred = false;

			printf ("proximity: source channel=%d  time=%010ld  ", 
				prox_event_desc.channel, prox_data.timestamp);

			if (SCALED_DATA) {
				printf("Chan1:%s Chan2:%s Chan3:%s\r\n",
					prox_labels[prox_data.proximity.value[0]],
					prox_labels[prox_data.proximity.value[1]],
					prox_labels[prox_data.proximity.value[2]]);
			} else {
				printf("Chan1:%4d Chan2:%4d Chan3:%4d\r\n", 
					(int16_t) prox_data.proximity.value[0],
					(int16_t) prox_data.proximity.value[1],
					(int16_t) prox_data.proximity.value[2]);
			}
		}
#  endif

#endif


		mdelay(100);			// minimum active time = 100 msec
	}


	return EXIT_SUCCESS;
}


