## @file  SensorDemo_Central.py
## @brief This script implements a basic version of the Bluetooth Low Energy (BLE) Sensor Profile Central role which emulates the BLE Sensor Demo applications available for smartphones (iOS and android). This script configures a BLE device as a BLE Sensor device, Central role which is able to find, connect and properly configure the free fall, acceleration and environment sensors characteristics provided by a BlueNRG/BlueNRG-MS development platform  configured as a BLE Sensor device, Peripheral role. 
## @li The script is implemented using the new BlueNRG GUI language scripts based on the BLE  ACI APIs. The BlueNRG GUI with script engine is delivered within BlueNRG GUI SW package.
## @li The script can also be run using the script launcher standalone utility available within BlueNRG GUI SW package.
## @li A PC running Windows Vista/7 operating systems (64 and 32 bit version) or Windows XP operating systems (32 bit edition), with available USB ports is required for installing the BlueNRG GUI SW package.
##
## <!-- Copyright 2016 by STMicroelectronics.  All rights reserved.       *80*-->
##
##  @section S2 SensorDemo_Central.py script, preliminary setup steps: configure a BLE platform as a BLE Sensor device, Peripheral role. 
##
##  @li <b>BlueNRG/BlueNRG-MS platform configuration steps (valid only for STEVAL-IDB002V1,STEVAL-IDB005V1, STEVAL-IDB005V1D platforms)</b>: <ol>
##  <li> Open the IAR Embedded Workbench for ARM tool.
##  <li> Connect the selected  platform to a PC USB port and to a STLink HW tool 
##  <li> From the File->Open->Workspace menu, open the IAR workspace available on BlueNRG/BlueNRG-MS DK SW package: "Projects\Projects_STD_Library\SensorDemo\EWARM\SensorDemo.eww (BlueNRG), Projects\Projects_STD_Library\SensorDemo\EWARM_BlueNRG-MS\SensorDemo.eww (BlueNRG-MS)" 
##  <li> Select desired configuration to build
##  <li> Select Project->Rebuild All. This will recompile and link the entire application
##  <li> To download the binary image on the selected development platform, please connect STLink to the platform JTAG connector 
##  <li> Select Project->Download and Debug to download the related binary image
##  <li> Alternatively, open the BlueNRG GUI, put the board in DFU mode and download the built binary image
##  <li> NOTE: BlueNRG/BlueNRG-MS Sensor Demo, Peripheral role prebuilt binary image is also available on BlueNRG/BlueNRG-MS DK SW package: "Firmware\STM32L1_prebuilt_images\BlueNRG\BlueNRG_SensorDemo.hex, Firmware\STM32L1_prebuilt_images\BlueNRG-MS\BlueNRG-MS_SensorDemo.hex "  </ol>
##  @li <b>BlueNRG-1 platform configuration steps (valid only for STEVAL-IDB007V1 platform)</b>: <ol>
##  <li> Connect the selected platform to a PC USB port
##  <li> Open the BlueNRG-1 Flasher tool avalable on STSW-BLUENRG1-DK SW package and download the "C:\Program Files (x86)\STMicroelectronics\BlueNRG-1 DK x.x.x\Firmware\BLE_Examples\BLE_SensorDemo\BLE_SensorDemo.exe" file on the BlueNRG-1 platform</ol>
##
##  @section S21 SensorDemo_Central.py script execution steps through BlueNRG GUI Script Engine
##
##  @li Open BlueNRG GUI 
##  @li <b>BlueNRG/BlueNRG-MS platform configuration steps</b>: <ol>
##  <li> Connect the selected platform to a PC USB port
##  <li> Put selected platform in DFU mode and download the "C:\Program Files (x86)\STMicroelectronics\BlueNRG GUI x.x.x\Firmware\STM32L1_prebuilt_images\BlueNRG_VCOM_x_x.hex" using the BlueNRG GUI, Tools, Flash Motherboard FW... utility </ol>
##  @li <b>BlueNRG-1 platform configuration steps</b>: <ol>
##  <li> Connect the selected platform to a PC USB port
##  <li> Download the "C:\Program Files (x86)\STMicroelectronics\BlueNRG GUI x.x.x\Firmware\BlueNRG1\DTM\DTM_UART_16MHz.hex or DTM_SPI.hex" file on the BlueNRG-1 platform using the BlueNRG Flasher utility available on  BlueNRG-1 DK SW package (STSW-BLUENRG1-DK) <ul>
##  <li> <b> NOTES</b>: SPI Network coprocessor configuration file (DTM_SPI.hex) requires some HW changes to be applied to the BlueNRG-1 development platform. Refer to STSW-BNRGUI user manual UM2058. </ul></ol>
##  @li On BlueNRG GUI, open the USB Port associated to the plugged platform
##  @li On BlueNRG GUI, select the Scripts window 
##  @li On BlueNRG GUI, Scripts window, Scripts Engine section, click on ... tab and browse to the script location and select  it
##  @li On BlueNRG GUI, click on  Run Script tab for running the selected script
##  @li BLE SensorDemo, Central device will look for the BLE advertising devices. Then it will connect to first discovered BLE Sensor Demo, Peripheral device and it will enable the related characteristic notifications
##  @li BLE SensorDemo, Central device reads three times the temperature characteristic  and it receives back the related  values (EVT-BLUE_ATT_READ_RESP) from the BLE Sensor, Peripheral device (temperature value is printed on GUI "Sent/Received Packets" section)
##  @li Once the acceleration characteristic notification is enabled from the BLE SensorDemo, Central device, the associated ACI_GATT_NOTIFICATION_EVENT events are received and the acceleration values on the threee axis (x,y,z) are displayed on GUI "Sent/Received Packets" section (further the free fall condition is detected)
##  @li For terminating the script (and closing the current active BLE connection), click on "Terminate Script" tab
##
##  @section S22 Supported platforms
##
##  @li BlueNRG-1 development platform (order code: STEVAL-IDB007V1)
##  @li BlueNRG-MS development platform (order code: STEVAL-IDB005V1) 
##  @li BlueNRG-MS Daughter Board (order code: STEVAL-IDB005V1D)
##  @li BlueNRG-MS USB dongle (order code: STEVAL-IDB006V1)
##  @li BlueNRG development platform (order code: STEVAL-IDB002V1)
##  @li BlueNRG USB dongle (order code: STEVAL-IDB003V1)
## 
##  @section S23 Supported BLE devices and stack versions
##
##  @li BlueNRG-1 stack v1.x
##  @li BlueNRG-MS stack v7.2x
##  @li BlueNRG stack v6.4x
##

## \addtogroup BLE_scripts
## @{
## \see SensorDemo_Central.py for documentation.
## 
## @} 

## @cond DOXYGEN_SHOULD_SKIP_THIS


#Device Name Characteristic UUID
DEVICE_NAME_CHAR_UUID= 0x2A00


# Acceleration service 128 bits UUID
ACCELERATION_SERVICE_UUID=[0x1B,0xC5,0xD5,0xA5,0x02,0x00,0xB4,0x9A,0xE1,0x11,0x3A,0xCF,0x80,0x6E,0x36,0x02]
# Free Fall characteristic 128 bits UUID
FREE_FALL_CHAR_UUID = [0x1B,0xC5,0xD5,0xA5,0x02,0x00,0xFC,0x8F,0xE1,0x11,0x4A,0xCF,0xA0,0x78,0x3E,0xE2]
# Acceleration characteristic 128 bits UUID
ACCELERATION_CHAR_UUID = [0x1B,0xC5,0xD5,0xA5,0x02,0x00,0x36,0xAC,0xE1,0x11,0x4B,0xCF,0x80,0x1B,0x0A,0x34]


# Environmental service 128 bits UUID
ENVIRONMENTAL_SERVICE_UUID=[0x1B,0xC5,0xD5,0xA5,0x02,0x00,0xD0,0x82,0xE2,0x11,0x77,0xE4,0x40,0x1A,0x82,0x42]
#Temperature characteristic
TEMPERATURE_CHAR_UUID =[0x1B,0xC5,0xD5,0xA5,0x02,0x00,0xE3,0xA9,0xE2,0x11,0x77,0xE4,0x20,0x55,0x2E,0xA3]         

   
#HW Reset and Address configuration
RESET()

status = SET_PUBLIC_ADDRESS()
if status!=0x00:
    ERROR('SET_PUBLIC_ADDRESS FAILED')

#Set TX Power
status = ACI_HAL_SET_TX_POWER_LEVEL(En_High_Power=0x01,PA_Level=0x4)
if status!=0x00:
    ERROR('ACI_HAL_SET_TX_POWER_LEVEL FAILED')


#  ACI_GATT_INIT 
if  ACI_GATT_INIT()!=0x00:
    ERROR('ACI_GATT_INIT FAILED')

## ACI_GAP_INIT returns four parameters (refer to UM1755 for detailed API description)
status,_,_,_=  ACI_GAP_INIT(Role=CENTRAL)
if status !=0x00:
    ERROR('ACI_GAP_INIT CALL FAILED')

# Clear Security Database
status= ACI_GAP_CLEAR_SECURITY_DB()
if status!=0x00:
    ERROR('ACI_GAP_CLEAR_SECURITY_DB FAILED')

status=ACI_GAP_START_GENERAL_DISCOVERY_PROC()

if status!=0x00:
    ERROR('ACI_GAP_START_GENERAL_DISCOVERY_PROC FAILED')

listOfDeviceInADV=[]  #List of Device in Advertising with Name = 'BlueNRG'
while (True):
    event = WAIT_EVENT()
   
    if event.event_code==HCI_VENDOR_EVENT and event.get_param('Ecode').val==ACI_GAP_PROC_COMPLETE_EVENT:
        break
    elif (event.event_code==HCI_VENDOR_EVENT and event.get_param('Ecode').val==ACI_GAP_DEVICE_FOUND_EVENT and event.get_param('Event_Type').val != 0x4) :  #SCAN_RSP    :
            name=GET_NAME(event.get_param('Data').val)
            if name==None:
                continue

            #Check Device name in advertising message
            if name=='BlueNRG':             
                type_address =event.get_param('Address_Type').val
                address =event.get_param('Address').val                
                if type_address!=None and address!=None:
                    #If the device is not already inserted, insert it in the list of device in advertising
                    if ([type_address,address] in listOfDeviceInADV)==False:
                        listOfDeviceInADV.insert(0,[type_address,address])
                        
    elif event.event_code==HCI_LE_META_EVENT and event.get_param('Subevent_Code').val==HCI_LE_ADVERTISING_REPORT_EVENT and event.get_param('Event_Type').val[0] != 0x4:  #SCAN_RSP    :
        name=GET_NAME(event.get_param('Data').val[0])
        if name==None:
            continue

        #Check Device name in advertising message
        if name=='BlueNRG':               
            type_address =event.get_param('Address_Type').val[0]
            address =event.get_param('Address').val[0]              
            if type_address!=None and address!=None:
                #If the device is not already inserted, insert it in the list of device in advertising
                if ([type_address,address] in listOfDeviceInADV)==False:
                    listOfDeviceInADV.insert(0,[type_address,address])
        
           
# Check if no device has been discovered 
if (listOfDeviceInADV ==[]):
    ERROR('FAILURE: No BlueNRG SensorDemo discovered')

# Connect to first detected BLE Sensor Demo device 
status=ACI_GAP_CREATE_CONNECTION(Peer_Address_Type=listOfDeviceInADV[0][0],
                                  Peer_Address=listOfDeviceInADV[0][1])

if status!=0x00:
    ERROR('ACI_GAP_CREATE_CONNECTION FAILED')
    
WAIT_EVENT(HCI_VENDOR_EVENT,Ecode=ACI_GAP_PROC_COMPLETE_EVENT )
event = WAIT_EVENT(HCI_LE_META_EVENT, timeout=30, Subevent_Code=HCI_LE_CONNECTION_COMPLETE_EVENT )
if event.get_param('Status').val==0x00:
    conn_handle= event.get_param('Connection_Handle').val
else:
    ERROR('NO CONNECTION ESTABLISHED')

#DISCOVERY OF ALL SERVICES   
#Search ACCELERATION Service
found=False
status=ACI_GATT_DISC_PRIMARY_SERVICE_BY_UUID(Connection_Handle=conn_handle, UUID_Type=0x02, UUID_128=ACCELERATION_SERVICE_UUID)
if (status != 0x00):
    ERROR('DISCOVERY PRIMARY SERVICE (ACCELERATION SERIVCE) FAILED')
                                             
eCode = ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT
while (eCode != ACI_GATT_PROC_COMPLETE_EVENT):
    e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30)
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_ATT_FIND_BY_TYPE_VALUE_RESP_EVENT):
        found=True
    elif (eCode == ACI_GATT_ERROR_RESP_EVENT):
        if not found:
            ERROR('ACCELERATION SERVICE NOT FOUND')
            
found=False
###Read ENVIRONMENTAL Services
status=ACI_GATT_DISC_PRIMARY_SERVICE_BY_UUID(Connection_Handle=conn_handle, UUID_Type=0x02, UUID_128=ENVIRONMENTAL_SERVICE_UUID)
if (status != 0x00):
    ERROR('DISCOVERY PRIMARY SERVICE (ENVIRONMENTAL SERIVCE) FAILED')
                                             
eCode = ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT
while (eCode != ACI_GATT_PROC_COMPLETE_EVENT):
    e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30)
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_ATT_FIND_BY_TYPE_VALUE_RESP_EVENT):
        found=True
    elif (eCode == ACI_GATT_ERROR_RESP_EVENT):
        if not found:
            ERROR('ENVIRONMENTAL SERVICE NOT FOUND')

#Read Device Name with UUID 0x2A00 on Service 0x1800 (GAP Profile Service)
status= ACI_GATT_READ_USING_CHAR_UUID(Connection_Handle=conn_handle,
                   Start_Handle=0x0001, End_Handle=0xffff, UUID_Type=0x01, UUID_16=DEVICE_NAME_CHAR_UUID)  
if (status != 0x00):
    ERROR  ('READ DEVICE NAME CHARACTERISTIC FAILED')

# Check expected device name: BlueNRG [0x42,0x6C,0x75,0x65,0x4E,0x52,0x47]
eCode = ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT
deviceName =[0x42,0x6C,0x75,0x65,0x4E,0x52,0x47]  #BlueNRG
while (eCode != ACI_GATT_PROC_COMPLETE_EVENT):
    e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30)
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT):
        if (e.get_param('Attribute_Value').get_list()  != deviceName):
            ERROR ('DEVICE NAME IS NOT BlueNRG!')

###Discovery all characteristics of Service Changed  
status =  ACI_GATT_DISC_ALL_CHAR_OF_SERVICE( Connection_Handle=conn_handle,Start_Handle=1, End_Handle=4)
if (status != 0x00):
    ERROR('SERVICE CHANGED FAILED')

eCode = ACI_ATT_READ_BY_TYPE_RESP_EVENT
servChanged = [0x02,0x00,0x20,0x03,0x00,0x05,0x2A]
while (eCode != ACI_GATT_PROC_COMPLETE_EVENT):
    e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30)
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_ATT_READ_BY_TYPE_RESP_EVENT):
        if (IS_BlueNRG() or IS_BlueNRG_MS()):
           if (e.get_param('Attribute_Handles').val[0] == 0x0002) and  (e.get_param('Attribute_Values').val[0] !=servChanged[2:]):
                ERROR ('READ SERVICE CHANGED FAILED') 
        else:
            if (e.get_param('Handle_Value_Pair_Data').get_list() !=servChanged):
                ERROR ('READ SERVICE CHANGED FAILED')    

# Enable indication for  Service Changed Characteristic 
status =  ACI_GATT_WRITE_CHAR_VALUE(Connection_Handle=conn_handle, Attr_Handle=0x0004, Attribute_Val_Length=2, Attribute_Val=0x0002)
if (status!= 0x00):
    ERROR('ENABLE INDICATION SERVICE CHANGED FAILED')

e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30, Ecode=ACI_GATT_PROC_COMPLETE_EVENT)


############################################
#Discovery Sensor Demo Characteristics
############################################

##Discovery Acceleration Service Characteristic

##Free Fall  characteristic
found=False
status= ACI_GATT_DISC_CHAR_BY_UUID(Connection_Handle=conn_handle, Start_Handle=0x0001, End_Handle=0xffff, UUID_Type=0x02, UUID_128=FREE_FALL_CHAR_UUID)
if (status != 0x00):
    ERROR('DISCOVERY FREE FALL ACCELERATION CHARACTERISTIC FAILED')
    
eCode = ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT
while (eCode != ACI_GATT_PROC_COMPLETE_EVENT):
    e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30)
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT): 
        freeFall_attr_handle =e.get_param('Attribute_Handle').val    
        found=True
    elif (eCode == ACI_GATT_ERROR_RESP_EVENT):
        if not found:
            ERROR('FREE FALL CHARACTERISTIC NOT FOUND')
 
            
##Acceleration characteristic
found=False
status= ACI_GATT_DISC_CHAR_BY_UUID(Connection_Handle=conn_handle, Start_Handle=0x0001, End_Handle=0xffff, UUID_Type=0x02, UUID_128=ACCELERATION_CHAR_UUID)
if (status != 0x00):
    ERROR('DISCOVERY ACCELERATION CHARACTERISTIC FAILED')
    
eCode = ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT
while (eCode != ACI_GATT_PROC_COMPLETE_EVENT):
    e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30)
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT):
        acc_attr_handle =e.get_param('Attribute_Handle').val   
        found=True
    elif (eCode == ACI_GATT_ERROR_RESP_EVENT):
        if not found:
            ERROR('ACCELERATION CHARACTERISTIC NOT FOUND')
        
##Discovery  Environmental Sensor  Characteristic: only temperature characteristic (no expansion board is considered)
##Temperature characteristic
found=False
status= ACI_GATT_DISC_CHAR_BY_UUID(Connection_Handle=conn_handle, Start_Handle=0x0001, End_Handle=0xffff, UUID_Type=0x02, UUID_128=TEMPERATURE_CHAR_UUID)
if (status != 0x00):
    ERROR('DISCOVERY TEMPERATURE CHARACTERISTIC FAILED')
    
eCode = ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT
while (eCode != ACI_GATT_PROC_COMPLETE_EVENT):
    e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30)
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT):
        temp_attr_handle =e.get_param('Attribute_Handle').val 
        found=True
    elif (eCode == ACI_GATT_ERROR_RESP_EVENT):
        if not found:
            ERROR('TEMPERATURE CHARACTERISTICS NOT FOUND')
        
            
# Enable Notification for Free Fall Characteristic
#The handle of descriptor is equal to freeFall_attr_handle+2 
status =  ACI_GATT_WRITE_CHAR_VALUE( Connection_Handle=conn_handle,
                   Attr_Handle=freeFall_attr_handle+2, Attribute_Val_Length=2, Attribute_Val=0x0001)
if (status != 0x00):
    ERROR ('ENABLE NOTIFICATION FOR FREE FALL CHARACTERISTIC FAILED')

e = WAIT_EVENT( HCI_VENDOR_EVENT, timeout=30, Ecode=ACI_GATT_PROC_COMPLETE_EVENT)


# Enable Notification for Acceleration Characteristic
#The handle of descriptor is equal to acc_attr_handle+2 
status=  ACI_GATT_WRITE_CHAR_VALUE(Connection_Handle=conn_handle, Attr_Handle=acc_attr_handle+2, Attribute_Val_Length=2, Attribute_Val=0x0001) 
if (status != 0x00):
    ERROR( 'ENABLE NOTIFICATION FOR ACCELERATION CHARACTERISTIC FAILED')

e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30, Ecode=ACI_GATT_PROC_COMPLETE_EVENT)


#Read the Temperature Characteristic for 3 times
for i in range (0,4):
    status=  ACI_GATT_READ_CHAR_VALUE(Connection_Handle=conn_handle,Attr_Handle=temp_attr_handle+1) 
    if (status != 0x00):
        ERROR( "READ TEMPERATURE CHARACTERISTIC FAILED (TIME %d)" %(i+1))

    while (True):
        e = WAIT_EVENT(HCI_VENDOR_EVENT)
        if (e.get_param('Ecode').val == ACI_ATT_READ_RESP_EVENT):
            PRINT ( "READ TEMPERATURE CHARAC (TIME %d) -> " %(i+1) + str(SENSORDEMO_GET_TEMPERATURE(e.get_param('Attribute_Value').val)))
        elif (e.get_param('Ecode').val == ACI_GATT_PROC_COMPLETE_EVENT):
            break           
    SLEEP(1)  #Read Temperature Characteristic each 1 sec.


#Receive Notifications
while(True):
    e = WAIT_EVENT(HCI_VENDOR_EVENT) 
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_GATT_NOTIFICATION_EVENT):
        if e.get_param('Attribute_Handle').val == freeFall_attr_handle+1:
            PRINT('---FREE FALL---')
        elif e.get_param('Attribute_Handle').val == acc_attr_handle+1: 
            x,y,z = SENSORDEMO_GET_ACCELERATION(e.get_param('Attribute_Value').get_list())  
            ## Print acceleration value
            PRINT('Acc -> X: '+ str(x) + ',Y: ' + str(y) + ',Z: ' +str(z))

## @endcond
