Атмосферный датчик BMP280 вместо BMP180, проблема.
- Войдите на сайт для отправки комментариев
Пнд, 20/11/2017 - 18:56
Доброго времени суток, форумчане. Ну новичек, все дела, вы поняли, в каждом сообщении наверное это пишут, не буду...
Возникла проблема. Есть устройство, в котором используется барометр BMP180. У меня более новый датчик BMP280, к которому не подходит библиотека предыдущего. Установил нужные библиотеки, проверил приложеным примером - работает. Попытался заменить библиотеку в исходном коде на нужную - ловлю ошибку. В общем вопрос такой: как мне прикрутить к этому коду мой датчик?
исходный код:
[code] /* * Raketa-mini или альтиметр в бюджетном варианте. * Программа для записи летных характеристик ракеты * Используется контролер микро ардуино, * барометр BMP085 (лучше использовать BMP180) * ЗУМ(6), кнопка(8) для стирания, светодиод(7) для индикации * база транзистора (9) для вышибного заряда * ПЗУ используется встроенное всего 1кБайт, поэтому * высота записывается 255 раз с выборкой ttt мсек. * в 0 адрес записывается максимальная высота * Высота округляется до 1м. */ //Добавляем библиотеки #include <EEPROM.h> #include <Wire.h> #include <Adafruit_BMP085.h> Adafruit_BMP085 dps = Adafruit_BMP085(); // Digital Pressure Sensor long Al1 = 0; // высота с барометра int hmax = 0; // максимальная высота полета int led = 7; // номер порта для светодиода int knop = 8; // номер порта для кнопки очистки памяти int fire = 9; // номер порта для вышибного заряда int p = 6; // номер порта пьезодинамик int hh = 3; // высота с которой начинается запись полета в память // а так же высота снижения при котором сработает вышибной заряд unsigned long tt = 0; // сюда записывает время начало полета unsigned long t = 1; //время кратное ttt int ttt = 80; //будем записывать каждые ttt мсек (от 20 до 1000 мсек) //20 = 255*20=5100 это примерно 5 сек полета //1000 = 255 секунд полета boolean rec = 0;// 0=память чистая, 1=в памяти хранятся данные о полете void setup() { pinMode(led, OUTPUT); pinMode(p, OUTPUT); //объявляем пин как выход pinMode(knop, INPUT); pinMode(fire, OUTPUT); digitalWrite(fire, LOW); Wire.begin(); Serial.begin(9600); // запуск последовательного порта dps.init(MODE_STANDARD, 0, true); } void loop() { digitalWrite(led, HIGH); //включаем светодиод // Проверяем есть ли данные if (EEPROM.read(0)>1)//если по адресу 0 есть данные подымаем флаг { rec = 1; } int h = 0;//высота полета if (rec == 1)//если есть данные в памяти { vyvod();//вывод информации на экран while (digitalRead(knop) == 0) //ждем нажатия кнопки стирания { delay (100); } digitalWrite(led, HIGH); //включаем светодиод delay (200); digitalWrite(led, LOW); //выключаем светодиод for (int i = 0; i < 512; i++) EEPROM.write(i, 0);//стираем память // EEPROM_int_write(0, 0); Serial.println("Clear"); // печатает в порт rec = 0;//ставим флаг - память чистая }else //если данных нет { do { dps.getAltitude(&Al1);//берем высоту из барометра h=round(Al1*0.01);//округляем высоту до 1м } while (h < hh);// ждем когда ракета подымится на высоту hh digitalWrite(led,LOW); tt = millis();//запоминаем сколько прошло времени с начало работы контролера // начинаем записывать данные for (int i=2; i<511; i++)//начиная со 2 адресса { if (hmax < h)//если текущая высота выше максимальной(значит идет набор высоты) { hmax = h;//записываем новое значение максимальной высоты EEPROM_int_write(0, hmax);//по адресу 0 записываем макс.высоту } else //начинаем снижатся { if (hmax > (h+hh))//ждем пока не опустится ниже hh от макс.высоты { digitalWrite(fire, HIGH); // активируем вышибной заряд } } EEPROM_int_write(i, h);//записываем текущую высоту по адресу i while ((t*ttt)>(millis()-tt)) ; t++; dps.getAltitude(&Al1); h=round(Al1*0.01); i++; } t = 1; // Serial.print(" mil="); // печатает в порт // Serial.println(millis()-tt); // печатает в порт rec=1; digitalWrite(fire, LOW); // turn the LED on (HIGH is the voltage level) while(millis()>0)// будет пищать пока есть питание :) { tone (p, 500); //включаем на 500 Гц delay(100); //ждем 100 Мс tone(p, 1000); //включаем на 1000 Гц delay(100); //ждем 100 Мс } } } // запись в пямять по 2 байта void EEPROM_int_write(int addr, int num) { byte raw[2]; (int&)raw = num; for(byte i = 0; i < 2; i++) EEPROM.write(addr+i, raw[i]); } // чтение из памяти по 2 байта int EEPROM_int_read(int addr) { byte raw[2]; for(byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr+i); int &num = (int&)raw; return num; } // вывод на экран результат полета int vyvod(){ Serial.println(" Polet ="); // печатает в порт Serial.print(" max="); // печатает в порт Serial.println(EEPROM_int_read(0)); // печатает в порт for (int i=2; i<512; i++) { digitalWrite(led, HIGH); Serial.println(EEPROM_int_read(i)); // печатает в порт i++; digitalWrite(led, LOW); delay (50); } } [/code]
Код, который я изменил (изменения только в строчках 18-23)
[code] /* * Raketa-mini или альтиметр в бюджетном варианте. * Программа для записи летных характеристик ракеты * Используется контролер микро ардуино, * барометр BMP085 (лучше использовать BMP180) * ЗУМ(6), кнопка(8) для стирания, светодиод(7) для индикации * база транзистора (9) для вышибного заряда * ПЗУ используется встроенное всего 1кБайт, поэтому * высота записывается 255 раз с выборкой ttt мсек. * в 0 адрес записывается максимальная высота * Высота округляется до 1м. */ //Добавляем библиотеки #include <EEPROM.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BMP280.h> Adafruit_BMP280 bmp; // I2C Adafruit_BMP280 dps = Adafruit_BMP280(); // Digital Pressure Sensor long Al1 = 0; // высота с барометра int hmax = 0; // максимальная высота полета int led = 7; // номер порта для светодиода int knop = 8; // номер порта для кнопки очистки памяти int fire = 9; // номер порта для вышибного заряда int p = 6; // номер порта пьезодинамик int hh = 3; // высота с которой начинается запись полета в память // а так же высота снижения при котором сработает вышибной заряд unsigned long tt = 0; // сюда записывает время начало полета unsigned long t = 1; //время кратное ttt int ttt = 80; //будем записывать каждые ttt мсек (от 20 до 1000 мсек) //20 = 255*20=5100 это примерно 5 сек полета //1000 = 255 секунд полета boolean rec = 0;// 0=память чистая, 1=в памяти хранятся данные о полете void setup() { pinMode(led, OUTPUT); pinMode(p, OUTPUT); //объявляем пин как выход pinMode(knop, INPUT); pinMode(fire, OUTPUT); digitalWrite(fire, LOW); Wire.begin(); Serial.begin(9600); // запуск последовательного порта dps.init(MODE_STANDARD, 0, true); } void loop() { digitalWrite(led, HIGH); //включаем светодиод // Проверяем есть ли данные if (EEPROM.read(0)>1)//если по адресу 0 есть данные подымаем флаг { rec = 1; } int h = 0;//высота полета if (rec == 1)//если есть данные в памяти { vyvod();//вывод информации на экран while (digitalRead(knop) == 0) //ждем нажатия кнопки стирания { delay (100); } digitalWrite(led, HIGH); //включаем светодиод delay (200); digitalWrite(led, LOW); //выключаем светодиод for (int i = 0; i < 512; i++) EEPROM.write(i, 0);//стираем память // EEPROM_int_write(0, 0); Serial.println("Clear"); // печатает в порт rec = 0;//ставим флаг - память чистая }else //если данных нет { do { dps.getAltitude(&Al1);//берем высоту из барометра h=round(Al1*0.01);//округляем высоту до 1м } while (h < hh);// ждем когда ракета подымится на высоту hh digitalWrite(led,LOW); tt = millis();//запоминаем сколько прошло времени с начало работы контролера // начинаем записывать данные for (int i=2; i<511; i++)//начиная со 2 адресса { if (hmax < h)//если текущая высота выше максимальной(значит идет набор высоты) { hmax = h;//записываем новое значение максимальной высоты EEPROM_int_write(0, hmax);//по адресу 0 записываем макс.высоту } else //начинаем снижатся { if (hmax > (h+hh))//ждем пока не опустится ниже hh от макс.высоты { digitalWrite(fire, HIGH); // активируем вышибной заряд } } EEPROM_int_write(i, h);//записываем текущую высоту по адресу i while ((t*ttt)>(millis()-tt)) ; t++; dps.getAltitude(&Al1); h=round(Al1*0.01); i++; } t = 1; // Serial.print(" mil="); // печатает в порт // Serial.println(millis()-tt); // печатает в порт rec=1; digitalWrite(fire, LOW); // turn the LED on (HIGH is the voltage level) while(millis()>0)// будет пищать пока есть питание :) { tone (p, 500); //включаем на 500 Гц delay(100); //ждем 100 Мс tone(p, 1000); //включаем на 1000 Гц delay(100); //ждем 100 Мс } } } // запись в пямять по 2 байта void EEPROM_int_write(int addr, int num) { byte raw[2]; (int&)raw = num; for(byte i = 0; i < 2; i++) EEPROM.write(addr+i, raw[i]); } // чтение из памяти по 2 байта int EEPROM_int_read(int addr) { byte raw[2]; for(byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr+i); int &num = (int&)raw; return num; } // вывод на экран результат полета int vyvod(){ Serial.println(" Polet ="); // печатает в порт Serial.print(" max="); // печатает в порт Serial.println(EEPROM_int_read(0)); // печатает в порт for (int i=2; i<512; i++) { digitalWrite(led, HIGH); Serial.println(EEPROM_int_read(i)); // печатает в порт i++; digitalWrite(led, LOW); delay (50); } } [/code]
ошибка, которую я получаю
Arduino: 1.8.6 Hourly Build 2017/11/07 04:33 (Windows 7), Плата:"Arduino Pro or Pro Mini, ATmega328P (5V, 16 MHz)" D:\Паялка\РђСЂРґСѓРёРЅРѕ\arduino-nightly\libraries\Adafruit_BMP280_Library-master\examples\bmp280test\Raketa_mini\Raketa_mini.ino: In function 'void setup()': Raketa_mini:49: error: 'class Adafruit_BMP280' has no member named 'init' Raketa_mini:49: error: 'MODE_STANDARD' was not declared in this scope D:\Паялка\РђСЂРґСѓРёРЅРѕ\arduino-nightly\libraries\Adafruit_BMP280_Library-master\examples\bmp280test\Raketa_mini\Raketa_mini.ino: In function 'void loop()': Raketa_mini:81: error: 'class Adafruit_BMP280' has no member named 'getAltitude' Raketa_mini:104: error: 'class Adafruit_BMP280' has no member named 'getAltitude' exit status 1 'class Adafruit_BMP280' has no member named 'init' Этот отчёт будет иметь больше информации с включенной опцией Файл -> Настройки -> "Показать подробный вывод во время компиляции"
Заранее благодарен, мучаюсь уже долго, надеюсь на Вашу помощь.
Ну, чего тут, ну нет у класса Adafruit_BMP280 методов init и getAltitude. Нету и взять негде. Да и константа MODE_STANDARD в данной библиотеке не определена.
как мне прикрутить к этому коду мой датчик?.
Либо переписать код так, чтобы не использовать несуществующие методы, либо найти библиотеку в которой они есть.
то есть простой правкой тут не отделатсья? Код переписать я не смогу, именно по этому обратился сюда. Хорошо, я смотрел код библиотеки, он не такой и большой, в таком слуае может можно добавить этот метод в библиотеку?
то есть простой правкой тут не отделатсья? Код переписать я не смогу, именно по этому обратился сюда. Хорошо, я смотрел код библиотеки, он не такой и большой, в таком слуае может можно добавить этот метод в библиотеку?
Думаю, что можно, но наверняка там аналогичный и так есть.
Как я и писал с самого начала - тут именно в этом и проблема, в том, что я меняю библиотеки. Я смотрел код и менял поб себя на столько, на сколько понимаю. А понимаю я немного.
Думаю, что dps.init(...) можно заменить на dps.begin(), а вот с getAltitude() что делать - ума не приложу. Видимо скетч очень старый, ибо в библиотеке с изменениями, сделанными четыре года назад (https://github.com/adafruit/Adafruit-BMP085-Library), уже нет этого метода, а имеется readAltitude(float seaLevelhPa), в вашем же скетче передается высота, а не давление. Т.е. тут еще нужно посмотреть, что было в той, старой, библиотеке.
У меня есть два примера программ для этого датчика, и в обоих используется библиотека BMP085.h , а не Adafruit_BMP085.h
Я так посмотрел, getAltitude в нем упоминается, в 86 строчке.
https://github.com/jarzebski/Arduino-BMP085-BMP180/blob/master/BMP085.h
Я так посмотрел, getAltitude в нем упоминается, в 86 строчке.
Так она ж константная, сама в себе и ни на что не влияет
Просто допишите её в другой класс и ничего не изменится.
Только, у Вас беда в том, что Вы даже не знаете где смотреть надо. Вы совсем ни строчки не пониманаете и просто хотите взять готовую программу, чтобы она заработала? Тогда, Вам, думаю, лучше всё же обратиться в "Ищу исполнителя". Здесь Вы будете очень долго мучиться.
Так она ж константная, сама в себе и ни на что не влияет
Так я для этого и оставил подсказку ТС, чтобы он сам понял, что нужно подставлять в readAltitude(...).Библиотеку и не нужно даже править - просто подумать и в своем скетче изменить пару строк.
Вы совсем ни строчки не пониманаете
Ну пару может и понимаю...)
заменил init на begin, getAltitude на readAltitude и в целом строчка вышла float Al1 = bmp.readAltitude();
скетч скомпилировался без ошибок, но не уверен, выполняет ли он дальше свои функции...
да, не выполняет...
Как вы это поняли? Не исключено, что выполняет, просто не так, как вы ожидаете.
Как вы это поняли? Не исключено, что выполняет, просто не так, как вы ожидаете.
собрал устройство на макетной плате, залил скетч и запустил. При включении ардуино шлет в порт серию данных, а точнее -1 секунд 15, и все. Она и должна слать, но когда получит результат полетных данных. Но алгоритм не выполняется, ждать после включения взлета (подъем на три метра), начинать записывать данные и ждать падения (спуск на 3 метра), активировать систему спасения, звуковой маяк и начинать слать данные в порт, пока не будет нажата кнопка сброса (не ресет, а предусмотренная схемой)
Могу вам только посоветовать на той же плате залить пример из адафруитовской библиотеки, убедиться, что данные с сенсора поступают и их размерность не отличается от тех, что были при BMP085. А дальше - тыкать Serial.println() в места, где производятся вычисления и сравнения. Скорее всего у вас в районе 84-й строки не выполняется условие и нужно понять - почему.
Заработало, вроде. Код, видимо, правильный. Нужно еще несколько раз тесты повторить, что бы убедится. Но работает. Запитал от повербанка и запихнул в... банку, дурацкий каламбур. Примитивным вакуумным насосом откачал давление, через примерно 13 сек. сработал звуковой маяк. Считал данные, регистрируются, устройство считает, что подымается (за счет снижения давления). Это отобразилось в мониторе порта. Как я понял, звуковой маяк (порт 6) активируется, когда заканчивается память. Но запал (порт 9) - только когда будет выполнено условие, что устройство опустилось на три метра. Я это понял на основе эмпирических методов. Когда я успел за 13 сек. откачать воздух и открыть крышку, что бы давление возрасло, запал сработал. В данных тоже показало, что устройство снижалось. Работает значит. В принципе все устраивает, только вот автор скетча заявляет, что памяти хватает на 20 сек. при этом он использовал ардуино с атмегой 168, у меня же 328, в ней на сколько знаю больше встроенной памяти, но хватает на меньшее время... как так?
Может банально выйти, что ракета пролетит больше 13 секунд, и система спасения не сработает...
Если не хватит килобайта EEPROM, можно прилепить внешнюю I2C память на AT24C256 (256 kbit = 32 kbyte serial EEPROM) - https://playground.arduino.cc/Code/I2CEEPROM. Думаю, что ваша ракета не полетит со скоростью, опережающей работу I2C.
...правда, придётся скетчик поперепахать...
Вижу. И не вижу). Я же говорил, что не разбираюсь... Если я вас правильно понимаю, то в строчке 73 и 143 нужно заменить значения с 512 на 1024. А в строчке 88 на 1023, оставив один байт для чего-то там...предпологаю для записи максимальной высоты...
да я бы с удовольствие добавил карту памяти, тем более модуль под нее есть. Но как вы понимаете и видите - сделать я этого не смогу, по крайней мере на данной стадии. Я ардуино начал изучать ровно тогда, когда начал строить именно эту ракету. То есть неделю назад.
Вижу. И не вижу). Я же говорил, что не разбираюсь...
Ну, тут незамысловатый код - достаточно включить логическое мышление, которое у вас есть, раз имитационные опыты с откачкой воздуха проводите. Данные пишутся в энергонезависимую память, такую как EEPROM. Соответственно - в коде нужно найти места, где с ней работают и посмотреть, в каких пределах изменяется адресация. В данный момент, как я вижу, адрес ограничен ячейкой #512, что соответствует 512 байтам 168-й атмеги. Отсюда вывод - если есть больше памяти, нужно сдвигать ограничение в бОльшую сторону.
да я бы с удовольствие добавил карту памяти, тем более модуль под нее есть. Но как вы понимаете и видите - сделать я этого не смогу, по крайней мере на данной стадии. Я ардуино начал изучать ровно тогда, когда начал строить именно эту ракету. То есть неделю назад.
Мне кажется, что карта памяти не лучший вариант в ракете, которую может ударить обо что-то - механическое соединение в слоте. Да и сложный для вас программный интерфейс, энергопотребление... EEPROM в восьминогом DIP-корпусе, который можно надежно припаять и закрепить - нормальное бюджетное решение. На мой взгляд.
Кстати, у 280-го боша есть набор разных режимов работы, которые будут давать результаты с большей скоростью или большей точностью, а так же встроенная функция оверсемплинга и фильтр, позволяющий сгладить шумы АЦП (см даташит). Правда, адафруитовская библиотека их не позволяет выставить, но возможно, что иные варианты, включая самописные (я баловался как-то с этим сенсором), позволят их активировать.
Да, это сработало! Время выросло ровно в 2 раза. Наконец-то можно переходить от макетки к изготовлению! Очень благодарен за помощь, особенно вам, sadman41, за вашу уникальную способность не только помочь, но и заставить учиться!
Но я, возможно, еще обращусь)
на счет разных режимов работы этого датчика. Да, я знаю, что они есть. Именно по этому мне пришлось удалить код, который задавал режим работы для датчика, который изначально использовался в этом скетче. Я выкладывал в первом посте сообщение ошибки, там фигурирует этот момент, MODE_STANDARD вроде. Я пытался заменить на MODE_NORMAL, как это указано для этого датчика, но была ошибка. Я удалил , по примеру из библиотеки. Так что этот вопрос остался для меня открытым.
Интересный датчик и устройство. Почитал поискал, хотелось бы разобраться с настройкой его режимов.
Datasheet почитал, страницы 12, 24
Разрешение находится в регистре 0xF4 2,3,4 ,бит. Если на максимум поставить разрешение то записать нужно это (см. скетч)? (адрес датчика взял из библиотеки). Как не затронуть остальные биты адреса и как их перписывать если вписать нужно значение 0?
Установка режима производится так: в BMP280_REGISTER_CONTROL (0xF4) нужно заслать ((BMP280_OSRS_T << 6) | (BMP280_OSRS_P << 3) | BMP280_MODE), где BMP280_OSRS_T и BMP280_OSRS_P - значения из Table 5 даташита (она для osrs_t, но соответствие oversampling и бинарного значения справедливо и для osrs_p, по-моему), а BMP280_MODE - из Table 10 (0x01 для Forced-режима или 0x03 для Normal).
Предварительно (так как конверсия/цикл конверсий начинается после установки значений в 0xF4) и однократно в BMP280_REGISTER_CONFIG (0xF5) можно закинуть ((BMP280_STANDBY_TIME_MS << 6)| (BMP280_FILTER_COEF << 3) | BMP280_SPI_ENABLE)). BMP280_STANDBY_TIME_MS из Table 11, BMP280_FILTER_COEF - из Table 6, BMP280_SPI_ENABLE = 0, если используется I2C.
С режимами есть небольшая заморочка - в Normal mode конверсии осуществляются циклически и результаты перемещаются во внутренний массив. Команда чтения с I2C останавливает перемещение до того момента, пока чтение не будет завершено. Т.е. для того, чтобы получить данные с одного замера, а не с разных, массив нужно читать одной трансмиссией, а не по байту за трансмиссию. Таким образом, если вы не можете обеспечить такой режим считывания, следует перевести датчик в Forced mode, в котором он будет работать как BMP180 и запускать конверсию, когда попросят. Однако и тут есть ловушка - при операции чтения в этом режиме вы получаете не актуальные данные, а данные прошлого замера. Т.е. для редкого получения актуальных данных необходимо проводить две конверсии сразу и только после этого проводить чтение, если же конверсии запускаются в быстром цикле, то достаточно однократного запуска конверсии и последующего забора данных.
Надеюсь, что это вам поможет ))
PS. замену режима можно провести чтением из регистра (положим 0xF4), наложением битовой маски и засылкой обратно. Но проще - тупо закидывать туда байт целиком.
Можно ли забрасывать значения вида (для BMP280_OSRS_P) Wire.write(0b111<<3); ?
Не советую. Потому что в этом случае все остальные биты придут туда нулевыми. А непредсказуемого поведения датчика вам не нужно.
Как то так?
p.s. ошибок навыдавало
p.p.s. исправил чар на байт
Наложение маски - да, в принципе. Только излишние присвоения не нужны. А остальное... Байт как char зачем объявлять, если есть byte? 0b0.. не пишут, пишут B00011100. В одной трансмиссии, как мне помниться, нельзя одновременно и чтение и запись делать. Чтение - одна трансмиссия, запись - вторая.
Не понял я в чем была ошибка, переписал часть от руки и все пошло. (какой то обратный слэш был причиной, я его не видел-невидимый). Почитал, переписал, исправил.
И что, работает? По моему мнению нужно действовать так:
Я вас понял. В примерх которые смотрел реквест без трансмишн
Здесь я ошибку допустил
Wire.requestFrom(0x77, 1);
нужно
Wire.requestFrom(0xF4, 1);
feonor12 с Вами можно как нибудь связаться, есть общий интерес моё мыло vakulenko78@mail.ru dmitriyvakulenko1978@gmail.com