Регистратор данных — это электронное устройство или прибор, который записывает данные в течение некоторого периода. Это устройство позволяет записывать данные с меткой о времени или местоположении, которые можно будет посмотреть позже или в реальном времени.
Независимо от природы и типа записываемых данных, регистратор обычно состоит из двух главных частей: сенсорный блок и блок хранения или передачи данных.
Сенсорный блок может включать в себя как внешние измерительные средства, так и датчики, встроенные в устройство.
Всё чаще (но всё же не всегда) устройства регистрации строят на основе микропроцессоров и микроконтроллеров, которые позволили вывести сбор и хранение данных на новый уровень.
Независимо от природы и типа записываемых данных, регистратор обычно состоит из двух главных частей: сенсорный блок и блок хранения или передачи данных. Сенсорный блок может включать в себя как внешние измерительные средства, так и датчики, встроенные в устройство.
Всё чаще (но всё же не всегда) устройства регистрации строят на основе микропроцессоров и микроконтроллеров, которые позволили вывести сбор и хранение данных на новый уровень.
В этом руководстве мы займёмся созданием регистратора на основе платформы Arduino, который каждые несколько минут считывает показатели температуры окружающей среды и сохраняет их на карте MicroSD.
В проекте используются плата Arduino Nano и модуль DS3231, который содержит часы реального времени и датчик температуры.
Для проекта нам понадобятся следующие компоненты:
Для проекта понадобятся следующие компоненты:
Схема сборки модуля регистратора данных
Соедините элементы, как показано на нижеследующей схеме, созданной с помощью программы Fritzing (полный обзор программы по ссылке).
Регистратор данных состоит из платы Arduino Nano, модуля часов реального времени DS3231 и модуля для карты MicroSD. Модуль карты MicroSD обменивается данными с платой Arduino через интерфейс SPI, а модуль DS3231 — через интерфейс I²C. Подробное руководство для модуля DS3231 можно найти здесь.
Для удобства ниже приведены схемы соединения выводов платы и модулей.
Соединение выводов платы Arduino и модуля MicroSD:
SD | Arduino |
GND | GND |
VCC | VCC |
MISO | D12 |
MOSI | D11 |
SCK | D13 |
CS | D10 |
Соединение выводов платы Arduino и модуля DS32316:
DS3231 | Arduino |
VCC | 5 В |
GND | GND |
SDA | A4 |
SCL | A5 |
После подключения всех элементов, мы можем приступить к написанию кода для этого проекта.
Код для модуля регистратора данных
Перед тем как приступить к написанию кода, важно ясно понимать, какими функциями вы хотите его наделить. В рамках этого проекта мы станем измерять температуру и сохранять в текстовом файле на карте SD эти данные и (через запятую) время их получения.
Целью будет извлечение карты спустя некоторое время, копирование данных на компьютер и импорт их в таблицу Microsoft Excel, чтобы составить диаграмму и произвести анализ данных.
Как обычно, первое, что мы делаем в нашем коде, это включаем библиотеки, которые будем использовать. В этом проекте мы используем библиотеку rtc_ds3231, которая доступна по ссылке, а также библиотеки, включаемые заголовочными файлами wire.h, SD.h и SPI.h.
1 2 3 4 5 6 7 8 9 10 |
///////////////////////////////////////////////////////////////// // Arduino Simple Data Logger v1.03 // // Get the latest version of the code here: // // http://educ8s.tv/arduino-data-logger-project // ///////////////////////////////////////////////////////////////// #include "Wire.h" #include "ds3231.h" #include "SD.h" #include "SPI.h" |
Далее мы объявляем переменные. Вначале определяем максимальный размер буфера BUFF_MAX, равный 128, затем определяем вывод платы Arduino (вывод D10), который будет подключен к выводу Chipselect (CS, выбор микросхемы) карты SD. Затем создаём объект файла, объявляем переменные для хранения температуры, времени и даты и объявляем целочисленную переменную для идентификатора элементов данных.
1 2 3 4 5 6 7 8 9 10 11 |
#define BUFF_MAX 128 int CS_PIN = 10; File file; String temperature; String timeString; String dateString; int entryId = 0; |
Объявляем массив для хранения времени и устанавливаем время (в миллисекундах), которое устройство должно прождать перед тем, как регистрировать следующие данные. Это может пригодиться, кроме всего прочего, для экономии заряда батареи во время долгой автономной работы.
1 2 3 4 |
uint8_t time[8]; char recv[BUFF_MAX]; unsigned int recv_size = 0; unsigned long prev, interval = 1000; |
Далее переходим к этапу запуска. Сначала запускаем последовательный интерфейс SPI и шину I²C, после чего инициализируем модуль для карты SD и модуль DS3231.
1 2 3 4 5 6 7 8 9 |
void setup() { Serial.begin(9600); Wire.begin(); initializeSD(); DS3231_init(DS3231_INTCN); memset(recv, 0, BUFF_MAX); |
Затем при первом запуске программы нам будет необходимо установить время. Следующая строка кода используется для установки времени. Оно вводится в следующем порядке:
T”секунды””минуты””часы””день_недели””день_месяца””месяц””год”.
1 |
parse_cmd("T302410426032015",16); //Set time} |
Обратите внимание, что кавычки для каждого элемента не включаются, они приведены только для описания.
После первого запуска программы, строка должны быть закомментирована перед загрузкой для развёртывания системы.
После запуска переходим к функции loop(). Эта главная функция, которая содержит команды для получения температуры и даты и для их сохранения на карте SD.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void loop() { String logEntry; char in; char buff[BUFF_MAX]; unsigned long now = millis(); // Log data once in a while if ((now - prev > interval) && (Serial.available() <= 0)) { getTime(); getTemperature(); logEntry = createLogEntry(); writeEntryToFile(logEntry); prev = now; } if (Serial.available() > 0) { in = Serial.read(); if ((in == 10 || in == 13) && (recv_size > 0)) { parse_cmd(recv, recv_size); recv_size = 0; recv[0] = 0; } else if (in < 48 || in > 122) {; // ignore ~[0-9A-Za-z] } else if (recv_size > BUFF_MAX - 2) { // drop lines that are too long // drop recv_size = 0; recv[0] = 0; } else if (recv_size < BUFF_MAX - 2) { recv[recv_size] = in; recv[recv_size + 1] = 0; recv_size += 1; } } } |
Остальные функции, содержащиеся в коде, мы вызываем из этой главной функции loop().
Полный код для проекта можно скачать по ссылке.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
///////////////////////////////////////////////////////////////// // Arduino Simple Data Logger v1.03 // // Get the latest version of the code here: // // http://educ8s.tv/arduino-data-logger-project // ///////////////////////////////////////////////////////////////// #include #include "ds3231.h" #include #include #define BUFF_MAX 128 int CS_PIN = 10; File file; String temperature; String timeString; String dateString; int entryId = 0; uint8_t time[8]; char recv[BUFF_MAX]; unsigned int recv_size = 0; unsigned long prev, interval = 1000; void setup() { Serial.begin(9600); Wire.begin(); initializeSD(); DS3231_init(DS3231_INTCN); memset(recv, 0, BUFF_MAX); //parse_cmd("T302410426032015",16); //Set time } void loop() { String logEntry; char in; char buff[BUFF_MAX]; unsigned long now = millis(); // Log data once in a while if ((now - prev > interval) && (Serial.available() <= 0)) { getTime(); getTemperature(); logEntry = createLogEntry(); writeEntryToFile(logEntry); prev = now; } if (Serial.available() > 0) { in = Serial.read(); if ((in == 10 || in == 13) && (recv_size > 0)) { parse_cmd(recv, recv_size); recv_size = 0; recv[0] = 0; } else if (in < 48 || in > 122) {; // ignore ~[0-9A-Za-z] } else if (recv_size > BUFF_MAX - 2) { // drop lines that are too long // drop recv_size = 0; recv[0] = 0; } else if (recv_size < BUFF_MAX - 2) { recv[recv_size] = in; recv[recv_size + 1] = 0; recv_size += 1; } } } void parse_cmd(char *cmd, int cmdsize) { uint8_t i; uint8_t reg_val; char buff[BUFF_MAX]; struct ts t; // TssmmhhWDDMMYYYY aka set time if (cmd[0] == 84 && cmdsize == 16) { //T355720619112011 t.sec = inp2toi(cmd, 1); t.min = inp2toi(cmd, 3); t.hour = inp2toi(cmd, 5); t.wday = inp2toi(cmd, 7); t.mday = inp2toi(cmd, 8); t.mon = inp2toi(cmd, 10); t.year = inp2toi(cmd, 12) * 100 + inp2toi(cmd, 14); DS3231_set(t); //Serial.println("OK"); } else if (cmd[0] == 49 && cmdsize == 1) { // "1" get alarm 1 DS3231_get_a1(&buff[0], 59); //Serial.println(buff); } else if (cmd[0] == 50 && cmdsize == 1) { // "2" get alarm 1 DS3231_get_a2(&buff[0], 59); // Serial.println(buff); } else if (cmd[0] == 51 && cmdsize == 1) { // "3" get aging register //Serial.print("aging reg is "); //Serial.println(DS3231_get_aging(), DEC); } else if (cmd[0] == 65 && cmdsize == 9) { // "A" set alarm 1 DS3231_set_creg(DS3231_INTCN | DS3231_A1IE); //ASSMMHHDD for (i = 0; i < 4; i++) { time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // ss, mm, hh, dd } byte flags[5] = { 0, 0, 0, 0, 0 }; DS3231_set_a1(time[0], time[1], time[2], time[3], flags); DS3231_get_a1(&buff[0], 59); // Serial.println(buff); } else if (cmd[0] == 66 && cmdsize == 7) { // "B" Set Alarm 2 DS3231_set_creg(DS3231_INTCN | DS3231_A2IE); //BMMHHDD for (i = 0; i < 4; i++) { time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // mm, hh, dd } byte flags[5] = { 0, 0, 0, 0 }; DS3231_set_a2(time[0], time[1], time[2], flags); DS3231_get_a2(&buff[0], 59); //Serial.println(buff); } else if (cmd[0] == 67 && cmdsize == 1) { // "C" - get temperature register //Serial.print("temperature reg is "); //Serial.println(DS3231_get_treg(), DEC); } else if (cmd[0] == 68 && cmdsize == 1) { // "D" - reset status register alarm flags reg_val = DS3231_get_sreg(); reg_val &= B11111100; DS3231_set_sreg(reg_val); } else if (cmd[0] == 70 && cmdsize == 1) { // "F" - custom fct reg_val = DS3231_get_addr(0x5); // Serial.print("orig "); //Serial.print(reg_val,DEC); //Serial.print("month is "); //Serial.println(bcdtodec(reg_val & 0x1F),DEC); } else if (cmd[0] == 71 && cmdsize == 1) { // "G" - set aging status register DS3231_set_aging(0); } } void getTemperature() { parse_cmd("C",1); temperature = String(DS3231_get_treg()); } void getTime() { String minute; String hour; struct ts t; DS3231_get(&t); if(t.min<10) { minute = "0"+String(t.min); }else { minute = String(t.min); } timeString = String(t.hour)+":"+minute; dateString = String(t.mon)+"/"+t.mday; } String createLogEntry() { String logEntry; entryId ++; logEntry = String(entryId)+","+dateString+","+timeString+","+temperature; return logEntry; } void writeEntryToFile(String entry) { openFileToWrite("log.txt"); Serial.println(entry); writeToFile(entry); closeFile(); } void initializeSD() { pinMode(CS_PIN, OUTPUT); if (SD.begin()) { } else { return; } } int openFileToWrite(char filename[]) { file = SD.open(filename, FILE_WRITE); if (file) { return 1; } else { return 0; } } int writeToFile(String text) { if (file) { file.println(text); return 1; } else { return 0; } } void closeFile() { if (file) { file.close(); } } |
Демонстрация работы регистратора данных
Скопируйте вышеприведённый код в среду Arduino, подключите библиотеки и нажмите кнопку Upload («Загрузить»). Не забудьте установить время, когда будете запускать код в первый раз.
После выполнения всех шагов подождите несколько минут и извлеките карту SD. Если всё сделали верно, вы должны получить на мониторе компьютера при открытии файла на карте примерно то же, что показано на рисунке.
Объём сохранённых данных зависит от того, сколько времени вы отвели на регистрацию данных перед тем как извлечь карту. Например, для этого проекта мы запустили устройство на один день и регистровали данных каждые 5 минут.
По прошествии 24 часов было сделано боле 300 записей (см. рисунок ниже).
Просмотр записанных данных в Excel
ПО Excel значительно облегчает построение диаграмм и анализ данных.
Для построения графика зависимости температуры от времени откройте новую электронную таблицу, в меню «Данные» (Data) выберите пункт «Из текста» (From text), выберите файл на карте SD и нажмите «Импорт» (кнопка OK). ПО Excel запросит подробную информацию о файле.
Выберите вариант «с разделителями»(delimited), а далее — «запятая» (by commas), после чего будет автоматически произведён импорт данных в таблицу.
Чтобы построить график на основе импортированных данных, выберите две колонки времени и температуры (удерживая кнопку Ctrl), затем перейдите на вкладку «Вставка» (Insert) и нажмите «График» (Insert chart). Вы должны увидеть график как на рисунке.
На этом всё на сегодня. Надеюсь, вы узнали что-то полезное из этой статьи. Как обычно, вы можете свободно задавать вопросы и внести свой вклад в раскрытие темы в разделе комментариев под статьёй.
8 комментариев. Оставить новый
Добрый день!
в 112 строчке ошибка ,как исправить?
exit status 1
‘DS3231_INTCN’ was not declared in this scope
Здравствуйте! Скорее всего не подключили библиотеку
Какой датчик температуры используется и, как подключается?
Логгер может собирать любые данные. Какой датчик подключите, те данные и сможете собрать
Здравствуйте. Если я подключу датчик для получения данных об электрических зарядах, то в кодах нужно менять пункты? Если да то какие, есть ли готовые коды или литературу. Посоветуйте пожалуйста. Спасибо.
Здравствуйте. А что будет если закончится память на карте памяти?
Как сделать так, чтобы запись шла по кругу, чтобы новые данные заменяли старые при заполнении карты памяти? Спасибо.
Здравствуйте, если мне надо регистрировать и сохранять данные электрических зарядов, тогда тоже буду пользоваться функцией loop? Если да, то коды поменяются которые прописаны выше или нет. Если надо применять другие коды посоветуйте пожалуйста как могу их получить, литературу или есть готовые решения. Спасибо за ответ.
А есть ли возможность через USB port снимать данные с карты памяти?