Arduino MP3 Pause after 10 seconds - arduino

I am working on an Halloween prop and would like to play an MP3 file for about 10 seconds and then pause. As of now, the file plays, but it never pauses and just plays the whole MP3 file. I have the code below as basic where the setup is just starting the function. I am sure I am missing the HEX code and doing it wrong. Any assistance would be greatly appreciated.
#include <SoftwareSerial.h>
#define ARDUINO_RX 3//should connect to TX of the Serial MP3 Player module
#define ARDUINO_TX 2//connect to RX of the module
SoftwareSerial mySerial(ARDUINO_RX, ARDUINO_TX);
static int8_t Send_buf[8] = {0} ;
#define CMD_PLAY_W_INDEX 0X03
#define CMD_SET_VOLUME 0X06
#define CMD_SEL_DEV 0X09
#define DEV_TF 0X02
#define CMD_PLAY 0X0D
#define CMD_PAUSE 0X0E
#define CMD_SINGLE_CYCLE 0X19
#define SINGLE_CYCLE_ON 0X00
#define SINGLE_CYCLE_OFF 0X01
#define CMD_PLAY_W_VOL 0X22
unsigned long PauseMusic;
void setup() {
mySerial.begin(9600);
delay(500);//Wait chip initialization is complete
sendCommand(CMD_SEL_DEV, DEV_TF);//select the TF card
delay(200);//wait for 200ms
//timer settings
PauseMusic = millis() + 10000;
AudioPlay();
}
void loop()
{
}
void AudioPlay()
{
if (millis() <= PauseMusic) {
sendCommand(CMD_PLAY_W_VOL, 0X0F01);//play the first song with volume 15 class
} else {
sendCommand(CMD_PAUSE, 0);
}
}
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] = 0x00;//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]) ;
}
}

I do not recommend calling the AudioPlay() function inside the setup() function. The way you are implementing it will not update the elapsed time, as you are executing it only ONCE. Therefore it will never be paused. This is because at run time millis() will invariably be lower than the value from pauseMusic and the conditional clause to pause the music (CMD_PAUSE) won't be satisfied.
if (millis() <= PauseMusic) {
sendCommand(CMD_PLAY_W_VOL, 0X0F01);//play the first song with volume 15 class
} else {
sendCommand(CMD_PAUSE, 0);
}
I would rather set a couple of boolean flags:
bool playDone = false and bool musicPlaying = false, where the first stops the execution of the whole routine while the second tells the controller to send the command to play music only once.
then, the AudioPlay function can be rewritten as:
bool AudioPlay(unsigned long t)
{
// Send play command only ONCE
if (musicPlaying == false) {
sendCommand(CMD_PLAY_W_VOL, 0X0F01);//play the first song with volume 15 class
musicPlaying = true;
}
// Once we reach the 10 seconds mark, pause it
if (t >= PauseMusic){
sendCommand(CMD_PAUSE, 0);
return true; // here the execution and forces the loop to exit
}
return false;
}
then in the main loop() I force the function to run only for the specified time:
void loop(){
while(playDone == false){
unsigned long now = millis();
playDone = AudioPlay(now);
}
} // end of loop
EXTRA thought:
You could also work with elapsed times, as follows:
unsigned long elapsed;
unsigned long startTime;
void setup(){
// initial declarations
startTime = millis();
}
and inside your main loop() you work with elapsed times like:
void loop (){
elapsed = millis() - startTime();
// pass elapsed to my proposed AudioPlay function
}
}
The advantage of using elapsed timings is that you can always restart execution by resetting the value of the startTime variable and the boolean flags.
I have not tested this but I believe that it will work, however, I do not know your whole hardware configuration. It's worth giving it a try

Related

I am creating this temp monitoring system

I am creating this temp monitoring system...Therefore, i want to get messages/alerts when the temperature are below 6 and again when they come back to above 6. Note: I don't want the alert (sendMailNormalValue():wink: to come when the system boots up....How do I deactivate the sendMailNormalValue(); and activate it only when the temp are below 6 (only to alert me when comes back to above 6)..
#include <OneWire.h>
#include <DallasTemperature.h>
// GPIO where the DS18B20 is connected to
const int oneWireBus = 5;
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
//========================================
float t;
int period = 30000;
unsigned long time_now = 0;
bool DisplayFirstTime = true;
int period1 = 30000;
unsigned long time_now1 = 0;
void sendMailBelowValue(){
}
void sendMailNormalValue(){
}
void setup() {
// put your setup code here, to run once:
Serial.begin (9600);
}
void loop() {
// put your main code here, to run repeatedly:
sensors.requestTemperatures();
t = sensors.getTempCByIndex(0);
float p=t-2;
// Check if any reads failed and exit early (to try again).
if (isnan(t)) {
Serial.println("Failed to read from sensor !");
delay(500);
return;
}
if (t>6){
if(millis() > time_now + period){
time_now = millis();
sendMailNormalValue();
Serial.print ("You got messagae");
}
}
if (t<6){
if(millis() > time_now1 + period1 || DisplayFirstTime){
DisplayFirstTime = false;
time_now = millis();
sendMailBelowValue();
}
}
}
I think I understand what you mean. On error (temp < 6), you want to send an email every 30 seconds; when the teperature reaches 6 or mode, send a single email to say the error condition has been fixed. On boot, only send an email if in error.
If that's it, you'll need to keep track of the error condition using a global flag.
// ...
bool tempError; // initialized in setup()
void setup()
{
// ...
float t;
for(;;) // loop until we get a valid reading.
{
t = sensors.getTempCByIndex(0);
if (!isnan(t))
break;
Serial.println("Failed to read from sensor !");
delay(500);
}
tempError = (t < 6);
}
void loop()
{
// read temp and check sensor...
// ...
if (t < 6)
{
tempError = true;
// send error email, set timer, etc...
}
else if (tempError)
{
// temp is now fine, after being too low.
tempError = false; // Clear error flag.
// send OK email, only once.
// Don't forget to clear the 30 second email timer !!!
}
}
Usually, you'd want some hysteresis in an alert system. You should consider waiting some seconds after the temperature has been 'fixed' before sending the 'OK' email. Otherwise you may end up getting lots of fail/fixed emails when the temperature is around 6 degrees. This is usually done with a simple state machine engine, but that is a bit beyond the scope of this questkon.

Arduino AccelStepper Library: Instant speed never reaches set speed

I am trying to develop a arduino code which runs a stepper motor with C# program via serial communication. I also use Accelstepper library, especially moveTo() and run() functions. I sent maxSpeed and step values as 3500 and 200.000 from C# and motor start to run immediately. I sure that it completes all steps, but after a while, I noticed that stepper motor never reaches its max Speed and it stuck at 3200-3300 range. So because of that finish time is increased. If I give steps more than 200.000, the gap between estimated finish time and real finish time is increased exponentially. If I sent speed as 1000, real speed more or less 970. I have to use acceleration function by the reason of needed torque. Then I search the problem and some people said that it occurs because of Accelstepper library which consist run() function and other stuff that I wrote in the loop section. Especially I could not ensure the reason of the problem is Arduino, AccelStepper library or code that I wrote. Can you please help me to solve problem?
NOTE: Arduino Mega 2560 is used.
Arduino code is below:
#include <AccelStepper.h>
#include <stdio.h>
#define STEP_PIN_C 5 //31
#define DIRECTION_PIN_C 23 //32
#define ENABLE_PIN_C 24 //33
#define SET_ACCELERATION 600.0
AccelStepper stepper(1, STEP_PIN_C, DIRECTION_PIN_C);
unsigned long oldTime=0;
unsigned long now;
float newSpeed;
float maxSpeed = 3500.0;
bool newDataBit, runAllowed = false,addingProg=false,mainProg=false;
char commandChar;
long currentPosition;
long int steps = 0, mainNewStep, addedNewStep,memMainStep;
void checkSerial();
void checkRunning();
void stopMotor();
void runMotor();
void sendInfo();
const unsigned long delayTime = 1000;
unsigned long timer;
int count = 0;
bool running = false;
void setup()
{
Serial.begin(9600);
pinMode(ENABLE_PIN_C, OUTPUT);
digitalWrite(ENABLE_PIN_C, HIGH);
stepper.setCurrentPosition(0); //initial value
stepper.setMaxSpeed(0.0); //initial value
stepper.setAcceleration(SET_ACCELERATION); //initial value
}
void loop()
{
sendInfo();
checkRunning();
checkSerial();
}
void checkRunning()
{
if (runAllowed == true)
{
if (stepper.distanceToGo() == 0)
{
stopMotor();
checkSerial();
}
else
{
runMotor();
checkSerial();
}
}
}
void checkSerial()
{
if (Serial.available())
{
newDataBit = true;
commandChar = Serial.read();
}
if (newDataBit == true)
{
///DoStuff depends on what is received as commandChar via serial port
mainProgram(stepper.currentPosition(),newSpeed,mainNewStep);
newDataBit = false;
}
}
void runMotor(){
digitalWrite(ENABLE_PIN_C, LOW);
stepper.run();
running = true;
}
void stopMotor(){
stepper.setCurrentPosition(0);
digitalWrite(ENABLE_PIN_C, HIGH);
stepper.stop();
running = false;
timer = millis() + delayTime;
}
void mainProgram(long currentPositionValue,float maxSpeedValue,long stepValue)
{
mainProg = true;
if (stepper.distanceToGo() == 0) //YOLUMU TAMAMLADIM
{
addingProg = false;
steps = stepValue;
stepper.setCurrentPosition(currentPositionValue);
//stepper.setSpeed(0);
stepper.setMaxSpeed(maxSpeedValue);
stepper.moveTo(steps);
}
else
{
steps = stepValue + steps;
stepper.setCurrentPosition(currentPositionValue);
//stepper.setSpeed(0);
stepper.setMaxSpeed(newSpeed);
stepper.moveTo(steps);
}
}
void sendInfo(){
now = millis();
if(now-oldTime > 1000){ //saniyede 1
Serial.print(stepper.currentPosition());
Serial.print(" ");
Serial.print(stepper.isRunning());
Serial.print(" ");
Serial.println(stepper.speed());
oldTime = now;
}
}
From AccelStepper documentation:
The fastest motor speed that can be reliably supported is about 4000
steps per second at a clock frequency of 16 MHz on Arduino such as Uno
etc.
This is if you do nothing else but running the stepper.
You check your serial interface and send multiple lines every second. Both is quite expensive.

I'm having trouble implementing the Atmega328 timer into my arduino networking

I am trying to implement error correction over an r/f communication between two arduinos. I tried adding a timer to it, in order to create a packet resend, but whenever it gets past the first send, it starts printing garbage ad infinity instead of doing the timer interrupt.
I tried messing around with the inside loop conditions some as well as trying to figure out what was wrong with the timer, but I couldn't figure it out. The problem seems to happen right around the first serial print, which is strange, because that part of the code is mostly unchanged.
(packets is a structure of two ints)
#include <ELECHOUSE_CC1101.h>
#include "packets.h"
// These examples are from the Electronics Cookbook by Simon Monk
// Connections (for an Arduino Uno)
// Arduino CC1101
// GND GND
// 3.3V VCC
// 10 CSN/SS **** Must be level shifted to 3.3V
// 11 SI/MOSI **** Must be level shifted to 3.3V
// 12 SO/MISO
// 13 SCK **** Must be level shifted to 3.3V
// 2 GD0
const int n = 61;
unsigned short int sequence = 0;
byte buffer[n] = "";
void setup() {
Serial.begin(9600);
Serial.println("Set line ending to New Line in Serial Monitor.");
Serial.println("Enter Message");
ELECHOUSE_cc1101.Init(F_433); // set frequency - F_433, F_868, F_965 MHz
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 0xFFFF; // Max value for overflow for now
TCCR1B |= (1 << CS12); // 256 prescaler
interrupts(); // enable all interrupts
}
Packet pckt, recieve;
ISR(TIMER1_OVR_vect){ // timer compare interrupt service routine
//Resend packet
ELECHOUSE_cc1101.SendData(buffer, pckt.data + pckt.seqNum);
int len = ELECHOUSE_cc1101.ReceiveData(buffer);
buffer[len] = '\0';
recieve.seqNum = buffer[n];
Serial.println("Interrupt");
}
void loop() {
if (Serial.available()) {
pckt.data = Serial.readBytesUntil('\n', buffer, n);
pckt.seqNum = sequence;
buffer[pckt.data] = '\0';
buffer[n-1] = pckt.seqNum;
Serial.println((char *)buffer);
ELECHOUSE_cc1101.SendData(buffer, pckt.data + pckt.seqNum);
TCNT1 = 0; // clear timer
TIMSK1 |= (1 << TOIE0); // enable timer compare interrupt
int len = ELECHOUSE_cc1101.ReceiveData(buffer);
while (recieve.seqNum <= sequence) {
}
TIMSK1 &= ~(1 << TOIE0); // turn off the timer interrupt
}
}
Sending data takes too long for interrupts. You should keep calls to send and receive buffers of data within the loop() function call tree. For example, sending a 12 bytes message via UART at 9600 bauds can take up to about 12ms.
You can use the timer interrupt to decrement a timeout counter, as is usually done on micro controllers, or use the millis() function to handle timings, as is easily done on Arduino.
I suggest you use the millis() function to compute timeouts.
example:
/* ... */
// I could not figure out what you were trying to do with
// pckt.seqNum.... Putting it at the end of the buffer
// makes no sense, so I've left it out.
// Moreover, its size is 2, so placing it at buffer[n-1] overflows the buffer...
enum machineState {
waitingForSerial,
waitingForResponse,
};
unsigned int time_sent; // Always use unsigned for variables holding millis()
// can use unsigned char for timeouts of 255
// milliseconds or less. unsigned int is good for about
// 65.535 seconds or less.
machineState state = waitingForSerial;
void loop()
{
switch(state)
{
case waitingForSerial:
pckt.data = Serial.readBytesUntil('\n', buffer, sizeof(buffer));
if (pckt.data > 0)
{
++pckt.seqNum;
Serial.write(buffer, pckt.data);
ELECHOUSE_cc1101.SetReceive();
ELECHOUSE_cc1101.SendData(buffer, pckt.data);
time_sent = millis();
state = waitingForResponse;
}
break;
case waitingForResponse:
if (ELECHOUSE_cc1101.CheckReceiveFlag())
{
auto len = ELECHOUSE_cc1101.ReceiveData(buffer)) // can use C++17 with duinos!!!
Serial.print("cc1101: ");
Serial.write(buffer, len);
state = waitingForSerial; // wait for another command from PC
}
// 1 second timeout, note the cast and subtraction, this is to avoid any
// issues with rollover of the millis() timestamp.
else if ((unsigned int)millis() - time_sent > 1000)
{
// resend ... stays stuck this way.
Serial.println("Retrying :(");
ELECHOUSE_cc1101.SendData(buffer, pckt.data);
time_sent = millis();
}
break;
default:
state = waitingForSerial;
Serial.println("unhandled state");
break;
}
}

Arduino SD card fails to write when used with another SPI device

I have an ADXL355 accelerometer attached to an Adafruit Feather Adalogger. I can configure and read the sensor. I can also write binary values to the SD card. The problem occurs when I try to read from the sensor and then write that data to the SD card. The only thing I can think of is I'm somehow messing up the SPI communication but I can't see where. I looked through pins_arduino.h for my board and the SD Card (pin 4) is on a different register than pin 10 so I don't see how I'm breaking things.
My operations proceed like this. Global sensor creation, Serial.begin, SD.begin, SPI.begin, Test sensor connection, Create file for output on SD card, Initialize sensor, Read sensor FIFO, Write to file, repeat last 2 forever.
The file is created but remains at 0 file size, ie nothing is actually written to the card.
The sensor can operate at 4 kHz which was hard to achieve using the digitalWrite functions so I switched to using the port registers on the Feather. I do it like this:
#include <SM_ADXL355_SPI_fast.h>
#include <SPI.h>
#include <SD.h>
#define cardSelect 4
ADXL355_SPIF adxl355(&DDRB, &PORTB, _BV(6)); // values taken from pins_arduino.h, tested and working on pin 10
void setup() {
Serial.begin(57600);
while(!Serial){
// wait for Serial
}
SD.begin(cardSelect);
SPI.begin();
while(!adxl355.TestConnection()){
delay(1000);
}
adxl355.OpenFile("TestSPI.bin");
adxl355.Initialize(1, 10, 0); // set range to 2g's, frequency to 4 Hz and filter to off
}
void loop() {
while(true){ // avoid Arduino overhead of their loop function
adxl355.ReadFIFO();
adxl355.WriteFIFOToFile();
}
}
Here is the ADXL constructor
ADXL355_SPIF::ADXL355_SPIF(volatile uint8_t * outReg, volatile uint8_t * outPort, uint8_t bitValue) : sensorOutReg(outReg), sensorPort(outPort), sensorBitValue(bitValue){
*sensorOutReg |= sensorBitValue;
*sensorPort |= sensorBitValue;
sensorWriteCount = 0;
}
TestConnection tests that the DeviceID reads 0xAD. Initialize sets the G range, sample rate in Hz and filter. I have tested these with serial output and they work properly.
OpenFile looks like this:
bool ADXL355_SPIF::OpenFile(const String& fileName){
sensorFile = SD.open(fileName, FILE_WRITE);
if (!sensorFile){
Serial.print("Could not create file: ");
Serial.println(fileName);
return false;
}
return true;
}
After running this a file does get created on the SD card called "TESTSPI.BIN" with 0 file size.
ReadFIFO reads the numbers of entries in FIFO, stored as fifoCount and then populates a buffer (sensorFIFO[32][3]) with the values from the FIFO. I've printed this buffer to Serial to show that it's working. Here is that function
void ADXL355_SPIF::ReadFIFO(){
ReadRegister(ADXL355_RA_FIFO_ENTRIES, 1);
fifoCount = buffer[0];
ReadFIFOInternal();
return;
}
void ADXL355_SPIF::ReadFIFOInternal(){
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
*sensorPort &= ~sensorBitValue;
uint8_t spiCommand = ADXL355_RA_FIFO_DATA << 1 | ADXL355_READ;
SPI.transfer(spiCommand);
int i = 0;
unsigned long tempV;
unsigned long value;
while(i < fifoCount){
for (int ptr = 0; ptr < 3; ++ptr){
buffer[0] = SPI.transfer(0x0);
value = buffer[0];
value <<= 12;
tempV = SPI.transfer(0x0);
tempV <<= 4;
value |= tempV;
tempV = SPI.transfer(0x0);
tempV >>=4;
value |= tempV;
if (buffer[0] & 0x80) {
value |= 0xFFF00000;
}
long lValue = static_cast<long>(value);
sensorFIFO[i][ptr] = scaleFactor * lValue;
}
i += 3;
}
SPI.endTransaction();
*sensorPort |= sensorBitValue;
return;
}
Here is WriteFIFOToFile:
void ADXL355_SPIF::WriteFIFOToFile(){
if (fifoCount > 0){
sensorFile.write(reinterpret_cast<const char *>(&sensorFIFO), 4 * fifoCount);
}
sensorWriteCount += fifoCount;
if (sensorWriteCount >= 100){
sensorFile.flush();
sensorWriteCount = 0;
}
}
After allowing this to run for a while the file size is always 0. I tried a simple binary write function just to test the card. It looks like this and it worked.
#include <SD.h>
#define cardSelectPin 4
const float pi=3.14159;
File oFile;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial){
// wait for serial
}
SD.begin(cardSelectPin);
oFile = SD.open("Test.bin", FILE_WRITE);
Serial.println(sizeof(int));
Serial.println(sizeof(float));
float testFloat[32][3];
for (int i = 0; i < 32; ++i){
for (int j = 0; j < 3; ++j){
testFloat[i][j] = pi * (i + 1) + j;
}
}
oFile.write(reinterpret_cast<const char *>(&testFloat), sizeof(float) * 96);
oFile.close();
Serial.println("Finished writing file.");
}
void loop() {
// put your main code here, to run repeatedly:
}
The problem was that flush was not being called correctly. I had created a buffer to hold data from the FIFO and it would flush the card when it would get full enough such that a subsequent read would overflow. At that time it would call flush. This is what was intended with the variable sensorWriteCount. This variable was of type uint8_t when it should have been a uint16_t.
Changing to the correct type fixed the problem. I would have deleted this question because it boils down to a typo, but once an answer has been posted the system doesn't allow that.
The only difference between the not-working sketch and the working one is the closing of the sd card. The sd card MUST be closed, I had the same problem you have and I assume that the file gets its boundaries written in its filesystem at file close call.
To solve your issue, use a push button. When you push it, it will close the file and stop reading/processing sensors. You can also use this button to start reading and recording sensors data on sd card again (toggle).

Ignore interrupt within time range arduino (lowpass filter)

I'm trying to attach interrupts to the rising edge of a signal (PWM). However, the signal is somewhat noisy when it's HIGH which causes the code to register another interrupt when it should not. I obviously tried to fix this in my circuit but that's not quite working so I moved to the software part.
The question is how I can filter out interrupts within a given frequency range? I need to apply a lowpass filter so that the interrupts do not get triggered when the signal is HIGH. My idea was detach the interrupt for a given amount of time or simply ignore the interrupt if it happens within a certain time range.
I'm just not sure how to achieve this.
This is my code:
unsigned long tsend = 0;
unsigned long techo = 0;
const int SEND = 2;
const int ECHO = 3;
unsigned long telapsed = 0;
unsigned long treal = 0;
void setup() {
Serial.begin(115200);
Serial.println("Start");
pinMode(SEND, INPUT);
pinMode(ECHO, INPUT);
attachInterrupt(digitalPinToInterrupt(SEND), time_send, RISING);
attachInterrupt(digitalPinToInterrupt(ECHO), time_echo, RISING);
}
void loop() {
telapsed = techo - tsend;
if (telapsed > 100 && telapsed < 10000000) {
treal = telapsed;
Serial.println(treal);
}
}
void time_send() {
tsend = micros();
}
void time_echo() {
techo = micros();
}
Below is the signal (yellow) which has a lot of noise. I need to ignore the interrupts when the signal is HIGH. Here is an image of the PWM Signal
I would try the following:
#define DEBOUNCE_TIME 100
void time_send() {
static long last = micros() ;
if (last-tsend > DEBOUNCE_TIME)
tsend = last;
}
void time_echo() {
static long last = micros() ;
if (last-techo > DEBOUNCE_TIME)
techo = last;
}
And adjust DEBOUNCE_TIME until I get a satisfactory result.
const byte intrpt_pin = 18;
volatile unsigned int count = 0;
#define DEBOUNCE_TIME 5000
void isr()
{
cli();
delayMicroseconds(DEBOUNCE_TIME);
sei();
count++;
}
void setup()
{
pinMode(intrpt_pin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(intrpt_pin), isr, FALLING);
}
void loop()
{
}
cli() : Disables all interrupts by clearing the global interrupt mask.
sei() : Enables interrupts by setting the global interrupt mask.
So, basically this program will ignore all the interrupt that occurs between these two lines, that is for DEBOUNCE_TIME.
Check your your interrupt bouncing time and adjust DEBOUNCE_TIME accordingly for the best result.

Resources