Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: TextLCD nRF24L01P mbed
main.cpp
- Committer:
- jackmax
- Date:
- 2017-01-17
- Revision:
- 9:53da36425601
- Parent:
- 8:0de75e0480aa
- Child:
- 10:3417715786be
File content as of revision 9:53da36425601:
#include "mbed.h"
#include "rtos.h"
#include "TextLCD.h"
#include "custom-chars.h"
#include "melodies.h"
#include "nRF24L01P.h"
#include <string>
// Host PC Communication channels
Serial pc(USBTX, USBRX); // tx, rx
I2C i2c_lcd(D5,D7); // SDA, SCL pins. Change if necessary
TextLCD_I2C lcd(&i2c_lcd, 0x4E, TextLCD::LCD16x2); // I2C exp: I2C bus, PCF8574 Slaveaddress, LCD Type
//Light sensor
AnalogIn light_sensor(A1);
//2.4 GHz radio
#define RX_ADDRESS      ((unsigned long long) 0xABCDEF00)
#define TX_ADDRESS      ((unsigned long long) 0xABCDEF01)
#define TRANSFER_SIZE   32
nRF24L01P radio(PB_15, PB_14, PB_13, PB_12, PB_1, PB_2);
void initRadio(int channel, int power, int datarate){    
    radio.powerDown();
    radio.powerUp();
    
    radio.setAirDataRate(datarate);
    radio.setRfOutputPower(power);
    radio.setRfFrequency(NRF24L01P_MIN_RF_FREQUENCY + 4 * channel);
    
    radio.setCrcWidth(NRF24L01P_CRC_8_BIT);
    radio.enableAutoAcknowledge(NRF24L01P_PIPE_P0);
    radio.enableAutoAcknowledge(NRF24L01P_PIPE_P1);
    radio.enableAutoRetransmit(0x0F, 0x0F);
    
    radio.setTxAddress(TX_ADDRESS, 4);
    radio.setRxAddress(TX_ADDRESS, 4, NRF24L01P_PIPE_P0);
    
    radio.setRxAddress(RX_ADDRESS, 4, NRF24L01P_PIPE_P1);
    
    // Display the (default) setup of the nRF24L01+ chip
    pc.printf( "nRF24L01+ Frequency    : %d MHz\r\n",  radio.getRfFrequency() );
    pc.printf( "nRF24L01+ Output power : %d dBm\r\n",  radio.getRfOutputPower() );
    pc.printf( "nRF24L01+ Data Rate    : %d kbps\r\n", radio.getAirDataRate() );
    pc.printf( "nRF24L01+ TX Address   : 0x%010llX\r\n", radio.getTxAddress() );
    pc.printf( "nRF24L01+ RX0 Address   : 0x%010llX\r\n", radio.getRxAddress(NRF24L01P_PIPE_P0) );
    pc.printf( "nRF24L01+ RX1 Address   : 0x%010llX\r\n", radio.getRxAddress(NRF24L01P_PIPE_P1) );
    radio.setTransferSize(TRANSFER_SIZE, NRF24L01P_PIPE_P0);
    radio.setTransferSize(TRANSFER_SIZE, NRF24L01P_PIPE_P1);
    radio.setReceiveMode();
    radio.enable();
};
//TODO: I don't think it needs to be in a separate thread,
//we probably can just put it into the main loop.
void radio_recv(void const* args){
    char rxData[TRANSFER_SIZE+1];
    while (1) {
        int rx_bytes=0;
        if(radio.readable(NRF24L01P_PIPE_P1)){
            rx_bytes = radio.read(NRF24L01P_PIPE_P1, rxData, TRANSFER_SIZE);
            rxData[TRANSFER_SIZE] = '\0';
            pc.printf("Received %d >>%s<<\r\n",rx_bytes, rxData);
        }
        
    }
}
//Display
void backlightTimeout(void const *arg);
RtosTimer backlightTimer(&backlightTimeout, osTimerPeriodic, (void*)0);
class Display{
public:
    int wifi_on;
    int alarm_on;
    int sync_in_progress;
    int wireless_in_progress;
    int frame;
    
    time_t seconds;
    char time_str[9];
    char date_str[9];
    
    static const int backlightTime = 5000;
    int backlightState;
    Display()
    {
        wifi_on = 0;
        alarm_on = 0;
        sync_in_progress = 0;
        wireless_in_progress = 0;
        frame = 0;
        backlightState = 0;
        
        lcd.setCursor(TextLCD::CurOff_BlkOff);
        lcd.setUDC(C_ALRM, cc_dzwonek);
        lcd.setUDC(C_WIFI, cc_wifi);
        lcd.setUDC(C_WLC , cc_wireless);
    }
    
    void backlightOff(){
        lcd.setBacklight(TextLCD::LightOff);
        backlightState = 0;
    }
    
    void backlightOn(){
        lcd.setBacklight(TextLCD::LightOn); 
        backlightState = 1;
        backlightTimer.start(backlightTime);
    }
    
    void update(){
        //Top row of display
        char ico1 = ' ';
        char ico2 = wireless_in_progress ? (frame % 2 ? C_WLC : ' ' ) : ' ';
        char ico3 = C_ALRM;
        int ah=85, am=84;
        
        time_t seconds_now = time(NULL);
        if (seconds_now != seconds) {
            seconds = seconds_now;
            strftime(time_str, 9, "%X", localtime(&seconds));
            strftime(date_str, 9, "%x", localtime(&seconds));
        }
        
        lcd.locate(0,0); //Put in top row
        lcd.printf("%s%c%c%c%02d:%02d",time_str,ico1,ico2,ico3,ah,am);
        
        lcd.locate(0,1); //Put in bottom row
        if (sync_in_progress) {
            lcd.printf("Synchronizacja..");
        }
        else {
            if (frame % 60 < 30) {
                lcd.printf("SmartAlarm+ Pro ");
            }
            else {
                lcd.printf("    %08s    ", date_str);
            }
        }
        frame++;
    }
};
Display disp;
//Handling user button presses
InterruptIn button(D6);
int userButtonLongPress = 300; //Time in ms; threshold for long press
Timer userButtonTimer;
int userButtonPressed = 0, userButtonReleased = 0, backlightTimedOut = 0;
void userButtonPress(){
    userButtonPressed = 1;   
}
void userButtonRelease(){
    userButtonReleased = 1; 
}
void backlightTimeout(void const *arg){
    backlightTimedOut = 1;
}
//Wi-Fi communication
#define WIFI_SIZE 1000
Serial wifi(PA_9, PA_10);
char wifiData[WIFI_SIZE];
bool wifiDataReady = false;
bool readInProgress = false;
int currentReadIndex = 0;
void wifiCallback() {    
    char c = wifi.getc();
    if (c == '*') {
        readInProgress = !readInProgress;
        wifiData[currentReadIndex] = '\0';
        currentReadIndex = 0;
        wifiDataReady = true;
    } else if (readInProgress) {
        wifiData[currentReadIndex++] = c;
    }
}
std::string wifiGetData(){
    if (!wifiDataReady)
        return "";
        
    std::string result = wifiData;
    wifiDataReady = false;
    return result;
}
std::string httpGETWithRetry(const char* query, float timeout, int retries){
    Timer t;
    for (int retry = 0; retry<retries; retry++){
        wifi.printf(query);
        t.reset();
        t.start();
        while (!wifiDataReady && t <= timeout){
            wait(0.01f);
        }
        pc.printf("http GET trying #%d\r\n", retry);
        if (wifiDataReady)
            break;
    }
    return wifiGetData();
}
//Synchronization
time_t next_sync = 0;
bool sync_in_progress = false;
time_t next_alarm = (time_t)-1; //TODO: bardzo brzydki hack
time_t getNextSync(){
    //TODO: zmiennej czasu nie powinno sie chyba tak modyfikowac
    return time (NULL) + 123;
}
//Reads are blocking now, so we need to put it into a different thread
void syncThreadFunction(void const* args){
    std::string time_string = httpGETWithRetry("GET " 
                "/time/current/ "
                "HTTP/1.1\r\n"
                "Host: 10.1.8.202:8080\r\n\r\n", 5.0, 5);
    if (time_string == ""){
        lcd.locate(0,1);
        lcd.printf("Blad synchroniz."); //TODO: to nie bedzie dzialac, trzeba przekazac te dane do obiektu disp
    }
    else {
        set_time(atoi(time_string.c_str()));
    }
    
    time_string = httpGETWithRetry("GET " 
                "/alarm/latest/ "
                "HTTP/1.1\r\n"
                "Host: 10.1.8.202:8080\r\n\r\n", 5.0, 5); //TODO: Jaki endpoint do alarmow?
    if (time_string == ""){
        lcd.locate(0,1);
        lcd.printf("Blad synchroniz.");
    }
    else {
        next_alarm = atoi(time_string.c_str());
    }
    
    next_sync = getNextSync();
    sync_in_progress = false;
}
//Alarm
PwmOut sound(D2);
bool alarm_playing = false;
bool alarm_stopped = false;
//We probably could do sound playback with timers and whatnot
//but why when we have THREADS!
void alarmThreadFunction(void const* args){
    //TODO: opcja wyboru dzwonka? whatever, i tak tego nie dokonczymy
    const int* freq = axelf_freq;
    const int* per = axelf_per;
    float bpm = 100.0f; //tempo
    alarm_stopped = false;
    while (!alarm_stopped){
        float base_period = 0.5f;
        float base_time = bpm / 60.0f;
        for (int i = 0; i < 100; i++){ //TODO: no bounds checking, -1 sentinel at the end required
            if (per[i] == -1)
                break;
            if (freq[i] != 0) {
                sound.period(base_period/freq[i]);
                sound = 0.5f;
            }
            else {
                sound = 0.0f;
            }
            wait(base_time/per[i]);
        }
        sound = 0.0f;
    }
    alarm_playing = false;
}
Ticker display_update_ticker;
int main() {
    set_time(1256729737);  //DEBUG: Set RTC time to Wed, 28 Oct 2009 11:35:37
    //Initialization
    disp.backlightOn();
    button.rise(&userButtonPress);
    button.fall(&userButtonRelease);
    
    wifi.baud(115200); 
    wifi.attach(&wifiCallback);
    
    pc.printf("Waiting for time\r\n");
    
    std::string time_string = httpGETWithRetry("GET " 
                "/time/current/ "
                "HTTP/1.1\r\n"
                "Host: 10.1.8.202:8080\r\n\r\n", 5.0, 5);
    
    pc.printf("Get time returned >>%s<<", time_string.c_str());
    
    if (time_string == ""){
        lcd.locate(0,1); //Put in top row
        lcd.printf("Blad synchroniz.");
        wait(1);
    }
    else {
        set_time(atoi(time_string.c_str()));
    }
    
    initRadio(6, NRF24L01P_TX_PWR_ZERO_DB, NRF24L01P_DATARATE_250_KBPS);
    Thread radio_thread(radio_recv);
    
    while (1){
        pc.printf("ok\r\n");
        
        //Handling button presses
        if (userButtonPressed) {
            userButtonPressed = 0;
            userButtonTimer.reset();
            userButtonTimer.start();
        }
        
        if (userButtonReleased) {
            userButtonReleased = 0;
            userButtonTimer.stop();
            if (userButtonTimer.read_ms() > userButtonLongPress){
                pc.printf("User button long pressed");
                //disp.backlightOff(); //that was just debug code anyway
                if (alarm_playing){
                    alarm_stopped = true;
                }
            }
            else {
                pc.printf("User button short pressed");
                disp.backlightOn();
            }  
        }
        
        //Handling backlight
        if (backlightTimedOut){
            backlightTimedOut = 0;
            disp.backlightOff();
        }
        
        //Synchronize with server
        if (time(NULL) >= next_sync && !sync_in_progress){
            sync_in_progress = true;
            Thread sync_thread(syncThreadFunction);
        }
        
        //Check if alarm should be played
        if (time(NULL) >= next_alarm && !alarm_playing){
            alarm_playing = true;
            Thread alarm_thread(alarmThreadFunction);
        }
        
        //Updating display
        disp.update();
        
        Thread::wait(100);
    };
}
            
    
