How I Built an IoT Device to Monitor Sailing Conditions at Community Boating Boston ⛵
This summer, I decided to jump into the world of sailing by signing up for classes at Community Boating in Boston. The experience has been nothing short of amazing, filled with the excitement of learning new skills and the calm of being out on the water. The way it works at Community Boating is that you start with basic theoretical classes, and once you’ve completed them, you earn a “Green Flag.” This flag indicates that you can sail, but only when the Green Flag is raised, signaling favorable weather conditions for beginners. As you keep practicing, you can earn higher-level flags like Yellow or Red, which correlate with more challenging conditions.
Here is the GitHub repo for this project: Link.
Every time I want to head out for a sail, I need to check the Community Boating website to see which flag is currently raised. For now, I’m primarily looking for the Green Flag since I’m still a beginner. The problem? It’s a bit of a hassle to constantly visit the website just to check the flag status. So, I decided to create a solution: an IoT device that does the checking for me. 👇
The Problem: Constantly Checking the Website 🖥️
As someone who loves automation and hates repetitive tasks, I quickly realized that manually checking the flag status every time I wanted to sail was not ideal. The process was simple but time-consuming, and I thought, “There has to be a better way!” That’s when the idea hit me: why not build a tiny device that could monitor the flag status in real-time? 💡
The Solution: An IoT Flag Monitor 📡
To solve this problem, I decided to create a monitor using a small IoT device. The idea was straightforward: connect the device to the Community Boating real-time API to fetch the current flag status and display it on a screen. This way, I could know the flag status at a glance, without the need to visit the website. It might seem like a small thing, but it saves me those precious few seconds every time I’m getting ready to sail. 😎
Materials I Used 🛠️
Here’s a quick list of the components I used to build this project:
- T-Display ESP32 WiFi Bluetooth Development Board: This board includes a 1.14-inch ST7789V IPS LCD, making it perfect for displaying the flag status. Product: Link
- 3D-Printed Case: To neatly house the components and keep the setup secure. Original. Link to the original model.
- 3D-Printed Boat Model: Just for fun, I added a small boat model to the setup to give it a more thematic feel. Link to the original model.
- Here is the modified 3D model version for this project: Link
Getting the Real-Time Data 🕒
The key to this project was accessing the real-time data from the Community Boating website. Here’s how I discovered the API and fetched the flag status:
- Inspecting the Website: First, I navigated to the Community Boating website where the flag status is displayed. I then pressed the
F12
key on my keyboard to open the browser's developer tools. This gave me access to the console and network activity. - Finding the Right API: With the developer tools open, I clicked on the
Network
tab. To filter the network requests, I set the filter toFetch/XHR
. This narrowed down the list to show only the relevant API calls. Among the requests, I found the one responsible for fetching the flag color data, labeled asflag-color
. API Link. - Retrieving the Status Data: I repeated the same process to locate the API responsible for fetching the status information, which was labeled as
fotv
. This API provided the specific status updates, which are crucial for understanding the conditions on the Community Boat center. API Link.
By following these steps, I was able to access the exact APIs that power the flag status on the Community Boating website, allowing me to retrieve real-time data directly for my IoT device.
The Code 💻
To bring everything together, I wrote a script that connects to the WiFi, fetches the flag color and status data from the Community Boating API, and displays the information on the TFT screen of my Lylygo T-Display (ESP32 device). The code handles everything from parsing the JSON response to displaying the flag color with a neat wave effect, along with the corresponding status message.
Here’s a brief rundown of what the code does:
- WiFi Connection: The device connects to the specified WiFi network.
- API Requests: It makes HTTP GET requests to retrieve the current flag color and status.
- Data Parsing: The JSON responses are parsed to extract the relevant information, such as the flag color and active status title.
- Display Logic: The code updates the TFT display with the current flag color, status message, and a custom-drawn flag graphic.
- Periodic Updates: The device continuously checks for updates every 10 seconds to ensure the displayed information is always current.
If you’re interested in the full code, you can find it in my GitHub repository. Feel free to check it out and customize it to suit your own needs!
// 8/19/2024 - BY JEGF
#include <TFT_eSPI.h>
#include <SPI.h>
#include "WiFi.h"
#include <HTTPClient.h>
// WiFi credentials
const char* ssid = "myWIFINetwork";
const char* password = "myPassword";
// API URLs
const char* apiURL = "https://tv.community-boating.org/api/flag-color";
const char* textApiURL = "https://tv.community-boating.org/api/fotv";
TFT_eSPI tft = TFT_eSPI(135, 240); // Initialize the TFT display
String activeTitle = ""; // Variable to store the active title
void setup() {
Serial.begin(115200);
tft.init();
tft.setRotation(3); // Rotate display 180 degrees
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
tft.setCursor(0, 0);
tft.setTextDatum(MC_DATUM);
tft.setTextSize(1);
// Connect to WiFi
WiFi.begin(ssid, password);
tft.setTextColor(TFT_GREEN);
tft.drawString("Connecting to WiFi...", tft.width() / 2, tft.height() / 2);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_GREEN);
tft.drawString("Connected to WiFi", tft.width() / 2, tft.height() / 2);
// Fetch the active title from the API
fetchActiveTitle();
// Fetch and display the flag color for the first time
fetchAndDisplayFlagColor();
}
void fetchActiveTitle() {
HTTPClient http;
http.begin(textApiURL);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
String payload = http.getString();
Serial.println(payload);
// Find the title with active: true in the JSON response
int startIndex = payload.indexOf("\"active\":true");
if (startIndex != -1) {
int titleIndex = payload.lastIndexOf("\"title\":\"", startIndex) + 9;
int endIndex = payload.indexOf("\"", titleIndex);
activeTitle = payload.substring(titleIndex, endIndex);
}
} else {
Serial.println("Error on HTTP request");
}
http.end();
}
void fetchAndDisplayFlagColor() {
HTTPClient http;
http.begin(apiURL);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
String payload = http.getString();
Serial.println(payload);
// Parse the JSON response
int index = payload.indexOf("flagColor\":\"") + 12;
char flagColor = payload.charAt(index);
// Determine the status and corresponding flag color
String status;
uint16_t flagColorCode;
switch (flagColor) {
case 'C':
status = "Closed";
flagColorCode = TFT_WHITE;
break;
case 'G':
status = "Green";
flagColorCode = TFT_GREEN;
break;
case 'Y':
status = "Yellow";
flagColorCode = TFT_YELLOW;
break;
case 'R':
status = "Red";
flagColorCode = TFT_RED;
break;
default:
status = "Unknown";
flagColorCode = TFT_WHITE;
break;
}
// Add the "FLAG" label in blue
tft.fillScreen(TFT_BLACK);
tft.setTextSize(3);
tft.setTextColor(TFT_BLUE);
tft.drawString("FLAG", 10, 10); // Draw "FLAG" on the top left
// Display the status text below the "FLAG" label in the appropriate color
tft.setTextColor(flagColorCode);
tft.drawString(status, 10, 39); // Status text directly below "FLAG"
// Draw the flag (a rectangle with a pole and wave effect)
int flagWidth = 50; // Increased width to make the flag bigger
int flagHeight = 35; // Increased height to make the flag bigger
int flagX = tft.width() - flagWidth - 10; // Top right with a margin of 10 pixels
int flagY = 10; // Top with a margin of 10 pixels
// Draw the pole
tft.fillRect(flagX - 5, flagY, 3, flagHeight, TFT_DARKGREY);
// Draw the flag with a simple wave effect
for (int i = 0; i < flagHeight; i++) {
int waveOffset = (i % 2 == 0) ? 0 : 4; // Adjusted for the larger flag
tft.drawFastHLine(flagX + waveOffset, flagY + i, flagWidth, flagColorCode);
}
// Display the "status:" label and active title on separate lines
tft.setTextSize(1);
tft.setTextColor(TFT_PURPLE); // Purple color for the title label
tft.drawString("status:", 10, tft.height() - 40); // Label "status:" above the title
tft.setTextColor(TFT_WHITE);
tft.drawString(activeTitle, 10, tft.height() - 20); // Title text on the next line
} else {
Serial.println("Error on HTTP request");
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Error fetching data", tft.width() / 2, tft.height() / 2);
}
http.end();
}
void loop() {
fetchAndDisplayFlagColor(); // Fetch and display flag color continuously
delay(10000); // Wait 10 seconds before making another API call
}
Conclusion 🎯
This small project not only saved me time but also added a bit of fun to my sailing routine ⛵. Now, every time I’m about to head out, I just glance at my IoT device to see the current flag status — no more unnecessary web browsing! Whether you’re a fellow sailor or just someone who loves tinkering with IoT, I hope this post inspires you to build something that simplifies your life. If you’re interested in the details, feel free to reach out or check the GitHub repository for the complete code!