Rok ŠtefanFilip OšlajLeon Rojc
Published

Sheep distress detection

Detection of sheep in distress for early attack detection.

IntermediateWork in progress20 hours65
Sheep distress detection

Things used in this project

Hardware components

Nature Guard
×1
LoRaWan Module
×1
USB Power/Battery
×1

Software apps and online services

Android Studio
Android Studio
Edge Impulse Studio
Edge Impulse Studio
Arduino IDE
Arduino IDE

Story

Read more

Code

SheepDistressDetection.ino

Arduino
/* Edge Impulse ingestion SDK
 * Copyright (c) 2022 EdgeImpulse Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
#define EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW 1
#define EI_CLASSIFIER_FREQUENCY 16000

#include <Zaznavanje-Ovc_inferencing.h>
#include <Arduino.h>
#include <Adafruit_TinyUSB.h>
#include <PDM.h>
#include <stdlib.h>
#include <string.h>

// LoRaWan credentials 
const char* APPEUI = "1212121212121212";
const char* DEVEUI = "70B3D57ED0070838";
const char* APPKEY = "19B1B4AECA76F0ED657FC47AA6C1F386";

// last 20 detected sounds
#define SIZE 20
static bool soundHistory[SIZE] = {0};
static int  historyIndex = 0;
static unsigned long lastBatteryAlert = 0;

// Inference buffers & state
typedef struct {
    signed short *buffers[2];
    unsigned char buf_select;
    unsigned char buf_ready;
    unsigned int  buf_count;
    unsigned int  n_samples;
} inference_t;
static inference_t inference;
static bool record_ready = false;
static signed short *sampleBuffer;

// Forward prototypes
static void pdm_data_ready_inference_callback(void);
static bool microphone_inference_start(uint32_t n_samples);
static bool microphone_inference_record(void);
static int  microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr);
static void microphone_inference_end(void);


// LoraWan messages
void sendDistressAlert() {
    String message = "AT+MSG=\"{\\\"type\\\":\\\"alarm\\\",\\\"data\\\":{\\\"status\\\":\\\"distress\\\"}}\"";
    Serial1.println(message);
    Serial.println("DISTRESS ALERT SENT!.");
}

void sendBatteryLevel() {
    int batteryLevel = 75;  
    String message = "AT+MSG=\"{\\\"type\\\":\\\"battery\\\",\\\"data\\\":{\\\"level\\\":" + String(batteryLevel) + "}}\"";
    Serial1.println(message);
    Serial.println("Sent battery level.");
}

void sendLocation() {
    String latitude = "46.04476";
    String longitude = "14.48926";
    String message = "AT+MSG=\"{\\\"type\\\":\\\"location\\\",\\\"data\\\":{\\\"latitude\\\":" + latitude + ",\\\"longitude\\\":" + longitude + "}}\"";
    Serial1.println(message);
    Serial.println("Sent location.");
}


//------------------------------------------------------------------------------
void setup() {
    // USB serial (testing)
    Serial.begin(115200);
    while (!Serial);
    Serial.println("EdgeImpulse on arduino through LoRaWan -> sheep distress");

    // Power on LoRaWan module
    pinMode(5, OUTPUT);
    digitalWrite(5, HIGH);
    Serial1.begin(9600);
    delay(100);

    // Set up LoRaWan connection
    Serial1.write("AT+ID=AppEui,\"1212121212121212\"\r\n");
    delay(1000);
    Serial1.write("AT+ID=DevEui,\"70B3D57ED0070838\"\r\n");
    delay(1000);
    Serial1.write("AT+KEY=AppKey,\"19B1B4AECA76F0ED657FC47AA6C1F386\"\r\n");
    delay(1000);
    Serial1.write("AT+MODE=LWOTAA\r\n");
    delay(1000);
    Serial1.write("AT+JOIN\r\n");
    delay(5000);
    Serial.println("Connection succesfull!");

    //EdgeImpulse initialization
    run_classifier_init();
    if (!microphone_inference_start(EI_CLASSIFIER_SLICE_SIZE)) {
        ei_printf("ERR: Could not allocate audio buffer\n");
        while (1);
    }
}

//------------------------------------------------------------------------------
void loop() {
    if (!microphone_inference_record()) {
        ei_printf("ERR: Failed to record audio\n");
        return;
    }

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

    if (run_classifier_continuous(&signal, &result, false) != EI_IMPULSE_OK) {
        ei_printf("ERR: Classifier error\n");
        return;
    }

    // Print out each detected class and its confidence
    for (size_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
        Serial.printf("%s: %.5f\n",
                      result.classification[i].label,
                      result.classification[i].value);
    }

    //  Distress probability
    float distressProbability = 0;
    for (size_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
        if (!strcmp(result.classification[i].label, "iven Klic")) {
            distressProbability = result.classification[i].value;
            break;
        }
    }

    // update history
    soundHistory[historyIndex] = distressProbability > 0.5f;
    historyIndex = (historyIndex + 1) % SIZE;

    // Count positives
    int count = 0;
    for (int i = 0; i < SIZE; i++) {
        if (soundHistory[i]) count++;
    }

    // If 5/20 are positive -> send alert!
    if (count >= 5) {
        sendDistressAlert();
        // Reset the history array
        for (int i = 0; i < SIZE; i++) {
            soundHistory[i] = false;
        }
    }

    // battery and location update every 3min
    if (millis() - lastBatteryAlert >= 180000) {
        sendBatteryLevel();
        sendLocation();
        lastBatteryAlert = millis();
    }
}

//------------------------------------------------------------------------------
// PDM Buffer Full Callback
static void pdm_data_ready_inference_callback(void) {
    int bytesAvailable = PDM.available();
    int bytesRead      = PDM.read((char *)&sampleBuffer[0], bytesAvailable);

    if (record_ready) {
        for (int i = 0; i < (bytesRead >> 1); i++) {
            inference.buffers[inference.buf_select][inference.buf_count++] = sampleBuffer[i];
            if (inference.buf_count >= inference.n_samples) {
                inference.buf_select ^= 1;
                inference.buf_count  = 0;
                inference.buf_ready  = 1;
            }
        }
    }
}

//------------------------------------------------------------------------------
// Allocate buffers & start PDM
static bool microphone_inference_start(uint32_t n_samples) {
    inference.buffers[0] = (signed short*)malloc(n_samples * sizeof(signed short));
    if (!inference.buffers[0]) return false;
    inference.buffers[1] = (signed short*)malloc(n_samples * sizeof(signed short));
    if (!inference.buffers[1]) { free(inference.buffers[0]); return false; }
    sampleBuffer = (signed short*)malloc((n_samples >> 1) * sizeof(signed short));
    if (!sampleBuffer) { free(inference.buffers[0]); free(inference.buffers[1]); return false; }

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

    PDM.onReceive(&pdm_data_ready_inference_callback);
    PDM.setBufferSize((n_samples >> 1) * sizeof(int16_t));
    if (!PDM.begin(1, EI_CLASSIFIER_FREQUENCY)) {
        ei_printf("Failed to start PDM!\n");
    }
    PDM.setGain(127);
    record_ready = true;
    return true;
}

//------------------------------------------------------------------------------
// Wait for next filled buffer
static bool microphone_inference_record(void) {
    bool ret = true;
    if (inference.buf_ready == 1) {
        ei_printf("Error sample buffer overrun. Decrease slices per window\n");
        ret = false;
    }
    while (inference.buf_ready == 0) {
        delay(1);
    }
    inference.buf_ready = 0;
    return ret;
}

//------------------------------------------------------------------------------
// Copy raw int16  float
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;
}

//------------------------------------------------------------------------------
// Tear down PDM & free buffers
static void microphone_inference_end(void) {
    PDM.end();
    free(inference.buffers[0]);
    free(inference.buffers[1]);
    free(sampleBuffer);
}

ei-zaznavanje-ovc-arduino-1.0.1.zip

C/C++
No preview (download only).

Android App

Credits

Rok Štefan
1 project • 0 followers
Filip Ošlaj
1 project • 0 followers
Leon Rojc
1 project • 0 followers

Comments