Introduction: Audible Telltales for Sailing

Telltales are pieces of string used in sailing to indicate whether there is turbulent or laminar flow across the sail. However, the different colored pieces of yarn attached to each side of the sail are purely visual indicators. These audible telltales are an assistive device that aims to communicate the visual information in an auditory form for both sighted and visually-impaired sailors, such as Pauline.

The device consists of an input system, which reads the motion of the telltale, and an output system, which emits a series of beeps conveying airflow information.

Access to soldering equipment and a 3D printer is required in the fabrication of this device.

Step 1: Bill of Materials

BOM with links and prices

Note: you’ll need 2 sets of all of the following.

Input System

  • Arduino Nano
  • Adafruit perma-proto half-sized breadboard PCB
  • nRF24L01 Wireless Transceiver Module
  • Photo Interrupter
  • Sparkfun Photo Interrupter Breakout Board
  • Arduino Compatible 9V battery pack
  • 9V battery
  • Several lengths of 22 Gauge wire
  • Yarn
  • Neodymium Magnets
  • Epoxy

Output System

  • Arduino Nano
  • Adafruit perma-proto half-sized breadboard PCB
  • nRF24L01 Wireless Transceiver Module
  • Arduino Compatible 9V battery pack
  • 1K Ohm potentiometer
  • 120 Ohm resistor
  • 2N3904 transistor
  • 0.1 uF capacitor
  • Arduino compatible Speaker

GitHub Files

  • All of the code and STL files needed to construct these telltales can be found in this GitHub repo.
  • You'll need two sets of the enclosure, and one of the speaker housing.

Step 2: Tools/Machines/Software Requirements

To program the Arduino you’ll need to download the Arduino IDE. The download link can be found here.

To program the nRF24L01 module, you will need to download its library through the Arduino IDE. Tools > Manage Libraries… > install library RF24

To assemble the electronic components access to basic soldering tools is required. A desoldering pump may also be useful but is not necessary.

To construct the tell-tale frame and speaker case you will need access to a 3D printer.

Step 3: Telltale Hardware

Assemble the circuit according to the diagrams above. The Arduino Nano should be aligned with the top of the protoboard. This allows you to have access to the USB port even after all of the electronics are attached.

In order to avoid shorting the electronics, make sure to cut the traces of the protoboard on the rows which the nRF24 will occupy as shown in the image above.

Otherwise you’ll need jumper cables to connect the nRF24 to the protoboard.

The resistor connection, GND, and 5V wires to the photo interrupter are not depicted. Wire up the photo interrupter as indicated on its breakout board. An image of the breakout board is included.

The circuits for the Right and Left telltales are exactly the same.

Step 4: Telltale Software

Here is the code for the Right telltale. Connect the Right telltale’s nano to your computer, open the Arduino IDE, copy and paste this code into it, and upload it to the board.

/** Program that uses photogate to examine tell-tale
 */

#include <Arduino.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10); // CE, CSN
const byte address[6] = "00010";

//---program consts---

//time
const int string_check_time = 1;
const int flow_check_time = 30;
const int base_delay = 5;
const int flow_check_delay = 0;

const int GATE_PIN = 6;
const int GATE_PIN_2 = 7;

const int max_when_testing = flow_check_time * 0.6; 
//set the var above based on your own experimental trials


const int max_in_flow = min(max_when_testing, int(flow_check_time/string_check_time));
const int msg_max_val = 9;

//const int string_thresh = 20;
#define STRING_THRESH 0.2

//---program vars---
int num_string_seen = 0;
int num_loops = 0;

void setup() {
  //while(!Serial); // for flora
  //delay(500);
  
  
  num_string_seen = 0;
  num_loops = 0;

  pinMode(GATE_PIN, INPUT);
  pinMode(GATE_PIN_2, INPUT);
  Serial.begin(115200);       // for debugging
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
  
}

void loop() {
  // put your main code here, to run repeatedly:

  if(num_loops % string_check_time == 0){
    //check string state
    check_string();
  }

  if(num_loops == flow_check_time){
    //examine flow
    //Serial.println(num_string_seen);
    
    int flow_num = examine_flow();
    
    //send values
    send_out(flow_num);
    
    //reset vars
    num_string_seen = 0;
    num_loops = 0;
    delay(flow_check_delay);
    
  }
  num_loops++;
  delay(base_delay);
 }


/*
 *Method to check if string crosses gate 
 */
void check_string(){
  int string_state = digitalRead(GATE_PIN);
  //Serial.println(string_state);
  
  if (string_state == 0){
    num_string_seen++;
    //Serial.println("Saw string!");
    
  }</p><p>  int bot_state = digitalRead(GATE_PIN_2);
  if (bot_state == 0){
    num_string_seen--;
    //Serial.println("string on bottom!");
    
  }
  
  //Serial.print("Counting string passes: ");
  //Serial.println(num_string_seen);
  
  return;
}

/*
 * Method to analyze what fraction of time string covered the gate
 */
int examine_flow(){
  double percent_seen = double(num_string_seen)/max_in_flow;
  Serial.print("Percent covered: ");
  printDouble(percent_seen, 100);

   //scale the value to communication scale
  int scaled_flow = int(percent_seen * msg_max_val);

  if(scaled_flow > msg_max_val){
    scaled_flow = msg_max_val;
  }
  if(scaled_flow < 0){
    scaled_flow = 0;
  }
  return scaled_flow;
  
}

/*
 * Method to send results to receiving device
 */
void send_out(int msg){
  Serial.println(msg); //for debugging
 
  //send over radio
  char the_num = msg + '0';
  char text[] = {the_num};
  
    
  Serial.print("text is : ");
  Serial.println(text[0]);
  
  //Serial.println("trying to send");
  
  radio.write(&text, sizeof(text));
  
  //Serial.println("sent");
   
  return;
  
}

void printDouble( double val, unsigned int precision){
// prints val with number of decimal places determine by precision
// NOTE: precision is 1 followed by the number of zeros for the desired number of decimal places
// example: printDouble( 3.1415, 100); // prints 3.14 (two decimal places)

  Serial.print (int(val));  //prints the int part
   Serial.print("."); // print the decimal point
   unsigned int frac;
   if(val >= 0)
       frac = (val - int(val)) * precision;
   else
       frac = (int(val)- val ) * precision;
   Serial.println(frac,DEC) ;
}

Here is the code for the Left telltale. Follow the same steps as above for the Left telltale. As you can see, the only difference is the address to which the telltale sends its results.

/** Program that uses photogate to examine tell-tale

 */

#include <Arduino.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10); // CE, CSN
const byte address[6] = "00001";

//---program consts---

//time
const int string_check_time = 1;
const int flow_check_time = 30;
const int base_delay = 5;
const int flow_check_delay = 0;

const int GATE_PIN = 6;
const int GATE_PIN_2 = 7;

const int max_when_testing = flow_check_time * 0.6; 
//set the var above based on your own experimental trials

const int max_in_flow = min(max_when_testing, int(flow_check_time/string_check_time));
const int msg_max_val = 9;

//const int string_thresh = 20;
#define STRING_THRESH 0.2

//---program vars---
int num_string_seen = 0;
int num_loops = 0;

void setup() {
  //while(!Serial); // for flora
  //delay(500);
    
  num_string_seen = 0;
  num_loops = 0;</p><p>  pinMode(GATE_PIN, INPUT);
  pinMode(GATE_PIN_2, INPUT);
  Serial.begin(115200);       // for debugging
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
  
}void loop() {
  // put your main code here, to run repeatedly:

  if(num_loops % string_check_time == 0){
    //check string state
    check_string();
  }

 if(num_loops == flow_check_time){
    //examine flow
    //Serial.println(num_string_seen);
    
    int flow_num = examine_flow();
    
    //send values
    send_out(flow_num);
    
    //reset vars
    num_string_seen = 0;
    num_loops = 0;
    delay(flow_check_delay);
    
  }
  num_loops++;
  delay(base_delay);
 }

/*
 *Method to check if string crosses gate 
 */
void check_string(){
  int string_state = digitalRead(GATE_PIN);
  //Serial.println(string_state);
  
  if (string_state == 0){
    num_string_seen++;
    //Serial.println("Saw string!");
    
  }</p><p>  int bot_state = digitalRead(GATE_PIN_2);
  if (bot_state == 0){
    num_string_seen--;
    //Serial.println("string on bottom!");
    
  }
  
  //Serial.print("Counting string passes: ");
  //Serial.println(num_string_seen);
  
  return;
}

/*
 * Method to analyze what fraction of time string covered the gate
 */
int examine_flow(){
  double percent_seen = double(num_string_seen)/max_in_flow;
  Serial.print("Percent covered: ");
  printDouble(percent_seen, 100);

   //scale the value to communication scale
  int scaled_flow = int(percent_seen * msg_max_val);

  if(scaled_flow > msg_max_val){
    scaled_flow = msg_max_val;
  }
  if(scaled_flow < 0){
    scaled_flow = 0;
  }
  return scaled_flow;
  
}

/*
 * Method to send results to receiving device
 */
void send_out(int msg){
  Serial.println(msg); //for debugging
 
  //send over radio
  char the_num = msg + '0';
  char text[] = {the_num};
  
    
  Serial.print("text is : ");
  Serial.println(text[0]);
  
  //Serial.println("trying to send");
  
  radio.write(&text, sizeof(text));
  
  //Serial.println("sent");
   
  return;
  
}

void printDouble( double val, unsigned int precision){
// prints val with number of decimal places determine by precision
// NOTE: precision is 1 followed by the number of zeros for the desired number of decimal places
// example: printDouble( 3.1415, 100); // prints 3.14 (two decimal places)

 Serial.print (int(val));  //prints the int part
   Serial.print("."); // print the decimal point
   unsigned int frac;
   if(val >= 0)
       frac = (val - int(val)) * precision;
   else
       frac = (int(val)- val ) * precision;
   Serial.println(frac,DEC) ;
}

Step 5: Telltale Assembly

Individual Parts

  • Telltale frame
  • Yarn
  • Constructed telltale circuit
  • Battery pack
  • Electrical tape
  • Epoxy or glue

STLs for 3D printing telltale components

Assembly Instructions

  1. Place bar magnets in the slots of the 3D printed telltale frame. Make sure the magnets line up properly between the right frame and the left frame, then use epoxy (or glue) to secure the magnets to the frame. Allow epoxy (or glue) to set completely.
  2. Place the photo interrupters in the top and bottom slots at the back of the frame. Carefully epoxy (or glue) the photo interrupter boards to the frame. Allow epoxy (or glue) to set completely
  3. Cut a ~7 in piece of yarn. Tie one end of the yarn at the notch of the first vertical bar. Cut a small piece of electrical tape and wrap the electrical tape over the section of the yarn that will be in the region of the photo interrupters. Thread the yarn through the frame so that it passes through the gap of the photo interrupter gate.
  4. Place bar magnets in the slots of the 3D printed electronics box bottom. Make sure the magnets line up properly between the right box and the left box, then use epoxy (or glue) to secure the magnets to the frame. Allow epoxy (or glue) to set completely.
  5. Place the constructed telltale circuit in the electronics box, aligning the different components to their slots. Close the box with the 3D printed electronics box top. Epoxy (or glue) the battery pack to the box top so that the switch is exposed.

Step 6: Speaker Hardware

The output system consists of two speaker circuits, one for each telltale, equipped with wireless communication and a volume adjustment knob. First, prepare the protoboards for use with the nRF24L01 modules like we did for the telltale circuits by cutting the leads separating the two rows of pins where the board will be placed.

Then, assemble the circuit as shown in the diagram above while referring to the photos of the completed circuits.

Board Assembly Instructions

In order to stack the boards in the speaker enclosure, the main components must be placed in certain areas of the board. In the following instructions, I will be referring to the coordinate system used to denote rows and columns on the Adafruit protoboard:

  1. The Arduino Nano must be placed against the upper edge of the board in the center so that the Vin pin is positioned at G16. This will allow easy reprogramming of the Arduino Nano after the circuit has been assembled.
  2. The nRF24L01 board must be placed in the lower right corner of the board spanning the eight positions from C1 to D5. This will leave the nRF24L01 hanging off of the protoboard to allow for better wireless communication.
  3. The battery pack for the speaker system powers both protoboards, so be sure to connect the two Arduino Nano's GND rails/pins and Vin pins to the power supply.
  4. For the ‘bottom’ circuit, the potentiometer should be placed on the top of the board facing outwards so that its pins are placed at positions J2, J4, and J6
    1. J2 ↔ Arduino Nano output from digital pin 3 (D3)
    2. J4 ↔ base pin of 2N3904 transistor
    3. J6 ↔ unconnected
  5. For the ‘top’ circuit, the potentiometer should be placed on the bottom of the board facing outwards so that its pins are placed at positions J9, J11, and J13
    1. J13 ↔ Arduino Nano output from digital pin 3 (D3)
    2. J11 ↔ base pin of 2N3904 transistor
    3. J9 ↔ unconnected

Step 7: Speaker Software

Here is the code for the speaker communicating with the Left telltale. Connect the Arduino Nano on the bottom speaker board to your computer, open the Arduino IDE, copy and paste this code into it, and upload it to the board.

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7,8); // CE, CSN

//left telltale, top speaker board
const byte address[6] = "00001";
const int pitch = 2000;
const int pitch_duration = 200;
const int speaker = 3;
const int delay_gain = 100;

int status = 0;
int cur_delay = 0;
char read[2];

void setup() {
  pinMode(speaker, OUTPUT);
  Serial.begin(115200);
  Serial.println("Starting wireless communication...");
  radio.begin();
  radio.openReadingPipe(0,address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
}

void loop() {
  if(radio.available()) {
    radio.read(&read, sizeof(read));
    status = (int)(read[0]-'0');
    Serial.print("Received: ");
    Serial.println(status);
    cur_delay = delay_gain*status;
  }

  if (cur_delay) {
    tone(speaker, pitch, pitch_duration);
    delay(cur_delay + pitch_duration);
    Serial.println("Beep!");
  }
}

Here is the code for the speaker communicating with the right telltale. Connect the Arduino Nano on the top speaker board to your computer, open the Arduino IDE, copy and paste this code into it, and upload it to the board.

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7, 8); // CE, CSN

//right telltale, bottom speaker board
const byte address[6] = "00010";
const int pitch = 1500;
const int pitch_duration = 200;
const int speaker = 3;
const int delay_gain = 100;

int status = 0;
int cur_delay = 0;
char read[2];

void setup() {
  pinMode(speaker, OUTPUT);
  Serial.begin(115200);
  Serial.println("Starting wireless communication...");
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
}

void loop() {
  if (radio.available()) {
    radio.read(&read, sizeof(read));
    status = (int)(read[0]-'0');
    Serial.print("Received: ");
    Serial.println(status);
    cur_delay = delay_gain*status;
  }

  if (cur_delay) {
    tone(speaker, pitch, pitch_duration);
    delay(cur_delay+pitch_duration);
    Serial.println("Beep!");
  }
}

Step 8: Speaker Assembly

Individual Parts

  • 2 constructed speaker circuits
  • 2 speakers
  • 1 battery pack

STLs for 3D printing

Physical Assembly Instructions

  1. Carefully place the speaker circuits into the bottom of the box, one board on top of the other such that the volume knobs are next to each other and slip into the holes. The communication chips should be exposed at the back of the box.
  2. Place the speakers on the left and right of the circuit board, making sure the speakers correspond to the correct telltale sides. Align the speakers to the slots on the sides of the box.
  3. Feed the battery pack wires through the small hole at the back off the box. Epoxy (or glue) the battery pack to the back of the box such that the switch is exposed.
  4. Place 3D printed box top over the box bottom to contain everything.

Step 9: Setup/Mounting

  1. Turn on the telltales by flipping the switches on the battery packs to the 'ON' position. Do the same for the speaker assembly in order to turn the output system on.
  2. Mounting audible telltales is most easily done with two people, but can be done with one. For mounting on a non-furling jib, the telltales would most easily be put on before hoisting the sail.
  3. To make sure the tell tale frame is oriented correctly, look at the notch on one of the vertical bars. When holding the frame upright, the notch should be towards the top. The side of the frame with that bar should also face towards the front of the boat.
  4. Place one of the tell tales at the desired height and position on the sail. It should be placed such that the yarn is in the same place it would be if it were part of a traditional tell tale.
  5. Once you have one tell tale in the desired position. Place the other tell tale on the other side of the sail, exactly opposite the first one you placed, such that the magnets line up.Once the magnets make a connection they should hold the frame securely to the sail. Line up the magnets of the electronics enclosures, for each tell tale on either side of the sail, such that they connect as well.
  6. If you notice that when the string flows straight back it doesn’t cross in front of the top gate, rotate the tell tale frame such that the back half of the frame heads downward. Rotate the frame until the string passes through the top photo interrupter when the yarn flows straight back.

Step 10: Troubleshooting

All pieces of code have debug print statements to indicate that they are sending, receiving, and processing data. Opening the COM port using the Arduino IDE with one of the Arduino Nano subsystems plugged into a computer will allow you to view these status messages.

If the system is not operating properly, toggle the switches on all of the components.

Step 11: Possible Next Steps

  • Waterproofing
  • Longer range communication. WiFi be a promising option.
  • Our current setup currently uses 2 photo interrupters per telltale. Adding more photo interrupters to the system could be interesting to try.