Mbed Smart Watch with Android Notifications

Team Members
- Jordan Keller
- Haines Todd
- Sorya Tith
- Gerrod Uben
Project Description
A custom android application forwards notifications and the current time to an mbed via bluetooth. The mbed has an LCD display with a faux analog watchface and speaker that chimes when an android notification is received. When a notification is received, the text is displayed on the lcd screen until a button is pressed to dismiss the notification and restore the watchface.
Hardware Items Used
- mbed
- uLCD-144-G2 Smart Color LCD
- Adafruit Bluefruit LE UART
- Sparkfun Audio Amplifier and speaker
Block Diagram

Libraries Used
- uLCD-144-G2 library https://os.mbed.com/users/4180_1/code/4DGL-uLCD-SE/#2cb1845d7681
- RPC Interface Library https://os.mbed.com/users/MichaelW/code/RPCInterface/#bcc2e05e5da4
- time.h
- math.h
- mbed rtos
RPC Commands Used
- Setting Time - /setTime/run <unix time> <UTC offset>
- Sending Notification - /notify/run <notification title> <notification message>
Program
Import programECE4180_Final_Project
Final project that implements smart watch features on the mbed
Program Main.c
main.c
#include "mbed.h"
#include "rtos.h"
#include "mbed_rpc.h"
#include "uLCD_4DGL.h"
#include <time.h>
#include <math.h>
#include "chime.h"
/* Example RPC commands that have currently been implemented
/notify/run <notification title> <notification message>
/setTime/run <unix time> <UTC offset (-5 for Atlanta)>
/setTime/run 1256729737 -5
*/
volatile bool display_notification = false;
volatile bool display_time = true;
volatile int utc_offset; //keeps track of the current timezone of the watch
uLCD_4DGL uLCD(p9,p10,p11); // serial tx, serial rx, reset pin;
//Serial bluetooth(p13,p14);
Serial pc(USBTX, USBRX);
InterruptIn view_button(p12);
Mutex stdio_mutex; //mutex used when accessing stdio functions
Mutex lcd_mutex; //mutex used when accessing the lcd object
Thread bluetooth_thread; //thread responsible for receiving rpc commands over bluetooth
Thread time_thread; //thread responsible for updating the lcd with the current time
//rpc function prototypes
void display_notification_rpc_func(Arguments *in, Reply *out);
void set_time_rpc_func (Arguments *in, Reply *out);
RPCFunction rpcWriteLCD(&display_notification_rpc_func, "notify");
RPCFunction rpcset_time_rpc_func(&set_time_rpc_func, "setTime");
#define sample_freq 16000.0
AnalogOut speaker(p18);
Ticker notification_chime_ticker;
//interrupt routine to play next audio sample from array in flash
void audio_sample () {
static int i=0;
speaker.write_u16(((uint16_t)data[i]<<7));
i++;
if (i>= sizeof(data)) {
i = 0;
notification_chime_ticker.detach();
}
}
//interrupt routine for when the input button is pressed
//when the view button is pressed, dismiss the currently shown notification and display the current time
void view_button_pressed(void){
display_notification = false;
display_time = true;
}
//flip the y coordinate around so that standard cartesian coordinates can be used
int flipy(int y_coord){
return (128-y_coord);
}
#define C_X 64
#define C_Y 64
#define M_PI 3.141592
//create the tick marks for an analog clock on the lcd display
void setup_analog_clock(uint32_t color){
lcd_mutex.lock();
uLCD.filled_circle(64, 64, 5, color); //centercircle
double angle;
//start from 3 oclock and draw all the clock tick marks counter-clockwise
for(angle = 0; angle < (2*M_PI)-(M_PI/12); angle += M_PI/6){
uLCD.line(54*cos(angle)+C_X,flipy(54*sin(angle)+C_Y), 64*cos(angle)+C_X,flipy(64*sin(angle)+C_Y), color); //3 oclock tick mark
}
lcd_mutex.unlock();
}
#define RAD_PER_SEC (2*M_PI)/60
#define RAD_PER_MIN (2*M_PI)/60
#define RAD_PER_HOUR (2*M_PI)/12
//function to handle the display of the time on the lcd screen
void show_time_analog(int sec, int minute, int hour, int day, int month, int year, uint32_t sec_color, uint32_t min_color, uint32_t hour_color, uint32_t back_color) {
static double angle;
static int prev_sec;
static int prev_minute;
static int prev_hour;
lcd_mutex.lock();
//tear down the previous hands that were drawn
angle = -(RAD_PER_SEC*prev_sec) + M_PI/2;
uLCD.line(C_X,C_Y,64*cos(angle)+C_X, flipy(64*sin(angle)+C_Y),back_color);
angle = -(RAD_PER_MIN*prev_minute) + M_PI/2;
uLCD.line(C_X,C_Y,52*cos(angle)+C_X, flipy(52*sin(angle)+C_Y),back_color);
angle = -(RAD_PER_HOUR*prev_hour) + M_PI/2;
uLCD.line(C_X,C_Y,40*cos(angle)+C_X, flipy(40*sin(angle)+C_Y),back_color);
//draw the new hands
angle = -(RAD_PER_SEC*sec) + M_PI/2;
uLCD.line(C_X,C_Y,64*cos(angle)+C_X, flipy(64*sin(angle)+C_Y),sec_color);
angle = -(RAD_PER_MIN*minute) + M_PI/2;
uLCD.line(C_X,C_Y,52*cos(angle)+C_X, flipy(52*sin(angle)+C_Y),min_color);
angle = -(RAD_PER_HOUR*hour) + M_PI/2;
uLCD.line(C_X,C_Y,40*cos(angle)+C_X, flipy(40*sin(angle)+C_Y),hour_color);
stdio_mutex.lock();
//print the current date in a month/day/year format
uLCD.locate(4,10);
uLCD.printf("%2d/%2d/%4d",month, day, year);
uLCD.locate(8,11);
if (hour < 12) {uLCD.printf("AM");}
else {uLCD.printf("PM");}
stdio_mutex.unlock();
lcd_mutex.unlock();
//store the location of the current hands
prev_sec = sec;
prev_minute = minute;
prev_hour = hour;
}
//function to update the time displayed on the lcd approximately every second
void time_thread_func() {
struct tm * t; //time struct defined in time.h
static time_t unix_time; //the time in unix time
static bool prev_display_time; //indicates whether time was being displayed the last time the thread ran
while (true) {
if (display_time == true) {
if (prev_display_time == false){ //clear whatever was previously on the screen
lcd_mutex.lock();
uLCD.cls();
lcd_mutex.unlock();
}
unix_time = time(NULL);
t = localtime(&unix_time);
setup_analog_clock(WHITE);
int hour = (t->tm_hour + utc_offset);
if (hour < 0){ hour += 24;}
else if (hour >= 24) {hour -= 24;}
show_time_analog(t->tm_sec,t->tm_min,hour,t->tm_mday,t->tm_mon+1,t->tm_year+1900,RED+BLUE,WHITE,BLUE,BLACK);
}
prev_display_time = display_time;
Thread::wait(1000); //only update every second
}
}
//function to continuously take in characters over bluetooth serial and parse them as RPC commands
void bluetooth_thread_func() {
char buf[256], outbuf[256];
uint16_t buf_pos = 0;
while(true) {
if (pc.readable() == true) { //comment out when using bluetooth to receive rpc commands
//if (bluetooth.readable() == true) {
stdio_mutex.lock();
buf[buf_pos] = pc.getc(); //comment out when using bluetooth to receive rpc commands
//buf[buf_pos] = bluetooth.getc();
stdio_mutex.unlock();
if (buf[buf_pos] == '\n') { //the end of the RPC command has been received
buf[buf_pos] = '\0'; //replace the newline character with a null character
buf_pos = 0;
RPC::call(buf, outbuf); //make an RPC call
stdio_mutex.lock();
pc.printf("%s\n", outbuf); //send the response
stdio_mutex.unlock();
}
else {
buf_pos++;
}
} else {
Thread::yield();
}
}
}
//RPC function to receive the current time
//the first argument is unix time and the second argument is the offset from UTC time
void set_time_rpc_func (Arguments *in, Reply *out) {
static const char * unix_time_str;
uint32_t unix_time;
int offset;
unix_time_str = in->getArg<const char*>(); //get a pointer to the location where the argument string is stored
offset = in->getArg<int>(); //get the second argument which indicates the offeset (in hours) from UTC time
unix_time = atoll(unix_time_str);
utc_offset = offset;
set_time(unix_time); // Set RTC time to Wed, 28 Oct 2009 11:35:37
}
//RPC function to receive notification strings
//note notification strings should not contain the character ' ', in lieu
//they should contain '_' to indicate spaces
void display_notification_rpc_func (Arguments *in, Reply *out) {
static char display_str[18];
static const char * msg_str;
int i,j;
bool break_out = false;
display_notification = true;
display_time = false;
msg_str = in->getArg<const char*>(); //get a pointer to the location where the argument string is stored
stdio_mutex.lock();
lcd_mutex.lock();
uLCD.cls();
uLCD.locate(0,0);
i = 0;
while(true){
for(j=0; j<18; j++){
if (msg_str[i+j] == '_'){
display_str[j] = ' ';
} else {
display_str[j] = msg_str[i+j];
}
if (msg_str[i+j] == '\0') {break_out = true; break;}
}
i+= 18;
uLCD.printf("%s\r\n",display_str);
if (break_out){break;}
}
stdio_mutex.unlock();
lcd_mutex.unlock();
notification_chime_ticker.attach(&audio_sample, 1.0/sample_freq);
}
int main() {
uLCD.baudrate(3000000); //increase the lcd baud rate
//configure the input button and attach an interrupt routine to it
view_button.mode(PullUp);
view_button.fall(&view_button_pressed);
bluetooth_thread.start(bluetooth_thread_func); //start the thread that takes in characters to construct RPC commands
time_thread.start(time_thread_func); //start the thread that updates the displayed time
}
Please log in to post comments.
