├── .gitignore ├── Makefile ├── README.md ├── ancs.h ├── ancs_base.h ├── ancs_data_source.cpp ├── ancs_data_source.h ├── ancs_notification.cpp ├── ancs_notification.h ├── ancs_notification_list.cpp ├── ancs_notification_list.h ├── ancs_notification_source.cpp ├── ancs_notification_source.h ├── examples └── ancs_lcd │ └── ancs_lcd.ino ├── linked_list.h ├── my_services.h ├── notif.cpp ├── notif.h ├── nrf8001_ancs.xml ├── pack_lib.h ├── services.h ├── services_lock.h ├── ublue_setup.gen.out.txt ├── utilities.cpp └── utilities.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.disabled 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building Arduino sketches with Arduino 1.0. 2 | # 3 | # Makefile-arduino v0.8 by Bernard Pratz 4 | # I release my changes under WTFPL2.0 licence, contact other authors for their favorite licences. 5 | # Updates on : https://github.com/guyzmo/Arduino-Tools/blob/master/Makefile 6 | # 7 | # Makefile-arduino v0.7 by Akkana Peck 8 | # Adapted from a long-ago Arduino 0011 Makefile by mellis, eighthave, oli.keller 9 | # 10 | # This Makefile allows you to build sketches from the command line 11 | # without the Arduino environment (or Java). 12 | # 13 | # Detailed instructions for using this Makefile: 14 | # 15 | # 1. Copy this file into the folder with your sketch. 16 | # There should be a file with the extension .pde (e.g. blink.pde). 17 | # cd into this directory. Be sure your directory has the same name 18 | # as the .pde file. 19 | # 20 | # 2. Modify the line containg "ARDUINO_DIR" to point to the directory that 21 | # contains the Arduino installation (for example, under Mac OS X, this 22 | # might be /Applications/arduino-1.0). If it's in your home directory, 23 | # you can include $(HOME) as part of the path. 24 | # 25 | # 3. Set MODEL to your Arduino model. 26 | # Tested so far on uno, atmega328, diecimila and mega. 27 | # but there are lots of other options: 28 | # Use the "make list" command to know all the available models 29 | # 30 | # 4. Run "make" to compile/verify your program. 31 | # 32 | # 5. Run "make upload" (and reset your Arduino if it requires it) 33 | # to download your program to the Arduino. 34 | # 35 | # Nota Bene: 36 | # if reset does not work, add RESET_MODE='python' or RESET_MODE='perl' to your env 37 | # * Perl version needs libdevice-serialport-perl : 38 | # * Python version needs python-serial : 39 | # if you want to compile against ATTiny, see ATTINY_DIR below. 40 | 41 | ############################################################################ 42 | # Project's settings 43 | ############################################################################ 44 | 45 | DEFINES= 46 | 47 | PORT=/dev/arduino 48 | 49 | AVR_TOOLS_PATH=/usr/local/bin 50 | AVRDUDE_CONF=-V -F -C /usr/local/etc/avrdude.conf 51 | 52 | AVRDUDE_FLASH_PGER=jtag3isp 53 | AVRDUDE_FLASH_RATE=-b 250000 -B0.1 54 | AVRDUDE_FLASH_PORT=usb 55 | 56 | VERSION = 0.1 57 | 58 | # Standard Arduino libraries it will import, e.g. LiquidCrystal: 59 | ARDLIBS = Wire Wire/utility SPI EEPROM . 60 | 61 | # User-specified (in ~/sketchbook/libraries/) libraries : 62 | USERLIBS = 63 | 64 | # Libs in local directory for current project 65 | # uncomment value if you have libs in directories inside current project's directory 66 | LOCALLIBS = . ble_lib ancs_lib ancs_lib/data_lib 67 | 68 | # Arduino model: 69 | # You can set this to be a string, such as uno, atmega328... 70 | # do 'make list' for a full list of supported models 71 | MODEL ?= leonardo 72 | 73 | DEFINES ?= 74 | 75 | ############################################################################ 76 | # Platform's settings 77 | ############################################################################ 78 | 79 | # Determine operating system environment 80 | ifneq "$(wildcard C:/Windows/)" "" 81 | UNAME=Windows 82 | else 83 | UNAME=$(shell uname) 84 | endif 85 | 86 | # Name of the program and source .pde file: 87 | TARGET = $(shell basename $(PWD)) 88 | 89 | # Where do you keep the official Arduino software package? 90 | ifeq "$(UNAME)" "Darwin" 91 | ARDUINO_DIR = /Applications/Arduino.app/Contents/Resources/Java 92 | else 93 | ifeq "$(UNAME)" "Linux" 94 | ARDUINO_DIR = /usr/share/arduino 95 | endif 96 | endif 97 | HOME_LIB = $(HOME)/Documents/Arduino/libraries 98 | # path to ATTiny files look at http://code.google.com/p/arduino-tiny/ 99 | ATTINY_DIR=$(ARDUINO_DIR)/hardware/tiny 100 | 101 | ############################################################################ 102 | # Below here nothing should need to be changed. Cross your fingers! 103 | ############################################################################ 104 | 105 | # Where are tools like avr-gcc located on your system? 106 | ifeq "$(UNAME)" "Darwin" 107 | # AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin 108 | AVR_TOOLS_PATH ?= /usr/local/bin 109 | else 110 | AVR_TOOLS_PATH ?= /usr/bin 111 | endif 112 | 113 | ifeq "$(UNAME)" "Windows" 114 | PORT ?= COM1 #XXX needs to be checked ! 115 | else 116 | ifeq "$(UNAME)" "Darwin" 117 | ifeq ($(MODEL),$(filter $(MODEL),uno leonardo)) 118 | PORT ?= /dev/tty.usbmodem* 119 | else 120 | PORT ?= /dev/tty.usbserial* 121 | endif 122 | else 123 | ifeq "$(UNAME)" "Linux" 124 | ifeq ($(MODEL),$(filter $(MODEL),uno leonardo)) 125 | PORT ?= /dev/ttyACM* 126 | else 127 | PORT ?= /dev/ttyUSB* 128 | endif 129 | endif 130 | endif 131 | endif 132 | 133 | # How to reset the device before downloading a new program. 134 | # These don't always work; if the default one doesn't work, 135 | # try uncommenting one of the others instead. 136 | ifeq "$(MODEL)" "leonardo" 137 | RESET_DEVICE = python -c "import time, serial, sys ; ser = serial.Serial(port=sys.argv[1], baudrate=1200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS) ; ser.isOpen() ; time.sleep(1) ; ser.close(); time.sleep(2)" $(wildcard $(PORT)) 138 | else 139 | ifeq "$(RESET_MODE)" "python" 140 | RESET_DEVICE = python -c "import serial; s = serial.SERIAL('$(PORT)', 57600); s.setDTR(True); sleep(1); s.setDTR(False)" 141 | else 142 | ifeq "$(RESET_MODE)" "perl" 143 | RESET_DEVICE = perl -MDevice::SerialPort -e 'Device::SerialPort->new("$(PORT)")->pulse_dtr_on(1000)' 144 | else 145 | ifeq "$(UNAME)" "Windows" 146 | RESET_DEVICE = echo "CAN'T RESET DEVICE ON WINDOWS !" 147 | else 148 | ifeq "$(UNAME)" "Linux" 149 | RESET_DEVICE = stty -F $(PORT) hupcl 150 | else 151 | # BSD uses small f 152 | RESET_DEVICE = stty -f $(PORT) hupcl 153 | endif 154 | endif 155 | endif 156 | endif 157 | endif 158 | 159 | # 160 | # set up attiny boards.txt path 161 | # 162 | ifeq "$(wildcard $(ATTINY_DIR))" "" 163 | ATTINY_BOARDS= 164 | else 165 | ATTINY_BOARDS=$(ATTINY_DIR)/boards.txt 166 | endif 167 | 168 | # 169 | # Set up values according to what the IDE uses: 170 | # 171 | UPLOAD_RATE ?= $(shell grep "^$(MODEL)\." $(ARDUINO_DIR)/hardware/arduino/boards.txt $(ATTINY_BOARDS) | grep upload.speed | sed 's/.*=//') 172 | MCU = $(shell grep "^$(MODEL)\." $(ARDUINO_DIR)/hardware/arduino/boards.txt $(ATTINY_BOARDS) | grep build.mcu | sed 's/.*=//') 173 | F_CPU = $(shell grep "^$(MODEL)\." $(ARDUINO_DIR)/hardware/arduino/boards.txt $(ATTINY_BOARDS) | grep build.f_cpu | sed 's/.*=//') 174 | 175 | # man avrdude says to use arduino, but the IDE mostly uses stk500. 176 | # One rumor says that the difference is that arduino does an auto-reset, stk500 doesn't. 177 | # Might want to grep for upload.protocol as with previous 3 values. 178 | ifneq ($(findstring tiny,$(MODEL)),) 179 | AVRDUDE_PROGRAMMER ?= $(shell grep "^$(MODEL)\." $(ARDUINO_DIR)/hardware/arduino/boards.txt $(ATTINY_BOARDS) | grep upload.using | sed 's/.*=\(.*\):.*/\1/') 180 | ARDUINO_CORE=$(ATTINY_DIR)/cores/tiny 181 | ARDUINO_VARIANT=$(ATTINY_DIR)/cores/tiny 182 | ARDUINO_PROG_HEADER=WProgram.h 183 | SRC=$(ARDUINO_CORE)/pins_arduino.c 184 | ifeq ($(UPLOAD_RATE),) 185 | UPLOAD_RATE?=9600 186 | endif 187 | else 188 | AVRDUDE_PROGRAMMER ?= $(shell grep "^$(MODEL)\." $(ARDUINO_DIR)/hardware/arduino/boards.txt $(ATTINY_BOARDS) | grep upload.protocol | sed 's/.*=//') 189 | ARDUINO_CORE=$(ARDUINO_DIR)/hardware/arduino/cores/arduino 190 | ARDUINO_PROG_HEADER=Arduino.h 191 | SRC= 192 | 193 | # This has only been tested on standard variants. I'm guessing 194 | # at what mega and micro might need; other possibilities are 195 | # leonardo and "eightanaloginputs". 196 | ifeq "$(MODEL)" "mega" 197 | ARDUINO_VARIANT=$(ARDUINO_DIR)/hardware/arduino/variants/mega 198 | else 199 | ifeq "$(MODEL)" "micro" 200 | ARDUINO_VARIANT=$(ARDUINO_DIR)/hardware/arduino/variants/micro 201 | else 202 | ifeq "$(MODEL)" "leonardo" 203 | ARDUINO_VARIANT=$(ARDUINO_DIR)/hardware/arduino/variants/leonardo 204 | else 205 | ARDUINO_VARIANT=$(ARDUINO_DIR)/hardware/arduino/variants/standard 206 | endif 207 | endif 208 | endif 209 | endif 210 | 211 | CWDBASE = $(shell basename $(PWD)) 212 | TARFILE = $(TARGET)-$(VERSION).tar.gz 213 | 214 | # $(ARDUINO_DIR)/hardware/arduino/variants/standard/pins_arduino.c 215 | SRC += \ 216 | $(ARDUINO_CORE)/wiring.c \ 217 | $(ARDUINO_CORE)/wiring_analog.c $(ARDUINO_CORE)/wiring_digital.c \ 218 | $(ARDUINO_CORE)/wiring_pulse.c \ 219 | $(ARDUINO_CORE)/wiring_shift.c $(ARDUINO_CORE)/WInterrupts.c \ 220 | $(foreach l,$(ARDLIBS),$(wildcard $(ARDUINO_DIR)/libraries/$l/*.c)) \ 221 | $(foreach l,$(USERLIBS),$(wildcard $(HOME_LIB)/$l/*.c)) \ 222 | $(foreach l,$(LOCALLIBS),$(wildcard $l/*.c)) 223 | 224 | CXXSRC = $(ARDUINO_CORE)/HardwareSerial.cpp $(ARDUINO_CORE)/WMath.cpp \ 225 | $(ARDUINO_CORE)/WString.cpp $(ARDUINO_CORE)/Print.cpp \ 226 | $(ARDUINO_CORE)/USBCore.cpp $(ARDUINO_CORE)/HID.cpp $(ARDUINO_CORE)/CDC.cpp \ 227 | $(foreach l,$(ARDLIBS),$(wildcard $(ARDUINO_DIR)/libraries/$l/*.cpp)) \ 228 | $(foreach l,$(USERLIBS),$(wildcard $(HOME_LIB)/$l/*.cpp)) \ 229 | $(foreach l,$(LOCALLIBS),$(wildcard $l/*.cpp)) 230 | 231 | FORMAT = ihex 232 | 233 | # Name of this Makefile (used for "make depend"). 234 | MAKEFILE = Makefile 235 | 236 | # Debugging format. 237 | # Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. 238 | # AVR (extended) COFF requires stabs, plus an avr-objcopy run. 239 | DEBUG = stabs 240 | 241 | OPT = s 242 | 243 | # Place -D or -U options here 244 | ifeq "$(MODEL)" "leonardo" 245 | CDEFS = -DF_CPU=$(F_CPU) -DUSB_VID=0x2341 -DUSB_PID=0x0036 246 | else 247 | CDEFS = -DF_CPU=$(F_CPU) 248 | endif 249 | 250 | # Include directories 251 | CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_VARIANT) \ 252 | $(patsubst %,-I$(ARDUINO_DIR)/libraries/%,$(ARDLIBS)) \ 253 | $(patsubst %,-I$(HOME_LIB)/%,$(USERLIBS)) \ 254 | $(patsubst %,-I%/,$(LOCALLIBS)) 255 | 256 | # Compiler flag to set the C Standard level. 257 | # c89 - "ANSI" C 258 | # gnu89 - c89 plus GCC extensions 259 | # c99 - ISO C99 standard (not yet fully implemented) 260 | # gnu99 - c99 plus GCC extensions 261 | CSTANDARD = -std=gnu99 262 | CXXSTANDARD = -std=gnu++11 263 | CDEBUG = -g$(DEBUG) 264 | CWARN = -Wall -Wstrict-prototypes 265 | CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 266 | #CEXTRA = -Wa,-adhlns=$(<:.c=.lst) 267 | 268 | # Extra flags to match what the Arduino 1.0 IDE is doing: 269 | # Something about the -ffunction-sections -fdata-sections reduces 270 | # final text size by roughly half! 271 | CEXTRA= -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -DARDUINO=100 272 | 273 | CFLAGS = $(CDEBUG) $(DEFINES) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA) 274 | CXXFLAGS = $(CDEFS) $(DEFINES) $(CINCS) -O$(OPT) $(CXXSTANDARD) $(CEXTRA) 275 | #ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs 276 | LDFLAGS = -Os -Wl,--gc-sections -mmcu=$(MCU) -lm 277 | 278 | # Programming support using avrdude. Settings and variables. 279 | AVRDUDE_WRITE_FLASH = -U flash:w:applet/$(TARGET).hex 280 | ifeq "$(UNAME)" "Darwin" 281 | # AVRDUDE_CONF = -V -F -C $(ARDUINO_DIR)/hardware/tools/avr/etc/avrdude.conf 282 | AVRDUDE_CONF ?= -V -F -C /usr/local/etc/avrdude.conf 283 | else 284 | AVRDUDE_CONF ?= -V -F -C /etc/avrdude.conf 285 | endif 286 | AVRDUDE_FLAGS = $(AVRDUDE_CONF) \ 287 | -p $(MCU) -P $(PORT) -c $(AVRDUDE_PROGRAMMER) \ 288 | -b $(UPLOAD_RATE) -e 289 | 290 | # Program settings 291 | CC = $(AVR_TOOLS_PATH)/avr-gcc 292 | CXX = $(AVR_TOOLS_PATH)/avr-g++ 293 | OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy 294 | OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump 295 | AR = $(AVR_TOOLS_PATH)/avr-ar 296 | SIZE = $(AVR_TOOLS_PATH)/avr-size 297 | NM = $(AVR_TOOLS_PATH)/avr-nm 298 | AVRDUDE = $(AVR_TOOLS_PATH)/avrdude 299 | SREC_CAT = /usr/bin/env srec_cat 300 | TERM = /usr/local/bin/miniterm.py 301 | REMOVE = rm -f 302 | MV = mv -f 303 | 304 | # Define all object files. 305 | OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) 306 | 307 | # Define all listing files. 308 | LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) 309 | 310 | # Combine all necessary flags and optional flags. 311 | # Add target processor to flags. 312 | ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) 313 | ALL_CXXFLAGS = -mmcu=$(MCU) -I. $(CXXFLAGS) 314 | ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) 315 | 316 | # Default target. 317 | all: applet_files build sizeafter 318 | 319 | test: 320 | @echo CXXSRC = $(CXXSRC) 321 | 322 | build: elf hex 323 | 324 | ifneq "$(wildcard $(TARGET).pde)" "" 325 | applet/$(TARGET).cpp: $(TARGET).pde 326 | # Here is the "preprocessing". 327 | # It creates a .cpp file based with the same name as the .pde file. 328 | # On top of the new .cpp file comes the WProgram.h header. 329 | # At the end there is a generic main() function attached, 330 | # plus special magic to get around the pure virtual error 331 | # undefined reference to `__cxa_pure_virtual' from Print.o. 332 | # Then the .cpp file will be compiled. Errors during compile will 333 | # refer to this new, automatically generated, file. 334 | # Not the original .pde file you actually edit... 335 | test -d applet || mkdir applet 336 | echo '#include "$(ARDUINO_PROG_HEADER)"' > applet/$(TARGET).cpp 337 | cat $(TARGET).pde >> applet/$(TARGET).cpp 338 | echo 'extern "C" void __cxa_pure_virtual() { while (1) ; }' >> applet/$(TARGET).cpp 339 | cat $(ARDUINO_CORE)/main.cpp >> applet/$(TARGET).cpp 340 | else 341 | ifneq "$(wildcard $(TARGET).ino)" "" 342 | applet/$(TARGET).cpp: $(TARGET).ino 343 | # Here is the "preprocessing". 344 | # It creates a .cpp file based with the same name as the .ino file. 345 | # On top of the new .cpp file comes the WProgram.h header. 346 | # At the end there is a generic main() function attached, 347 | # plus special magic to get around the pure virtual error 348 | # undefined reference to `__cxa_pure_virtual' from Print.o. 349 | # Then the .cpp file will be compiled. Errors during compile will 350 | # refer to this new, automatically generated, file. 351 | # Not the original .ino file you actually edit... 352 | test -d applet || mkdir applet 353 | echo '#include "$(ARDUINO_PROG_HEADER)"' > applet/$(TARGET).cpp 354 | cat $(TARGET).ino >> applet/$(TARGET).cpp 355 | echo 'extern "C" void __cxa_pure_virtual() { while (1) ; }' >> applet/$(TARGET).cpp 356 | cat $(ARDUINO_CORE)/main.cpp >> applet/$(TARGET).cpp 357 | else 358 | applet/$(TARGET).cpp: 359 | @echo "FAILURE: Missing .pde or .ino file in current directory !" 360 | @exit 2 361 | endif 362 | endif 363 | 364 | elf: applet/$(TARGET).elf 365 | hex: applet/$(TARGET).hex 366 | eep: applet/$(TARGET).eep 367 | lss: applet/$(TARGET).lss 368 | sym: applet/$(TARGET).sym 369 | 370 | # Display size of file. 371 | HEXSIZE = $(SIZE) --mcu atmega32u4 --target=$(FORMAT) applet/$(TARGET).hex 372 | ELFSIZE = $(SIZE) -C --mcu atmega32u4 --target=$(FORMAT) applet/$(TARGET).elf 373 | size_merge: 374 | @if [ -f applet/$(TARGET)-full.hex ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi 375 | 376 | sizebefore: 377 | @if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); echo; fi 378 | 379 | sizeafter: 380 | @if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); echo; fi 381 | 382 | reset: 383 | $(RESET_DEVICE) 384 | 385 | merge: applet/$(TARGET).hex $(ARDUINO_DIR)/hardware/arduino/bootloaders/caterina/Caterina.hex 386 | $(SREC_CAT) applet/$(TARGET).hex -Intel $(ARDUINO_DIR)/hardware/arduino/bootloaders/caterina/Caterina.hex -Intel -Output applet/$(TARGET)-full.hex 387 | 388 | flash: sizebefore merge size_merge 389 | $(AVRDUDE) $(AVRDUDE_CONF) -p $(MCU) -P $(AVRDUDE_FLASH_PORT) -c $(AVRDUDE_FLASH_PGER) $(AVRDUDE_FLASH_RATE) -e -U flash:w:applet/$(TARGET)-full.hex 390 | 391 | flash2: sizebefore 392 | $(AVRDUDE) $(AVRDUDE_CONF) -p $(MCU) -P $(AVRDUDE_FLASH_PORT) -c $(AVRDUDE_FLASH_PGER) $(AVRDUDE_FLASH_RATE) -e -U flash:w:applet/$(TARGET).hex 393 | 394 | # Program the device. 395 | upload: applet/$(TARGET).hex 396 | echo 'r' > $(PORT) 397 | sleep 1 398 | $(AVRDUDE) $(AVRDUDE_CONF) -p $(MCU) -P $(PORT) -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE) -U flash:w:applet/$(TARGET).hex 399 | 400 | monitor: 401 | /bin/zsh -c 'while [ ! -c ${PORT} ]; do; done' 402 | sleep 4 403 | $(TERM) $(PORT) 115200 404 | 405 | # Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. 406 | COFFCONVERT=$(OBJCOPY) --debugging \ 407 | --change-section-address .data-0x800000 \ 408 | --change-section-address .bss-0x800000 \ 409 | --change-section-address .noinit-0x800000 \ 410 | --change-section-address .eeprom-0x810000 411 | 412 | coff: applet/$(TARGET).elf 413 | $(COFFCONVERT) -O coff-avr applet/$(TARGET).elf $(TARGET).cof 414 | 415 | 416 | extcoff: $(TARGET).elf 417 | $(COFFCONVERT) -O coff-ext-avr applet/$(TARGET).elf $(TARGET).cof 418 | 419 | .SUFFIXES: .elf .hex .eep .lss .sym 420 | 421 | .elf.hex: 422 | $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ 423 | 424 | .elf.eep: 425 | -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ 426 | --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ 427 | 428 | # Create extended listing file from ELF output file. 429 | .elf.lss: 430 | $(OBJDUMP) -h -S $< > $@ 431 | 432 | # Create a symbol table from ELF output file. 433 | .elf.sym: 434 | $(NM) -n $< > $@ 435 | 436 | # Link: create ELF output file from library. 437 | applet/$(TARGET).elf: applet/$(TARGET).o applet/core.a 438 | $(CC) -o $@ applet/$(TARGET).o -L. applet/core.a $(LDFLAGS) 439 | 440 | #applet/$(TARGET).elf: applet/$(TARGET).o applet/core.a 441 | # $(CC) $(ALL_CFLAGS) -o $@ applet/$(TARGET).cpp -L. applet/core.a $(LDFLAGS) 442 | 443 | applet/core.a: $(OBJ) 444 | @for i in $(OBJ); do echo $(AR) rcs applet/core.a $$i; $(AR) rcs applet/core.a $$i; done 445 | 446 | # Compile: create object files from C++ source files. 447 | .cpp.o: 448 | $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ 449 | 450 | # Compile: create object files from C source files. 451 | .c.o: 452 | $(CC) -c $(ALL_CFLAGS) $< -o $@ 453 | 454 | 455 | # Compile: create assembler files from C source files. 456 | .c.s: 457 | $(CC) -S $(ALL_CFLAGS) $< -o $@ 458 | 459 | 460 | # Assemble: create object files from assembler source files. 461 | .S.o: 462 | $(CC) -c $(ALL_ASFLAGS) $< -o $@ 463 | 464 | # Target: clean project. 465 | clean: 466 | $(REMOVE) applet/$(TARGET).eep applet/$(TARGET).cof applet/$(TARGET).elf \ 467 | applet/$(TARGET).map applet/$(TARGET).sym applet/$(TARGET).lss applet/core.a \ 468 | $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) 469 | 470 | flush: 471 | $(REMOVE) -rf applet/ \ 472 | $(REMOVE) $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) 473 | 474 | list: 475 | # LIST OF ALL THE BOARDS AVAILABLE AS TARGETS FOR $$MODEL ENV VARIABLE 476 | @cat $(ARDUINO_DIR)/hardware/arduino/boards.txt $(ATTINY_BOARDS) \ 477 | | grep '.name' \ 478 | | sed 's/\(.*\)\.name=\(.*\)/MODEL=\1;\2/' \ 479 | | column -t -s';' 480 | 481 | tar: $(TARFILE) 482 | 483 | $(TARFILE): 484 | ( cd .. && \ 485 | tar czvf $(TARFILE) --exclude=applet --owner=root $(CWDBASE) && \ 486 | mv $(TARFILE) $(CWD) && \ 487 | echo Created $(TARFILE) \ 488 | ) 489 | 490 | depend: 491 | if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \ 492 | then \ 493 | sed -e '/^# DO NOT DELETE/,$$d' $(MAKEFILE) > \ 494 | $(MAKEFILE).$$$$ && \ 495 | $(MV) $(MAKEFILE).$$$$ $(MAKEFILE); \ 496 | fi 497 | echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \ 498 | >> $(MAKEFILE); \ 499 | $(CC) -M -mmcu=$(MCU) $(CDEFS) $(CINCS) $(SRC) $(ASRC) >> $(MAKEFILE) 500 | 501 | .PHONY: all build elf hex eep lss sym program coff extcoff clean depend applet_files sizebefore sizeafter 502 | 503 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ANCS Library for Arduino and nrf8001 2 | === 3 | 4 | Apple Notification Center Services (ANCS) makes it easy to get notifications from an iOS device over Bluetooth Low Energy (BLE). The nrf8001 is a BLE chip from Nordic Semiconductor that makes BLE easy. There are Arduino libraries for that make it easy to integrate into your projects. 5 | 6 | This library integrates this all together, and lets you easily make cool Arduino projects like, waving a flag or blinking lights whenever you get a new email. 7 | 8 | 9 | Hardware 10 | -- 11 | The library has been tested with the [RedBear Labs BLE shield](http://redbearlab.com/bleshield/) and should also work with the [Bluefruit LE Breakout Board](https://www.adafruit.com/products/1697). 12 | 13 | Install 14 | -- 15 | - Download and follow the directions for installing the Nordic Arduino libraries: [https://github.com/NordicSemiconductor/ble-sdk-arduino](https://github.com/NordicSemiconductor/ble-sdk-arduino) 16 | - Download this library, unzip it, and copy it to the Arduino Library folder. 17 | 18 | Examples 19 | -- 20 | - LCD - this example will display notification on an LCD display using the LiquidCrystal library. It should provide a good starting point for building your own awesomeness. **Make sure you update the example with the Pins you are using for the BLE Shield and the LCD Shield!** 21 | 22 | Usage 23 | -- 24 | A couple of additional libraries are needed in order to make things work. Start off by including the following: 25 | 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | Next include this library: 32 | 33 | #include 34 | 35 | The library handles setting up the BLE chip and you need to provide it with the pins you are using for `RDYN` & `REQN` - `Notif(uint8_t reqnPin, uint8_t rdynPin`: 36 | 37 | Notif notif(12,11); 38 | 39 | In the setup() function of you program you need to call `notif.setup();` and also tell the library the callbacks to use when ANCS events occur. More on that in the Callbacks section below... and of course change the names of the functions you pass to match yours. 40 | 41 | void setup(void) 42 | { 43 | notif.setup(); 44 | notif.set_notification_callback_handle(ancs_notifications); 45 | notif.set_connect_callback_handle(ancs_connected); 46 | notif.set_disconnect_callback_handle(ancs_disconnected); 47 | } 48 | 49 | Finally you need to call the library in your loop() to read and process the BLE messages. 50 | 51 | void loop() 52 | { 53 | notif.ReadNotifications(); 54 | } 55 | 56 | Of course, if you run it now nothing will happen. You need to setup some Callback functions.... *read on* 57 | 58 | Callbacks 59 | -- 60 | When interesting things happen the library will call the functions you set. This is how you make things happen! 61 | 62 | ###Notification 63 | This Callback is called everytime a new notification is sent from the bonded iOS device. Use the following template for the function in your sketch you want called: `void function_name(ancs_notification_t* notif)` 64 | 65 | The callback is set by passing the library the function in your sketch you want called: 66 | 67 | notif.set_notification_callback_handle(ancs_notifications); 68 | 69 | ###Connect 70 | This Callback is called when an iOS device connects to the BLE board. Use the following template for the function in your sketch you want called: `void function_name()` 71 | 72 | The callback is set by passing the library the function in your sketch you want called: 73 | 74 | notif.set_connect_callback_handle(ancs_connected); 75 | 76 | ###Connect 77 | This Callback is called when an iOS device disconnects from the BLE board. Use the following template for the function in your sketch you want called: `void function_name()` 78 | 79 | 80 | The callback is set by passing the library the function in your sketch you want called: 81 | notif.set_disconnect_callback_handle(ancs_connected); 82 | 83 | 84 | Defines 85 | -- 86 | `#define DEBUG1` 87 | Turns on debugging using a Macro. With this off, the serial.prints are ignored and a lot of memory is saved. 88 | 89 | `#define ANCS_USE_APP` 90 | `#define ANCS_USE_SUBTITLE` 91 | Turns on the respective Notification fields. With this off, they will not be fetched and cached, which saves memory. 92 | 93 | 94 | Pairing an iOS Device 95 | -- 96 | Once you have a Sketch written and loaded on your Arduino, it is time to pair your iPhone or iPad with it. On the iOS device go into `Settings`, then `Bluetooth`. Under `Devices` look for `Notif` and tap it. It should ask you if you would like to Pair. After that, both the BLE board and the iOS device will be Bonded and remember the connection. If things are working right, both sides will automatically reconnect when they are in range and this information should be remembered even after the Arduino is reset. 97 | 98 | Unpairing 99 | -- 100 | You can break the Pairing on the iOS device go into `Settings`, then `Bluetooth` and then click on the `i` next to `Notif`. It will bring you to a screen with an option to `Forget This Device`. After doing this on the iOS side, you may need to reset the Arduino before it will forget its pairing information and allow for a new pairing. 101 | 102 | BluefruitLE Breakout Board 103 | -- 104 | If you are using the Breakout board from Adafruit you need to modify notif.cc and let it know what pins you are using for Reset and Interupt. Goto Line ~800 of notif.cc and edit appropriately: 105 | 106 | aci_state.aci_pins.reset_pin = 9; // was UNUSED 107 | aci_state.aci_pins.active_pin = UNUSED; 108 | aci_state.aci_pins.optional_chip_sel_pin = UNUSED; 109 | 110 | aci_state.aci_pins.interface_is_interrupt = true; // was false 111 | aci_state.aci_pins.interrupt_number = 1; // (on the Micro/Leo, pin2 is int1) was UNUSED 112 | 113 | LICENSES 114 | -- 115 | 116 | Below, you'll find the license notice for each software included in this source tree. 117 | 118 | === DISCLAIMER === 119 | 120 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 121 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 122 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 123 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 124 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 125 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 126 | OTHER DEALINGS IN THE SOFTWARE. 127 | 128 | 129 | === ANCS LIBRARY === 130 | 131 | (c)2013, 2014 Bernard Pratz, bernard@pratz.net, All rights reserved. 132 | 133 | Entirely based on public Apple Specification for the ANCS 134 | 135 | This program is free software: you can redistribute it and/or modify 136 | it under the terms of the GNU General Public License as published by 137 | the Free Software Foundation, either version 3 of the License, or 138 | (at your option) any later version. 139 | 140 | This program is distributed in the hope that it will be useful, 141 | but WITHOUT ANY WARRANTY; without even the implied warranty of 142 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 143 | GNU General Public License for more details. 144 | 145 | You should have received a copy of the GNU General Public License 146 | along with this program. If not, see . 147 | -------------------------------------------------------------------------------- /ancs.h: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | #ifndef _ANCS_H_ 5 | #define _ANCS_H_ 6 | 7 | #include "ancs_notification.h" 8 | #include "ancs_notification_list.h" 9 | #include "ancs_notification_source.h" 10 | #include "ancs_data_source.h" 11 | 12 | // #define NO_CACHE // disables caching 13 | // #define ANCS_DATA_SIZE 10 // sets the length of the attributes to be grabbed 14 | 15 | // in setup(): 16 | // ancs_init(); // initializes the ancs library 17 | // in aci_loop(): 18 | // ancs_run(); // executes ancs routines 19 | // in ACI_EVT_DATA_RECEIVED: 20 | // ancs_notification_source_parser() // to be called with the data incoming for the notification source pipe 21 | // ancs_data_source_parser() // to be called with the data incoming for the data source pipe 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /ancs_base.h: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | #ifndef _ANCS_BASE_H_ 5 | #define _ANCS_BASE_H_ 6 | 7 | #include 8 | #include 9 | 10 | // user definable variable 11 | #define ANCS_NOTIFICATION_ATTRIBUTE_DATA_SIZE 16 12 | #define TITLE_LEN 16 13 | #define LINE_SIZE 16 14 | #define MESSAGE_SIZE 40 15 | 16 | 17 | #define CACHE_SIZE 5 18 | 19 | //////////////////////////////////////////////////////////////////////////////////// 20 | #define ANCS_EVT_NOTIFICATION_ADDED 0x0 21 | #define ANCS_EVT_NOTIFICATION_MODIFIED 0x1 22 | #define ANCS_EVT_NOTIFICATION_REMOVED 0x2 23 | // reserved ids up to 0xFF 24 | 25 | #define ANCS_EVT_FLAG_SILENT 0x1 26 | #define ANCS_EVT_FLAG_IMPORTANT 0x2 27 | // reserved flags 0x4, 0x8, 0x10, 0x20, 0x40 and 0x80 28 | 29 | #define ANCS_CATEGORY_OTHER 0x0 30 | #define ANCS_CATEGORY_INCOMING_CALL 0x1 31 | #define ANCS_CATEGORY_MISSED_CALL 0x2 32 | #define ANCS_CATEGORY_VOICEMAIL 0x3 33 | #define ANCS_CATEGORY_SOCIAL 0x4 34 | #define ANCS_CATEGORY_SCHEDULE 0x5 35 | #define ANCS_CATEGORY_EMAIL 0x6 36 | #define ANCS_CATEGORY_NEWS 0x7 37 | #define ANCS_CATEGORY_HEALTH_FITNESS 0x8 38 | #define ANCS_CATEGORY_BUSINESS_FINANCE 0x9 39 | #define ANCS_CATEGORY_LOCATION 0xA 40 | #define ANCS_CATEGORY_ENTERTAINMENT 0xB 41 | // reserved ids up to 0xFF 42 | 43 | #define ANCS_COMMAND_GET_NOTIF_ATTRIBUTES 0x0 44 | #define ANCS_COMMAND_GET_APP_ATTRIBUTES 0x1 45 | // reserved ids up to 0xFF 46 | 47 | #define ANCS_NOTIFICATION_ATTRIBUTE_APP_IDENTIFIER 0x0 48 | #define ANCS_NOTIFICATION_ATTRIBUTE_TITLE 0x1 // shall be followed by 2bytes max length 49 | #define ANCS_NOTIFICATION_ATTRIBUTE_SUBTITLE 0x2 // shall be followed by 2bytes max length 50 | #define ANCS_NOTIFICATION_ATTRIBUTE_MESSAGE 0x3 // shall be followed by 2bytes max length 51 | #define ANCS_NOTIFICATION_ATTRIBUTE_MESSAGE_SIZE 0x4 52 | #define ANCS_NOTIFICATION_ATTRIBUTE_DATE 0x5 53 | // reserved ids up to 0xFF 54 | 55 | #define ANCS_NOTIFICATION_APP_ID_DISPLAY_NAME 0x0 56 | // reserved ids up to 0xFF 57 | 58 | // Datasource definitions 59 | #define ANCS_DATA_LEN 20//ACI_PIPE_RX_DATA_MAX_LEN-2 60 | #define ANCS_HEADER_LEN 5 61 | #define ANCS_ATTR_REQ_LEN 3 62 | #define ANCS_FIRST_DATA_LEN 12 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /ancs_data_source.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | //#define DEBUG1 4 | //#define DEBUG2 5 | //#define DEBUG3 6 | #define NO_PACK 7 | #define PACK_LITTLE_ENDIAN 8 | 9 | #include 10 | #include "pack_lib.h" 11 | #include "utilities.h" 12 | 13 | #include "ancs_base.h" 14 | #include "ancs_data_source.h" 15 | #include "ancs_notification_list.h" 16 | #include "ancs_notification_source.h" 17 | 18 | 19 | extern boolean command_send_enable; 20 | extern unsigned long last_command_send; 21 | static ancs_parsing_env_t ancs_parsing_env; 22 | 23 | ancs_notification_t* ancs_data_source_parser(const uint8_t* buffer) { 24 | debug_println(F("Notification Attribute Details")); 25 | debug_println(F("[ANCS DS] ancs_data_source_hook()")); 26 | 27 | debug2_print(F("[ANCS DS] -> Buffer received: ")); 28 | #ifdef DEBUG2 29 | for (uint8_t i=0; i COMMAND: ")); 48 | debug2_println(cmd, HEX); 49 | debug2_print(F("[ANCS DS] -> NOTIFICATION: ")); 50 | debug2_println(nid, DEC); 51 | debug2_print(F("[ANCS DS] -> ATTRIBUTE: ")); 52 | debug2_println(aid, HEX); 53 | debug2_print(F("[ANCS DS] -> DATA LENGTH: ")); 54 | debug2_println(len, DEC); 55 | if (len < ((ANCS_DATA_LEN - ANCS_HEADER_LEN) - ANCS_ATTR_REQ_LEN)) { 56 | debug2_println(F("[ANCS DS] -> All data is in this datagram")); 57 | debug2_print(F("[ANCS DS] -> DATA: ")); 58 | ancs_parsing_env.buffer = (uint8_t*)malloc(len+1); 59 | buffer = buffer + ANCS_HEADER_LEN + ANCS_ATTR_REQ_LEN; 60 | for (uint8_t i=0; i Data continuing in next datagram Len:")); 82 | debug2_print(len); 83 | ancs_parsing_env.buffer = (uint8_t*)malloc(len+1); 84 | ancs_parsing_env.ahead = len-ANCS_FIRST_DATA_LEN; 85 | buffer = buffer + ANCS_HEADER_LEN + ANCS_ATTR_REQ_LEN; 86 | debug2_print(F("[ANCS DS] -> DATA: ")); 87 | for (uint8_t i=0; i All data left is in this datagram")); 100 | debug2_print(F("[ANCS DS] -> BUFFER IDX: ")); 101 | debug2_println(ancs_parsing_env.index, DEC); 102 | debug2_print(F("[ANCS DS] -> DATA: ")); 103 | for (uint8_t i=0; i Data continuing in next datagram")); 124 | debug2_print(F("[ANCS DS] -> BUFFER IDX: ")); 125 | debug2_println(ancs_parsing_env.index, DEC); 126 | debug2_print(F("[ANCS DS] -> DATA: ")); 127 | for (uint8_t i=0; iflags & ANCS_EVT_FLAG_SILENT) == ANCS_EVT_FLAG_SILENT) 148 | // Serial.print(F("-")); 149 | // else if ((notif->flags & ANCS_EVT_FLAG_IMPORTANT) == ANCS_EVT_FLAG_IMPORTANT) 150 | // Serial.print(F("!")); 151 | // else 152 | // Serial.print(F(" ")); 153 | // Serial.print(F("]")); 154 | // switch (notif->category) { 155 | // case ANCS_CATEGORY_OTHER: 156 | // Serial.println(F("Other")); 157 | // break; 158 | // case ANCS_CATEGORY_INCOMING_CALL: 159 | // Serial.println(F("Incoming call")); 160 | // break; 161 | // case ANCS_CATEGORY_MISSED_CALL: 162 | // Serial.println(F("Missed call")); 163 | // break; 164 | // case ANCS_CATEGORY_VOICEMAIL: 165 | // Serial.println(F("Voicemail")); 166 | // break; 167 | // case ANCS_CATEGORY_SOCIAL: 168 | // Serial.println(F("Social")); 169 | // break; 170 | // case ANCS_CATEGORY_SCHEDULE: 171 | // Serial.println(F("Schedule")); 172 | // break; 173 | // case ANCS_CATEGORY_EMAIL: 174 | // Serial.println(F("Email")); 175 | // break; 176 | // case ANCS_CATEGORY_NEWS: 177 | // Serial.println(F("News")); 178 | // break; 179 | // case ANCS_CATEGORY_HEALTH_FITNESS: 180 | // Serial.println(F("Health & Fitness")); 181 | // break; 182 | // case ANCS_CATEGORY_BUSINESS_FINANCE: 183 | // Serial.println(F("Business & Finance")); 184 | // break; 185 | // case ANCS_CATEGORY_LOCATION: 186 | // Serial.println(F("Location")); 187 | // break; 188 | // case ANCS_CATEGORY_ENTERTAINMENT: 189 | // Serial.println(F("Entertainment")); 190 | // break; 191 | // } 192 | // Serial.print(F(": ")); 193 | // Serial.println(notif->title); 194 | } 195 | 196 | ancs_notification_t* ancs_cache_attribute(uint32_t nid, uint8_t aid, const char* buffer, uint16_t len) { 197 | char* datetime; 198 | ancs_notification_t* notif = ancs_notification_list_get(nid); 199 | debug3_print(F("ancs_cache_attribute(")); 200 | debug3_print(nid, DEC); 201 | debug3_print(F(", 0x")); 202 | debug3_print(aid, HEX); 203 | debug3_print(F(", '")); 204 | debug3_print(buffer); 205 | debug3_println(F("')")); 206 | debug2_print(F(" Notif #")); 207 | debug2_print(nid, DEC); 208 | 209 | if (notif != NULL) { 210 | 211 | switch (aid) { 212 | #ifdef ANCS_USE_APP 213 | case ANCS_NOTIFICATION_ATTRIBUTE_APP_IDENTIFIER: 214 | debug2_print(F(", App: ")); 215 | strncpy(notif->app, buffer, strlen(buffer)); 216 | break; 217 | #endif 218 | case ANCS_NOTIFICATION_ATTRIBUTE_TITLE: 219 | debug2_print(F(", Title: ")); 220 | strncpy(notif->title, buffer, strlen(buffer)); 221 | break; 222 | case ANCS_NOTIFICATION_ATTRIBUTE_DATE: 223 | debug_print(F(", Date: ")); 224 | // YYYYMMDDTHHMM 225 | datetime = (char*)malloc(5); 226 | strncpy(datetime, buffer, 4); 227 | datetime[4] = '\0'; 228 | notif->time.Y = atoi(datetime); 229 | buffer = buffer+5; 230 | strncpy(datetime, buffer, 2); 231 | datetime[2] = '\0'; 232 | notif->time.M = atoi(datetime); 233 | buffer = buffer+7; 234 | strncpy(datetime, buffer, 2); 235 | datetime[2] = '\0'; 236 | notif->time.D = atoi(datetime); 237 | buffer = buffer+10; 238 | strncpy(datetime, buffer, 2); 239 | datetime[2] = '\0'; 240 | notif->time.h = atoi(datetime); 241 | buffer = buffer+12; 242 | strncpy(datetime, buffer, 2); 243 | datetime[2] = '\0'; 244 | notif->time.m = atoi(datetime); 245 | free(datetime); 246 | break; 247 | case ANCS_NOTIFICATION_ATTRIBUTE_MESSAGE_SIZE: 248 | debug_print(F(", Msglen: ")); 249 | notif->msg_len = atoi(buffer); 250 | break; 251 | #ifdef ANCS_USE_SUBTITLE 252 | case ANCS_NOTIFICATION_ATTRIBUTE_SUBTITLE: 253 | debug_print(F(", SubTitle: ")); 254 | strncpy(notif->subtitle, buffer, strlen(buffer)); 255 | break; 256 | #endif 257 | case ANCS_NOTIFICATION_ATTRIBUTE_MESSAGE: 258 | debug_print(F(", Message: ")); 259 | strncpy(notif->message, buffer, strlen(buffer)); 260 | debug_println(buffer); 261 | return notif; 262 | 263 | break; 264 | default: 265 | debug_print(F(", Attribute unknown 0x")); 266 | debug_print(aid, HEX); 267 | debug_print(F(": ")); 268 | } 269 | } else { 270 | debug_println(F("ERROR: Notification not found in the Cache")); 271 | } 272 | debug_println(buffer); 273 | return NULL; 274 | } 275 | 276 | -------------------------------------------------------------------------------- /ancs_data_source.h: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | 5 | #ifndef _DATA_SOURCE_H_ 6 | #define _DATA_SOURCE_H_ 7 | 8 | #include 9 | #include "ancs_base.h" 10 | #include "ancs_notification.h" 11 | 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct ancs_parsing_env_t { 18 | uint8_t* buffer; 19 | uint8_t index; 20 | uint8_t ahead; 21 | uint32_t nid; 22 | uint8_t aid; 23 | uint16_t len; 24 | } ancs_parsing_env_t; 25 | 26 | ancs_notification_t* ancs_data_source_parser(const uint8_t* buffer); 27 | ancs_notification_t* ancs_cache_attribute(uint32_t nid, uint8_t aid, const char* buffer, uint16_t len); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /ancs_notification.cpp: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | 5 | #include "ancs_notification.h" 6 | #include 7 | #include "utilities.h" 8 | 9 | void ancs_notification_init(ancs_notification_t* n) { 10 | debug_println(F("ancs_notification_init()")); 11 | n->uid = 0xFF; 12 | } 13 | 14 | void ancs_notification_copy(ancs_notification_t* dst, 15 | ancs_notification_t* src) { 16 | debug_println(F("ancs_notification_copy()")); 17 | dst->uid = src->uid; 18 | dst->flags = src->flags; 19 | dst->category = src->category; 20 | dst->action = src->action; 21 | dst->msg_len = src->msg_len; 22 | #ifdef ANCS_USE_APP 23 | strncpy(dst->app, src->app, LINE_SIZE+1); 24 | #endif 25 | strncpy(dst->title, src->title, LINE_SIZE+1); 26 | #ifdef ANCS_USE_SUBTITLE 27 | strncpy(dst->subtitle, src->subtitle, LINE_SIZE+1); 28 | #endif 29 | strncpy(dst->message, src->message, LINE_SIZE+1); 30 | } 31 | 32 | 33 | ancs_notification_t* ancs_notification_cached() { 34 | static ancs_notification_t notification_cache; 35 | debug_print(F("ancs_notification_cached(")); 36 | debug_print(notification_cache.uid); 37 | debug_println(F(")")); 38 | return ¬ification_cache; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /ancs_notification.h: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | #ifndef _ANCS_NOTIFICATION_H_ 5 | #define _ANCS_NOTIFICATION_H_ 6 | 7 | #include 8 | #include 9 | 10 | #include "ancs_base.h" 11 | 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct date_t { 18 | uint8_t Y; 19 | uint8_t M; 20 | uint8_t D; 21 | uint8_t h; 22 | uint8_t m; 23 | } date_t; 24 | 25 | typedef struct ancs_notification_t { 26 | uint32_t uid; 27 | uint8_t action; 28 | uint8_t flags; 29 | uint8_t category; 30 | uint16_t msg_len; 31 | date_t time; 32 | char title[LINE_SIZE+1]; 33 | #ifdef ANCS_USE_SUBTITLE 34 | char subtitle[LINE_SIZE+1]; 35 | #endif 36 | #ifdef ANCS_USE_APP 37 | char app[LINE_SIZE+1]; 38 | #endif 39 | char message[MESSAGE_SIZE+1]; 40 | } ancs_notification_t; 41 | 42 | void ancs_notification_init(ancs_notification_t* n); 43 | void ancs_notification_copy(ancs_notification_t* dst, ancs_notification_t* src); 44 | 45 | ancs_notification_t* ancs_notification_cached(); 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /ancs_notification_list.cpp: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | //#define DEBUG1 5 | #include "ancs_base.h" 6 | #include "ancs_notification.h" 7 | #include "ancs_notification_list.h" 8 | 9 | #include "utilities.h" 10 | 11 | 12 | // keep a buffer for each kind 13 | static ancs_notification_t _notification_buffer[CACHE_SIZE]; 14 | 15 | static uint8_t _first_index = 0; 16 | static uint8_t _last_index = 0; 17 | 18 | void* _ancs_notification_list_alloc() { 19 | debug_println(F("[ANCS NL] _ancs_notification_list_alloc()")); 20 | return NULL; 21 | } 22 | void ancs_notification_list_init () { 23 | free_ram(); 24 | return; 25 | } 26 | 27 | void ancs_notification_list_clear(uint8_t index) { 28 | _notification_buffer[index].uid = 0; 29 | _notification_buffer[index].flags = 0; 30 | _notification_buffer[index].category = 0; 31 | _notification_buffer[index].action = 0; 32 | _notification_buffer[index].msg_len = 0; 33 | #ifdef ANCS_USE_APP 34 | memset(_notification_buffer[index].app, 0, LINE_SIZE+1); 35 | #endif 36 | memset(_notification_buffer[index].title, 0, LINE_SIZE+1); 37 | #ifdef ANCS_USE_SUBTITLE 38 | memset(_notification_buffer[index].subtitle, 0, LINE_SIZE+1); 39 | #endif 40 | memset(_notification_buffer[index].message, 0, LINE_SIZE+1); 41 | } 42 | 43 | void ancs_notification_list_push(ancs_notification_t* notif) { 44 | free_ram(); 45 | 46 | ++_last_index; 47 | 48 | // if last index has reached CACHE_SIZE, make it cycle 49 | if (_last_index == CACHE_SIZE) 50 | _last_index = 0; 51 | // if last index has reached first index, step first index 52 | if (_last_index == _first_index) 53 | { 54 | ancs_notification_list_clear(_first_index); 55 | ++_first_index; 56 | if (_first_index == CACHE_SIZE) 57 | _first_index = 0; 58 | } 59 | ancs_notification_copy(&_notification_buffer[_last_index], notif); 60 | return; 61 | } 62 | ancs_notification_t* ancs_notification_list_get(uint32_t nid) { 63 | uint8_t idx; 64 | 65 | for (uint8_t i=0; i 4 | #include 5 | 6 | 7 | #define PACK_LITTLE_ENDIAN 8 | //#define DEBUG2 9 | //#define DEBUG1 10 | #include "pack_lib.h" 11 | #include "utilities.h" 12 | #include "linked_list.h" 13 | 14 | #include "ancs_base.h" 15 | #include "ancs_notification_source.h" 16 | #include "ancs_notification_list.h" 17 | 18 | boolean command_send_enable = true; 19 | unsigned long last_command_send = 0; 20 | 21 | static linked_list_t* buffer_commands; 22 | static bool ancs_updated = true; 23 | 24 | bool ancs_send_buffered_command() { 25 | 26 | if (!command_send_enable && ((millis() - last_command_send) < 1000)) { 27 | return true; 28 | } 29 | uint8_t* buffer; 30 | size_t len = fifo_pop(buffer_commands, &buffer); 31 | if (len == 0) { 32 | return false; 33 | } 34 | uint8_t cmd, aid; 35 | uint32_t uid; 36 | unpack(buffer, "BIB", &cmd, &uid, &aid); 37 | debug_print(F("[ANCS CP] Command sent 0x")); 38 | debug_print(cmd, HEX); 39 | debug_print(F(" for notification #")); 40 | debug_print(uid, DEC); 41 | debug_print(F(" attribute 0x")); 42 | debug_print(aid, HEX); 43 | debug_println(); 44 | 45 | if (lib_aci_send_data(PIPE_ANCS_CONTROL_POINT_TX_ACK, buffer, len)) { 46 | command_send_enable = false; 47 | last_command_send = millis(); 48 | } else { 49 | Serial.print(F("!! Error sending data for notification #")); 50 | Serial.println(uid, DEC); 51 | } 52 | free(buffer); 53 | return true; 54 | } 55 | 56 | void ancs_run() { 57 | 58 | 59 | if (ancs_send_buffered_command()) { 60 | 61 | ancs_updated = false; 62 | } else if (!ancs_updated) { 63 | ancs_updated = true; 64 | //ancs_notification_list_apply(&ancs_notifications_use_hook); 65 | free_ram(); 66 | } 67 | } 68 | 69 | void ancs_init() { 70 | buffer_commands = fifo_create(); 71 | ancs_notification_list_init(); 72 | } 73 | 74 | void ancs_get_notification_data(uint32_t uid) { 75 | if (command_send_enable || ((millis() - last_command_send) > 1000)) { 76 | Serial.print(F("[ANCS NS] ancs_get_notification_data(")); 77 | Serial.print(uid, DEC); 78 | Serial.println(F(")")); 79 | debug2_print(F("[ANCS NS] Buffering commands to get details for notification #")); 80 | debug2_println(uid, DEC); 81 | 82 | 83 | 84 | uint8_t* buffer; 85 | 86 | #ifdef ANCS_USE_APP 87 | buffer = (uint8_t*)malloc(6); 88 | // 89 | pack(buffer, "BIB", ANCS_COMMAND_GET_NOTIF_ATTRIBUTES, uid, 90 | ANCS_NOTIFICATION_ATTRIBUTE_APP_IDENTIFIER); 91 | 92 | fifo_push(buffer_commands, buffer, 6); 93 | free(buffer); 94 | #endif 95 | // 96 | buffer = (uint8_t*)malloc(8); 97 | pack(buffer, "BIBH", ANCS_COMMAND_GET_NOTIF_ATTRIBUTES, uid, 98 | ANCS_NOTIFICATION_ATTRIBUTE_TITLE, 99 | ANCS_NOTIFICATION_ATTRIBUTE_DATA_SIZE); 100 | fifo_push(buffer_commands, buffer, 8); 101 | free(buffer); 102 | // 103 | buffer = (uint8_t*)malloc(6); 104 | pack(buffer, "BIB", ANCS_COMMAND_GET_NOTIF_ATTRIBUTES, uid, 105 | ANCS_NOTIFICATION_ATTRIBUTE_DATE); 106 | fifo_push(buffer_commands, buffer, 6); 107 | free(buffer); 108 | buffer = (uint8_t*)malloc(6); 109 | pack(buffer, "BIB", ANCS_COMMAND_GET_NOTIF_ATTRIBUTES, uid, 110 | ANCS_NOTIFICATION_ATTRIBUTE_MESSAGE_SIZE); 111 | fifo_push(buffer_commands, buffer, 6); 112 | free(buffer); 113 | // 114 | #ifdef ANCS_USE_SUBTITLE 115 | buffer = (uint8_t*)malloc(8); 116 | pack(buffer, "BIBB", ANCS_COMMAND_GET_NOTIF_ATTRIBUTES, uid, 117 | ANCS_NOTIFICATION_ATTRIBUTE_SUBTITLE, 118 | ANCS_NOTIFICATION_ATTRIBUTE_DATA_SIZE); 119 | fifo_push(buffer_commands, buffer, 8); 120 | free(buffer); 121 | #endif 122 | // 123 | buffer = (uint8_t*)malloc(8); 124 | pack(buffer, "BIBH", ANCS_COMMAND_GET_NOTIF_ATTRIBUTES, uid, 125 | ANCS_NOTIFICATION_ATTRIBUTE_MESSAGE, 126 | MESSAGE_SIZE); 127 | fifo_push(buffer_commands, buffer, 8); 128 | free(buffer); 129 | free_ram(); 130 | debug_println(F("[ANCS NS] ancs_get_notification_data(): end")); 131 | debug_print(F("[ANCS NS] Command Send Enable: ")); 132 | debug_println(command_send_enable); 133 | } else { 134 | Serial.print(F("[ANCS NS] ancs_get_notification_data(")); 135 | Serial.print(uid, DEC); 136 | Serial.print(F(") - Need Command Send - ")); 137 | Serial.println(millis() - last_command_send); 138 | debug_print(command_send_enable); 139 | } 140 | 141 | } 142 | 143 | void ancs_notification_source_parser(const uint8_t* buffer) { 144 | Serial.println(F("[ANCS NS] ancs_notification_source_parser()")); 145 | uint8_t event_id; 146 | uint8_t event_flags; 147 | uint8_t category_id; 148 | uint8_t category_count; 149 | uint32_t nid; 150 | 151 | ancs_notification_t* notif = ancs_notification_cached(); 152 | 153 | unpack(buffer, "BBBBI", &event_id, 154 | &event_flags, 155 | &category_id, 156 | &category_count, 157 | &nid); 158 | 159 | Serial.print(F("N #")); 160 | Serial.print(nid, DEC); 161 | Serial.print(F(": ")); 162 | 163 | notif->uid = nid; 164 | notif->flags = event_flags; 165 | notif->category = category_id; 166 | notif->action = event_id; 167 | 168 | if (event_flags != 0) { 169 | Serial.print(F("FLAGS: ")); 170 | if ((event_flags & ANCS_EVT_FLAG_SILENT) == ANCS_EVT_FLAG_SILENT) 171 | Serial.print(F("SILENT ")); 172 | if ((event_flags & ANCS_EVT_FLAG_IMPORTANT) == ANCS_EVT_FLAG_IMPORTANT) 173 | Serial.print(F("IMPORTANT ")); 174 | debug_println(F("; ")); 175 | } else 176 | debug_println(F("NO FLAGS; ")); 177 | 178 | Serial.print(F("Category: ")); 179 | switch (category_id) { 180 | case ANCS_CATEGORY_OTHER: 181 | Serial.println(F("Other")); 182 | #ifndef DROP_NOTIF_OTHER 183 | break; 184 | #else 185 | debug_println(F(": ignored")); 186 | return; 187 | #endif 188 | case ANCS_CATEGORY_INCOMING_CALL: 189 | Serial.println(F("Incoming call")); 190 | #ifndef DROP_NOTIF_INCOMING_CALL 191 | break; 192 | #else 193 | debug_println(F(": ignored")); 194 | return; 195 | #endif 196 | case ANCS_CATEGORY_MISSED_CALL: 197 | Serial.println(F("Missed call")); 198 | #ifndef DROP_NOTIF_MISSED_CALL 199 | break; 200 | #else 201 | debug_println(F(": ignored")); 202 | return; 203 | #endif 204 | case ANCS_CATEGORY_VOICEMAIL: 205 | Serial.println(F("Voicemail")); 206 | #ifndef DROP_NOTIF_VOICEMAIL_CALL 207 | break; 208 | #else 209 | debug_println(F(": ignored")); 210 | return; 211 | #endif 212 | case ANCS_CATEGORY_SOCIAL: 213 | Serial.println(F("Social")); 214 | #ifndef DROP_NOTIF_SOCIAL 215 | break; 216 | #else 217 | debug_println(F(": ignored")); 218 | return; 219 | #endif 220 | case ANCS_CATEGORY_SCHEDULE: 221 | debug_print(F("Schedule")); 222 | #ifndef DROP_NOTIF_SCHEDULE 223 | break; 224 | #else 225 | debug_println(F(": ignored")); 226 | return; 227 | #endif 228 | case ANCS_CATEGORY_EMAIL: 229 | debug_print(F("Email")); 230 | #ifndef DROP_NOTIF_EMAIL 231 | break; 232 | #else 233 | debug_println(F(": ignored")); 234 | return; 235 | #endif 236 | case ANCS_CATEGORY_NEWS: 237 | debug_print(F("News")); 238 | #ifndef DROP_NOTIF_NEWS 239 | break; 240 | #else 241 | debug_println(F(": ignored")); 242 | return; 243 | #endif 244 | case ANCS_CATEGORY_HEALTH_FITNESS: 245 | debug_print(F("Health & Fitness")); 246 | #ifndef DROP_NOTIF_HEALTH_FITNESS 247 | break; 248 | #else 249 | debug_println(F(": ignored")); 250 | return; 251 | #endif 252 | case ANCS_CATEGORY_BUSINESS_FINANCE: 253 | debug_print(F("Business & Finance")); 254 | #ifndef DROP_NOTIF_BUSINESS_FINANCE 255 | break; 256 | #else 257 | debug_println(F(": ignored")); 258 | return; 259 | #endif 260 | case ANCS_CATEGORY_LOCATION: 261 | debug_print(F("Location")); 262 | #ifndef DROP_NOTIF_CATEGORY_LOCATION 263 | break; 264 | #else 265 | debug_println(F(": ignored")); 266 | return; 267 | #endif 268 | case ANCS_CATEGORY_ENTERTAINMENT: 269 | debug_print(F("Entertainment")); 270 | #ifndef DROP_NOTIF_ENTERTAINMENT 271 | break; 272 | #else 273 | debug_println(F(": ignored")); 274 | return; 275 | #endif 276 | default: 277 | Serial.println(F("UNKOWN CATEGORY")); 278 | return; 279 | } 280 | debug_print(F("[")); 281 | debug_print(category_count); 282 | debug_print(F("] -> ")); 283 | switch (event_id) { 284 | case ANCS_EVT_NOTIFICATION_ADDED: 285 | debug_println(F("ADDED")); 286 | if (ancs_notification_list_get(nid) == NULL) { 287 | 288 | ancs_notification_list_push(notif); 289 | 290 | debug_print(F("Adding notification to cache: ")); 291 | debug_println(nid); 292 | ancs_get_notification_data(nid); 293 | } else { 294 | debug_print(F("Notification already in cache: ")); 295 | debug_println(nid); 296 | } 297 | break; 298 | case ANCS_EVT_NOTIFICATION_MODIFIED: 299 | debug_println(F("MODIFIED")); 300 | ancs_get_notification_data(nid); 301 | break; 302 | case ANCS_EVT_NOTIFICATION_REMOVED: 303 | debug_println(F("REMOVED")); 304 | //ancs_notifications_remove_hook(notif); 305 | break; 306 | } 307 | } 308 | 309 | -------------------------------------------------------------------------------- /ancs_notification_source.h: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | 5 | #include "ancs_base.h" 6 | #include "ancs_notification.h" 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef void (*apply_cb)(ancs_notification_t* notif); 13 | 14 | extern void ancs_notifications_use_hook(ancs_notification_t* notif); 15 | extern void ancs_notifications_remove_hook(ancs_notification_t* notif); 16 | 17 | void ancs_notification_source_parser(const uint8_t* buffer); 18 | void ancs_get_notification_data(uint32_t uid); 19 | void ancs_run(); 20 | void ancs_init(); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /examples/ancs_lcd/ancs_lcd.ino: -------------------------------------------------------------------------------- 1 | // 2 | // ancs_lcd.ino 3 | // 4 | // 5 | // Created by Luke Berndt on 8/24/14. 6 | // 7 | // 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | 16 | 17 | #define LCD_SIZE 16 18 | LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7); 19 | 20 | Notif notif(12,11); 21 | ancs_notification_t* current_notif = NULL; 22 | uint8_t message_char = 0; 23 | 24 | 25 | 26 | 27 | // Pins in use 28 | #define BUTTON_ADC_PIN A0 // A0 is the button ADC input 29 | #define LCD_BACKLIGHT_PIN 10 // D10 controls LCD backlight 30 | // ADC readings expected for the 5 buttons on the ADC input 31 | #define RIGHT_10BIT_ADC 0 // right 32 | #define UP_10BIT_ADC 145 // up 33 | #define DOWN_10BIT_ADC 329 // down 34 | #define LEFT_10BIT_ADC 505 // left 35 | #define SELECT_10BIT_ADC 741 // right 36 | #define BUTTONHYSTERESIS 10 // hysteresis for valid button sensing window 37 | //return values for ReadButtons() 38 | #define BUTTON_NONE 0 // 39 | #define BUTTON_RIGHT 1 // 40 | #define BUTTON_UP 2 // 41 | #define BUTTON_DOWN 3 // 42 | #define BUTTON_LEFT 4 // 43 | #define BUTTON_SELECT 5 // 44 | //some example macros with friendly labels for LCD backlight/pin control, tested and can be swapped into the example code as you like 45 | #define LCD_BACKLIGHT_OFF() digitalWrite( LCD_BACKLIGHT_PIN, LOW ) 46 | #define LCD_BACKLIGHT_ON() digitalWrite( LCD_BACKLIGHT_PIN, HIGH ) 47 | #define LCD_BACKLIGHT(state) { if( state ){digitalWrite( LCD_BACKLIGHT_PIN, HIGH );}else{digitalWrite( LCD_BACKLIGHT_PIN, LOW );} } 48 | 49 | byte antenna_char[8] = { 50 | B00000, 51 | B00000, 52 | B00000, 53 | B00100, 54 | B00100, 55 | B00100, 56 | B01110, 57 | }; 58 | 59 | 60 | 61 | byte connected_char[8] = { 62 | B00000, 63 | B01110, 64 | B10001, 65 | B00100, 66 | B00100, 67 | B00100, 68 | B01110, 69 | }; 70 | 71 | 72 | byte disconnected_char[8] = { 73 | B00000, 74 | B01010, 75 | B00100, 76 | B01010, 77 | B00100, 78 | B00100, 79 | B01110, 80 | }; 81 | 82 | byte text_char[8] = { 83 | B00000, 84 | B01110, 85 | B01110, 86 | B01010, 87 | B01010, 88 | B01110, 89 | B00000, 90 | }; 91 | 92 | byte email_char[8] = { 93 | B00000, 94 | B11111, 95 | B11011, 96 | B10101, 97 | B10001, 98 | B11111, 99 | B00000, 100 | 101 | }; 102 | 103 | char wait = ' '; 104 | unsigned long screen_update_timer = 0; 105 | unsigned long backlight_timer = 0; 106 | boolean backlight = false; 107 | boolean connected = false; 108 | 109 | void ancs_connected() { 110 | backlight = 1; 111 | LCD_BACKLIGHT_ON(); 112 | backlight_timer = millis(); 113 | connected = true; 114 | } 115 | void ancs_disconnected() { 116 | connected = false; 117 | } 118 | 119 | void ancs_reset() { 120 | connected = false; 121 | lcd.clear(); 122 | lcd.setCursor(0,0); 123 | lcd.print(" Bond Cleared "); 124 | lcd.setCursor(0,1); 125 | lcd.print("Please Reset"); 126 | } 127 | 128 | void ancs_notifications(ancs_notification_t* notif) { 129 | 130 | 131 | /* 132 | Serial.print (F("[")); 133 | if ((notif->flags & ANCS_EVT_FLAG_SILENT) == ANCS_EVT_FLAG_SILENT) 134 | Serial.print(F("-")); 135 | else if ((notif->flags & ANCS_EVT_FLAG_IMPORTANT) == ANCS_EVT_FLAG_IMPORTANT) 136 | Serial.print(F("!")); 137 | else 138 | Serial.print(" "); 139 | Serial.print (F("] ")); 140 | Serial.print(F("Notif #")); Serial.print( notif->uid); Serial.print( F(" ; from: '")); 141 | #ifdef ANCS_USE_APP 142 | Serial.print( notif->app); 143 | #endif 144 | Serial.println( F("'")); 145 | Serial.print(F(" category: ")); 146 | switch (notif->category) { 147 | case ANCS_CATEGORY_INCOMING_CALL: 148 | //Serial.println(F("incoming call")); 149 | break; 150 | case ANCS_CATEGORY_MISSED_CALL: 151 | //Serial.println(F("missed call")); 152 | break; 153 | case ANCS_CATEGORY_VOICEMAIL: 154 | // Serial.println(F("voicemail call")); 155 | break; 156 | case ANCS_CATEGORY_SOCIAL: 157 | // Serial.println(F("social msg")); 158 | break; 159 | case ANCS_CATEGORY_OTHER: 160 | // Serial.println(F("other")); 161 | break; 162 | case ANCS_CATEGORY_SCHEDULE: 163 | // Serial.println(F("schedule")); 164 | break; 165 | case ANCS_CATEGORY_EMAIL: 166 | // Serial.println(F("email")); 167 | break; 168 | case ANCS_CATEGORY_NEWS : 169 | // Serial.println(F("news")); 170 | break; 171 | case ANCS_CATEGORY_HEALTH_FITNESS: 172 | // Serial.println(F("health & fitness")); 173 | break; 174 | case ANCS_CATEGORY_BUSINESS_FINANCE: 175 | // Serial.println(F("business & finance")); 176 | break; 177 | case ANCS_CATEGORY_LOCATION: 178 | // Serial.println(F("location")); 179 | break; 180 | case ANCS_CATEGORY_ENTERTAINMENT: 181 | // Serial.println(F("entertainment")); 182 | break; 183 | default: 184 | Serial.print(F("unknown: ")); 185 | Serial.println(notif->category, DEC); 186 | break; 187 | //return; 188 | } 189 | Serial.print(F(" title: '")); Serial.print( notif->title ); Serial.println("'"); 190 | #ifdef ANCS_USE_SUBTITLE 191 | Serial.print(F(" subtitle: '")); Serial.print( notif->subtitle ); Serial.println("'"); 192 | #endif 193 | Serial.print(F(" message: '")); Serial.print( notif->message ); Serial.println("'");*/ 194 | 195 | 196 | current_notif = notif; 197 | message_char = 0; 198 | backlight = 1; 199 | LCD_BACKLIGHT_ON(); 200 | backlight_timer = millis(); 201 | 202 | } 203 | 204 | void eepromWrite(int address, uint8_t value) 205 | { 206 | eeprom_write_byte((unsigned char *) address, value); 207 | } 208 | 209 | void setup(void) 210 | { 211 | /* 212 | Serial.begin(115200); 213 | //Wait until the serial port is available (useful only for the Leonardo) 214 | //As the Leonardo board is not reseted every time you open the Serial Monitor 215 | 216 | #if defined (__AVR_ATmega32U4__) 217 | while(!Serial) 218 | {} 219 | #endif*/ 220 | 221 | //If things get really crazy, uncomment this line. It wipes the saved EEPROM information for the Nordic chip. Good to do this if the services.h file gets updated. 222 | //After it is wiped, comment and reupload. 223 | //eepromWrite(0, 0xFF); 224 | 225 | Serial.println(F("Arduino setup")); 226 | notif.setup(); 227 | notif.set_notification_callback_handle(ancs_notifications); 228 | notif.set_connect_callback_handle(ancs_connected); 229 | notif.set_disconnect_callback_handle(ancs_disconnected); 230 | notif.set_reset_callback_handle(ancs_reset); 231 | lcd.begin(LCD_SIZE, 2); 232 | digitalWrite( LCD_BACKLIGHT_PIN, HIGH ); //backlight control pin D3 is high (on) 233 | pinMode( LCD_BACKLIGHT_PIN, OUTPUT ); //D3 is an output 234 | lcd.createChar(0, antenna_char); 235 | lcd.createChar(1, connected_char); 236 | lcd.createChar(2, disconnected_char); 237 | lcd.createChar(3, text_char); 238 | lcd.createChar(4, email_char); 239 | 240 | lcd.clear(); 241 | lcd.setCursor(0,0); 242 | lcd.print(" Apple iOS "); 243 | lcd.setCursor(0,1); 244 | lcd.print(" Notifications "); 245 | 246 | } 247 | 248 | void update_lcd() { 249 | 250 | 251 | 252 | lcd.clear(); 253 | 254 | 255 | 256 | if (current_notif) { 257 | 258 | lcd.setCursor(2,0); 259 | lcd.print(current_notif->title); 260 | lcd.setCursor(0,0); 261 | 262 | 263 | switch (current_notif->category) { 264 | case ANCS_CATEGORY_INCOMING_CALL: 265 | //Serial.println(F("incoming call")); 266 | break; 267 | case ANCS_CATEGORY_MISSED_CALL: 268 | //Serial.println(F("missed call")); 269 | break; 270 | case ANCS_CATEGORY_VOICEMAIL: 271 | // Serial.println(F("voicemail call")); 272 | break; 273 | case ANCS_CATEGORY_SOCIAL: 274 | lcd.print((char)3); 275 | break; 276 | case ANCS_CATEGORY_OTHER: 277 | // Serial.println(F("other")); 278 | break; 279 | case ANCS_CATEGORY_SCHEDULE: 280 | // Serial.println(F("schedule")); 281 | break; 282 | case ANCS_CATEGORY_EMAIL: 283 | lcd.print((char)4); 284 | break; 285 | } 286 | 287 | 288 | 289 | lcd.setCursor(0,1); 290 | lcd.print(&(current_notif->message[message_char])); 291 | if (strlen(current_notif->message) >= LCD_SIZE) { 292 | message_char++; 293 | if (message_char == strlen(current_notif->message)) { 294 | message_char = 0; 295 | } 296 | } 297 | } 298 | 299 | lcd.setCursor(15,0); 300 | lcd.print(wait); 301 | if (wait == 0) 302 | { 303 | if (connected){ 304 | wait = 1; 305 | }else { 306 | wait = 2; 307 | } 308 | } else { 309 | wait=0; 310 | } 311 | screen_update_timer = millis(); 312 | } 313 | 314 | void loop() 315 | { 316 | if((millis() - screen_update_timer) > 1000) { 317 | update_lcd(); 318 | } 319 | if(backlight && ((millis() - backlight_timer) > 60000)) { 320 | backlight = 0; 321 | LCD_BACKLIGHT_OFF(); 322 | } 323 | 324 | notif.ReadNotifications(); 325 | } 326 | -------------------------------------------------------------------------------- /linked_list.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINKED_LIST_H_ 2 | #define _LINKED_LIST_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" { 12 | 13 | /** @brief structure for a node in the linked list used for the fifo */ 14 | typedef struct linked_list_node_t { 15 | struct linked_list_node_t* next; 16 | uint8_t* data; 17 | size_t size; 18 | } linked_list_node_t; 19 | 20 | /** @brief structure for the fifo queue 21 | * @details first is the entry point of the structure, 22 | * _last is used to expand the fifo 23 | */ 24 | typedef struct linked_list_t { 25 | linked_list_node_t* first; 26 | linked_list_node_t* _last; 27 | } linked_list_t; 28 | 29 | linked_list_node_t* fifo_create_node() { 30 | linked_list_node_t* node = (linked_list_node_t*)malloc(sizeof(linked_list_node_t)); 31 | node->next = NULL; 32 | node->data = NULL; 33 | node->size = 0; 34 | return node; 35 | } 36 | 37 | /** @brief create a new fifo structure 38 | * @return an empty fifo structure 39 | */ 40 | linked_list_t* fifo_create() { 41 | linked_list_t* ll = (linked_list_t*)malloc(sizeof(linked_list_t)); 42 | ll->first = NULL; 43 | ll->_last = NULL; 44 | return ll; 45 | } 46 | 47 | /** @brief push a byte array into the fifo 48 | * @param ll the linked list structure used as fifo 49 | * @param data bytearray to push 50 | */ 51 | void fifo_push(linked_list_t* ll, uint8_t* data, size_t len) { 52 | // linked list is empty, populate first node 53 | if (ll->first == NULL) { 54 | ll->first = fifo_create_node(); 55 | ll->first->data = (uint8_t*)malloc(len); 56 | memcpy(ll->first->data, data, len); 57 | ll->first->size = len; 58 | ll->first->next = NULL; 59 | ll->_last = ll->first; 60 | // otherwise append a new node 61 | } else { 62 | ll->_last->next = fifo_create_node(); 63 | ll->_last = ll->_last->next; 64 | ll->_last->data = (uint8_t*)malloc(len); 65 | memcpy(ll->_last->data, data, len); 66 | ll->_last->size = len; 67 | ll->_last->next = NULL; 68 | } 69 | } 70 | 71 | /** @brief pops out a byte array from the fifo 72 | * @details removes the bottom of the queue and returns it 73 | * @param ll the linked list structure used as fifo 74 | * @return bytearray poped out 75 | */ 76 | size_t fifo_pop(linked_list_t* ll, uint8_t** data) { 77 | if (ll->first == NULL) { 78 | (*data) = NULL; 79 | return 0; 80 | } 81 | *data = ll->first->data; 82 | size_t len = ll->first->size; 83 | linked_list_node_t* tmp = ll->first; 84 | ll->first = ll->first->next; 85 | free(tmp); 86 | return len; 87 | } 88 | 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /my_services.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Nordic Semiconductor ASA 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * - Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * - The name of Nordic Semiconductor ASA may not be used to endorse or promote 16 | * products derived from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * This file is autogenerated by nRFgo Studio 1.16.1.3119 32 | */ 33 | 34 | #ifndef SETUP_MESSAGES_H__ 35 | #define SETUP_MESSAGES_H__ 36 | 37 | #include "hal_platform.h" 38 | #include "aci.h" 39 | 40 | 41 | #define SETUP_ID 1 42 | #define SETUP_FORMAT 3 /** nRF8001 D */ 43 | #define ACI_DYNAMIC_DATA_SIZE 143 44 | 45 | /* Service: GATT - Characteristic: Service Changed - Pipe: TX_ACK */ 46 | #define PIPE_GATT_SERVICE_CHANGED_TX_ACK 1 47 | #define PIPE_GATT_SERVICE_CHANGED_TX_ACK_MAX_SIZE 4 48 | 49 | /* Service: ANCS - Characteristic: Notification Source - Pipe: RX */ 50 | #define PIPE_ANCS_NOTIFICATION_SOURCE_RX 2 51 | #define PIPE_ANCS_NOTIFICATION_SOURCE_RX_MAX_SIZE 1 52 | 53 | /* Service: ANCS - Characteristic: Data Source - Pipe: RX */ 54 | #define PIPE_ANCS_DATA_SOURCE_RX 3 55 | #define PIPE_ANCS_DATA_SOURCE_RX_MAX_SIZE 60 56 | 57 | /* Service: ANCS - Characteristic: Control Point - Pipe: TX_ACK */ 58 | #define PIPE_ANCS_CONTROL_POINT_TX_ACK 4 59 | #define PIPE_ANCS_CONTROL_POINT_TX_ACK_MAX_SIZE 1 60 | 61 | 62 | #define NUMBER_OF_PIPES 4 63 | 64 | #define SERVICES_PIPE_TYPE_MAPPING_CONTENT {\ 65 | {ACI_STORE_LOCAL, ACI_TX_ACK}, \ 66 | {ACI_STORE_REMOTE, ACI_RX}, \ 67 | {ACI_STORE_REMOTE, ACI_RX}, \ 68 | {ACI_STORE_REMOTE, ACI_TX_ACK}, \ 69 | } 70 | 71 | #define GAP_PPCP_MAX_CONN_INT 0x320 /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 72 | #define GAP_PPCP_MIN_CONN_INT 0x190 /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 73 | #define GAP_PPCP_SLAVE_LATENCY 0 74 | #define GAP_PPCP_CONN_TIMEOUT 0x258 /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */ 75 | 76 | #define NB_SETUP_MESSAGES 19 77 | #define SETUP_MESSAGES_CONTENT {\ 78 | {0x00,\ 79 | {\ 80 | 0x07,0x06,0x00,0x00,0x03,0x02,0x41,0xfe,\ 81 | },\ 82 | },\ 83 | {0x00,\ 84 | {\ 85 | 0x1f,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x02,0x02,0x01,0x04,0x05,0x01,0x01,0x00,0x00,0x06,0x00,0x04,\ 86 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 87 | },\ 88 | },\ 89 | {0x00,\ 90 | {\ 91 | 0x1f,0x06,0x10,0x1c,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf4,0x31,0x02,\ 92 | 0x00,0x00,0x08,0x10,0x00,0x00,0x08,0x10,0x03,0x90,0x04,0x64,\ 93 | },\ 94 | },\ 95 | {0x00,\ 96 | {\ 97 | 0x1f,0x06,0x10,0x38,0x02,0xff,0x02,0x58,0x0a,0x05,0x00,0x00,0x08,0x10,0x00,0x00,0x08,0x10,0x00,0x00,\ 98 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 99 | },\ 100 | },\ 101 | {0x00,\ 102 | {\ 103 | 0x05,0x06,0x10,0x54,0x01,0x00,\ 104 | },\ 105 | },\ 106 | {0x00,\ 107 | {\ 108 | 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\ 109 | 0x02,0x28,0x03,0x01,0x02,0x03,0x00,0x00,0x2a,0x04,0x04,0x14,\ 110 | },\ 111 | },\ 112 | {0x00,\ 113 | {\ 114 | 0x1f,0x06,0x20,0x1c,0x05,0x00,0x03,0x2a,0x00,0x01,0x4e,0x6f,0x74,0x69,0x66,0x63,0x73,0x65,0x6d,0x69,\ 115 | 0x2e,0x63,0x6f,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,\ 116 | },\ 117 | },\ 118 | {0x00,\ 119 | {\ 120 | 0x1f,0x06,0x20,0x38,0x05,0x05,0x00,0x04,0x28,0x03,0x01,0x02,0x05,0x00,0x01,0x2a,0x06,0x04,0x03,0x02,\ 121 | 0x00,0x05,0x2a,0x01,0x01,0x40,0x02,0x04,0x04,0x05,0x05,0x00,\ 122 | },\ 123 | },\ 124 | {0x00,\ 125 | {\ 126 | 0x1f,0x06,0x20,0x54,0x06,0x28,0x03,0x01,0x02,0x07,0x00,0x04,0x2a,0x06,0x04,0x09,0x08,0x00,0x07,0x2a,\ 127 | 0x04,0x01,0x90,0x01,0x20,0x03,0x00,0x00,0x58,0x02,0x04,0x04,\ 128 | },\ 129 | },\ 130 | {0x00,\ 131 | {\ 132 | 0x1f,0x06,0x20,0x70,0x02,0x02,0x00,0x08,0x28,0x00,0x01,0x01,0x18,0x04,0x04,0x05,0x05,0x00,0x09,0x28,\ 133 | 0x03,0x01,0x22,0x0a,0x00,0x05,0x2a,0x26,0x0c,0x05,0x04,0x00,\ 134 | },\ 135 | },\ 136 | {0x00,\ 137 | {\ 138 | 0x17,0x06,0x20,0x8c,0x0a,0x2a,0x05,0x01,0x00,0x00,0x00,0x00,0x46,0x34,0x03,0x02,0x00,0x0b,0x29,0x02,\ 139 | 0x01,0x00,0x00,0x00,\ 140 | },\ 141 | },\ 142 | {0x00,\ 143 | {\ 144 | 0x0d,0x06,0x30,0x00,0xf4,0x31,0x02,0x01,0x03,0x18,0x01,0x01,0x04,0x01,\ 145 | },\ 146 | },\ 147 | {0x00,\ 148 | {\ 149 | 0x1f,0x06,0x40,0x00,0x2a,0x05,0x01,0x00,0x04,0x04,0x00,0x0a,0x00,0x0b,0x12,0x0d,0x03,0x00,0x08,0x04,\ 150 | 0x00,0x00,0x00,0x00,0xc6,0xe9,0x04,0x00,0x08,0x04,0x00,0x00,\ 151 | },\ 152 | },\ 153 | {0x00,\ 154 | {\ 155 | 0x19,0x06,0x40,0x1c,0x00,0x00,0xd8,0xf3,0x05,0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x2a,0x05,0x01,0x00,\ 156 | 0x10,0x04,0x00,0x00,0x00,0x00,\ 157 | },\ 158 | },\ 159 | {0x00,\ 160 | {\ 161 | 0x1f,0x06,0x50,0x00,0xd0,0x00,0x2d,0x12,0x1e,0x4b,0x0f,0xa4,0x99,0x4e,0xce,0xb5,0x00,0x00,0x05,0x79,\ 162 | 0xbd,0x1d,0xa2,0x99,0xe6,0x25,0x58,0x8c,0xd9,0x42,0x01,0x63,\ 163 | },\ 164 | },\ 165 | {0x00,\ 166 | {\ 167 | 0x1f,0x06,0x50,0x1c,0x00,0x00,0xbf,0x9f,0xfb,0x7b,0x7c,0xce,0x6a,0xb3,0x44,0xbe,0xb5,0x4b,0xd6,0x24,\ 168 | 0x00,0x00,0xea,0x22,0xd9,0xd9,0xaa,0xfd,0xbd,0x9b,0x21,0x98,\ 169 | },\ 170 | },\ 171 | {0x00,\ 172 | {\ 173 | 0x0b,0x06,0x50,0x38,0xa8,0x49,0xe1,0x45,0x00,0x00,0xd1,0x69,\ 174 | },\ 175 | },\ 176 | {0x00,\ 177 | {\ 178 | 0x12,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 179 | },\ 180 | },\ 181 | {0x00,\ 182 | {\ 183 | 0x06,0x06,0xf0,0x00,0x03,0x71,0x71,\ 184 | },\ 185 | },\ 186 | } 187 | 188 | #endif 189 | -------------------------------------------------------------------------------- /notif.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // notif.cpp 3 | // 4 | // 5 | // Created by Luke Berndt on 8/23/14. 6 | // 7 | // 8 | //#include "Arduino.h" 9 | 10 | #include "notif.h" 11 | 12 | //#define DEBUG1 13 | 14 | #include "utilities.h" 15 | #include "services.h" 16 | #include "pack_lib.h" 17 | #include 18 | 19 | extern boolean command_send_enable; 20 | 21 | void __ble_assert(const char *file, uint16_t line) 22 | { 23 | Serial.print(F("ERROR ")); 24 | Serial.print(file); 25 | Serial.print(F(": ")); 26 | Serial.print(line); 27 | Serial.print(F("\n")); 28 | while(1); 29 | } 30 | 31 | 32 | uint8_t eeprom_read(int address) 33 | { 34 | return eeprom_read_byte((unsigned char *) address); 35 | } 36 | 37 | void eeprom_write(int address, uint8_t value) 38 | { 39 | eeprom_write_byte((unsigned char *) address, value); 40 | } 41 | 42 | 43 | #ifdef SERVICES_PIPE_TYPE_MAPPING_CONTENT 44 | static services_pipe_type_mapping_t 45 | services_pipe_type_mapping[NUMBER_OF_PIPES] = SERVICES_PIPE_TYPE_MAPPING_CONTENT; 46 | #else 47 | #define NUMBER_OF_PIPES 0 48 | static services_pipe_type_mapping_t * services_pipe_type_mapping = NULL; 49 | #endif 50 | 51 | 52 | /* Store the setup for the nRF8001 in the flash of the AVR to save on RAM */ 53 | const static hal_aci_data_t setup_msgs[NB_SETUP_MESSAGES] PROGMEM = SETUP_MESSAGES_CONTENT; 54 | 55 | static struct aci_state_t aci_state; 56 | 57 | static hal_aci_evt_t aci_data; 58 | 59 | 60 | void Notif::print_pipes(aci_evt_t* aci_evt) { 61 | Serial.print("Here are the available open pipes: "); 62 | for (uint8_t i=1; i<=NUMBER_OF_PIPES; ++i) 63 | if (lib_aci_is_pipe_available(&aci_state, i)) { 64 | Serial.print(i); 65 | Serial.print(", "); 66 | } 67 | Serial.println(""); 68 | Serial.print(F("Here are the available closed pipes: ")); 69 | for (uint8_t i=1; i<=NUMBER_OF_PIPES; ++i) 70 | if (lib_aci_is_pipe_closed(&aci_state, i)) { 71 | Serial.print(i); 72 | Serial.print(", "); 73 | } 74 | Serial.println(""); 75 | } 76 | 77 | void Notif::set_notification_callback_handle(void (*fptr)(ancs_notification_t* notif)) { 78 | notification_callback_handle = fptr; 79 | } 80 | 81 | void Notif::set_connect_callback_handle(void (*fptr)(void)) { 82 | connect_callback_handle = fptr; 83 | } 84 | 85 | void Notif::set_disconnect_callback_handle(void (*fptr)(void)) { 86 | disconnect_callback_handle = fptr; 87 | } 88 | 89 | void Notif::set_reset_callback_handle(void (*fptr)(void)) { 90 | reset_callback_handle = fptr; 91 | } 92 | 93 | /* 94 | Read the Dymamic data from the EEPROM and send then as ACI Write Dynamic Data to the nRF8001 95 | This will restore the nRF8001 to the situation when the Dynamic Data was Read out 96 | */ 97 | aci_status_code_t Notif::bond_data_restore( uint8_t eeprom_status, bool *bonded_first_time_state) 98 | { 99 | aci_evt_t *aci_evt; 100 | uint8_t eeprom_offset_read = 1; 101 | uint8_t write_dyn_num_msgs = 0; 102 | uint8_t len =0; 103 | 104 | // Get the number of messages to write for the eeprom_status 105 | write_dyn_num_msgs = eeprom_status & 0x7F; 106 | 107 | //Read from the EEPROM 108 | while(1) 109 | { 110 | len = eeprom_read(eeprom_offset_read); 111 | eeprom_offset_read++; 112 | aci_cmd.buffer[0] = len; 113 | 114 | for (uint8_t i=1; i<=len; i++) 115 | { 116 | aci_cmd.buffer[i] = eeprom_read(eeprom_offset_read); 117 | eeprom_offset_read++; 118 | } 119 | //Send the ACI Write Dynamic Data 120 | if (!hal_aci_tl_send(&aci_cmd)) 121 | { 122 | Serial.println(F("bond_data_restore: Cmd Q Full")); 123 | return ACI_STATUS_ERROR_INTERNAL; 124 | } 125 | 126 | //Spin in the while loop waiting for an event 127 | while (1) 128 | { 129 | if (lib_aci_event_get(&aci_state, &aci_data)) 130 | { 131 | aci_evt = &aci_data.evt; 132 | 133 | if (ACI_EVT_CMD_RSP != aci_evt->evt_opcode) 134 | { 135 | //Got something other than a command response evt -> Error 136 | Serial.print(F("bond_data_restore: Expected cmd rsp evt. Got: 0x")); 137 | Serial.println(aci_evt->evt_opcode, HEX); 138 | return ACI_STATUS_ERROR_INTERNAL; 139 | } 140 | else 141 | { 142 | write_dyn_num_msgs--; 143 | 144 | //ACI Evt Command Response 145 | if (ACI_STATUS_TRANSACTION_COMPLETE == aci_evt->params.cmd_rsp.cmd_status) 146 | { 147 | //Set the state variables correctly 148 | *bonded_first_time_state = false; 149 | aci_state.bonded = ACI_BOND_STATUS_SUCCESS; 150 | 151 | delay(10); 152 | 153 | return ACI_STATUS_TRANSACTION_COMPLETE; 154 | } 155 | if (0 >= write_dyn_num_msgs) 156 | { 157 | //should have returned earlier 158 | return ACI_STATUS_ERROR_INTERNAL; 159 | } 160 | if (ACI_STATUS_TRANSACTION_CONTINUE == aci_evt->params.cmd_rsp.cmd_status) 161 | { 162 | //break and write the next ACI Write Dynamic Data 163 | break; 164 | } 165 | } 166 | } 167 | } 168 | } 169 | } 170 | 171 | 172 | /* 173 | This function is specific to the atmega328 174 | @params ACI Command Response Evt received from the Read Dynmaic Data 175 | */ 176 | void Notif::bond_data_store(aci_evt_t *evt) 177 | { 178 | static int eeprom_write_offset = 1; 179 | 180 | //Write it to non-volatile storage 181 | eeprom_write( eeprom_write_offset, evt->len -2 ); 182 | eeprom_write_offset++; 183 | 184 | eeprom_write( eeprom_write_offset, ACI_CMD_WRITE_DYNAMIC_DATA); 185 | eeprom_write_offset++; 186 | 187 | for (uint8_t i=0; i< (evt->len-3); i++) 188 | { 189 | eeprom_write( eeprom_write_offset, evt->params.cmd_rsp.params.padding[i]); 190 | eeprom_write_offset++; 191 | } 192 | } 193 | 194 | bool Notif::bond_data_read_store() 195 | { 196 | /* 197 | The size of the dynamic data for a specific Bluetooth Low Energy configuration 198 | is present in the ublue_setup.gen.out.txt generated by the nRFgo studio as "dynamic data size". 199 | */ 200 | bool status = false; 201 | aci_evt_t * aci_evt = NULL; 202 | uint8_t read_dyn_num_msgs = 0; 203 | 204 | //Start reading the dynamic data 205 | lib_aci_read_dynamic_data(); 206 | read_dyn_num_msgs++; 207 | 208 | while (1) 209 | { 210 | if (true == lib_aci_event_get(&aci_state, &aci_data)) 211 | { 212 | aci_evt = &aci_data.evt; 213 | 214 | if (ACI_EVT_CMD_RSP != aci_evt->evt_opcode ) 215 | { 216 | //Got something other than a command response evt -> Error 217 | status = false; 218 | break; 219 | } 220 | 221 | if (ACI_STATUS_TRANSACTION_COMPLETE == aci_evt->params.cmd_rsp.cmd_status) 222 | { 223 | //Store the contents of the command response event in the EEPROM 224 | //(len, cmd, seq-no, data) : cmd ->Write Dynamic Data so it can be used directly 225 | bond_data_store(aci_evt); 226 | 227 | //Set the flag in the EEPROM that the contents of the EEPROM is valid 228 | eeprom_write(0, 0x80|read_dyn_num_msgs ); 229 | //Finished with reading the dynamic data 230 | status = true; 231 | 232 | break; 233 | } 234 | 235 | if (!(ACI_STATUS_TRANSACTION_CONTINUE == aci_evt->params.cmd_rsp.cmd_status)) 236 | { 237 | //We failed the read dymanic data 238 | //Set the flag in the EEPROM that the contents of the EEPROM is invalid 239 | eeprom_write(0, 0xFF); 240 | 241 | status = false; 242 | break; 243 | } 244 | else 245 | { 246 | //Store the contents of the command response event in the EEPROM 247 | // (len, cmd, seq-no, data) : cmd ->Write Dynamic Data so it can be used directly when re-storing the dynamic data 248 | bond_data_store(aci_evt); 249 | 250 | //Read the next dynamic data message 251 | lib_aci_read_dynamic_data(); 252 | read_dyn_num_msgs++; 253 | } 254 | } 255 | } 256 | return status; 257 | } 258 | 259 | void Notif::DeviceStarted( aci_evt_t *aci_evt) { 260 | 261 | aci_state.data_credit_total = aci_evt->params.device_started.credit_available; 262 | switch(aci_evt->params.device_started.device_mode) 263 | { 264 | case ACI_DEVICE_SETUP: 265 | /** 266 | When the device is in the setup mode 267 | */ 268 | debug_println(F("Evt Device Started: Setup")); 269 | setup_required = true; 270 | break; 271 | 272 | case ACI_DEVICE_STANDBY: 273 | debug_println(F("Evt Device Started: Standby")); 274 | if (aci_evt->params.device_started.hw_error) 275 | { 276 | delay(20); //Magic number used to make sure the HW error event is handled correctly. 277 | } 278 | else 279 | { 280 | //Manage the bond in EEPROM of the AVR 281 | { 282 | uint8_t eeprom_status = 0; 283 | eeprom_status = eeprom_read(0); 284 | if (eeprom_status != 0xFF) 285 | {/* 286 | Serial.println(F("Previous Bond present. Restoring")); 287 | Serial.println(F("Using existing bond stored in EEPROM.")); 288 | Serial.println(F(" To delete the bond stored in EEPROM, connect Pin 6 to 3.3v and Reset.")); 289 | Serial.println(F(" Make sure that the bond on the phone/PC is deleted as well."));*/ 290 | //We must have lost power and restarted and must restore the bonding infromation using the ACI Write Dynamic Data 291 | if (ACI_STATUS_TRANSACTION_COMPLETE == bond_data_restore( eeprom_status, &bonded_first_time)) 292 | { 293 | Serial.println(F("Bond information loaded into nRF8001")); 294 | } 295 | else 296 | { 297 | Serial.println(F("Bond restore failed. Delete the bond and try again.")); 298 | } 299 | } 300 | } 301 | 302 | // Start bonding as all proximity devices need to be bonded to be usable 303 | if (ACI_BOND_STATUS_SUCCESS != aci_state.bonded) 304 | { 305 | lib_aci_bond(180/* in seconds */, 0x0050 /* advertising interval 50ms*/); 306 | Serial.println(F("No Bond present in EEPROM.")); 307 | Serial.println(F("Advertising started : Waiting to be connected and bonded")); 308 | } 309 | else 310 | { 311 | //connect to an already bonded device 312 | //Use lib_aci_direct_connect for faster re-connections with PC, not recommended to use with iOS/OS X 313 | lib_aci_connect(100/* in seconds */, 0x0020 /* advertising interval 20ms*/); 314 | Serial.println(F("Already bonded : Advertising started : Waiting to be connected")); 315 | } 316 | } 317 | break; 318 | } 319 | 320 | } 321 | void Notif::CommandResponse( aci_evt_t *aci_evt) { 322 | debug_print(F("Evt Command Response: ")); 323 | //If an ACI command response event comes with an error -> stop 324 | switch (aci_evt->params.cmd_rsp.cmd_status) { 325 | case ACI_STATUS_SUCCESS: 326 | debug_println(F(": Success!")); 327 | break; 328 | case ACI_STATUS_ERROR_PIPE_STATE_INVALID: 329 | debug_println(F(": failed with error 'pipe state invalid'")); 330 | break; 331 | case ACI_STATUS_ERROR_REJECTED: 332 | debug_println(F(": failed with error 'command rejected'")); 333 | break; 334 | case ACI_STATUS_ERROR_DEVICE_STATE_INVALID: 335 | debug_println(F(": Command invalid in the current device state")); 336 | break; 337 | default: 338 | debug_print(F(": Error ")); 339 | debug_println(aci_evt->params.cmd_rsp.cmd_status, HEX); 340 | } 341 | 342 | switch (aci_evt->params.cmd_rsp.cmd_opcode) { 343 | case ACI_CMD_GET_DEVICE_ADDRESS: 344 | debug_print(F("Device Address")); 345 | //Store the version and configuration information of the nRF8001 in the Hardware Revision String Characteristic 346 | break; 347 | case ACI_CMD_WAKEUP: debug_println(F("Wake Up" )); break; 348 | case ACI_CMD_SLEEP: debug_println(F("Sleep" )); break; 349 | case ACI_CMD_GET_DEVICE_VERSION: debug_println(F("Device Version")); break; 350 | case ACI_CMD_GET_BATTERY_LEVEL: debug_println(F("Battery Level" )); break; 351 | case ACI_CMD_GET_TEMPERATURE: debug_println(F("Temperature" )); break; 352 | case ACI_CMD_ECHO: debug_println(F("Echo" )); break; 353 | case ACI_CMD_BOND: debug_println(F("Bond" )); break; 354 | case ACI_CMD_CONNECT: debug_println(F("Connect" )); break; 355 | case ACI_CMD_DISCONNECT: debug_println(F("Disconnect" )); break; 356 | case ACI_CMD_CHANGE_TIMING: debug_println(F("Change Timing" )); break; 357 | case ACI_CMD_OPEN_REMOTE_PIPE: debug_println(F("Open Remote Pipe" )); break; 358 | case ACI_CMD_RADIO_RESET: debug_println(F("Radio Reset")); break; 359 | /** 360 | * Start a security request in bonding mode 361 | */ 362 | case ACI_CMD_BOND_SECURITY_REQUEST: debug_println(F("Bond Sec Request")); break; 363 | 364 | /** 365 | * Close a previously opened remote pipe 366 | */ 367 | case ACI_CMD_CLOSE_REMOTE_PIPE: debug_println(F("Close Remote Pipe")); break; 368 | /** 369 | * Invalid ACI command opcode 370 | */ 371 | case ACI_CMD_INVALID: debug_println(F("Invalid Command")); break; 372 | 373 | default: 374 | Serial.print(F("Evt Unk Cmd: ")); 375 | Serial.println( aci_evt->params.cmd_rsp.cmd_opcode); //hex(aci_evt->params.cmd_rsp.cmd_opcode); 376 | } 377 | } 378 | 379 | void Notif::PipeStatus(aci_evt_t *aci_evt) 380 | { 381 | debug_println(F("Evt Pipe Status")); 382 | //Link is encrypted when the PIPE_LINK_LOSS_ALERT_ALERT_LEVEL_RX_ACK_AUTO is available 383 | 384 | if((ACI_BOND_STATUS_SUCCESS == aci_state.bonded) && 385 | (true == bonded_first_time) && 386 | lib_aci_is_pipe_available(&aci_state, PIPE_ANCS_NOTIFICATION_SOURCE_RX)) { 387 | //debug_println("Storing Bond Data in EEPROM"); 388 | //lib_aci_disconnect(&aci_state, ACI_REASON_TERMINATE); 389 | 390 | /* bonded_first_time = false; 391 | //Store away the dynamic data of the nRF8001 in the Flash or EEPROM of the MCU 392 | // so we can restore the bond information of the nRF8001 in the event of power loss 393 | if (bond_data_read_store()) 394 | { 395 | debug_println(F("Dynamic Data read and stored successfully")); 396 | } else { 397 | debug_println(F("Dynamic Data read and stored FAILED!!!")); 398 | }*/ 399 | } 400 | 401 | 402 | if (lib_aci_is_discovery_finished(&aci_state) && (ACI_BOND_STATUS_SUCCESS != aci_state.bonded)) { 403 | debug_println(F("Upgrading security!")); 404 | lib_aci_bond_request(); 405 | } 406 | /* 407 | if (true == bonded_first_time) { 408 | debug_println(F("First time! Upgrading security!")); 409 | lib_aci_bond_request(); 410 | } 411 | 412 | if (true ==force_discovery_required){ 413 | debug_println(F("Force Discovery Required! Upgrading security!")); 414 | lib_aci_bond_request(); 415 | }*/ 416 | if (ACI_BOND_STATUS_SUCCESS == aci_state.bonded) { 417 | 418 | //Note: This may be called multiple times after the Arduino has connected to the right phone 419 | debug_println(F("phone Detected.")); 420 | 421 | command_send_enable = true; 422 | 423 | // Detection of ANCS pipes 424 | if (lib_aci_is_discovery_finished(&aci_state)) { 425 | debug_println(F(" Service Discovery is over.")); 426 | 427 | print_pipes(aci_evt); 428 | 429 | if (lib_aci_is_pipe_closed(&aci_state, PIPE_GATT_SERVICE_CHANGED_TX_ACK)) { 430 | debug_println(F(" -> GATT Service Changed.")); 431 | if (!lib_aci_open_remote_pipe(&aci_state, PIPE_GATT_SERVICE_CHANGED_TX_ACK)){ 432 | debug_println(F(" -> GATT Service Changed: Failure opening.")); 433 | } else { 434 | debug_println(F(" -> GATT Service Changed: Success opening.")); 435 | } 436 | } else { 437 | debug_println(F(" -> GATT Service Changed open.")); 438 | } 439 | 440 | // Test ANCS Pipes availability 441 | if (lib_aci_is_pipe_closed(&aci_state, PIPE_ANCS_CONTROL_POINT_TX_ACK)) { 442 | debug_println(F(" -> ANCS Control Point closed.")); 443 | if (!lib_aci_open_remote_pipe(&aci_state, PIPE_ANCS_CONTROL_POINT_TX_ACK)){ 444 | debug_println(F(" -> ANCS Control Point Pipe: Failure opening.")); 445 | } else { 446 | debug_println(F(" -> ANCS Control Point Pipe: Success opening.")); 447 | } 448 | } else { 449 | debug_println(F(" -> ANCS Control Point open.")); 450 | } 451 | 452 | if (lib_aci_is_pipe_closed(&aci_state, PIPE_ANCS_DATA_SOURCE_RX)) { 453 | debug_println(F(" -> ANCS Data Source Closed")); 454 | 455 | if (!lib_aci_open_remote_pipe(&aci_state, PIPE_ANCS_DATA_SOURCE_RX)){ 456 | debug_println(F(" -> ANCS Data Source Pipe: Failure opening.")); 457 | } else { 458 | debug_println(F(" -> ANCS Data Source Pipe: Success opening.")); 459 | } 460 | 461 | } else { 462 | debug_println(F(" -> ANCS Data Source Open")); 463 | if (!reset_notification_required && reset_data_required && lib_aci_is_pipe_available(&aci_state, PIPE_ANCS_DATA_SOURCE_RX)) { 464 | debug_println(F(" -> ANCS Data Source: Reseting Pipe")); 465 | lib_aci_close_remote_pipe(&aci_state,PIPE_ANCS_DATA_SOURCE_RX); 466 | 467 | reset_data_required = false; 468 | 469 | if (connect_callback_handle != NULL){ 470 | connect_callback_handle(); 471 | } 472 | 473 | } 474 | } 475 | if (lib_aci_is_pipe_closed(&aci_state, PIPE_ANCS_NOTIFICATION_SOURCE_RX)) { 476 | debug_println(F(" -> ANCS Notification Source closed")); 477 | 478 | if ( (!lib_aci_open_remote_pipe(&aci_state, PIPE_ANCS_NOTIFICATION_SOURCE_RX))) { 479 | debug_println(F(" -> ANCS Notification Source Pipe: Failure opening.")); 480 | } else { 481 | debug_println(F(" -> ANCS Notification Source Pipe: Success opening.")); 482 | } 483 | 484 | } else { 485 | debug_println(F(" -> ANCS Notification Source Open")); 486 | if (reset_notification_required && lib_aci_is_pipe_available(&aci_state, PIPE_ANCS_NOTIFICATION_SOURCE_RX)) { 487 | debug_println(F(" -> ANCS Notification Source: Reseting Pipe")); 488 | 489 | lib_aci_close_remote_pipe(&aci_state,PIPE_ANCS_NOTIFICATION_SOURCE_RX); 490 | 491 | /* 492 | uint8_t* buffer; 493 | buffer = (uint8_t*)malloc(4); 494 | pack(buffer, "BB", 0x0000, 0xFFFF ); 495 | lib_aci_send_data(PIPE_GATT_SERVICE_CHANGED_TX_ACK, buffer, 4); 496 | free(buffer); 497 | */ 498 | 499 | reset_notification_required = false; 500 | 501 | if (connect_callback_handle != NULL){ 502 | connect_callback_handle(); 503 | } 504 | 505 | } 506 | } 507 | 508 | } else { 509 | debug_println(F(" Service Discovery is still going on.")); 510 | } 511 | 512 | 513 | } 514 | 515 | } 516 | 517 | void Notif::Disconnected(aci_evt_t *aci_evt) 518 | { 519 | debug_print(F("Evt Disconnected: ")); 520 | if (ACI_BOND_STATUS_SUCCESS == aci_state.bonded) 521 | { 522 | if (disconnect_callback_handle != NULL) { 523 | disconnect_callback_handle(); 524 | } 525 | if (ACI_STATUS_EXTENDED == aci_evt->params.disconnected.aci_status) //Link was disconnected 526 | { 527 | 528 | if (bonded_first_time) 529 | { 530 | bonded_first_time = false; 531 | //Store away the dynamic data of the nRF8001 in the Flash or EEPROM of the MCU 532 | // so we can restore the bond information of the nRF8001 in the event of power loss 533 | if (bond_data_read_store()) 534 | { 535 | debug_println(F("Dynamic Data read and stored successfully")); 536 | } else { 537 | debug_println(F("Dynamic Data read and stored FAILED!!!")); 538 | } 539 | } 540 | if (0x24 == aci_evt->params.disconnected.btle_status) 541 | { 542 | //The error code appears when phone or Arduino has deleted the pairing/bonding information. 543 | //The Arduino stores the bonding information in EEPROM, which is deleted only by 544 | // the user action of connecting pin 6 to 3.3v and then followed by a reset. 545 | //While deleting bonding information delete on the Arduino and on the phone. 546 | debug_println(F("phone/Arduino has deleted the bonding/pairing information")); 547 | debug_println(F("Pairing/Bonding info cleared from EEPROM.")); 548 | //Address. Value 549 | eeprom_write(0, 0xFF); 550 | lib_aci_disconnect(&aci_state, ACI_REASON_TERMINATE); 551 | delay(500); 552 | lib_aci_radio_reset(); 553 | if (reset_callback_handle != NULL) { 554 | reset_callback_handle(); 555 | } 556 | 557 | } 558 | 559 | debug_print(F("Disconnected: ")); 560 | // btle_status == 13 when distant device removes bonding 561 | debug_println((int)aci_evt->params.disconnected.btle_status, HEX); 562 | } 563 | if(ACI_STATUS_ERROR_BOND_REQUIRED == aci_evt->params.disconnected.aci_status) { 564 | debug_println(F("phone has deleted the bonding/pairing information")); 565 | //Clear the pairing 566 | debug_println(F("Pairing/Bonding info cleared from EEPROM.")); 567 | //Address. Value 568 | eeprom_write(0, 0xFF); 569 | lib_aci_disconnect(&aci_state, ACI_REASON_TERMINATE); 570 | delay(500); 571 | lib_aci_radio_reset(); 572 | if (reset_callback_handle != NULL) { 573 | reset_callback_handle(); 574 | } 575 | 576 | } else { 577 | 578 | lib_aci_connect(180/* in seconds */, 0x0100 /* advertising interval 100ms*/); 579 | debug_println(F("Using existing bond stored in EEPROM.")); 580 | debug_print(F("Advertising started. Trying to Connect. Disconnect Status: ")); 581 | debug_println((int)aci_evt->params.disconnected.aci_status, HEX); 582 | debug_print(F("BTLE status: ")); 583 | debug_println((int)aci_evt->params.disconnected.btle_status, HEX); 584 | } 585 | free_ram(); 586 | } 587 | else 588 | { 589 | //There is no existing bond. Try to bond. 590 | lib_aci_bond(180/* in seconds */, 0x0050 /* advertising interval 50ms*/); 591 | debug_println(F("No existing bond stored in EEPROM.")); 592 | debug_print(F("Advertising started. Bonding. Dissconnect status: ")); 593 | debug_println((int)aci_evt->params.disconnected.aci_status, HEX); 594 | debug_print(F("BTLE status: ")); 595 | debug_println((int)aci_evt->params.disconnected.btle_status, HEX); 596 | 597 | } 598 | if (aci_evt->params.disconnected.btle_status == DISCONNECT_REASON_CX_CLOSED_BY_PEER_DEVICE) 599 | { 600 | debug_println(F("Remote device disconnected")); 601 | } 602 | if (aci_evt->params.disconnected.btle_status == DISCONNECT_REASON_CX_CLOSED_BY_LOCAL_DEVICE) 603 | { 604 | debug_println(F("Local device disconnected")); 605 | } 606 | 607 | } 608 | 609 | void Notif::HwError(aci_evt_t *aci_evt) 610 | { 611 | debug_print(F("HW error: ")); 612 | debug_println(aci_evt->params.hw_error.line_num, DEC); 613 | 614 | for(uint8_t counter = 0; counter <= (aci_evt->len - 3); counter++) 615 | { 616 | Serial.write(aci_evt->params.hw_error.file_name[counter]); //uint8_t file_name[20]; 617 | } 618 | debug_println(); 619 | 620 | //Manage the bond in EEPROM of the AVR 621 | { 622 | uint8_t eeprom_status = 0; 623 | eeprom_status = eeprom_read(0); 624 | if (eeprom_status != 0xFF) 625 | { 626 | /* debug_println(F("Previous Bond present. Restoring")); 627 | debug_println(F("Using existing bond stored in EEPROM.")); 628 | debug_println(F(" To delete the bond stored in EEPROM, connect Pin 6 to 3.3v and Reset.")); 629 | debug_println(F(" Make sure that the bond on the phone/PC is deleted as well."));*/ 630 | //We must have lost power and restarted and must restore the bonding infromation using the ACI Write Dynamic Data 631 | if (ACI_STATUS_TRANSACTION_COMPLETE == bond_data_restore( eeprom_status, &bonded_first_time)) 632 | { 633 | debug_println(F("Bond restored successfully")); 634 | } 635 | else 636 | { 637 | debug_println(F("Bond restore failed. Delete the bond and try again.")); 638 | } 639 | } 640 | } 641 | 642 | // Start bonding as all proximity devices need to be bonded to be usable 643 | if (ACI_BOND_STATUS_SUCCESS != aci_state.bonded) 644 | { 645 | lib_aci_bond(180/* in seconds */, 0x0050 /* advertising interval 50ms*/); 646 | debug_println(F("No Bond present in EEPROM.")); 647 | debug_println(F("Advertising started : Waiting to be connected and bonded")); 648 | } 649 | else 650 | { 651 | //connect to an already bonded device 652 | //Use lib_aci_direct_connect for faster re-connections with PC, not recommended to use with iOS/OS X 653 | lib_aci_connect(100/* in seconds */, 0x0020 /* advertising interval 20ms*/); 654 | debug_println(F("Already bonded : Advertising started : Waiting to be connected")); 655 | } 656 | 657 | } 658 | 659 | void Notif::ReadNotifications() 660 | { 661 | 662 | 663 | // We enter the if statement only when there is a ACI event available to be processed 664 | if (lib_aci_event_get(&aci_state, &aci_data)) 665 | { 666 | aci_evt_t * aci_evt; 667 | 668 | aci_evt = &aci_data.evt; 669 | switch(aci_evt->evt_opcode) 670 | { 671 | /** 672 | As soon as you reset the nRF8001 you will get an ACI Device Started Event 673 | */ 674 | case ACI_EVT_DEVICE_STARTED: 675 | DeviceStarted(aci_evt); 676 | 677 | break; //ACI Device Started Event 678 | 679 | case ACI_EVT_CMD_RSP: 680 | CommandResponse(aci_evt); 681 | break; 682 | 683 | case ACI_EVT_CONNECTED: 684 | debug_println(F("Evt Connected")); 685 | aci_state.data_credit_available = aci_state.data_credit_total; 686 | timing_change_done = false; 687 | reset_notification_required = true; 688 | reset_data_required = true; 689 | 690 | /* 691 | Get the device version of the nRF8001 and store it in the Hardware Revision String 692 | */ 693 | debug_print(F("aci_state.bonded: ")); 694 | debug_println(aci_state.bonded, HEX); 695 | if (lib_aci_is_discovery_finished(&aci_state) && (ACI_BOND_STATUS_SUCCESS != aci_state.bonded)) { 696 | debug_println(F("Upgrading security! From Event Connected - never seen this called")); 697 | lib_aci_bond_request(); 698 | } 699 | break; 700 | 701 | case ACI_EVT_BOND_STATUS: 702 | debug_print(F("Evt Bond Status: ")); 703 | debug_println(aci_evt->params.bond_status.status_code, HEX); 704 | aci_state.bonded = aci_evt->params.bond_status.status_code; 705 | 706 | break; 707 | 708 | 709 | case ACI_EVT_PIPE_STATUS: 710 | 711 | PipeStatus( aci_evt); 712 | break; 713 | 714 | case ACI_EVT_DISCONNECTED: 715 | Disconnected(aci_evt); 716 | break; 717 | 718 | case ACI_EVT_DATA_RECEIVED: 719 | debug_println(F("Evt Data Received")); 720 | switch (aci_evt->params.data_received.rx_data.pipe_number) { 721 | case PIPE_ANCS_NOTIFICATION_SOURCE_RX: 722 | 723 | ancs_notification_source_parser(aci_evt->params.data_received.rx_data.aci_data); 724 | 725 | break; 726 | case PIPE_ANCS_DATA_SOURCE_RX: 727 | 728 | ancs_notification_t* notif; 729 | notif = ancs_data_source_parser(aci_evt->params.data_received.rx_data.aci_data); 730 | if ((notif != NULL) && (notification_callback_handle != NULL)) { 731 | notification_callback_handle(notif); 732 | } 733 | 734 | break; 735 | default: 736 | debug_println(F("Un Covered Evt Data received on Pipe #")); 737 | debug_println(aci_evt->params.data_received.rx_data.pipe_number, DEC); 738 | debug_println(F(" -> ")); 739 | debug_println(aci_evt->params.data_received.rx_data.aci_data[0], DEC); 740 | } 741 | break; 742 | 743 | case ACI_EVT_DATA_CREDIT: 744 | aci_state.data_credit_available = aci_state.data_credit_available + aci_evt->params.data_credit.credit; 745 | break; 746 | case ACI_EVT_DATA_ACK: 747 | break; 748 | 749 | case ACI_EVT_PIPE_ERROR: 750 | //See the appendix in the nRF8001 Product Specication for details on the error codes 751 | debug_print(F("ACI Evt Pipe Error: Pipe #:")); 752 | debug_print(aci_evt->params.pipe_error.pipe_number, DEC); 753 | debug_print(F(" Pipe Error Code: 0x")); 754 | debug_println(aci_evt->params.pipe_error.error_code, HEX); 755 | 756 | //Increment the credit available as the data packet was not sent. 757 | //The pipe error also represents the Attribute protocol Error Response sent from the peer and that should not be counted 758 | //for the credit. 759 | if (ACI_STATUS_ERROR_PEER_ATT_ERROR != aci_evt->params.pipe_error.error_code) 760 | { 761 | aci_state.data_credit_available++; 762 | } 763 | break; 764 | 765 | case ACI_EVT_HW_ERROR: 766 | HwError( aci_evt); 767 | break; 768 | default: 769 | debug_print("Unknown evt code: "); 770 | debug_println(aci_evt->evt_opcode, HEX); 771 | break; 772 | } 773 | } 774 | else 775 | { 776 | 777 | // No event in the ACI Event queue and if there is no event in the ACI command queue the arduino can go to sleep 778 | // Arduino can go to sleep now 779 | // Wakeup from sleep from the RDYN line 780 | 781 | ancs_run(); 782 | 783 | } 784 | 785 | /* setup_required is set to true when the device starts up and enters setup mode. 786 | * It indicates that do_aci_setup() should be called. The flag should be cleared if 787 | * do_aci_setup() returns ACI_STATUS_TRANSACTION_COMPLETE. 788 | */ 789 | if(setup_required) 790 | { 791 | if (SETUP_SUCCESS == do_aci_setup(&aci_state)) 792 | { 793 | debug_println(F(" - Succesfully setup nRF8001")); 794 | setup_required = false; 795 | } 796 | } 797 | } 798 | 799 | 800 | 801 | void Notif::setup() { 802 | 803 | if (NULL != services_pipe_type_mapping) 804 | { 805 | aci_state.aci_setup_info.services_pipe_type_mapping = &services_pipe_type_mapping[0]; 806 | } 807 | else 808 | { 809 | aci_state.aci_setup_info.services_pipe_type_mapping = NULL; 810 | } 811 | aci_state.aci_setup_info.number_of_pipes = NUMBER_OF_PIPES; 812 | aci_state.aci_setup_info.setup_msgs = (hal_aci_data_t*) setup_msgs; 813 | aci_state.aci_setup_info.num_setup_msgs = NB_SETUP_MESSAGES; 814 | 815 | //Tell the ACI library, the MCU to nRF8001 pin connections 816 | aci_state.aci_pins.board_name = BOARD_DEFAULT; //See board.h for details 817 | aci_state.aci_pins.reqn_pin = reqnPin; //The REQN and RDYN jumpers are settable, make sure this is the same 818 | aci_state.aci_pins.rdyn_pin = rdynPin; 819 | aci_state.aci_pins.mosi_pin = MOSI; 820 | aci_state.aci_pins.miso_pin = MISO; 821 | aci_state.aci_pins.sck_pin = SCK; 822 | 823 | aci_state.aci_pins.spi_clock_divider = SPI_CLOCK_DIV8;//SPI_CLOCK_DIV8 = 2MHz SPI speed 824 | //SPI_CLOCK_DIV16 = 1MHz SPI speed 825 | 826 | aci_state.aci_pins.reset_pin = UNUSED; //4 for Nordic board, UNUSED for REDBEARLABS 827 | aci_state.aci_pins.active_pin = UNUSED; 828 | aci_state.aci_pins.optional_chip_sel_pin = UNUSED; 829 | 830 | aci_state.aci_pins.interface_is_interrupt = false; 831 | aci_state.aci_pins.interrupt_number = UNUSED; 832 | 833 | //We reset the nRF8001 here by toggling the RESET line connected to the nRF8001 834 | //and initialize the data structures required to setup the nRF8001 835 | //The second parameter is for turning debug printing on for the ACI Commands and Events so they be printed on the Serial 836 | lib_aci_init(&aci_state, false); 837 | aci_state.bonded = ACI_BOND_STATUS_FAILED; 838 | 839 | 840 | //If things get really crazy, uncomment this line. It wipes the saved EEPROM information for the Nordic chip. Good to do this if the services.h file gets updated. 841 | //After it is wiped, comment and reupload. 842 | //eeprom_write(0, 0xFF); 843 | 844 | ancs_init(); 845 | } 846 | 847 | Notif::Notif(uint8_t rqPin, uint8_t rdPin) { 848 | rdynPin = rdPin; 849 | reqnPin = rqPin; 850 | notification_callback_handle = NULL; 851 | connect_callback_handle = NULL; 852 | disconnect_callback_handle = NULL; 853 | reset_callback_handle = NULL; 854 | bonded_first_time = true; 855 | setup_required = false; 856 | timing_change_done = false; 857 | reset_data_required = true; 858 | reset_notification_required = true; 859 | 860 | 861 | } 862 | -------------------------------------------------------------------------------- /notif.h: -------------------------------------------------------------------------------- 1 | // 2 | // nrf_ancs.h 3 | // 4 | // 5 | // Created by Luke Berndt on 8/23/14. 6 | // 7 | // 8 | 9 | #ifndef notif_h 10 | #define notif_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #include "ancs.h" 20 | 21 | 22 | void eepromWrite(int address, uint8_t value); 23 | uint8_t eepromRead(int address); 24 | class Notif 25 | { 26 | public: 27 | Notif(uint8_t reqnPin, uint8_t rdynPin); 28 | 29 | 30 | void ReadNotifications(); 31 | 32 | void set_notification_callback_handle(void (*fptr)(ancs_notification_t* notif)); 33 | void set_connect_callback_handle(void (*fptr)(void)); 34 | void set_disconnect_callback_handle(void (*fptr)(void)); 35 | void set_reset_callback_handle(void (*fptr)(void)); 36 | void setup(); 37 | 38 | 39 | private: 40 | void print_pipes(aci_evt_t* aci_evt); 41 | unsigned char ble_busy(); 42 | void (*notification_callback_handle)(ancs_notification_t* notif); 43 | void (*connect_callback_handle)(void); 44 | void (*disconnect_callback_handle)(void); 45 | void (*reset_callback_handle)(void); 46 | aci_status_code_t bond_data_restore( uint8_t eeprom_status, bool *bonded_first_time_state); 47 | void bond_data_store(aci_evt_t *evt); 48 | bool bond_data_read_store(); 49 | void DeviceStarted( aci_evt_t *aci_evt); 50 | void CommandResponse( aci_evt_t *aci_evt); 51 | void PipeStatus(aci_evt_t *aci_evt); 52 | void Disconnected( aci_evt_t *aci_evt); 53 | void HwError( aci_evt_t *aci_evt); 54 | uint8_t reqnPin; 55 | uint8_t rdynPin; 56 | int _pin; 57 | /* 58 | We will store the bonding info for the nRF8001 in the EEPROM/Flash of the MCU to recover from a power loss situation 59 | */ 60 | bool bonded_first_time; 61 | 62 | /* 63 | Timing change state variable 64 | */ 65 | bool timing_change_done; 66 | bool setup_required; 67 | bool reset_notification_required; 68 | bool reset_data_required; 69 | 70 | // aci_struct that will contain 71 | // total initial credits 72 | // current credit 73 | // current state of the aci (setup/standby/active/sleep) 74 | // open remote pipe pending 75 | // close remote pipe pending 76 | // Current pipe available bitmap 77 | // Current pipe closed bitmap 78 | // Current connection interval, slave latency and link supervision timeout 79 | // Current State of the the GATT client (Service Discovery) 80 | // Status of the bond (R) Peer address 81 | //aci_state_t aci_state; 82 | 83 | /* 84 | Temporary buffers for sending ACI commands 85 | */ 86 | 87 | hal_aci_data_t aci_cmd; 88 | }; 89 | 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /nrf8001_ancs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | nRF8001_Dx 6 | 7 | ANCS 8 | f431 9 | 10 | Notification Source 11 | 120d 12 | 13 | 0 14 | 1 15 | 1 16 | false 17 | 18 | false 19 | false 20 | false 21 | true 22 | false 23 | false 24 | 25 | false 26 | 27 | 0 28 | 29 | 30 | 31 | Data Source 32 | c6e9 33 | 34 | 0 35 | 60 36 | 1 37 | false 38 | 39 | false 40 | false 41 | false 42 | true 43 | false 44 | false 45 | 46 | false 47 | 48 | 0 49 | 50 | 51 | 52 | Control Point 53 | d8f3 54 | 55 | 0 56 | 1 57 | 1 58 | false 59 | 60 | false 61 | false 62 | true 63 | false 64 | false 65 | false 66 | 67 | false 68 | 69 | 0 70 | 71 | 72 | 73 | 74 | Notif 75 | 0 76 | false 77 | 4 78 | 0240 79 | 1 80 | 0 81 | 0 82 | 600 83 | 10 84 | 7 85 | 16 86 | c10 87 | c10 88 | 0 89 | 2c10 90 | 2c10 91 | 0 92 | 0 93 | 0 94 | 0 95 | 0 96 | 0 97 | 0 98 | 0 99 | 400 100 | 800 101 | 0 102 | 600 103 | true 104 | true 105 | 106 | f431 107 | 108 | 109 | 110 | 19 111 | 112 | 113 | 114 | 18 115 | 116 | 117 | 118 | 119 | 120 | 1 121 | 1 122 | 3 123 | 0 124 | 0 125 | 0 126 | 0 127 | true 128 | 129 | 130 | 220 131 | 10 132 | 1000 133 | 0 134 | 0 135 | 1280 136 | 137 | 138 | -------------------------------------------------------------------------------- /pack_lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | helper functions to work with bytearrays 3 | 4 | usage: 5 | 6 | pack(unsigned char *buf, const char *fmt, ...); 7 | 8 | similar to sprintf(): 9 | put all arguments in "..." into the buffer, according to the format 10 | string "fmt", in a way that they can be easily retrieved with unpack() 11 | 12 | 13 | unpack(const unsigned char *buf, const char *fmt, ...); 14 | 15 | similar to sscanf(): 16 | retrieves data from the buffer according to "fmt". arguments in "..." 17 | must be pointers to appropriate types. 18 | 19 | 20 | format specifiers: 21 | b = int8_t B = uint8_t 22 | h = int16_t H = uint16_t 23 | i = int32_t I = uint32_t 24 | l = int64_t L = uint64_t 25 | _ = skip one byte (mainly useful for unpacking) 26 | 27 | example: 28 | uint8_t a; 29 | uint16_t b = 1337; 30 | int32_t c = -12345678; 31 | 32 | pack(buf, "BBHi", 255, 0, b, c); 33 | unpack(buf, "B_Hi", &a, &b, &c); 34 | 35 | assert(a == 255); 36 | assert(b == 1337); 37 | assert(c == -12345678); 38 | 39 | source: http://sprunge.us/LEDM 40 | license: WTFPL 41 | by rob`` on freenode, ##c 42 | 43 | */ 44 | #ifndef _PACK_H_ 45 | #define _PACK_H_ 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #ifndef NO_PACK 53 | static size_t pack(unsigned char *buf, const char *fmt, ...); 54 | #endif 55 | #ifndef NO_UNPACK 56 | static size_t unpack(const unsigned char *buf, const char *fmt, ...); 57 | #endif 58 | 59 | #ifndef NO_PACK 60 | static void buffer_put(unsigned char *buffer, uint64_t value, size_t bytes); 61 | #endif 62 | #ifndef NO_UNPACK 63 | static uintmax_t buffer_get(const unsigned char *buffer, size_t bytes); 64 | #endif 65 | 66 | #define PACK_UNPACK(c, name, CASE) \ 67 | static size_t name(c unsigned char *buf, const char *fmt, ...) \ 68 | { \ 69 | va_list ap; \ 70 | size_t sz, total = 0; \ 71 | \ 72 | va_start(ap, fmt); \ 73 | \ 74 | for (; *fmt; fmt++) \ 75 | { \ 76 | switch (*fmt) \ 77 | { \ 78 | CASE('b', int, int8_t); \ 79 | CASE('B', int, uint8_t); \ 80 | \ 81 | CASE('h', int, int16_t); \ 82 | CASE('H', int, uint16_t); \ 83 | \ 84 | CASE('i', int32_t, int32_t); \ 85 | CASE('I', uint32_t, uint32_t); \ 86 | \ 87 | CASE('l', int64_t, int64_t); \ 88 | CASE('L', uint64_t, uint64_t); \ 89 | \ 90 | case '_': \ 91 | sz = 1; \ 92 | break; \ 93 | \ 94 | default: \ 95 | va_end(ap); \ 96 | return total; \ 97 | } \ 98 | \ 99 | buf += sz; \ 100 | total += sz; \ 101 | } \ 102 | \ 103 | va_end(ap); \ 104 | \ 105 | return total; \ 106 | } 107 | 108 | #define CASE_PACK(chr, ap_type, type) \ 109 | case chr: \ 110 | { \ 111 | type val = va_arg(ap, ap_type); \ 112 | sz = sizeof (type); \ 113 | buffer_put(buf, val, sz); \ 114 | } \ 115 | break 116 | 117 | #define CASE_UNPACK(chr, ap_type, type) \ 118 | case chr: \ 119 | { \ 120 | type *p = va_arg(ap, type*); \ 121 | sz = sizeof (type); \ 122 | *p = buffer_get(buf, sz); \ 123 | } \ 124 | break 125 | 126 | #ifndef NO_PACK 127 | /* define pack() */ 128 | PACK_UNPACK(, pack, CASE_PACK) 129 | #endif 130 | 131 | #ifndef NO_UNPACK 132 | /* define unpack() */ 133 | PACK_UNPACK(const, unpack, CASE_UNPACK) 134 | #endif 135 | 136 | #ifdef PACK_LITTLE_ENDIAN 137 | // LITTLE ENDIAN VERSION 138 | #ifndef NO_PACK 139 | static void buffer_put(unsigned char *buffer, uint64_t value, size_t bytes) 140 | { 141 | size_t i = 0; 142 | while (bytes-(i++)) { 143 | buffer[i-1] = value & 0xFF; 144 | value >>= 8; 145 | } 146 | } 147 | #endif 148 | 149 | #ifndef NO_UNPACK 150 | static uint64_t buffer_get(const unsigned char *buffer, size_t bytes) 151 | { 152 | uint64_t value = 0; 153 | const unsigned char *end = buffer + bytes; 154 | 155 | while (buffer < end) { 156 | value <<= 8; 157 | value += *(--end); 158 | } 159 | 160 | return value; 161 | } 162 | #endif 163 | #else 164 | // BIG ENDIAN version 165 | #ifndef NO_PACK 166 | static void buffer_put(unsigned char *buffer, uint64_t value, size_t bytes) 167 | { 168 | while (bytes--) { 169 | buffer[bytes] = value & 0xFF; 170 | value >>= 8; 171 | } 172 | } 173 | #endif 174 | 175 | #ifndef NO_UNPACK 176 | static uint64_t buffer_get(const unsigned char *buffer, size_t bytes) 177 | { 178 | uint64_t value = 0; 179 | const unsigned char *end = buffer + bytes; 180 | 181 | while (buffer < end) { 182 | value <<= 8; 183 | value += *buffer++; 184 | } 185 | 186 | return value; 187 | } 188 | #endif // NO_UNPACK 189 | 190 | #endif // ENDIAN 191 | 192 | #endif // PACK_H 193 | 194 | -------------------------------------------------------------------------------- /services.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Nordic Semiconductor ASA 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * - Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * - The name of Nordic Semiconductor ASA may not be used to endorse or promote 16 | * products derived from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * This file is autogenerated by nRFgo Studio 1.16.1.3119 32 | */ 33 | 34 | #ifndef SETUP_MESSAGES_H__ 35 | #define SETUP_MESSAGES_H__ 36 | 37 | #include "hal_platform.h" 38 | #include "aci.h" 39 | 40 | 41 | #define SETUP_ID 1 42 | #define SETUP_FORMAT 3 /** nRF8001 D */ 43 | #define ACI_DYNAMIC_DATA_SIZE 169 44 | 45 | /* Service: GATT - Characteristic: Service Changed - Pipe: TX_ACK */ 46 | #define PIPE_GATT_SERVICE_CHANGED_TX_ACK 1 47 | #define PIPE_GATT_SERVICE_CHANGED_TX_ACK_MAX_SIZE 4 48 | 49 | /* Service: TX Power - Characteristic: TX Power Level - Pipe: SET */ 50 | #define PIPE_TX_POWER_TX_POWER_LEVEL_SET 2 51 | #define PIPE_TX_POWER_TX_POWER_LEVEL_SET_MAX_SIZE 1 52 | 53 | /* Service: Battery - Characteristic: Battery Level - Pipe: TX */ 54 | #define PIPE_BATTERY_BATTERY_LEVEL_TX 3 55 | #define PIPE_BATTERY_BATTERY_LEVEL_TX_MAX_SIZE 1 56 | 57 | /* Service: Battery - Characteristic: Battery Level - Pipe: SET */ 58 | #define PIPE_BATTERY_BATTERY_LEVEL_SET 4 59 | #define PIPE_BATTERY_BATTERY_LEVEL_SET_MAX_SIZE 1 60 | 61 | /* Service: Device Information - Characteristic: Hardware Revision String - Pipe: SET */ 62 | #define PIPE_DEVICE_INFORMATION_HARDWARE_REVISION_STRING_SET 5 63 | #define PIPE_DEVICE_INFORMATION_HARDWARE_REVISION_STRING_SET_MAX_SIZE 9 64 | 65 | /* Service: ANCS - Characteristic: Notification Source - Pipe: RX */ 66 | #define PIPE_ANCS_NOTIFICATION_SOURCE_RX 6 67 | #define PIPE_ANCS_NOTIFICATION_SOURCE_RX_MAX_SIZE 20 68 | 69 | /* Service: ANCS - Characteristic: Control Point - Pipe: TX_ACK */ 70 | #define PIPE_ANCS_CONTROL_POINT_TX_ACK 7 71 | #define PIPE_ANCS_CONTROL_POINT_TX_ACK_MAX_SIZE 20 72 | 73 | /* Service: ANCS - Characteristic: Data Source - Pipe: RX */ 74 | #define PIPE_ANCS_DATA_SOURCE_RX 8 75 | #define PIPE_ANCS_DATA_SOURCE_RX_MAX_SIZE 20 76 | 77 | 78 | #define NUMBER_OF_PIPES 8 79 | 80 | #define SERVICES_PIPE_TYPE_MAPPING_CONTENT {\ 81 | {ACI_STORE_LOCAL, ACI_TX_ACK}, \ 82 | {ACI_STORE_LOCAL, ACI_SET}, \ 83 | {ACI_STORE_LOCAL, ACI_TX}, \ 84 | {ACI_STORE_LOCAL, ACI_SET}, \ 85 | {ACI_STORE_LOCAL, ACI_SET}, \ 86 | {ACI_STORE_REMOTE, ACI_RX}, \ 87 | {ACI_STORE_REMOTE, ACI_TX_ACK}, \ 88 | {ACI_STORE_REMOTE, ACI_RX}, \ 89 | } 90 | 91 | #define GAP_PPCP_MAX_CONN_INT 0x320 /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 92 | #define GAP_PPCP_MIN_CONN_INT 0x190 /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 93 | #define GAP_PPCP_SLAVE_LATENCY 0 94 | #define GAP_PPCP_CONN_TIMEOUT 0x258 /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */ 95 | 96 | #define NB_SETUP_MESSAGES 26 97 | #define SETUP_MESSAGES_CONTENT {\ 98 | {0x00,\ 99 | {\ 100 | 0x07,0x06,0x00,0x00,0x03,0x02,0x41,0xfe,\ 101 | },\ 102 | },\ 103 | {0x00,\ 104 | {\ 105 | 0x1f,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x02,0x02,0x04,0x04,0x09,0x01,0x01,0x00,0x00,0x06,0x00,0x03,\ 106 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 107 | },\ 108 | },\ 109 | {0x00,\ 110 | {\ 111 | 0x1f,0x06,0x10,0x1c,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf4,0x31,0x02,\ 112 | 0x00,0x00,0x40,0x10,0x00,0x00,0x40,0x10,0x03,0x90,0x04,0x64,\ 113 | },\ 114 | },\ 115 | {0x00,\ 116 | {\ 117 | 0x1f,0x06,0x10,0x38,0x02,0xff,0x02,0x58,0x63,0x05,0x00,0x00,0x08,0x40,0x00,0x00,0x08,0x40,0x00,0x00,\ 118 | 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,\ 119 | },\ 120 | },\ 121 | {0x00,\ 122 | {\ 123 | 0x05,0x06,0x10,0x54,0x01,0x01,\ 124 | },\ 125 | },\ 126 | {0x00,\ 127 | {\ 128 | 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\ 129 | 0x02,0x28,0x03,0x01,0x02,0x03,0x00,0x00,0x2a,0x04,0x04,0x14,\ 130 | },\ 131 | },\ 132 | {0x00,\ 133 | {\ 134 | 0x1f,0x06,0x20,0x1c,0x09,0x00,0x03,0x2a,0x00,0x01,0x41,0x4e,0x43,0x53,0x20,0x52,0x75,0x73,0x6b,0x69,\ 135 | 0x2e,0x63,0x6f,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,\ 136 | },\ 137 | },\ 138 | {0x00,\ 139 | {\ 140 | 0x1f,0x06,0x20,0x38,0x05,0x05,0x00,0x04,0x28,0x03,0x01,0x02,0x05,0x00,0x01,0x2a,0x06,0x04,0x03,0x02,\ 141 | 0x00,0x05,0x2a,0x01,0x01,0x40,0x02,0x04,0x04,0x05,0x05,0x00,\ 142 | },\ 143 | },\ 144 | {0x00,\ 145 | {\ 146 | 0x1f,0x06,0x20,0x54,0x06,0x28,0x03,0x01,0x02,0x07,0x00,0x04,0x2a,0x06,0x04,0x09,0x08,0x00,0x07,0x2a,\ 147 | 0x04,0x01,0x90,0x01,0x20,0x03,0x00,0x00,0x58,0x02,0x04,0x04,\ 148 | },\ 149 | },\ 150 | {0x00,\ 151 | {\ 152 | 0x1f,0x06,0x20,0x70,0x02,0x02,0x00,0x08,0x28,0x00,0x01,0x01,0x18,0x04,0x04,0x05,0x05,0x00,0x09,0x28,\ 153 | 0x03,0x01,0x22,0x0a,0x00,0x05,0x2a,0x26,0x0c,0x05,0x04,0x00,\ 154 | },\ 155 | },\ 156 | {0x00,\ 157 | {\ 158 | 0x1f,0x06,0x20,0x8c,0x0a,0x2a,0x05,0x01,0x00,0x00,0x00,0x00,0x46,0x34,0x03,0x02,0x00,0x0b,0x29,0x02,\ 159 | 0x01,0x00,0x00,0x04,0x04,0x02,0x02,0x00,0x0c,0x28,0x00,0x01,\ 160 | },\ 161 | },\ 162 | {0x00,\ 163 | {\ 164 | 0x1f,0x06,0x20,0xa8,0x04,0x18,0x04,0x04,0x05,0x05,0x00,0x0d,0x28,0x03,0x01,0x02,0x0e,0x00,0x07,0x2a,\ 165 | 0x06,0x0c,0x02,0x01,0x00,0x0e,0x2a,0x07,0x01,0x00,0x04,0x04,\ 166 | },\ 167 | },\ 168 | {0x00,\ 169 | {\ 170 | 0x1f,0x06,0x20,0xc4,0x02,0x02,0x00,0x0f,0x28,0x00,0x01,0x0f,0x18,0x04,0x04,0x05,0x05,0x00,0x10,0x28,\ 171 | 0x03,0x01,0x12,0x11,0x00,0x19,0x2a,0x16,0x0c,0x02,0x01,0x00,\ 172 | },\ 173 | },\ 174 | {0x00,\ 175 | {\ 176 | 0x1f,0x06,0x20,0xe0,0x11,0x2a,0x19,0x01,0x64,0x46,0x34,0x03,0x02,0x00,0x12,0x29,0x02,0x01,0x00,0x00,\ 177 | 0x04,0x04,0x02,0x02,0x00,0x13,0x28,0x00,0x01,0x0a,0x18,0x04,\ 178 | },\ 179 | },\ 180 | {0x00,\ 181 | {\ 182 | 0x1f,0x06,0x20,0xfc,0x04,0x05,0x05,0x00,0x14,0x28,0x03,0x01,0x02,0x15,0x00,0x27,0x2a,0x04,0x0c,0x09,\ 183 | 0x00,0x00,0x15,0x2a,0x27,0x01,0x00,0x00,0x00,0x00,0x00,0x00,\ 184 | },\ 185 | },\ 186 | {0x00,\ 187 | {\ 188 | 0x07,0x06,0x21,0x18,0x00,0x00,0x00,0x00,\ 189 | },\ 190 | },\ 191 | {0x00,\ 192 | {\ 193 | 0x0d,0x06,0x30,0x00,0xf4,0x31,0x02,0x04,0x03,0x18,0x01,0x01,0x07,0x01,\ 194 | },\ 195 | },\ 196 | {0x00,\ 197 | {\ 198 | 0x1f,0x06,0x40,0x00,0x2a,0x05,0x01,0x00,0x04,0x04,0x00,0x0a,0x00,0x0b,0x2a,0x07,0x01,0x00,0x80,0x04,\ 199 | 0x00,0x0e,0x00,0x00,0x2a,0x19,0x01,0x00,0x82,0x04,0x00,0x11,\ 200 | },\ 201 | },\ 202 | {0x00,\ 203 | {\ 204 | 0x1f,0x06,0x40,0x1c,0x00,0x12,0x2a,0x27,0x01,0x00,0x80,0x04,0x00,0x15,0x00,0x00,0x12,0x0d,0x05,0x00,\ 205 | 0x08,0x04,0x00,0x00,0x00,0x00,0xd8,0xf3,0x04,0x00,0x04,0x04,\ 206 | },\ 207 | },\ 208 | {0x00,\ 209 | {\ 210 | 0x1b,0x06,0x40,0x38,0x00,0x00,0x00,0x00,0xc6,0xe9,0x03,0x00,0x08,0x04,0x00,0x00,0x00,0x00,0x2a,0x05,\ 211 | 0x01,0x00,0x10,0x04,0x00,0x00,0x00,0x00,\ 212 | },\ 213 | },\ 214 | {0x00,\ 215 | {\ 216 | 0x1f,0x06,0x50,0x00,0xd0,0x00,0x2d,0x12,0x1e,0x4b,0x0f,0xa4,0x99,0x4e,0xce,0xb5,0x00,0x00,0x05,0x79,\ 217 | 0xfb,0x7b,0x7c,0xce,0x6a,0xb3,0x44,0xbe,0xb5,0x4b,0xd6,0x24,\ 218 | },\ 219 | },\ 220 | {0x00,\ 221 | {\ 222 | 0x1f,0x06,0x50,0x1c,0x00,0x00,0xea,0x22,0xd9,0xd9,0xaa,0xfd,0xbd,0x9b,0x21,0x98,0xa8,0x49,0xe1,0x45,\ 223 | 0x00,0x00,0xd1,0x69,0xbd,0x1d,0xa2,0x99,0xe6,0x25,0x58,0x8c,\ 224 | },\ 225 | },\ 226 | {0x00,\ 227 | {\ 228 | 0x0b,0x06,0x50,0x38,0xd9,0x42,0x01,0x63,0x00,0x00,0xbf,0x9f,\ 229 | },\ 230 | },\ 231 | {0x00,\ 232 | {\ 233 | 0x1b,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 234 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 235 | },\ 236 | },\ 237 | {0x00,\ 238 | {\ 239 | 0x19,0x06,0x70,0x00,0x19,0x02,0x40,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 240 | 0x00,0x00,0x00,0x00,0x00,0x00,\ 241 | },\ 242 | },\ 243 | {0x00,\ 244 | {\ 245 | 0x06,0x06,0xf0,0x00,0x03,0x8c,0xbc,\ 246 | },\ 247 | },\ 248 | } 249 | 250 | #endif 251 | -------------------------------------------------------------------------------- /services_lock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Nordic Semiconductor ASA 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * - Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * - The name of Nordic Semiconductor ASA may not be used to endorse or promote 16 | * products derived from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * This file is autogenerated by nRFgo Studio 1.16.1.3119 32 | */ 33 | 34 | #ifndef SETUP_MESSAGES_H__ 35 | #define SETUP_MESSAGES_H__ 36 | 37 | #include "hal_platform.h" 38 | #include "aci.h" 39 | 40 | // You have now chosen to upload the configuration to OTP on the device. 41 | // This will result in a device that you can not modify afterwards. If this is your intention, 42 | // remove this comment and the #error below 43 | #error Generating configuration for OTP. Please verify usage by removing this error message from include file. 44 | 45 | #define SETUP_ID 1 46 | #define SETUP_FORMAT 3 /** nRF8001 D */ 47 | #define ACI_DYNAMIC_DATA_SIZE 143 48 | 49 | /* Service: GATT - Characteristic: Service Changed - Pipe: TX_ACK */ 50 | #define PIPE_GATT_SERVICE_CHANGED_TX_ACK 1 51 | #define PIPE_GATT_SERVICE_CHANGED_TX_ACK_MAX_SIZE 4 52 | 53 | /* Service: ANCS - Characteristic: Notification Source - Pipe: RX */ 54 | #define PIPE_ANCS_NOTIFICATION_SOURCE_RX 2 55 | #define PIPE_ANCS_NOTIFICATION_SOURCE_RX_MAX_SIZE 1 56 | 57 | /* Service: ANCS - Characteristic: Data Source - Pipe: RX */ 58 | #define PIPE_ANCS_DATA_SOURCE_RX 3 59 | #define PIPE_ANCS_DATA_SOURCE_RX_MAX_SIZE 60 60 | 61 | /* Service: ANCS - Characteristic: Control Point - Pipe: TX_ACK */ 62 | #define PIPE_ANCS_CONTROL_POINT_TX_ACK 4 63 | #define PIPE_ANCS_CONTROL_POINT_TX_ACK_MAX_SIZE 1 64 | 65 | 66 | #define NUMBER_OF_PIPES 4 67 | 68 | #define SERVICES_PIPE_TYPE_MAPPING_CONTENT {\ 69 | {ACI_STORE_LOCAL, ACI_TX_ACK}, \ 70 | {ACI_STORE_REMOTE, ACI_RX}, \ 71 | {ACI_STORE_REMOTE, ACI_RX}, \ 72 | {ACI_STORE_REMOTE, ACI_TX_ACK}, \ 73 | } 74 | 75 | #define GAP_PPCP_MAX_CONN_INT 0x320 /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 76 | #define GAP_PPCP_MIN_CONN_INT 0x190 /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 77 | #define GAP_PPCP_SLAVE_LATENCY 0 78 | #define GAP_PPCP_CONN_TIMEOUT 0x258 /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */ 79 | 80 | #define NB_SETUP_MESSAGES 19 81 | #define SETUP_MESSAGES_CONTENT {\ 82 | {0x00,\ 83 | {\ 84 | 0x07,0x06,0x00,0x00,0x03,0x02,0x41,0xfe,\ 85 | },\ 86 | },\ 87 | {0x00,\ 88 | {\ 89 | 0x1f,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x02,0x02,0x01,0x04,0x05,0x01,0x01,0x00,0x00,0x06,0x00,0x04,\ 90 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 91 | },\ 92 | },\ 93 | {0x00,\ 94 | {\ 95 | 0x1f,0x06,0x10,0x1c,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf4,0x31,0x02,\ 96 | 0x00,0x00,0x08,0x10,0x00,0x00,0x08,0x10,0x03,0x90,0x04,0x64,\ 97 | },\ 98 | },\ 99 | {0x00,\ 100 | {\ 101 | 0x1f,0x06,0x10,0x38,0x02,0xff,0x02,0x58,0x0a,0x05,0x00,0x00,0x08,0x10,0x00,0x00,0x08,0x10,0x00,0x00,\ 102 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 103 | },\ 104 | },\ 105 | {0x00,\ 106 | {\ 107 | 0x05,0x06,0x10,0x54,0x01,0x00,\ 108 | },\ 109 | },\ 110 | {0x00,\ 111 | {\ 112 | 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\ 113 | 0x02,0x28,0x03,0x01,0x02,0x03,0x00,0x00,0x2a,0x04,0x04,0x14,\ 114 | },\ 115 | },\ 116 | {0x00,\ 117 | {\ 118 | 0x1f,0x06,0x20,0x1c,0x05,0x00,0x03,0x2a,0x00,0x01,0x4e,0x6f,0x74,0x69,0x66,0x63,0x73,0x65,0x6d,0x69,\ 119 | 0x2e,0x63,0x6f,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,\ 120 | },\ 121 | },\ 122 | {0x00,\ 123 | {\ 124 | 0x1f,0x06,0x20,0x38,0x05,0x05,0x00,0x04,0x28,0x03,0x01,0x02,0x05,0x00,0x01,0x2a,0x06,0x04,0x03,0x02,\ 125 | 0x00,0x05,0x2a,0x01,0x01,0x40,0x02,0x04,0x04,0x05,0x05,0x00,\ 126 | },\ 127 | },\ 128 | {0x00,\ 129 | {\ 130 | 0x1f,0x06,0x20,0x54,0x06,0x28,0x03,0x01,0x02,0x07,0x00,0x04,0x2a,0x06,0x04,0x09,0x08,0x00,0x07,0x2a,\ 131 | 0x04,0x01,0x90,0x01,0x20,0x03,0x00,0x00,0x58,0x02,0x04,0x04,\ 132 | },\ 133 | },\ 134 | {0x00,\ 135 | {\ 136 | 0x1f,0x06,0x20,0x70,0x02,0x02,0x00,0x08,0x28,0x00,0x01,0x01,0x18,0x04,0x04,0x05,0x05,0x00,0x09,0x28,\ 137 | 0x03,0x01,0x22,0x0a,0x00,0x05,0x2a,0x26,0x0c,0x05,0x04,0x00,\ 138 | },\ 139 | },\ 140 | {0x00,\ 141 | {\ 142 | 0x17,0x06,0x20,0x8c,0x0a,0x2a,0x05,0x01,0x00,0x00,0x00,0x00,0x46,0x34,0x03,0x02,0x00,0x0b,0x29,0x02,\ 143 | 0x01,0x00,0x00,0x00,\ 144 | },\ 145 | },\ 146 | {0x00,\ 147 | {\ 148 | 0x0d,0x06,0x30,0x00,0xf4,0x31,0x02,0x01,0x03,0x18,0x01,0x01,0x04,0x01,\ 149 | },\ 150 | },\ 151 | {0x00,\ 152 | {\ 153 | 0x1f,0x06,0x40,0x00,0x2a,0x05,0x01,0x00,0x04,0x04,0x00,0x0a,0x00,0x0b,0x12,0x0d,0x03,0x00,0x08,0x04,\ 154 | 0x00,0x00,0x00,0x00,0xc6,0xe9,0x04,0x00,0x08,0x04,0x00,0x00,\ 155 | },\ 156 | },\ 157 | {0x00,\ 158 | {\ 159 | 0x19,0x06,0x40,0x1c,0x00,0x00,0xd8,0xf3,0x05,0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x2a,0x05,0x01,0x00,\ 160 | 0x10,0x04,0x00,0x00,0x00,0x00,\ 161 | },\ 162 | },\ 163 | {0x00,\ 164 | {\ 165 | 0x1f,0x06,0x50,0x00,0xd0,0x00,0x2d,0x12,0x1e,0x4b,0x0f,0xa4,0x99,0x4e,0xce,0xb5,0x00,0x00,0x05,0x79,\ 166 | 0xbd,0x1d,0xa2,0x99,0xe6,0x25,0x58,0x8c,0xd9,0x42,0x01,0x63,\ 167 | },\ 168 | },\ 169 | {0x00,\ 170 | {\ 171 | 0x1f,0x06,0x50,0x1c,0x00,0x00,0xbf,0x9f,0xfb,0x7b,0x7c,0xce,0x6a,0xb3,0x44,0xbe,0xb5,0x4b,0xd6,0x24,\ 172 | 0x00,0x00,0xea,0x22,0xd9,0xd9,0xaa,0xfd,0xbd,0x9b,0x21,0x98,\ 173 | },\ 174 | },\ 175 | {0x00,\ 176 | {\ 177 | 0x0b,0x06,0x50,0x38,0xa8,0x49,0xe1,0x45,0x00,0x00,0xd1,0x69,\ 178 | },\ 179 | },\ 180 | {0x00,\ 181 | {\ 182 | 0x12,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 183 | },\ 184 | },\ 185 | {0x00,\ 186 | {\ 187 | 0x06,0x06,0xf0,0x00,0x83,0xe0,0xf9,\ 188 | },\ 189 | },\ 190 | } 191 | 192 | #endif 193 | -------------------------------------------------------------------------------- /ublue_setup.gen.out.txt: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------ 2 | uBlue Setup generation report 3 | Generated with uBlue setup DLL version: 1.0.0.16894 4 | Generated: Sun Sep 21 02:01:51 2014 (UTC) 5 | This file is automatically generated, do not modify 6 | ------------------------------------------------------------------------------ 7 | 8 | [Counts] 9 | 10 | Setup data size = 468 bytes 11 | Local database size = 160 bytes 12 | Local attribute count = 1 13 | Remote attribute count = 4 14 | Total pipe count = 5 15 | Dynamic data size = 143 bytes (worst case) 16 | 17 | [Setup Area Layout] 18 | 19 | Setup area, total = 1595 bytes 20 | Setup area, used = 299 bytes ( 18% of total ) 21 | Local services = 160 bytes ( 53% of used ) 22 | Remote services = 10 bytes ( 3% of used ) 23 | Pipes = 50 bytes ( 16% of used ) 24 | VS UUID area = 64 bytes ( 21% of used ) 25 | Extended Attr area = 15 bytes ( 5% of used ) 26 | 27 | [Device Settings] 28 | 29 | Setup ID = 0x00000001 30 | Setup Format = 0x03 31 | Security = ENCRYPTED (2) 32 | Bond Timeout = 600 33 | Security Request Delay = 10 34 | Change Timing Delay = 5 35 | Whitelist = Disabled 36 | 37 | [Advertisement Data] 38 | 39 | Bond Advertise = 0x00000810 [LOCAL_NAME_COMPLETE | SERVICE_SOL_128] 40 | Bond Scan Resp = 0x00000810 [LOCAL_NAME_COMPLETE | SERVICE_SOL_128] 41 | General Advertise = 0x00000810 [LOCAL_NAME_COMPLETE | SERVICE_SOL_128] 42 | General Scan Resp = 0x00000810 [LOCAL_NAME_COMPLETE | SERVICE_SOL_128] 43 | Broadcast Advertise = 0x00000000 [] 44 | Broadcast Scan Resp = 0x00000000 [] 45 | 46 | Custom Bond Advertise = 0x00 [] 47 | Custom Bond Scan Resp = 0x00 [] 48 | Custom General Advertise = 0x00 [] 49 | Custom General Scan Resp = 0x00 [] 50 | Custom Broadcast Advertise = 0x00 [] 51 | Custom Broadcast Scan Resp = 0x00 [] 52 | 53 | No custom AD types 54 | 55 | [Vendor Specific UUIDs] 56 | 57 | VS UUID #0 (type=0x02): 0xD0 0x00 0x2D 0x12 0x1E 0x4B 0x0F 0xA4 0x99 0x4E 0xCE 0xB5 0x00 0x00 0x05 0x79 58 | VS UUID #1 (type=0x03): 0xBD 0x1D 0xA2 0x99 0xE6 0x25 0x58 0x8C 0xD9 0x42 0x01 0x63 0x00 0x00 0xBF 0x9F 59 | VS UUID #2 (type=0x04): 0xFB 0x7B 0x7C 0xCE 0x6A 0xB3 0x44 0xBE 0xB5 0x4B 0xD6 0x24 0x00 0x00 0xEA 0x22 60 | VS UUID #3 (type=0x05): 0xD9 0xD9 0xAA 0xFD 0xBD 0x9B 0x21 0x98 0xA8 0x49 0xE1 0x45 0x00 0x00 0xD1 0x69 61 | 62 | [Local Database] 63 | 64 | Handle Pipes Structure 65 | ------ ----- --------- 66 | 0x0001 +----- Service (Primary): "GAP" (01:0x1800) 67 | 0x0002 |----- |Characteristic: "Device Name" (01:0x2A00) [rd] [rd:allow|wr:none] 68 | 0x0003 |Value: {0x4E 0x6F 0x74 0x69 0x66} [rd:allow|wr:none] 69 | 0x0004 |----- |Characteristic: "Appearance" (01:0x2A01) [rd] [rd:allow|wr:none] 70 | 0x0005 |Value: {0x40 0x02} [rd:allow|wr:none] 71 | 0x0006 |----- |Characteristic: "PPCP" (01:0x2A04) [rd] [rd:allow|wr:none] 72 | 0x0007 |Value: {0x90 0x01 0x20 0x03 0x00 0x00 0x58 0x02} [rd:allow|wr:none] 73 | 0x0008 +----- Service (Primary): "GATT" (01:0x1801) 74 | 0x0009 |----- |Characteristic: "Service Changed" (01:0x2A05) [rd|ind] [rd:allow|wr:none] 75 | 0x000A > |Value: {0x00 0x00 0x00 0x00} [rd:enc|wr:none] 76 | 0x000B |----- |Descriptor: "Client Characteristic Configuration" (01:0x2902) Value: {0x00 0x00} [rd:allow|wr:enc] 77 | 78 | [Remote Database] 79 | 80 | Handle Pipes Structure 81 | ------ ----- --------- 82 | -- +----- Service: "?" (02:0xF431) 83 | -- < |----- |Characteristic: "?" (03:0x120D) 84 | -- < |----- |Characteristic: "?" (04:0xC6E9) 85 | -- > |----- |Characteristic: "?" (05:0xD8F3) 86 | -- +----- Service: "GATT" (01:0x1801) 87 | -- < |----- |Characteristic: "Service Changed" (01:0x2A05) 88 | 89 | [Pipe Map] 90 | 91 | Pipe Store Type Service Char. CPF Desc. 92 | ---- ------ ------ ---------- --------- ----------- --------- 93 | 01 Local TX_ACK 01:0x1801 01:0x2A05 -- -- 94 | 02 Remote RX 02:0xF431 03:0x120D -- -- 95 | 03 Remote RX 02:0xF431 04:0xC6E9 -- -- 96 | 04 Remote TX_ACK 02:0xF431 05:0xD8F3 -- -- 97 | 05 Remote RX_ACK 01:0x1801 01:0x2A05 -- -- 98 | 99 | [Setup Data] 100 | 101 | 07-06-00-00-03-02-41-FE 102 | 1F-06-10-00-01-00-00-00-02-02-01-04-05-01-01-00-00-06-00-04-00-00-00-00-00-00-00-00-00-00-00-00 103 | 1F-06-10-1C-00-00-10-00-00-00-00-00-00-00-00-00-00-F4-31-02-00-00-08-10-00-00-08-10-03-90-04-64 104 | 1F-06-10-38-02-FF-02-58-0A-05-00-00-08-10-00-00-08-10-00-00-00-00-00-00-00-00-00-00-00-00-00-00 105 | 05-06-10-54-01-00 106 | 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 107 | 1F-06-20-1C-05-00-03-2A-00-01-4E-6F-74-69-66-63-73-65-6D-69-2E-63-6F-6D-00-00-00-00-00-00-04-04 108 | 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-40-02-04-04-05-05-00 109 | 1F-06-20-54-06-28-03-01-02-07-00-04-2A-06-04-09-08-00-07-2A-04-01-90-01-20-03-00-00-58-02-04-04 110 | 1F-06-20-70-02-02-00-08-28-00-01-01-18-04-04-05-05-00-09-28-03-01-22-0A-00-05-2A-26-0C-05-04-00 111 | 17-06-20-8C-0A-2A-05-01-00-00-00-00-46-34-03-02-00-0B-29-02-01-00-00-00 112 | 0D-06-30-00-F4-31-02-01-03-18-01-01-04-01 113 | 1F-06-40-00-2A-05-01-00-04-04-00-0A-00-0B-12-0D-03-00-08-04-00-00-00-00-C6-E9-04-00-08-04-00-00 114 | 19-06-40-1C-00-00-D8-F3-05-00-04-04-00-00-00-00-2A-05-01-00-10-04-00-00-00-00 115 | 1F-06-50-00-D0-00-2D-12-1E-4B-0F-A4-99-4E-CE-B5-00-00-05-79-BD-1D-A2-99-E6-25-58-8C-D9-42-01-63 116 | 1F-06-50-1C-00-00-BF-9F-FB-7B-7C-CE-6A-B3-44-BE-B5-4B-D6-24-00-00-EA-22-D9-D9-AA-FD-BD-9B-21-98 117 | 0B-06-50-38-A8-49-E1-45-00-00-D1-69 118 | 12-06-60-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 119 | 06-06-F0-00-83-E0-F9 120 | -------------------------------------------------------------------------------- /utilities.cpp: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | 5 | #include "utilities.h" 6 | 7 | void free_ram (void) { 8 | extern int __heap_start, *__brkval; 9 | int v; 10 | Serial.print(F("[free SRAM] ")); 11 | Serial.print((int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval)); 12 | Serial.println(F(" bytes")); 13 | } 14 | 15 | void serial_print_char(char c) { 16 | if (c >= 32 && c < 128) { 17 | Serial.print((char)c); 18 | } else { 19 | Serial.print(F("0x")); 20 | Serial.print(c, HEX); 21 | } 22 | Serial.print(' '); 23 | } 24 | -------------------------------------------------------------------------------- /utilities.h: -------------------------------------------------------------------------------- 1 | /** (c)2013, Bernard Pratz, bernard at pratz dot net 2 | * under the WTFPL License 3 | */ 4 | #ifndef _UTILITIES_H_ 5 | #define _UTILITIES_H_ 6 | 7 | #include 8 | 9 | void free_ram (void); 10 | void serial_print_char(char c); 11 | 12 | #ifdef DEBUG1 13 | #define debug_println(...) Serial.println(__VA_ARGS__) 14 | #define debug_print(...) Serial.print(__VA_ARGS__) 15 | #else 16 | #define debug_println(...) 17 | #define debug_print(...) 18 | #endif 19 | 20 | #ifdef DEBUG2 21 | #define debug2_println(...) Serial.println(__VA_ARGS__) 22 | #define debug2_print(...) Serial.print(__VA_ARGS__) 23 | #else 24 | #define debug2_println(...) 25 | #define debug2_print(...) 26 | #endif 27 | 28 | #ifdef DEBUG3 29 | #define debug3_println(...) Serial.println(__VA_ARGS__) 30 | #define debug3_print(...) Serial.print(__VA_ARGS__) 31 | #else 32 | #define debug3_println(...) 33 | #define debug3_print(...) 34 | #endif 35 | 36 | static uint8_t base=0; 37 | inline Print &operator <<(Print &obj, unsigned long arg) 38 | { 39 | switch (base) { 40 | case HEX: obj.print("0x"); break; 41 | case BIN: obj.print("0b"); break; 42 | } 43 | obj.print(arg, (int)base); 44 | base = 0; 45 | return obj; 46 | } 47 | template 48 | inline Print &operator <<(Print &obj, T arg) 49 | { 50 | obj.print(arg); 51 | return obj; 52 | } 53 | inline unsigned long hex(unsigned long arg) 54 | { 55 | base = HEX; 56 | return arg; 57 | } 58 | inline unsigned long bin(unsigned long arg) 59 | { 60 | base = BIN; 61 | return arg; 62 | } 63 | 64 | #define endl F("\n") 65 | #define cout Serial 66 | 67 | #endif 68 | --------------------------------------------------------------------------------