Project tutorial

Measure and Analyze Tide Levels with ThingSpeak and MATLAB © MIT

Learn how to create a tide gauge that publishes tidal water levels to ThingSpeak and uses MATLAB for tide prediction, analysis, and alerts.

  • 2,948 views
  • 0 comments
  • 18 respects

Components and supplies

Apps and online services

About this project

Getting your boat stuck in the mud in a shallow bay is easy if you do not know how deep the water is. Since accurate tide predictions and real-time water levels are not available for most bays and estuaries, including the one I boat in, I built my own low-cost, real-time tide gauge.

My tide gauge publishes tidal water levels on the Internet, and it sends alerts via a Twitter account using a combination of open source Arduino hardware, the free ThingSpeak service, and MATLAB.

Doing nothing stuck in the mud Just pumping the blood The water level’s getting low Something ugly is going to show …
Peter Gabriel – Flotsam and Jetsam

Peter Gabriel’s lyrics in Flotsam and Jetsam aptly describe the hapless boaters stuck in the mud during spring lows on Ockway Bay in Mashpee, Cape Cod. There is a good chance of something ugly happening as a boat motor churns up a rooster tail of black mud while making absolutely no headway.

Watching boaters getting stuck in the mud during spring lows on Ockway Bay is a regular weekend pastime for the residents of the bay who are intimately familiar with local water depths and NOAA tidal forecasts. A number of years ago, as a new resident of the bay, I was determined not to become part of the comic relief. To my surprise, during my first summer of boating I came too close to getting stuck a number of times, despite diligently paying attention to NOAA predictions.

I quickly learned that NOAA predictions for the bay are not sufficiently accurate to cut the return trip too close, particularly during spring lows!

The Challenge of Accurately Predicting Tide Levels

Reading NOAA’s Tidal Analysis and Prediction reveals that the timing and amplitude of tide levels is highly dependent on location. Note in this chart the significant difference in the tidal measurements between two bays, Green Pond and Ockway Bay, which are only six miles apart on the south shore of Cape Cod.

On top of the relative periodic motions of the Earth, moon, and sun—the primary drivers of the timing of the tides—the hydrodynamics (the physics of the water movement) of the ocean and the bay also determine the shape and amplitude of the tides. The hydrodynamics of shallow bays and estuaries, such as those shown here, result in further changes to the shape and timing of the tidal fluctuations. Adding to this already complicated situation are the effects of winds and river flow that can result in significant variations in expected water levels. Changes in the depth or shape of the bay through dredging or as the result of storms also change the hydrodynamics and subsequent tidal variation.

Predicting the effects of local hydrodynamics on tide levels requires the use of local measurements ideally measured over a period of one year or more. Ideally 18.6 years of measurements are needed, but useful predictions can be made using measurements taken over a period of 15 days. Even with accurate long-term measurements, there is a limit to the accuracy of tidal predictions. NOAA has published data on the Tide Prediction Error for United States Stations, which provides an indication of the accuracy of tide predictions. Like many smaller bays and estuaries, NOAA does not have a tidal measurement station in Ockway Bay or (a contiguous bay). But NOAA has determined that the high and low tides in Popponesset Bay can be approximated from the Boston predictions with fixed offsets in timing and height. Since the Popponesset Bay forecasts are based on Boston, it can be inferred that the Popponesset Bay tide heights will have an accuracy of no better than 0.16 feet and the timing of the high and low tide predictions will be no better than 9 minutes.

Not surprisingly, comparisons between predicted and measured tide levels in Popponesset Bay show that the accuracy of these forecasts is nowhere near as good as the Boston predictions.

ThingSpeak, MATLAB, and the Internet of Things to the Rescue

With knowledge of boat drafts and bay depths, it is relatively easy to avoid getting stuck in the mud by dipping a manual tide level stick into the water prior to leaving the bay. The greater challenge is knowing when it necessary to start a return trip to avoid getting stuck out for hours.

With a few inches of water making all the difference, NOAA predictions are not a safe bet. And so for the past five years, an Internet-connected electronic tide gauge has been tweeting the tide levels to provide me with fair warning when the time has come to return home through the bay.

Data Collection Hardware and Firmware

Over the past few years, the popularity of the Internet of Things has increased immensely along with the availability of low-cost connected hardware devices designed specifically for IoT applications. Popular examples include the Electric Imp, Intel Edison, and Particle hardware along with a number of Arduino-based solutions with Wi-Fi or cellular shields. The new hardware uses a readily available Arduino cellular shield available from Sparkfun. Cellular coverage provides greater reliability than the typical home Internet connection, albeit at a greater operating cost.

Hardware Configuration and Assembly

I assembled the tide gauge using these parts:

Miscellaneous:

  • PVC pipe
  • Shielded CAT5 cable
  • Wire Jumpers
  • Plastic soda bottle
  • White spray paint
  • Outdoor electrical tape
  • Stainless steel cable assembly

The electronics are mounted inside a water proof enclosure, which in turn is mounted on the side of a wooden pier. The DS18B20 waterproof sensor is mounted inside a PVC Tee to shield it from the sun. Power is available at the end of the pier.

A shielded multiwire cable runs from the enclosure to a Maxbotix range sensor mounted on a fixed piling in deeper water. The shielded cable is supported by a stainless steel cable and connects the power, data, and control pins on the sensor to the Arduino device. The range sensor is mounted on an arm pointing downward to measure the distance from the sensor to the water. To shield the built-in temperature compensation sensor from the sun, I cut a soda bottle, sprayed it white, and mounted it around the sensor.

This schematic diagram shows how the electronics are connected. The electronics include a lithium polymer battery and charger to ensure continued operation of the circuit during temporary power failures.

Tide Gauge Firmware

I wrote firmware for the Arduino the median of 99 range readings to a channel on ThingSpeak approximately every minute, along with the air temperature. Download the firmware.

ThingSpeak Data Collection and Tide Level Display

The tide gauge sends range and temperature data to a channel on ThingSpeak approximately once a minute.

Since the data is collected as range in mm, I convert the data to water depth for a more useful display. I did this by using a ThingSpeak TimeControl that runs a MATLAB Analysis to read the range data, convert it into water depth, and write the data to a new channel as follows:

% Read the last 5 minutes of measurements of the range in mm from the
% sensor to the water and take the median of the readings
 
range = median(thingSpeakRead(22641,'Fields',[1],'NumMinutes',5,... 'URL','http://thingspeak.com/'));
% Convert the range to depth from the mud in inches.  Note that this depth 
% is specific to the depth of the water at the end of my dock.  Another 
% approach is to use the mean low low water levels or some other datum. The 
% distance from the sensor to the mud is 3449 mm  
 
depth = (3449 - range)/24.5;  
 
% Round the value for better display depth = round(depth,1);  
% Write the depth to a new ThingSpeak channel 
 
thingSpeakWrite(50289, depth, 'WriteKey', '6RS44ZPWVI48QEKZ',... 'URL', 'https://api.thingspeak.com'); 

Here is the MATLAB code above included in a ThingSpeak MATLAB Analysis.

A ThingSpeak Time Control runs the “Fetch and write Ockway Tide” MATLAB Analysis every 5 minutes.

On the new Ockway Bay Tide Level channel (50289), the tide level displays as water depth in inches and updates every 5 minutes.

Using a mobile device with a browser, I can check the tide level remotely and avoid getting stuck in the mud.

Tide Alerts

Remembering to check the tide level when fishing or lazing on the beach is not particularly convenient. A much more useful approach is to have the system send a message when the time has come to pack up and start heading back to the dock.

The timing of the alert depends on how much water depth is needed by a particular boat. For this project, about 15 inches of water is needed before the boat motors start churning up mud.

One solution to the problem, described below, is to have ThingSpeak tweet both when the water depth has dropped below 20 inches and when the depth has risen above 15 inches.

I used the ThingSpeak ThingTweet app to link my Twitter account to ThingSpeak.

Detect Tidal Thresholds

Here is the MATLAB code that I wrote to detect when the tide level goes below 20 inches or rises above 15 inches and write the value of the tide levels to ThingSpeak Falling Tide and Rising Tide channels that I created.

% This code reads the past 30 minutes tide range data from ThingSpeak 
% channel 22641. Calculates the median of the tide level 5 minutes ago and 
% 25 minutes ago.  It then uses these values to check if either a) the tide 
% is going down and has passed a defined 'falling threshold' or b) is going 
% up and has passed a defined 'rising threshold'. When either of these 
% thresholds is passed the tide height when that happens is written to a 
% ThingSpeak Falling Tide or Rising Tide channel as appropriate. The code 
% also checks to make sure that sufficient time has past before writing a 
% new value to avoid multiple triggers for the same threshold. 
% Read the last 30 minutes of tide data 
range = thingSpeakRead(22641,'Fields',[1],'NumMinutes',30, ... 
   'URL','http://thingspeak.com/'); 
% Convert range from sensor to water in mm to depth from mud in inches 
depth = (3449 - range)/24.5; %depth from mud in inches 
% Define threshold level in inches 
fallingthreshold = 20; 
risingthreshold = 15; 
% Define minimum time in minutes between alerts 
alertGap = 60; % minutes 
alertGapNum = alertGap/60/24; % Convert to datenum 
% Set up index to divide data into roughly 10 minute periods 
i = round(length(depth)/3); 
% Make sure we are working with enough data 
if i>8 
   % Calculate the median of each 10 minute period 
   depth25minago = median(depth(1:i)); 
   depth5minago = round(median(depth(2*i+1:end)),1); 
   % Check time since last change used below to avoid double triggers 
   timeNow = datetime('now'); 
   % Read the last falling time 
   [lastFallingHeight,lastFallingTime] = thingSpeakRead(50192,... 
       'ReadKey', '9QSZ88F42HE2BA4H',... 
       'URL','https://api.thingspeak.com/'); 
   if isempty(lastFallingTime) % at the start this will be empty 
       sinceLastFall = 1; 
   else 
       sinceLastFall = datenum(timeNow)-datenum(lastFallingTime); 
   end 
   % Read the last rising time 
   [lastRisingHeight,lastRisingTime] = thingSpeakRead(50193,... 
       'ReadKey', '9AL2W9R0Y07GFR02',... 
       'URL','https://api.thingspeak.com/'); 
   if isempty(lastRisingTime) % at the start this will be empty 
       sinceLastRise = 1; 
   else 
       sinceLastRise = datenum(timeNow)-datenum(lastRisingTime); 
   end 
       % If the tide has fallen below the falling threshold write to the 
       % ThingSpeak falling tide channel 
       if (depth25minago > fallingthreshold)... 
           && (depth5minago <= fallingthreshold)... 
           && (sinceLastFall > alertGapNum) 
           thingSpeakWrite(50192, depth5minago, ... 
               'WriteKey', '928PKIJS64PXECSL',... 
               'URL','https://api.thingspeak.com/'); 
       end 
       % If the tide has fallen below the rising threshold write to the 
       % ThingSpeak rising tide channel 
       if (depth25minago < risingthreshold)... 
           && (depth5minago >= risingthreshold)... 
           && (sinceLastRise > alertGapNum) 
           thingSpeakWrite(50193, depth5minago,... 
               'WriteKey', 'WYVR5N22N4NABWR6',... 
               'URL','https://api.thingspeak.com/'); 
       end 
end 

The code was included in a MATLAB Analysis that I called “Tide threshold detection”.

React to the Events and Send Alerts

I created two ThingSpeak Reacts to detect each tidal event and to send an alert using ThingTweet every time the tide passed a certain threshold.

I receive a tweet from @TidalAlerts each time the tide level falls below 20 inches or rises above 15 inches.

Next Steps

Next steps include using MATLAB to analyze the tide level data and to perform local tidal predictions. A potential project includes a real-time display of tide levels powered by a NeoPixel LED strip.

Schematics

Tide Sensor
Pic06 1024x625 lec2d0pf7z

Code

Detect Tidal ThresholdsMATLAB
% This code reads the past 30 minutes tide range data from ThingSpeak
% channel 22641. Calculates the median of the tide level 5 minutes ago and
% 25 minutes ago.  It then uses these values to check if either a) the tide
% is going down and has passed a defined 'falling threshold' or b) is going
% up and has passed a defined 'rising threshold'. When either of these
% thresholds is passed the tide height when that happens is written to a
% ThingSpeak Falling Tide or Rising Tide channel as appropriate. The code
% also checks to make sure that sufficient time has past before writing a
% new value to avoid multiple triggers for the same threshold.

% Read the last 30 minutes of tide data
range = thingSpeakRead(22641,'Fields',[1],'NumMinutes',30, ...
    'URL','http://thingspeak.com/');

% Convert range from sensor to water in mm to depth from mud in inches
depth = (3449 - range)/24.5; %depth from mud in inches

% Define threshold level in inches
fallingthreshold = 20;
risingthreshold = 15;

% Define minimum time in minutes between alerts
alertGap = 60; % minutes
alertGapNum = alertGap/60/24; % Convert to datenum

% Set up index to divide data into roughly 10 minute periods
i = round(length(depth)/3);

% Make sure we are working with enough data
if i>8

    % Calculate the median of each 10 minute period
    depth25minago = median(depth(1:i));
    depth5minago = round(median(depth(2*i+1:end)),1);

    % Check time since last change used below to avoid double triggers
    timeNow = datetime('now');


    % Read the last falling time
    [lastFallingHeight,lastFallingTime] = thingSpeakRead(50192,...
        'ReadKey', '9QSZ88F42HE2BA4H',...
        'URL','https://api.thingspeak.com/');
    if isempty(lastFallingTime) % at the start this will be empty
        sinceLastFall = 1;
    else
        sinceLastFall = datenum(timeNow)-datenum(lastFallingTime);
    end
    % Read the last rising time
    [lastRisingHeight,lastRisingTime] = thingSpeakRead(50193,...
        'ReadKey', '9AL2W9R0Y07GFR02',...
        'URL','https://api.thingspeak.com/');
    if isempty(lastRisingTime) % at the start this will be empty
        sinceLastRise = 1;
    else
        sinceLastRise = datenum(timeNow)-datenum(lastRisingTime);
    end

        % If the tide has fallen below the falling threshold write to the
        % ThingSpeak falling tide channel
        if (depth25minago > fallingthreshold)...
            && (depth5minago <= fallingthreshold)...
            && (sinceLastFall > alertGapNum)
            thingSpeakWrite(50192, depth5minago, ...
                'WriteKey', '928PKIJS64PXECSL',...
                'URL','https://api.thingspeak.com/');
        end

        % If the tide has fallen below the rising threshold write to the
        % ThingSpeak rising tide channel
        if (depth25minago < risingthreshold)...
            && (depth5minago >= risingthreshold)...
            && (sinceLastRise > alertGapNum)
            thingSpeakWrite(50193, depth5minago,...
                'WriteKey', 'WYVR5N22N4NABWR6',...
                'URL','https://api.thingspeak.com/');
        end

end

Comments

Similar projects you might like

KITtyBot

Project tutorial by StaffanEk

  • 732 views
  • 0 comments
  • 3 respects

Drive with PID Control on an Arduino Mega 2560

Project tutorial by Team MATLAB Makers

  • 2,749 views
  • 0 comments
  • 6 respects

Helping the Disabled and Chronic Patients battle the heat

Project tutorial by Manan Rai

  • 569 views
  • 0 comments
  • 6 respects

CUTSIE WHUN Version 2 - The Ultimate Balancing Robot

Project in progress by Pigeon-Kicker

  • 219 views
  • 0 comments
  • 2 respects

AutoHome - Internet of Things (IoT) for Home Automation

Project showcase by Team AutoHome

  • 272 views
  • 0 comments
  • 0 respects

Bluetooth MIDI-Operated Antique Reed Organ

Project in progress by Willem Hillier

  • 2,485 views
  • 0 comments
  • 11 respects
Add projectSign up / Login