====== Бортовой самописец на Arduino Mega ====== Мы решили покорить Космос. Глобальные цели, достигаются малыми шагами. Поэтому первый рывок — покорение линии Армстронга. Мы готовимся запустить Arduino Uno в стратосферу, на высоту не менее 20 километров. {{ :projects:flight-recorder:strato_1.jpg?nolink |}} Чтобы не ограничиваться миганием светодиодами, мы собрали простой бортовой самописец. [[amp>product/arduino-mega-2560?utm_source=proj&utm_campaign=space-two&utm_medium=wiki|Arduino Mega]] получает текущие координаты, время и высоту от [[amp>product/troyka-gps-glonass?utm_source=proj&utm_campaign=space-two&utm_medium=wiki|модуля GPS]] и записывает их через [[amp>product/troyka-sd?utm_source=proj&utm_campaign=space-two&utm_medium=wiki|SD-ридер]] на карту памяти. Альтернативные данные о высоте и температуре мы получаем от [[amp>product/troyka-barometer?utm_source=proj&utm_campaign=space-two&utm_medium=wiki|IMU-барометра]], а забортную температуру — с [[amp>product/sealed-temperature-sensor-ds18b20?utm_source=proj&utm_campaign=space-two&utm_medium=wiki|герметичного датчика DS18b20]]. {{ :projects:flight-recorder:flight-recorder.jpg?nolink |}} Искать шар мы будем по SMS-кам с координатами, которые будут отправляться раз в десять минут с помощью [[amp>product/arduino-gprs-shield?utm_source=proj&utm_campaign=space-two&utm_medium=wiki|GPRS-шилда]]. * Платформы: Arduino Mega 2560 * Языки программирования: Arduino (C++) * Тэги: GPS, GLONASS, стратосфера, ближний космос, бортовой самописец ===== Первое испытание ===== Высокие слои атмосферы не для неженок. Температуры до минус пятидесяти, сильная турбулентность, крайне низкое атмосферное давление и скачки влажности — всё это губительно для любительской электроники. Поэтому до запуска мы протестировали наши самописцы на Земле. {{youtube>-Ad01l9mrUY?large}} Один бортовой компьютер мы оставили в обычных «комнатных» условиях, а второй — на сутки поместили в контейнер с сухим льдом. Показания датчиков писались на SD-карточки и передавались между самописцами по RS-485. Кроме сенсоров мы протестировали аккумуляторы — Power Shield. Питание электроники осуществлялось от обычной розетки, а на аккумуляторах отслеживали саморазряд. ==== Пример кода ==== Для работы ниже приведённого скетча скачайте и установите библиотеки: - {{:projects:flight-recorder:onewire.zip|OneWire}} и {{:projects:flight-recorder:dallastemperature.zip|DallasTemperature}} для работы с [[amp>product/sealed-temperature-sensor-ds18b20?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|датчиком температуры DS18B20]] - [[https://github.com/amperka/TroykaThermometer|TroykaThermometer]] — библиотека для работы с [[amp>product/troyka-temperature-sensor?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|с аналоговым термометром (Troyka-модуль)]] - [[https://github.com/amperka/Troyka-IMU|Troyka-IMU]] — библиотека для работы с [[amp>product/troyka-imu-10-dof?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|IMU-сенсором на 10 степеней свободы]]. - [[https://github.com/amperka/TroykaGPS|TroykaGPS]] — библиотека для работы с модулем [[amp>product/troyka-gps-glonass-extended-receiver?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|GPS/GLONASS]] - {{:projects:flight-recorder:amperkagprs.zip|AmperkaGPRS}} — библиотека для работы с [[amp>product/arduino-gprs-shield?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|GPRS Shield]] // библиотеки для работы с датчиком 18B20 #include #include // библиотека для работы с аналоговым термометром (Troyka-модуль) #include // библиотека для работы с SPI #include // библиотека для работы с SD-картами #include // библиотека для работы I²C #include // библиотека для работы с модулями IMU #include // библиотека для работы с GPS устройством #include // serial-порт к которому подключён GPS-модуль #define GPS_SERIAL Serial1 // serial-порт к которому подключён RS485 #define RS485_SERIAL Serial2 #define RS485_PIN_MASTER 22 // пин подключения CS microSD-карты #define SD_CS_PIN A5 // пин подключения State Volt #define VOLT_PIN A0 // пин подключения датчика 18B20 #define TEMP_18B20_PIN A1 // пин подключения датчика TMP36 #define TEMP_TMP36_PIN A2 // задаём размер массива для времени #define MAX_SIZE_MASS 16 // интервал времени записи данных на карту #define INTERVAL 10000 // создадаём объект для работы с библиотекой OneWire OneWire oneWire(TEMP_18B20_PIN); // создадаём объект для работы с библиотекой DallasTemperature DallasTemperature sensor18B20(&oneWire); // создаём объект для работы с аналоговым термометром // и передаём ему номер пина выходного сигнала TroykaThermometer sensorTMP36(TEMP_TMP36_PIN); // создаём объект для работы с барометром Barometer barometer; // создаём объект класса GPS и передаём в него объект Serial1 GPS gps(GPS_SERIAL); // массив для хранения текущего времени char strTime[MAX_SIZE_MASS]; // массив для хранения текущей даты char strDate[MAX_SIZE_MASS]; // запоминаем текущее время long startMillis = millis(); // данные модулей для записи String dataString = ""; String dataStringRS485 = ""; float temperature18B20; float temperatureTMP36; float temperatureBarometer; float pressureBarometer; float altitudeBarometer; String timeGPS; String dateGPS; String altitudeGPS; float voltPowerShield; int i = 0; void setup() { // открываем последовательный порт для мониторинга действий в программе Serial.begin(115200); // выводим сообщение в Serial-порт о поиске карты памяти Serial.println("Initializing SD card..."); // если microSD-карта не была обнаружена if (!SD.begin(SD_CS_PIN)) { // выводим сообщение об ошибке Serial.println("Card failed, or not present"); // don't do anything more: return; } else { Serial.println("Card initialized"); } // начало работы с датчиком DS18B20 sensor18B20.begin(); // установим разрешение датчика sensor18B20.setResolution(12); // выводим сообщение об удачной инициализации Serial.println("18B20 is OK"); // выводим сообщение об удачной инициализации Serial.println("TMP36 is OK"); // инициализация барометра barometer.begin(); // выводим сообщение об удачной инициализации Serial.println("LPS331 is OK"); // открываем Serial-соединение с GPS-модулем GPS_SERIAL.begin(115200); // выводим сообщение об удачной инициализации Serial.println("GPS is OK"); // открываем Serial-соединение с GPS-модулем RS485_SERIAL.begin(115200); // подаём высокий уровень на пин направление // значит устройство будет передавать данные pinMode(RS485_PIN_MASTER, OUTPUT); digitalWrite(RS485_PIN_MASTER, LOW); // выводим сообщение об удачной инициализации Serial.println("GPS is OK"); } void loop() { getTemperature18B20(); getTemperatureTMP36(); getDataBarometer(); getDataGPS(); getVoltState(); dataString = dateGPS; dataString += "\t"; dataString += timeGPS; dataString += "\t"; dataString += temperatureTMP36; dataString += "\t"; dataString += temperature18B20; dataString += "\t"; dataString += temperatureBarometer; dataString += "\t"; dataString += pressureBarometer; dataString += "\t"; dataString += altitudeBarometer; dataString += "\t"; dataString += altitudeGPS; dataString += "\t"; dataString += voltPowerShield; dataString += "\t"; if (millis() - startMillis > INTERVAL) { readDataRS485(); dataString += "|\t"; dataString += dataStringRS485; // сохраняем данные с модулей на карту памяти saveSD(); // запоминаем текущее время startMillis = millis(); } } void readDataRS485() { dataStringRS485 = ""; RS485_SERIAL.flush(); // если появились данные с модуля RS-485 if (RS485_SERIAL.available() > 0) { while (RS485_SERIAL.available() > 0) { // считываем данные char c = RS485_SERIAL.read(); dataStringRS485 += c; } } else { dataStringRS485 = "No Data from RS485"; } } void getVoltState() { int sensorValue = analogRead(VOLT_PIN); voltPowerShield = sensorValue * (5.0 / 1023.0); } void getDataGPS() { String timeGPSString; String dateGPSString; // если пришли данные с GPS-модуля if (gps.available()) { // считываем данные и парсим gps.readParsing(); // проверяем состояние GPS-модуля switch(gps.getState()) { // всё OK case GPS_OK: gps.getTime(strTime, MAX_SIZE_MASS); gps.getDate(strDate, MAX_SIZE_MASS); timeGPSString = (String)strTime; timeGPS = timeGPSString; dateGPSString = (String)strDate; dateGPS = dateGPSString; altitudeGPS = gps.getAltitude(); break; // ошибка данных case GPS_ERROR_DATA: //Serial.println("GPS error data"); dateGPS = "Error"; timeGPS = "Error"; altitudeGPS = "Error"; break; // нет соединение со спутниками case GPS_ERROR_SAT: //Serial.println("GPS no connect to satellites!!!"); dateGPS = "No Sat"; timeGPS = "No Sat"; altitudeGPS = "No Sat"; break; } } } void getDataBarometer() { temperatureBarometer = barometer.readTemperatureC(); pressureBarometer = barometer.readPressureMillibars(); altitudeBarometer = barometer.pressureToAltitudeMeters(pressureBarometer); } void getTemperature18B20() { // считываем данные с цифрового термометра 18B20 sensor18B20.requestTemperatures(); temperature18B20 = sensor18B20.getTempCByIndex(0); } void getTemperatureTMP36() { // считываем данные с аналогового термометра TMP36 sensorTMP36.read(); temperatureTMP36 = sensorTMP36.getTemperatureC(); } void saveSD() { // создаём файл для записи File dataFile = SD.open("datalog.txt", FILE_WRITE); // если файл доступен для записи if (dataFile) { // сохраняем данные dataFile.println(dataString); Serial.print(dataString); // закрываем файл dataFile.close(); // выводим сообщение об удачной записи Serial.println("Save OK"); } else { // если файл не доступен Serial.println("Error opening datalog.txt"); } } ==== Результаты испытаний ==== * [[https://drive.google.com/open?id=1hAQsNHcrC2s-ccSjlizzCJf2fcxRYFv7|«сырой» лог-файл]] * [[https://drive.google.com/file/d/11CmFhwR33WH45SD6wv7WinlizCJaR6hn/view?usp=sharing|протокол в формате Excel]] ===== Как собрать бортовой компьютер ===== {{ :projects:flight-recorder:arduino-in-space-3.jpg?nolink |}} Перед полётом в космос мы избавились от модулей RS-485 b аналоговых термометров. Перве свою задачу уже выполнили, вторые — не пригодятся вовсе. Наружную температуру мы будем измерять с помощью DS18b20, а температуру в гондоле узнаем от барометра. ==== Необходимые модули ==== * [[amp>product/arduino-mega-2560?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|Arduino Mega 2560]] * [[amp>product/troyka-gps-glonass-extended-receiver?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|GPS приёмник с выносной антенной]] * [[amp>product/arduino-gprs-shield?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|GPRS шилд]] * [[amp>product/troyka-barometer?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|Барометр LPS331AP]] * [[amp>product/sealed-temperature-sensor-ds18b20?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|Герметичный датчик температуры DS18B20]] * [[amp>product/arduino-troyka-shield-lp?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|Плата расширения Troyka Shield LP]] * [[amp>product/arduino-troyka-mega-tail-shield?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|Плата расширения для дополнительных пинов Arduino Mega]] Для питания самописца мы использовали литий-ионные аккумуляторы Samsung INR18650-30Q. ==== Схема сборки ==== На Arduino Mega установим Troyka LP Sield и Troyka Mega Tail Shield {{ :projects:flight-recorder:11.png?nolink |}} Для удобства подключения распаяем DS18B20 на Troyka Protoboard с резистором на 4.7 кОм. Теперь подключим Troyka модули и датчики с помощью трёхпроводных шлейфов. {{ :projects:flight-recorder:2.png?nolink |}} Сверху установим GPRS Shield и переназначим управляющие пины передачи данных с помощью двух проводов мама-мама. {{ :projects:flight-recorder:3.png?nolink |}} Готово! Осталось загрузить код прошивки в Arduino Mega. ==== Код прошивки ==== Для работы ниже приведённого скетча скачайте и установите библиотеки: - {{:projects:flight-recorder:onewire.zip|OneWire}} и {{:projects:flight-recorder:dallastemperature.zip|DallasTemperature}} для работы с [[amp>product/sealed-temperature-sensor-ds18b20?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|датчиком температуры DS18B20]] - [[https://github.com/amperka/Troyka-IMU|Troyka-IMU]] — библиотека для работы с [[amp>product/troyka-imu-10-dof?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|IMU-сенсором на 10 степеней свободы]]. - [[https://github.com/amperka/TroykaGPS|TroykaGPS]] — библиотека для работы с модулем [[amp>product/troyka-gps-glonass-extended-receiver?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|GPS/GLONASS]] - {{:projects:flight-recorder:amperkagprs.zip|AmperkaGPRS}} — библиотека для работы с [[amp>product/arduino-gprs-shield?utm_source=proj&utm_campaign=space-four&utm_medium=wiki|GPRS Shield]] // библиотеки для работы с датчиком 18B20 #include #include // библиотека для работы с SPI #include // библиотека для работы с SD-картами #include // библиотека для работы I²C #include // библиотека для работы с модулями IMU #include // библиотека для работы с GPS устройством #include // библиотека для работы с GPRS устройством #include // serial-порт к которому подключён GPS-модуль #define GPS_SERIAL Serial1 // serial-порт к которому подключён GPRS-модуль #define GPRS_SERIAL Serial3 // пин подключения CS microSD-карты #define SD_CS_PIN 10 // пин подключения датчика 18B20 #define TEMP_18B20_PIN A1 // задаём размер массива для времени #define MAX_SIZE_MASS 16 // интервал времени записи данных на SD-карту #define INTERVAL_SD 1000 // интервал времени передачи данных через СМС #define INTERVAL_SMS 600000 // номер на который будем отправлять сообщение #define PHONE_NUMBER "+74959379992" // создадаём объект для работы с библиотекой OneWire OneWire oneWire(TEMP_18B20_PIN); // создадаём объект для работы с библиотекой DallasTemperature DallasTemperature sensor18B20(&oneWire); // создаём объект для работы с барометром Barometer barometer; // создаём объект класса GPS и передаём в него объект Serial1 GPS gps(GPS_SERIAL); // создаём объект класса GPRS и передаём в него объект Serial1 GPRS gprs(GPRS_SERIAL); // можно указать дополнительные параметры — пины PK и ST // по умолчанию: PK = 2, ST = 3 // GPRS gprs(GPRS_SERIAL, 2, 3); // массив для хранения текущего времени char strTime[MAX_SIZE_MASS]; // массив для хранения текущей даты char strDate[MAX_SIZE_MASS]; // массив для хранения широты в градусах, минутах и секундах char latitudeBase60[MAX_SIZE_MASS]; // массив для хранения долготы в градусах, минутах и секундах char longitudeBase60[MAX_SIZE_MASS]; // запоминаем текущее время long startMillisSD = millis(); // запоминаем текущее время long startMillisSMS = millis(); // данные модулей для записи на карту SD String dataStringSD = ""; // данные модулей для отправки СМС String dataStringSMS = ""; // переменные для хранения данных с датчиков и модулей float temperature18B20; float temperatureBarometer; float pressureBarometer; float altitudeBarometer; String timeGPS; String dateGPS; String altitudeGPS; String latitudeBase60GPS; String longitudeBase60GPS; void setup() { // открываем последовательный порт для мониторинга действий в программе Serial.begin(115200); // выводим сообщение в Serial-порт о поиске карты памяти Serial.println("Initializing SD card..."); // если microSD-карта не была обнаружена if (!SD.begin(SD_CS_PIN)) { // выводим сообщение об ошибке Serial.println("Card failed, or not present"); } else { Serial.println("Card initialized"); } // начало работы с датчиком DS18B20 sensor18B20.begin(); // установим разрешение датчика sensor18B20.setResolution(12); // выводим сообщение об удачной инициализации Serial.println("18B20 is OK"); // инициализация барометра barometer.begin(); // выводим сообщение об удачной инициализации Serial.println("LPS331 is OK"); // открываем Serial-соединение с GPS-модулем GPS_SERIAL.begin(115200); // выводим сообщение об удачной инициализации Serial.println("GPS is OK"); // открываем Serial-соединение с GPRS Shield GPRS_SERIAL.begin(115200); // включаем GPRS-шилд gprs.powerOn(); delay(1000); if (!gprs.begin()) { // если связи нет, ждём 1 секунду // и выводим сообщение об ошибке; Serial.print("GPRS Begin error\r\n"); } // вывод об удачной инициализации GPRS Shield Serial.println("GPRS is OK"); } void loop() { // считываем данные с модулей и датчиков getTemperature18B20(); getDataBarometer(); getDataGPS(); // собираем пакет данных для записи на SD-карту dataStringSD = dateGPS; dataStringSD += "\t"; dataStringSD += timeGPS; dataStringSD += "\t"; dataStringSD += latitudeBase60GPS; dataStringSD += "\t"; dataStringSD += longitudeBase60GPS; dataStringSD += "\t"; dataStringSD += temperature18B20; dataStringSD += "\t"; dataStringSD += temperatureBarometer; dataStringSD += "\t"; dataStringSD += pressureBarometer; dataStringSD += "\t"; dataStringSD += altitudeBarometer; dataStringSD += "\t"; dataStringSD += altitudeGPS; dataStringSD += "\t"; // собираем пакет данных для СМС dataStringSMS = dateGPS; dataStringSMS += " "; dataStringSMS += timeGPS; dataStringSMS += " "; dataStringSMS += latitudeBase60GPS; dataStringSMS += " "; dataStringSMS += longitudeBase60GPS; dataStringSMS += " "; dataStringSMS += temperature18B20; dataStringSMS += " "; dataStringSMS += temperatureBarometer; dataStringSMS += " "; dataStringSMS += pressureBarometer; dataStringSMS += " "; dataStringSMS += altitudeBarometer; dataStringSMS += " "; dataStringSMS += altitudeGPS; dataStringSMS += " "; if (millis() - startMillisSD > INTERVAL_SD) { // сохраняем данные с модулей на карту памяти saveSD(); // запоминаем текущее время startMillisSD = millis(); } if (millis() - startMillisSMS > INTERVAL_SMS) { // отправляем данные СМС сообщениям sendSMS(); // запоминаем текущее время startMillisSMS = millis(); } } // функция считывания данных с GPS-модуля в глобальные переменные void getDataGPS() { // если пришли данные с GPS-модуля if (gps.available()) { // считываем данные и парсим gps.readParsing(); // проверяем состояние GPS-модуля switch(gps.getState()) { // всё OK case GPS_OK: gps.getTime(strTime, MAX_SIZE_MASS); gps.getDate(strDate, MAX_SIZE_MASS); gps.getLatitudeBase60(latitudeBase60, MAX_SIZE_MASS); gps.getLongitudeBase60(longitudeBase60, MAX_SIZE_MASS); timeGPS = (String)strDate; dateGPS = (String)strDate; latitudeBase60GPS = (String)latitudeBase60; longitudeBase60GPS = (String)longitudeBase60; altitudeGPS = gps.getAltitude(); break; // ошибка данных case GPS_ERROR_DATA: dateGPS = "Error"; timeGPS = "Error"; latitudeBase60GPS = "Error"; longitudeBase60GPS = "Error"; altitudeGPS = "Error"; break; // нет соединение со спутниками case GPS_ERROR_SAT: dateGPS = "No Sat"; timeGPS = "No Sat"; latitudeBase60GPS = "No Sat"; longitudeBase60GPS = "No Sat"; altitudeGPS = "No Sat"; break; } } } // функция считывания данных с барометра в глобальные переменные void getDataBarometer() { temperatureBarometer = barometer.readTemperatureC(); pressureBarometer = barometer.readPressureMillibars(); altitudeBarometer = barometer.pressureToAltitudeMeters(pressureBarometer); } // функция считывания данных с датчика 18B20 в глобальную переменную void getTemperature18B20() { // считываем данные с цифрового термометра 18B20 sensor18B20.requestTemperatures(); temperature18B20 = sensor18B20.getTempCByIndex(0); } // функция записи данных с модулей на SD-карту void saveSD() { // создаём файл для записи File dataFile = SD.open("datalog.txt", FILE_WRITE); // если файл доступен для записи if (dataFile) { // сохраняем данные dataFile.println(dataStringSD); Serial.println(dataStringSD); // закрываем файл dataFile.close(); // выводим сообщение об удачной записи Serial.println("Save OK"); } else { // если файл не доступен // выводим сообщение об ошибке Serial.println("Error opening file"); } } // функция отправки данные с модулей СМС сообщением void sendSMS() { char dataSMS[128] = ""; dataStringSMS.toCharArray(dataSMS, 128); gprs.sendSMS(PHONE_NUMBER, dataSMS); Serial.println("SMS sent OK"); } ===== Полёт в стратосферу ===== {{youtube>XO4KWPQMSdc?large}} Полный протокол полёта готовится к публикации. {{ :projects:flight-recorder:profile.jpg?nolink |}} * [[https://drive.google.com/file/d/1lYXRzHtBSLnoobYmI3PJF8WH-Xnp87wp/view?usp=sharing|«Сырые» данные]] первого самописца * [[https://drive.google.com/file/d/1rhYWjvbBGs-tBB2T0lEDOg3TufGVJrkL/view?usp=sharing|«Сырые» данные]] второго самописца