Full Code : https://github.com/rifqieeeee/goat_guards_system
PREPARATION
In this project, we're combining a few different components to make the system work. We'll be using the Unihiker to measure the decibel levels of goat sounds. When a goat screams, the sound's decibel level spikes.
But how do we tell the difference between a scream that means the goat is in trouble and a normal scream? The plan is to use a time threshold. For instance, if a goat screams for a minute or more at a certain loudness level, we'll consider it a distress signal. I mean, goats don't usually scream continuously unless something's wrong, right?. To program the Unihiker, we're going with Mind+, which is one of the best platforms for this. You can check out the documentation and examples.
Next, we'll work on sending the data using LoRa. The LoRa component we'll be using is the Grove Wio-E5. This module will be responsible for transmitting data from the Unihiker to the receiver. To make it work, we'll use two Grove Wio-E5 modules: one as the transmitter and the other as the receiver. This way, we can ensure the data travels seamlessly from the goat pen to wherever the receiver is located.
I ran into a bit of an issue while trying to send data using the Unihiker. To keep it simple, we can use Python on the Unihiker to handle the data transmission. First, we need to initialize UART communication using AT Commands. Next, we set the Wio-E5 module to enter TEST mode. After that, we configure the LoRa module by setting the necessary parameters. Finally, we can test it by attempting to send the data. Following these steps should help resolve the issue and get the transmission working.
Command sent: AT
Response: +AT: OK
AT command successful.
Command sent: AT+MODE=TEST
Response: +MODE: TEST
Module entered TEST mode successfully.
Command sent: AT+TEST=RFCFG,866,SF12,125,12,15,14,ON,OFF,OFF
Response: Response: +AT: ERROR(-24)
In theory, this should be totally doable. But when I tried it with Python, it just didn't work. Instead, I kept getting the error: Response: +AT: ERROR(-24), and honestly, I have no idea what's going on. I even tried looping it continuously and waited for 30 minutes, hoping the device would eventually be ready, but no luck. If you have any suggestions on how to fix this, feel free to drop them in the comments!
I decided to use an additional component to solve the problem. I'll be using the XIAO ESP32S3 to handle data transmission via LoRa, a method that has proven successful in several of my previous attempts. So, how will we send the data? The Unihiker will read the sound from the goats, then use digital communication to send the data to the XIAO ESP32S3, which will handle transmitting it via LoRa to the receiver. This is an alternative solution for the issue, and if I manage to find a direct fix for the original problem, I'll make sure to update this here.
The data transmitted via LoRa by the XIAO ESP32S3 will be received by the Wio Terminal. In this setup, the Wio Terminal will function as the receiver for the system. Whenever an issue is detected in the goat pen, the Wio Terminal will activate a siren to alert my dad to check the pen immediately, whether it's day or night.
Here's an overview of the entire system we're building. It starts with the Unihiker, which reads the sound data, then sends it to the XIAO ESP32S3 via digital communication. The XIAO ESP32S3 transmits the data to the Wio Terminal using LoRa, and finally, the Wio Terminal activates a siren. Check out the diagram below to see the detailed schematic I used for this project.
READ GOAT CRIES USING UNIHIKER
Let's kick things off with the first part of this project: reading sound data from the goats using the Unihiker. To do this, we'll use the audio library available on the Unihiker to measure the noise levels in the environment. We'll set a specific threshold to define what counts as unusually loud noise.
But we're not stopping there. The system will also monitor if the noise level exceeds this threshold for around 10 seconds. If it does, we'll consider it a potential danger or emergency in the goat pen. This logic helps us filter out random loud noises and focus only on sustained sounds that could indicate a problem.
On top of that, we'll display the noise data in real time using the GUI library on the Unihiker. This way, we can easily visualize the noise levels and ensure the system is working correctly during testing and deployment. Here's a snippet of the code from the void loop section.
while True:
# Calculate the current second
elapsed_time = time.time() - start_time
second_index = int(elapsed_time % 10)
# Update the displayed sound value
sound_value = audio.sound_level()
sound_text.config(text=sound_value)
if (sound_value > threshold):
sound_levels[i] = 1
int_time = int(elapsed_time)
if i != int_time:
status = "Detected" if sound_levels[i] == 1 else "Not Detected"
print(f"Time now = {i}")
print(f"Status = {status}")
i = i + 1
if (i>9):
total_detections = sum(sound_levels) # Sum all values in sound_levels
print(f"Total Detections: {total_detections}\n\n")
timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
detection_string = f"{timestamp}, {total_detections}, {sound_levels[0]}, {sound_levels[1]}, {sound_levels[2]}, {sound_levels[3]}, {sound_levels[4]}, {sound_levels[5]}, {sound_levels[6]}, {sound_levels[7]}, {sound_levels[8]}, {sound_levels[9]}\n"
with open("goat_guards_data.txt", "a") as file:
file.write(detection_string)
i = 0
start_time = time.time()
sound_levels = [0] * 10
count_text.config(text=f"Count: {total_detections}")
# Add a small sleep to prevent high CPU usage
time.sleep(0.1)
Here's how the display looks. You'll notice a section showing the sound level, which indicates the decibel value detected by the device. Below that, there's a counter that tracks how many seconds within a 10-second window the sound exceeded the threshold. For example, if the sound crosses the threshold at seconds 5, 6, and 8, the counter will show a value of 3, representing the total number of seconds above the threshold within that 10-second period. The assumption here is that if a goat is in distress, it will keep screaming consistently, causing the counter value to be high. This data will be key in determining whether a goat is facing an issue or not. By monitoring the count, we can reliably assess if something unusual is happening in the goat pen.
And here's the output I printed on the terminal in the Mind+ application. It provides a more detailed illustration of how the system works, as explained in the sections above.
Now we can detect whether a goat is in distress based on its sounds. Let's try a simple simulation to see how it works. For this test, I used a threshold value of 18dB, but I plan to increase it for the actual implementation to better suit real-world conditions. Check out the video below!
As an added feature, I'm also saving all the reading data into a.txt file. This allows me to keep a record of all the data for further analysis or troubleshooting. Storing the data in a.txt format is one of the simplest and most efficient approaches. It's easy to read, lightweight, and compatible with virtually any platform or text editor. Additionally, saving the data this way makes it convenient to review historical data, debug any issues, or even use it for further processing or visualization in other applications. This method ensures flexibility and accessibility while keeping the process straightforward and effective.
The data I'm saving includes everything: the timestamp, total detections, and values for each second within a 10-second window data_0, data_1, data_2, and so on up to data_9. This comprehensive dataset gives me a clear picture of what's happening over time. The best part? I can easily download it by simply connecting the Unihiker to my laptop. Now, you might wonder, why not store this data in the cloud? Well, it goes back to the initial challenge: my location doesn't have cable internet or WiFi, so relying on cloud storage isn't an option. That's why saving the data locally in a.txt file is the most practical solution for now. Check out the image below for an example of the saved data. You'll see how the readings are structured and how easy it is to review or analyze them directly.
DIGITAL CONNECTION UNIHIKER TO XIAO ESP32C6
We've completed the setup for measuring and reading the data, so now it's time to move on to the next big step: transmitting the data using LoRa. In the previous section, we successfully obtained the "count" value, which represents how many seconds within a 10-second window the goat screamed louder than the defined threshold. This "count" value is critical because it serves as the core data for our notification system. It will be transmitted to the receiving device using LoRa, ensuring that alerts can be sent even in remote locations where other communication methods aren't feasible.
If we think about it, we can actually make this whole process way simpler by just sending a boolean value. Basically, we only need to send whether there's a potential problem with the goat or not. How? It's pretty straightforward we just need to decide on a threshold for the "count" value that tells us when something might be wrong. For this setup, I'm using 5 seconds as the limit. So, if the goat screams loudly enough to go over the threshold for 5 seconds (out of the 10-second window), we'll treat it as a sign that something's wrong in the pen. This way, the system gets simpler and more efficient without sending unnecessary data. Easy, right?
Once we've set this up, we can quickly notify the XIAO ESP32S3 using just GPIO. Here, I connected pin P3 on the Unihiker to pin D0 on the XIAO ESP32S3. The method is super simple: we'll make pin P3 on the Unihiker go HIGH whenever the total count exceeds 5. This straightforward approach allows the system to act fast and keep the communication between devices clean and efficient.
if(total_detections > 5):
led.write_digital(1)
else:
led.write_digital(0)
And on the XIAO ESP32S3 side, we simply read the signal using a basic digitalRead program on A0. Here's the result displayed in the serial monitor of the XIAO ESP32S3. You can see that it's successfully showing the boolean data, indicating whether there's a potential issue in the goat pen. Simple and effective!
LORA CONNECTION
Now that we've got the data, let's send it using LoRa communication. This is the most important part of the project, where we'll transmit the data using LoRa. I'm using the XIAO ESP32S3 connected to the Grove-Wio-E5 on pins 5 and 6, while the Wio Terminal is paired with its Grove-Wio-E5 on pins 1 and 2.
We can use the ESP32S3 to send the data we've collected. Data transmission with both the Grove-Wio-E5 and the Wio Terminal is typically done using AT Commands. Here's how it works: The first command, AT, is used to check if the LoRa module is connected and responsive. If the module is active, it responds with +AT: OK. Next, the module is set to test mode using AT+MODE=TEST. This mode allows us to test LoRa functions like data transmission and radio settings without fully entering operational mode. Then, the radio frequency settings are configured using the command AT+TEST=RFCFG, 866, SF12, 125, 12, 15, 14, ON, OFF, OFF. This includes setting the operating frequency to 866 MHz, a spreading factor (SF) of 12, a bandwidth of 125 kHz, a coding rate of 12, a power level of 15 dBm, and an output power of 14 dBm. CRC is enabled for error checking, while IQ inversion and Adaptive Data Rate (ADR) are turned off.
To send the data, the program uses the command AT+TEST=TXLRPKT. The data is sent as a string, starting with the prefix "5345454544", followed by additional data like the sensor value (data1). If the transmission is successful, the module responds with TX DONE, indicating that the data was successfully sent using the LoRa protocol. And here are the test results, it worked perfectly!
It looks like the data has been successfully sent and will soon be received by the Wio Terminal. So, what's happening on the Wio Terminal side? The program we'll use for the Wio Terminal isn't all that different from what we used on the XIAO ESP32S3. It's still based on AT Commands, and we'll configure the LoRa module with the same settings using AT+TEST=RFCFG. The main difference is in the main loop. The program continuously monitors the data received through the LoRa module. The AT+TEST=RXLRPKT command is used to activate the data reception mode. When data is received, the program processes it to extract specific information encoded in a certain format.
You can see that the data has been received, and it shows Data: ON, which indicates there's a problem in the goat pen. So, when the received data contains the value "01", the program displays a "Danger!" status in red on the TFT screen and activates the control pin (sets it to HIGH). If the data contains "00", it shows a "Safety" status in green, and the control pin stays inactive (LOW). Here's what's displayed on the Wio Terminal.
And if it shows Data: OFF, meaning the condition is safe, the status will change to "Safety". By the way, the "Safety" text is actually green, but because of the low quality of my phone camera, it looks more like white in the image. Check out the image below!
HOW THE ALLERTS WORKS
The last step is setting up the alarm. We'll use one of the GPIO pins on the Wio Terminal to make it work. For the alert, we're hooking up a 12V siren module to let us know if there's an issue in the goat pen. This way, we'll get an immediate warning if something's wrong!
The funny thing is, the siren is insanely loud when I connect it to a 12V power supply. I'm not kidding, my ears almost exploded when I heard it, haha, just joking! That's why I initially planned to use a relay as a switch to control the siren, but I scrapped that idea after experiencing how loud it was. Instead, I decided to power it with a lower voltage, just 3.3V, by connecting it directly to the GPIO. The sound is much quieter, more reasonable, and not deafening anymore. Perfect for the job!
Next, we'll create a simple program to turn on the siren when there's a problem in the pen. If an issue occurs, the data received by the Wio Terminal will be 1. So, we can use an if-else function to handle this. All we need to do is write a digitalWrite command to activate the siren. The siren will be connected to pin BCM 24, and we can add a bit of code inside the recv_parse function, like this.
bool status = strstr(recv_buf, "01") != NULL;
digitalWrite(CONTROL_PIN, status ? HIGH : LOW);
All we need to do is solder the positive and negative pins of the siren onto a dot PCB and connect them based on the setup we've planned. Once that's done, plug it into the Wio Terminal, and the siren is good to go!
TESTING
We've done all the steps, and everything's ready. Let's test it out! Check out the video below to see how it works!
CLOSING
I'm incredibly happy to have solved a real-life problem I experienced by creating a solution on my own. This project isn't just a makeshift fix; it's a well-thought-out implementation of technology tailored to the specific conditions of my situation. For example, I used LoRa for communication between devices because the location of our farm doesn't support WiFi connectivity.
In this project, I collected real-world environmental data, specifically the sounds of goats in distress, to address a genuine need. I also stored this data to monitor my goats' condition overnight. This stored data can serve as a backup for future developments, who knows, maybe one day I could create an AI model and implement it in an upgraded version of this system, right?
Lastly, this project includes a practical alert system: a siren that sounds whenever there's a potential issue with my goats. It's a direct, actionable use of the data collected, ensuring the safety of my goats with a system I designed myself.
Finally, something I often share during workshops on IoT: "IoT projects aren't about how advanced or cutting-edge the technology you use is. It's about how you can solve real problems around you in meaningful and practical ways". So, take a look at your surroundings, identify what's not working or could be improved, think of a solution, and take action to address it.
Are you ready to create your first IoT project? Let's get started! :D