My project is a church clock (already operational for years , that i have now been asked to modfiy it by adding the options to play a set of chimes for either wedding, funeral or general sevrvice, this has been complicated by a request to include a line input section and later bluetooth (just to make use of amplifier at other times.
My problem is , i last coded an arduino in 2015 and then as a beginner, (still at that stage now but learning by doing , so my knowledge is still limited)
The problem i have is in trying to monitor the line input for audio (or lack of) so that i can give back amplifier control to either the hourly clock chime or the bells selections (these sections all function (may not be the best coded ,happy for any advice on it).
the code i tried uses a dc bias simple circuit of a voltage divder (2 x 10k resistors) and a 1mf capacitor to feed the audio in. I realise after testing the code seprately that its demo code, is there a better way to write it, or another method i could try to acheive what i need. As it is now if line input is selected i cant figure a way to disconnect it while it is possibly still in use.
on the seperate test i can get readings in serial monitor but they dont go to 512 (approx) on silence , I am using a cd player line sockets for the audio feed which should be at least 1v p-p.
```cpp
/***********************************************************/
//church bells control 08/04/2024 part of clock / bells / bt & line in system
// Hardware: yx5300, relays 2 x dpdt, plus 1 x amplifier control relay, 5 puhsbuttons,
// Board: Arduino Nano x2 in finshed version.
//
#include <SoftwareSerial.h>
#define ARDUINO_RX 5 //should connect to TX of the Serial MP3 Player module
#define ARDUINO_TX 6 //connect to RX of the module
SoftwareSerial mySerial(ARDUINO_RX, ARDUINO_TX);
static int8_t Send_buf[8] = { 0 };
static uint8_t ansbuf[10] = { 0 }; // Buffer for the answers. // BETTER LOCALLY
String mySerialAnswer; // Answer from the MP3.
/************ Command byte **************************/
#define CMD_NEXT_SONG 0X01 // Play next song.
#define CMD_PREV_SONG 0X02 // Play previous song.
#define CMD_PLAY_W_INDEX 0X03
#define CMD_VOLUME_UP 0X04
#define CMD_VOLUME_DOWN 0X05
#define CMD_SET_VOLUME 0X06
#define CMD_SNG_CYCL_PLAY 0X08 // Single Cycle Play.
#define CMD_SEL_DEV 0X09
#define CMD_SLEEP_MODE 0X0A
#define CMD_WAKE_UP 0X0B
#define CMD_RESET 0X0C
#define CMD_PLAY 0X0D
#define CMD_PAUSE 0X0E
#define CMD_PLAY_FOLDER_FILE 0X0F
#define CMD_STOP_PLAY 0X16
#define CMD_FOLDER_CYCLE 0X17
#define CMD_SHUFFLE_PLAY 0x18 //
#define CMD_SET_SNGL_CYCL 0X19 // Set single cycle.
#define CMD_SET_DAC 0X1A
#define DAC_ON 0X00
#define DAC_OFF 0X01
#define CMD_SEL_DEV 0X09
#define DEV_TF 0X02
#define CMD_PLAY_W_VOL 0X22
#define CMD_PLAYING_N 0x4C
#define CMD_QUERY_STATUS 0x42
#define CMD_QUERY_VOLUME 0x43
#define CMD_QUERY_FLDR_TRACKS 0x4e
#define CMD_QUERY_TOT_TRACKS 0x48
#define CMD_QUERY_FLDR_COUNT 0x4f
const int clkChime = 2; // clock chime amp trigger set
const int lineRelay = 3; // line input switching relay
const int switch4Pin = 4; // line input selector switch
const int switch1Pin = 7; // S1 switch connected to digital pin 7
const int switch2Pin = 8; // S2 switch connected to digital pin 8
const int switch3Pin = 9; // S3 switch connected to digital pin 9
const int ampMute = 10; // amp mute relay control
const int relaycontrol = 11; // clock / bells audio switch relay
const int w1Led = A1; // A1 to A4 button leds
const int w2Led = A2; // A1 to A4 button leds
const int w3Led = A3; // A1 to A4 button leds
const int w4Led = A4; // A1 to A4 button leds
bool state = true;
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 200;
// analogue sense init.
// Global Variables
int Bias = 512; //Nominally half of the 1023 range = 512. Adjust as necessary
int NoiseThreshold = 6; //Increase to minimize false triggers with silence or background noise
int Analog; // The analog reading (with the bias subtracted-out)
int Max; // The waveform peak over a short period of time
int Average; // A "sampled" moving average of Max values used as the LED-on threshold (not the true-average).
unsigned long ReadStartTime; //millis() used to set time
int SampleTime = 50; //Sample for short time to find waveform peak
unsigned long ArrayStartTime; //millis() again
int ArrayTime = 1000; //Update array once per second
const int ArraySize = 20; // 20 values in the moving-average array
int ArrayIndex = 0; // Index/pointer
int AverageArray[ArraySize]; // Saves a sample of 20 Max values for averaging
//Function prototype
int UpdateAverage(int Max); //UpdateAverage() function prototype
// end of analogue sense init
void setup() {
pinMode(switch1Pin, INPUT_PULLUP); // Set the S1 switch pin as input with internal pull-up resistor enabled
pinMode(switch2Pin, INPUT_PULLUP); // Set the S2 switch pin as input with internal pull-up resistor enabled
pinMode(switch3Pin, INPUT_PULLUP); // Set the S3 switch pin as input with internal pull-up resistor enabled
pinMode(switch4Pin, INPUT_PULLUP); // Set the S4 switch pin as input with internal pull-up resistor enabled
pinMode(relaycontrol, OUTPUT); // Set the relaycontrol pin as output
pinMode(lineRelay, OUTPUT); // Set the line in sellector relay pin as output
pinMode(clkChime, INPUT_PULLUP); // Set the clock module amp in pin as input
pinMode(ampMute, OUTPUT);
digitalWrite(relaycontrol, HIGH);
pinMode(w1Led, OUTPUT); // Set the switch LED as output
pinMode(w2Led, OUTPUT); // Set the switch LED as output
pinMode(w3Led, OUTPUT); // Set the switch LED as output
pinMode(w4Led, OUTPUT); // Set the switch LED as output
digitalWrite(w1Led, LOW); // Set the switch LED off initially
digitalWrite(w2Led, LOW); // Set the switch LED off initially
digitalWrite(w3Led, LOW); // Set the switch LED off initially
digitalWrite(w4Led, LOW); // Set the switch LED off initially
digitalWrite(lineRelay, HIGH); //initial state of line in relay output
mySerial.begin(9600);
Serial.begin(9600);
delay(500); //Wait chip initialization is complete
sendCommand(CMD_SEL_DEV, DEV_TF); //select the TF card
delay(500); //wait for 200ms
for (int i = 0; i < 20; i++)
AverageArray[i] = 0; //initialize the array with zeros
}
void loop() {
int switch1State = digitalRead(switch1Pin); // Read the state of S1 switch
int switch2State = digitalRead(switch2Pin); // Read the state of S2 switch
int switch3State = digitalRead(switch3Pin); // Read the state of S3 switch
int switch4State = digitalRead(switch4Pin); // Read the state of S3 switch
int lineInState = digitalRead(lineRelay); // Read the state of line input relay output
int relayState = digitalRead(relaycontrol); // read state of main amp / audio path relay
unsigned long currentMillis = millis();
if (switch1State == LOW && (relayState == 1)) { // Check if S1 switch is pressed and if relaycontrol is high
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
digitalWrite(lineRelay, HIGH);
digitalWrite(relaycontrol, LOW);
switchLed1();
if (currentMillis - previousMillis >= interval)
previousMillis = currentMillis;
}
sendCommand(CMD_PLAY_W_VOL, 0X1E01); //play the frirst track with volume 30 class
}
if (switch2State == LOW && (relayState == 1)) { // Check if S2 switch is pressed and if relaycontrol is high
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
digitalWrite(lineRelay, HIGH);
digitalWrite(relaycontrol, LOW);
switchLed2();
if (currentMillis - previousMillis >= interval)
previousMillis = currentMillis;
}
sendCommand(CMD_PLAY_W_VOL, 0X1E02); //play the second track withme 30 class
}
if (switch3State == LOW && (relayState == 1)) { // Check if S3 switch is pressed and if relaycontrol is high
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
digitalWrite(lineRelay, HIGH);
digitalWrite(relaycontrol, LOW);
switchLed3();
if (currentMillis - previousMillis >= interval)
previousMillis = currentMillis;
}
sendCommand(CMD_PLAY_W_VOL, 0X1E03); //play the second track with volume 30 class
}
if (switch4State == LOW && (relayState == 1)) { // Check if S4 switch is pressed and if relaycontrol is high
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
digitalWrite(relaycontrol, HIGH);

digitalWrite(lineRelay, LOW); //line in relay on
switchLed4();
if (currentMillis - previousMillis >= interval)
previousMillis = currentMillis;
}
sendCommand(CMD_PAUSE, 0);
}
ampControl();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
decodemySerialAnswer();
if (state == 0) {
digitalWrite(relaycontrol, HIGH);
digitalWrite(lineRelay, HIGH);
state = 1;
switchLedclear();
// line in analogue sense (silence)
Max = 0;
//Initilize/reset before running while() loop
ReadStartTime = millis(); //Save/update loop-starting time
while (millis() - ReadStartTime < SampleTime) {
Analog = abs(analogRead(A0) - Bias); // Read, take out the 2.5V bias/offset, make positive.
if (Analog > Max) //Find Max before exiting loop
Max = Analog;
} // End waveform peak loop
if (Average < NoiseThreshold) // Make sure LED is off with silence or background noise
Average = NoiseThreshold;
if(Max > Average) //If the latest Max reading is > average, turn-on the LED
digitalWrite(LED_BUILTIN, HIGH); //Turn the LED on
else
digitalWrite(LED_BUILTIN, LOW); //Turn the LED off
if (millis() - ArrayStartTime > ArrayTime) // Time to update array?
Average = UpdateAverage(Max); // Call the UpdateAverage() function once per second
int lineInState = digitalRead(lineRelay);
if ((Average >= 20 ) && (lineInState == LOW))
digitalWrite(lineRelay, HIGH);
digitalWrite(ampMute, LOW); //Turn the relay off to test , will be other way around when corredct relay used
//digitalWrite(LED_BUILTIN, HIGH); //Turn the LED off
// end analogue sense
}
}
}
void sendCommand(int8_t command, int16_t dat) {
delay(20);
Send_buf[0] = 0x7e; //starting byte
Send_buf[1] = 0xff; //version
Send_buf[2] = 0x06; //the number of bytes of the command without starting byte and ending byte
Send_buf[3] = command; //
Send_buf[4] = 0x01; //0x00 = no feedback, 0x01 = feedback
Send_buf[5] = (int8_t)(dat >> 8); //datah
Send_buf[6] = (int8_t)(dat); //datal
Send_buf[7] = 0xef; //ending byte
for (uint8_t i = 0; i < 8; i++) //
{
mySerial.write(Send_buf[i]);
}
}
/*Function decodeMP3Answer: Decode MP3 answer. */
/*Parameter:-void */
/*Return: The */
String decodemySerialAnswer() {
String decodedmySerialAnswer = "";
decodedmySerialAnswer += sanswer();
switch (ansbuf[3]) {
case 0x3D:
decodedmySerialAnswer += " -> Completed play num " + String(ansbuf[6], DEC);
if ((ansbuf[3] == 0x3D))
;
{
state = false; // flag for end of track
break;
}
case 0x4C:
decodedmySerialAnswer += " -> Playing: " + String(ansbuf[6], DEC);
break;
}
return decodedmySerialAnswer;
}
/*Function: sbyte2hex. Returns a byte data in HEX format. */
/*Parameter:- uint8_t b. Byte to convert to HEX. */
/*Return: String */
String sbyte2hex(uint8_t b) {
String shex;
shex = "0X";
if (b < 16) shex += "0";
shex += String(b, HEX);
shex += " ";
return shex;
}
/*Function: sanswer. Returns a String answer from mp3 UART module. */
/*Parameter:- uint8_t b. void. */
/*Return: String. If the answer is well formated answer. */
String sanswer(void) {
uint8_t i = 0;
String mySerialAnsweranswer = "";
// Get only 10 Bytes
while (mySerial.available() && (i < 10)) {
uint8_t b = mySerial.read();
ansbuf[i] = b;
i++;
mySerialAnswer += sbyte2hex(b);
}
// if the answer format is correct.
if ((ansbuf[0] == 0x7E) && (ansbuf[9] == 0xEF)) {
return mySerialAnswer;
}
return "???: " + mySerialAnswer;
}
//analogues sense
// UpdateAverage() function =================================================================================
// Takes-in latest Max reading, updates the array, returns the average
int UpdateAverage(int Max) {
int Sum = 0; // Initialize/reset Sum before calculating average
AverageArray[ArrayIndex] = Max; //Update one array element with the latest Max
for (int i = 0; i < 20; i++) //Sum-up the data in the array
{
Sum = Sum + AverageArray[i];
}
Average = Sum / 20; // Find average of the saved Max values
ArrayIndex++; //Next index for next time
if (ArrayIndex > 19)
ArrayIndex = 0; //Back to the beginning of the circuar buffer
ArrayStartTime = millis(); //New Array Start Time
return Average;
}
// end analogue sense
void switchLed1() {
digitalWrite(w1Led, HIGH);
digitalWrite(w2Led, LOW);
digitalWrite(w3Led, LOW);
digitalWrite(w4Led, LOW);
}
void switchLed2() {
digitalWrite(w1Led, LOW);
digitalWrite(w2Led, HIGH);
digitalWrite(w3Led, LOW);
digitalWrite(w4Led, LOW);
}
void switchLed3() {
digitalWrite(w1Led, LOW);
digitalWrite(w2Led, LOW);
digitalWrite(w3Led, HIGH);
digitalWrite(w4Led, LOW);
}
void switchLed4() {
digitalWrite(w1Led, LOW);
digitalWrite(w2Led, LOW);
digitalWrite(w3Led, LOW);
digitalWrite(w4Led, HIGH);
}
void switchLedclear() {
digitalWrite(w1Led, LOW);
digitalWrite(w2Led, LOW);
digitalWrite(w3Led, LOW);
digitalWrite(w4Led, LOW);
}
void ampControl() {
int relayState2 = digitalRead(relaycontrol);
int lineInState2 = digitalRead(lineRelay);
int clkChime2 = digitalRead(clkChime);
if (relayState2 == LOW || lineInState2 == LOW || clkChime2 == HIGH) {
digitalWrite(ampMute, HIGH); // nb reverse logic for test with active high relay
} else {
digitalWrite(ampMute, LOW);
}
}