Timothy LovettKerin Lovett
Published © CC BY-NC-SA

Bird Detection with TinyML and a Blues Notecard

I built a project to identify birds at a bird feeder using Machine Learning (TinyML) and transmit data to the cloud with a Blues Notecard

IntermediateFull instructions providedOver 3 days1,503
Bird Detection with TinyML and a Blues Notecard

Things used in this project

Hardware components

Blues Notecard (Wi-Fi)
Blues Notecard (Wi-Fi)
×1
Blues Notecarrier A
Blues Notecarrier A
×1
Grove Vision AI Module V2
Seeed Studio Grove Vision AI Module V2
×1
Seeed Studio XIAO ESP32C3
Seeed Studio XIAO ESP32C3
×1
Waterproof Enclosure Box
×1
Seeed Studio Small Solar Panel
×1
Adafruit Lithium Ion Cylindrical Battery
×1
Level-Up Board
https://www.cranberrygrape.com/mini-projects/level-up-board/
×1
Raspberry Pi Camera Module 3
Raspberry Pi Camera Module 3
×1
Tilt Sensor
I used a sw18015p but there are various models out there that are easier to source (I just had this on hand).
×1
Cable Gland, PG 7
Cable Gland, PG 7
×1
Bird feeder
×1

Software apps and online services

TensorFlow
TensorFlow

Story

Read more

Custom parts and enclosures

bird_classifier 96x96 38 outputs

38 outputs vela file

Schematics

Fritzing

Somewhat represents the circuit diagram but keep in mind it is using different parts

Fritzing

Code

Birds.ino

C/C++
#include <Seeed_Arduino_SSCMA.h>
#include <Notecard.h>

SSCMA AI;
Notecard notecard;

#define accuracyThreshold 95
#define tiltPin D7
#define productUID "PRODUCT_UID_HERE"

String birdNames[] = {
    "AMERICAN AVOCET",
    "AMERICAN BITTERN",
    "AMERICAN COOT",
    "AMERICAN FLAMINGO",
    "AMERICAN GOLDFINCH",
    "AMERICAN KESTREL",
    "AMERICAN PIPIT",
    "AMERICAN REDSTART",
    "AMERICAN ROBIN",
    "AMERICAN WIGEON",
    "BALD EAGLE",
    "BELTED KINGFISHER",
    "BLACK SKIMMER",
    "BOBOLINK",
    "BROWN HEADED COWBIRD",
    "BROWN THRASHER",
    "CEDAR WAXWING",
    "CHIPPING SPARROW",
    "COMMON GRACKLE",
    "CROW",
    "DOUBLE BRESTED CORMARANT",
    "DUNLIN",
    "EASTERN BLUEBIRD",
    "EASTERN MEADOWLARK",
    "EASTERN TOWEE",
    "MOURNING DOVE",
    "NORTHERN CARDINAL",
    "NORTHERN MOCKINGBIRD",
    "PAINTED BUNTING",
    "RED HEADED WOODPECKER",
    "RED SHOULDERED HAWK",
    "RUBY CROWNED KINGLET",
    "RUBY THROATED HUMMINGBIRD",
    "SCARLET CROWNED FRUIT DOVE",
    "SCARLET TANAGER",
    "SNOWY EGRET",
    "VICTORIA CROWNED PIGEON",
    "WOOD DUCK"
};

void setup()
{
    Serial.begin(9600);

    // Set tiltPin as an input pin
    pinMode(tiltPin, INPUT); 

    // Initialize Seeed Grove Vision AI V2 via SSCMA
    AI.begin();

    // Initialize notecard
    notecard.begin();

    J *req = notecard.newRequest("hub.set");
    if (req) {
        JAddStringToObject(req, "product", productUID);
        JAddStringToObject(req, "mode", "periodic");
        JAddNumberToObject(req, "outbound", 5);
        JAddNumberToObject(req, "inbound", 60);
        if (!notecard.sendRequest(req)) {
            notecard.logDebug("FATAL: Failed to configure Notecard!\n");
            while(1);
        }
    }
}

void loop()
{
    // If the tilt pin reads 0 (in this case indicating movement for this sensor)
    if (digitalRead(tiltPin) == 0) {
        // Invoke SSCMA asking inferencing and returning the image (third parameter true)
        if (!AI.invoke(1, false, true))
        {
            // Debug logic
            Serial.println("invoke success");
            Serial.print("perf: prepocess=");
            Serial.print(AI.perf().prepocess);
            Serial.print(", inference=");
            Serial.print(AI.perf().inference);
            Serial.print(", postpocess=");
            Serial.println(AI.perf().postprocess);

            for (int i = 0; i < AI.classes().size(); i++)
            {
                if (AI.classes()[i].score >= accuracyThreshold) {
                    Serial.print("Class[");
                    Serial.print(i);
                    Serial.print("] target=");
                    Serial.print(AI.classes()[i].target);
                    Serial.print(", score=");
                    Serial.println(AI.classes()[i].score);

                    Serial.print("Image:");
                    Serial.println();

                    J *req = notecard.newRequest("note.add");
                    if (req != NULL)
                    {
                        JAddStringToObject(req, "file", "events.qo");
                        J *body = JAddObjectToObject(req, "body");
                        if (body)
                        {
                            JAddStringToObject(body, "bird", birdNames[AI.classes()[i].target].c_str());
                        }
                        JAddStringToObject(req, "payload", AI.last_image().c_str());
                        notecard.sendRequest(req);
                    }
                }
            }
        }
    }
}

Blues bird Rust server

Blues bird Arduino logic and models

Credits

Timothy Lovett

Timothy Lovett

16 projects • 16 followers
Maker. I spent over a decade working on backend systems in various languages.
Kerin Lovett

Kerin Lovett

2 projects • 3 followers

Comments