When I move to the new apartment, I've met two cats showing around without their owners. I just wondering if there's a home for them since the weather is getting cold and there's no enough food prepared out there. So I thought several times to take care of them. But there are still some concerns that I can't make this decision. I don't have time to pet them. I usually have a crazy morning since I need to get early to catch the bus in order to go to work on time. There's not enough time to prepare food for both of us. All of these problems go around in my mind all the time.
That's where my idea of this project coming from. Since I'm not at home all the time, I just need to pressure the time that I can meet these cuties at. So I would like to choose wio terminal as the device that can give me a notice when the cat shows around my door. I classify them with other animals and background noise by their sound, like all kinds of "meow".
So let's jump into it right away!
The first thing that I was thinking of the device is that I need a cover page to show what kind of device is this. So cutie cat logo is like this:
For bewbies in Arduino code combining with hardware setup, I would like to introduce Codecraft as the first choice to get hands on the project. It's a code free platform providing blocks and logic flows to combine all of the factors that you need in the project. The cover page is also designed in this platform.
Codecraft is a programming software based on Scratch3.0 and supports both graphical and text programming languages. It’s a versatile software tool for STEM education. With Codecraft, children are able to design engaging stories, games and animations, and use various electronic kits which CH Maker Ed and Seeedstudio provides to create interactive smart applications. Furthermore, when you’re ready, you can always convert the code blocks to Arduino, Python, or JavaScript to learn more about the most popular languages.
Step Two: Data AcquisitionThe second part is to collect massive dataset that I would like to classify. In this project I choose cat sound, background noise like people talking and sound of object rubbing against each other and also other animals sound like dog and bird which could also shown around near my apartment. For each kind of sound, I collect 35 seconds. And finally the prediction accuracy is 95. 6%, which could be very nice to get trained in the next model step.
Step Three: Training Data & Model DeploymentI deploy the sound classifier Training model to train the data, trying to make the device learn which sounds represents what kind of objects. So, I set the condition to trigger buzz noise when the device make a judgement of cat. So I will be noticed and rushing out to pet it and giving some milk and food from these cuties. If those sound is not making from cat, the screen will only show which kind of sound it is. The content of screen is just showing for people to know the why buzz noise is making. And it will also help others to see what kind of sound is around.
For now I'll not miss those moments that I can pet these cuties. But what if ai'm not at home? That would be the next step for my project. Thinking about letting this device sending message to my phone if it detects there's cat showing around my door. And I would like to link this device to a pet feeder machine. So all of this procedure is automated. All I want is these cutie cat eat well and live warm.
Hopefully there's no more poor cat abandoned by their owners. They just need a sweet home.
#include"TFT_eSPI.h"
#include <project_165381_inferencing.h>
enum {ADC_BUF_LEN = 1600};
typedef struct {
uint16_t btctrl;
uint16_t btcnt;
uint32_t srcaddr;
uint32_t dstaddr;
uint32_t descaddr;
}dmacdescriptor;
typedef struct {
signed short *buffers[2];
unsigned char buf_select;
unsigned char buf_ready;
unsigned int buf_count;
unsigned int n_samples;
}inference_t;
volatile uint8_t recording = 0;
uint16_t adc_buf_0[ADC_BUF_LEN];
uint16_t adc_buf_1[ADC_BUF_LEN];
volatile dmacdescriptor wrb[DMAC_CH_NUM] __attribute__ ((aligned (16)));
dmacdescriptor descriptor_section[DMAC_CH_NUM] __attribute__ ((aligned (16)));
dmacdescriptor descriptor __attribute__ ((aligned (16)));
static inference_t inference;
class FilterBuHp1{
public:
FilterBuHp1(){
v[0] = 0.0;
}
private:
float v[2];
public:
float step(float x)
{
v[0] = v[1];
v[1] = (9.621952458291035404e-1f * x) + (0.92439049165820696974f * v[0]);
return (v[1] - v[0]);
}
};
FilterBuHp1 filter;
static void audio_rec_callback(uint16_t *buf, uint32_t buf_len) {
if (recording) {
for (uint32_t i = 0; i < buf_len; i++) {
inference.buffers[inference.buf_select][inference.buf_count++] = filter.step(((int16_t)buf[i] - 1024) * 16);
if (inference.buf_count >= inference.n_samples) {
inference.buf_select ^= 1;
inference.buf_count = 0;
inference.buf_ready = 1;
}
}
}
}
void DMAC_1_Handler() {
static uint8_t count = 0;
if (DMAC->Channel[1].CHINTFLAG.bit.SUSP) {
DMAC->Channel[1].CHCTRLB.reg = DMAC_CHCTRLB_CMD_RESUME;
DMAC->Channel[1].CHINTFLAG.bit.SUSP = 1;
if (count) {
audio_rec_callback(adc_buf_0, ADC_BUF_LEN);
}else {
audio_rec_callback(adc_buf_1, ADC_BUF_LEN);
}
count = (count + 1) % 2;
}
}
void config_dma_adc() {
DMAC->BASEADDR.reg = (uint32_t)descriptor_section;
DMAC->WRBADDR.reg = (uint32_t)wrb;
DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
DMAC->Channel[1].CHCTRLA.reg = DMAC_CHCTRLA_TRIGSRC(TC5_DMAC_ID_OVF) |
DMAC_CHCTRLA_TRIGACT_BURST;
descriptor.descaddr = (uint32_t)&descriptor_section[1];
descriptor.srcaddr = (uint32_t)&ADC1->RESULT.reg;
descriptor.dstaddr = (uint32_t)adc_buf_0 + sizeof(uint16_t) * ADC_BUF_LEN;
descriptor.btcnt = ADC_BUF_LEN;
descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD |
DMAC_BTCTRL_DSTINC |
DMAC_BTCTRL_VALID |
DMAC_BTCTRL_BLOCKACT_SUSPEND;
memcpy(&descriptor_section[0], &descriptor, sizeof(descriptor));
descriptor.descaddr = (uint32_t)&descriptor_section[0];
descriptor.srcaddr = (uint32_t)&ADC1->RESULT.reg;
descriptor.dstaddr = (uint32_t)adc_buf_1 + sizeof(uint16_t) * ADC_BUF_LEN;
descriptor.btcnt = ADC_BUF_LEN;
descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD |
DMAC_BTCTRL_DSTINC |
DMAC_BTCTRL_VALID |
DMAC_BTCTRL_BLOCKACT_SUSPEND;
memcpy(&descriptor_section[1], &descriptor, sizeof(descriptor));
NVIC_SetPriority(DMAC_1_IRQn, 0);
NVIC_EnableIRQ(DMAC_1_IRQn);
DMAC->Channel[1].CHINTENSET.reg = DMAC_CHINTENSET_SUSP;
ADC1->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_AIN12_Val;
while (ADC1->SYNCBUSY.bit.INPUTCTRL);
ADC1->SAMPCTRL.bit.SAMPLEN = 0x00;
while (ADC1->SYNCBUSY.bit.SAMPCTRL);
ADC1->CTRLA.reg = ADC_CTRLA_PRESCALER_DIV128;
ADC1->CTRLB.reg = ADC_CTRLB_RESSEL_12BIT |
ADC_CTRLB_FREERUN;
while (ADC1->SYNCBUSY.bit.CTRLB);
ADC1->CTRLA.bit.ENABLE = 1;
while (ADC1->SYNCBUSY.bit.ENABLE);
ADC1->SWTRIG.bit.START = 1;
while (ADC1->SYNCBUSY.bit.SWTRIG);
DMAC->Channel[1].CHCTRLA.bit.ENABLE = 1;
GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_CHEN |
GCLK_PCHCTRL_GEN_GCLK1;
TC5->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
TC5->COUNT16.CC[0].reg = 3000 - 1;
while (TC5->COUNT16.SYNCBUSY.bit.CC0);
TC5->COUNT16.CTRLA.bit.ENABLE = 1;
while (TC5->COUNT16.SYNCBUSY.bit.ENABLE);
}
static bool microphone_inference_record(void) {
bool ret = true;
while (inference.buf_ready == 0) {
delay(1);
}
inference.buf_ready = 0;
return ret;
}
static int microphone_audio_signal_get_data(size_t offset,
size_t length,
float *out_ptr) {
numpy::int16_to_float(&inference.buffers[inference.buf_select ^ 1][offset], out_ptr, length);
return 0;
}
TFT_eSPI tft;
ei_impulse_result_classification_t currentClassification[EI_CLASSIFIER_LABEL_COUNT];
const char* maxConfidenceLabel;
void runClassifier()
{
bool m = microphone_inference_record();
if (!m) {
return;
}
signal_t signal;
signal.total_length = EI_CLASSIFIER_SLICE_SIZE;
signal.get_data = µphone_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) {
return;
}
float maxValue = 0;
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_impulse_result_classification_t classification_t = result.classification[ix];
ei_printf(" %s: %.5f\n", classification_t.label, classification_t.value);
float value = classification_t.value;
if (value > maxValue) {
maxValue = value;
maxConfidenceLabel = classification_t.label;
}
currentClassification[ix] = classification_t;
}
}
void setup(){
tft.begin();
run_classifier_init();
inference.buffers[0] = (int16_t *)malloc(EI_CLASSIFIER_SLICE_SIZE * sizeof(int16_t));
if (inference.buffers[0] == NULL) {
return;
}
inference.buffers[1] = (int16_t *)malloc(EI_CLASSIFIER_SLICE_SIZE * sizeof(int16_t));
if (inference.buffers[1] == NULL) {
free(inference.buffers[0]);
return;
}
inference.buf_select = 0;
inference.buf_count = 0;
inference.n_samples = EI_CLASSIFIER_SLICE_SIZE;
inference.buf_ready = 0;
config_dma_adc();
recording = 1;
pinMode(WIO_BUZZER, OUTPUT);
tft.setRotation(3);
tft.fillScreen(0x933F);
tft.setTextSize(2);
tft.setTextColor(0xFFFF);
tft.drawTriangle(150,125,155,150,178,150,0xFD87);
tft.fillTriangle(150,125,155,150,178,150,0xFD87);
tft.drawTriangle(225,115,210,150,239,150,0xFD87);
tft.fillTriangle(225,115,210,150,239,150,0xFD87);
tft.drawCircle(200,175,50,0xFD67);
tft.fillCircle(200,175,50,0xFD67);
tft.drawCircle(200,180,4,0xFE38);
tft.fillCircle(200,180,4,0xFE38);
tft.drawCircle(220,172,8,0x9);
tft.fillCircle(220,172,8,0x9);
tft.drawCircle(180,172,8,0x9);
tft.fillCircle(180,172,8,0x9);
tft.drawCircle(200,205,3,0x0);
tft.drawLine(140,192,182,185,0xFFFF);
tft.drawLine(135,205,178,194,0xFFFF);
tft.drawLine(260,192,218,185,0xFFFF);
tft.drawLine(265,205,222,194,0xFFFF);
tft.drawString((String)"Welcome ! Sweet Home", 40, 45);
tft.drawRoundRect(30,35,260,40,5,0xFFFF);
delay(5000);
}
void loop(){
runClassifier();
if ((maxConfidenceLabel == "cat" > 0.8)) {
tone(WIO_BUZZER,262);
delay(1000);
noTone(WIO_BUZZER);
tft.fillScreen(0xFE6C);
tft.drawRoundRect(30,35,260,40,5,0xFFFF);
tft.fillRoundRect(30,35,260,40,5,0xFFFF);
tft.setTextSize(2);
tft.setTextColor(0x31A6);
tft.drawString((String)"Cutie CAT Coming !", 40, 45);
delay(1000);
} else {
if ((maxConfidenceLabel == "other_animal_sound" > 0.8)) {
tft.fillScreen(0x67FA);
tft.drawRoundRect(30,35,260,40,5,0xFFFF);
tft.fillRoundRect(30,35,260,40,5,0xFFFF);
tft.setTextSize(2);
tft.setTextColor(0x31A6);
tft.drawString((String)"Other Animals", 40, 45);
delay(1000);
} else {
tft.fillScreen(0x67FA);
tft.drawRoundRect(30,35,260,40,5,0xFFFF);
tft.fillRoundRect(30,35,260,40,5,0xFFFF);
tft.setTextSize(2);
tft.setTextColor(0x31A6);
tft.drawString((String)"Background Noise", 40, 45);
delay(1000);
}
}
}
Comments
Please log in or sign up to comment.