From 5a7e7237188793363d7553bd78209f74c905a37e Mon Sep 17 00:00:00 2001 From: Trond Einar Snekvik Date: Fri, 16 Jan 2015 15:40:09 +0100 Subject: [PATCH 1/2] Added temperature_broadcast example --- .../.ble_temperature_template.ino.swp | Bin 0 -> 16384 bytes .../ble_temperature_broadcast/Makefile | 48 +++ .../ble_temperature_broadcast.ino | 344 ++++++++++++++++++ .../health_thermometer_application.xml | 94 +++++ .../run_me_compile_xml_to_nRF8001_setup.bat | 6 + .../ble_temperature_broadcast/services.h | 125 +++++++ .../ble_temperature_broadcast/services_lock.h | 129 +++++++ .../ble_temperature_broadcast/timer1.cpp | 97 +++++ .../ble_temperature_broadcast/timer1.h | 28 ++ .../ublue_setup.gen.out.txt | 102 ++++++ 10 files changed, 973 insertions(+) create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/.ble_temperature_template.ino.swp create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/Makefile create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/ble_temperature_broadcast.ino create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/health_thermometer_application.xml create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/run_me_compile_xml_to_nRF8001_setup.bat create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/services.h create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/services_lock.h create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/timer1.cpp create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/timer1.h create mode 100644 libraries/BLE/examples/ble_temperature_broadcast/ublue_setup.gen.out.txt diff --git a/libraries/BLE/examples/ble_temperature_broadcast/.ble_temperature_template.ino.swp b/libraries/BLE/examples/ble_temperature_broadcast/.ble_temperature_template.ino.swp new file mode 100644 index 0000000000000000000000000000000000000000..c641cdb31942e32510ef7f58cb5ff989b16489cb GIT binary patch literal 16384 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 V2VO`BgJWC8K)ABZg&eOI`XA6eXiWeB literal 0 HcmV?d00001 diff --git a/libraries/BLE/examples/ble_temperature_broadcast/Makefile b/libraries/BLE/examples/ble_temperature_broadcast/Makefile new file mode 100644 index 0000000..f86504e --- /dev/null +++ b/libraries/BLE/examples/ble_temperature_broadcast/Makefile @@ -0,0 +1,48 @@ +### Get the current directory +CURRENT_DIR = $(shell basename $(CURDIR)) + +### PROJECT_DIR +PROJECT_DIR = $(CURRENT_DIR) + +### ARDMK_DIR +### Path to the Arduino-Makefile directory. +### Change this depending on where you have saved the main makefile +ARDMK_DIR =/cygdrive/c/Users/emga/Arduino-Makefile + +### ARDUINO_DIR +### Path to the Arduino application and resources directory. +### Change this variable as it depends where the make file is located +ARDUINO_DIR =../../../../../Arduino + +### USER_LIB_PATH +### Path to where the your project's libraries are stored. +#USER_LIB_PATH := $(PROJECT_DIR)/lib + +### BOARD_TAG +### It must be set to the board you are currently using. (i.e uno, mega2560, etc.) +BOARD_TAG = uno + +### MONITOR_BAUDRATE +### It must be set to Serial baudrate value you are using. +MONITOR_BAUDRATE = 115200 + +### ARDUINO_LIBS +### Libraries used on the BLE project +ARDUINO_LIBS = SPI BLE EEPROM + +### MONITOR_PORT +### The port to which the Arduino is connected +MONITOR_PORT = com7 + +### CPPFLAGS +### Flags you might want to set for debugging purpose. Comment to stop. +#CPPFLAGS = -pedantic -Wall -Wextra DEFINED ON THE MAKE FILE + +### OBJDIR +### This is were you put the binaries you just compile using 'make' +#OBJDIR = $(PROJECT_DIR)/bin/$(BOARD_TAG)/$(CURRENT_DIR) DEFINED ON THE MAKEFILE + +### path to Arduino.mk, inside the ARDMK_DIR +include $(ARDMK_DIR)/Arduino.mk + + diff --git a/libraries/BLE/examples/ble_temperature_broadcast/ble_temperature_broadcast.ino b/libraries/BLE/examples/ble_temperature_broadcast/ble_temperature_broadcast.ino new file mode 100644 index 0000000..7b4716c --- /dev/null +++ b/libraries/BLE/examples/ble_temperature_broadcast/ble_temperature_broadcast.ino @@ -0,0 +1,344 @@ +/* Copyright (c) 2014, Nordic Semiconductor ASA + * + * 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 THE + * AUTHORS OR COPYRIGHT HOLDERS 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. + */ + +/** + * + * IMPORTANT: This example still is not compatible with CHIPKIT + * + * Click on the "Serial Monitor" button on the Arduino IDE to get reset the Arduino and start the application. + * The setup() function is called first and is called only one for each reset of the Arduino. + * The loop() function as the name implies is called in a loop. + * The setup() and loop() function are called in this way. + * main() + * { + * setup(); + * while(1) + * { + * loop(); + * } + * } + * + * Use the PERSONAL HEALTH DEVICES TRANSCODING WHITE PAPER in bluetooth.org to understand the + * format of the temperature measurement characteristic. + * The format used is IEEE 11073-20601 FLOAT + */ +#include +#include +#include + +#include "services.h" +#include + +#include +#include "timer1.h" + +#ifdef SERVICES_PIPE_TYPE_MAPPING_CONTENT + static services_pipe_type_mapping_t + services_pipe_type_mapping[NUMBER_OF_PIPES] = SERVICES_PIPE_TYPE_MAPPING_CONTENT; +#else + #define NUMBER_OF_PIPES 0 + static services_pipe_type_mapping_t * services_pipe_type_mapping = NULL; +#endif + +#define TEMPERATURE_NUM_SAMPLES 5 + +static hal_aci_data_t setup_msgs[NB_SETUP_MESSAGES] PROGMEM = SETUP_MESSAGES_CONTENT; +// aci_struct that will contain +// total initial credits +// current credit +// current state of the aci (setup/standby/active/sleep) +// open remote pipe pending +// close remote pipe pending +// Current pipe available bitmap +// Current pipe closed bitmap +// Current connection interval, slave latency and link supervision timeout +// Current State of the the GATT client (Service Discovery) +static struct aci_state_t aci_state; +static hal_aci_evt_t aci_data; + +/** +Counter in seconds. +When this counter counts down to zero -> wakeup the nRF8001 +*/ +static int16_t sleep_to_wakeup_timeout; + +/* +Variables used for the temperature measurement and transmission +*/ +//static h_thermo_temp_measure_t h_temperature; +//static h_temp_type_t current_type; + +static int32_t temperature = 0; // Needs to be an int32 to measure negative values +static float temperature_f; +static uint8_t temperature_count = TEMPERATURE_NUM_SAMPLES; +static int32_t temperature_total = 0; +static bool first_temp_measure_pending = true; + +/* +Variables used for the timer on the AVR +*/ +volatile uint8_t timer1_f = 1; + +/* Define how assert should function in the BLE library */ +void __ble_assert(const char *file, uint16_t line) +{ + Serial.print("ERROR "); + Serial.print(file); + Serial.print(": "); + Serial.print(line); + Serial.print("\n"); + while(1); +} + +void aci_loop() +{ + static bool setup_required = false; + + // We enter the if statement only when there is a ACI event available to be processed + if (lib_aci_event_get(&aci_state, &aci_data)) + { + aci_evt_t * aci_evt; + aci_evt = &aci_data.evt; + + switch(aci_evt->evt_opcode) + { + case ACI_EVT_DEVICE_STARTED: + { + aci_state.data_credit_available = aci_evt->params.device_started.credit_available; + switch(aci_evt->params.device_started.device_mode) + { + case ACI_DEVICE_SETUP: + /** + When the device is in the setup mode + */ + aci_state.device_state = ACI_DEVICE_SETUP; + Serial.println(F("Evt Device Started: Setup")); + setup_required = true; + break; + + case ACI_DEVICE_STANDBY: + aci_state.device_state = ACI_DEVICE_STANDBY; + sleep_to_wakeup_timeout = 30; + Serial.println(F("Evt Device Started: Standby")); + if (aci_evt->params.device_started.hw_error) + { + delay(20); //Magic number used to make sure the HW error event is handled correctly. + } + else + { + // prepare first temperature measurement + Timer1start(); + } + break; + } + } + break; //ACI Device Started Event + + case ACI_EVT_CMD_RSP: + //If an ACI command response event comes with an error -> stop + if (ACI_STATUS_SUCCESS != aci_evt->params.cmd_rsp.cmd_status ) + { + //ACI ReadDynamicData and ACI WriteDynamicData will have status codes of + //TRANSACTION_CONTINUE and TRANSACTION_COMPLETE + //all other ACI commands will have status code of ACI_STATUS_SCUCCESS for a successful command + + Serial.print(F("ACI Status of ACI Evt Cmd Rsp 0x")); + Serial.println(aci_evt->params.cmd_rsp.cmd_status, HEX); + Serial.print(F("ACI Command 0x")); + Serial.println(aci_evt->params.cmd_rsp.cmd_opcode, HEX); + Serial.println(F("Evt Cmd respone: Error. Arduino is in an while(1); loop")); + while (1); + } + else + { + switch (aci_evt->params.cmd_rsp.cmd_opcode) + { + case ACI_CMD_GET_TEMPERATURE: + if (0 != temperature_count) + { + temperature_total = temperature_total + + (aci_evt->params.cmd_rsp.params.get_temperature.temperature_value); + Serial.print(F("Sampling Temperature ")); + Serial.print(temperature_count); + Serial.print(F(" ")); + Serial.println(aci_evt->params.cmd_rsp.params.get_temperature.temperature_value); + temperature_count--; + } + if (0 == temperature_count) + { + Serial.println(temperature_total); + temperature_count = TEMPERATURE_NUM_SAMPLES; + /** + Multiply by 100 for exp = -2 : divide by 4 -> See ACI GetTemperature in datasheet + */ + temperature = temperature_total * (float)(25/(float)TEMPERATURE_NUM_SAMPLES); + temperature_total = 0; + temperature_f = (float)temperature/100.00; + Serial.print(F("Temperature :")); + Serial.print(temperature_f, 2); + Serial.println(" C"); + if (true)//lib_aci_is_pipe_available(&aci_state, PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_SET)) + { + Serial.println(F("Setting the temperature")); + temperature &= 0x00FFFFFF; //Mask the exponent part + temperature |= 0xFE000000; //Exponent is -2 since we multipled by 100 + lib_aci_set_local_data(&aci_state, PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_SET, (uint8_t*) &temperature, 4); + + //Start broadcasting our temperature value, now that we have one + if (first_temp_measure_pending) + { + lib_aci_open_adv_pipe(PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_BROADCAST); + lib_aci_broadcast(0/* indefinitely */, 0x0100 /* advertising interval 100ms*/); + Serial.println(F("Broadcasting started")); + + first_temp_measure_pending = false; + } + } + } + break; + } + } + break; + + case ACI_EVT_HW_ERROR: + Serial.println(F("HW error: ")); + Serial.println(aci_evt->params.hw_error.line_num, DEC); + + for(uint8_t counter = 0; counter <= (aci_evt->len - 3); counter++) + { + Serial.write(aci_evt->params.hw_error.file_name[counter]); //uint8_t file_name[20]; + } + break; + + default: + Serial.print(F("Evt Opcode 0x")); + Serial.print(aci_evt->evt_opcode, HEX); + Serial.println(F(" unhandled")); + break; + } + } + else + { + /** + No event in the ACI Event queue and if there are no commands in the command queue + Arduino can go to sleep + */ + } + + /* setup_required is set to true when the device starts up and enters setup mode. + * It indicates that do_aci_setup() should be called. The flag should be cleared if + * do_aci_setup() returns ACI_STATUS_TRANSACTION_COMPLETE. + */ + if(setup_required) + { + if (SETUP_SUCCESS == do_aci_setup(&aci_state)) + { + setup_required = false; + } + } +} + + +void setup(void) +{ + Serial.begin(115200); + //Wait until the serial port is available (useful only for the Leonardo) + //As the Leonardo board is not reseted every time you open the Serial Monitor + #if defined (__AVR_ATmega32U4__) + while(!Serial) + {} + delay(5000); //5 seconds delay for enabling to see the start up comments on the serial board + #elif defined(__PIC32MX__) + delay(1000); + #endif + + Serial.println(F("Arduino setup")); + + /** + Point ACI data structures to the the setup data that the nRFgo studio generated for the nRF8001 + */ + if (NULL != services_pipe_type_mapping) + { + aci_state.aci_setup_info.services_pipe_type_mapping = &services_pipe_type_mapping[0]; + } + else + { + aci_state.aci_setup_info.services_pipe_type_mapping = NULL; + } + aci_state.aci_setup_info.number_of_pipes = NUMBER_OF_PIPES; + aci_state.aci_setup_info.setup_msgs = setup_msgs; + aci_state.aci_setup_info.num_setup_msgs = NB_SETUP_MESSAGES; + + /* + Tell the ACI library, the MCU to nRF8001 pin connections. + The Active pin is optional and can be marked UNUSED + */ + aci_state.aci_pins.board_name = BOARD_DEFAULT; //See board.h for details + aci_state.aci_pins.reqn_pin = 9; + aci_state.aci_pins.rdyn_pin = 8; + aci_state.aci_pins.mosi_pin = MOSI; + aci_state.aci_pins.miso_pin = MISO; + aci_state.aci_pins.sck_pin = SCK; + + aci_state.aci_pins.spi_clock_divider = SPI_CLOCK_DIV8;//SPI_CLOCK_DIV8 = 2MHz SPI speed + //SPI_CLOCK_DIV16 = 1MHz SPI speed + + aci_state.aci_pins.reset_pin = 4; //4 for Nordic board, UNUSED for REDBEARLAB_SHIELD_V1_1 + aci_state.aci_pins.active_pin = UNUSED; + aci_state.aci_pins.optional_chip_sel_pin = UNUSED; + + aci_state.aci_pins.interface_is_interrupt = false; //Interrupts still not available in Chipkit + aci_state.aci_pins.interrupt_number = 1; + + /** We reset the nRF8001 here by toggling the RESET line connected to the nRF8001 + * and initialize the data structures required to setup the nRF8001 + */ + //The second parameter is for turning debug printing on for the ACI Commands and Events so they be printed on the Serial + lib_aci_init(&aci_state, false); + + /** + * Initalize the data structures required for the GATT Health Thermometer Service + */ +} + +void loop() +{ + aci_loop(); + + /** + Temperature sampling application that samples the temperature every 4 seconds + OR + Wakes up the sleeping nRF8001 every 4 seconds + */ + if (1 == timer1_f) + { + uint8_t i = 0; + timer1_f = 0; + + for(i=0; i + + + 0 + nRF8001_Dx + + Health Thermometer + 1809 + + Temperature Measurement + 2a1c + + 0 + 13 + 2 + false + false + + false + false + false + false + true + + true + true + + 0 + + + + + Health_thermometer + 0 + false + 5 + 0000 + 0 + 0 + 0 + 600 + 5 + 7 + 16 + 1a + 0 + 1000 + 0 + 0 + 10 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 65535 + 65535 + 0 + 65535 + false + false + 5 + + + 0 + 00 + + + 0 + 00 + + + + + 1 + 1 + 3 + 0 + 0 + 0 + 0 + false + + + 220 + 10 + 1000 + 10 + 11.1111 + 1280 + + diff --git a/libraries/BLE/examples/ble_temperature_broadcast/run_me_compile_xml_to_nRF8001_setup.bat b/libraries/BLE/examples/ble_temperature_broadcast/run_me_compile_xml_to_nRF8001_setup.bat new file mode 100644 index 0000000..3af7e7e --- /dev/null +++ b/libraries/BLE/examples/ble_temperature_broadcast/run_me_compile_xml_to_nRF8001_setup.bat @@ -0,0 +1,6 @@ + +del services.h +del services_lock.h +del ublue_setup.gen.out.txt + +"%NRFGOSTUDIOPATH%\nrfgostudio.exe" -nrf8001 -g health_thermometer_application.xml -codeGenVersion 1 -o . \ No newline at end of file diff --git a/libraries/BLE/examples/ble_temperature_broadcast/services.h b/libraries/BLE/examples/ble_temperature_broadcast/services.h new file mode 100644 index 0000000..12a5700 --- /dev/null +++ b/libraries/BLE/examples/ble_temperature_broadcast/services.h @@ -0,0 +1,125 @@ +/** +* This file is autogenerated by nRFgo Studio 1.17.1.3252 +*/ + +#ifndef SETUP_MESSAGES_H__ +#define SETUP_MESSAGES_H__ + +#include "hal_platform.h" +#include "aci.h" + + +#define SETUP_ID 0 +#define SETUP_FORMAT 3 /** nRF8001 D */ +#define ACI_DYNAMIC_DATA_SIZE 122 + +/* Service: Health Thermometer - Characteristic: Temperature Measurement - Pipe: BROADCAST */ +#define PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_BROADCAST 1 +#define PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_BROADCAST_MAX_SIZE 13 + +/* Service: Health Thermometer - Characteristic: Temperature Measurement - Pipe: SET */ +#define PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_SET 2 +#define PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_SET_MAX_SIZE 13 + + +#define NUMBER_OF_PIPES 2 + +#define SERVICES_PIPE_TYPE_MAPPING_CONTENT {\ + {ACI_STORE_LOCAL, ACI_TX_BROADCAST}, \ + {ACI_STORE_LOCAL, ACI_SET}, \ +} + +#define GAP_PPCP_MAX_CONN_INT 0xffff /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ +#define GAP_PPCP_MIN_CONN_INT 0xffff /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ +#define GAP_PPCP_SLAVE_LATENCY 0 +#define GAP_PPCP_CONN_TIMEOUT 0xffff /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */ + +#define NB_SETUP_MESSAGES 15 +#define SETUP_MESSAGES_CONTENT {\ + {0x00,\ + {\ + 0x07,0x06,0x00,0x00,0x03,0x02,0x42,0x07,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x01,0x01,0x00,0x00,0x06,0x00,0x05,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x10,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x03,0x90,0x00,0xff,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x10,0x38,0xff,0xff,0x02,0x58,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x10,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x05,0x06,0x10,0x54,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\ + 0x02,0x28,0x03,0x01,0x02,0x03,0x00,0x00,0x2a,0x04,0x04,0x14,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x1c,0x12,0x00,0x03,0x2a,0x00,0x01,0x48,0x65,0x61,0x6c,0x74,0x68,0x5f,0x74,0x68,0x65,\ + 0x72,0x6d,0x6f,0x6d,0x65,0x74,0x65,0x72,0x00,0x00,0x04,0x04,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x38,0x05,0x05,0x00,0x04,0x28,0x03,0x01,0x02,0x05,0x00,0x01,0x2a,0x06,0x04,0x03,0x02,\ + 0x00,0x05,0x2a,0x01,0x01,0x00,0x00,0x04,0x04,0x05,0x05,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x54,0x06,0x28,0x03,0x01,0x02,0x07,0x00,0x04,0x2a,0x06,0x04,0x09,0x08,0x00,0x07,0x2a,\ + 0x04,0x01,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x04,0x04,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x70,0x02,0x02,0x00,0x08,0x28,0x00,0x01,0x01,0x18,0x04,0x04,0x02,0x02,0x00,0x09,0x28,\ + 0x00,0x01,0x09,0x18,0x04,0x04,0x05,0x05,0x00,0x0a,0x28,0x03,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x8c,0x01,0x02,0x0b,0x00,0x1c,0x2a,0x04,0x04,0x0d,0x00,0x00,0x0b,0x2a,0x1c,0x01,0x00,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x04,0x06,0x20,0xa8,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x0d,0x06,0x40,0x00,0x2a,0x1c,0x01,0x00,0x81,0x04,0x00,0x0b,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x06,0x06,0x60,0x00,0x00,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x06,0x06,0xf0,0x00,0x03,0xdd,0xd5,\ + },\ + },\ +} + +#endif diff --git a/libraries/BLE/examples/ble_temperature_broadcast/services_lock.h b/libraries/BLE/examples/ble_temperature_broadcast/services_lock.h new file mode 100644 index 0000000..6a74879 --- /dev/null +++ b/libraries/BLE/examples/ble_temperature_broadcast/services_lock.h @@ -0,0 +1,129 @@ +/** +* This file is autogenerated by nRFgo Studio 1.17.1.3252 +*/ + +#ifndef SETUP_MESSAGES_H__ +#define SETUP_MESSAGES_H__ + +#include "hal_platform.h" +#include "aci.h" + +// You have now chosen to upload the configuration to OTP on the device. +// This will result in a device that you can not modify afterwards. If this is your intention, +// remove this comment and the #error below +#error Generating configuration for OTP. Please verify usage by removing this error message from include file. + +#define SETUP_ID 0 +#define SETUP_FORMAT 3 /** nRF8001 D */ +#define ACI_DYNAMIC_DATA_SIZE 122 + +/* Service: Health Thermometer - Characteristic: Temperature Measurement - Pipe: BROADCAST */ +#define PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_BROADCAST 1 +#define PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_BROADCAST_MAX_SIZE 13 + +/* Service: Health Thermometer - Characteristic: Temperature Measurement - Pipe: SET */ +#define PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_SET 2 +#define PIPE_HEALTH_THERMOMETER_TEMPERATURE_MEASUREMENT_SET_MAX_SIZE 13 + + +#define NUMBER_OF_PIPES 2 + +#define SERVICES_PIPE_TYPE_MAPPING_CONTENT {\ + {ACI_STORE_LOCAL, ACI_TX_BROADCAST}, \ + {ACI_STORE_LOCAL, ACI_SET}, \ +} + +#define GAP_PPCP_MAX_CONN_INT 0xffff /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ +#define GAP_PPCP_MIN_CONN_INT 0xffff /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ +#define GAP_PPCP_SLAVE_LATENCY 0 +#define GAP_PPCP_CONN_TIMEOUT 0xffff /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */ + +#define NB_SETUP_MESSAGES 15 +#define SETUP_MESSAGES_CONTENT {\ + {0x00,\ + {\ + 0x07,0x06,0x00,0x00,0x03,0x02,0x42,0x07,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x01,0x01,0x00,0x00,0x06,0x00,0x05,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x10,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x03,0x90,0x00,0xff,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x10,0x38,0xff,0xff,0x02,0x58,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x10,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x05,0x06,0x10,0x54,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\ + 0x02,0x28,0x03,0x01,0x02,0x03,0x00,0x00,0x2a,0x04,0x04,0x14,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x1c,0x12,0x00,0x03,0x2a,0x00,0x01,0x48,0x65,0x61,0x6c,0x74,0x68,0x5f,0x74,0x68,0x65,\ + 0x72,0x6d,0x6f,0x6d,0x65,0x74,0x65,0x72,0x00,0x00,0x04,0x04,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x38,0x05,0x05,0x00,0x04,0x28,0x03,0x01,0x02,0x05,0x00,0x01,0x2a,0x06,0x04,0x03,0x02,\ + 0x00,0x05,0x2a,0x01,0x01,0x00,0x00,0x04,0x04,0x05,0x05,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x54,0x06,0x28,0x03,0x01,0x02,0x07,0x00,0x04,0x2a,0x06,0x04,0x09,0x08,0x00,0x07,0x2a,\ + 0x04,0x01,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x04,0x04,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x70,0x02,0x02,0x00,0x08,0x28,0x00,0x01,0x01,0x18,0x04,0x04,0x02,0x02,0x00,0x09,0x28,\ + 0x00,0x01,0x09,0x18,0x04,0x04,0x05,0x05,0x00,0x0a,0x28,0x03,\ + },\ + },\ + {0x00,\ + {\ + 0x1f,0x06,0x20,0x8c,0x01,0x02,0x0b,0x00,0x1c,0x2a,0x04,0x04,0x0d,0x00,0x00,0x0b,0x2a,0x1c,0x01,0x00,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x04,0x06,0x20,0xa8,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x0d,0x06,0x40,0x00,0x2a,0x1c,0x01,0x00,0x81,0x04,0x00,0x0b,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x06,0x06,0x60,0x00,0x00,0x00,0x00,\ + },\ + },\ + {0x00,\ + {\ + 0x06,0x06,0xf0,0x00,0x83,0x4c,0x5d,\ + },\ + },\ +} + +#endif diff --git a/libraries/BLE/examples/ble_temperature_broadcast/timer1.cpp b/libraries/BLE/examples/ble_temperature_broadcast/timer1.cpp new file mode 100644 index 0000000..c6d2513 --- /dev/null +++ b/libraries/BLE/examples/ble_temperature_broadcast/timer1.cpp @@ -0,0 +1,97 @@ +/* Copyright (c) 2014, Nordic Semiconductor ASA + * + * 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 THE + * AUTHORS OR COPYRIGHT HOLDERS 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. + */ + +#include + +extern uint8_t timer1_f; + +/*** FUNC + +Name: Timer1start + +Function: Start timer 1 to interrupt periodically. Call this from + the Arduino setup() function. + +Description: The pre-scaler and the timer count divide the timer-counter + clock frequency to give a timer overflow interrupt rate: + + Interrupt rate = 16MHz / (prescaler * (255 - TCNT2)) + + TCCR2B[b2:0] Prescaler Freq [KHz], Period [usec] after prescale + 0x0 (TC stopped) 0 0 + 0x1 1 16000. 0.0625 + 0x2 8 2000. 0.500 + 0x3 32 500. 2.000 + 0x4 64 250. 4.000 + 0x5 128 125. 8.000 + 0x6 256 62.5 16.000 + 0x7 1024 15.625 64.000 + + +Parameters: void + +Returns: void + +FUNC ***/ + +void Timer1start(void) +{ + + // Setup Timer1 overflow to fire every 4000ms + // period [sec] = (1 / f_clock [sec]) * prescale * (count) + // (1/16000000) * 1024 * (count) = 4000 ms + + + TCCR1B = 0x00; // Disable Timer1 while we set it up + + TCNT1H = 11; // Approx 4000ms when prescaler is set to 1024 + TCNT1L = 0; + TIFR1 = 0x00; // Timer1 INT Flag Reg: Clear Timer Overflow Flag + TIMSK1 = 0x01; // Timer1 INT Reg: Timer1 Overflow Interrupt Enable + TCCR1A = 0x00; // Timer1 Control Reg A: Wave Gen Mode normal + TCCR1B = 0x05; // Timer1 Control Reg B: Timer Prescaler set to 1024 +} + +void Timer1stop(void) +{ + TCCR1B = 0x00; + TIMSK1 = 0x00; +} + +/*** FUNC + +Name: Timer1 ISR + +Function: Handles the Timer1-overflow interrupt + +FUNC ***/ + +ISR(TIMER1_OVF_vect) +{ + if (0 == timer1_f) + { + timer1_f = 1; + } + + TCNT1H = 11; // Approx 4000 ms - Reload + TCNT1L = 0; + TIFR1 = 0x00; // timer1 int flag reg: clear timer overflow flag +}; diff --git a/libraries/BLE/examples/ble_temperature_broadcast/timer1.h b/libraries/BLE/examples/ble_temperature_broadcast/timer1.h new file mode 100644 index 0000000..31f4f9e --- /dev/null +++ b/libraries/BLE/examples/ble_temperature_broadcast/timer1.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2014, Nordic Semiconductor ASA + * + * 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 THE + * AUTHORS OR COPYRIGHT HOLDERS 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. + */ + +#ifndef _TIMER_1_H +#define _TIMER_1_H + +void Timer1start(void); +void Timer1stop(void); + +#endif //_TIMER_1_H diff --git a/libraries/BLE/examples/ble_temperature_broadcast/ublue_setup.gen.out.txt b/libraries/BLE/examples/ble_temperature_broadcast/ublue_setup.gen.out.txt new file mode 100644 index 0000000..4df48ac --- /dev/null +++ b/libraries/BLE/examples/ble_temperature_broadcast/ublue_setup.gen.out.txt @@ -0,0 +1,102 @@ +------------------------------------------------------------------------------ + uBlue Setup generation report + Generated with uBlue setup DLL version: 1.0.0.16903 + Generated: Fri Jan 16 13:31:02 2015 (UTC) + This file is automatically generated, do not modify +------------------------------------------------------------------------------ + +[Counts] + +Setup data size = 335 bytes +Local database size = 169 bytes +Local attribute count = 1 +Remote attribute count = 0 +Total pipe count = 2 +Dynamic data size = 122 bytes (worst case) + +[Setup Area Layout] + +Setup area, total = 1595 bytes +Setup area, used = 182 bytes ( 11% of total ) +Local services = 169 bytes ( 92% of used ) +Remote services = 0 bytes ( 0% of used ) +Pipes = 10 bytes ( 5% of used ) +VS UUID area = 0 bytes ( 0% of used ) +Extended Attr area = 3 bytes ( 1% of used ) + +[Device Settings] + +Setup ID = 0x00000000 +Setup Format = 0x03 +Security = OPEN (0) +Bond Timeout = 600 +Security Request Delay = 5 +Change Timing Delay = 5 +Whitelist = Enabled + +[Advertisement Data] + +Bond Advertise = 0x00000010 [LOCAL_NAME_COMPLETE] +Bond Scan Resp = 0x00000000 [] +General Advertise = 0x00000000 [] +General Scan Resp = 0x00000000 [] +Broadcast Advertise = 0x00001000 [SERVICE_DATA] +Broadcast Scan Resp = 0x00000010 [LOCAL_NAME_COMPLETE] + +Custom Bond Advertise = 0x00 [] +Custom Bond Scan Resp = 0x00 [] +Custom General Advertise = 0x00 [] +Custom General Scan Resp = 0x00 [] +Custom Broadcast Advertise = 0x00 [] +Custom Broadcast Scan Resp = 0x00 [] + +No custom AD types + +[Vendor Specific UUIDs] + + +[Local Database] + +Handle Pipes Structure +------ ----- --------- +0x0001 +----- Service (Primary): "GAP" (01:0x1800) +0x0002 |----- |Characteristic: "Device Name" (01:0x2A00) [rd] [rd:allow|wr:none] +0x0003 |Value: {0x48 0x65 0x61 0x6C 0x74 0x68 0x5F 0x74 0x68 0x65 0x72 0x6D 0x6F 0x6D 0x65 0x74 0x65 0x72} [rd:allow|wr:none] +0x0004 |----- |Characteristic: "Appearance" (01:0x2A01) [rd] [rd:allow|wr:none] +0x0005 |Value: {0x00 0x00} [rd:allow|wr:none] +0x0006 |----- |Characteristic: "PPCP" (01:0x2A04) [rd] [rd:allow|wr:none] +0x0007 |Value: {0xFF 0xFF 0xFF 0xFF 0x00 0x00 0xFF 0xFF} [rd:allow|wr:none] +0x0008 +----- Service (Primary): "GATT" (01:0x1801) +0x0009 +----- Service (Primary): "?" (01:0x1809) +0x000A |----- |Characteristic: "?" (01:0x2A1C) [rd] [rd:allow|wr:none] +0x000B x> |Value: {} [rd:allow|wr:none] + +[Remote Database] + +Handle Pipes Structure +------ ----- --------- + +[Pipe Map] + +Pipe Store Type Service Char. CPF Desc. +---- ------ ------ ---------- --------- ----------- --------- +01 Local TX_BC 01:0x1809 01:0x2A1C -- -- +02 Local SET 01:0x1809 01:0x2A1C -- -- + +[Setup Data] + +07-06-00-00-03-02-42-07 +1F-06-10-00-00-00-00-00-00-00-01-00-02-01-01-00-00-06-00-05-00-00-00-00-00-00-00-00-00-00-00-00 +1F-06-10-1C-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-10-00-00-00-00-03-90-00-FF +1F-06-10-38-FF-FF-02-58-05-05-00-00-00-00-00-00-00-00-00-00-10-00-00-00-00-10-00-00-00-00-00-00 +05-06-10-54-00-00 +1F-06-20-00-04-04-02-02-00-01-28-00-01-00-18-04-04-05-05-00-02-28-03-01-02-03-00-00-2A-04-04-14 +1F-06-20-1C-12-00-03-2A-00-01-48-65-61-6C-74-68-5F-74-68-65-72-6D-6F-6D-65-74-65-72-00-00-04-04 +1F-06-20-38-05-05-00-04-28-03-01-02-05-00-01-2A-06-04-03-02-00-05-2A-01-01-00-00-04-04-05-05-00 +1F-06-20-54-06-28-03-01-02-07-00-04-2A-06-04-09-08-00-07-2A-04-01-FF-FF-FF-FF-00-00-FF-FF-04-04 +1F-06-20-70-02-02-00-08-28-00-01-01-18-04-04-02-02-00-09-28-00-01-09-18-04-04-05-05-00-0A-28-03 +1F-06-20-8C-01-02-0B-00-1C-2A-04-04-0D-00-00-0B-2A-1C-01-00-00-00-00-00-00-00-00-00-00-00-00-00 +04-06-20-A8-00 +0D-06-40-00-2A-1C-01-00-81-04-00-0B-00-00 +06-06-60-00-00-00-00 +06-06-F0-00-83-4C-5D From 6a709e53890aec6b038c818c27afea8de1e58810 Mon Sep 17 00:00:00 2001 From: Trond Einar Snekvik Date: Fri, 16 Jan 2015 15:52:01 +0100 Subject: [PATCH 2/2] temp_bcast cleanup swap file --- .../.ble_temperature_template.ino.swp | Bin 16384 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 libraries/BLE/examples/ble_temperature_broadcast/.ble_temperature_template.ino.swp diff --git a/libraries/BLE/examples/ble_temperature_broadcast/.ble_temperature_template.ino.swp b/libraries/BLE/examples/ble_temperature_broadcast/.ble_temperature_template.ino.swp deleted file mode 100644 index c641cdb31942e32510ef7f58cb5ff989b16489cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 V2VO`BgJWC8K)ABZg&eOI`XA6eXiWeB