ReSpeaker Switch V02

Dependencies:   mbed MbedJSONValue

main.cpp

Committer:
Arkadi
Date:
2019-06-08
Revision:
13:b80d07c4f6e6
Parent:
12:1b8560db3dff

File content as of revision 13:b80d07c4f6e6:

////////////////////////////////////////
//      Tau_ReSpeaker_Sitch_V02       //
//  Arkadiraf@gmail.com - 08/06/2019  //
////////////////////////////////////////
/*
 json format: json:{"name":"auto"}                               - automatic mode
 Json Format: json:{"name":"switch","mic":0, "spk": [0,1,0,0,0]} - manual mode
*/

/*
   Board : Nucleo STM32F446RE
   Power Source : DC-DC 5V Regulator E5V Jumper
*/

/*
    Nucleo board modification:
    XBee UART Shield
    SB13 , SB14 Off
    SB62 , SB63 On

    to use PH_0 / PH_1 (MUX_S2/S3) as IO modify boards as followed:
    SB54 and SB55 ON
    SB16 and SB50 (MCO) OFF
    http://www.st.com/content/ccc/resource/technical/document/user_manual/98/2e/fa/4b/e0/82/43/b7/DM00105823.pdf/files/DM00105823.pdf/jcr:content/translations/en.DM00105823.pdf
*/

/*
    Pinout:
    PC - Serial 2
    PA_2 (Tx) --> STLINK
    PA_3 (Rx) --> STLINK

    Switch - Serial 3
    PC_10 (Tx) --> SW_Rx
    PC_11 (Rx) --> SW_Tx

    Digital output :
    PA_5  --> led
    PC_8  --> led1
    PC_6  --> led2
    PA_15 --> TRIG


    MUX: CD74HC4067
    PC_12 --> MUX_S0
    PD_2  --> MUX_S1
    PH_0  --> MUX_S2
    PH_1  --> MUX_S3

    Speaker Switch : SN74LVC1G3157
    PB_2  --> EN_SPK_1
    PB_1  --> EN_SPK_2
    PB_15 --> EN_SPK_3
    PB_14 --> EN_SPK_4
    PB_13 --> EN_SPK_5

    DSP Interrupt
    PA_10 --> DSP_Trigger

    MIC Interrupts:
    PA_6 --> INTER_1
    PA_7 --> INTER_2
    PB_6 --> INTER_3
    PC_7 --> INTER_4
    PA_9 --> INTER_5

    Digital Input
    PC_13 --> BTN (Blue)

    Analog Input
    PA_0 --> A_DATA_1
    PA_1 --> A_DATA_2
    PA_4 --> A_DATA_3
    PB_0 --> A_DATA_4
    PC_1 --> A_DATA_5

    Analog Output


*/

///////////////
// Libraries //
///////////////
#include "mbed.h"
#include "MbedJSONValue.h"
//#include <string>

///////////////
// #defines  //
///////////////

//#define DEBUG_MOD1  // json packet recognise
//#define DEBUG_MOD2   // json parse
//#define DEBUG_MOD3   // switch handler
//#define DEBUG_MOD4   // serial with dsp module
//#define DEBUG_MOD10   // responsivity msges to gui
bool debuggingEn = 0;

#define MSG_BUFFER_SIZE 1024
#define HEADER_SIZE 5
#define FOOTER_SIZE 2

#define TICKS2TOGGLE 1000000
/////////////
// Objects //
/////////////

// Time stamp timer
Timer timeStamp;

// Frequency Counter timer
Timer freqTimer;

// json
MbedJSONValue guiCmd;

// uart
Serial pc(USBTX, USBRX);

// uart switch_dsp
Serial dsp(PC_10, PC_11);

// digital input
DigitalIn user_button(PC_13);

// digital output
DigitalOut led(PA_5);
DigitalOut led1(PC_8);
DigitalOut led2(PC_6);
DigitalOut trigger(PA_15);

// interrup from dsp mcu (frequency counter)
InterruptIn dsp_trigger(PA_10);

// MUX: CD74HC4067
DigitalOut mux_s0(PC_12);
DigitalOut mux_s1(PD_2);
DigitalOut mux_s2(PH_0);
DigitalOut mux_s3(PH_1);

// speaker switch
DigitalOut en_spk_1(PB_2);
DigitalOut en_spk_2(PB_1);
DigitalOut en_spk_3(PB_15);
DigitalOut en_spk_4(PB_14);
DigitalOut en_spk_5(PB_13);

// MIC interrupts
DigitalIn inter_1(PA_6, PullDown);
DigitalIn inter_2(PA_7, PullDown);
DigitalIn inter_3(PB_6, PullDown);
DigitalIn inter_4(PC_7, PullDown);
DigitalIn inter_5(PA_9, PullDown);

// analog input
AnalogIn a_data_1(PA_0);
AnalogIn a_data_2(PA_1);
AnalogIn a_data_3(PA_4);
AnalogIn a_data_4(PB_0);
AnalogIn a_data_5(PC_1);

///////////////
// variables //
///////////////
// analog input from microphone
uint16_t micAData[5]= {0};

// mic interrupt flag
int micInterrupt = 0;
bool autoFlag = 0; // automatic mode flag

// json buffer
char json[MSG_BUFFER_SIZE];

// packet variables
struct packetMSG_struct {
    // recieve message variables
    uint8_t header[HEADER_SIZE];
    uint8_t footer[FOOTER_SIZE];
    uint8_t syncIndex; // sync index for header / footer
    uint8_t syncFlag; // 0 - waiting for header, 1 -  waiting for footer, 2 - verify footer, 3 - finish footer send to parser, flash buffer
    // buffer
    uint16_t bufferIndex; // buffer index
    uint8_t buffer[MSG_BUFFER_SIZE];
} ;
packetMSG_struct packetMSG;


// Frequency calculator
volatile int freqTimeInterval = 0;
volatile bool dspIntFlag = 0;
volatile bool loopFreqFlag = 0;
///////////////
// Functions //
///////////////

// mic interrupt functions - not used, Polling mode
//void micInt_1()
//{
//    micInterrupt = 1;
//}
//void micInt_2()
//{
//    micInterrupt = 2;
//}
//void micInt_3()
//{
//    micInterrupt = 3;
//}
//void micInt_4()
//{
//    micInterrupt = 4;
//}
//void micInt_5()
//{
//    micInterrupt = 5;
//}
// Serial Event function
void rxCallback(void);

// serial event from DSP
void rxDspCallback(void);

// initialize packet struct
void initPacket(void);

// Packet Parser
void parsePacket(void);

// switch Packet Handler
void switchPacket(void);

// initialize switch
void initSwitch(void);

// poll mic interrupt gpio
bool micPolling(void);

// Atuomatic Switch Handler
void autoSwitch(int micEvent);

// interrup event from the dsp (frequency counter)
void dspInterrupt();

////////////////////////
//  Main Code Setup : //
////////////////////////
int main()
{
    // init packet:
    initPacket();
    // init uart
    pc.baud(57600);
    dsp.baud(57600);
    // attach serial event interrupt
    pc.attach(&rxCallback, Serial::RxIrq);

    // attach serial event interrupt
    dsp.attach(&rxDspCallback, Serial::RxIrq);

    // initialize switch
    initSwitch();
#ifdef DEBUG_MOD1
    pc.printf("ReSpeaker Test \r\n");
#endif
    ///////////////////////
    //  Main Code Loop : //
    ///////////////////////
    while(1) { // GPIO polling for mic events
        // frquency counter
        if (dspIntFlag) {
            double calculatedFrequencyDouble = (double) (TICKS2TOGGLE * 2.0 ) / freqTimeInterval; // Calculated in MHz
            int calculatedFrequency = (int) (calculatedFrequencyDouble * 1000000); // convert to hz
            if ((calculatedFrequency < 10000000) && loopFreqFlag) { // cannot be larger, update when requested
                pc.printf("{\"event\":\"dspFreq\",\"Freq\":%d}\r\n",calculatedFrequency); // Printf
            }
            dspIntFlag=0;
        }
        if(0) { // no analog data to read (not implemented yet
            micAData[0] = a_data_1.read_u16();
            micAData[1] = a_data_2.read_u16();
            micAData[2] = a_data_3.read_u16();
            micAData[3] = a_data_4.read_u16();
            micAData[4] = a_data_5.read_u16();
            pc.printf("Data:%d,%d,%d,%d,%d\r\n",micAData[0],micAData[1],micAData[2],micAData[3],micAData[4]);
            wait(1);
        }
        // change to interrupt - Problematic as PA_14 and PA_13 are share with stlink
        if (micPolling()) { // simple polling of interrupt signals
            if (autoFlag)  { // update switch based on automatic selection
                autoSwitch(micInterrupt);
            }

            if (1) { // send to pc event time
                float callTime = timeStamp.read();
                //pc.printf("IntMic: %d time: %.3f \r\n",micInterrupt,callTime); // Printf generates delay of about 5 millis
                // send json formatted
                pc.printf("{\"event\":\"micInt\",\"mic\":%d,\"time\":%.3f}\r\n",micInterrupt,callTime); // Printf generates delay of about 5 millis

                // led blink
                led2 = !led2;
                //wait(0.1);
            }
            // reset micInterrupt flag
            trigger = 0; // pull trigger down
            micInterrupt=0;
        }
    }// end main loop
}// end main
///////////////
// Functions //
///////////////

// Interrup event from the dsp (frequency counter)
void dspInterrupt()
{
    freqTimeInterval = freqTimer.read_us() + 1; // compensate the time takes to read / reset
    freqTimer.reset();
    dspIntFlag = 1;
}//end dspInterrupt

// Atuomatic Switch Handler
void autoSwitch(int micEvent)
{
    int micChannle = 0;
    bool spkChannle[5]= {0};

    // speaker selection:

    // simple selection mode
    micChannle = micEvent;
    if ((micChannle >= 1) && (micChannle <= 5)) {// verify mic is in the limit
        spkChannle[micChannle-1] = 1;
    }

    // update hardware
    // update mic select mux
    uint8_t micByte = (uint8_t) micChannle;
    // flip mic order around 3, I guess it is the stupidest implementation but hey :) tada.
    if (micByte != 0) {
        micByte = ((~(micByte + 0b00001001)) & 0b00001111);
    }
    mux_s0.write((bool)(micByte & 0b00000001));
    mux_s1.write((bool)(micByte & 0b00000010));
    mux_s2.write((bool)(micByte & 0b00000100));
    mux_s3.write((bool)(micByte & 0b00001000));

    // update speakers:
    en_spk_1.write(spkChannle[0]);
    en_spk_2.write(spkChannle[1]);
    en_spk_3.write(spkChannle[2]);
    en_spk_4.write(spkChannle[3]);
    en_spk_5.write(spkChannle[4]);

    // send updated values json formatted
    pc.printf("{\"event\":\"switch\",\"mic\":%d,\"spk\":[%d,%d,%d,%d,%d]}\r\n",micChannle,spkChannle[0],spkChannle[1],spkChannle[2],spkChannle[3],spkChannle[4]); // Printf generates delay of about 5 millis

}// end autoSwitch

// initialize switch
void initSwitch(void)
{
    // attach serial event interrupt
    pc.attach(&rxCallback, Serial::RxIrq);

    // attach interrupt event for frequency counting
    dsp_trigger.rise(&dspInterrupt);

    // start timer
    freqTimer.start();

    // initialize timer
    timeStamp.start();

    // attach mic interrupts - Not used, polling mode
    //inter_1.rise(&micInt_1);
    //inter_2.rise(&micInt_2);
    //inter_3.rise(&micInt_3);
    //inter_4.rise(&micInt_4);
    //inter_5.rise(&micInt_5);

    // reset output / input
    mux_s0.write(0);
    mux_s1.write(0);
    mux_s2.write(0);
    mux_s3.write(0);
    en_spk_1.write(0);
    en_spk_2.write(0);
    en_spk_3.write(0);
    en_spk_4.write(0);
    en_spk_5.write(0);
}// end init switch

// poll mic interrupt gpio
bool micPolling(void)
{
    bool eventFlag=0;
    // implementation of rising interrupt in polling mode:
    if (inter_1.read() && (micInterrupt != 1)) {
        eventFlag=1;
        micInterrupt=1;
        trigger=1;
    }
    if (inter_2.read() && (micInterrupt != 2)) {
        eventFlag=1;
        micInterrupt=2;
        trigger=1;
    }
    if (inter_3.read() && (micInterrupt != 3)) {
        eventFlag=1;
        micInterrupt=3;
        trigger=1;
    }
    if (inter_4.read() && (micInterrupt != 4)) {
        eventFlag=1;
        micInterrupt=4;
        trigger=1;
    }
    if (inter_5.read() && (micInterrupt != 5)) {
        eventFlag=1;
        micInterrupt=5;
        trigger=1;
    }
    return eventFlag;
}

// serial event from DSP
void rxDspCallback(void)
{
    while (dsp.readable()) {
        uint8_t in_byte = dsp.getc();
        pc.putc(in_byte);
    }
} // end rxDspCallback

// Serial Event function
void rxCallback(void)
{
    while (pc.readable()) {
        // read icoming
        led = !led;
        uint8_t in_byte = pc.getc();
#ifdef DEBUG_MOD1
        pc.putc(in_byte);
#endif
        // detect start message , end message
        switch (packetMSG.syncFlag) {
            // waiting for header
            case 0: {
                if (packetMSG.header[packetMSG.syncIndex] == in_byte) {
                    packetMSG.syncIndex++;
                    if (packetMSG.syncIndex == HEADER_SIZE) { // finish header SYNC
                        packetMSG.syncFlag = 1; // start collecting data, wait for footer
                        packetMSG.bufferIndex = 0;
                        packetMSG.syncIndex=0;
                    }
                } else { // reinit sync
                    packetMSG.syncIndex=0;
                }
                //pc.printf("case 0 , %d  \r\n",packetMSG.syncIndex);
                break;
            }
            // waiting for footer
            case 1: {
                // add byte to buffer
                packetMSG.buffer[packetMSG.bufferIndex] = in_byte;
                packetMSG.bufferIndex++;
                if (packetMSG.bufferIndex >= MSG_BUFFER_SIZE) { // buffer overflow
                    // reset buffer
                    packetMSG.bufferIndex = 0;
                    packetMSG.syncIndex = 0;
                    packetMSG.syncFlag = 0;
                } else if (packetMSG.footer[packetMSG.syncIndex] == in_byte) { // footer char recieved
                    packetMSG.syncIndex++;
                    packetMSG.syncFlag=2; // move to verify footer
                }
                //pc.printf("case 2 , %d  \r\n",packetMSG.syncIndex);
                break;
            }
            // verify footer
            case 2: {
                // add byte to buffer
                packetMSG.buffer[packetMSG.bufferIndex] = in_byte;
                packetMSG.bufferIndex++;
                if (packetMSG.bufferIndex >= MSG_BUFFER_SIZE) { // buffer overflow
                    // reset buffer
                    packetMSG.bufferIndex = 0;
                    packetMSG.syncIndex = 0;
                    packetMSG.syncFlag = 0;
                } else if (packetMSG.footer[packetMSG.syncIndex] == in_byte) { // footer char recieved
                    packetMSG.syncIndex++;
                    if (packetMSG.syncIndex == FOOTER_SIZE) { // finish footer SYNC
                        packetMSG.syncFlag = 3;
                        // copy packet to json buffer
                        memcpy (&json, &packetMSG.buffer, packetMSG.bufferIndex);
                        json[packetMSG.bufferIndex]=NULL; // end with NULL to indicate end of string
                        // copy packet to json buffer with sprintf
                        //sprintf(json, "%.*s", packetMSG.bufferIndex, packetMSG.buffer );
                        // send msg to parse.
                        parsePacket();
                        // reset buffer
                        packetMSG.bufferIndex = 0;
                        packetMSG.syncIndex = 0;
                        packetMSG.syncFlag = 0;
                    }
                } else { // footer broke restart wait for footer
                    packetMSG.syncFlag=1;
                    // verify that it didnt broke on first footer char
                    if (packetMSG.footer[0] == in_byte) {
                        packetMSG.syncIndex=1;
                    } else {
                        packetMSG.syncIndex=0;
                    }
                }
                break;
            }
            default: {
                pc.printf("Sonmething went wrong \r\n");
                break;
            }
        } // end switch
    }// end uart readable
} // end rxCallback


// initialize packet struct
void initPacket(void)
{
    // init variables to default:
    packetMSG.header[0] = 'j';
    packetMSG.header[1] = 's';
    packetMSG.header[2] = 'o';
    packetMSG.header[3] = 'n';
    packetMSG.header[4] = ':';

    packetMSG.footer[0]= 13; // /r
    packetMSG.footer[1]= 10; // /n

    packetMSG.syncIndex=0; // sync index for header / footer
    packetMSG.syncFlag=0; // 0 - waiting for header, 1 -  waiting for footer, 2 - verify footer, 3 - finish footer send to parser, flash buffer
    packetMSG.bufferIndex=0; // buffer index
}

// Packet Parser
void parsePacket(void)
{
    string targetName;
#ifdef DEBUG_MOD2
    // write buffer to screen
    //pc.printf("%d, %.*s", packetMSG.bufferIndex ,packetMSG.bufferIndex, packetMSG.buffer );
    pc.printf("%s", json);
#endif
    // verification its a full json
    if ((json[0] == '{') &&  (json[strlen(json)-3] == '}')) {
        // GUI message format Switch: {"name":"switch","mic":0, "spk": [0,1,0,0,0]}
        parse(guiCmd, json);
        if (debuggingEn) {
            pc.printf("%s", json);
        }
        // get target:
        targetName = guiCmd["name"].get<string>(); // switch / dsp

#ifdef DEBUG_MOD2
        // send parsed values
        pc.printf("targetName: %s \r\n", targetName.c_str());
#endif

        // select handler based on target mcu
        if (targetName == "switch") {
            // disable automatic mode
            autoFlag=0;
            // update controls
            switchPacket();
        } else if ((targetName == "dsp") || (targetName == "dspParam") || (targetName == "dspFilter") || (targetName == "dspPlay")) {
            if (targetName == "dspPlay"){
                // if target is to play a sound, trigger the daq to record.
                trigger = 1;
                wait(0.001);
                trigger = 0; 
            }
            // send msg to dsp
            dsp.printf("json:%s", json);
#ifdef DEBUG_MOD2
            pc.printf("json:%s", json);
#endif
        }
        if (targetName == "auto") {
            // automatic mode
            autoFlag=1;
            pc.printf("{\"Ack\":\"switch\",\"auto\":%d}\r\n",autoFlag); // Printf
        }  else {
#ifdef DEBUG_MOD2
            // unrecognised target
            pc.printf("unrecognised target: %s \r\n", targetName.c_str());
#endif
        }
    } else { // not a full json message
        pc.printf("Error: %s", json);
    }
    // led blink
    led1 = !led1;
}// end parse

// switch Packet Handler
void switchPacket(void)
{
    int micChannle = 0;
    bool spkChannle[5]= {0};

    // get debbug status
    debuggingEn = guiCmd["debug"].get<bool>();

    // get mic channle
    micChannle = guiCmd["mic"].get<int>();

    // get loop frequency calculator flag
    loopFreqFlag = guiCmd["LoopFreq"].get<bool>();

    // get speakers output
    for (int ii=0 ; ii < 5 ; ii++) {
        spkChannle[ii] = guiCmd["spk"][ii].get<bool>();
    }
#ifdef DEBUG_MOD10
    // send parsed values
    pc.printf("mic: %d , spk: [%d,%d,%d,%d,%d]\r\n", micChannle,spkChannle[0],spkChannle[1],spkChannle[2],spkChannle[3],spkChannle[4]);
#endif
    // update hardware
    // update mic select mux
    uint8_t micByte = (uint8_t) micChannle;
    // flip mic order around 3, I guess it is the stupidest implementation but hey :) tada.
    if (micByte != 0) {
        micByte = ((~(micByte + 0b00001001)) & 0b00001111);
    }
    mux_s0.write((bool)(micByte & 0b00000001));
    mux_s1.write((bool)(micByte & 0b00000010));
    mux_s2.write((bool)(micByte & 0b00000100));
    mux_s3.write((bool)(micByte & 0b00001000));
    // update speakers:
    en_spk_1.write(spkChannle[0]);
    en_spk_2.write(spkChannle[1]);
    en_spk_3.write(spkChannle[2]);
    en_spk_4.write(spkChannle[3]);
    en_spk_5.write(spkChannle[4]);
    // end switch target parse
    pc.printf("{\"Ack\":\"switch\",\"mic\":%d,\"spk\":[%d,%d,%d,%d,%d],\"loopFreqFlag\":%d}\r\n",micChannle,spkChannle[0],spkChannle[1],spkChannle[2],spkChannle[3],spkChannle[4],loopFreqFlag);
}// end switch packet