Thanks Aurelian, here you go. This is in a main_functions.cpp file. main.cpp is just a very simple executable that calls setup() once and does a while loop on loop() to maintain Arduino compatibility.
// Globals, used for compatibility with Arduino-style sketches.
namespace {
tflite::ErrorReporter * error_reporter = nullptr;
int8_t image[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = {0};
static bool debug_nn = true;
} // namespace
#ifndef DO_NOT_OUTPUT_TO_UART
// RX interrupt handler
void on_uart_rx() {
char cameraCommand = 0;
while (uart_is_readable(UART_ID)) {
cameraCommand = uart_getc(UART_ID);
// Can we send it back?
if (uart_is_writable(UART_ID)) {
uart_putc(UART_ID, cameraCommand);
}
}
}
void setup_uart() {
// Set up our UART with the required speed.
uint baud = uart_init(UART_ID, BAUD_RATE);
// Set the TX and RX pins by using the function select on the GPIO
// Set datasheet for more information on function select
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
// Set our data format
uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);
// Turn off FIFO's - we want to do this character by character
uart_set_fifo_enabled(UART_ID, false);
// Set up a RX interrupt
// We need to set up the handler first
// Select correct interrupt for the UART we are using
int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ;
// And set up and enable the interrupt handlers
irq_set_exclusive_handler(UART_IRQ, on_uart_rx);
irq_set_enabled(UART_IRQ, true);
// Now enable the UART to send interrupts - RX only
uart_set_irq_enables(UART_ID, true, false);
}
#else
void setup_uart() {}
#endif
void r565_to_rgb(uint16_t color, uint8_t *r, uint8_t *g, uint8_t *b) {
*r = (color & 0xF800) >> 8;
*g = (color & 0x07E0) >> 3;
*b = (color & 0x1F) << 3;
}
int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
size_t bytes_left = length;
size_t out_ptr_ix = 0;
// read byte for byte
while (bytes_left != 0) {
// grab the value and convert to r/g/b
uint16_t pixel = (uint16_t)image[out_ptr_ix];
uint8_t r, g, b;
r565_to_rgb(pixel, &r, &g, &b);
// then convert to out_ptr format
float pixel_f = (r << 16) + (g << 8) + b;
out_ptr[out_ptr_ix] = pixel_f;
// and go to the next pixel
out_ptr_ix++;
offset++;
bytes_left--;
}
// and done!
return 0;
}
// The name of this function is important for Arduino compatibility.
void setup() {
static tflite::MicroErrorReporter micro_error_reporter;
error_reporter = µ_error_reporter;
setup_uart();
stdio_usb_init();
TfLiteStatus setup_status = ScreenInit(error_reporter);
if (setup_status != kTfLiteOk) {
TF_LITE_REPORT_ERROR(error_reporter, "Screen Set up failed\n");
ei_printf("Failed to init screen\n");
}
}
int loop_count = 0;
// The name of this function is important for Arduino compatibility.
void loop() {
// Get image from camera.
int kNumCols = 96;
int kNumRows = 96;
int kNumChannels = 1;
if (kTfLiteOk
!= GetImage(error_reporter, kNumCols, kNumRows, kNumChannels, image)) {
TF_LITE_REPORT_ERROR(error_reporter, "Image capture failed.");
ei_printf("Failed to get image\n");
}
// Run the model on this input and make sure it succeeds.
// put Edge Impulse model code here
// Turn the raw buffer in a signal which we can the classify
// this is where we'll write all the output
float image_output[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = {0};
signal_t signal;
signal.total_length = EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE;
signal.get_data = &raw_feature_get_data;
// read through the signal buffered, like the classifier lib also does
for (size_t ix = 0; ix < signal.total_length; ix += 1024) {
size_t bytes_to_read = 1024;
if (ix + bytes_to_read > signal.total_length) {
bytes_to_read = signal.total_length - ix;
}
int r = signal.get_data(ix, bytes_to_read, image_output + ix);
ei_printf("Call to get_data (%d)\n", r);
}
// Run the classifier
ei_impulse_result_t result = { 0 };
ei_printf("Before run_classifier");
//this is where the program hangs on the 2nd iteration
int err = run_classifier(&signal, &result, debug_nn);
ei_printf("Call to run_classifier (%d)\n", err);
if (err != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)\n", err);
return;
}
// print the predictions
ei_printf("Predictions ");
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_printf(" %s: %.5f\n", result.classification[ix].label, result.classification[ix].value);
}
// Process the inference results.
int8_t no_poison_ivy_score = floor(result.classification[0].value * 100);
int8_t poison_ivy_score = floor(result.classification[1].value * 100);
#if SCREEN
char array[10];
sprintf(array, "%d%%", poison_ivy_score);
ST7735_FillRectangle(10, 120, ST7735_WIDTH, 60, ST7735_BLACK);
if(poison_ivy_score > 60)
{
ST7735_WriteString(10, 120, array, Font_14x26, ST7735_RED, ST7735_BLACK);
}
else
{
ST7735_WriteString(10, 120, array, Font_14x26, ST7735_GREEN, ST7735_BLACK);
}
#endif
TF_LITE_REPORT_ERROR(error_reporter, "**********");
}
I’ll try out the Pico4ML board as well. I hadn’t thought of that.
thanks,
Justin