From 804ba7628b8e4cff165cd213e745d8a9db3a4d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Sun, 27 Sep 2020 12:00:22 +0200 Subject: [PATCH] application: allows BT audio pairing, play random songs depending on time --- application.c | 205 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 153 insertions(+), 52 deletions(-) diff --git a/application.c b/application.c index 5366e84..1c9be82 100644 --- a/application.c +++ b/application.c @@ -72,7 +72,9 @@ static volatile bool mp3_rx_flag = false; /**< if data has been received from th static volatile bool door_flag = false; /**< set when the door lock switch changes */ /** @} */ -/** switch in the door to verify if its locked (closed when locked) */ +/** switch in the door to verify if its locked (closed when locked) + * @note use external pull-up resistor (10k to 5V) since the internal pull up does not cope for the long cable + */ #define DOOR_PIN PB8 /** USART port used to communicate with catalex MP3 player */ @@ -82,6 +84,24 @@ static volatile uint8_t mp3_rx_data[10]; /** number of byte received from MP3 player */ static volatile uint8_t mp3_rx_len; +/** pin to simulate button press (go high) to switch on/off Bluetooth audio transmitter + * - 2s press: on/off + * - 5s press when off: pair + */ +#define BTAUDIO_BUTTON_PIN PA0 +/** status output (e.g. LED) of Bluetooth audio transmitter + * - off: off + * - fast flash (periodic blink every 0.3s): pairing + * - slow flash (double blink every 5.3s): unconnected + * - very slow flash (single blink every 10s): connected + */ +#define BTAUDIO_STATUS_PIN PA1 +/** time (in ms) to press on the button to switch Bluetooth audio transmitter */ +#define BTAUDIO_ON 3500 + +/** number of timer led Bluetooth audio transmitter blinked, to determine the status */ +static uint8_t btaudio_status = 0; + /** catalex MP3 player commands */ enum mp3_commands_t { MP3_CMD_NEXT_SONG = 0x01, @@ -110,6 +130,15 @@ enum mp3_commands_t { MP3_CMD_QUERY_FLDR_COUNT = 0x4f, }; +/** number of possible music tracks */ +#define MUSIC_TRACKS 19 +/** if a music track has been played */ +static bool music_played = false; +/** number of possible talk tracks */ +#define TALK_TRACKS 27 +/** if a talk track has been played */ +static bool talk_played = false; + size_t putc(char c) { size_t length = 0; // number of characters printed @@ -190,6 +219,8 @@ static bool mp3_response(void) if (0xef != mp3_rx_data[3 + mp3_rx_data[2]]) { // end by is not correct return false; } + + puts("MP3 response: "); /* // display message puts("> "); @@ -198,6 +229,7 @@ static bool mp3_response(void) } putc('\n'); */ + // check checksum int16_t checksum = 0; for (uint8_t i = 1; i < mp3_rx_len && i < mp3_rx_data[2] + 1U; i++) { @@ -251,6 +283,24 @@ static void mp3_command(enum mp3_commands_t cmd, uint16_t data) case MP3_CMD_NEXT_SONG: puts("next"); break; + case MP3_CMD_STOP_PLAY: + puts("stop"); + break; + case MP3_CMD_PLAY_FOLDER_FILE: + puts("playing "); + const uint8_t folder = data >> 8; + const uint8_t track = data & 0xff; + printf("%02u/%03u", folder, track); + if (0 == folder) { + puts(" (invalid input folder 0)"); + } + if (0 == track) { + puts(" (invalid input track 0"); + } + break; + case MP3_CMD_SET_VOLUME: + printf("set volume to %u", data); + break; case MP3_CMD_RESET: puts("reset"); break; @@ -258,7 +308,7 @@ static void mp3_command(enum mp3_commands_t cmd, uint16_t data) printf("%+02x"); break; } - putc(' '); + putc('\n'); uint8_t command[] = {0x7e, 0xff, 0x06, cmd, 0x01, data >> 8, data, 0xef}; // command template (with feedback) for (uint8_t i = 0; i < LENGTH(command); i++) { @@ -271,31 +321,34 @@ static void mp3_command(enum mp3_commands_t cmd, uint16_t data) */ static void command_play(void* argument) { - (void)argument; // argument not used - puts("sending play command\n"); - mp3_command(MP3_CMD_PLAY, 0); -} - -/** open/close door - * @param[in] argument no argument required - */ -static void command_door(void* argument) -{ - (void)argument; // argument not used - if (0 == timer_door_closed) { - puts("closing door\n"); - timer_door_closed = rtc_get_counter_val(); - led_tm1637_time(0, 0); - led_tm1637_on(); - mp3_command(MP3_CMD_PLAY, 0); + if (NULL == argument) { + puts("playing first track\n"); + mp3_command(MP3_CMD_PLAY, 1); // play first song } else { - puts("opening door\n"); - timer_door_closed = 0; - led_tm1637_off(); - mp3_command(MP3_CMD_STOP_PLAY, 0); // stop playing + const uint32_t track = *(uint32_t*)argument; // argument not used + printf("playing track %u\n", track); + mp3_command(MP3_CMD_PLAY_FOLDER_FILE, ((track / 100) << 8) + (track % 100)); // play specific track } } +/** put Bluetooth audi transmitter into pairing mode + * @param[in] argument no argument required + */ +static void command_pair(void* argument) +{ + (void)argument; // argument not used + // we assume the transmitter is on + puts("switching BT audio off\n"); + gpio_set(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // press button to switch off + sleep_ms(BTAUDIO_ON); // keep pressed + gpio_clear(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // release button + puts("putting BT audio into pairing mode\n"); + sleep_ms(1000); // wait a bit + gpio_set(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // press button to put into pairing mode + sleep_ms(6000); // keep pressed + gpio_clear(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // release button + puts("BT transmitter should be looking for speaker\n"); +} /** list of all supported commands */ static const struct menu_command_t menu_commands[] = { @@ -353,17 +406,17 @@ static const struct menu_command_t menu_commands[] = { .shortcut = 'p', .name = "play", .command_description = "play MP3 song", - .argument = MENU_ARGUMENT_NONE, - .argument_description = NULL, + .argument = MENU_ARGUMENT_UNSIGNED, + .argument_description = "track folder+number fftt", .command_handler = &command_play, }, { - .shortcut = 'd', - .name = "door", - .command_description = "open/close door", + .shortcut = 'a', + .name = "audio", + .command_description = "put BT audio transmitter into pairing mode", .argument = MENU_ARGUMENT_NONE, .argument_description = NULL, - .command_handler = &command_door, + .command_handler = &command_pair, }, }; @@ -503,7 +556,7 @@ void main(void) uart_setup(); // setup USART (for printing) #endif usb_cdcacm_setup(); // setup USB CDC ACM (for printing) - puts("\nwelcome to the CuVoodoo STM32F1 example application\n"); // print welcome message + puts("\nwelcome to the CuVoodoo Dachboden Klo-Assistant\n"); // print welcome message #if DEBUG // show reset cause @@ -558,17 +611,32 @@ void main(void) puts("setup TM1637 7-segment display: "); bool led_tm1637_setup_ok = true; - led_tm1637_setup(); - led_tm1637_setup_ok &= led_tm1637_brightness(LED_TM1637_14DIV16); // set maximum brightess + led_tm1637_setup(true); + led_tm1637_setup_ok &= led_tm1637_brightness(LED_TM1637_14DIV16); // set maximum brightness led_tm1637_setup_ok &= led_tm1637_time(88, 88); // light up all segments led_tm1637_setup_ok &= led_tm1637_on(); // switch on to test it - sleep_ms(1000); // let used check display - led_tm1637_setup_ok &= led_tm1637_off(); // switch off and let main loop handle it if (led_tm1637_setup_ok) { puts("OK\n"); } else { puts("error\n"); } + + puts("setup Bluetooth audio: "); + rcc_periph_clock_enable(GPIO_RCC(BTAUDIO_BUTTON_PIN)); // enable clock for button + gpio_clear(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // set as unpressed + gpio_set_mode(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(BTAUDIO_BUTTON_PIN)); // set button pin as output + gpio_set(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // press button to switch on + sleep_ms(BTAUDIO_ON); // keep pressed + gpio_clear(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // release button + rcc_periph_clock_enable(GPIO_RCC(BTAUDIO_STATUS_PIN)); // enable clock for GPIO peripheral to read status input + gpio_set_mode(GPIO_PORT(BTAUDIO_STATUS_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(BTAUDIO_STATUS_PIN)); // set status pin as input + exti_set_trigger(GPIO_EXTI(BTAUDIO_STATUS_PIN), EXTI_TRIGGER_FALLING); // trigger when LED goes on + exti_enable_request(GPIO_EXTI(BTAUDIO_STATUS_PIN)); // enable external interrupt + nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(BTAUDIO_STATUS_PIN)); // enable interrupt + btaudio_status = 0; // number of LED blinks received, to determine the BT audio state + puts("OK\n"); + + led_tm1637_setup_ok &= led_tm1637_off(); // switch off and let main loop handle it // setup LEDs puts("setup SK6812RGBW LEDs: "); @@ -602,10 +670,14 @@ void main(void) usart_enable(USART(MP3_UART)); // enable UART puts("OK\n"); mp3_command(MP3_CMD_RESET, 0); // reset all settings + sleep_ms(500); + mp3_command(MP3_CMD_SEL_DEV, 2); // set volume lower (BT audio transmitter saturates) + sleep_ms(500); + mp3_command(MP3_CMD_SET_VOLUME, 15); // set volume lower (BT audio transmitter saturates) puts("setup door lock switch: "); rcc_periph_clock_enable(GPIO_RCC(DOOR_PIN)); // enable clock for button - gpio_set_mode(GPIO_PORT(DOOR_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(DOOR_PIN)); // set button pin to input + gpio_set_mode(GPIO_PORT(DOOR_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(DOOR_PIN)); // set button pin to input rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt exti_select_source(GPIO_EXTI(DOOR_PIN), GPIO_PORT(DOOR_PIN)); // mask external interrupt of this pin only for this port gpio_set(GPIO_PORT(DOOR_PIN), GPIO_PIN(DOOR_PIN)); // pull up to be able to detect button push (go low) @@ -621,7 +693,6 @@ void main(void) // start main loop bool action = false; // if an action has been performed don't go to sleep - button_flag = false; // reset button flag while (true) { // infinite loop iwdg_reset(); // kick the dog if (user_input_available) { // user input is available @@ -636,11 +707,31 @@ void main(void) if (0 == (rtc_get_counter_val() % RTC_TICKS_SECOND)) { // one second has passed led_toggle(); // toggle LED (good to indicate if main function is stuck) } - if (timer_door_closed && 0 == ((rtc_get_counter_val() - timer_door_closed) % RTC_TICKS_SECOND)) { // on second has passed since since door closed - uint16_t time_passed = (rtc_get_counter_val() - timer_door_closed) / RTC_TICKS_SECOND; // in seconds - led_tm1637_time(time_passed / 60, time_passed % 60); - if (time_passed && 0 == (time_passed % 5)) { - mp3_command(MP3_CMD_NEXT_SONG, 0); + if (0 == (rtc_get_counter_val() % (RTC_TICKS_SECOND * 11))) { // 11 seconds have passed, time to check BT audio state + if (0 == btaudio_status) { + puts("BT audio off, switching on\n"); + gpio_set(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // press button to switch on + sleep_ms(BTAUDIO_ON); // keep pressed + gpio_clear(GPIO_PORT(BTAUDIO_BUTTON_PIN), GPIO_PIN(BTAUDIO_BUTTON_PIN)); // release button + } else if (btaudio_status <= 2) { + puts("BT audio connected\n"); + } else if (btaudio_status <= 6) { + puts("BT audio disconnected\n"); + } else { + puts("BT audio searching\n"); + } + btaudio_status = 0; // reset counter + } + if (timer_door_closed && 0 == ((rtc_get_counter_val() - timer_door_closed) % (RTC_TICKS_SECOND / 4))) { // 1/4 second has passed since since door closed + const uint16_t time_passed = (rtc_get_counter_val() - timer_door_closed) / RTC_TICKS_SECOND; // how many seconds have passed since door has been closed + led_tm1637_time(time_passed / 60, time_passed % 60); // show time passed + if (!music_played && time_passed > 10) { // start playing song after the welcome message has been played + mp3_command(MP3_CMD_PLAY_FOLDER_FILE, (2 << 8) + (rtc_get_counter_val() % MUSIC_TRACKS) + 1); // play random music track + music_played = true; // remember we have played the music + } + if (!talk_played && time_passed >= 3 * 60) { // start playing talk track after 3 minutes + mp3_command(MP3_CMD_PLAY_FOLDER_FILE, (3 << 8) + (rtc_get_counter_val() % TALK_TRACKS) + 1); // play random talk track + talk_played = true; // remember we played the talk track } } } @@ -649,18 +740,20 @@ void main(void) const bool closed = (0 == gpio_get(GPIO_PORT(DOOR_PIN), GPIO_PIN(DOOR_PIN))); // get door lock state door_flag = false; // clear flag action = true; // action has been performed - if (closed && 0 == timer_door_closed) { + if (closed && 0 == timer_door_closed) { // door has been closed puts("door closed\n"); - timer_door_closed = rtc_get_counter_val(); - leds_sign(true); - led_tm1637_time(0, 0); - led_tm1637_on(); - mp3_command(MP3_CMD_PLAY, 0); - } else if (!closed && timer_door_closed) { - puts("door opned\n"); - timer_door_closed = 0; - leds_sign(false); - led_tm1637_off(); + timer_door_closed = rtc_get_counter_val(); // remember when the door has been closed + music_played = false; // the music has not been played yet + talk_played = false; // the talk has not been played + leds_sign(true); // show on the sign that the toilet is occupied + led_tm1637_time(0, 0); // start showing time on display + led_tm1637_on(); // ensure the display is on + mp3_command(MP3_CMD_PLAY_FOLDER_FILE, (1 << 8) + 1); // play welcome track + } else if (!closed && timer_door_closed) { // door has been opened + puts("door opened\n"); + timer_door_closed = 0; // remember door is now open + leds_sign(false); // show on sign the toilet is free + led_tm1637_off(); // stop showing time mp3_command(MP3_CMD_STOP_PLAY, 0); // stop playing } } @@ -697,8 +790,16 @@ void USART_ISR(MP3_UART)(void) } } +/** door has been opened/closed */ void GPIO_EXTI_ISR(DOOR_PIN)(void) { exti_reset_request(GPIO_EXTI(DOOR_PIN)); // reset interrupt/clear flag door_flag = true; // notify main loop } + +/** Bluetooth audio transmitter switched on LED */ +void GPIO_EXTI_ISR(BTAUDIO_STATUS_PIN)(void) +{ + exti_reset_request(GPIO_EXTI(BTAUDIO_STATUS_PIN)); // reset interrupt/clear flag + btaudio_status++; // count for main loop +}