Вольтлоггер восьмиканальный
Давно я не брал в руки метлу, но тут пришлось, т.к. братья китайцы ничего не смогли мне предложить.
Задача - наблюдать на графике заряды и разряды последовательно подключенных аккумуляторов в автономных системах, уровень напряжения солнечных панелей и ветрогенераторов да все это в течение длительного времени - сутки-двое-трое...
В общем пришлось создать простенький вольтлоггер, а по русски восьмиканальный напряжометр с записью на СДху.
Основа Ардуино нано, все остальное из подножного хлама.
Кликабельно (если кому-то порвет монитор - это ваши проблемы, зато все видно)
Схема простейшая, лень даже рисовать. Вот вам подключение SD карты без шилдов. Ради разъема раздербанил картридер из фикспрайса.
К ардуине цепляем делители (по принципу как для SDхи) для чтения напряжения через АЦП к ногам А0-А7. Без делителей напряжения мерять можно только до 5 Вольт, для моих задач мало.
Делители считаем на 5В, что бы максимальное напряжение на входе давало 5В на выходе. И сопротивление реальных резисторов заносим в скетч в специальный массив.
Например: А0 подключен напрямую к плюсу АКБ без делителя, ведь там напряжение не превысит 4,3В
На А1 у меня напряжение для замера не должно превышать 16В (один сильно перезаряженный АКБ). Соответственнов на делитель идут резисторы на 4,3кб и 2,0кб. Реально же получились 4300 Ом и 1984 Ома - эти данные в соответствующую позицию массива и заносим.
А2 - 32В (15к и 2,7к) - два АКБ последовательно
А3 - 48В (23к и 2,7к) - три АКБ последовательно
А4 - 64В (18к и 1,5к) - четрые АКБ последовательно
а дальше мне не нужно, но до А7 - можно использовать по тому же принципу. Если на порту подключен делитель и замер не производится - в логах будет ноль. Если нога ардуины без делителя и не подтянута к земле - в логах сыпется замер наводок, порядка 1,5В - на графиках ниже это видно. Это нормально.
Упаковал все в маленький корпус от какой-то китайчатины. Аккумулятор для питания ардуины взял что попался на 1500 мАч, от какого-то сотового. Так же использовал мелкую платку DC-DC на 5В на выходе, забыл отметить на фото, приклеена к АКБ сверху, красная плата под основными делителями. Никакие шилды не использовал.
Вкорячивал все ногами - по супер плотному монтажу видно, но все влезло и работает. Светодиод состояния взял двухцветный с общим катодом от сидюка, токоограничители по 330 Ом подключены на D2 и D3. Горит зеленый и гаснет раз в 15 секунд - все нормально, запись прошла, горит красный - жопа!. Файл на флешке открывается на запись и сразу закрывается. Флеха проживет не долго, лет 150, но зато выдергивать ее можно чуть ли не влюбой момент - все сохранится.
Все измеренные данные льются так же и в последовательный порт, можно подключить комп и посмотреть.
Скрин с выводимыми данным в порт, можно заморочиться с экраном, но мне это лишнее.
Скетч с подробными комментами под спойлером, не разберется только ленивый
- Скетч Вольтлоггера v4
-
/****************************************************************************
* Восьмиканальный вольтлоггер для последовательно соединенных
* аккумуляторных или солнечных батарей с записью данных на SD карту
*
* http://www.roft.ru
* http://www.kavelsib.ru
*
****************************************************************************/
#include //Библиотека работы с SD картой
const int chipSelect = 10; //нога чтения данных из SD карты
// резисторы делителей напряжения по портам А0-А7, измерь как можно точнее и занеси в массив
//от их точности зависит точность измерений
const float r1[8] = {0, 4300, 15115, 23410, 18210, 0, 0, 0}; // Омы к вх. напряжению по портам А0-А7
const float r2[8] = {99999999999, 1984, 2691, 2676, 1495, 99999999999, 99999999999, 99999999999}; // Омы подтяжки к земле по портам А0-А7
//нули и девятки там, где делитель не нужен и идет прямой замер,
//например напряжения питания или напряжение батарейки
// эту константу (typVbg) необходимо откалибровать индивидуально, у каждой платы свой внутренний опорник
//используй качественный вольтметр и подстрой показания по напряжению питания
const float typVbg = 1.068; // по докам между 1.0 -- 1.2
float Vcc = 0.0;
float MaxVoltage = 0.0;
#define COUNT 20 //сколько замеров АЦП, чем больше - тем точнее, но дольше каждый замер напряжений
float curVoltage;
String Filename = "log"; //перфикс файла
String dataString = ""; //создание строки для сбора данных в лог
void setup() {
pinMode(2, OUTPUT); //Светодиод ошибки
pinMode(3, OUTPUT); //Светодиод норм
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("--- Setup ");
Serial.print("
");
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println(" FAILED!!!! ");
digitalWrite(2, HIGH); //Если карта не инициализировалась - зажигаем светодиод ошибки и
digitalWrite(3, LOW); //гасим светодиод норм
// don't do anything more:
return;
}
Serial.println(" OK");
digitalWrite(2, LOW); //Если карта инициализировалась - гасим светодиод ошибки и
digitalWrite(3, HIGH); //зажигаем светодиод норм
// Определяем имя файла в который будем писАть
int Addname=1;
while (SD.exists(Filename + Addname+ ".csv"))//ищем имя файла, которого нет на карте
{
Serial.print ("file ");
Serial.print (Filename + Addname+ ".csv");
Serial.print (" exist");
Serial.print("
");
Addname=Addname+1;
}
Filename=Filename+Addname+".csv"; //собираем имя файла
Serial.print ("log file: ");// объявляем куда будем писать в этот раз
Serial.print (Filename);
Serial.print("
");
delay(1000); //ожидание секунды перед замером
// определение опорного напряжения
analogReference(DEFAULT); // DEFAULT INTERNAL использовать Vcc как AREF
delay(100);
Vcc = readVcc();
MaxVoltage = Vcc / (r2[1] / (r1[1] + r2[1])); //определяем макс напр при замере с учетом резисторов делителя
//analogWrite(A_PIN, 0);
Serial.print("Vcc = ");
Serial.println(Vcc, 4);
Serial.print("Max Vm = ");
Serial.print( MaxVoltage, 4 );
Serial.print("
");
Serial.print("---------------");
Serial.print("
");
delay(200);
dataString += "T,s;Vcc;Vbat;A1;A2;A3;A4;A5;A6;A7
";
}
void loop() {
unsigned long stime=millis()/1000; //замер времени
dataString += stime;
dataString += ";";
dataString += Vcc;
dataString += ";";
// читаем 8 портов и пишем в строку
for (int analogPin = 0; analogPin < 8; analogPin++) {
Vcc = readVcc();
// считываем точное значение АЦП с A0-A7, где находятся делители напряжения
curVoltage = 0.0;
for (int i = 0; i < COUNT; i++) {
curVoltage = curVoltage + analogRead(analogPin);
delay(10);
}
curVoltage = curVoltage / COUNT;
float v = (curVoltage * Vcc) / 1024.0;
float v2 = v / (r2[analogPin] / (r1[analogPin] + r2[analogPin]));
Serial.print(stime);
Serial.print(" Vm");
Serial.print(analogPin);
Serial.print("= ");
Serial.print(v2, 4);
Serial.print("/ Vcc=");
Serial.print(Vcc, 4);
Serial.print("
");
delay(100);
dataString += v2;
if (analogPin < 7) {
dataString += ";";
}
}
// открываем файл
File dataFile = SD.open(Filename, FILE_WRITE);
// если файл доступен - пишем туда:
if (dataFile) {
digitalWrite(3, LOW); //погасили светодиод норм
dataFile.println(dataString);
dataFile.close();
// print to the serial port too:
Serial.print(dataString);
Serial.print("
");
delay(1000);
digitalWrite(3, HIGH); //зажгли светодиод норм
dataString = ""; //очистили строку
}
// если файл не открывается - пишем егогг и зажигаем ошибку
else {
Serial.print("ERROR opening file !!!
");
digitalWrite(2, HIGH); //Если файл недоступен - зажигаем светодиод ошибки и
digitalWrite(3, LOW); //гасим светодиод норм
}
delay(10000); //вкупе примерно 15 сек между измерениями
}
/****************************************************************************
* Функции
****************************************************************************/
float readVcc() {
byte i;
float result = 0.0;
float tmp = 0.0;
for (i = 0; i < COUNT; i++) {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
// works on an Arduino 168 or 328
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(3); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
tmp = (high<<8) | low;
tmp = (typVbg * 1023.0) / tmp;
result = result + tmp;
delay(5);
}
result = result / COUNT;
return result;
}
Пояснения по коду: замер ведется в несколько этапов. Сначала меряем 20 раз напряжение питания относительно внутреннего источника напряжения ардуины 1,1В и берем среднее.
Потом относительно измеренного напряжения питания меряем по 20 раз и усредняем напряжение на портах А0-А7. Точность до 4го знака после запятой не обещаю, но у меня 2й знак стабильно совпадает с приличным мультиметром, т.ч. для бытового применения пойдет. Точность зависит от резисторов делителя - берите классом повыше, но если нет - пойдут любые, главное измерьте их сопротивление поточнее и занесите в массив, можно вычислить их и программно, если нет точного прибора, но придется долго перебирать значения.
И выставьте в скетче точно напряжение внутреннего референсного источника ардуины - его нельзя измерить, только подобрать опытным путем. По даташиту лежит в пределах от 1,0 до 1,2 В, у меня 1,068В. Меняем это напряжение до тех пор, пока показания ардуины и показания вашего суперточного напряжометра (вольтметра или мультиметра) не сойдутся. Измеряем напряжение питания 5В и на нем подбираем референсное последовательным приближением.
Если надо подстроить измеренное напряжение по портам А0-А7 - меняйте данные о сопротивлении делителей, в пределах 10-50 Ом у меня все устаканилось.
А теперь самое интересное - графики! то ради чего все затевалось.
Данные пишутся в формате CSV, в качестве разделителя - точка с запятой. Ёксель или опен офис прекрасно понимает. Перед построением графика нужно заменить все точки на зяпятые для жителей постсоветского пространства (десятичный разделитель в соответствии с локализацией своего компа)
Первый график - напряжение аккумулятора в автомобиле от включения зажигания и до выключения зажигания. Видна работа бортового оборудования, работа генератора, включение жопогреек и пр.
Колонки по порядку: T's - время в секундах от момента включения ардуины
Vcc - напряжение питания, чтоб знать, что все хорошо
Vbat, оно же А0, оно же Vm0 - напряжение внутреннего АКБ
А1-А7 замеряемые напряжения по портам.
Второй график - работа умной зарядки на AGM аккумуляторе - постепенный подъем напряжения вкупе с редкими контролями при отключенном заряде.
Внизу видно напряжение питания ардуины и напряжение встроенного АКБ. АКБ к концу замера упал до 2,9В, что здоровья ему не прибавляет. Но т.к. мне данные важнее, то DC-DC будет его сосать до 0,9В - это убъет АКБ, но я буду получать информацию до последнего.
Тот график ради которого эта железка и создавалась. Количество энергии выработанное солнечной батареей за день. В данном случае на графике 31 октября 2016г, день был пасмурный с редкими просветами солнца, после обеда небо совсем заволокло и пошел дождь.
Данным логгером можно снимать такие графики с 8 шт солнечных батарей одновременно и потом сравнивать выработку находя слабые места или бракованные панели. Заряда встроенного аккумулятора хватило на сутки непрерывного замера и судя по напряжению Vbat еще осталась половина.
Обратите внимание - у всех портов для замера будет общий минус. Соответственно 8 каналов - это зависимые каналы. Т.е. для наблюдения последовательно соединенными аккумуляторами пойдет, а для отдельно стоящими - нет, только по одному, если нет возможности их объединить минусами.
Есть вопросы - пишите. Можно в личку.
- Самодельная автоматическая система полива
- Светодиодная подсветка в LEDниковый период
- Diversion (Dump) Load Controller
- Подставка-подъемник для кросс/эндуро мотоцикла
- Эндурасты, снег, и все остальные. Часть первая: Мототоксикоз
- Питание энергосберегающих ламп от 12 Вольт
- Компрессор вместо насоса для велосипеда
Комментариев: 2
Круто, а что с замерами по току? Давно хотел себе наа солнечные батареи поставить логгер выработки. Кстати, покупал у вас в магазине, Электромаг - праильно я понимаю, это же в нем работаете? Прошлым летом покупал у вас панели и все работает. Спасибо.
New search engine. - 1000 000$