How I Built an IoT Device to Monitor Sailing Conditions at Community Boating Boston ⛵

Enrique Gamboa
7 min readAug 20, 2024

--

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.

IoT Flag Monitor connected to Community Boating Boston API, Displaying: ‘Yellow Flag’ and ‘Limited Sonar Availability’ in real-time

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. 👇

IoT Flag Monitor for Community Boating Boston displaying ‘Yellow Flag’ and Status ‘No Adults’.

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? 💡

Website of Community Boating Boston displaying a Yellow Flag

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. 😎

IoT Flag Monitor for Community Boating Boston displaying a Green Flag and ‘Limited Sonar Availability’

Materials I Used 🛠️

Here’s a quick list of the components I used to build this project:

Lilygo — T-Display Apparence
3D Model of Boat being printed
  • Here is the modified 3D model version for this project: Link
Bambu Studio displaying the 3D parts to be printed

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:

  1. 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.
  2. Finding the Right API: With the developer tools open, I clicked on the Network tab. To filter the network requests, I set the filter to Fetch/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 as flag-color. API Link.
  3. 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.
Boston Community Boating Website inspected using the Browser Developing Tools to get the API.

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!

Launching site of Community Boating in Boston displaying a Yellow Flag

--

--

Enrique Gamboa

If art is a human abstraction, Artificial Intelligence is the abstraction of humanity 🦾