OldRemmote allows you to record IR remotes and upload the IR code to your smartphone to replay it later. Check www.julesthuillier.com for more information

Dependencies:   BLE_API mbed nRF51822

main.cpp

Committer:
JulesThuillier
Date:
2015-02-09
Revision:
4:688a955562ad
Parent:
3:a491b09cfd3b

File content as of revision 4:688a955562ad:

#include "mbed.h"
#include "BLEDevice.h"

#include "DFUService.h"

//UART Primary Service
#include "UARTService.h"
 
//Battery and DeviceInformation Auxilary Services
#include "BatteryService.h"
#include "DeviceInformationService.h"

#define NEED_CONSOLE_OUTPUT 1 /* Set this if you need debug messages on the console;
                               * it will have an impact on code-size and power consumption. */
#if NEED_CONSOLE_OUTPUT
#define DEBUG(...) { printf(__VA_ARGS__);}
#else
#define DEBUG(...) /* nothing */
#endif /* #if NEED_CONSOLE_OUTPUT */ 

#define RECORD_OFF_TIMER 12000
#define ADV_INTERVAL 1600 /* 1000ms; in multiples of 0.625ms. */
#define ADV_TIMEOUT 30 /* in seconds */
#define BUTTON_SHUTDOWN_DELAY 4

#define NRF51822 0

    /* Initialisation des entrées sorties */

PinName ir = P0_6;
PwmOut irled(P0_12); 

static char const DEVICE_NAME[] = "OldRemote";

BLEDevice ble;
UARTService *uartServicePtr;
BatteryService *battServicePtr;

enum States {DISCONNECTED, OFF, ADVERTISING, CONNECTED};
States currentState = DISCONNECTED;

// Timer pour connaitre la duree de l'enfoncement
Timer timer;
Timer timerUp;
Timer timerDown;
Timeout recordOffTimer;

// Ticker pour un reveil toutes ls 1 secondes
Ticker ticker;
bool startPeriodick = false;
// Turn the device off

uint16_t recordBuffers [3][100];
uint8_t indexi = 0, indexj = 0;

uint16_t sendBuffer [100];
uint8_t sendBufferIndex = 0;

bool receivingDatas = false;
bool recording = false;

// Convertit un tableau de char en int
int char_to_int(char *data)
{
    int i;
    sscanf(data, "%d", &i);
    return i;
}


/* Envoi en infrarouge les données passées en parametre
 * PROTOCOL :
 * first data : array size - 1 (number of HIGH - LOW datas)
 * every odd index : duration of HIGH
 * every even index : duration of LOW
 */
void sendIR(uint16_t *data)
{
    uint8_t size = data[0];
    // Start at index 1, as index 0 is buffer length
    for(int i=1; i<size; i++)
    {
            // HIGH 
            if((i%2)==1)
            {
                irled.write(0.33f);
            }    
            // LOW 
            else
            {
                irled.write(0);
            }
            
            wait_us(data[i]);   
    }
    irled.write(0);
}


/* 
 * Renvoie le dernier signal IR
 */
void repeat()
{
    if(sendBuffer[0] != 0)
    {
        sendIR(sendBuffer); 
    }   
}

void prepareForReception()
{
    for(int i=0; i<100; i++)
    {
     sendBuffer[i] = 0;   
    }    
}


/* Set the IR frequency 
 * Param : frequency in Kilohertz
 */
void setFrequency(int freq)
{
    DEBUG("Frequency : %d\r\n", freq);
    uint8_t period = 1000/freq;
    irled.period_us(period);
}


// Vide le buffer dont l'index est passe en parametre
void clearBuffer(int index)
{
    for(int j=0; j<100;j++){
        recordBuffers[index][j] = 0;
    }
}


// Vide tous les buffers
void clearBuffers()
{
    for(int j=0; j<3;j++){
        clearBuffer(j);
    }
}


void disconnect()
{
    DEBUG("Shutting down\r\n");
    
    // Arrete le timer
    timer.stop();
    

    // Supprime l'appel toutes les 1 secondes
    ticker.detach();
  
    Gap::DisconnectionReason_t myreason;
    ble.disconnect(myreason);
    
    currentState = OFF;
}

uint8_t getBattery()
{
 // Configure ADC
    NRF_ADC->CONFIG     = (ADC_CONFIG_RES_8bit                        << ADC_CONFIG_RES_Pos)     |
                          (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos)  |
                          (ADC_CONFIG_REFSEL_VBG                      << ADC_CONFIG_REFSEL_Pos)  |
                          (ADC_CONFIG_PSEL_Disabled                   << ADC_CONFIG_PSEL_Pos)    |
                          (ADC_CONFIG_EXTREFSEL_None                  << ADC_CONFIG_EXTREFSEL_Pos);
    NRF_ADC->EVENTS_END = 0;
    NRF_ADC->ENABLE     = ADC_ENABLE_ENABLE_Enabled;

    NRF_ADC->EVENTS_END  = 0;    // Stop any running conversions.
    NRF_ADC->TASKS_START = 1;
    
    while (!NRF_ADC->EVENTS_END)
    {
    }
    
    uint16_t vbg_in_mv = 1200;
    uint8_t adc_max = 255;
    uint16_t vbat_current_in_mv = (NRF_ADC->RESULT * 3 * vbg_in_mv) / adc_max;
    
    NRF_ADC->EVENTS_END     = 0;
    NRF_ADC->TASKS_STOP     = 1;
    
    return (uint8_t) ((vbat_current_in_mv * 100) / 3700);   
 
 //   return 12;   
}


// Fontion appelé toutes les 1 secondes lorsque le telephoen est connecté
void periodicCallback(void)
{
  //  uint8_t batt = getBattery();
 //   uint8_t batt = 42;
   // DEBUG("Periodic Callback \n\r");
    //if (battServicePtr != NULL)
   //     battServicePtr->updateBatteryLevel(batt);
   // DEBUG("Battery : %d \n\r", batt);

}
// Callback appelé en cas de deconnection
void onDisconnection(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
{
    DEBUG("Disconnected \n\r");

    if(currentState != OFF)
    {
         //   printf("Restarting the advertising process\n\r");
        ble.startAdvertising();
        currentState = DISCONNECTED;
    }
    
    switch (reason) {
    case Gap::REMOTE_USER_TERMINATED_CONNECTION:
        DEBUG("Disconnected (REMOTE_USER_TERMINATED_CONNECTION)\n\r");
        break;
    case Gap::LOCAL_HOST_TERMINATED_CONNECTION:
        DEBUG("Disconnected (LOCAL_HOST_TERMINATED_CONNECTION)\n\r");
        break;
    case Gap::CONN_INTERVAL_UNACCEPTABLE:
        DEBUG("Disconnected (CONN_INTERVAL_UNACCEPTABLE)\n\r");
        break;
    }

}

// Callback appelé lorsque la connection est etablie
void onConnection(Gap::Handle_t handle, Gap::addr_type_t peerAddrType, const Gap::address_t peerAddr, const Gap::ConnectionParams_t *param)
{
    DEBUG("Connected\n\r"); 
    currentState = CONNECTED;
}


// Callback appelé lorsque l'advertising arrive a son timeout avant que la connexion ait été établie
void onTimeout()
{
    DEBUG("Timeout \n\r");
    disconnect();
}


void sendUART(char *data, int size)
{
    uint8_t  buf[size];
    for(int i=0; i< size; i++)
        {
           buf[i] = (uint8_t)data[i];
        }
    ble.updateCharacteristicValue(uartServicePtr->getRXCharacteristicHandle(), buf, size);
}


// Callback appelé lorsque le service uart recoit des données
void receiveUART(const GattCharacteristicWriteCBParams *params)
{
    if ((uartServicePtr != NULL) && (params->charHandle == uartServicePtr->getTXCharacteristicHandle())) {
        uint16_t bytesRead = params->len;

        DEBUG("Data received : %*s\n\r", params->len, params->data);
        DEBUG("Data length : %d\n\r", bytesRead);
        
        // get all the data in one array
        uint8_t  buf[bytesRead];
        uint16_t split = bytesRead;
        for(int i=0; i< bytesRead; i++)
        {
           buf[i] = params->data[i];
           if((char)buf[i] == ':' )
           {
            DEBUG("Split at %u\n\r", i);
            split = i;
           } 
        }
        char  option[split];
        char  arg[bytesRead - (split+1)];
        
        // split array to get option
        for(int i=0; i< split; i++)
        {
           option[i] = buf[i];
        }

        // split array to get argument
        if(split<bytesRead)
        {
            for(int i=split+1; i < bytesRead; i++)
            {
               arg[i-(split+1)] = buf[i];
            }
            
        }
        
        // Get ready to receive the datas to send
        if(strncmp(option, "send", split) == 0)
        {   
            
            prepareForReception();
            sendBuffer[0] = char_to_int(arg); 
            receivingDatas = true;  
            DEBUG("Receiving datas\r\n");
        }
        
        
        // Set the frequency
        else if(strncmp(option, "setfreq", split) == 0)
        {   
            setFrequency(char_to_int(arg));    
        }
        
        // Repeat the last emission
        else if(strncmp(option, "repeat", split) == 0)
        {   
            repeat();    
        }
        
        // Start recording
        else if(strncmp(option, "record", split) == 0)
        {   
             
        }
        
        
        else if(receivingDatas)
        {  
            int duration = char_to_int(option);
            DEBUG("data : %u\r\n", duration);
            
            if(duration != 0)
            {
                sendBufferIndex++;
                sendBuffer[sendBufferIndex] = duration;
                
                if(sendBufferIndex == sendBuffer[0])
                {
                    DEBUG("Reception Over\r\n"); 
                    receivingDatas = false;   
                    sendIR(sendBuffer); 
                }
            }   
            else {
                sendUART("FAIL:send", 9);
                DEBUG("FAIL:send\r\n"); 
                receivingDatas = false;   
 
            }
        }
        else
        {
            DEBUG("UNPARSED : %s\r\n", option);
        }
    }    
}



void recordFinished()
{
    if(recording){
        timerUp.reset();
        timerDown.reset();
        indexi = 0;
        indexj = 0;
        DEBUG("Record is over\r\n"); 
        for(int i=0; i<100; i++)
            DEBUG("%d - ",recordBuffers[0][i]);  
        DEBUG("\r\n");
        for(int i=0; i<100; i++)
            DEBUG("%d - ",recordBuffers[1][i]);  
        DEBUG("\r\n");
    }
    
}

void irFall()
{
    if(recording){
        // Arrete le timer
        timerUp.stop();
        
        timerDown.reset();
        timerDown.start();
        
        // Demarrage du timer de shutdown;
        recordOffTimer.attach_us(&recordFinished, RECORD_OFF_TIMER);
        if(indexj<100 && indexi <3)
            recordBuffers[indexi][indexj] = timerUp.read_us();
        indexj++;
    }
}

void irRise()
{
    if(recording){
        timerDown.stop();
        
        timerUp.reset();
        timerUp.start();
        
        // Supprime le shutdown timer
        recordOffTimer.detach();
        if(indexj<100 && indexi <3)
            recordBuffers[indexi][indexj] = timerDown.read_us();
        indexj++;
    }
}


void initIRInterrupt(PinName pin)
{
    // Interruption sur le bouton
    InterruptIn *interruption;

    // Initalize interruption
    interruption = new InterruptIn(pin);
    
    if(NRF51822)
    {
        interruption->rise(irRise);
        interruption->fall(irFall);
    }
    else
    {    
        interruption->fall(irRise);
        interruption->rise(irFall);
    }
}


int main() {
    /*
    NRF_CLOCK->TASKS_LFCLKSTOP = 1;
    NRF_CLOCK->LFCLKSRC = 2;    // 0 = RC, 1 = XTAL, 2 = SYNTH
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_LFCLKSTART = 1;
    while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0){}
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; 
 */

    irled.period_us(26);
//    irled.write(0.33f);
    
    DEBUG("Hello world\r\n");
    
    // Demarre l'appel periodique toutes les 1 seconde
    ticker.attach(periodicCallback, 1.0f);

    initIRInterrupt(ir);
    
    DEBUG("Fin interrupt Set\r\n");
     // Initialisation et set des callback
    ble.init();
    ble.reset();
    ble.onDisconnection(onDisconnection);
    ble.onDataWritten(receiveUART);
    ble.onConnection(onConnection);
    ble.onTimeout(onTimeout);
 
 /*****************************************
 ********** AJOUT DES SERVICES ************
 *****************************************/
  
    // Creation du service UART
    UARTService uartService(ble);
    uartServicePtr = &uartService;
  //  uartService.retargetStdout(); // renvoie la sortie printf sur l'UART
    // Creation du battery service
    BatteryService battery(ble);
    battServicePtr = &battery;
   
   // Creation du DFU service 
    DFUService dfu(ble); 
    
    // Creation du device information service
    DeviceInformationService deviceInfo(ble, DEVICE_NAME, "OldRemote", "SN1", "hw-rev1", "fw-rev1", "soft-rev1");
 

    // Setup advertising
    static const uint16_t uuid16_list[] = {GattService::UUID_BATTERY_SERVICE, GattService::UUID_DEVICE_INFORMATION_SERVICE};

    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,(const uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME) - 1);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS,(const uint8_t *)UARTServiceUUID_reversed, sizeof(UARTServiceUUID_reversed));
    ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));                               
    ble.setAdvertisingInterval(ADV_INTERVAL); /* 100ms; in multiples of 0.625ms. */
//    ble.setAdvertisingTimeout(ADV_TIMEOUT); /* in seconds */
    ble.startAdvertising();
 
    currentState = ADVERTISING;   
    
    DEBUG("Initialisation done\r\n");

    while (true) { 
        ble.waitForEvent();
        
    }
}