JNP3 SmartAlarm / Mbed 2 deprecated budzik

Dependencies:   TextLCD nRF24L01P mbed

main.cpp

Committer:
jackmax
Date:
2017-01-22
Revision:
10:3417715786be
Parent:
9:53da36425601
Child:
11:c94b9db597e9

File content as of revision 10:3417715786be:

#include "mbed.h"
//#include "rtos.h"
#include "TextLCD.h"
#include "custom-chars.h"
#include "melodies.h"
#include "nRF24L01P.h"
#include <string>
#include <vector>

time_t next_alarm = 0;

// 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
#define LIGHT_INTERVAL 2
AnalogIn light_sensor(A1);
double light_sum;
int light_samples;
time_t next_light = 0;
//Mutex light_mutex;

void lightSensor(){
    light_sum += light_sensor;
    light_samples++;
    next_light = time(NULL) + LIGHT_INTERVAL;
}

//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();
};

bool movement_detected = false;

std::vector<std::string> last_wireless_data;
time_t last_wireless_time;

void processWirelessData(char* data_string){
    std::string s = data_string;
    std::vector<std::string> data;
    size_t pos = 0;
    std::string token;
    std::string delimiter = "&";
    
    while ((pos = s.find(delimiter)) != std::string::npos) {
        token = s.substr(0, pos);
        data.push_back(token);
        s.erase(0, pos + delimiter.length());
    }
    
    last_wireless_data = data;
    last_wireless_time = time(NULL);
    
    if (data.size() >= 3){
        double min = atof(data[0].c_str());
        double max = atof(data[1].c_str());
        double avg = atof(data[2].c_str());
        
        if (max > 0.3 && avg > 0.1)
            movement_detected = true;
        else
            movement_detected = false;
    }
}

void radio_recv(){
    char rxData[TRANSFER_SIZE+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);
        processWirelessData(rxData);
    }
}

//Handling display
void backlightTimeout();
//RtosTimer backlightTimer(&backlightTimeout, osTimerPeriodic, (void*)0);
Timeout backlightTimer;

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 float backlightTime = 5.000;
    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.attach(&backlightTimeout, backlightTime);
        
    }
    
    void update(){
        //Top row of display
        char ico1 = ' ';
        char ico2 = sync_in_progress ? (frame % 2 ? C_WIFI : ' ' ) : ' ';
        char ico3 = ' ';
        char alarm_time[6];
        if (next_alarm != 0){
            ico3 = C_ALRM;
            strftime(alarm_time, 6, "%H:%M", localtime(&next_alarm));
        }
        else {
            strcpy(alarm_time, "     ");
        }
        
        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%s",time_str,ico1,ico2,ico3,alarm_time);
        
        lcd.locate(0,1); //Put in bottom row
        if (sync_in_progress) {
            lcd.printf("Synchronizacja..");
        }
        if (alarm_on){
            lcd.printf("Wstawaj!        ");
        }
        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(){
    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;
        if (readInProgress == false)
            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;
time_t last_sync = 0;
bool sync_in_progress = false;

time_t getNextSync(){
    //TODO: zmiennej czasu nie powinno sie chyba tak modyfikowac
    return time (NULL) + 23;
}

int syncFunction(){
    disp.sync_in_progress = 1;
    last_sync = time(NULL);
    std::string time_string = httpGETWithRetry("GET " 
                "/time/current/ "
                "HTTP/1.1\r\n"
                "Host: 10.1.8.202:8080\r\n\r\n", 2.0, 5);
    if (time_string == ""){
        //TODO: błąd synchronizacji
    }
    else {
        set_time(atoi(time_string.c_str()));
    }
    
    time_string = httpGETWithRetry("GET " 
                "/alarm/get/nearest/id/5881e409e93a2218d42fe888 "
                "HTTP/1.1\r\n"
                "Host: 10.1.8.202:8080\r\n\r\n", 2.0, 5);
    if (time_string == ""){
    }
    else {
        next_alarm = atoi(time_string.c_str());
    }
    
    time_t now;
    time(&now);
    char time_str[50];
    strftime(time_str, 50, "%FT%TZ", gmtime(&now));
    
    //Get light sensor data
    double light_sensor_avg;
    light_sensor_avg = light_samples == 0 ? 0 : light_sum / light_samples ;
    light_sum = 0;
    light_samples = 0;
    
    wifi.printf("POST " 
                "/alarm/get/nearest/id/5881e409e93a2218d42fe888 "
                "HTTP/1.1\r\n"
                "Host: 10.1.8.202:8080\r\n\r\n"
                "{"
                "\"date\":\"%s\""
                "\"value\":%f"
                "\"device_id\":\"5881e409e93a2218d42fe888\""
                "\"type\":\"light\""
                "}\r\n\r\n", time_str, light_sensor_avg);
    next_sync = getNextSync();
    return 0;
}

//Alarm
PwmOut sound(D13);
bool alarm_playing = false;
bool alarm_stopped = false;

const int* freq = axelf_freq;
const int* per = axelf_per;
int next_note = 0;

void play_next_note();
Timeout next_note_timeout;

void start_alarm(){
    disp.alarm_on = 1;
    alarm_playing = true;
    next_note = 0;
    next_note_timeout.attach(&play_next_note, 0.01);
}

void stop_alarm(){
    next_alarm = 0;
    alarm_playing = false;
    disp.alarm_on = 0;
    sound = 0.0f;
}

//TODO: won't work if melody contains -1 per at the beginning, has to have at least one note
void play_next_note(){
    if (!alarm_playing)
        return;
    
    const float bpm = 100.0f; //tempo
    const float base_period = 0.5f;
    const float base_time = bpm / 60.0f;

    if (per[next_note] <= 0)
        next_note = 0;

    if (freq[next_note] != 0) {
        sound.period(base_period/freq[next_note]);
        sound = 0.5f;
    }
    else {
        sound = 0.0f;
    }
    
    next_note++;
    next_note_timeout.attach(&play_next_note, base_time/per[next_note]);    
}

int main() {
    wifi.baud(115200); 
    wifi.attach(&wifiCallback);
    //Initialization
    disp.backlightOn();
    button.rise(&userButtonPress);
    button.fall(&userButtonRelease);
    
    pc.printf("Waiting for time\r\n");
    
    std::string time_string = httpGETWithRetry("GET " 
                "/time/current/ "
                "HTTP/1.1\r\n"
                "Host: 10.4.8.136:8080\r\n\r\n", 5.0, 5);
    
    pc.printf("Get time returned >>%s<< %d\r\n", time_string.c_str());
    
    if (time_string == ""){
        lcd.locate(0,1); //Put in top row
        lcd.printf("Blad synchroniz.");
        wait(1);
        set_time(1256729737);  //DEBUG: Set RTC time to Wed, 28 Oct 2009 11:35:37
    }
    else {
        set_time(atoi(time_string.c_str()));
    }
    
    initRadio(6, NRF24L01P_TX_PWR_ZERO_DB, NRF24L01P_DATARATE_250_KBPS);
    
    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){
                    stop_alarm();
                }
                else {
                    //Force synchronization
                    next_sync = time(NULL);
                }
            }
            else {
                pc.printf("User button short pressed");
                disp.backlightOn();
            }  
        }
        
        //Handling backlight
        if (backlightTimedOut){
            backlightTimedOut = 0;
            disp.backlightOff();
        }
        
        if (time(NULL) >= next_light){
            lightSensor();
        }
        
        //Synchronize with server
        if (time(NULL) >= next_sync){
            disp.sync_in_progress = 1;
            disp.update();
            syncFunction();
            disp.sync_in_progress = 0;
            disp.update();
        }
        
        //Check if alarm should be played
        if (next_alarm != 0 && !alarm_playing){
            if (time(NULL) >= next_alarm-(15*60) ){ //Hardcoded to 15 minutes before set time
                if (movement_detected || time(NULL) >= next_alarm){
                    start_alarm();
                }
            }
        }
        
        //Try to receive from radio
        radio_recv();
        
        //Updating display
        disp.update();
        
        wait_ms(100);
    };
}