aboutsummaryrefslogtreecommitdiffstats
path: root/arduino/libraries/Bluefruit52Lib/examples/Peripheral/ancs_oled/ancs_oled.ino
diff options
context:
space:
mode:
Diffstat (limited to 'arduino/libraries/Bluefruit52Lib/examples/Peripheral/ancs_oled/ancs_oled.ino')
-rwxr-xr-xarduino/libraries/Bluefruit52Lib/examples/Peripheral/ancs_oled/ancs_oled.ino458
1 files changed, 458 insertions, 0 deletions
diff --git a/arduino/libraries/Bluefruit52Lib/examples/Peripheral/ancs_oled/ancs_oled.ino b/arduino/libraries/Bluefruit52Lib/examples/Peripheral/ancs_oled/ancs_oled.ino
new file mode 100755
index 0000000..ce91a31
--- /dev/null
+++ b/arduino/libraries/Bluefruit52Lib/examples/Peripheral/ancs_oled/ancs_oled.ino
@@ -0,0 +1,458 @@
+/*********************************************************************
+ This is an example for our nRF52 based Bluefruit LE modules
+
+ Pick one up today in the adafruit shop!
+
+ Adafruit invests time and resources providing this open source code,
+ please support Adafruit and open-source hardware by purchasing
+ products from Adafruit!
+
+ MIT license, check LICENSE for more information
+ All text above, and the splash screen below must be included in
+ any redistribution
+*********************************************************************/
+
+/*
+ * This sketch is similar to 'ancs', but it also uses a Feather OLED
+ * Wing to display incoming ANCS alerts:
+ * https://www.adafruit.com/product/2900
+ *
+ * BUTTON A: Up or accept call
+ * BUTTON B: Not used since it is hard to press
+ * BUTTON C: Down or decline call
+ */
+#include <Wire.h>
+#include <Adafruit_GFX.h>
+#include <Adafruit_SSD1306.h>
+#include <bluefruit.h>
+
+/*------------- OLED and Buttons -------------*/
+#if defined ARDUINO_NRF52_FEATHER
+// Feather nRF52832
+#define BUTTON_A 31
+#define BUTTON_B 30
+#define BUTTON_C 27
+
+#elif defined ARDUINO_NRF52840_FEATHER
+// Feather nRF52840
+#define BUTTON_A 9
+#define BUTTON_B 6
+#define BUTTON_C 5
+
+#else
+#error board not supported
+#endif
+
+
+#define OLED_RESET 4 // TODO remove ?
+Adafruit_SSD1306 oled(OLED_RESET);
+
+
+/*------------- Notification List -------------*/
+#define MAX_COUNT 20
+#define BUFSIZE 64
+
+typedef struct
+{
+ AncsNotification_t ntf;
+ char title[BUFSIZE];
+ char message[BUFSIZE];
+ char app_name[BUFSIZE];
+} MyNotif_t;
+
+MyNotif_t myNotifs[MAX_COUNT] = { 0 };
+
+// Number of notifications
+int notifCount = 0;
+
+/*------------- Display Management -------------*/
+#define ONSCREEN_TIME 5000 // On-screen time for each notification
+
+int activeIndex = 0; // Index of currently displayed notification
+int displayIndex = -1; // Index of notification about to display
+
+uint32_t drawTime = 0; // Last time oled display notification
+
+/*------------- BLE Client Service-------------*/
+BLEAncs bleancs;
+
+void setup()
+{
+ // Button configured
+ pinMode(BUTTON_A, INPUT_PULLUP);
+ pinMode(BUTTON_B, INPUT_PULLUP);
+ pinMode(BUTTON_C, INPUT_PULLUP);
+
+ // init with the I2C addr 0x3C (for the 128x32) and show splashscreen
+ oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
+ oled.display();
+
+ oled.setTextSize(1);// max is 4 line, 21 chars each
+ oled.setTextColor(WHITE);
+
+ // Config the peripheral connection with maximum bandwidth
+ // more SRAM required by SoftDevice
+ // Note: All config***() function must be called before begin()
+ //Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
+
+ Bluefruit.begin();
+ // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
+ Bluefruit.setTxPower(4);
+ Bluefruit.setName("Bluefruit52");
+ Bluefruit.setConnectCallback(connect_callback);
+ Bluefruit.setDisconnectCallback(disconnect_callback);
+
+ // Configure and Start Service
+ bleancs.begin();
+ bleancs.setNotificationCallback(ancs_notification_callback);
+
+ // Set up and start advertising
+ startAdv();
+
+ // splash screen effect
+ delay(100);
+
+ oled.clearDisplay();
+ oled.setCursor(0, 0);
+ oled.println("Not connected");
+ oled.display();
+}
+
+void startAdv(void)
+{
+ // Advertising packet
+ Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
+ Bluefruit.Advertising.addTxPower();
+
+ // Include ANCS 128-bit uuid
+ Bluefruit.Advertising.addService(bleancs);
+
+ // Secondary Scan Response packet (optional)
+ // Since there is no room for 'Name' in Advertising packet
+ Bluefruit.ScanResponse.addName();
+
+ /* Start Advertising
+ * - Enable auto advertising if disconnected
+ * - Interval: fast mode = 20 ms, slow mode = 152.5 ms
+ * - Timeout for fast mode is 30 seconds
+ * - Start(timeout) with timeout = 0 will advertise forever (until connected)
+ *
+ * For recommended advertising interval
+ * https://developer.apple.com/library/content/qa/qa1931/_index.html
+ */
+ Bluefruit.Advertising.restartOnDisconnect(true);
+ Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
+ Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
+ Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
+}
+
+void loop()
+{
+ // If service is not yet discovered
+ if ( !bleancs.discovered() ) return;
+
+ // No notifications, do nothing
+ if ( notifCount == 0 ) return;
+
+ // Check buttons
+ uint32_t presedButtons = readPressedButtons();
+
+ if ( myNotifs[activeIndex].ntf.categoryID == ANCS_CAT_INCOMING_CALL )
+ {
+ /* Incoming call event
+ * - Button A to accept call
+ * - Button C to decline call
+ */
+ if ( presedButtons & bit(BUTTON_A) )
+ {
+ bleancs.actPositive(myNotifs[activeIndex].ntf.uid);
+ }
+
+ if ( presedButtons & bit(BUTTON_C) )
+ {
+ bleancs.actNegative(myNotifs[activeIndex].ntf.uid);
+ }
+ }
+ else
+ {
+ /* Normal events navigation (wrap around)
+ * - Button A to display previous notification
+ * - Button C to display next notification
+ *
+ * When a notification is display ONSCREEN_TIME,
+ * we will display the next one
+ */
+ if ( presedButtons & bit(BUTTON_A) )
+ {
+ displayIndex = (activeIndex != 0) ? (activeIndex-1) : (notifCount-1) ;
+ }
+
+ if ( presedButtons & bit(BUTTON_C) )
+ {
+ displayIndex = (activeIndex != (notifCount-1)) ? (activeIndex + 1) : 0;
+ }
+
+ // Display requested notification
+ if ( displayIndex >= 0 )
+ {
+ activeIndex = displayIndex;
+ displayIndex = -1;
+
+ displayNotification(activeIndex);
+ drawTime = millis(); // Save time we draw
+ }
+ // Display next notification if time is up
+ else if ( drawTime + ONSCREEN_TIME < millis() )
+ {
+ activeIndex = (activeIndex+1)%notifCount;
+
+ displayNotification(activeIndex);
+ drawTime = millis(); // Save time we draw
+ }
+ }
+}
+
+/**
+ * Display notification contents to oled screen
+ * @param index index of notification
+ */
+void displayNotification(int index)
+{
+ // safeguard
+ if ( index < 0 || (index >= notifCount) ) return;
+
+ // let's Turn on and off RED LED when we draw to get attention
+ digitalWrite(LED_RED, HIGH);
+
+ /*------------- Display to OLED -------------*/
+ MyNotif_t* myNtf = &myNotifs[index];
+
+ oled.clearDisplay();
+ oled.setCursor(0, 0);
+
+ // Incoming call event, display a bit differently
+ if ( myNtf->ntf.categoryID == ANCS_CAT_INCOMING_CALL )
+ {
+ oled.println(myNtf->title);
+ oled.println(" is calling");
+ oled.println(" Btn A to ACCEPT");
+ oled.println(" Btn C to DECLINE");
+ }else
+ {
+ // Text size = 1, max char is 21. Text size = 2, max char is 10
+ char tempbuf[22];
+ sprintf(tempbuf, "%-15s %02d/%02d", myNtf->app_name, index+1, notifCount);
+
+ oled.println(tempbuf);
+ oled.println(myNtf->title);
+
+ oled.print(" ");
+ oled.print(myNtf->message);
+ }
+
+ oled.display();
+
+ digitalWrite(LED_RED, LOW);
+}
+
+/**
+ * Connect Callback
+ * Perform ANCS discovering, request Pairing
+ */
+void connect_callback(uint16_t conn_handle)
+{
+ oled.clearDisplay();
+ oled.setCursor(0, 0);
+ oled.println("Connected.");
+ oled.print("Discovering ... ");
+ oled.display();
+
+ if ( bleancs.discover( conn_handle ) )
+ {
+ oled.println("OK");
+
+ // ANCS requires pairing to work
+ oled.print("Paring ... ");
+
+ oled.display();
+
+ if ( Bluefruit.requestPairing() )
+ {
+ oled.println("OK");
+
+ bleancs.enableNotification();
+ oled.println("Receiving ...");
+ }else
+ {
+ oled.println("Failed");
+ }
+ }else
+ {
+ oled.println("Failed");
+ }
+
+ oled.display();
+}
+
+/**
+ * Notification callback
+ * @param notif Notification from iDevice
+ *
+ * Save/Modify notification into myNotifs struct to display later
+ */
+void ancs_notification_callback(AncsNotification_t* notif)
+{
+ if (notif->eventID == ANCS_EVT_NOTIFICATION_ADDED )
+ {
+ myNotifs[ notifCount ].ntf = *notif;
+
+ /*------------- Retrieve Title, Message, App Name -------------*/
+ MyNotif_t* myNtf = &myNotifs[notifCount];
+ uint32_t uid = myNtf->ntf.uid;
+
+ // iDevice often include Unicode "Bidirection Text Control" in the Title.
+ // Mostly are U+202D as beginning and U+202C as ending. Let's remove them
+ if ( bleancs.getTitle (uid, myNtf->title , BUFSIZE) )
+ {
+ char u202D[3] = { 0xE2, 0x80, 0xAD }; // U+202D in UTF-8
+ char u202C[3] = { 0xE2, 0x80, 0xAC }; // U+202C in UTF-8
+
+ int len = strlen(myNtf->title);
+
+ if ( 0 == memcmp(&myNtf->title[len-3], u202C, 3) )
+ {
+ len -= 3;
+ myNtf->title[len] = 0; // chop ending U+202C
+ }
+
+ if ( 0 == memcmp(myNtf->title, u202D, 3) )
+ {
+ memmove(myNtf->title, myNtf->title+3, len-2); // move null-terminator as well
+ }
+ }
+
+ bleancs.getMessage(uid, myNtf->message , BUFSIZE);
+ bleancs.getAppName(uid, myNtf->app_name, BUFSIZE);
+
+ displayIndex = notifCount++; // display new notification
+ }else if (notif->eventID == ANCS_EVT_NOTIFICATION_REMOVED )
+ {
+ for(int i=0; i<notifCount; i++)
+ {
+ if ( notif->uid == myNotifs[i].ntf.uid )
+ {
+ // remove by swapping with the last one
+ notifCount--;
+ myNotifs[i] = myNotifs[notifCount];
+
+ // Invalid removed data
+ memset(&myNotifs[notifCount], 0, sizeof(MyNotif_t));
+
+ if (activeIndex == notifCount)
+ {
+ // If remove the last notification, adjust display index
+ displayIndex = notifCount-1;
+ }else if (activeIndex == i)
+ {
+ // Re-draw if remove currently active one
+ displayIndex = activeIndex;
+ }
+
+ break;
+ }
+ }
+ }else
+ {
+ // Modification
+ for(int i=0; i<notifCount; i++)
+ {
+ if ( notif->uid == myNotifs[i].ntf.uid )
+ {
+ // Display modification
+ displayIndex = i;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Callback invoked when a connection is dropped
+ * @param conn_handle connection where this event happens
+ * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
+ * https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/cores/nRF5/nordic/softdevice/s140_nrf52_6.1.1_API/include/ble_hci.h
+ */
+void disconnect_callback(uint16_t conn_handle, uint8_t reason)
+{
+ (void) conn_handle;
+ (void) reason;
+
+ // reset notification array
+ notifCount = 0;
+ activeIndex = 0;
+ displayIndex = -1;
+
+ memset(myNotifs, 0, sizeof(myNotifs));
+
+ oled.clearDisplay();
+ oled.setCursor(0, 0);
+ oled.println("Not connected");
+ oled.display();
+}
+
+/**
+ * Check if button A,B,C state are pressed, include some software
+ * debouncing.
+ *
+ * Note: Only set bit when Button is state change from
+ * idle -> pressed. Press and hold only report 1 time, release
+ * won't report as well
+ *
+ * @return Bitmask of pressed buttons e.g If BUTTON_A is pressed
+ * bit 31 will be set.
+ */
+uint32_t readPressedButtons(void)
+{
+ // must be exponent of 2
+ enum { MAX_CHECKS = 8, SAMPLE_TIME = 10 };
+
+ /* Array that maintains bounce status/, which is sampled
+ * 10 ms each. Debounced state is regconized if all the values
+ * of a button has the same value (bit set or clear)
+ */
+ static uint32_t lastReadTime = 0;
+ static uint32_t states[MAX_CHECKS] = { 0 };
+ static uint32_t index = 0;
+
+ // Last Debounced state, used to detect changed
+ static uint32_t lastDebounced = 0;
+
+ // Too soon, nothing to do
+ if (millis() - lastReadTime < SAMPLE_TIME ) return 0;
+
+ lastReadTime = millis();
+
+ // Take current read and masked with BUTTONs
+ // Note: Bitwise inverted since buttons are active (pressed) LOW
+ uint32_t debounced = ~(*portInputRegister( digitalPinToPort(0) ));
+ debounced &= (bit(BUTTON_A) | bit(BUTTON_B) | bit(BUTTON_C));
+
+ // Copy current state into array
+ states[ (index & (MAX_CHECKS-1)) ] = debounced;
+ index++;
+
+ // Bitwise And all the state in the array together to get the result
+ // This means pin must stay at least MAX_CHECKS time to be realized as changed
+ for(int i=0; i<MAX_CHECKS; i++)
+ {
+ debounced &= states[i];
+ }
+
+ // result is button changed and current debounced is set
+ // Mean button is pressed (idle previously)
+ uint32_t result = (debounced ^ lastDebounced) & debounced;
+
+ lastDebounced = debounced;
+
+ return result;
+}