We have tested the hydrophone we are using so we know that works.
For the receiver we are using a ESP32, a small preamp, 4 1.5 V batteries in a battery pack, and a sealed piezo I found on Etsy that was listed as a hydrophone.
I tried to recreate something similar in Tinkercad to try to make it look cleaner, but the components are not part of the options available, so this is close:
This is the code for the receiver:
#include <LiquidCrystal_I2C.h>
// Define LCD pins
const int lcdColumns = 16;
const int lcdRows = 2;
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);
// Define sound sensor pin
const int soundPin = 36;
// Define Morse code mapping
const char* morseCode[] = {
".-", // A
"-...", // B
"-.-.", // C
"-..", // D
".", // E
"..-.", // F
"--.", // G
"....", // H
"..", // I
".---", // J
"-.-", // K
".-..", // L
"--", // M
"-.", // N
"---", // O
".--.", // P
"--.-", // Q
".-.", // R
"...", // S
"-", // T
"..-", // U
"...-", // V
".--", // W
"-..-", // X
"-.--", // Y
"--.." // Z
};
void setup() {
Serial.begin(115200);
lcd.init();
lcd.backlight();
pinMode(soundPin, INPUT);
}
void loop() {
static unsigned long lastSoundTime = 0;
static String currentSymbol = "";
static String currentMorse = "";
int soundValue = analogRead(soundPin);
unsigned long currentTime = millis();
if (soundValue > 500) { // Adjust threshold based on testing
if (currentTime - lastSoundTime > 1000) { // Long pause - end of letter
processMorse(currentMorse);
currentMorse = "";
}
unsigned long soundDuration = 0;
lastSoundTime = currentTime;
while (analogRead(soundPin) > 500) { // Sound is detected
soundDuration = millis() - currentTime;
}
// Determine if dot or dash based on duration
if (soundDuration < 200) { // Dot threshold (adjust based on testing)
currentMorse += ".";
} else { // Dash threshold
currentMorse += "-";
}
}
if (currentTime - lastSoundTime > 3000 && currentMorse != "") { // End of word
processMorse(currentMorse);
currentMorse = "";
lcd.print(" "); // Space for word separation
}
}
void processMorse(String morse) {
for (int i = 0; i < 26; i++) {
if (morse == morseCode[i]) {
lcd.print((char)(i + 65)); // Display character on LCD
break;
}
}
}
For the transmitter, currently we have just the buzzer that came with our kit providing the morse code tone to be picked up and translated by the receiver.
Here is the Tinkercad version:

These are harder to understand:


And this code would allow us to use buttons to send a message to test:
// Pin definitions
const int buttonPin1 = 1; // Button for "HIGH"
const int buttonPin2 = 2; // Button for "MEDIUM"
const int buttonPin3 = 3; // Button for "LOW"
const int buzzerPin = 12; // Buzzer pin
const int ledPin = 6; // LED pin
// Morse code definitions
const String HIGH_MORSE = ".... .. --. ...."; // Morse for HIGH
const String MEDIUM_MORSE = "-- . -.. .. ..- --"; // Morse for MEDIUM
const String LOW_MORSE = ".-.. --- .--"; // Morse for LOW
// Timing constants
const int dotDuration = 200; // Duration for a dot (milliseconds)
const int dashDuration = 600; // Duration for a dash (milliseconds)
const int letterPause = 600; // Pause between letters (3 dots duration)
void setup() {
pinMode(buttonPin1, INPUT_PULLUP); // Buttons are input with pullup
pinMode(buttonPin2, INPUT_PULLUP);
pinMode(buttonPin3, INPUT_PULLUP);
pinMode(buzzerPin, OUTPUT); // Set buzzer as output
pinMode(ledPin, OUTPUT); // Set LED as output
}
void loop() {
// Check if button 1 (HIGH) is pressed
if (digitalRead(buttonPin1) == LOW) {
playMorseCode(HIGH_MORSE); // Play Morse code for "HIGH"
}
// Check if button 2 (MEDIUM) is pressed
if (digitalRead(buttonPin2) == LOW) {
playMorseCode(MEDIUM_MORSE); // Play Morse code for "MEDIUM"
}
// Check if button 3 (LOW) is pressed
if (digitalRead(buttonPin3) == LOW) {
playMorseCode(LOW_MORSE); // Play Morse code for "LOW"
}
}
// Function to play Morse code using the buzzer and LED
void playMorseCode(String message) {
for (int i = 0; i < message.length(); i++) {
char currentChar = message[i];
if (currentChar == '.') {
// Short beep and blink for dot
tone(buzzerPin, 1000); // Short beep for dot
digitalWrite(ledPin, HIGH); // LED ON for dot
delay(dotDuration); // Dot duration
noTone(buzzerPin); // Turn off buzzer
digitalWrite(ledPin, LOW); // LED OFF for dot
delay(200); // Silence between elements
}
else if (currentChar == '-') {
// Long beep and blink for dash
tone(buzzerPin, 1000); // Long beep for dash
digitalWrite(ledPin, HIGH); // LED ON for dash
delay(dashDuration); // Dash duration
noTone(buzzerPin); // Turn off buzzer
digitalWrite(ledPin, LOW); // LED OFF for dash
delay(200); // Silence between elements
}
else if (currentChar == ' ') {
delay(letterPause); // Space between letters (3 dots duration)
}
}
delay(1000); // Space between different Morse code messages
}