Погружение устройства в сон по достижении заданного минимального напряжения на аккуме - и наоборот
- Войдите на сайт для отправки комментариев
Втр, 24/11/2020 - 14:40
Здравствуйте, уважаемые участники. Ниже - немножко подпиленный код из топика "Барометр-барограф", адаптированный под свои нужды.
Всё работает, кроме одного - устройство не засыпает (не переходит в режим с потреблением единиц микроампер) по достижении заданного в скетче минимального напряжения на литиевом аккумуляторе 14500. Ток потребления остаётся в районе нескольких сот микроампер, причём течёт он до "полного не могу", уже с потухшим дисплеем, проваливая напругу на аккумуляторе глубоко ниже 2 В...
Пробовал разбираться с библиотекой LowPower. Пытался, "по образцу и подобию", вставлять строчки из скетча гайверовского "Предсказателя погоды" (там тоже используется эта же библиотека), но что-то ничего не выходит... Всё компилируется без замечаний, но устройство продолжает потреблять значительный ток при напряжении питания ниже заданного порога, с уже погасшим экраном. Ниже в этом сообщении привожу рабочий исходник, без моих правок, в котором работает всё, кроме засыпания.
#include <LCD5110_Graph.h> // rinkydinkelectronics.com/library.php #include <Wire.h> #include <Adafruit_BMP085.h> // github.com/adafruit/Adafruit-BMP085-Library #include <LowPower.h> // github.com/rocketscream/Low-Power Adafruit_BMP085 dps; // ********** Опции компиляции ************** //#define SeaLevel // раскоментировать для коррекции на высоту //#define debug // для тестирования графиков (ан.вход a1 как задатчик давления) #define cont 67 // для изменения раскоментировать эту строку (70 по умолчанию) #define zapolnen // Заполнение графика 1 //#define gasim_lcd // если экономим батарейки гашением LCD long P_win = 165 * 133.3; // Верхняя граница абс. графика по умолчанию до сдвига (600 + 165) = 765мм #if defined (SeaLevel) // если не включена коррекция, экономим память float Alt_corr = 32.4; // Коррекция высоты в метрах. #endif uint32_t Pressure; float temp; unsigned char sotki ; // сотые для давления unsigned char p_count, p2; long P_Min, P_Max, P_Dt, P_Mid; uint16_t P_Mem[217]; //-79980 = 600мм (3 day 20min memory, 72 per 24 hour) signed char x, y, last_x, last_y; // nokia 5110 connect // pin d3 - Serial clock out (SCLK) // pin d4 - Serial data out (DIN) // pin d5 - Data/Command select (D/C) // pin d6 - LCD reset (RST) // pin d7 - LCD chip select (CS) LCD5110 myGLCD(3, 4, 5, 6, 7); // bmp180 connect // sda - a4 // scl - a5 // Use pin 2 as wake up pin const char wakeUpPin = 2; // d2 для кнопки const char lightPin1 = 12; // d11 подсветка дисплея const char lightPin2 = 11; // d12 инверсная подсветка дисплея const char batPin = 0; // a0 - ан. вход для контроля батареи 220к/51к volatile char scr_num = 0; // Номер экрана для отображения volatile boolean key_pressed = false; // Признак что кнопку жали volatile boolean in_int = false; // Признак нахождения в прерывании volatile boolean Refresh = true; // Признак что нужно обновить отрисовку (для экономии батарей) volatile boolean Lcd_On = true; // Признак что дисплей включен volatile char light_on = 1; // длительность подсветки при включении 4*4 = 16с. extern unsigned char BigNumbers[]; extern unsigned char MediumNumbers[]; extern unsigned char SmallFont[]; extern unsigned char TinyFont[]; // внешние шрифты unsigned char tik = 0; // счетчики для времени unsigned char Count_Minute = 0; uint32_t Son_count = 0; //******************************** START *************************************** void setup() { myGLCD.InitLCD(); #if defined (cont) myGLCD.setContrast(cont); //регулировка контраста при необходимости #endif myGLCD.setFont(SmallFont); Wire.begin(); analogReference(INTERNAL); //1.1v internal op.source pinMode(batPin,INPUT); //Вход для контроля батареи #if defined (debug) pinMode(1,INPUT); // а1 - задание давления analogRead(1); #endif LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF); //dps.begin(BMP085_ULTRALOWPOWER); dps.begin(BMP085_STANDARD); //dps.begin(BMP085_HIGHRES); //dps.begin(BMP085_ULTRAHIGHRES); pinMode(wakeUpPin, INPUT); // Configure wake up pin as input. digitalWrite(wakeUpPin, 1); // pullup pinMode(lightPin1, OUTPUT);// lcd light pin as output; pinMode(lightPin2, OUTPUT);// lcd light inv pin as output; #if defined (SeaLevel) Pressure = dps.readSealevelPressure(Alt_corr); // с коррекцией на уровень моря #else Pressure = dps.readPressure(); // без коррекции к уровню моря #endif P_Mid = Pressure - 79980; // (79980 = 600mm) отсекаем лишнее P_Mem[p_count] = P_Mid; // заносим в память первое значение давления для правильной работы усреднения // Serial.begin(9600); // while (!Serial) ; // wait for Arduino Serial Monitor // delay(200); } void loop() { // ********************************************************* // Allow wake up pin to trigger interrupt on low. #if defined (debug) #else if ((Son_count < 5)||((!(Son_count % 5))&&(tik==0))) { // через 5 минут активности, не заходим в тело по 5 минут #endif // Serial.print("S=");Serial.print(Son_count);Serial.print(" %=");Serial.println(Son_count % 5); detachInterrupt(0); // запрет прерываний #if defined (SeaLevel) Pressure = dps.readSealevelPressure(Alt_corr); // с коррекцией на уровень моря #else Pressure = dps.readPressure()+0; // без коррекции к уровню моря; "-73" = коррекция данных давления (подставить нужный знак и число Па) #endif temp = dps.readTemperature()-0; // "-0.5" = коррекция данных температуры (подставить нужный знак и число градусов Цельсия) attachInterrupt(0, wakeUp, LOW); // восстановить прерывания от кнопки #if defined (debug) Pressure = map(analogRead(1),0,1023,730*133.3,790*133.3); // для отладки резистор на а1 - задатчик давления #endif // для экранов с инверсным подсветом использовать lightPin2 if (light_on > 0) { digitalWrite(lightPin1, 1);digitalWrite(lightPin2, 0); light_on--; } else {digitalWrite(lightPin1, 0);digitalWrite(lightPin2, 1);} // lcd light on/off if (Pressure > 79900){ // 600mm P_Mid = (P_Mid + (Pressure - 79980))/2; // усреднение замеров, отбрасываем показания ниже 600мм.(сбоит бывает) } #if defined (debug) if (Count_Minute >= 2){ Refresh = true; // раз в секунду #else if (Count_Minute >= 20){ Refresh = true; // каждые 20 мин. запоминаем усредненное значение в память и обновляем графики #endif if (p_count == 216) { for (int i = 0; i < 216; i++) // сдвигаем если буфер полон { P_Mem[i] = P_Mem[i+1]; } } else { p_count++; } P_Mem[p_count] = P_Mid; if (Pressure > 79980){ P_Mid = Pressure - 79980; // новый старт усреднения } Count_Minute = 0; } #if defined (gasim_lcd) if ((Son_count < 3)&&(!Lcd_On)) {myGLCD.disableSleep(); Lcd_On = true;} // Если разрешено, гасим дисплей через 15 мин. if ((Son_count >= 3)&&(Lcd_On)) {myGLCD.enableSleep(); Lcd_On = false;} #endif // Отображение экранов if (Lcd_On) {switch (scr_num) { case 0: // summary screen Refresh = true; // Обновить отрисовку каждый цикл myGLCD.clrScr(); // Очистка экрана myGLCD.setFont(BigNumbers); //Давление с BMP180 myGLCD.printNumI((Pressure / 133.3), 15, 5, 3); //расчет атмосферного давления; myGLCD.setFont(SmallFont); myGLCD.print(".", 57, 22); sotki = ((Pressure / 133.3)-int(Pressure / 133.3))*100; myGLCD.printNumI((sotki / 10), 63, 22, 1);// десятые; myGLCD.printNumI((sotki % 10), 69, 22, 1);// сотые myGLCD.print("Hg", 63, 6); myGLCD.print("C", 29, 35); myGLCD.printNumF(temp, 1, LEFT, 35); //температура myGLCD.print("V", RIGHT, 35); myGLCD.printNumF(analogRead(batPin)/176.5, 2, 53, 35); //напряжение питания (коэффициент xxx.x подбирается, точка и десятые обязательны) //одна единица коэффициента (ххХ.х) =~ 0,02 В; (чем коэффициент >, тем напряжение <) myGLCD.setFont(TinyFont); myGLCD.print("o", 26, 33); break; case 1: // давление в кПа ***************************************************** Refresh = true; // Обновить отрисовку каждый цикл myGLCD.clrScr(); // Очистка экрана myGLCD.setFont(MediumNumbers); //Давление с BMP180 myGLCD.printNumI(Pressure, CENTER, 1); //расчет атмосферного давления; myGLCD.setFont(SmallFont); //myGLCD.print(".", 57, 22); // sotki = ((Pressure/1) - int (Pressure/1))*100; //myGLCD.printNumI((sotki / 10), 63, 22, 1);// десятые; // myGLCD.printNumI((sotki % 10), 69, 22, 1);// сотые myGLCD.print("Pa", CENTER, 22); myGLCD.print("C", 29, 35); myGLCD.printNumF(temp, 1, LEFT, 35); //температура myGLCD.print("V", RIGHT, 35); myGLCD.printNumF(analogRead(batPin)/176.5, 2, 53, 35); //напряжение питания (коэффициент xxx.x подбирается, точка и десятые обязательны) //одна единица коэффициента (ххХ.х) =~ 0,02 В; (чем коэффициент >, тем напряжение <) myGLCD.setFont(TinyFont); myGLCD.print("o", 26, 33); break; case 2: // график давления за сутки с масштабированием if (Refresh){ // Рисуем если разрешено обновить P_Min = P_Mem[p_count]; P_Max = P_Min; // сбросить значения if (p_count < 72) { p2 = 0; } else { p2 = p_count - 72; } for (int i=p_count; i>p2 ; i--) { if (P_Min > P_Mem[i]) { P_Min = P_Mem[i]; } if (P_Max < P_Mem[i]) { P_Max = P_Mem[i]; } } P_Dt = P_Max - P_Min; // макс. разница myGLCD.clrScr(); // Очистка экрана myGLCD.setFont(TinyFont); // Установка набора символов x=77; if (P_Dt > 0){ for (int i=p_count; i>p2 ; i--) { y = ((36 * (P_Max - P_Mem[i]))/P_Dt)+6; if (x == 77) { myGLCD.setPixel(x, y); } else { myGLCD.drawLine(last_x, last_y, x, y); } last_x = x; last_y = y; x--; } } myGLCD.print("Max:", LEFT, 0); myGLCD.printNumF((P_Max +79980)/ 133.3, 2, 20, 0); myGLCD.print("24 Hrs", RIGHT, 0); myGLCD.print("Min:", LEFT, 42); myGLCD.printNumF((P_Min+79980) / 133.3, 2, 20, 42); } break; case 3: // график давления за трое суток if (Refresh){ // Рисуем если разрешено обновить P_Min = P_Mem[p_count]; P_Max = P_Min; // сбросить значения for (int i=p_count; i>0 ; i = i - 3) { if (P_Min > P_Mem[i]) { P_Min = P_Mem[i]; } if (P_Max < P_Mem[i]) { P_Max = P_Mem[i]; } } P_Dt = P_Max - P_Min; // макс. разница myGLCD.clrScr(); // Очистка экрана myGLCD.setFont(TinyFont); // Установка набора символов x=77; if (P_Dt > 0){ for (int i=p_count; i>0 ; i = i - 3) { y = ((36 * (P_Max - P_Mem[i]))/P_Dt)+6; if (x == 77) { myGLCD.setPixel(x, y); } else { myGLCD.drawLine(last_x, last_y, x, y); } last_x = x; last_y = y; x--; } } myGLCD.print("Max:", LEFT, 0); myGLCD.printNumF((P_Max +79980)/ 133.3, 2, 20, 0); myGLCD.print("72 Hrs", RIGHT, 0); myGLCD.print("Min:", LEFT, 42); myGLCD.printNumF((P_Min+79980) / 133.3, 2, 20, 42); } break; /*case 4: // напряжение аккумулятора для контроля Refresh = true; // Обновить отрисовку каждый цикл myGLCD.clrScr(); // Очистка экрана myGLCD.setFont(SmallFont); // Установка набора символов myGLCD.print("Bat:", LEFT, 15); myGLCD.printNumF(analogRead(batPin)/176.6, 2, 24, 15); //напр.батареи (коэфф. подобрать при желании) break; */ } if (Refresh) {myGLCD.update(); Refresh = false;} // Вывод вместимого буфера на дисплей и сброс признака обновления } // if (Lcd_On) #if defined (debug) #else } // скобка для условия сна в начале цикла в отладке не спим #endif attachInterrupt(0, wakeUp, LOW); // Проснемся от кнопки // Отключить питание на 4с #if defined (debug) LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF); #else LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); #endif if (!key_pressed) {tik++;} else {key_pressed = false; Son_count = 0;} // в зачёт 4с. если не тыкали кнопку #if defined (debug) if (tik >=1) {Count_Minute++; Son_count++; tik = 0;} // минута натикала #else if (tik >=15) {Count_Minute++; Son_count++; tik = 0;} // минута натикала #endif } // Конец цикла void wakeUp() // прерывание сна по нажатию кнопки { // Just a handler for the pin interrupt. if (!in_int){ in_int = true; detachInterrupt(0); if (light_on > 0) {scr_num++;} // Первый тычок только подсвет light_on = 2; // после нажатия на кнопку подсветка 2*4 = 8с. Refresh = true; // Обновить отрисовку key_pressed = true; // кнопку жали if (scr_num > 3) { // цифра = количество окон (case 1, case 2, case 3) scr_num = 0; } LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF); //задержка антидребезга attachInterrupt(0, wakeUp, LOW); // восстановить прерывания от кнопки in_int = false; } }
А вот строчки из проекта "Предсказатель погоды" Александра Гайвера, которые погружают устройство в вечный сон по достижении минимально допустимого напряжения на аккумуляторе:
Пытался вставлять эту конструкцию, объявив соотв. переменные, в разные места исходника. Но в итоге неизменно получаю пустой экран с постоянно включенной подсветкой... Караул!
ты реально думаешь, что если ардуину погрузить в сон, то и вся её обвязка тут же отключится?
не вижу, где бы в коде меряли напряжение на батарее...
Ну и строчка 80 - доставляет :)
у меня такое впечатление, что вы говорите о каком-то другом коде. Ничего похоже в коде и первого сообщения не вижу.
Выложите именно тот код, который вы правили и про который спрашивайте
Вот правленый код:
Да... Насчёт отключения ЖКИ перед засыпанием я как-то не подумал... (( Похоже, всё усложняется...
Насчёт 80-й строчки ничего сказать не могу, к сожалению. Не тот уровень.
Вбей себе в мосх: Ардуина НЕ ПРЕДНАЗНАЧЕНА для глубокого сна. Для минимума потребления нужен голый контроллер со схемой управления питанием своей периферии.
Там 8 МГц ПроМини со снятой обвязкой. На плате только кварц и 328.
А без навесного ключа ЖКИ 5110 никак нельзя усыпить?
Во втором сообщении внизу речь шла за ардуину.
А без навесного ключа ЖКИ 5110 никак нельзя усыпить?
Вот он у тебя эти 200мкА и жрёт, как у него в паспорте написано.
в DeepSleep контроллер жрёт
без буквы P на конце(Pico Power), с Р еще меньше
Насчёт 80-й строчки ничего сказать не могу, к сожалению. Не тот уровень.
ага, в новом коде ошибка на месте - строка 81
о каком тогда "режиме сна" мы ведем речь...
А без навесного ключа ЖКИ 5110 никак нельзя усыпить?
Если у тебя Nokia5110, попробуй его ногу CS к плюсу подтянуть через резистор килоом на сто, а у контроллера все выходы в Hi-Z перед сном настроить, посмотри скока тогда жрать будет. Тока по просыпанию не забудь CS на землю переключить.
upd. Или CE он у него называется, не упомню счас
А без навесного ключа ЖКИ 5110 никак нельзя усыпить?
Не понимаю, в чём проблема поставить какойнить мелкий р-mosfetик с двумя резисторами на включение периферии, места много занимает?
Спасибо за рекомендации, но выполнить их мне нереально. Я простой повторяльщик, дедушка старенький. Паяльником паяю, а вот в программировании - только в самых общих чертах ... и в очевидных местах (координаты символов на экране настроить, например). Я думал, что этот момент со старта будет ясен... ))
"Все выходы в ХиЗ перез сном настроить"... Заметьте, не абы когда, а именно - перед сном! :DDD Ну т.е. да, я прошу именно то, что по крайней мере в ЭТОМ топике не поощряется раздавать, а именно - готовое решение. )) В общем, для себя вижу один-един суровый выход на данный момент: механический тумблер (какой-нибудь слайдер под ногтик), физически отрубающий аккум, когда устройство находится на хранении. А в рабочем режиме у него на экране напряжение аккума выведено, чёрным по серому. Вот пусть пользователь и следит... А иначе, как я понимаю, для меня сложновато получается.
Не в мосфетике проблема. Проблема в управлении этим мосфетиком "волшебными заклинаниями". Не владею. За тем и пришёл.
Осмелюсь посоветовать "старенькому дедушке" книжку на ночь
1000 и одна микроконтроллерная схема, Выпуск 4, Рюмик С.М., 2017
очень способствует. :)
На рутрекере есть.
Таку ешшо ничитал,
. Спасибо!
ага, в новом коде ошибка на месте - строка 81
Нормально там. Отладка не включена.
О, спасибо за книжку! Качну... Я Рюмика ещё в "Радиоаматоре" почитывал, цикл статей... Типа "Микроконтроллер? Это ж просто!" Или что-то вроде того... Земляк, однако! Но там он, насколько я помню, всё больше на ассемблере... Да и Ардуины в те героические времена ещё, наверное, и в проектах не было (90-е)... Если эта книжка именно 17 года именно первое издание - спасибо ещё раз!
Во красавец:
https://mega.nz/file/cAdRmCCZ#-L2nluDXcmu57g6Vxam_iy4v4t8ddtcWmzGtveooskY
Модуль зарядки TP4056 надо было применять с контроллером РАЗРЯДА. Такой модуль есть на Али, очень похожий и по виду, и по цене. Чуть длиннее (в смысле - глубже). И встал бы отлично, места валом... Но - что имеем, то имеем.
зы: Зацените "колхозный БГА". Вполне себе технологично получается ТАК модуля "лепить" со стороны фольги, причём на односторонний текстолит.