Почему я могу разбудить Arduino с помощью этого кода только один раз?
Это мой расширенный код для дневного будильника на Arduino UNO с добавленным MP3-плеером. Предполагается, что устройство находится в спящем режиме все время, за исключением случаев, когда я нажимаю кнопку воспроизведения (тогда оно должно воспроизводить музыку) или когда RTC пробуждает его, и то и другое через прерывание. Когда RTC разбудит его, он начнет свою настоящую процедуру дневного будильника.
Я только что реализовал код сна/выключения питания и заставил его работать, чтобы я мог разбудить его и перевести в спящий режим с помощью контактов 9 (сон) и 2 (пробуждение через прерывание). Однако возникла проблема, и мне пришлось изменить код.
Видимо, пытаясь починить, я что-то сломал. Текущее поведение при запуске: 1. Иди спать (правильно) 2. Когда я замыкаю контакт 2 и землю, он просыпается. (правильный) 3. Когда я перемыкаю контакт 9 и землю, он переходит в режим сна. (правильный). 4. Когда я снова замыкаю контакт 2 и заземляю его, он больше не просыпается.
Я изменил код, чтобы помимо воспроизведения музыки загорался встроенный светодиод.
#include <avr/sleep.h>
/************ init mp3-player ***************/
#include <SoftwareSerial.h>
#define ARDUINO_RX 4 // должен подключаться к TX модуля последовательного MP3-плеера
#define ARDUINO_TX 5 //подключаемся к RX модуля
SoftwareSerial mp3(ARDUINO_RX, ARDUINO_TX);
static int8_t Send_buf[8] = {0}; // Буфер для команд отправки. // ЛУЧШЕ ЛОКАЛЬНО
String sbyte2hex(uint8_t b);
#define CMD_SLEEP_MODE 0X0A
#define CMD_WAKE_UP 0X0B
#define CMD_SEL_DEV 0X09
#define CMD_PLAY 0X0D
#define CMD_STOP_PLAY 0X16 // Прекращаем непрерывное воспроизведение.
#define CMD_FOLDER_CYCLE 0X17
#define CMD_SET_DAC 0X1A
#define DAC_ON 0X00
#define DAC_OFF 0X01
#define DEV_TF 0X02
/**************** init the real time clock ***************/
#include <Rtc_Pcf8563.h>
Rtc_Pcf8563 rtc;
/**************** setup pins & buttons ***************/
const byte led = 11; // Контактный светодиод подключен к
const byte sixAmPin = 6; // Пин для кнопки установки будильника на 6 утра
const byte sevenAmPin = 7; // Пин для кнопки установки будильника на 7 утра
const byte eightAmPin = 8; // Пин для кнопки установки будильника на 8 утра
const byte stopPin = 9; // Пин для кнопки остановки
const byte timeResetPin = 10; // Пин для кнопки сброса часов до 9 утра (полезно, например, после сбоя батареи)
const byte playPin = 2; // Пин для кнопки воспроизведения музыки (и прерывания!)
const byte rtcIntPin = 3; // Вывод для прерывания RTC
byte sixAmButton = 1; // Состояние кнопки установки в 6 утра
byte sevenAmButton = 1; // Состояние кнопки установки в 7 утра
byte eightAmButton = 1; // Состояние кнопки установки в 8 утра
byte stopButton = 1; // Состояние кнопки остановки
byte timeResetButton = 1; // Состояние кнопки установки времени
byte playButton = 1; // Состояние кнопки воспроизведения музыки
byte brightness = 0; // Яркость светодиода
bool wakeupMode = false;
unsigned long prevStepTime = millis();
unsigned long time;
/*************** Changeable variables here **************/
byte setHour = 6;
byte setMin = 30;
byte fadeDuration = 30; // Продолжительность затухания в минутах (по умолчанию: 30)
byte afterBurn = 30; // Продолжать светиться после тревоги в течение указанного количества минут (по умолчанию: 30)
/********************************************************/
unsigned long keepLight = afterBurn * 60000UL; // Вычисляем время, в течение которого светодиод будет гореть после затухания (в мс)
int fadeStepLength = fadeDuration * 60000 / 255; // Рассчитываем длительность шага увеличения яркости светодиода (в мс) (по умолчанию: FadeDuration * 60000/255)
void setup() {
/*DEBUG*/
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
/*DEBUG END*/
// Serial.begin(9600); //раскомментируем после тестирования
mp3.begin(9600);
delay(500);
sendCommand(CMD_SEL_DEV, 0, DEV_TF);
delay(500);
// объявляем вывод светодиода выходом:
pinMode(led, OUTPUT);
// объявляем контакты кнопок как входы:
pinMode(stopPin, INPUT_PULLUP);
pinMode(sixAmPin, INPUT_PULLUP);
pinMode(sevenAmPin, INPUT_PULLUP);
pinMode(eightAmPin, INPUT_PULLUP);
pinMode(timeResetPin, INPUT_PULLUP);
pinMode(playPin, INPUT_PULLUP);
pinMode(rtcIntPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(playPin), wakeUpPlay, FALLING);
attachInterrupt(digitalPinToInterrupt(rtcIntPin), wakeAlarm, FALLING);
}
void loop() {
goToSleep();
}
void wakeup() {
// Если режим пробуждения равен true и яркость < 255, прибавляйте +1 к яркости каждые 7 секунд (256 шагов)
wakeupMode = true;
while (wakeupMode == true) {
//обновляем часы
time = millis();
// Если текущее время — время пробуждения, медленно увеличивайте яркость светодиода в течение 30 минут.
if (brightness < 255 ) {
// Задержка повышения яркости поэтапно. Делайте это только в том случае, если интервал прошел.
if (time > prevStepTime + fadeStepLength) {
brightness ++;
prevStepTime = millis(); // Сбрасывает счетчик для интервала перезапуска
}
}
// Выключаем свет и выходим из режима wakupMode через x минут после достижения максимальной яркости
else if (brightness == 255 && time > prevStepTime + keepLight) {
brightness = 0;
wakeupMode = false; // Выход из цикла режима пробуждения
}
readButtons(); // Проверка ввода кнопки
analogWrite(led, brightness); // Установите яркость светодиода соответственно
}
goToSleep;
}
void readButtons() {
// Всегда запускайте это:
// Проверяем, установлен ли будильник на 6, 7 или 8 утра
sixAmButton = digitalRead(sixAmPin);
if (sixAmButton == LOW) {
rtc.setAlarm(5, 30, 99, 99);
}
sevenAmButton = digitalRead(sevenAmPin);
if (sevenAmButton == LOW) {
rtc.setAlarm(6, 30, 99, 99);
}
eightAmButton = digitalRead(eightAmPin);
if (eightAmButton == LOW) {
rtc.setAlarm(7, 30, 99, 99);
}
// Если нажата кнопка остановки: остановить воспроизведение музыки, выключить светодиод, перевести mp3-плеер в режим сна, перейти в режим сна Arduino
stopButton = digitalRead(stopPin);
if (stopButton == LOW) {
wakeupMode = false;
brightness = 0; // раскомментируем после тестирования
analogWrite(led, brightness); // Установите яркость светодиода соответственно
sendCommand(CMD_STOP_PLAY);
sendCommand(CMD_SLEEP_MODE);
delay(200);
goToSleep();
}
// Если нажата кнопка timeResetButton, сбрасываем RTC на 9 утра
if (timeResetButton == LOW) {
rtc.setTime(9, 0, 0);
}
// Если нажата кнопка «Воспроизвести музыку», начнём воспроизведение музыки
playButton = digitalRead(playPin);
if (playButton == LOW) {
playMusic();
}
}
void playMusic(){
digitalWrite(LED_BUILTIN, HIGH);
sendCommand(CMD_WAKE_UP);
delay(200);
sendCommand(CMD_FOLDER_CYCLE, 1, 0);
while (1){
readButtons();
}
}
void wakeUpPlay(){
sleep_disable();
detachInterrupt(digitalPinToInterrupt(playPin));
detachInterrupt(digitalPinToInterrupt(rtcIntPin));
playMusic();
}
void wakeAlarm(){
sleep_disable();
detachInterrupt(digitalPinToInterrupt(playPin));
detachInterrupt(digitalPinToInterrupt(rtcIntPin));
wakeup();
}
void goToSleep() {
sleep_enable();
attachInterrupt(digitalPinToInterrupt(playPin), wakeUpPlay, FALLING);
attachInterrupt(digitalPinToInterrupt(rtcIntPin), wakeAlarm, FALLING);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
sleep_cpu();
}
/**************************/
/** Stuff for MP3 player **/
/**************************/
/********************************************************************************/
/*Function: Send command to the MP3 */
/*Parameter: byte command */
/*Parameter: byte dat1 parameter for the command */
/*Parameter: byte dat2 parameter for the command */
void sendCommand(byte command){
sendCommand(command, 0, 0);
}
void sendCommand(byte command, byte dat1, byte dat2){
delay(20);
Send_buf[0] = 0x7E; //
Send_buf[1] = 0xFF; //
Send_buf[2] = 0x06; // Лен
Send_buf[3] = command; //
Send_buf[4] = 0x01; // 0x00 НЕТ, 0x01 обратная связь
Send_buf[5] = dat1; // данные
Send_buf[6] = dat2; // данные
Send_buf[7] = 0xEF; //
Serial.print("Sending: ");
for (uint8_t i = 0; i < 8; i++)
{
mp3.write(Send_buf[i]) ;
Serial.print(sbyte2hex(Send_buf[i]));
}
Serial.println();
}
/********************************************************************************/
/*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: shex2int. Returns a int from an HEX string. */
/*Parameter: s. char *s to convert to HEX. */
/*Parameter: n. char *s' length. */
/*Return: int */
int shex2int(char *s, int n){
int r = 0;
for (int i=0; i<n; i++){
if(s[i]>='0' && s[i]<='9'){
r *= 16;
r +=s[i]-'0';
}else if(s[i]>='A' && s[i]<='F'){
r *= 16;
r += (s[i] - 'A') + 10;
}
}
return r;
}
Я могу только представить, что проблема заключается в том, как я отправляю Arduino в спящий режим при воспроизведении музыки (т. е. в том, как я вызываю goToSleep(); из класса WakeUpPlay()). Пробуждение от сна работает, когда сон был вызван из основного цикла (по-видимому). Вы хоть представляете, что не так? (Я думал о том, чтобы сократить свой код до минимума, чтобы он меньше запутывал вас, но тогда я, вероятно, изменил бы слишком много, чтобы найти ошибку).
@fertchen, 👍1
Обсуждение1 ответ
Лучший ответ:
Весь код воспроизведения музыки находится внутри прерывания. Пока выполняется прерывание, все остальные прерывания отключены. Рекомендуется делать процедуры обработки прерываний как можно короче.
Вы можете установить в программе прерывания логическую переменную. Затем в функции loop
прочитайте эту переменную и вызовите функцию play-music.
volatile boolean startPlayback = false;
...
void wakeUpPlay(){
startPlayback = true;
sleep_disable();
detachInterrupt(digitalPinToInterrupt(playPin));
detachInterrupt(digitalPinToInterrupt(rtcIntPin));
}
...
void loop() {
goToSleep();
if( startPlayback )
{
startPlayback = false;
playMusic();
}
}
Сделайте нечто подобное с WakeAlarm.
Спасибо. Мне было интересно, где заканчивается программа, попав в процедуру WakeUpPlay, или куда она пойдет потом. Но я не сделал шаг назад, необходимый, чтобы посмотреть, откуда оно «пришло», и поэтому просто не осознавал, что оно, по сути, все еще находится внутри прерывания., @fertchen
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Датчик PIR и сон (прерывание) на Mega2560
- POWER_MODE_IDLE пробуждается при любом изменении ввода?
- Как разбудить Arduino с помощью rtc?
- attiny85 сбрасывает себя вместо процедуры пробуждения
- Как одной кнопкой с прерыванием включать и отключать спящий режим?
- ATtiny85 со сном и последовательным портом
- Порог сигнала пробуждения
сокращение кода до минимума - это способ найти ошибку... просто закомментируйте фрагменты кода, @jsotola
Верно, я уменьшил его, но не смог найти проблему. Но, задавая вопросы опрошенным, я решил, что лучше представить код целиком., @fertchen