iBreathe Breathalyzer can now talk thanks to the Text to Speech Click Board
Dependencies: Hexi_KW40Z Hexi_OLED_SSD1351 text_to_speak_mbed
Fork of iBreathe_Breathalyzer by
main.cpp
- Committer:
- daveyclk
- Date:
- 2016-10-07
- Revision:
- 9:fe5114551ec3
- Parent:
- 7:5d272a0e250b
File content as of revision 9:fe5114551ec3:
/**************************************************************************** * Title : iBreathe Breathalyzer * Filename : Talking_breathalyzer * Author : Dave Clarke * Origin Date : 27/09/2016 * Notes : Breathalyzer utilizing Hexiware, Alcohol click and Wolksense. Now with added voice thanks to the Text-To-Speech Click board *****************************************************************************/ /**************************CHANGE LIST ************************************** * * Date Software Version Initials Description * 07/10/16 1.0.0 DC Interface Created. * *****************************************************************************/ /** * @page TEST_CFG Test Configurations * <h3> Test configuration : </h3> * @par * <ul> * <li><b> MCU </b> : MK64FN1M0XXX12 </li> * <li><b> Dev. Board </b> : HEXIWEAR </li> * <li><b> Oscillator </b> : 12 MHz external </li> * <li><b> Ext. Modules </b> : Alcohol Click on mikroBUS 1 </li> * <li><b> Ext. Modules </b> : TTS Click on mikroBUS 2 </li> * <li><b> SW </b> : mBed OS5 </li> * </ul> */ /** * @mainpage * <h3> Breathalyser created with HEXIWEAR and mBed OS 5 </h3> * @par This will show you how much you've drunk and tell you with an Emoticon if * you're too hammered to even consider driving. Using the Hexiware app the readings * are transmitted to the cloud via bluetooth. Is it time to give up drinking yet?! * It now talks to you! * <h3> Alcohol Features </h3> * @par Text to Speech Click, Alcohol click, Hexiwear docking station, Hexiware */ /****************************************************************************** * Includes *******************************************************************************/ #include "mbed.h" #include "Hexi_KW40Z.h" #include "Hexi_OLED_SSD1351.h" #include "OLED_types.h" #include "OpenSans_Font.h" #include "string.h" #include "iBreatheImages.h" #include "text_to_speech.h" #include "text_to_speech_hal.h" #include "text_to_speech_hw.h" #include "text_to_speech_img.h" /****************************************************************************** * Module Variable Definitions *******************************************************************************/ #define LED_ON 0 #define LED_OFF 1 // Pointers to: const uint8_t *welcome, // Welcome screen image *blank, // blank image *blow, // Start Blowing Image *drink, // You've been drinking image *drive, // Don't drive image *hang, // You'll have a hangover image *ini, // Initialising image *sober; // Sober as a judge image const float Vadc_3V3 = 0.00005035; // 16-Bit ADC step 3V3/65535 = 0.05035 mV float Vrl = 0, // Output voltage ambientAlc = 0, // Abmient Output voltage from sensor SensorRes = 0, // SensorRes (Ohm) - Sensor resistance SensorRes1 = 0, // SensorRes (Ohm) - Sensor resistance ppm = 0, // ppm ppm_1 = 0, // Ambient ppm variable ratio = 0; // SensorRes/LoadRes ratio unsigned short adc_rd = 0; //Initialise anologue read variable const uint8_t ppmText[] = "ppm:"; // text for PPM label char text[20], // Text array variables text2[20], text3[20]; float value[20], // initial sensor set up values value1[20]; // initial sensor set up values bool isFirstBoot = true; /* Indication Flags */ static volatile bool _tts_rdy_f; static volatile bool _spc_rdy_f; static volatile bool _tts_fin_f; static volatile bool _spc_fin_f; /* Error Buffers */ static uint16_t _req_err; static uint16_t _err_code; /* Default Configuration */ static ACONF_t _audio_conf; static TTSCONF_t _tts_conf; static PMANCONF_t _pman_conf; static bool _flush_enable; /* Timer flag and counter */ static volatile bool _ticker_f; static volatile uint16_t _ticker; /* Input and output buffers */ static ISC_REQ_t _last_req; static ISC_RESP_t _last_rsp; static uint8_t test[ 8 ] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int frequency = 750000; VER_t *version; /****************************************************************************** * Function Prototypes *******************************************************************************/ /* Checks for indications */ static int _parse_ind( void ); /* Message block and error callback function pointers */ static void ( *_fatal_err_callback )( uint16_t *err_code ); static void ( *_err_callback )( uint16_t *err_code ); static void ( *_msg_block_callback )( uint16_t *msg_code, uint16_t *err_code ); void voiceinit(void); void sysinit(void); void ReadSensor(); void CalculatePPM(int times, bool amb); void ambient(int times); void StartHaptic(void); void StopHaptic(void const *n); void ButtonUp(void); void txTask(void); void fatal_err( uint16_t *err ); void msg_blk( uint16_t *req, uint16_t *err ); /****************************************************************************** * Instance setups *******************************************************************************/ /* Define timer for haptic feedback */ RtosTimer hapticTimer(StopHaptic, osTimerOnce); //set up Analog read pin for alcohol sensor AnalogIn Alcohol(PTB2); /* Instantiate the SSD1351 OLED Driver */ SSD1351 oled(PTB22,PTB21,PTC13,PTB20,PTE6, PTD15); /* (MOSI,SCLK,POWER,CS,RST,DC) */ /* Get OLED Class Default Text Properties */ oled_text_properties_t textProperties = {0}; /* Instantiate the Hexi KW40Z Driver (UART TX, UART RX) */ KW40Z kw40z_device(PTE24, PTE25); /* LED and Haptic Set ups */ DigitalOut redLed(LED1); DigitalOut greenLed(LED2); DigitalOut blueLed(LED3); DigitalOut haptic(PTB9); /* Instantiate the Click Text to Speech pinout */ DigitalOut TTS_RST(PTB19); DigitalOut TTS_CS(PTC3); DigitalOut TTS_MUTE(PTB3); DigitalIn TTS_RDY(PTB8); //Instantiate SPI for comms with Speak module SPI TextToSpeech(PTC6, PTC7, PTC5); // MOSI, MISO, SCK // Debug Serial Serial pc(USBTX, USBRX); /****************************************************************************** * Bluetooth button functions and passkey function *******************************************************************************/ void ButtonRight(void) { StartHaptic(); kw40z_device.ToggleAdvertisementMode(); blueLed = kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/ redLed = !kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/ greenLed = !kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/ } void ButtonLeft(void) { StartHaptic(); kw40z_device.ToggleAdvertisementMode(); blueLed = kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/ redLed = !kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/ greenLed = !kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/ } void PassKey(void) { StartHaptic(); strcpy((char *) text,"PAIR CODE"); oled.TextBox((uint8_t *)text,0,25,95,18); /* Display Bond Pass Key in a 95px by 18px textbox at x=0,y=40 */ sprintf(text3,"%d", kw40z_device.GetPassKey()); oled.TextBox((uint8_t *)text3,0,40,95,18); } /****************************************************************************** * Main *******************************************************************************/ ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// int main() { /* Set pointers to the BMPs stored in iBreatheImages.c */ welcome = iBreatheWS_bmp; // Welcome screen image blank = iBreatheBlank_bmp; // blank image blow = iBreatheBlow_bmp; // Start Blowing Image drink = iBreatheDrink_bmp; // You've been drinking image drive = iBreatheDrive_bmp; // Don't drive image hang = iBreatheHang_bmp; // You'll have a hangover image ini = iBreatheini_bmp; // Initialising image sober = iBreatheSober_bmp; // Sober as a judge image /* Set initial Values */ sysinit(); /* initialise voice engine */ voiceinit(); /* Welcome message on reset */ tts_speak( "Hello, welcome to the eye brethalizer " ); wait(0.5); tts_speak( "please press start. " ); } /****************************************************************************** * Public Function Definitions *******************************************************************************/ ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function readSensor(void) * ------------------------------------------------------------------------------------------------- * Overview: Read sensor * Input: None * Output: None **************************************************************************************************/ void ReadSensor() { /* Read 16 Bit Analog value */ adc_rd = Alcohol.read_u16(); // pause 200ms Thread::wait(200); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function CalculatePPM(Int times, bool amb) * ------------------------------------------------------------------------------------------------- * Overview: Calculation of PPM * Input: times = Number of samples to take, amb sets either ambient reading or test reading (true for test) * Output: None **************************************************************************************************/ void CalculatePPM(int times, bool amb) { float lgPPM; /* Read values x times */ for(int x = 0; x < times; x++) { ReadSensor(); value[x] = ((float)adc_rd * Vadc_3V3); StartHaptic(); Thread::wait(50); } /* Calculate the average value for accuratcy */ for(int y = 0; y < times; y++) { Vrl += value[y]; } Vrl = Vrl / times; /* Set SensorRes reference value */ SensorRes = (Vrl / 3.3); /* Set ratio */ ratio = SensorRes1 / SensorRes; /* Convert to PPM */ lgPPM = ( log10( ratio ) * -1.5512 ) + 2.5911; /* If true create test result, flase creates reference result */ if (amb == true) { ppm = pow( 10, lgPPM ); } else { ppm_1 = pow( 10, lgPPM ); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function void ambient(int times) * ------------------------------------------------------------------------------------------------- * Overview: Reading of the Ambient Voltgage of the sensor for reference * Input: times = Number of samples to take * Output: None **************************************************************************************************/ void ambient(int times) { /* Read ambient values x times flashing green led*/ for(int x = 0; x < times; x++) { redLed = LED_OFF; greenLed = LED_OFF; blueLed = LED_OFF; ReadSensor(); value1[x] = (float)adc_rd * Vadc_3V3; redLed = LED_OFF; greenLed = LED_ON; blueLed = LED_OFF; Thread::wait(48); } /* Calculate the average value for accuratcy */ for(int y = 0; y < times; y++) { ambientAlc+=value1[y]; } ambientAlc = ambientAlc / times; /* Set SensorRes1 reference value */ SensorRes1 = (ambientAlc / 3.3); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function void StartHaptic(void) * ------------------------------------------------------------------------------------------------- * Overview: Start Buzzing haptic motor * Input: None * Output: None **************************************************************************************************/ void StartHaptic(void) { hapticTimer.start(50); haptic = 1; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function void StopHaptic(void) * ------------------------------------------------------------------------------------------------- * Overview: Stop Buzzing haptic motor * Input: None * Output: None **************************************************************************************************/ void StopHaptic(void const *n) { haptic = 0; hapticTimer.stop(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function void ButtonUp(void) * ------------------------------------------------------------------------------------------------- * Overview: Function called whe up button pressed, Begins Breathilyzer testing procedure * Input: None * Output: None **************************************************************************************************/ void ButtonUp(void) { StartHaptic(); bool Ref = false; bool Test = true; /* LED set to green for test beginning*/ redLed = LED_OFF; greenLed = LED_ON; blueLed = LED_OFF; /* Fill 96px by 96px Screen with 96px by 96px Initialising Image starting at x=0,y=0 */ oled.DrawImage(ini,0,0); /* first boot bug work around to stop junk values */ if (isFirstBoot == true) { /*read ambient atmosphere levels with 10 samples and set flag to show it's ambient basline figure*/ ambient(1); CalculatePPM(1,Ref); isFirstBoot = false; } /*read ambient atmosphere levels with 10 samples and set flag to show it's ambient basline figure*/ tts_speak( "initializing" ); ambient(10); CalculatePPM(10,Ref); /* Fill 96px by 96px Screen with 96px by 96px Blowing Image starting at x=0,y=0 */ oled.DrawImage(blow,0,0); tts_speak( "please blow until buzzing stops" ); /*read breathe alcohol levels with 10 samples and set flag to show it's breathilyzer test figure*/ CalculatePPM(10,Test); /*Calculate the difference in Alcohol level based on Ambient and test sample*/ ppm = ppm - ppm_1; /*Throw away any values less than 0*/ if (ppm < 0) { ppm = 0; } /* Fill 96px by 96px Screen with 96px by 96px Blank Background Image starting at x=0,y=0 */ oled.DrawImage(blank,0,0); tts_speak( "Test Complete" ); Thread::wait(500); /* Show Calculated alcohol level in PPM and send data via bluetooth to the cloud */ oled.SetTextProperties(&textProperties); oled.Label(ppmText,20,36); tts_speak( "you blew" ); sprintf(text,"%.2f",ppm); Thread::wait(50); tts_speak( text ); oled.Label((uint8_t *)text,50,36); Thread::wait(50); tts_speak( "pee pee emm" ); Thread::wait(1000); /* Currently sending to the Pressure variable as a temp place holder */ kw40z_device.SendiBreathe(ppm); // this is used for custom Hexiware app kw40z_device.SendPressure(ppm * 10); // using this to record on Wolksense Cloud /* You've got a Hangover coming!*/ if ( ppm > 200) { tts_speak("you have got a hang over coming"); redLed = LED_ON; greenLed = LED_OFF; blueLed = LED_OFF; StartHaptic(); /* Fill 96px by 96px Screen with 96px by 96px Hangover Image starting at x=0,y=0 */ oled.DrawImage(hang,0,0); } /* You Shouldn't drive */ else if (ppm < 200 && ppm > 150) { tts_speak("you shouldn't drive"); redLed = LED_ON; greenLed = LED_OFF; blueLed = LED_ON; StartHaptic(); /* Fill 96px by 96px Screen with 96px by 96px Don't Drive Image starting at x=0,y=0 */ oled.DrawImage(drive,0,0); } /* You've had a drink */ else if (ppm < 150 && ppm > 50) { tts_speak("you have had a drinky"); redLed = LED_OFF; greenLed = LED_ON; blueLed = LED_ON; StartHaptic(); /* Fill 96px by 96px Screen with 96px by 96px Had a drink Image starting at x=0,y=0 */ oled.DrawImage(drink,0,0); } /* Sober as a judge*/ else { tts_speak("you are as sober as a judge"); redLed = LED_OFF; greenLed = LED_ON; blueLed = LED_OFF; StartHaptic(); oled.DrawImage(sober,0,0); } Thread::wait(5000); /* Go back to start screen */ sysinit(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function void sysint(void) * ------------------------------------------------------------------------------------------------- * Overview: System Initial Values Set up * Input: None * Output: None **************************************************************************************************/ void sysinit(void) { /* Set LED to Blue by default*/ redLed = LED_OFF; greenLed = LED_OFF; blueLed = LED_ON; /* Turn on the backlight of the OLED Display */ oled.DimScreenON(); /* Fill 96px by 96px Screen with 96px by 96px Welcome Image starting at x=0,y=0 */ oled.DrawImage(welcome,0,0); /* Register callbacks to application functions */ kw40z_device.attach_buttonUp(&ButtonUp); kw40z_device.attach_buttonLeft(&ButtonLeft); kw40z_device.attach_buttonRight(&ButtonRight); kw40z_device.attach_passkey(&PassKey); /* Send sensor data to bluetooth */ kw40z_device.SendSetApplicationMode(GUI_CURRENT_APP_SENSOR_TAG); /* Change font color to White */ oled.GetTextProperties(&textProperties); textProperties.fontColor = COLOR_WHITE; oled.SetTextProperties(&textProperties); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function void voiceinit(void) * ------------------------------------------------------------------------------------------------- * Overview: initialise voice engine * Input: None * Output: None **************************************************************************************************/ void voiceinit(void) { /*setting up the SPI defaults*/ TextToSpeech.lock(); TextToSpeech.format(8,3); TextToSpeech.frequency(frequency); TextToSpeech.unlock(); /* initialise voice */ pc.printf("System Init Done!\r\n"); tts_init(); pc.printf("tts Init Done!\r\n"); tts_setup(); pc.printf("tts setup Done!\r\n"); tts_msg_block_callback( msg_blk ); tts_fatal_err_callback( fatal_err ); tts_config( 0x10, false, TTSV_US, 0x0080 ); tts_unmute(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function msg_blk( uint16_t *req, uint16_t *err ) * ------------------------------------------------------------------------------------------------- * Overview: receive a blocked message from S1V30120; **************************************************************************************************/ void msg_blk( uint16_t *req, uint16_t *err ) { char txt[ 6 ]; pc.printf( " MSG BLOCKED \r\n" ); sprintf( txt, "%x\r\n", *req ); pc.printf( txt ); sprintf( txt, "%x\r\n", *err ); pc.printf( txt ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * Function fatal_err( uint16_t *err ) * ------------------------------------------------------------------------------------------------- * Overview: error detected **************************************************************************************************/ void fatal_err( uint16_t *err ) { pc.printf( "Fatal Error Detected" ); tts_init(); tts_fatal_err_callback( fatal_err ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * text_to_speech_hal.c * ------------------------------------------------------------------------------------------------- * Overview: Functions to control SPI interface. **************************************************************************************************/ void tts_hal_cs_high() { TTS_CS = 1; } void tts_hal_cs_low() { TTS_CS = 0; } void tts_hal_mut_high() { TTS_MUTE = 1; } void tts_hal_mut_low() { TTS_MUTE = 0; } void tts_hal_reset( void ) { tts_hal_cs_high(); TTS_RST = 0; tts_hal_write_pad(1); wait(0.01); TTS_RST = 1; wait(POR_TIME); } bool tts_hal_msg_rdy( void ) { return TTS_RDY; } void tts_hal_init() { tts_hal_reset(); tts_hal_cs_high(); tts_hal_mut_low(); } void tts_hal_write( uint8_t *buffer, uint16_t count, bool boot ) { TextToSpeech.lock(); while( count-- ) { if(!boot) pc.printf("%02X\r\n", *buffer); TextToSpeech.write( *buffer++ ); } TextToSpeech.unlock(); } void tts_hal_write_pad( int cnt ) { TextToSpeech.lock(); tts_hal_cs_low(); while(cnt--) { TextToSpeech.write( PADDING_BYTE ); } tts_hal_cs_high(); TextToSpeech.unlock(); } void tts_hal_read( uint8_t *buffer, uint16_t count ) { TextToSpeech.lock(); while(count--) { *buffer++ = TextToSpeech.write( DUMMY_BYTE ); //read spi bus //pc.printf("buffer = %X\n\r", *buffer); } TextToSpeech.unlock(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * text_to_speech.c * ------------------------------------------------------------------------------------------------- * Overview: Functions to control the setup and speech controls **************************************************************************************************/ static int _parse_ind( void ) { uint16_t rsp_idx = tts_rsp_idx(); if ( rsp_idx == ISC_MSG_BLOCKED_RESP ) { uint8_t rsp_data[ 4 ] = { 0 }; _req_err = 0; _err_code = 0; tts_rsp_data( rsp_data ); _req_err |= rsp_data[ 0 ]; _req_err |= rsp_data[ 1 ] << 8; _err_code |= rsp_data[ 2 ]; _err_code |= rsp_data[ 3 ] << 8; if( _msg_block_callback != NULL ) _msg_block_callback( &_req_err, &_err_code ); return 1; } else if ( rsp_idx == ISC_ERROR_IND ) { uint8_t rsp_data[ 4 ] = { 0 }; _req_err = 0; _err_code = 0; tts_rsp_data( rsp_data ); _err_code |= rsp_data[ 0 ]; _err_code |= rsp_data[ 1 ] << 8; if ( _err_code && _err_code < 0x8000 ) { if( _err_callback != NULL ) _err_callback( &_err_code ); } else if ( _err_code && _err_code > 0x7FFF ) { if( _fatal_err_callback != NULL ) _fatal_err_callback( &_err_code ); } return 1; } else if ( rsp_idx == ISC_TTS_READY_IND ) { _tts_rdy_f = 1; } else if ( rsp_idx == ISC_SPCODEC_READY_IND ) { _spc_rdy_f = 1; } else if ( rsp_idx == ISC_TTS_FINISHED_IND ) { _tts_fin_f = 1; } else if ( rsp_idx == ISC_SPCODEC_FINISHED_IND ) { _spc_fin_f = 1; } return 0; } void tts_init() { _req_err = 0; _err_code = 0; _tts_rdy_f = true; _spc_rdy_f = true; _tts_fin_f = true; _spc_fin_f = true; _audio_conf.as = 0x00; _audio_conf.ag = 0x43; _audio_conf.amp = 0x00; _audio_conf.asr = ASR_11KHZ; _audio_conf.ar = 0x00; _audio_conf.atc = 0x00; _audio_conf.acs = 0x00; _audio_conf.dc = 0x00; _tts_conf.sr = 0x01; _tts_conf.voice = 0x00; _tts_conf.ep = 0x00; _tts_conf.lang = TTSV_US; _tts_conf.sr_wpm_lsb = 0xc8; _tts_conf.sr_wpm_msb = 0x00; _tts_conf.ds = 0x00; _tts_conf.res = 0x00; _pman_conf.am_lsb = 0x01; _pman_conf.am_msb = 0x00; _pman_conf.spi_clk = 0x01; _pman_conf.pad = PADDING_BYTE; _flush_enable = false; _msg_block_callback = NULL; _fatal_err_callback = NULL; _err_callback = NULL; tts_hw_init(); } void tts_msg_block_callback( void( *msg_blk_ptr )( uint16_t *req_ptr, uint16_t *err_ptr ) ) { _msg_block_callback = msg_blk_ptr; } void tts_fatal_err_callback( void( *fatal_err_ptr )( uint16_t *err_ptr ) ) { _fatal_err_callback = fatal_err_ptr; } void tts_err_callback( void( *error_ptr )( uint16_t *err_ptr ) ) { _err_callback = error_ptr; } void tts_mute() { tts_mute_cmd( true ); } void tts_unmute() { tts_mute_cmd( false ); } void tts_setup() { //check HW version tts_version_boot(); int succ = tts_image_load( (uint8_t*)TTS_INIT_DATA, sizeof( TTS_INIT_DATA ) ); if ( succ != 0x0001 ) { // image load failed, try turning it off and on again pc.printf("tts init data failed!\n\r"); pc.printf("returned value: %X\n\r", succ); while(1); } pc.printf("tts image load done\n\r"); succ = tts_image_exec(); if ( succ != 0x0001 ) { // image boot failed, try turning it off and on again pc.printf("tts image exec failed!\n\r"); pc.printf("returned value: %X\n\r", succ); while(1); } pc.printf("tts image exec done\n\r"); tts_interface_test(); pc.printf("tts interface test done\n\r"); tts_power_default_config(); pc.printf("tts power default done\n\r"); tts_audio_default_config(); pc.printf("tts audio default done\n\r"); tts_volume_set( 0 ); pc.printf("tts volume set done\n\r"); tts_default_config(); pc.printf("tts default config done\n\r"); } void tts_version_boot( void ) { uint8_t tmp_resp[ 16 ] = { 0 }; wait( RESET_TO_BOOT_TIME ); tts_parse_req( ISC_VERSION_REQ_BOOT, NULL, 0 ); while( !tts_rsp_chk( ISC_VERSION_RESP_BOOT ) ) { tts_get_resp(); } tts_hal_write_pad( 16); tts_rsp_data( tmp_resp ); pc.printf("hwver0 %X \n\r", tmp_resp[ 0 ]); pc.printf("hwver1 %X \n\r", tmp_resp[ 1 ]); } uint16_t tts_image_load(const uint8_t *image, uint16_t count ) { uint16_t tmp_resp = 0; uint16_t index = 0; uint8_t raw_resp[ 2 ] = { 0 }; while ( ( count - index ) > ( BOOT_MESSAGE_MAX - 4 ) ) { tts_parse_boot_img( image + index, BOOT_MESSAGE_MAX - 4 ); wait(0.01); index += ( BOOT_MESSAGE_MAX - 4 ); } tts_parse_boot_img( image + index, count - index ); wait(0.01); while( !tts_rsp_chk( ISC_BOOT_LOAD_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_image_exec() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; tts_parse_req( ISC_BOOT_RUN_REQ, NULL, 0 ); while( !tts_rsp_chk( ISC_BOOT_RUN_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_interface_test() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; wait( BOOT_TO_MAIN_MODE ); tts_parse_req( ISC_TEST_REQ, test, 8 ); while( !tts_rsp_chk( ISC_TEST_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_version_main( VER_t *buffer ) { char tmp_char[ 3 ] = { 0 }; uint32_t tmp_fwf = 0; uint32_t tmp_fwef = 0; uint8_t tmp_resp[ 20 ] = { 0 }; tts_parse_req( ISC_VERSION_REQ_MAIN, NULL, 0 ); while( !tts_rsp_chk( ISC_VERSION_RESP_MAIN ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( tmp_resp ); sprintf(tmp_char, "%c", tmp_resp[ 0 ]); strcpy( buffer->hwver, tmp_char ); strcat( buffer->hwver, "." ); sprintf(tmp_char, "%c", tmp_resp[ 1 ]); strcat( buffer->hwver, tmp_char ); sprintf(tmp_char, "%c", tmp_resp[ 2 ]); strcpy( buffer->fwver, tmp_char ); strcat( buffer->fwver, "." ); sprintf(tmp_char, "%c", tmp_resp[ 3 ]); strcat( buffer->fwver, tmp_char ); strcat( buffer->fwver, "." ); sprintf(tmp_char, "%c", tmp_resp[ 12 ]); strcat( buffer->fwver, tmp_char ); tmp_fwf |= tmp_resp[ 4 ]; tmp_fwf |= tmp_resp[ 5 ] << 8; tmp_fwf |= tmp_resp[ 6 ] << 16; tmp_fwf |= tmp_resp[ 7 ] << 24; buffer->fwf = (FF_t)tmp_fwf; tmp_fwef |= tmp_resp[ 8 ]; tmp_fwef |= tmp_resp[ 9 ] << 8; tmp_fwef |= tmp_resp[ 10 ] << 16; tmp_fwef |= tmp_resp[ 11 ] << 24; buffer->fwef = (EFF_t)tmp_fwef; return 0x0000; } uint16_t tts_power_default_config() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; tts_parse_req( ISC_PMAN_CONFIG_REQ, ( uint8_t* )&_pman_conf, 4 ); while( !tts_rsp_chk( ISC_PMAN_CONFIG_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_standby_enter() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; tts_parse_req( ISC_PMAN_STANDBY_ENTRY_REQ, NULL, 0 ); while( !tts_rsp_chk( ISC_PMAN_STANDBY_ENTRY_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_standby_exit() { wait( STBY_MODE_ENTERY ); tts_parse_req( ISC_PMAN_STANDBY_EXIT_IND, NULL, 0 ); while( !tts_rsp_chk( ISC_PMAN_STANDBY_EXIT_IND ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } return 0x0000; } uint16_t tts_audio_default_config() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; tts_parse_req( ISC_AUDIO_CONFIG_REQ, ( uint8_t* )&_audio_conf, 8 ); while( !tts_rsp_chk( ISC_AUDIO_CONFIG_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_audio_config( int8_t audio_gain, ASR_t sample_rate, bool dac_control ) { ACONF_t audio_conf; uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; if ( audio_gain < -48 || audio_gain > 18 ) return 0xFFFF; if ( sample_rate != 0 || sample_rate != 1 || sample_rate != 3 ) return 0xFFFF; audio_conf.ag = ( uint8_t )audio_gain; audio_conf.asr = sample_rate; audio_conf.dc = dac_control; tts_parse_req( ISC_AUDIO_CONFIG_REQ, ( uint8_t* )&audio_conf, 8 ); while( !tts_rsp_chk( ISC_AUDIO_CONFIG_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_volume_set( int16_t gain ) { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_gain[ 2 ] = { 0 }; tmp_gain[ 0 ] = gain & 0x00FF; tmp_gain[ 1 ] = ( gain & 0xFF00 ) >> 8; tts_parse_req( ISC_AUDIO_VOULME_REQ, tmp_gain, 2 ); while( !tts_rsp_chk( ISC_AUDIO_VOLUME_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_audio_mute() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_mute[ 2 ] = { 1, 0 }; tts_parse_req( ISC_AUDIO_MUTE_REQ, tmp_mute, 2 ); while( !tts_rsp_chk( ISC_AUDIO_MUTE_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_audio_unmute() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_mute[ 2 ] = { 0, 0 }; tts_parse_req( ISC_AUDIO_MUTE_REQ, tmp_mute, 2 ); while( !tts_rsp_chk( ISC_AUDIO_MUTE_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_default_config() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; tts_parse_req( ISC_TTS_CONFIG_REQ, ( uint8_t* )&_tts_conf, 8 ); while( !tts_rsp_chk( ISC_TTS_CONFIG_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_config( uint8_t voice_type, bool epson_parse, TTSV_t language, uint16_t speaking_rate ) { TTSCONF_t tts_conf; uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; if ( voice_type > 8 ) return 0xFFFF; if ( language > 4 ) return 0xFFFF; if ( speaking_rate < 0x004B || speaking_rate > 0x0258 ) return 0xFFFF; tts_conf.voice = voice_type; tts_conf.ep = epson_parse; tts_conf.lang = language; tts_conf.sr_wpm_lsb = ( speaking_rate & 0x00FF ); tts_conf.sr_wpm_msb = ( speaking_rate & 0xFF00 ) >> 8; tts_parse_req( ISC_TTS_CONFIG_REQ, ( uint8_t* )&tts_conf, 8 ); while( !tts_rsp_chk( ISC_TTS_CONFIG_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_speak( char *word ) { bool tmp_f = false; char *wptr = word; uint8_t raw_resp[ 2 ] = { 0 }; uint16_t tmp_resp = 0; uint32_t wlen = strlen( wptr ); tts_parse_speak_req( ISC_TTS_SPEAK_REQ, _flush_enable, wptr, wlen ); _tts_rdy_f = 0; _tts_fin_f = 0; while( !( tmp_f && _tts_rdy_f ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; if( tts_rsp_chk( ISC_TTS_SPEAK_RESP ) ) { tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; tmp_f = true; } } return tmp_resp; } uint16_t tts_pause( void ) { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_pause[ 2 ] = { 1, 0 }; tts_parse_req( ISC_TTS_PAUSE_REQ, tmp_pause, 2 ); while( !tts_rsp_chk( ISC_TTS_PAUSE_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_unpause( void ) { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_pause[ 2 ] = { 0 }; tts_parse_req( ISC_TTS_PAUSE_REQ, tmp_pause, 2 ); while( !tts_rsp_chk( ISC_TTS_PAUSE_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_stop( bool reset ) { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_reset[ 2 ] = { 0 }; if( reset ) tmp_reset[ 0 ] = 0x01; tts_parse_req( ISC_TTS_STOP_REQ, tmp_reset, 2 ); while( !tts_rsp_chk( ISC_TTS_STOP_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_user_dict( bool erase, uint8_t *udict_data, uint16_t count ) { uint16_t cnt = 2; uint16_t tmp_rsp = 0; uint8_t rsp_data[ 2 ] = { 0 }; uint8_t tmp_data[ BOOT_MESSAGE_MAX ] = { 0 }; if ( erase ) tmp_data[ 0 ] = 1; while ( count-- ) tmp_data[ cnt ++ ] = *( udict_data++ ); tts_parse_req( ISC_TTS_UDICT_DATA_REQ, tmp_data, count + 2 ); while( !tts_rsp_chk( ISC_TTS_UDICT_DATA_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( rsp_data ); tmp_rsp |= rsp_data[ 0 ]; tmp_rsp |= rsp_data[ 1 ] << 8; return tmp_rsp; } uint16_t tts_codec_configure() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_codec[ 32 ] = { 0 }; tmp_codec[ 0 ] = 0x01; tmp_codec[ 1 ] = 0x01; tmp_codec[ 24 ] = 0x02; tts_parse_req( ISC_SPCODEC_CONFIG_REQ , tmp_codec, 32 ); while( !tts_rsp_chk( ISC_SPCODEC_CONFIG_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_codec_start( uint8_t *codec_data, uint16_t count ) { bool tmp_f = false; uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; if ( count != 512 || count != 1024 || count != 2048 ) return 0xFFFF; while( !_spc_fin_f ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_parse_req( ISC_SPCODEC_START_REQ , codec_data, count ); _spc_rdy_f = 0; _spc_fin_f = 0; while( !( tmp_f && _spc_rdy_f ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; if( tts_rsp_chk( ISC_TTS_SPEAK_RESP ) ) { tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; tmp_f = true; } } return tmp_resp; } uint16_t tts_codec_pause() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_data[ 2 ] = { 1, 0 }; tts_parse_req( ISC_SPCODEC_PAUSE_REQ , tmp_data, 2 ); while( !tts_rsp_chk( ISC_SPCODEC_PAUSE_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_codec_unpause() { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_data[ 2 ] = { 0 }; tts_parse_req( ISC_SPCODEC_PAUSE_REQ , tmp_data, 2 ); while( !tts_rsp_chk( ISC_SPCODEC_PAUSE_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } uint16_t tts_codec_stop( bool reset ) { uint16_t tmp_resp = 0; uint8_t raw_resp[ 2 ] = { 0 }; uint8_t tmp_data[ 2 ] = { 0 }; if( reset ) tmp_data[ 0 ] = 1; tts_parse_req( ISC_SPCODEC_STOP_REQ, tmp_data, 2 ); while( !tts_rsp_chk( ISC_SPCODEC_PAUSE_RESP ) ) { tts_get_resp(); if( _parse_ind() ) return _err_code; } tts_rsp_data( raw_resp ); tmp_resp |= raw_resp[ 0 ]; tmp_resp |= raw_resp[ 1 ] << 8; return tmp_resp; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// /************************************************************************************************** * text_to_speech_hw.c * ------------------------------------------------------------------------------------------------- * Overview: Mainly read and write functions with parsing **************************************************************************************************/ void _read_rsp() { uint8_t tmp_byte = 0; uint16_t tmp_len = 0; tts_hal_cs_low(); tts_hal_read( &tmp_byte, 1 ); if( tmp_byte == START_MESSAGE ) { tts_hal_read( ( uint8_t* )&_last_rsp, 2 ); tmp_len |= _last_rsp.len[ 0 ]; tmp_len |= _last_rsp.len[ 1 ] << 8; tts_hal_read( ( uint8_t* )&_last_rsp + 2, tmp_len ); } else { wait( 0.005 ); } tts_hal_cs_high(); } void _write_req(bool boot) { uint16_t cnt = 0; uint8_t start = START_MESSAGE; cnt |= _last_req.len[ 0 ]; cnt |= _last_req.len[ 1 ] << 8; tts_hal_cs_low(); if(boot) // use for debug - displays the current buffer on serial { tts_hal_write( &start, 1, true ); tts_hal_write( ( uint8_t* )&_last_req, cnt, true); } else { tts_hal_write( &start, 1, false ); tts_hal_write( ( uint8_t* ) &_last_req, cnt, false ); } tts_hal_cs_high(); } /****************************************************************************** * Public Function Definitions *******************************************************************************/ void tts_hw_init( void ) { tts_hal_init(); _ticker_f = false; _ticker = 0; _last_req.idx[ 0 ] = 0; _last_req.idx[ 1 ] = 0; _last_req.len[ 0 ] = 0; _last_req.len[ 1 ] = 0; memset( _last_req.payload, 0, MAIN_MESSAGE_MAX ); _last_rsp.idx[ 0 ] = 255; _last_rsp.idx[ 1 ] = 255; _last_rsp.len[ 0 ] = 0; _last_rsp.len[ 1 ] = 0; memset( _last_rsp.payload, 0, RESP_MESSAGE_MAX ); } void tts_tick_isr() { _ticker++; if( _ticker > 500 ) _ticker_f == true; } void tts_mute_cmd( bool cmd ) { if( cmd ) tts_hal_mut_high(); else tts_hal_mut_low(); } void tts_parse_req( uint16_t req, uint8_t *payload, uint16_t pl_len ) { uint8_t *pl = payload; uint16_t i = 0; uint16_t tmp = pl_len + 4; _last_req.len[ 0 ] = tmp & 0x00FF; _last_req.len[ 1 ] = ( tmp & 0xFF00 ) >> 8; _last_req.idx[ 0 ] = req & 0x00FF; _last_req.idx[ 1 ] = ( req & 0xFF00 ) >> 8; _last_rsp.idx[ 0 ] = 0xFF; _last_rsp.idx[ 1 ] = 0xFF; if ( payload != NULL ) { while ( pl_len-- ) _last_req.payload[ i++ ] = *( pl++ ); } _write_req(true); } void tts_parse_boot_img( const uint8_t *payload, uint16_t pl_len ) { uint16_t i = 0; uint16_t tmp = pl_len + 0x04; _last_req.len[ 0 ] = tmp & 0x00FF; _last_req.len[ 1 ] = ( tmp & 0xFF00 ) >> 8; _last_req.idx[ 0 ] = 0x00; _last_req.idx[ 1 ] = 0x10; _last_rsp.idx[ 0 ] = 0xFF; _last_rsp.idx[ 1 ] = 0xFF; if ( payload != NULL ) { while ( pl_len-- ) _last_req.payload[ i++ ] = payload[ i ]; } _write_req(true); } void tts_parse_speak_req( uint16_t req, uint8_t flush_en, char *word, uint16_t word_len ) { char *ptr = word; uint16_t i = 1; uint16_t tmp = word_len; word_len += 7; _last_req.len[ 0 ] = word_len & 0x00FF; _last_req.len[ 1 ] = ( word_len & 0xFF00 ) >> 8; _last_req.idx[ 0 ] = req & 0x00FF; _last_req.idx[ 1 ] = ( req & 0xFF00 ) >> 8; _last_rsp.idx[ 0 ] = 0xFF; _last_rsp.idx[ 1 ] = 0xFF; if( flush_en ) { _last_req.payload[ 0 ] = 1; } else { _last_req.payload[ 0 ] = 0; } while( tmp-- ) _last_req.payload[ i++ ] = *( ptr++ ); _last_req.payload[ i++ ] = 0x20; _last_req.payload[ i ] = 0x00; _write_req(true); } void tts_get_resp() { // only read if ready if( tts_hal_msg_rdy() ) { _read_rsp(); } } bool tts_rsp_chk( uint16_t idx ) { uint16_t tmp = 0; tmp |= _last_rsp.idx[ 0 ]; tmp |= _last_rsp.idx[ 1 ] << 8; return ( idx == tmp ) ? true : false; } uint16_t tts_rsp_idx() { uint16_t tmp = 0; tmp |= _last_rsp.idx[ 0 ]; tmp |= _last_rsp.idx[ 1 ] << 8; return tmp; } void tts_rsp_data( uint8_t *buffer ) { uint8_t *bfr = buffer; uint16_t cnt = 0; uint8_t *ptr = _last_rsp.payload; cnt |= _last_rsp.len[ 0 ]; cnt |= _last_rsp.len[ 1 ] << 8; cnt -= 4; while( cnt-- ) *( bfr++ ) = *( ptr++ ); } /*************** END OF FUNCTIONS *********************************************/