====== Бортовой самописец на 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|«Сырые» данные]] второго самописца