I apologize for the delayed response, and thank you for your reply.
To answer your questions:
- Do you get anything else in the result object?
No, I don’t get anything else. When the error occurs, result->_raw_outputs is completely empty (all pointers inside are 0x00000000). I have attached a screenshot showing the contents of the result object right after executing run_classifier(). Please refer to the attached image for more details.
- Could you please share more code of your application?
I am basically just calling the standard run_classifier() function.I am sharing the entire code of Main.cpp.
Main.cpp (Main application code including the main() function):
#include <xc.h>
#include <cstdlib>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
extern "C"{
#include "definitions.h"
}
#include "Main.h"
#include "edge-impulse-sdk/classifier/ei_run_classifier.h"
extern void serial_printf(const char *format, ...);
using namespace std;
static float eiAccBuffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = {0}; // Buffer for accelerometer data (2000ms/4) used by EI (Edge Impulse AI)
template <typename T>
T my_map(T x, T in_min, T in_max, T out_min, T out_max);
// ---------------------------------------------------------
// Definition of the ring buffer and slice buffer
// ---------------------------------------------------------
#define RING_BUFFER_SIZE 200
volatile float ringBuffer[RING_BUFFER_SIZE] = {0};
volatile int writeIdx = 0;
int readIdx = 0;
uint32_t AccelReadSub(ADC_INPUT_POSITIVE pin);
// ---------------------------------------------------------
// TMR1 interrupt callback (Automatically executed exactly every 20ms)
// ---------------------------------------------------------
void Timer1_Callback(uint32_t status, uintptr_t context) {
// Acquire 3-axis data and map it to a range of 0 to 255
float x = (float)my_map<int>(AccelReadSub(PIN_ACC_X), 0, 1023, 0, 255);
float y = (float)my_map<int>(AccelReadSub(PIN_ACC_Y), 0, 1023, 0, 255);
float z = (float)my_map<int>(AccelReadSub(PIN_ACC_Z), 0, 1023, 0, 255);
// Write to the ring buffer and advance the index
ringBuffer[writeIdx] = x; writeIdx = (writeIdx + 1) % RING_BUFFER_SIZE;
ringBuffer[writeIdx] = y; writeIdx = (writeIdx + 1) % RING_BUFFER_SIZE;
ringBuffer[writeIdx] = z; writeIdx = (writeIdx + 1) % RING_BUFFER_SIZE;
}
int main(int argc, char** argv) {
// setup
SYS_Initialize ( NULL ); // Initialize PIC microcontroller related processes
// Register the TMR1 callback and start the timer
TMR1_CallbackRegister(Timer1_Callback, (uintptr_t)NULL);
TMR1_Start();
signal_t signal; // For passing raw data
static ei_impulse_result_t result; // For storing inference results
bool debugFlg = true; // Debug output flag
// Set the callback function and the required amount of data
signal.total_length = EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE;
signal.get_data = &get_signal_data;
int totalFloats = EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE;
int sliceFloats = EI_CLASSIFIER_SLICE_SIZE * EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME;
int shiftFloats = totalFloats - sliceFloats;
ADC_Enable(); // Start ADC (Analog-to-Digital) conversion
serial_printf("////////////// Start ////////////////\r\n");
// loop
while(1){
// Check the amount of new data available in the ring buffer
int availableFloats = (writeIdx >= readIdx)
? (writeIdx - readIdx)
: (RING_BUFFER_SIZE - readIdx + writeIdx);
// If new data for 1 slice (75 items) has accumulated, update the buffer and run inference
if (availableFloats >= sliceFloats) {
// [Step 1] Shift the old data in the main buffer to the left (forgetting process)
for (int i = 0; i < shiftFloats; i++) {
if(sliceFloats + i >= EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE){
serial_printf("sliceFloats + i %d\r\n", sliceFloats+i);
}
eiAccBuffer[i] = eiAccBuffer[i + sliceFloats];
}
// [Step 2] Write new data from the ring buffer into the freed space on the right
for(int i = 0; i < sliceFloats; i++) {
if(shiftFloats + i >= EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE){
serial_printf("shiftFloats + i %d , readIdx %d\r\n", shiftFloats+i, readIdx);
}
if(readIdx >= RING_BUFFER_SIZE){
serial_printf("shiftFloats + i %d , readIdx %d\r\n", shiftFloats+i, readIdx);
}
eiAccBuffer[shiftFloats + i] = ringBuffer[readIdx];
readIdx = (readIdx + 1) % RING_BUFFER_SIZE;
}
// [Step 3] Execute the standard run_classifier!
memset(&result, 0x00, sizeof(&result));
EI_IMPULSE_ERROR res = run_classifier(&signal, &result, debugFlg);
if (res == EI_IMPULSE_OK) {
if(result.anomaly > 0.3){
serial_printf("anomaly %f\r\n", result.anomaly);
}else{
serial_printf("attack :%f\r\n", result.classification[0].value);
serial_printf("circle :%f\r\n", result.classification[1].value);
serial_printf("poke :%f\r\n", result.classification[2].value);
serial_printf("\r\n");
}
} else {
serial_printf("fail %d\r\n", res);
}
}
}
return 0;
}
/////////////////////////////////////////////////////////////
// Actual process for reading the accelerometer
/////////////////////////////////////////////////////////////
uint32_t AccelReadSub(ADC_INPUT_POSITIVE pin) {
ADC_InputSelect(ADC_MUX_A, pin, ADC_INPUT_NEGATIVE_VREFL);
// 1. Start sampling (charge capacitor for conversion)
ADC_SamplingStart();
// 2. Wait briefly for sampling
CORETIMER_DelayUs(10);
// Safe empty loop wait to prevent conflict with the Core Timer
for(volatile int i = 0; i < 150; i++);
// 3. Start conversion (end sampling)
ADC_ConversionStart();
// 4. Wait for the result
uint32_t timeout = 100000;
while(!ADC_ResultIsReady() && timeout > 0) {
timeout--;
}
// If a timeout occurs, output a warning and return 0
if (timeout == 0) {
serial_printf("ADC Error!\r\n");
return 0;
}
return ADC_ResultGet(ADC_RESULT_BUFFER_0);
}
/////////////////////////////////////////////////////////////
// Buffer data acquisition function for EI
/////////////////////////////////////////////////////////////
int get_signal_data(size_t offset, size_t length, float *out_ptr) {
// The SDK requests data from the slice buffer
for (size_t i = 0; i < length; i++) {
out_ptr[i] = eiAccBuffer[offset + i];
}
return 0;
}
template <typename T>
T my_map(T x, T in_min, T in_max, T out_min, T out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
ei_pic32_porting.cpp (Porting functions such as memory allocation required by the Edge Impulse SDK):
#include <xc.h>
extern "C"{
#include "definitions.h"
}
#include "edge-impulse-sdk/porting/ei_classifier_porting.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
char text_buffer[256];
void serial_printf(const char *format, ...) {
va_list args;
// 可変長引数を処理して buffer に文字列を作成
va_start(args, format);
vsnprintf(text_buffer, sizeof(text_buffer), format, args);
va_end(args);
// 確実に動くことが分かっているUART関数で送信
UART1_Write((void*)text_buffer, strlen(text_buffer));
// while(UART1_WriteIsBusy());
}
__attribute__((weak)) EI_IMPULSE_ERROR ei_run_impulse_check_canceled() {
return EI_IMPULSE_OK;
}
__attribute__((weak)) void ei_printf(const char *format, ...) {
serial_printf(format);
}
__attribute__((weak)) void ei_printf_float(float f) {
ei_printf("%f", f);
}
__attribute__((weak)) void *ei_malloc(size_t size) {
void *ret = malloc(size);
if(ret == NULL){
serial_printf("malloc err \r\n");
}
return ret;
}
__attribute__((weak)) void *ei_calloc(size_t nitems, size_t size) {
void *ret = calloc(nitems, size);
if(ret == NULL){
serial_printf("calloc err\r\n");
}
return ret;
}
__attribute__((weak)) void ei_free(void *ptr) {
free(ptr);
}
#if defined(__cplusplus) && EI_C_LINKAGE == 1
extern "C"
#endif
__attribute__((weak)) void DebugLog(const char* s) {
ei_printf("%s", s);
}
__attribute__((weak)) EI_IMPULSE_ERROR ei_sleep(int32_t time_ms) {
if (time_ms > 0) {
// Harmony標準のコアタイマー遅延関数を使用
CORETIMER_DelayMs((uint32_t)time_ms);
}
return EI_IMPULSE_OK;
}
uint64_t ei_read_timer_ms() {
// コアタイマーのカウント値をミリ秒に変換して返す
// 32ビットタイマーのオーバーフローを考慮し、内部で適切に処理された値を使用
return (uint64_t)(CORETIMER_CounterGet() / (CORETIMER_FrequencyGet() / 1000));
}
uint64_t ei_read_timer_us() {
// 同様にマイクロ秒(us)を返す関数
return (uint64_t)(CORETIMER_CounterGet() / (CORETIMER_FrequencyGet() / 1000000));
}
extern "C" void _mon_putc(char c) {
// UART1が空くまで待ってから1文字送信
UART1_Write(&c, 1);
// while(UART1_WriteIsBusy());
}