Madhav
Published © MIT

Fauna-Flash

Fauna-Flash enhances road safety for both drivers and wildlife by providing an immediate, automated warning system for animal presence.

AdvancedWork in progress14 days365

Things used in this project

Hardware components

Grove Vision AI Module V2
Seeed Studio Grove Vision AI Module V2
×1
Seeed Studio XIAO ESP32S3
Seeed Studio XIAO ESP32S3
×1
RYLR998 transciever module REYAX TECHNOLOGY
×2
Arduino Nano R3
Arduino Nano R3
×1
Seeed Studio XIAO nRF52840 Sense (XIAO BLE Sense)
Seeed Studio XIAO nRF52840 Sense (XIAO BLE Sense)
×1

Hand tools and fabrication machines

Crimp Tool, Heavy-Duty
Crimp Tool, Heavy-Duty
Soldering iron (generic)
Soldering iron (generic)
Drill / Driver, Cordless
Drill / Driver, Cordless
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Post Cap

Sketchfab still processing.

Post Base

Sketchfab still processing.

Schematics

RECIEVER UNIT

TRANSMITTER UNIT

POWERING OF COMPONENTS USING SOLAR AND LITHIUM ION BATTERY

Code

Detection (Stack)

C/C++
This code is used for detection and sending of signal
#include <Seeed_Arduino_SSCMA.h>

SSCMA AI;

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

    pinMode(D0, OUTPUT); // Elephant pin
    pinMode(D1, OUTPUT); // Buffalo pin
    pinMode(D2, OUTPUT); // Target 2 pin

    digitalWrite(D0, LOW);
    digitalWrite(D1, HIGH);
    digitalWrite(D2, LOW);
}

void loop()
{
    if (!AI.invoke())
    {
        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);

        bool elephantDetected = false;
        bool buffaloDetected = false;
        bool target2Detected = false;

        for (int i = 0; i < AI.classes().size(); i++)
        {
            int target = AI.classes()[i].target;
            int score = AI.classes()[i].score;

            Serial.print("Class[");
            Serial.print(i);
            Serial.print("] target=");
            Serial.print(target);
            Serial.print(", score=");
            Serial.println(score);

            // Check for Elephant (target 0, score > 75)
            if (target == 0 && score > 75)
            {
                Serial.println("Elephant detected!");
                elephantDetected = true;
                digitalWrite(D0, HIGH);
                delay(1000); // Optional delay for elephant detection
            }

            // Check for Buffalo (target 1, score > 75)
            if (target == 1 && score > 75)
            {
                Serial.println("Buffalo detected!");
                buffaloDetected = true;
                digitalWrite(D1,LOW );
            }

            // Check for Target 2 (score > 50)
            if (target == 2 && score > 50)
            {
                Serial.println("Target 2 detected!");
                target2Detected = true;
                digitalWrite(D2, HIGH);
                
            }
        }

        // Reset pins if targets not detected
        if (!elephantDetected) digitalWrite(D0, LOW);
        if (!buffaloDetected) digitalWrite(D1, LOW);
        if (!target2Detected) digitalWrite(D2, LOW);

        // Optionally print other outputs for debugging
        for (int i = 0; i < AI.boxes().size(); i++)
        {
            Serial.print("Box[");
            Serial.print(i);
            Serial.print("] target=");
            Serial.print(AI.boxes()[i].target);
            Serial.print(", score=");
            Serial.print(AI.boxes()[i].score);
            Serial.print(", x=");
            Serial.print(AI.boxes()[i].x);
            Serial.print(", y=");
            Serial.print(AI.boxes()[i].y);
            Serial.print(", w=");
            Serial.print(AI.boxes()[i].w);
            Serial.print(", h=");
            Serial.println(AI.boxes()[i].h);
        }

        for (int i = 0; i < AI.points().size(); i++)
        {
            Serial.print("Point[");
            Serial.print(i);
            Serial.print("]: target=");
            Serial.print(AI.points()[i].target);
            Serial.print(", score=");
            Serial.print(AI.points()[i].score);
            Serial.print(", x=");
            Serial.print(AI.points()[i].x);
            Serial.print(", y=");
            Serial.println(AI.points()[i].y);
        }

        for (int i = 0; i < AI.keypoints().size(); i++)
        {
            Serial.print("keypoint[");
            Serial.print(i);
            Serial.print("] target=");
            Serial.print(AI.keypoints()[i].box.target);
            Serial.print(", score=");
            Serial.print(AI.keypoints()[i].box.score);
            Serial.print(", box:[x=");
            Serial.print(AI.keypoints()[i].box.x);
            Serial.print(", y=");
            Serial.print(AI.keypoints()[i].box.y);
            Serial.print(", w=");
            Serial.print(AI.keypoints()[i].box.w);
            Serial.print(", h=");
            Serial.print(AI.keypoints()[i].box.h);
            Serial.print("], points:[");

            for (int j = 0; j < AI.keypoints()[i].points.size(); j++)
            {
                Serial.print("[");
                Serial.print(AI.keypoints()[i].points[j].x);
                Serial.print(",");
                Serial.print(AI.keypoints()[i].points[j].y);
                Serial.print("],");
            }
            Serial.println("]");
        }
    }
}

Transmitter (LoRa)

C/C++
This Code receives signal from the stack and transmits the data using LoRa
// Transmitter Code for Xiao nRF52840 with RYLR998 LoRa Module

#include <SoftwareSerial.h>

// Define pins for SoftwareSerial communication with the RYLR998 LoRa module
// LoRa RX pin (connects to LoRa module's TX pin)
#define LORA_RX_PIN 9
// LoRa TX pin (connects to LoRa module's RX pin)
#define LORA_TX_PIN 10

// Define the digital input pin on the Xiao nRF52840
// This pin will be read to determine the signal to send
#define INPUT_PIN 2

// Create a SoftwareSerial object for communication with the LoRa module
// The baud rate for RYLR998 is typically 115200 by default.
SoftwareSerial loraSerial(LORA_RX_PIN, LORA_TX_PIN);

// LoRa Module Configuration Parameters
// These must match on both the transmitter and receiver for communication
const int LORA_ADDRESS = 0;    // Module address (0-65535)
const int LORA_NETWORK_ID = 10; // Network ID (0-16)

// Variable to store the current state of the input pin
int currentInputState = LOW;

void setup() {
  // Initialize the hardware serial for debugging output (optional, but recommended)
  Serial.begin(115200);
  Serial.println("Transmitter (Xiao nRF52840) Initializing...");

  // Initialize the SoftwareSerial communication with the LoRa module
  loraSerial.begin(115200); // Default baud rate for RYLR998

  // Set the input pin mode with internal pull-down resistor
  // This ensures the pin is LOW by default unless actively pulled HIGH.
  pinMode(INPUT_PIN, INPUT_PULLDOWN);

  // --- LoRa Module Configuration ---
  // It's crucial to wait for the module to be ready after power-up.
  // A small delay usually helps, or you can implement a more robust AT command response check.
  delay(1000); // Give the LoRa module time to power up and stabilize

  // Set LoRa Module Address
  // Format: AT+ADDRESS=<address>
  Serial.print("Setting LoRa Address to ");
  Serial.print(LORA_ADDRESS);
  Serial.print("... ");
  loraSerial.print("AT+ADDRESS=");
  loraSerial.print(LORA_ADDRESS);
  loraSerial.print("\r\n"); // CR+LF is required for AT commands
  delay(500); // Wait for module to process command
  // You can add a loop here to read loraSerial and check for "OK" response for robustness.
  while (loraSerial.available()) {
    Serial.write(loraSerial.read());
  }
  Serial.println("Done.");

  // Set LoRa Module Network ID
  // Format: AT+NETWORKID=<ID>
  Serial.print("Setting LoRa Network ID to ");
  Serial.print(LORA_NETWORK_ID);
  Serial.print("... ");
  loraSerial.print("AT+NETWORKID=");
  loraSerial.print(LORA_NETWORK_ID);
  loraSerial.print("\r\n");
  delay(500);
  while (loraSerial.available()) {
    Serial.write(loraSerial.read());
  }
  Serial.println("Done.");

  // You might also want to set AT+BAND, AT+PARAMETER etc.
  // For simplicity, we'll rely on default parameters or pre-configured ones.
  // Example for setting band (e.g., 868000000 for Europe, 915000000 for US):
  // loraSerial.print("AT+BAND=868000000\r\n");
  // delay(500);

  Serial.println("Transmitter setup complete. Ready to send signals.");
}

void loop() {
  // Read the current state of the input pin
  currentInputState = digitalRead(INPUT_PIN);

  // Send a signal if the input pin is HIGH
  if (currentInputState == HIGH) {
    String dataToSend = "H"; // Send 'H' for HIGH
    Serial.println("Input is HIGH. Sending 'H'...");

    // Send data using AT+SEND command
    // Format: AT+SEND=<destination_address>,<data_length>,<data>
    loraSerial.print("AT+SEND=");
    loraSerial.print(LORA_ADDRESS); // Destination address (receiver's address)
    loraSerial.print(",");
    loraSerial.print(dataToSend.length()); // Length of the data string
    loraSerial.print(",");
    loraSerial.print(dataToSend); // The actual data
    loraSerial.print("\r\n"); // CR+LF to terminate the AT command

    // Increased delay to allow the module more time to process the send command and respond
    delay(200);

    // Read and print any response from the LoRa module (e.g., "OK" or "ERR")
    // Added a small delay before reading to ensure the full response is available
    delay(50); // Give a moment for the response to arrive
    while (loraSerial.available()) {
      Serial.write(loraSerial.read());
    }
    Serial.println(); // New line for cleaner output
  }

  // A small delay to debounce the input and prevent rapid transmissions
  // Adjust this value based on how frequently you need to update the signal
  delay(50);
}

Reciver side

C/C++
// Receiver Code for Arduino Nano with RYLR998 LoRa Module

#include <SoftwareSerial.h>

// Define pins for SoftwareSerial communication with the RYLR998 LoRa module
// LoRa RX pin (connects to LoRa module's TX pin)
#define LORA_RX_PIN 4
// LoRa TX pin (connects to LoRa module's RX pin)
#define LORA_TX_PIN 5

// Define the digital output pin on the Arduino Nano (D13, often with built-in LED)
// This pin will be set HIGH or LOW based on the received signal
#define OUTPUT_PIN 13 // Changed from 3 to 13 as per user request

// Create a SoftwareSerial object for communication with the LoRa module
// The baud rate for RYLR998 is typically 115200 by default.
SoftwareSerial loraSerial(LORA_RX_PIN, LORA_TX_PIN);

// LoRa Module Configuration Parameters
// These must match on both the transmitter and receiver for communication
const int LORA_ADDRESS = 0;    // Module address (0-65535) - must match transmitter's destination address
const int LORA_NETWORK_ID = 10; // Network ID (0-16) - must match transmitter

// --- New variables for timed output ---
// Duration for which the output pin should remain HIGH when 'H' is received
const unsigned long HIGH_DURATION_MS = 5000; // 5 seconds (editable)

// Variable to store the time when the output pin was set HIGH
unsigned long outputPinHighStartTime = 0;

// Flag to indicate if the output pin is currently in a timed HIGH state
bool isOutputPinTimedHigh = false;

void setup() {
  // Initialize the hardware serial for debugging output (optional, but recommended)
  Serial.begin(115200);
  Serial.println("Receiver (Arduino Nano) Initializing...");

  // Initialize the SoftwareSerial communication with the LoRa module
  loraSerial.begin(115200); // Default baud rate for RYLR998

  // Set the output pin mode
  pinMode(OUTPUT_PIN, OUTPUT);
  digitalWrite(OUTPUT_PIN, LOW); // Ensure the output pin starts LOW

  // --- LoRa Module Configuration ---
  delay(1000); // Give the LoRa module time to power up and stabilize

  // Set LoRa Module Address
  Serial.print("Setting LoRa Address to ");
  Serial.print(LORA_ADDRESS);
  Serial.print("... ");
  loraSerial.print("AT+ADDRESS=");
  loraSerial.print(LORA_ADDRESS);
  loraSerial.print("\r\n");
  delay(500);
  while (loraSerial.available()) {
    Serial.write(loraSerial.read());
  }
  Serial.println("Done.");

  // Set LoRa Module Network ID
  Serial.print("Setting LoRa Network ID to ");
  Serial.print(LORA_NETWORK_ID);
  Serial.print("... ");
  loraSerial.print("AT+NETWORKID=");
  loraSerial.print(LORA_NETWORK_ID);
  loraSerial.print("\r\n");
  delay(500);
  while (loraSerial.available()) {
    Serial.write(loraSerial.read());
  }
  Serial.println("Done.");

  // Enable receive mode (RYLR998 usually enters receive mode automatically after setup)
  // You can explicitly send AT+RCV if needed, but often not necessary for continuous listening.
  // loraSerial.print("AT+RCV\r\n");
  // delay(100);

  Serial.println("Receiver setup complete. Waiting for signals...");
}

void loop() {
  // Check if data is available from the LoRa module
  if (loraSerial.available()) {
    // Read the incoming data
    String receivedData = "";
    while (loraSerial.available()) {
      char c = loraSerial.read();
      receivedData += c;
      // Small delay to allow entire message to arrive, or use a timeout
      // A more robust approach would be to wait for a newline or a specific end character.
      delay(2);
    }

    // The RYLR998 sends data in the format: "+RCV=<address>,<data_length>,<data>,<RSSI>,<SNR>"
    // We need to parse this string to extract the actual data.
    Serial.print("Received raw: ");
    Serial.println(receivedData);

    // Find the first comma after "+RCV="
    int firstComma = receivedData.indexOf(',');
    // Find the second comma
    int secondComma = receivedData.indexOf(',', firstComma + 1);
    // Find the third comma (which precedes RSSI)
    int thirdComma = receivedData.indexOf(',', secondComma + 1);

    // Ensure all necessary commas are found
    if (firstComma != -1 && secondComma != -1 && thirdComma != -1) {
      // Extract the actual data part, which is between the second and third comma
      String actualData = receivedData.substring(secondComma + 1, thirdComma);
      actualData.trim(); // Remove any leading/trailing whitespace

      Serial.print("Parsed data: ");
      Serial.println(actualData);

      // Check the parsed data and set the output pin accordingly
      if (actualData.equals("H")) {
        digitalWrite(OUTPUT_PIN, HIGH);
        Serial.println("Output pin set HIGH for timed duration.");
        outputPinHighStartTime = millis(); // Record the time when pin went HIGH
        isOutputPinTimedHigh = true;      // Activate the timed HIGH flag
      } else if (actualData.equals("L")) {
        digitalWrite(OUTPUT_PIN, LOW);
        Serial.println("Output pin set LOW (received 'L').");
        isOutputPinTimedHigh = false;     // Deactivate the timed HIGH flag
      } else {
        Serial.print("Unknown signal received: ");
        Serial.println(actualData);
      }
    } else {
      Serial.println("Could not parse received data format. Missing expected commas.");
    }
  }

  // --- Check for timed HIGH duration ---
  // If the output pin is in a timed HIGH state and the duration has passed
  if (isOutputPinTimedHigh && (millis() - outputPinHighStartTime >= HIGH_DURATION_MS)) {
    digitalWrite(OUTPUT_PIN, LOW);     // Turn the output pin LOW
    isOutputPinTimedHigh = false;      // Reset the flag
    Serial.println("Output pin timed HIGH duration ended. Set LOW.");
  }

  // Small delay to prevent busy-waiting and allow other tasks
  delay(10);
}

Credits

Madhav
2 projects • 3 followers
Hardware, PCB , Embedded

Comments