Voice activated microbit delay when keyword found?

Hello! I managed to google and find the resources i needed to figure out how to control a servo with the microbit and the voice activated tutorial you made. Great!

I changed the code when a keyword is heard, and when the microbit hears something else to:

/**

  • Invoked when we hear the keyword !
    */
    static void heard_keyword() {
    uBit.io.P0.setServoValue(180);
    uBit.sleep(2000);
    }

/**

  • Invoked when we hear something else
    */
    static void heard_other() {
    uBit.io.P0.setServoValue(0);
    }

So the servo moves 180 degrees when keyword is heard, then when no keyword is heard it turns back (I tried to put the command to rotate it back under “static void heard_keyword()” too, but the servo arm just goes back and forth then, like the command is looping every second or so…?)

This code above works great, however the timing seems off. After the keyword triggers the servo, it then waits about 4-5 seconds before triggering the uBit.io.P0.setServoValue(0); to send the servo back. I set the sleep value to two seconds, so there must be something else that is running in the background delaying this?

I was wondering why this is, and if there is something I can do to lessen the delay a bit?

Thanks! (btw, i am a design student, I dabble in everything, so this question might be a bit simple)
/Jonas

Hello @Instant_exit,

Could you share you entire code so I can have a deeper look at it?
I do not have a microbit so I won’t be able to reproduce it but I can have a quick look at your code to check if anything looks off.

Regards,

Louis

Of course! Thanks. I only modified the MicrophoneInferenceTest.cpp in your repository https://github.com/edgeimpulse/voice-activated-microbit , and changed the keyword by replacing the “edge-impulse-sdk”, “model-parameters” and “tflite-model” as Jan instructed. edit:not sure why i cant just paste the code in one piece, without extra scrolling? I hope you can read it!)

/*
The MIT License (MIT)

Copyright © 2020 EdgeImpulse Inc.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the “Software”),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include “MicroBit.h”
#include “ContinuousAudioStreamer.h”
#include “StreamNormalizer.h”
#include “Tests.h”
#include “edge-impulse-sdk/classifier/ei_run_classifier.h”
#include “edge-impulse-sdk/dsp/numpy.hpp”

#define INFERENCING_KEYWORD “data”

static NRF52ADCChannel *mic = NULL;
static ContinuousAudioStreamer *streamer = NULL;
static StreamNormalizer *processor = NULL;

static inference_t inference;

/**

  • Get raw audio signal data
    */
    static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr)
    {
    numpy::int8_to_float(&inference.buffers[inference.buf_select ^ 1][offset], out_ptr, length);
    return 0;
    }

/**

  • Invoked when we hear the keyword !
    */
    static void heard_keyword() {
    uBit.io.P0.setServoValue(180);
    uBit.sleep(2000);
    }

/**

  • Invoked when we hear something else
    */
    static void heard_other() {
    uBit.io.P0.setServoValue(0);
    }

void
mic_inference_test()
{
if (mic == NULL){
mic = uBit.adc.getChannel(uBit.io.microphone);
mic->setGain(7,0); // Uncomment for v1.47.2
//mic->setGain(7,1); // Uncomment for v1.46.2
}

// alloc inferencing buffers
inference.buffers[0] = (int8_t *)malloc(EI_CLASSIFIER_SLICE_SIZE * sizeof(int8_t));

if (inference.buffers[0] == NULL) {
    uBit.serial.printf("Failed to alloc buffer 1\n");
    return;
}

inference.buffers[1] = (int8_t *)malloc(EI_CLASSIFIER_SLICE_SIZE * sizeof(int8_t));

if (inference.buffers[0] == NULL) {
    uBit.serial.printf("Failed to alloc buffer 2\n");
    free(inference.buffers[0]);
    return;
}

uBit.serial.printf("Allocated buffers\n");

inference.buf_select = 0;
inference.buf_count = 0;
inference.n_samples = EI_CLASSIFIER_SLICE_SIZE;
inference.buf_ready = 0;

mic->output.setBlocking(true);

if (processor == NULL)
    processor = new StreamNormalizer(mic->output, 0.15f, true, DATASTREAM_FORMAT_8BIT_SIGNED);

if (streamer == NULL)
    streamer = new ContinuousAudioStreamer(processor->output, &inference);

uBit.io.runmic.setDigitalValue(1);
uBit.io.runmic.setHighDrive(true);

uBit.serial.printf("Allocated everything else\n");

// number of frames since we heard 'microbit'
uint8_t last_keywords = 0b0;

int heard_keyword_x_ago = 100;

while(1) {
    uBit.sleep(1);

    if (inference.buf_ready) {
        inference.buf_ready = 0;

        static int print_results = -(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW);

        signal_t signal;
        signal.total_length = EI_CLASSIFIER_SLICE_SIZE;
        signal.get_data = &microphone_audio_signal_get_data;
        ei_impulse_result_t result = { 0 };

        EI_IMPULSE_ERROR r = run_classifier_continuous(&signal, &result, false);
        if (r != EI_IMPULSE_OK) {
            ei_printf("ERR: Failed to run classifier (%d)\n", r);
            return;
        }

        bool heard_keyword_this_window = false;

        if (++print_results >= 0) {
            // print the predictions
            ei_printf("Predictions (DSP: %d ms., Classification: %d ms.): \n",
                result.timing.dsp, result.timing.classification);
            for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
                ei_printf("    %s: ", result.classification[ix].label);
                ei_printf_float(result.classification[ix].value);
                ei_printf("\n");

                if (strcmp(result.classification[ix].label, INFERENCING_KEYWORD) == 0 && result.classification[ix].value > 0.7) {
                    heard_keyword_this_window = true;
                }
            }

            last_keywords = last_keywords << 1 & 0x1f;

            if (heard_keyword_this_window) {
                last_keywords += 1;
            }

            uint8_t keyword_count = 0;
            for (size_t ix = 0; ix < 5; ix++) {
                keyword_count += (last_keywords >> ix) & 0x1;
            }

            if (heard_keyword_this_window) {
                ei_printf("\nHeard keyword: %s (%d times, needs 5)\n", INFERENCING_KEYWORD, keyword_count);
            }

            if (keyword_count >= 1) {
                ei_printf("\n\n\nDefinitely heard keyword: \u001b[32m%s\u001b[0m\n\n\n", INFERENCING_KEYWORD);
                last_keywords = 0;
                heard_keyword_x_ago = 0;
            }
            else {
                heard_keyword_x_ago++;
            }

            if (heard_keyword_x_ago <= 4) {
                heard_keyword();
            }
            else {
                heard_other();
            }
        }
    }
}

}

/**

  • Microbit implementations for Edge Impulse target-specific functions
    */
    EI_IMPULSE_ERROR ei_sleep(int32_t time_ms) {
    uBit.sleep(time_ms);
    return EI_IMPULSE_OK;
    }

void ei_printf(const char *format, …) {
char print_buf[1024] = { 0 };

va_list args;
va_start(args, format);
int r = vsnprintf(print_buf, sizeof(print_buf), format, args);
va_end(args);

uBit.serial.printf("%s", print_buf);

}

Hello @Instant_exit,

Here I think your issue comes from your program more than Edge Impulse inferencing :slight_smile:

If I focus on the while(1) loop, especially this part there is a lot of room for improvement. I think you could try to simplify how you want to call your heard_keyword():

if (++print_results >= 0) {
            // print the predictions
            ei_printf("Predictions (DSP: %d ms., Classification: %d ms.): \n",
                result.timing.dsp, result.timing.classification);
            for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
                ei_printf("    %s: ", result.classification[ix].label);
                ei_printf_float(result.classification[ix].value);
                ei_printf("\n");

                if (strcmp(result.classification[ix].label, INFERENCING_KEYWORD) == 0 && result.classification[ix].value > 0.7) {
                    heard_keyword_this_window = true;
                }
            }

            last_keywords = last_keywords << 1 & 0x1f;

            if (heard_keyword_this_window) {
                last_keywords += 1;
            }

            uint8_t keyword_count = 0;
            for (size_t ix = 0; ix < 5; ix++) {
                keyword_count += (last_keywords >> ix) & 0x1;
            }

            if (heard_keyword_this_window) {
                ei_printf("\nHeard keyword: %s (%d times, needs 5)\n", INFERENCING_KEYWORD, keyword_count);
            }

            if (keyword_count >= 1) {
                ei_printf("\n\n\nDefinitely heard keyword: \u001b[32m%s\u001b[0m\n\n\n", INFERENCING_KEYWORD);
                last_keywords = 0;
                heard_keyword_x_ago = 0;
            }
            else {
                heard_keyword_x_ago++;
            }

            if (heard_keyword_x_ago <= 4) {
                heard_keyword();
            }
            else {
                heard_other();
            }
        }

Good luck with your project!

Regards,

Louis

Thanks alot! I think @janjongboom might have written it for the tutorial? I dont really know where to start reading up on this, in the time frame of the project at least…I certainly feel a bit above my knowledge level at the moment so to speak (Though I want to learn this properly someday). I guess one of the downsides of being interested in many different things at the same time. :nerd_face:

Any pointers are of course greatly appreciated, and I am very much glad you pointed out the likely culprit. Thanks!

I just want it to react when keyword is found, send the servo command, wait, then return. I am trying now with 360 degrees continous servos and a linear actuator conversion i 3d printed, will try and report back how this works. Cheers,

/J