Ваттметр. Код работал а потом перестал.
- Войдите на сайт для отправки комментариев
Всем привет. Программист из меня никудышний, учусь так сказать на практике. Решил сделать ваттметр, но так, чтобы я понимал, как он считает и мог корректировать.
Измеряемое напряжение подается на А0 Ардуино (nano) через резистивный делитель (делит примерно на 10), падение напряжение на токовом шунте (0,001 Ом) подается на операционный усилитель, где усиливается примерно в 100 раз и идет на А1.
Мгновенные значения апряжения и тока и мощности рассчитываются просто. Энергия считается, как сумма средних значений энергии за каждый цикл void loop(). Энергия за цикл считаеся, как произведение мощности в Ваттах на время цикла void loop() в часах.
Пока я все выводил на экран 1602А - код работал корректно (сверял по внешним приборам), но когда я решил переделать код под экран OLED I2C 0.96" - энергия стала рассчитываться неправильно.. Я так понял это из-за i2c и ее прерываний произошло?
Прилагаю оба кода, под 1602 экран и под OLED 0.96 128х64.
/* Подключаем библиотеку для работы с LCD */ #include <LiquidCrystal.h> /* Создаём объект LCD-дисплея, используя конструктор класса LiquidCrystal * с 6ю аргументами. Библиотека по количеству аргументов сама определит, * что нужно использовать 4-битный интерфейс. * Подключаем экран: VSS к GND; VDD к +5V; VO(яркость подсветки) к делителю; RW к GND*/ LiquidCrystal lcd(12, 11, 5, 4, 3, 2); /*Указываем, к каким пинам Arduino подключены выводы дисплея: RS, E, DB4, DB5, DB6, DB7 */ /*Выход ОУ через резистор 1кОм подключаем к A0 Arduino, + измеряемого напряжения, через делитель 10/1 на вход А1*/ float susilitelya ; /*переменная с плавающей запятой (float) для хранения напряжения с операционного усилителя - ОУ)*/ float tok ; /*переменная для посчитанного тока */ float smeshenie = 0.1819 ; /*переменная равная напряжению на выходе ОУ при нулевом токе через шунт. Измеряется мультиметром на собранной схеме*/ float koefftoka = 10 ; /*коэффициент, связывающий значение на выходе ОУ и ток на шунте. Зависит от реального номинала шунта и реального коэффициента усиления ОУ. Если шунт 0,001 Ом, то при токе 1А напряжение м/у его концами будет 1А*0,001Ом=0,001В это напряжение пойдет на вход ОУ, который усилит его во столько раз, какой коэфф. усиления (КУ) мы зададим ему подбором резисторов на делителе в обратной связи. У нас 100кОм и 1кОм, то есть КУ = 100, значи на выходе ОУ будет напряжение усиленное в 100 раз = 0,001В*100 = 0,1В. Получается, что току 1А соответствует напряжение на на выходе ОУ 0,1В, значит коэффициент = 10. В реальности резисторы не точные, поэтому можно либо использовать потенциометр лоя подстройки, либо немного изменять значение переменной koefftoka для получения точных результатов измерения тока*/ float koeffnapr = 10.99; /*коэффициент делителя для измерения напряжения на входе А1 (измеряемое напряжение на нагрузке). При неточных показаниях напряжения, этим коэффициентом можно подогнать значения вместо подстроечного резистора*/ float napryazhenie ; /*напряжение на нагрузке*/ float moshnost ; /*переменная для мощности*/ float energiya ; /*переменная для суммарной энергии прошедшей через ваттметр с начала запуска*/ float energiyapred; /* переменная для энергии, прошедшей через шунт за время предыдущего цикла*/ float energiyacikla ; /*переменная для энергии, прошедшей через шунт за время текущего цикла*/ int vremyacikla; /* переменная для хранения времени цикла (измеряется автоматически при работе программы)*/ int vremyanachala; /*переменная для хранения времени начала цикла*/ int vremyakonca; /*переменная для хранения времени конца цикла*/ float opornoe = 5; /*переменная для хранения точно измеренного опорного напряжения. Это напряжение питания микроконтроллера, его нужно измерить мультиметром на готовом устройстве*/ void setup() { analogReference(DEFAULT); /*устанавливаем напряжение питания в качестве опорного*/ /* Инициализируем дисплей: 2 строки по 16 символов */ lcd.begin(16, 2); } void loop() { vremyacikla = vremyakonca - vremyanachala; /*рассчитываем время цикла, от цикла к циклу это время не изменяется, оно меняется только, если вносится изменение во время выполнения программы, в таком случае оно будет автоматически рассчитано и точность измерения не будет нарушена. При выполнении самого первого цикла это время будет посчитано, как 0, т.к. переменные еще не вычислены и равны 0, но этим можно принебречь, т.к. речь идет о примерно 10 первых милисекундах после включения.*/ vremyanachala = micros(); /*записываем в переменную время начала цикла - функция micros() возвращает время с момента старта программы в микросекундах*/ susilitelya = analogRead(0) * opornoe / 1023; /*измеряем напряжение с выхода ОУ, переводим значение в вольты*/ tok = (susilitelya - smeshenie) * koefftoka; /*считаем ток. Из измеренного на выходе ОУ напряжения нужно предварительно вычесть напряжение, соответствующее нулевому току*/ napryazhenie = analogRead(1) * opornoe / 1023 * koeffnapr; /*измеряем напряжение на А1 и переводим в вольты*/ moshnost = tok * napryazhenie ;/*считаем мгновенную мощность*/ energiyacikla = moshnost * vremyacikla / 3600000000; /*упрощенно считаем энергию протекщую за время выполнения цикла программы, приняв что мощность за время цикла не менялась. Переводим из Вт*мкс в Вт*ч */ energiya = energiyapred + energiyacikla; /*Считаем значение энергии, протекшей через шунт с момента запуска программы, прибавляя энергию за цикл к энергии за все предыдущие циклы*/ energiyapred = energiya;/*обновляем значение энергии за предыдущие циклы текущим значением. Это значение будет использовано в следующем цикле*/ lcd.setCursor(0, 0); /* Устанавливаем курсор в нужный столбец и строку. Нумерация идёт с нуля,первым аргументом идёт номер столбца */ lcd.print(" A V");/*выводим на экран единицы измерения*/ lcd.setCursor(0, 0); lcd.print(tok, 1);/*выводим на экран значение тока с точностью до 1 знака после запятой*/ lcd.setCursor(9, 0); lcd.print(napryazhenie, 1);/*выводим на экран значение напряжения с точностью до 1 знака после запятой*/ lcd.setCursor(0, 1); lcd.print(" W Wh"); lcd.setCursor(0, 1); lcd.print(moshnost, 2);/*выводим на экран значение мгновенной мощности с точностью до 2 знаков после запятой*/ lcd.setCursor(9, 1); lcd.print(energiya,1);/*выводим на экран значение потребленной энергии с точностью до 1 знака после запятой*/ vremyakonca = micros();/*записываем в переменную время окончания цикла*/ /* за такие подробные комментарии нужно молоко бесплатно давать ;) */ }
код для OLED
Да, такие комменты писал, т.к. планировал поделиться результатом с широкой общественностью. Объяснял так, как хотел бы чтобы объясняли мне. Так что это я на себя ориентировался. если что ;)
Вообще-то комментарии крайне бестолковые. Какой смысл дублировать в них то, что написано в коде? Это написано в самом коде, а комментарии должны содержать информацию зачем это делается.
Впрочем, если называть силу тока "napryazhenie", а напряжение - "tok", то, вероятно, и такие комментарии уместны.
ока я все выводил на экран 1602А - код работал корректно (сверял по внешним приборам), но когда я решил переделать код под экран OLED I2C 0.96" - энергия стала рассчитываться неправильно.. Я так понял это из-за i2c и ее прерываний произошло?
Прерывания по i2c могут только у слейва случиться, тут это не при чем.
Да, такие комменты писал, .... на себя ориентировался. если что ;)
Именно так и надо делать. Писать для себя. Другое дело, писать надо в терминах сутевой задачи "что делается по сути", а не в терминах программы - про программу и по коду всё понятно.
Т.е. напряжение и ток - правильно, а их произведение - неправильно. Так?
andriano , шибко грамотный - а не пойти ли тебе в пешее эротическое путешествие? Бестолковое твое замечание, потому, что только бестолковый может думать, что если информация содержится в его голове, то она должна содержаться и у других. Если мои комменты будет читать человек который только светодиодом на ардуине мигал, то походу чтения комментариев будет узнавать, также, что делает КАЖДАЯ строка. И напряжение током я не называл, умник ты чертов, посмотри комментарии, когда заданы переменные, вконце я поменял местами переменные, так было надо, а комменты не успел поменять. В чем вообще был смысл твоего высера, самоутвердиться, так вот я тебя разочарую - когда есть вся информация 2+2 сложить много ума не надо, а ты научился и тыкаешь свысока тем, кто только в начале пути. Так могут вести себя только крайне низкоинтеллектуальные люди. И да, я готов к бану.
5N62V, ЕвгенийП, спасибо за адекват!
Мощность, напряжение и ток рассчитываются верно, неверно рассчитывается энергия (сумма энергий всех циклов с момента запуска программы).
ЕвгенийП, Произведение тока и напряжения - это мощность, а энергия это произведение мощности на промежуток времени, который эта мощность действовала. Я измеряю однократно мощность и умножаю ее на время цикла "loop", которое отщитывается отдельно. Приближенно считая. что мощность за время цикла была постоянной, в следующем цикле будет другая мощность (если ток и напряжение изменились) и она опять умножится на время цикла, полученное значение энергии сложится со значением из предыдущего вычисления и т. д. Текущее накопленное значение выводится на экран и постоянно растет (как в электрическом счетчике).
Она получается меньше где-то в 2 раза. Я так понимаю, что время цикла рассчитывается меньше, чем оно есть в реальности. То есть какие-то вычисления происходят, но их время почему-то не учитывается. Явно по коду я этого понять не могу, видимо есть ньюанс о котором я не знаю.
Произведение тока и напряжения - это мощность...
Это справедливо только для постоянного тока. Такой ток вы измеряете?
Это справедливо только для постоянного тока. Такой ток вы измеряете?
Постоянный
questioner - что касается расчеты времени, вы уверены, что время исполнения цикла помещается в переменную типа int?
Ну и насчет комментов вы зря на Андриано наехали. Его замечание может и резкое, но по сути верное - если писать комменты для идиотов, только идиотам они и будут полезны. Все-таки мы предполагаем. что если человек дорос до скетчей уровня ваттметра, то комментировать назначение основных операторв языка Си ему уже не нужно - можно сосредоточится на сути программы. а не на пространных пояснениях в стиле "Кэп Очевидность"
Воот.. это уже по делу. Конечно не уверен - проверю.
И вы туда же! Каких идиотов? Если человек не знает, что делает та или иная команда, он идиот? Да он может посмотреть, но нафига людям делать одно и то же по over9000 раз, года можно "один сделал-поделился со всеми" эта схема продуктивней. Ну почему, стоит только человеку подняться на ступеньку повыше - он сразу начинает считать тех, кто пониже недочеловеками, вместо того, чтобы протянуть руку?! Я писал комменты НЕ для тех, кто знает. Вашу позицию по этому вопросу резко осуждаю!
Вашу позицию по этому вопросу резко осуждаю!
Взаимно :)
Не страшно, когда человек чего-то не знает. Но если он категорически не хочет учиться - и, главное, еще и настаивает, что ему ничего знать не нужно, так, мол, "продуктивней"! :) - я такую позицию всецело осуждаю. Таким не стоит и помогать.
Да и как им поможешь, если они учиться не хотят? Сделать за них?
Стеснюсь просить, Вы эффективный менеджер? Или продуктивный?
Это не ваша позиция - вы просто повторяете распространенный шаблон. Понимаю, так проще, не думая пользоватьться чужими готовыми конструкциями, а вы попробуйте проанализировать эту конструкцию... понимаю не охота вникать глубоко... философия, демогогия.. вот это всё... не для серьезных парней занятие ;)
Это не я
_________
По сути топика, если кому-то еще это надо: переменная float судя по описанию вмещает 32 бита данных, в десятичных числах это около 4,2 миллиардов. В секундах это 4200, не думаю, что мой цикл столько длится.. Эх, говорили мне - учись сначала на ассемблере ))
Это не ваша позиция - вы просто повторяете распространенный шаблон. Понимаю, так проще, не думая пользоватьться чужими готовыми конструкциями
строго по вашим заветам :) Зачем же мне овер9000раз выдумывать новое - я просто повторяю то, что кто-то уже написал за меня, так же "продуктивней", разве нет? :)
Что касается скетчей - я тоже не брезгую брать готовые в инете. Но я всегда досконально разбираюсь. что и как делает каждая строчка, если это сразу непонятно. И не только потому. что люблю учиться - но и хотя бы потому, что в этих скетчах встречается невроятное количество туфты, которую бездумно копируют друг у друга подобные вам "продуктивные" программисты.
По сути топика, если кому-то еще это надо: переменная float судя по описанию вмещает 32 бита данных, в десятичных числах это около 4,2 миллиардов. В секундах это 4200, не думаю, что мой цикл столько длится..
длительность цикла у вас выражена переменной типа int (а никак не float) которая"вмещает" всего 32768 микросекунд - то есть чуть менее чем 33 тысячных секунды.
а вот если использовать датчик тока INA219 всё будет точно не зависимо от дисплея
а вот если использовать датчик тока INA219 всё будет точно не зависимо от дисплея
если путать float и int, то и с сертифицированным по ISO ваттметром за ХХХ тыр ничего работать не будет :)
Так продуктивней только в том случае, если за вас написали правильно, не так ли? Строго по вашим заветам:
И что же я бездумно у кого скопировал, этот скетч писал-таки сам, о чем и сказал сразу? В чем конкретно они подобны мне?
Насчет на счет моей "продуктивности" в качестве программиста я написал в первой строке ервого поста: "Программист из меня никудышний, учусь так сказать на практике". Вы даже не представляете, насколько низок мой уровень в качестве программиста, до этого я пару раз мигал светодиодом, делал ШИМ по примеру и выводил текст на экран с помощью библиотеки.
а вот если использовать датчик тока INA219 всё будет точно не зависимо от дисплея
если путать float и int, то и с сертифицированным по ISO ваттметром за ХХХ тыр ничего работать не будет :)
Всё должно быть сделано просто, насколько возможно, но не проще А Эйнштейн
Все сложное – не нужно, все нужное – просто. М. Калашников
а с переменными да )))
По сути вопроса: Я действительно перепутал int и float, но не при написании программы, а при обсуждении с вами. Там действительно int, я намеренно это сделал. Но я не знал, что так мало бит вмещает этот тип данных. Спасибо, что заметили и сказали! Я попробовал изменить типы всех переменных времени на float - результат тот же - посчитанная энергия примерно в 2 раза меньше той, что должна быть. Это только из-за времени, ток, напряжение и мощность тут не при чем - эти величины вообще постоянны при проверке (я тупо подаю на входы 5В). Также попробовал использовать millis() вместо micros() - цифры те же.
Я действительно перепутал int и float...
Читаю и нихрена не могу понять - какие нахрен float? Для микросов и миллисов - unsigned long!
Я попробовал изменить типы всех переменных времени на float - результат тот же
Открою Вам страшный секрет. float вообще не предназначен для хранения точных значений. Он неточен по своей природе :(
Открою Вам страшный секрет. float вообще не предназначен для хранения точных значений. Он неточен по своей природе :(
Спасибо, прочел. Но дело тут не в типе переменных, c unsigned long то же самое. Ладненько, буду искать, пока не найду.
И в переменных, и много в чём другом тоже.
Ваша беда в том. что Вы качаете права, вместо того, чтобы слушать и учиться.
Вот в ответ на мой пост
Т.е. напряжение и ток - правильно, а их произведение - неправильно. Так?
Вы решили обратить внимание на неаккуратное использование терминов, по сути на описку. А самого важного в это посте Вы не заметили.
Я повторю вопрос, упростив его для десткого сада. Этот вопрос очень важен - в нём ключ к решению проблемы.
итак, у Вас три параметра - ток, напряжение и время. Какой из них начинает врать? Не могут же все быть одинаковыми, а результат иной. Если Вы их все три напечатаете и поймёте кто из них ломается, Вы втрое сузите пространство поиска проблемы.
Доходит?
Я же отвечал, что неверно считается только энергия, а т.к. произведение тока и напряжения считается верно, следовательно неверно считается время. Странно, что вы не заметили.
Вот я вам отвечал:
"Мощность, напряжение и ток рассчитываются верно, неверно рассчитывается энергия "
Как я и писал с самого начала - неверно считается время. Вот как я это проверил:
Чтобы не писать дополнительных строк в коде, я заменил расчет енергии
вместо
сделал
То есть на экране вместо энергии в Wh будет посчитанное программой ,а не взятое из micros() время с начала запуска, сложенное из значений времени каждого цикла. И да, временные переменные пришлось объявить как float.
В результате на эрене вышел счетчик секунд. Как и предполагалось - эти посчитанные секунды бегут медленнее, чем реальные почти в 2 раза - за 2 посчитанные минуты проходит 3,5 реальные. Именно с такой погрешностью и считалась энергия.
Вопрос изначально был именно в этом, по коду явно я не пойму, вроде все правильно..
Еще компилятор ругается:
In function 'void loop()':
warning: deprecated conversion from string constant to 'char*'
Из идей только одна. Микрос и миллис начинают врать когда либо любое прерывание, либо запрет прерываний длится более 4 мкс. Других причин вроде нет. Искать надо где это происходит. Для начала библиотеку i2c поменять.
nik182, спасибо! Действительно врет micros() . Вывел на экран отношение посчитанного времени к micros() в начале это отношение больше единицы, но секунд за 20 становится единицей.
Микрос и миллис начинают врать когда либо любое прерывание, либо запрет прерываний длится более 4 мкс.
Вот все и заработало корректно! Поменял библиотеку дисплея, как посоветовали, спасибо всем, кто помогал.
Чтобы цифры не менялись слишком быстро сделал вывод на экран через 5 циклов. Первый раз в жизни заюзал for()
Если кому нужно: код, хекс и файл для протеуса https://yadi.sk/d/Whd7ZFSY3Upor7
Потенциометры для регулировки напряжения источника и сопротивления нагрузки во время симуляции. Погрешность есть конечно, но небольшая, в пределах 1-2%.
Погрешность есть конечно, но небольшая, в пределах 1-2%.
Вы просто время в программе считаете неправильно, вот и все.Вы считаете время промежутка между 47 и 73 строчкой. Однако есть еще промежуток между 73 и 47 строчкой, которые вы не учитываете, отсюда и ошибка.
Вместо двух переменных время_начала и время_конца в двух разных местах цикла нужно оставить одну единственную в каком-то одном месте. Тогда время будет считаться точно.
Если кому нужно: код, хекс и файл для протеуса
Кому это может быть нужно - недоделанный глючный код с ошибками? Вы же очевидно нифига не смыслите в программировании. Так зачем вы свой г...-код выкладываете для других? Чтобы они копались в ваших ошибках? Первый раз в жизни for() использовал....
Время-таки как раз считается верно - проверял по секундомеру. Погрешность в измерении тока и напряжения из-за особенностей ОУ. Эту погрешность видно при симуляции в протеусе.
В программировании, вы правы, я смыслю крайне мало, ну прямо очень, но код тем не менее работает теперь корректно. И пригодиться он может начинающему, как я, коих тьмы.
Время-таки как раз считается верно - проверял по секундомеру.
Точность micros() по секундомеру контролируете? Это сильно!
Так секундомер-то тоже на micros() :) добавляю его в коде и вывожу на свободую строку экрана. Показания сходятся.
Так секундомер-то тоже на micros() :) добавляю его в коде и вывожу на свободую строку экрана. Показания сходятся.
ЭТО ПЯТЬ! :))))
Так секундомер-то тоже на micros() :) добавляю его в коде и вывожу на свободую строку экрана. Показания сходятся.
ЭТО ПЯТЬ! :))))
Метрология - наше всё )))