Почему я могу разбудить 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()). Пробуждение от сна работает, когда сон был вызван из основного цикла (по-видимому). Вы хоть представляете, что не так? (Я думал о том, чтобы сократить свой код до минимума, чтобы он меньше запутывал вас, но тогда я, вероятно, изменил бы слишком много, чтобы найти ошибку).

, 👍1

Обсуждение

сокращение кода до минимума - это способ найти ошибку... просто закомментируйте фрагменты кода, @jsotola

Верно, я уменьшил его, но не смог найти проблему. Но, задавая вопросы опрошенным, я решил, что лучше представить код целиком., @fertchen


1 ответ


Лучший ответ:

3

Весь код воспроизведения музыки находится внутри прерывания. Пока выполняется прерывание, все остальные прерывания отключены. Рекомендуется делать процедуры обработки прерываний как можно короче.

Вы можете установить в программе прерывания логическую переменную. Затем в функции 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