Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
Ever been comfy on the couch and left your phone in the other room, then you hear the message alert tone. Do you get up and check if its something important, or take the chance that its just spam about an accident you didn't have.
Well now you don't need to move from your seat, with this hack the Arduino will recieve the message alerts using the ANCS (Apple Notification Centre Service) from your iPhone and push them up to a server instantly, you can then just ask Alexa to 'check my notifications' and she will read the message to you.
serialtopost.ino
ArduinoThis is the code for the Arduino Ethernet which recieves the string of JSON over a soft serial interface from the Bluefruit and then posts it to my web app.
#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>
byte mac[] = {
0x90, 0xA2, 0xDA, 0x00, 0x6A, 0xF0
};
char server[] = "example.com";
char incomingSerialData[300]; // A character array to store received bytes
char c ;
int incomingSerialDataIndex = 0;
EthernetClient client;
SoftwareSerial mySerial(2, 3);
void setup() {
// start the Ethernet connection:
Ethernet.begin(mac);
Serial.begin(115200);
mySerial.begin(9600);
Serial.println(Ethernet.localIP());
postToServer();
}
void postToServer() {
Serial.println("POSTing...");
String PostData=incomingSerialData;
Serial.println(PostData);
if (client.connect(server, 80)) {
Serial.println("connected");
client.println("POST /event HTTP/1.1");
client.println("Host: sammachin.ngrok.io");
client.println("User-Agent: Arduino/1.0");
client.println("Connection: close");
client.println("Content-Type: application/json;");
client.print("Content-Length: ");
client.println(PostData.length());
client.println();
client.println(PostData);
Serial.println("sent");
client.stop();
} else {
Serial.println("connection failed");
}
}
void loop() {
while(mySerial.available() > 0)
{
c = mySerial.read();
incomingSerialData[incomingSerialDataIndex] = c; // Add the incoming byte to the array
incomingSerialDataIndex++; // Ensure the next byte is added in the next position
Serial.print(c);
switch (c){
case '\r':
case '\n':
Serial.println('CRLF Received');
postToServer();
mySerial.flush();
incomingSerialData[incomingSerialDataIndex] = '\0';
break;
}
}
}
ancstojson.ino
ArduinoThis is the code running on the Bluefruit it implements the ancs protocol deals with pairing and bonding to the iPhone then when a notificaiton is recieved on the iPhone it takes reads that using BLE and writes it out as a JSON string over the serial interface to the Arduino Ethernet
#include <bluefruit.h>
// BLE Client Service
BLEClientDis bleClientDis;
BLEAncs bleancs;
char buffer[128];
// Check BLEAncs.h for AncsNotification_t
const char* EVENT_STR[] = { "Added", "Modified", "Removed" };
const char* CAT_STR [] =
{
"Other" , "Incoming Call" , "Missed Call", "Voice Mail" ,
"Social" , "Schedule" , "Email" , "News" ,
"Health and Fitness", "Business and Finance", "Location" , "Entertainment"
};
void setup()
{
Serial.begin(9600);
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 DIS client
bleClientDis.begin();
// Configure ANCS client
bleancs.begin();
bleancs.setNotificationCallback(ancs_notification_callback);
// Set up and start advertising
startAdv();
}
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();
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()
{
// Not connected, wait for a connection
if ( !Bluefruit.connected() ) return;
// If service is not yet discovered
if ( !bleancs.discovered() ) return;
// Your code here
}
void connect_callback(uint16_t conn_handle)
{
Serial.print("Discovering DIS ... ");
if ( bleClientDis.discover(conn_handle) )
{
// Read and print Manufacturer string
memset(buffer, 0, sizeof(buffer));
if ( bleClientDis.getManufacturer(buffer, sizeof(buffer)) )
{
Serial.print('{"Manufacturer" : "');
Serial.print(buffer);
Serial.print('"},' );
}
// Read and print Model Number string
memset(buffer, 0, sizeof(buffer));
if ( bleClientDis.getModel(buffer, sizeof(buffer)) )
{
Serial.print('{"Model" : "');
Serial.print(buffer);
Serial.print('"},' );
}
}
if ( bleancs.discover(conn_handle) )
{
// ANCS requires pairing to work, it makes sense to request security here as well
if ( Bluefruit.requestPairing() )
{
bleancs.enableNotification();
Serial.print('{"status" : "connected"}');
Serial.println();
}
}
}
void ancs_notification_callback(AncsNotification_t* notif)
{
int n;
Serial.printf("{ \"Event\" : \"%s\", ", EVENT_STR[notif->eventID]);
// Print Category with padding
n = Serial.printf("\"Category\" : \"%s-%d\", ", CAT_STR[notif->categoryID], notif->categoryCount);
// Get notification Title
memset(buffer, 0, sizeof(buffer));
bleancs.getAttribute(notif->uid, ANCS_ATTR_TITLE, buffer, sizeof(buffer));
Serial.printf("\"title\" : \"%s\", ", buffer);
// Get notification Message
memset(buffer, 0, sizeof(buffer));
bleancs.getAttribute(notif->uid, ANCS_ATTR_MESSAGE, buffer, sizeof(buffer));
Serial.printf("\"message\" : \"%s\", ", buffer);
// Get App ID and store in the app_id variable
char app_id[64] = { 0 };
memset(buffer, 0, sizeof(buffer));
bleancs.getAttribute(notif->uid, ANCS_ATTR_APP_IDENTIFIER, buffer, sizeof(buffer));
strcpy(app_id, buffer);
Serial.printf(" \"app_id\" : \"%s\", ", app_id);
// Get Application Name
memset(buffer, 0, sizeof(buffer));
bleancs.getAppAttribute(app_id, ANCS_APP_ATTR_DISPLAY_NAME, buffer, sizeof(buffer));
Serial.printf("\"appname\" : \"%s\" }", buffer);
Serial.println();
}
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) reason;
}
server.py
PythonThis is the server which both receives the POSTed alert data from the arudio and then returns an appropriate response to the Alexa Skills Kit to read the latest message, its a simple server which stores the latest unread alert in a local pickle file.
You can run it on a local machine exposed to the internet via ngrok
You can run it on a local machine exposed to the internet via ngrok
from flask import Flask, request, jsonify
import json
import pickle
app = Flask(__name__)
alert = None
@app.route('/notifications', methods=['POST'])
def alexaHandler():
with open('alert.pkl', "r") as f:
alert = pickle.load(f)
resp = {
'version': '1.0',
'response': {
'outputSpeech': {
'text': 'You have no new notifications',
'type': 'PlainText'
},
'shouldEndSession': True
},
'sessionAttributes': {}
}
if alert != None:
resp['response']['outputSpeech']['text'] = "You have a new {} notification, from, {}, {}".format(alert['appname'], alert['title'], alert['message'])
return json.dumps(resp)
@app.route('/event', methods=['POST'])
def eventHandler():
if request.is_json:
data = request.get_json()
if data['Event'] == "Added":
with open('alert.pkl', 'w') as f:
pickle.dump(data, f)
else:
data = None
with open('alert.pkl', 'w') as f:
pickle.dump(data, f)
return 'ok'
if __name__ == '__main__':
app.run()
Comments