mp3 + arduino uno (некорректаня инфа Serial)

Тема в разделе "Arduino & Shields", создана пользователем Druids91, 15 дек 2014.

  1. Druids91

    Druids91 Нерд

    Добрый день. Столкнулся сегодня с такой проблемой. Решил сделать маленький термометр с озвучкой. Так чтобы проигрывался звук после нажатия на кнпоку и отображения информации. Все действия выполняются верно. НО если нажать второй раз на кнопку, то информация получаемая через SerialPort начинает искажаться. появляются неизвестные символы и т.д.
    Код (Text):

    Hour    Temperature
    1    28  
    ~я
     
    Код программы:
    Код (Text):
    #include <math.h>
    #include <mp3TF.h>
    #include <Wire.h>
    #define BUTTON_PIN  3
    int minute = 1;
    mp3TF mp3tf = mp3TF ();

    // Параметр конкретного типа термистора (из datasheet):
    #define TERMIST_B 4300
    #define VIN 5.0
    boolean buttonWasUp = true;  // была ли кнопка отпущена?
    void setup()
    {
      mp3tf.init (&Serial);
      Serial.begin(9600);
      Serial.println("Hour\t Temperature");
      pinMode(13, OUTPUT);
      pinMode(BUTTON_PIN, INPUT_PULLUP);
      }

    void loop()
    {
      boolean buttonIsUp = digitalRead(BUTTON_PIN);
      if (buttonWasUp && !buttonIsUp)
      {
        delay(10);
        if (!buttonIsUp)
        {
              digitalWrite(13, HIGH);
              delay(100);
              digitalWrite(13, LOW);
              delay(900);
              float voltage = analogRead(A0) * VIN / 1024.0;
              float r1 = voltage / (VIN - voltage);
              int temperature = 1. / ( 1. / (TERMIST_B) * log(r1) + 1. / (25. + 273.) ) - 273;
              Serial.print(minute);
              Serial.print("\t");
              Serial.print(temperature);
              Serial.println("\t");
              ++minute;    // увеличиваем значение час на 1
              delay (3000);
              mp3tf.play ();
          }
      }
    buttonWasUp = buttonIsUp;
    }
     
  2. Alex19

    Alex19 Гуру

    Как я понял, для связи с этим модулем используется Serial. То есть единственный Serial (arduino uno), который Вы используете для отладки занят.

    Попробуйте использовать для него SoftwareSerial.h, если модуль этот, то можно, так как он поддерживает SoftwareSerial.

    В общем виде, выглядит примерно так
    Код (Text):

    #include <SoftwareSerial.h> // Библиотека SoftwareSerial

    #include <math.h>
    #include <mp3TF.h>
    #include <Wire.h>
    #define BUTTON_PIN  3
    int minute = 1;
    mp3TF mp3tf = mp3TF ();

    // Параметр конкретного типа термистора (из datasheet):
    #define TERMIST_B 4300
    #define VIN 5.0
    boolean buttonWasUp = true;  // была ли кнопка отпущена?

    SoftwareSerial mySerial(10, 11); // RX, TX - Вы должны будете изменить подключения модуля на эти ножки

    void setup()
    {
      mySerial.begin(9600); // Задаем скорость SoftwareSerial

      mp3tf.init (&mySerial); // Передаем SoftwareSerial
      Serial.begin(9600);
      Serial.println("Hour\t Temperature");
      pinMode(13, OUTPUT);
      pinMode(BUTTON_PIN, INPUT_PULLUP);
      }

    void loop()
    {
      boolean buttonIsUp = digitalRead(BUTTON_PIN);
      if (buttonWasUp && !buttonIsUp)
      {
        delay(10);
        if (!buttonIsUp)
        {
              digitalWrite(13, HIGH);
              delay(100);
              digitalWrite(13, LOW);
              delay(900);
              float voltage = analogRead(A0) * VIN / 1024.0;
              float r1 = voltage / (VIN - voltage);
              int temperature = 1. / ( 1. / (TERMIST_B) * log(r1) + 1. / (25. + 273.) ) - 273;
              Serial.print(minute);
              Serial.print("\t");
              Serial.print(temperature);
              Serial.println("\t");
              ++minute;    // увеличиваем значение час на 1
              delay (3000);
              mp3tf.play ();
          }
      }
    buttonWasUp = buttonIsUp;
    }
    Но если это модуль от Амперки, почему такая библиотека mp3TF.h. Библиотека от производителя тута.

    И там есть пример, в папке DFPlayer-Mini-mp3/DFPlayer_Mini_Mp3/examples/DFPlayer_SoftwareSerial/DFPlayer_SoftwareSerial.ino.
     
  3. Druids91

    Druids91 Нерд

    Спасибо за ответ. Завтра в свободное от работы протестирую.
    Столкнулся с одной бедой - как и некоторые пользователи модуля DFPlayer.
    Вы указали ссылку на последнюю версию библиотек, я же использую более раннюю, хоть и с отсутствием некоторых функций, которые мне в принципе и не нужны.

    P.S. Прочитал о том что такое UART и понял, что мой вопрос был очень глупым
     
  4. Alex19

    Alex19 Гуру

    Не работал с этим модулем, глянул старую библиотеку, там нет поддержки SoftwareSerial. Тогда увы не знаю, можно попробовать дописать эту библиотеку.

    Все нормально, мы все учимся и помогаем друг другу по мере сил.
     
  5. Druids91

    Druids91 Нерд

    Решил не создавать новую тему, а задать вопрос в этой.
    Не знаю, как переписать скетч так, чтобы реакция на кнопку была мгновенной. (на данный момент реакция на кнопку происходит непонятно как, то она срабатывает сразу, то приходится несколько раз нажимать). Причину я нашёл (delay 3000) выделил вам кусок отдельно. Если убрать delay (3000); то значение влаги будет непрерывно обновляться, а мне нужно с задержкой, и так чтобы эта задержка никак не влияла на кнопку.
    Код ( (Unknown Language)):
        delay (3000);
        val = analogRead(1);
        lcd.setCursor (0,1);
        lcd.print("Vlaga:");
        lcd.print(val);
              if (val < 300)
              {
              delay (3000);
              mp3tf.play ();
    И ешё маленький вопросик. Как сделать, чтобы музыка проигрывалась всего 1 раз, а не постоянно?
    Код (Text):
    #include <LiquidCrystal.h> // Подключаем библиотеку LiquidCrystal.
    #include <math.h>
    #include <mp3TF.h>
    #include <Wire.h>
    #define BUTTON_PIN  9
    int val = 0;
    int minute = 1;
    LiquidCrystal lcd(2, 3, 4, 5, 6, 7); // контакты на Arduino = lcd: - (2=RS, 3=E, 4=D4, 5=D5, 6=D6, 7=D7)
    mp3TF mp3tf = mp3TF ();

    // Параметр конкретного типа термистора (из datasheet):
    #define TERMIST_B 4300
    #define VIN 5.0
    boolean buttonWasUp = true;  // была ли кнопка отпущена?
    void setup()
    {
      lcd.begin(20, 4); // устанавливаем размер (количество столбцов и строк) экрана.
      mp3tf.init (&Serial);
      Serial.begin (9600);
      Serial.println("Hour\t Temperature");
      pinMode(13, OUTPUT);
      pinMode(BUTTON_PIN, INPUT_PULLUP);
      }

    void loop()
    {
      boolean buttonIsUp = digitalRead(BUTTON_PIN);

      if (buttonWasUp && !buttonIsUp)
      {
        delay(10);
        if (!buttonIsUp)
        {
              digitalWrite(13, HIGH);
              delay(100);
              digitalWrite(13, LOW);
              delay(900);
              float voltage = analogRead(A0) * VIN / 1024.0;
              float r1 = voltage / (VIN - voltage);
              int temperature = 1. / ( 1. / (TERMIST_B) * log(r1) + 1. / (25. + 273.) ) - 273;
              Serial.print(minute);
              Serial.print("\t");
              Serial.print(temperature);
              Serial.println("\t");
              lcd.setCursor (0,0);
              lcd.print ("temperature");
              lcd.setCursor (13,0);
              lcd.print (temperature);
         
              ++minute;    // увеличиваем значение час на 1
              delay (3000);
              mp3tf.play ();
          }
      }
    buttonWasUp = buttonIsUp;
        delay (3000);
        val = analogRead(1);
        lcd.setCursor (0,1);
        lcd.print("Vlaga:");
        lcd.print(val);
              if (val < 300)
              {
              delay (3000);
              mp3tf.play ();
              }
    }
     
     
    Последнее редактирование: 21 дек 2014
  6. Alex19

    Alex19 Гуру

    Мне кажется, лучше создавать новые темы, во первых Вы быстрее получите ответ. А во вторых, проще будет найти по поиску аналогичную проблему и не задавать одинаковые вопросы другим.

    Старайтесь избегать delay, его можно использовать лишь в функции setup. К примеру для ожидания инициализации, каких-то модулей.

    Природа delay, в том, что останавливает работу программы.

    Если Вам нужно проверять, что-то через интервалы времени воспользуйтесь millis().
    Решение, пример из Arduino - Blink without Delay
    Код (Text):

    /* Blink without Delay
    Turns on and off a light emitting diode(LED) connected to a digital
    pin, without using the delay() function.  This means that other code
    can run at the same time without being interrupted by the LED code.
    The circuit:
    * LED attached from pin 13 to ground.
    * Note: on most Arduinos, there is already an LED on the board
    that's attached to pin 13, so no hardware is needed for this example.
    created 2005
    by David A. Mellis
    modified 8 Feb 2010
    by Paul Stoffregen
    This example code is in the public domain.

    http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
    */

    // constants won't change. Used here to
    // set pin numbers:
    const int ledPin =  13;      // the number of the LED pin

    // Variables will change:
    int ledState = LOW;            // ledState used to set the LED
    long previousMillis = 0;        // will store last time LED was updated

    // the follow variables is a long because the time, measured in miliseconds,
    // will quickly become a bigger number than can be stored in an int.
    long interval = 1000;          // interval at which to blink (milliseconds)

    void setup() {
      // set the digital pin as output:
      pinMode(ledPin, OUTPUT);  
    }

    void loop()
    {
      // here is where you'd put code that needs to be running all the time.

      // check to see if it's time to blink the LED; that is, if the
      // difference between the current time and last time you blinked
      // the LED is bigger than the interval at which you want to
      // blink the LED.
      unsigned long currentMillis = millis();
      if(currentMillis - previousMillis > interval) {
        // save the last time you blinked the LED
        previousMillis = currentMillis;

        // if the LED is off turn it on and vice-versa:
        if (ledState == LOW)
          ledState = HIGH;
        else
          ledState = LOW;

        // set the LED with the ledState of the variable:
        digitalWrite(ledPin, ledState);
      }
    }
    Что тут происходит, сохраняем время с момента старта программы в currentMillis. Вычитаем из currentMillis
    предыдущее значение previousMillis и проверяем является ли остаток больше interval. Затем присваиваем previousMillis текущее значение currentMillis.

    Хотя наверно более правильно >=, равно или больше.
    Код (Text):
    if(currentMillis - previousMillis >= interval)
    Иначе выполнение будет через interval + 1.

    Да и надо помнить, что millis, через 50 дней станет равно нулю (переполнение). Можно защитится с помощью простых функций.
    Код (Text):

    unsigned long ps2PreviousT = 0;
    unsigned long ps2T = 3000;
    unsigned long resultCycleTime = 0;
    unsigned long unsignedLongMax = 4294967295;
    unsigned long digitalOne = 1;

    /******************************  Функции работы с таймерами **********************************/
    /// <summary>
    /// Проверка таймера на основе millis
    /// </summary>
    boolean CheckTimerMillis(long PreviousTimmer, int Time)
    {
        // Если предыдущие значение, больше 0
        if (PreviousTimmer > 0)
        {
            // Получаем время с момента запуска программы и отнимем значение PreviousTimmer (время с начала запуска таймера). После это проверяем, прошло ли установленное время Time
            if (GetDifferenceULong(PreviousTimmer, millis()) >= Time)
            {
                // Возвращаем true
                return true;
            }
            else
            {
                // Возвращаем false
                return false;
            }
        }
        else
        {
            // Возвращаем false
            return true;
        }
    }

    /// <summary>
    /// Получение разницы между 2 unsigned long
    /// </summary>
    unsigned long GetDifferenceULong(unsigned long BeginTime, unsigned long EndTime)
    {
      if (EndTime<BeginTime)
      {
        // Защита от переполнения
        resultCycleTime = unsignedLongMax - BeginTime + EndTime + digitalOne;
      }
      else
      {
        resultCycleTime = EndTime - BeginTime;
      }

      return resultCycleTime;
    }
    /******************************  /Функции работы с таймерами **********************************/
    void setup()
    {
      pinMode(13, OUTPUT);
      ps2PreviousT = millis();
    }

    void loop()
    {
      if (CheckTimerMillis(ps2PreviousT, ps2T))
      {
        ps2PreviousT = millis();

        // Тут код который будет выполняться раз в 3 с

        digitalWrite(13, !digitalRead(13));
      }
    }
     
  7. geher

    geher Гуру

    Как показывает реальная практика, защищаться особо обычно вроде как и не надо.
    Для простых ардуин, если все переменные будут unsigned long, то currentMillis - previousMillis вернет правильное значение даже с учетом перехода через ноль, если значение currentMillis будет всегда получено позже, чем значение в previousMillis.
    Проверял специально, получалось именно так. Если это имеет значение, компилировал под линуксом.
    Так что смело можно сравнивать
    (currentMillis - previousMillis >= interval)
    Результат будет правильный. Естественно за исключением случаев двойного перехода через 0, т.е. если между предыдущим и текущим моментами прошло больше 50 суток. И в случае измерения интервалов, больших чем 0xFFFFFFFF миллисекунд данная проверка не сработает корректно.
    Вот если сравнивать
    (currentMillis - interval >= previousMillis)
    или
    (currentMillis >= previousMillis+interval), то можно и налететь.
    В первом случае, если currentMillis больше interval, а previousMillis чуть меньше 0xFFFFFFFF.
    Во втором, если currentMillis перевалил через ноль, а previousMillis+interval еще нет.
     
    Последнее редактирование: 21 дек 2014
  8. Alex19

    Alex19 Гуру

    Не знал, спасибо, что делитесь опытом. Надо будет как-нибудь попробовать.
     
  9. rav_75

    rav_75 Гик

    Да, все так и работает. На крайний случай можно перебздеть и ввести временную переменную
    uint32_t temp = currentMillis - previousMillis;
    if(temp>=interval){}
    но, как показывает практика, это лишнее.
    Не совсем понял про это:
    Что значит двойной переход через 0? Программа 50 и больше суток не проверяет условие что ли? А в случае измерения интервалов, больших чем 0xFFFFFFFF, данный код вообще не сработает, поскольку интервал не лежит в данном диапазоне. Здесь нужно будет мутить свою функцию.
    В данном случае грабли могут проявиться и при интервале, приближенном к 0xFFFFFFFF. Причем чем ближе, тем больше шанс. Например, если interval = 0xFFFFFFC, то условие (currentMillis - previousMillis >= interval) будет true только 4ms, и если программа в это время занята чем-то другим, а не проверяет условие, то проскочили, ждем второго - третьего круга.
     
  10. geher

    geher Гуру

    Именно. Теоретически возможно. Например, программа долго и тупо ждет Serial.available, а когда спустя 50 дней оттуда что-то вдруг свалится, она решит проверить, не истек ли какой-то интервал со времен какого-то старого события.

    Точно, про такой экзотический вариант среди других, не менее экзотических (я про двойной проход нуля и интервалы больше 0xFFFFFFFF), я как-то не подумал.