run this here

Dependencies:   Hexi_KW40Z Hexi_OLED_SSD1351

main.cpp

Committer:
trhackett
Date:
2018-06-14
Revision:
4:1ae9f76749b9
Parent:
3:f34bbdea0786

File content as of revision 4:1ae9f76749b9:

#include "mbed.h"
#include "Hexi_KW40Z.h"
#include "Hexi_OLED_SSD1351.h"
#include "Ticker.h"
#include "timeHelp.h"

void AlertReceived(uint8_t *data, uint8_t length);
void displayTimeToScreen();
void clearScreen();
void updateError();
void updateDisplayString();
void txTask();

KW40Z kw40z_device(PTE24, PTE25);

// oled driver and its text buffer
SSD1351 oled(PTB22,PTB21,PTC13,PTB20,PTE6, PTD15);
char text[20];

const float interval = 0.1;
int numTicks = 0;
int prevNumTicks = 0;
float intervalError = 0.0;

char prevTime[11] = "00:00:00.0";
char displayTime[11] = "00:00:00.0";

// important to know whether or not we've synced at least once with pi
// because we don't have an initial time until we hear from pi
bool firstSync = true;
bool syncedTwice = false;

DigitalOut pwm(PTA10);


// bluetooth thread
Thread txThread;

// event queue for displaying the time
EventQueue displayQueue;
// event queue for receiving the time
EventQueue receiveTimeQueue;

int main() {    
    
    kw40z_device.attach_alert(&AlertReceived);
    
    // thread that displays the time for us
    Thread displayThread;
    displayThread.start(callback(&displayQueue, &EventQueue::dispatch_forever));
    
    // thread that responds to pi's time updates
    Thread receiveTimeThread;
    receiveTimeThread.start(callback(&receiveTimeQueue, &EventQueue::dispatch_forever));
    
    // initialize the screen to blank
    displayQueue.call(&clearScreen);
    displayQueue.call(&displayTimeToScreen);
    
    // ticker that increments the time
    Ticker updateTimeTicker;
    updateTimeTicker.attach(receiveTimeQueue.event(&updateDisplayString), interval);
    
    // start toggling bluetooth so you can connect
    txThread.start(txTask);
    
    wait(osWaitForever);   
}

/******************************End of Main*************************************/

// initialize the screen to black
void clearScreen() {    
    oled.FillScreen(COLOR_BLACK);
}

void displayTimeToScreen() {
    /* Get OLED Class Default Text Properties */
    oled_text_properties_t textProperties = {0};
    oled.GetTextProperties(&textProperties);
    
    /* Change font color to Blue */ 
    textProperties.fontColor   = COLOR_BLUE;
    oled.SetTextProperties(&textProperties);

    if (secondsAreEven(displayTime)) {
        pwm = 0 ;
    } else {
        pwm = 1;
    }
    
    /* Display time Label at x=20,y=40 */ 
    strcpy((char *) text, displayTime);
    oled.Label((uint8_t *)text,20,40);   
}

// the alert that we've received is always the updated time from raspberry pi
// so we should put that in display time, calculate the new interval, and 
// display the new time
void AlertReceived(uint8_t *data, uint8_t length) {
    
    char buf[10];
    for (int i = 0; i < 10; ++i) {
        buf[i] = data[i];
    }
    
    // copy in the new data, storing the old only if there is old to store
    for (int i = 0; i < 10; i++) {
        if (!firstSync) {
            prevTime[i] = displayTime[i];
        }
        
        else {
            prevTime[i] = buf[i];
            
            // if you get here, then you've synced once
            if (!syncedTwice) {
                syncedTwice = true;
            }
        }
        displayTime[i] = buf[i];
    }
    
    // reset numTicks
    prevNumTicks = numTicks;
    numTicks = 0;
    
    // update the error as long as it isn't your first sync ... need two times
    if (!firstSync && syncedTwice) {
        receiveTimeQueue.call(&updateError);
    }
    
    firstSync = false;
}

// error is the total amount that hexiwear was off by
// averaged over all of the ticks ... moving forward we
// can add the error into out calculation of the current
// time and hopefully it will be more accurate.
void updateError() {
    
    // 
    float secsToAdd = prevNumTicks * (interval + intervalError);
    
    float T1 = currTimeSecs(displayTime);
    float T0 = currTimeSecs(prevTime);
    
    intervalError = (T1 - (T0+secsToAdd)) / prevNumTicks;
}

int numPrints = 20;

// you update the display string every 0.1 seconds, to add to the
// number of ticks, calculate the offset from the previous time,
// and display it
void updateDisplayString() {
    numTicks++;
    
    float offset = numTicks * (interval + intervalError);
    
    addSecondsToCurrentTime(prevTime, displayTime, offset);
    
    displayQueue.call(&displayTimeToScreen);
}

// take care of the bluetooth toggling so that it can conect
void txTask(){
   
   while (true) 
   {
        // there is about a half second delay before the hexiwear
        // realizes that the link state is down, so if you ever
        // do realize that, you should toggle the advertisement
        // state right away, I think. NO what you should do it, if
        // you ever realize that the link is down, wait a half a second
        // and then toggle advertisement mode to let it refresh

        // it seems like this doesn't perform quite as well, but I think
        // it is more robust...
        if (kw40z_device.GetLinkState() == 0) {
            Thread::wait(2500);
            if (kw40z_device.GetLinkState() == 0) {
                kw40z_device.ToggleAdvertisementMode();
            }
        }

        Thread::wait(5000);                 
    }
}