Добрый день. Столкнулся сегодня с такой проблемой. Решил сделать маленький термометр с озвучкой. Так чтобы проигрывался звук после нажатия на кнпоку и отображения информации. Все действия выполняются верно. НО если нажать второй раз на кнопку, то информация получаемая через 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; }
Как я понял, для связи с этим модулем используется 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.
Спасибо за ответ. Завтра в свободное от работы протестирую. Столкнулся с одной бедой - как и некоторые пользователи модуля DFPlayer. Вы указали ссылку на последнюю версию библиотек, я же использую более раннюю, хоть и с отсутствием некоторых функций, которые мне в принципе и не нужны. P.S. Прочитал о том что такое UART и понял, что мой вопрос был очень глупым
Не работал с этим модулем, глянул старую библиотеку, там нет поддержки SoftwareSerial. Тогда увы не знаю, можно попробовать дописать эту библиотеку. Все нормально, мы все учимся и помогаем друг другу по мере сил.
Решил не создавать новую тему, а задать вопрос в этой. Не знаю, как переписать скетч так, чтобы реакция на кнопку была мгновенной. (на данный момент реакция на кнопку происходит непонятно как, то она срабатывает сразу, то приходится несколько раз нажимать). Причину я нашёл (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 (); } }
Мне кажется, лучше создавать новые темы, во первых Вы быстрее получите ответ. А во вторых, проще будет найти по поиску аналогичную проблему и не задавать одинаковые вопросы другим. Старайтесь избегать 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)); } }
Как показывает реальная практика, защищаться особо обычно вроде как и не надо. Для простых ардуин, если все переменные будут 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 еще нет.
Да, все так и работает. На крайний случай можно перебздеть и ввести временную переменную uint32_t temp = currentMillis - previousMillis; if(temp>=interval){} но, как показывает практика, это лишнее. Не совсем понял про это: Что значит двойной переход через 0? Программа 50 и больше суток не проверяет условие что ли? А в случае измерения интервалов, больших чем 0xFFFFFFFF, данный код вообще не сработает, поскольку интервал не лежит в данном диапазоне. Здесь нужно будет мутить свою функцию. В данном случае грабли могут проявиться и при интервале, приближенном к 0xFFFFFFFF. Причем чем ближе, тем больше шанс. Например, если interval = 0xFFFFFFC, то условие (currentMillis - previousMillis >= interval) будет true только 4ms, и если программа в это время занята чем-то другим, а не проверяет условие, то проскочили, ждем второго - третьего круга.
Именно. Теоретически возможно. Например, программа долго и тупо ждет Serial.available, а когда спустя 50 дней оттуда что-то вдруг свалится, она решит проверить, не истек ли какой-то интервал со времен какого-то старого события. Точно, про такой экзотический вариант среди других, не менее экзотических (я про двойной проход нуля и интервалы больше 0xFFFFFFFF), я как-то не подумал.