Need bell for ship bell project

Well I have successfully completed my sketch to use my DS1307 RTC to accurately announce ships time (the old 8 bells). I used the buzzer for the development but would like to get a real bell sound. My thoughts were to drive a solenoid to ring a bell (repurposed bike bell was my initial thought), but I am running into issues with this idea in sourcing the proper relay/power supply/ solenoid to achieve this. Does anyone have a related bell ringing circuit to share?

Hello
Take a MP3 Player Shield or a sound chip.

Find a door bell? On that normally rings... it has a bell and a solenoid...

Like this one

I implemented a Ships Clock using the DFPlayer board. and an Arduino. The 8 Bell sounds files (mp3) are put on an SD Card in the DF Player. The Arduino controls the the DF Player. Amazon has 5 DF Players for $14.00.

The Arduino that I used was the NodeMCU ESP8266 that has Wifi Capabilities. The "Time" is sync'ed to a NTP server so it always up to date with the correct time

2 Likes

Yes ...

Hmmm... I had not thought of using a DF player, i have one for another project (Princess cruise sleep to waves and music ap that i was in process of writing). Can you share your sketch? I am using a rt clock to control the time, but would like to see your NTP server ap.

What did you sample for the bell sound files?

1 Like

you should be able to download 8 sound files from a site like soundsnap.com. those files would be put on the df player.

then it is just a matter of selecting the right file for play back at the right time

let me know if you need any help

Here's the made code. I am still adding features and cleaning up stuff, but I have had it fully operational with the basic functional for a while

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

#include <SoftwareSerial.h>

const int intVolume = 3;      // Set volume to maximum (0 to 30).
const int TimeOffset = -5;
int  lastTriggerMin = 99;

const byte CmdPlayNext = 0x01;
const byte CmdPlayPrevious = 0x02;
const byte CmdPlayRootTrack = 0x03;
const byte CmdIncreaseVol = 0x04;
const byte CmdDecreaseVol = 0x05;
const byte CmdSetVolume = 0x06;
const byte CmdSpecifyEQ = 0x07;

const byte CmdSetSleep = 0x0A;
const byte CmdReset = 0x0C;
const byte CmdPlay = 0x0D;
const byte CmdPause = 0x0E;

const byte CmdStop = 0x16;

//Return Code
const byte CmdProcessed = 0x41;


WiFiUDP ntpUDP;
NTPClient ntpClientX(ntpUDP, "192.168.1.1");
ESP8266WebServer server(80);

// Use pins 2 and 3 to communicate with DFPlayer Mini
static const uint8_t PIN_MP3_TX = D2; //
static const uint8_t PIN_MP3_RX = D3; //
SoftwareSerial SoftSerial(PIN_MP3_RX, PIN_MP3_TX);

int idxRec = 0;
const int Rows = 10;
String rec[Rows][4];

void setup() {
  //Init USB serial port for debugging
  Serial.begin(115200);

  InitWiFi();

  //NTP Client
  ntpClientX.setTimeOffset(TimeOffset * 3600);
  ntpClientX.begin();

  //Get the Current time
  ntpClientX.update();

  // Init serial port for DFPlayer Mini
  SoftSerial.begin(9600);

  delay(2000);

  //Clear Buffer
  Serial.print("Clear Buffer ");
  //This will probally result in a  "Serial Error"
  if (SoftSerial.available()) {
    byte rtn[20];
    GetResult(rtn);
  }

  SetVolume(intVolume );
  delay(1000);

  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);
  server.begin();
}

void loop() {
  if (SoftSerial.available()) {
    Serial.print("Main Line Serial... ");
    byte rtn[20];
    GetResult(rtn);
  }

  ntpClientX.update();
  AdjustTime();

  int intCurrMin = ntpClientX.getMinutes();

  // Only process for CurrMin = 0 or 30
  if ((lastTriggerMin != intCurrMin) && (intCurrMin % 30 ) == 0) {
    int intBells = GetBellCount(ntpClientX.getHours(), intCurrMin);

    String tm = ntpClientX.getFormattedTime();
    Serial.print("Current time: ");
    Serial.println(tm);

    Serial.print("--- Bells: ");
    Serial.println(intBells);

    Serial.println(QueryStatus(), HEX);  //Use this to make sure the unit is allve
    delay(100);

    //Play the Bells
    byte rtn = PlayTrack(intBells);
    lastTriggerMin = intCurrMin;

    Serial.print("LastTriggerMin (0 or 30): " );
    Serial.println(lastTriggerMin);
    delay(500);

    rec[idxRec][0] = tm;
    rec[idxRec][1] = String(intBells);
    rec[idxRec][2] = "0x" + String(rtn, HEX);
    //rec[idxRec][3] = String(DP);
    idxRec = (idxRec + 1) % Rows;
  }
  server.handleClient();
}

int GetBellCount(int intHour, int intMinute) {
  //Bells are on a 4 hour cycle
  //Even Number at Hour marks :00
  //Odd Number at 30 Minute mark :30
  //Max 8 Bells at 0, 4, 8, 12, 16, 20 hours.

  if (intHour < 0 || intHour >= 24 )
    return 0;

  if (intMinute % 30 > 0)
    return 0;

  if (intMinute < 0 || intMinute >= 60 )
    return 0;

  int intBells = ((intHour % 4) * 2);  // Top of Hour Calculation.  Always Even Number
  if (intMinute == 30)                 // Bottom of Hour Adjustment
    intBells += 1;

  if (intBells == 0)
    intBells = 8;

  return intBells;
}

byte SetVolume(byte level) {
  Serial.print ("Set Volume: ");
  Serial.println (level);

  if (level > 30)
    level = 30;

  return Send_Cmd(0x6, 0, level);
}

int GetVolume() {
  Serial.println("Get Volume");
  if (Send_Cmd(0x43, 0, 0) == CmdProcessed) {
    byte rtn[20];
    GetResult(rtn);

    return rtn[6];
  }
  return -1;
}

int GetRootFileCnt() {
  Serial.println("GetRootFileCnt");
  if (Send_Cmd(0x48, 0, 0) == CmdProcessed) {
    byte rtn[20];
    GetResult(rtn);

    return (rtn[5] << 8) + rtn[6];
  }
  return -1;
}

byte PlayTrack(byte track) {
  Serial.print ("Play Track: ");
  Serial.println (track);
  byte rtn = Send_Cmd(0x06, 0, track);
  if (rtn != CmdProcessed)  //Try it again
  {
    delay(250);
    rtn = Send_Cmd(0x03, 0, track);
  }
  return rtn;
}

byte QueryStatus() {
  Serial.println ("Query Status: ");
  return Send_Cmd(0x42, 0, 0);
}

byte Send_Cmd(byte cmd, byte param1, byte param2) {
  //Base Array

  byte b[] = {0x7E, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xEF};
  b[0] = 0x7E;  //Start Byte
  b[1] = 0xFF;  //Version. Always FF
  b[2] = 0x6;   //Command Length - Usally 6
  b[3] = cmd;
  b[4] = 1;     //Need Feedback. 0 = No. 1 = Yes.
  b[5] = param1;
  b[6] = param2;
  //Bytes 7 and 8 are Check Sum.
  // Calc Check Sum
  CheckSum(b);
  b[9] = 0xEF;  //End Byte

  // Send data to DF Player
  Serial.print("-> Send: ");
  for (int i = 0; i < 10; i++) {
    SoftSerial.write(b[i]);
    Serial.print(b[i], HEX);
    Serial.print(",");
  }
  Serial.println();

  byte rtn[20];
  return GetResult(rtn);
}

void CheckSum(byte code[]) {
  unsigned int sum = 0;
  for (int i = 1; i < 7; i++)
    sum += code[i];

  sum = 0xFFFF - sum + 1;

  //Serial.println();
  //Serial.print("CheckSum ");
  //Serial.println(sum >> 8, HEX);    //High Order Byte
  //Serial.println(sum & 0xff, HEX);  //Low Order Byte

  code[7] = sum >> 8;   //High Order Byte
  code[8] = sum & 0xFF; //Low Order Byte
}

byte GetResult(byte rtn[]) {
  byte b = 0;
  int idx = 0;

  unsigned long startMillis = millis();
  // 0xEF End Byte
  while ((b != 0xEF) && abs(startMillis - millis()) < 500) {
    if (SoftSerial.available())
    {
      b = SoftSerial.read();
      rtn[idx++] = b;
    }
  }

  ProcessRtn(rtn, idx);
  return rtn[3];
}

byte ProcessRtn(byte b[], int cnt) {
  Serial.print("<- Return: ");
  for (int i = 0; i < cnt ; i++) {
    Serial.print(b[i], HEX);
    Serial.print(",");
  }
  Serial.println();

  switch (b[3]) {
    case 0x40:  //Module Returned an Error
      GetErrorText(b[6]);
      break;
    case 0x41:  //Module Returned Feedback Response
      Serial.println("Cmd Processed");
      break;
    case 0x42:
      Serial.print("Current Status: ");
      switch (b[6]) {
        case 0:
          Serial.println("Stopped");
          break;
        case 1:
          Serial.println("Playing");
          break;
        case 2:
          Serial.println("Paused");
          break;
      }
      break;
    case 0x48:
      Serial.print("Root File Count: ");
      Serial.println((b[5] << 8) + b[6]);
      break;
    case 0x3A:
      if (b[6] = 2)
        Serial.println("SD Card Inserted");
      break;
    case 0x3b:
      if (b[6] = 2)
        Serial.println("SD Card Removed");
      break;
    case 0x3c:
    case 0x3D:
      Serial.println("Track Finished");
      break;
  }
  return b[3];
}

void GetErrorText(byte n) {
  switch (n) {
    case 1:
      Serial.println("Error: Module Busy");
      break;
    case 2:
      Serial.println("Error: Sleep Mode");
      break;
    case 3:
      Serial.println("Error: Serial Error");
      break;
    case 4:
      Serial.println("Error: Checksum error");
      break;
    case 5:
      Serial.println("Error: Checksum error");
      break;
    case 6:
      Serial.println("Error: Specified track is not found");
      break;
    case 7:
      Serial.println("Error: Insertion error");
      break;
    case 8:
      Serial.println("Error: SD card reading failed");
      break;
    case 0xA:
      Serial.println("Error: ntered into sleep mode");
      break;
    default:
      Serial.println("Error: SD card reading failed(");
      break;
  }
}

I do have a "NTP" File at is referenced in some function calls. It handles some UTC offset and automatically handles DST

void AdjustTime(){
    unsigned long epochTime = ntpClientX.getEpochTime();
    
    int offset = (TimeOffset + isDST(epochTime)) * 3600;
    ntpClientX.setTimeOffset(offset);
}


bool isDST(unsigned long epochTime) {
  //Get a time structure
  struct tm *ptm = gmtime ((time_t *) & epochTime);

  int intDay = ptm->tm_mday;
  int intMonth = ptm->tm_mon + 1;
  int dow = ntpClientX.getDay();
  
  //Serial.print(intMonth);
  //Serial.print("/");
  //Serial.println(intDay);

  if (intMonth < 3 || intMonth > 11) return 0;
  if (intMonth > 3 && intMonth < 11) return 1;

  int previousSunday = intDay - dow;
  //In march, we are DST if our previous Sunday was on or after the 8th.
  if (intMonth == 3) return previousSunday >= 8;

  //In November we must be before the first Sunday to be DST.
  //That means the previous Sunday must be before the 1st.
  return previousSunday <= 0;
}

As referenced previously, I am using the ESP8266 Node MCU with built in WiFI and the DF Player Module. I was not able to get the DF Player library working with the arduino, so I put my own code together.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.