Disclaimer: I have asked questions about this project before but this is yet another issue I'm facing.
What I really want to do: Build a battery powered device with 6 colored buttons. When you press one of the buttons the gadget should play a sound saying the name of the color.
It should not need an ON/OFF toggle to save battery capacity but rather have a very low idle current draw so that the battery will lasts for weeks (or even months) when it's not used.
Hardware: ATtiny85 (low draw in general, very low draw in sleep), DfPlayer, 3.7v Battery (probably 18650)
The 6 buttons are attached to a single input pin. The microcontroller can distinguish them by reading the ADC value. The buttons have different resistors attached to them
How I wanted to do it: My initial plan was to press a button that wakes the microcontroller up. The mcu then wakes the dfplayer, plays a sound and goes back to sleep. If that would not be responsive enough add a delay to the sleep like: if no button was pressed within the last 30s go to sleep. That worked after some initial problems but it turned out that the DfPlayer "sleep mode" doesn't mean it goes to sleep as in it draws less current but much rather that it's just idling, not playing any sound.
How I want to do it now: Pretty much the same as above but instead of sending the DfPlayer to sleep just turn it on and off:
Press a button that wakes the microcontroller up. The mcu then turns the DfPlayer on by setting the gate of an n-channel mosfet (IRF3708) HIGH, waits a short period of time, plays a sound, turns the dfplayer off and goes back to sleep. If that's not responsive enough add a sleep delay like explained above.
What I'm already able to do:
- make the DfPlayer play a specific sound over serial communication
- distinguish between the 6 input buttons
- wake the microcontroller up with any of these buttons
- turn the DfPlayer on/off by using an n-channel mosfet
The problem that I have: The microcontroller freezes when I set the output pin attached to the mosfet gate to HIGH. Freeze means it just won't execute any code after the digitalWrite(PIN_MOS, HIGH);
call. I tested it by turning an LED on a different pin on and off like so
digitalWrite(PIN_LED, HIGH);
digitalWrite(PIN_MOS, HIGH);
digitalWrite(PIN_LED, LOW);
The LED turns on but won't ever turn off. When I use the external reset of the attiny the mosfet will turn off and code will execute again just fine. Until I try to set the mosfet pin to HIGH again.
The DfPlayer will turn on though, I can play sounds on it by shorting 2 pins on it. It doesn't seem to be unstable either. It's just that the attiny hangs up.
I use a cheap lab psu but it should be good enough to be not the problem. I've also tried a fully charged 18650 battery and a CR2032 battery. The 18650 worked the same as the psu and the CR2032 just couldn't handle the draw and the DfPlayer wouldn't properly turn on.
I tried it different voltages between 3.3v and 5v. In theory everything should work and in reality it didn't change anything either.
Here is the breadboard layout I use.
I also added an electrolytic and ceramic cap between Vcc/GND on the DfPlayer but that didn't help either
Here is the code I use
#include <Arduino.h>
#include <avr/sleep.h>
#include <DFMiniMp3.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include "LemonSerial.h"
#include "mp3notify.h"
#define PIN_TX PB0
#define PIN_RX PB1
#define PIN_MOS PB2
#define PIN_LED PB3
#define PIN_A PB4
LemonSerial secondarySerial(PIN_RX, PIN_TX);
DfMp3 dfmp3(secondarySerial);
void sleep();
void initADC()
{
ADMUX =
(1 << ADLAR) | // left shift result
(0 << REFS1) | // Sets ref. voltage to VCC, bit 1
(0 << REFS0) | // Sets ref. voltage to VCC, bit 0
(0 << MUX3) | // use ADC2 for input (PB4), MUX bit 3
(0 << MUX2) | // use ADC2 for input (PB4), MUX bit 2
(1 << MUX1) | // use ADC2 for input (PB4), MUX bit 1
(0 << MUX0); // use ADC2 for input (PB4), MUX bit 0
ADCSRA =
(1 << ADEN) | // Enable ADC
(1 << ADPS2) | // set prescaler to 128, bit 2
(1 << ADPS1) | // set prescaler to 128, bit 1
(1 << ADPS0); // set prescaler to 128, bit 0
}
void setup()
{
initADC();
pinMode(PB5, INPUT_PULLUP);
pinMode(PIN_A, INPUT);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
pinMode(PIN_MOS, OUTPUT);
digitalWrite(PIN_MOS, LOW);
}
void play(uint16_t track)
{
if (digitalRead(PIN_MOS) == LOW)
{
digitalWrite(PIN_LED, HIGH);
delay(500);
digitalWrite(PIN_MOS, HIGH);
digitalWrite(PIN_LED, LOW);
delay(500); // wait for DfPlayer to turn on
dfmp3.begin();
dfmp3.reset();
dfmp3.disableDac();
dfmp3.setVolume(30);
dfmp3.setPlaybackSource(DfMp3_PlaySource_Sd);
}
dfmp3.playGlobalTrack(track);
}
void loop()
{
ADCSRA |= (1 << ADSC); // start ADC measurement
while (ADCSRA & (1 << ADSC))
{
// wait till conversion complete
}
uint8_t adcValue = ADCH;
// actually calculate the parameter based off adcValue here. omitted for readability
play(1);
}
// Interrupt Serviceroutine (Pin-Change-Interrupt)
ISR(PCINT0_vect)
{
LemonSerial::handle_interrupt();
}
// ADC interrupt service routine
ISR(ADC_vect)
{
; // noop
}
while (ADCSRA & (1 << ADSC)) { sleepIfIdle(started) ; }
. I think it is going to hang in this loop continuously sleeping because the variablestarted
is not refreshed. Try makingstarted
global and update it also at the end of your sleep code. If this is the problem then create an answer yourself showing the coding changes.