ок. я понял - это вопрос традиций, смысл которых канул в века.
Ты, в своей категоричности, тоже традиционен, и порой - без смысла ;) Отказ чего-либо принимать/понимать - не значит бессмысленность этого ;) Про первичный смысл разделения на заголовочные файлы и файлы с реализацией - я уже писал, если ты не заметил. Первичный смысл - это удобный и быстрый доступ к API, которое предоставляет тот или иной набор исходников: заметь, на несколько *.cpp-файлов может быть всего один *.h-файл ;)
Для меня (да и не для меня одного) - гораздо удобнее взглянуть на объявления, чтобы понять, как работать с этим добром. А уже при необходимости - в исходнички залезать. С#, кстати, отсутствием объявлений и вымораживает - в большой простыне хер соберёшь в кучку описание функционала, предоставленного исходником.
Ты, в своей категоричности, тоже традиционен, и порой - без смысла ;) Отказ чего-либо принимать/понимать - не значит бессмысленность этого ;)
ты не понимаешь - моя категоричность заключается исключительно в одном утверждении: не нужно кого-то чему-то учить методом копипаста примеров - без объяснения смысла действий.
*пример из жизни: чел - отличник по физике, победитель республиканских олимпиад по физике купил аудиокабеля для своей аппаратуры по 100$ за погонный метр. когда я ему сказал, что он-дыбил и у него по физике в школе была твёрдая тупая тройка - оказалось, что я жестоко неправ. когда ему на на его глазах за полчаса спаяли кабель себестоимостью 10$, то на слепом аудио-тесте - он оказался жестоко не прав. и, мы оба задумались.
ты не понимаешь - моя категоричность заключается исключительно в одном утверждении: не нужно кого-то чему-то учить методом копипаста примеров - без объяснения смысла действий.
... но от случайных помех или флюктуаций самого датчика, считанные и отданные значения могут колебаться. Бывает, датчик читает-читает значение 200, а потом ба-бах, в момент чтения приходит по шине случайная иголка, значение подскакивает до 800, сирена включается и наступает время брать под мышку сонного кота и лихорадочно думать "Куды бечь?". Чтоб этого не случалось, я всегда читаю пин несколько раз и усредняю полученные значения. Читаю ровно степеньдвойки раз, конкретно 8 или 16, чтоб среднее значение получалось простым сдвигом вправо на 3 или 4 бита.
Увы, предложенный метод не решает поставленной проблемы. От слова совсем.
Ну прочитали мы пин 8 раз, ну получили 7 раз 200 и один раз 800, ну вычислили среднее, в данном случае 275, разве это то, что нам нужно?
В условиях возможности возникновения явно "левых" значений, нужно не усреднять, а брать медиану: прочитали порт 3, 5 или 7 раз, отсортировали и вывели результат из серединки.
В условиях возможности возникновения явно "левых" значений, нужно не усреднять, а брать медиану:
Очень не всегда так. Зависит от распределения вероятностей. Общеприменимое (когда думать не хочется и исходных данных нет;) скорей статобработка - считаем СКО, три сигмы, отбрасываем что невписалось и снова считаем. Это для нормального распределения. Для каждого конкретного случая свой подход. Иногда и по первому большому значению нужно хватать кота и бежать, т.к. до конца цикла обработки уже некому будет решение принимать. (Пример - контролируем напряжение в цепи постоянного тока на предмет аварии при превышении порога допустим 24В, решили брать некое среднее, но в цепь попало 220В 50Гц, среднее 0, защита не сработала. А работали бы по мгновенному напряжении, то сработала бы.). Медианное среднее - один из вариантов, а их вобще много. Добавлю для расширения кругозора еще и геометрическое среднее и фильтр Кальмана.
ПС. Когда данные поступают поочередно и с интервалами, лучше сортировать их по мере поступления, поддерживая массив отсортированым все время.
Написано ж для подрастающего детсада, зачем седовласые старцы пытаюца разводить разводить срач ради срача? Выяснять у кого магия сильнее? Дак все вы - уважаемые луди, каждый силён по-своему. Давайте лучше вашу магию, против двух бутылок моего срецтва, да поживем здесь мирно некоторое время?
Я решил, опишу последовательно создание законченного класса да и забуду здесь писать, как страшный сон. Лучше телепузиков посмотрю под маринованных ежей с воткой.
Написано ж для подрастающего детсада, зачем седовласые старцы пытаюца разводить разводить срач ради срача? Выяснять у кого магия сильнее? Дак все вы - уважаемые луди, каждый силён по-своему. Давайте лучше вашу магию, против двух бутылок моего срецтва, да поживем здесь мирно некоторое время?
Я решил, опишу последовательно создание законченного класса да и забуду здесь писать, как страшный сон. Лучше телепузиков посмотрю под маринованных ежей с воткой.
так, нафига ты написал про инклюды кпп и ха, если должен был писать про классы?, классы, Карл!
классы для дуино и детей - это вот это:
class test {};
void setup() {}
void loop() {}
когда дети подрастут - научишь их, как этим более лучше пользоваться в разных позах.
Клапа, не злись, мы тя сиравно любим. Описание класса мне куда-то пхать надо? В проект? Дак нет, я щитаю надо учится сызмальства парадигме, что каждому классу - свое укрытие. Иначе, несчастный .ino файл догонит и перегонит Войну и Мир по размерам.
В след. серии будем размахивать Ржавой Секирой Ужоса, то есть millis()-а над головами незадачливых пользователей.
это будет отдельная тема про инклюды, где ты расскажешь, что любой кусок кода возможно хранить в отдельном файле.
DetSimen пишет:
Иначе, несчастный .ino файл догонит и перегонит Войну и Мир по размерам.
приехали. а, что, по твоему, инклюд файла класса делает, если не дописывает текст кода в ino перед компилляцией? ты кого собрался обмануть малым объёмом кода ino - детей или компиллятор?
дети будут понимать, почему 10 строк в ino не помещаются в контроллер.
Пока на улице мороз, работать уже лень, а квасить еще рано, продолжим.
В этом безумном мире всегда найдутся очень простые люди, которых 3 раза из ПТУ выгоняли за неуспеваемость и которые прям в loop() напишут:
void loop()
{
int i = MyPhoto.Read();
}
И будут читать значение с этого несчастного датчика несколько тысяч раз в секунду. В какой-то мере и данный подход оправдан, чо париться-то лишний раз в этом сложном мире, да вот (бида!, бида!), не все датчики допускают такое вольное с собой обращение. Хорошо, если он просто будет отдавать данные с потолка, а если возьмет да и подохнет? И что мы потом скажем над гробом безвременно почившего даччика? Что покойный жил в лупе, вел беспорядочную жизнь и часто болел? Потому и молодым и помер, не успев толком обжиться в доме? Да что не скажи, а перспектива безрадостная. Не хочется закапывать наши деньги в помойке, надо придумать механизм, чтоб не опрашивать датчик слишком часто. Люди которых не выгоняли из ПТУ скажут, а давайте читать датчик только если прошло достаточное количество времени, ну и они-таки будут правы. А чем нам померить время? А нам на помощь придёт функция millis(). Эта функция, как знатный бухгалтер, скурпулёзно подсчитывает, сколько миллисекунд проработал процессор с момента включения (наерна, чтоб знать, когда настанет пора начислять ему пенсию). Мы можем вызвать millis() один раз, потом другой и по разности полученных значений примерно-точно сказать, сколько миллисекунд программа проработала между вызовами. Значение, которое отдает millis() имеет тип unsigned long int, занимает целых 4 байта в памяти, зато может хранить значения от 0 до чуть больше 4 миллиардов. 4 лярда миллисекунд, в переводе на русский, это почти 50 суток, у кого папа калькулятор не отбрал могут сами проверить. Кто собирается читать датчик реже чем раз в 50 суток - дальше не читайте, там для вас ничего не написано.
Можно, канеш, и самим в loop() проверять интервал чтения датчика, как то так
unsigned long previous = 0;
unsigned long interval = 2000; // 2 секунды
void loop()
{
unsigned long now = millis();
int value;
if (now - previous > interval)
{
value = MyPhoto.Read();
previous = now;
}
}
работать будет и так, мы добились того, чтоб наш датчик не истёрся до дыр от частого использования, но как то это некрасиво. Для чего тогда класс городить? Мы и в if-е тогда analogRead() можем вызвать. Класс должен же ведь упрощать жизнь, а не делать ее сложнее. Мошт, пусть он сам решает, часто его читают или нет? Ну а почему бы и нет? Надо сделать так, чтобы класс помнил, когда он опрашивал датчик последний раз, и если время между вызовами Read() меньше заданного интервала, то можно просто отдать последнее, прочитанное ранее значение, а если времени прошло больше - то снова опросить датчик и запомнить. Для этого нам и понадобится-то совсем немного.
Поле для запоминания последнего значения, считанного с pin-а, оно должно быть скрыто, нехрен пользователю в него лезть, мы сами его отдавать будем, когда надо. Назовем его int fvalue.
Поле для запоминания времени последнего реального чтения датчика. Оно тоже должно быть скрыто, ведь никому, кроме самого класса это неинтересно. Назовем его unsigned long flasttime.
Не бойтесь, кстати, давать переменным длинные осмысленные имена (хотя, если с английским туго, о какой осмысленности речь?) в эпоху intellisence набирать их ну чуть помедленнее, канешна, чем i, но код читается потом вами же не в пример легче. А если придется через год переделывать прошивку? Немногие вспомнят, что означает переменная v1 или s2 в своей же собственной программе, посмотрят-посмотрят на текст, как новые ворота на стаю баранов, почешут репу "Какой идиот это писал?", да пойдут с друзьями пиво пить. А с осмысленными именами даже через пять лет жизнь будет существенно проще. (там будет другая проблема, вспомнить лагаритм и поржать над собой, неучем, пятилетней давности).
Кстати. В конструкторе можно присвоить начальные нулевые значения этим полям. Можно этого и не делать, нулями они и сами заполнятся при создании класса, это гарантировано стандартом, но мой вам совет, не полагайтесь на стандарт, присваивайте начальные значения полям сами. Стандарты С++ придумывают, как мне кажется, не очень адекватные люди, и я даже не удивлюсь, если узнаю, что все они содержатся в одной клинике на попечении государства(слава богу, не нашего). А завтра из этой психушки выйдет другой стандарт, предписывающий заполнять все поля единицами при создании класса, или пхать туда случайные числа, и что тогда? Вы должны чётко понимать в каком состоянии находится ваш класс после создания, поэтому вся инициализация - только сами, и не лениться. Не нравица - вот лопата, вот обьявление "Требуется кочегар", вам туда.
// --------------------------
// .h файл
#pragma once
#include "Arduino.h"
class TSensor
{
protected:
byte fpin; // поле "номер пина" типа байт, скрытый от пользователя
int fvalue; // последнее считанное с pin-а значение
unsigned long flasttime; // время последнего обращения к датчику
public:
TSensor(byte apin); // конструктор, принимающий только номер пина
TSensor(byte apin, bool apullup); // а этот еще и включает или нет подтягивающий резистор на pin
int Read(); // читать данные с сенсора
}
// --------------------------
// капец .h файла
//-------------------------------------
// срр файл
#include "TSensor.h" // перво-наперво подключаем свой же файл определений
// чтоб знать, реализацию чего мы пишем
// срр файл мы не включаем в проект через #include,
// поэтому #pragma once писать не надо
// Реализация нашего класса.
// Конструктор. В конструкторе мы просто вызываем другой конструктор, более общий
TSensor::TSensor(byte apin): TSensor(apin, false) {}
// Конструктор. В конструкторе мы запоминаем номер пина, который мы ему передали в качестве
// параметра во внутреннем поле нашего класса и настраиваем его на вход с подтяжкой или без
TSensor::TSensor(byte apin, bool apullup)
{
fpin = apin;
if (apullup) pinMode(fpin,INPUT_PULLUP); else pinMode(fpin, INPUT);
fvalue = 0; // инициализация ручками
flasttime = 0;
}
int TSensor::Read(void)
{
unsigned long now = millis(); // прочитаем время на сейчас
// Если с момента последнего обращения прошло меньше 2 секунд, просто отдадим
// наружу, то что прочитали последний раз, не дергая пин лишний раз
if (now - flasttime < 2000) return fvalue;
// Ну а уж если времени прошло больше, куда деваца, надо читать
int result = 0;
for (byte i=0; i<16; i++) result += analogRead(fpin);
flasttime = now; // запомним время, когда мы последний раз читали
fvalue = result >> 4; // и запомним, что прочитали при этом
return fvalue; // ну и то, что прочитали, то и отдадим.
}
// капец cpp файла
//--------------------------------------
Теперь можно в loop() вставлять чтение нашего датчика хоть как и хоть куда, все равно реально читаться значение с pin-а будет раз в 2 секунды, это намайна, нас не посадят за жестокое обращение с даччиками, да и сам он не устанет и не будет просица на пенсию по здоровью.
Кстати, а откуда взялись эти 2 секунды? Нам чо, судом присудили опрашивать датчик именно через 2 секунды? А если надо читать быстрее? А если по даташиту датчик нада опрашивать не чаще раза в 5 секунд, а у нас жёска настроено на 2? Что же делать-то? Да просто надо завести еще одно поле, обозвать его ReadInterval и дать возможность пользователю класса менять его значение. Так как оно должно быть видимо снаружи, то обьявить его надо в секции public, ну а в конструкторе присвоить ему некое начальное значение по умолчанию, ни большое, ни маленькое, да те же 2 секунды. Для большинства датчиков нет никакой необходимости читаться чаще раза в две секунды, а те, кто хочут, легко могут интервал изменить прямо после создания класса и лезть в исходник для этого не придётся. Платой за это будут лишних 4 байта на каждый датчик, которые придётся со слезами выкраивать из оперативки. Но, слава богу у Дуни всего 6 аналоговых пинов, много датчиков не подключишь, поэтому 24 байта за возможность индивидуальной настройки чтения каждого датчика, я думаю, это нормально. Падытожым.
// --------------------------
// .h файл
#pragma once
#include "Arduino.h"
class TSensor
{
protected:
byte fpin; // поле "номер пина" типа байт, скрытый от пользователя
int fvalue; // последнее считанное с pin-а значение
unsigned long flasttime; // время последнего обращения к датчику
public:
unsigned long ReadInterval; // частота реального чтения даччика в миллисекундах
TSensor(byte apin); // конструктор, принимающий только номер пина
TSensor(byte apin, bool apullup); // а этот еще и включает или нет подтягивающий резистор на pin
int Read(); // читать данные с сенсора
}
// --------------------------
// капец .h файла
//-------------------------------------
// срр файл
#include "TSensor.h" // перво-наперво подключаем свой же файл определений
// чтоб знать, реализацию чего мы пишем
// срр файл мы не включаем в проект через #include,
// поэтому #pragma once писать не надо
// Реализация нашего класса.
// Конструктор. В конструкторе мы просто вызываем другой конструктор, более общий
TSensor::TSensor(byte apin): TSensor(apin, false) {}
// Конструктор. В конструкторе мы запоминаем номер пина, который мы ему передали в качестве
// параметра во внутреннем поле нашего класса и настраиваем его на вход с подтяжкой или без
TSensor::TSensor(byte apin, bool apullup)
{
fpin = apin;
if (apullup) pinMode(fpin,INPUT_PULLUP); else pinMode(fpin, INPUT);
fvalue = 0;
flasttime = 0;
ReadInterval = 2000; // по умолчанию чтение с pin-а раз в две секунды
}
int TSensor::Read(void)
{
unsigned long now = millis(); // прочитаем время на сейчас
// Если с момента последнего обращения прошло меньше ReadInterval миллисекунд, просто отдадим
// наружу, то что прочитали последний раз, не дергая пин лишний раз
if (now - flasttime < ReadInterval) return fvalue;
// Ну а уж если времени прошло больше, куда деваца, надо читать
int result = 0;
for (byte i=0; i<16; i++) result += analogRead(fpin);
flasttime = now; // запомним время, когда мы последний раз читали
fvalue = result >> 4; // и запомним, что прочитали при этом
return fvalue; // ну и то, что прочитали, то и отдадим.
}
// капец cpp файла
//--------------------------------------
в основной программе
//--------------------------------------
// Файл Test.ino
#include "TSensor.h"
// ОБЪЯВЛЕНИЕ
TSensor MyPhoto(A0, true); // обьявление и вызов конструктора для пина А0 с подтяжкой
void setup()
{
// НАСТРОЙКА
MyPhoto.ReadInterval = 5000; // читать не чаще раза в 5 сек.
}
void loop()
{
// ИСПОЛЬЗОВАНИЕ
int value = MyPhoto.Read();
Serial.print("Value = "); Serial.println(value);
delay(1000);
}
// капец файла Test.ino
//--------------------------------------
правильно, красиво, спасибо, но для небольших проектов сложно и много писать :)
ЗЫ. С молодости не люблю классы....бр.....бяка :)
ЗЫЫ. Вспоминаю соседа по общаге в середине 90х, попросили его на Turbo Pascal написать какую то примитивную лабу из пары действий, так он умудрился из программы длиной максимум строк 20 написать целый проект через классы строк на 100 минимум, делающий то же самое. Естественно вопрошающий не сдал, т.к. не смог объяснить как это все работает.
правильно, красиво, спасибо, но для небольших проектов сложно и много писать :)
ЗЫ. С молодости не люблю классы....бр.....бяка :)
ЗЫЫ. Вспоминаю соседа по общаге в середине 90х, попросили его на Turbo Pascal написать какую то примитивную лабу из пары действий, так он умудрился из программы длиной максимум строк 20 написать целый проект через классы строк на 100 минимум, делающий то же самое. Естественно вопрошающий не сдал, т.к. не смог объяснить как это все работает.
Ну так вот - еще одна польза от применения классов.
DetSimen, вообще-то есть возможность задавать функциям параметры по умолчанию. Так что пример с двумя конструкторами явно притянут.
Ну и идеологически неверно открывать поля, раз уж ООП, значит - ООП: следовало бы внести период опроса в вызов конструктора необязательным параметром. Тем более, что минимальный период, как правило, определяется дэйташитом и крайне маловероятно, что кому-то может потребоваться менять его в процессе выполнения.
DetSimen, вообще-то есть возможность задавать функциям параметры по умолчанию. Так что пример с двумя конструкторами явно притянут.
Ну и идеологически неверно открывать поля, раз уж ООП, значит - ООП: следовало бы внести период опроса в вызов конструктора необязательным параметром. Тем более, что минимальный период, как правило, определяется дэйташитом и крайне маловероятно, что кому-то может потребоваться менять его в процессе выполнения.
естесственно, это будет написано дальше, в секции про устойчивость класса.
int TSensor::Read(void)
{
unsigned long now = millis(); // прочитаем время на сейчас
if (now - flasttime < 2000) return fvalue;
...
flasttime = now; // запомним время, когда мы последний раз читали
..
}
Теперь можно в loop() вставлять чтение нашего датчика хоть как и хоть куда, все равно реально читаться значение с pin-а будет раз в 2 секунды, это намайна, нас не посадят за жестокое обращение с даччиками, да и сам он не устанет и не будет просица на пенсию по здоровью.
Семен, тебя квон покусал что ли - на millis() экономишь...
В данном случае обращение к датчику будет происходить не раз в 2000 мс, а раз в 2000-<время на всякую фигню внутри if> мс. А если учесть, что внутри if() ты читаешь датчик, то он всяко будет опрашиваться чаще, чем раз в заданное время. Конечно это, скорее всего, будет быть снивелированно другими операциям вне процедуры чтения, но ржавое ружьё иногда и палит посреди ночи.
16 вызовов analogRead() на 328 Меге 16 Мгц выполняются ~2 мс. Критичненько? Я же не звездолеты строю и не осциллографы реального времени, где за каждый такт надабароца, я такого пока и не осилю. Мои поделки простые, как я. А в них и классы простые.
Да што ты заводишься сразу. Датчики-то разные бывают. Просто нужно запоминать время после окончания чтения, а не до него. Один лишний вызов millis(), а пользы от него - как от банки рассола на утро.
Это же тогда в тексте надо будет слова писать про целесообразность еще одного такого вызова. А я хорошо умею только лечь и лежать изредка переворачиваясь, чтоб не отлежать лежало. Выходной же. Дальше писать буду на работе.
int TSensor::Read(void)
{
unsigned long now = millis(); // прочитаем время на сейчас
if (now - flasttime < 2000) return fvalue;
...
flasttime = now; // запомним время, когда мы последний раз читали
..
}
Теперь можно в loop() вставлять чтение нашего датчика хоть как и хоть куда, все равно реально читаться значение с pin-а будет раз в 2 секунды, это намайна, нас не посадят за жестокое обращение с даччиками, да и сам он не устанет и не будет просица на пенсию по здоровью.
Семен, тебя квон покусал что ли - на millis() экономишь...
В данном случае обращение к датчику будет происходить не раз в 2000 мс, а раз в 2000-<время на всякую фигню внутри if> мс. А если учесть, что внутри if() ты читаешь датчик, то он всяко будет опрашиваться чаще, чем раз в заданное время. Конечно это, скорее всего, будет быть снивелированно другими операциям вне процедуры чтения, но ржавое ружьё иногда и палит посреди ночи.
Вы фигню писать изволили. Как раз приведеный код обеспечит время между чтениями не менее 2000мс. Т.е. равно и более 2 сек. Для обеспечения времени в среднем 2000 пишем flasttime += 2000;. И уж совершенно пофиг до или после чтения. Про экономию: а чё не так?! Экономить - не транжирить, к разорению не ведет. Я бы для экономии еще лонгинт убрал везде, там и инта с головой. И миллис бы один раз в начале лопа вызывал, сохранял бы и передавал бы всем заинтересованым.
Вы фигню писать изволили. Как раз приведеный код обеспечит время между чтениями не менее 2000мс. Т.е. равно и более 2 сек. Для обеспечения времени в среднем 2000 пишем flasttime += 2000;
Прогоните:
void setup() {
Serial.begin(9600);
}
void loop() {
uint8_t processSomething = 0;
uint32_t now, flasttime, sensorPrevReadingTime, sensorRestTime;
flasttime = sensorPrevReadingTime = millis();
while (true)
{
now = millis(); // прочитаем время на сейчас
if (now - flasttime < 2000) continue; // return fvalue;
processSomething++;
if (3 <= processSomething) {
delay(1000);
//Serial.print("Process something");
processSomething = 0;
}
// int result = 0;
sensorRestTime = millis() - sensorPrevReadingTime;
Serial.print("Sensor polled after: "); Serial.print(sensorRestTime); Serial.println(" ms");
sensorPrevReadingTime = millis();
flasttime = now; // запомним время, когда мы последний раз читали
//flasttime = millis();
// fvalue = result >> 4; // и запомним, что прочитали при этом
// return fvalue; // ну и то, что прочитали, то и отдадим.
}
}
Божеж как нихороше! Нихороше делеи ставить и после них выводить время опроса! Поправил.
void setup() {
Serial.begin(9600);
}
void loop() {
uint8_t processSomething = 0;
uint32_t now, sensorPrevReadingTime, sensorRestTime;
int flasttime;
flasttime=sensorPrevReadingTime = millis();
while (true)
{
int now = millis(); // прочитаем время на сейчас
if (now - flasttime < 2000) continue; // return fvalue;
Serial.print("now="); Serial.print(now);
Serial.print("delta="); Serial.println(now - flasttime);
processSomething++;
if (3 <= processSomething) {
delay(1000);
//Serial.print("Process something");
processSomething = 0;
}
// int result = 0;
sensorRestTime = millis() - sensorPrevReadingTime;
Serial.print("Sensor polled after: "); Serial.print(sensorRestTime); Serial.println(" ms");
sensorPrevReadingTime = millis();
flasttime = now; // запомним время, когда мы последний раз читали
//flasttime = millis();
// fvalue = result >> 4; // и запомним, что прочитали при этом
// return fvalue; // ну и то, что прочитали, то и отдадим.
}
}
Время чтения 16 раз analogRead() и взятия среднего - 2 мс. значить, время опроса самого датчика
2000 мс - 2 мс. = 1998 мс.
Нет конечно. Просто взятие среднего завершится на 2мс позже чем сработало условие завершения интервала. И так каждый цикл. Сработало условие ровно через 2сек, пошол опрос рсачет среднего и завершился через 2мс. И между этими завершениями сново будет 2 сек. В первый коде время скачет т.к. делей еще и под ифом, иногда в цикле он есть, иногда нет. Вы конечно дико удивитесь, но даже в таком случае среднее время опроса всеравно останется 2 сек )))
Я и он про то, что время между последним analogRead() прошлого цикла и первым analogRead() текущего цикла = 2000 - 2. Значит пин начинает читаться чаще, чем 2000 мс. Только и всего.
Если сделать так
53
intTSensor::Read(void)
54
{
55
56
unsigned longnow = millis(); // прочитаем время на сейчас
57
58
// Если с момента последнего обращения прошло меньше ReadInterval миллисекунд, просто отдадим
59
// наружу, то что прочитали последний раз, не дергая пин лишний раз
60
61
if(now - flasttime < ReadInterval) returnfvalue;
62
63
// Ну а уж если времени прошло больше, куда деваца, надо читать
64
65
intresult = 0;
66
67
for(bytei=0; i<16; i++) result += analogRead(fpin);
68
69
flasttime = millis(); // запомним время, когда мы последний раз читали ИЗМЕНЕНО ЗДЕСЬ
70
fvalue = result >> 4; // и запомним, что прочитали при этом
71
72
returnfvalue; // ну и то, что прочитали, то и отдадим.
73
}
То точно пройдет время НЕ МЕНЬШЕ 2000 мс между реальными обращением к пину.
И это. Извини, если обидел, я так больше не буду. Сонный был.
Я и он про то, что время между последним analogRead() прошлого цикла и первым analogRead() текущего цикла = 2000 - 2. Значит пин начинает читаться чаще, чем 2000 мс. Только и всего.
.....
То точно пройдет время НЕ МЕНЬШЕ 2000 мс между реальными обращением к пину.
И это. Извини, если обидел, я так больше не буду. Сонный был.
Та какие там обиды, проехали.
А я в #69 писал про два случая: НЕ МЕНЬШЕ и В СРЕДНЕМ. Это уже кому когда где что надо. Еще обращу таки внимание что в примерах выще int вместо long int прописан. Экономия по мелочам, тут 100гр, там 100гр, а в итоге скетч влазит в память.
А на вашем месте я бы про эти 2мсек чтения обработки вобще бы не вспоминал на фонее периода 2000. Дело в том что в большом скетче с прерываниями и крупными кусками кода обеспечить прокрутку лопа не реже 1мсек, чтоб сформировать точно 2000, очень сложно. Период будет плясать +-5мсек (и это еще очень хороше, может и 10 и 20 а с кривыми руками и 200 и более). На этом фоне те 2мсек не заметны. Если нужна реально высокая точность - прерывания таймера помогут.
Я и он про то, что время между последним analogRead() прошлого цикла и первым analogRead() текущего цикла = 2000 - 2. Значит пин начинает читаться чаще, чем 2000 мс. Только и всего.
Я понял, что увидел в посте Logic - симптомы дислексии. Все слова он узнал, а смысла написанного не понял.
Кстати, мне ведь 2мс не жалко. Обидно будет, когда люди по такому алгоритму будут читать что-то типа DHT или датчика с долгим временем конверсии. Вроде все работает, потому что остальные операции нивелируют, затем какой-нибудь деб... новичок ставит рядом два вызова функции readSensor() - один для чтения в переменную, а второй - для вывода на дисплей, а в if() появляется что-то с непредсказуемым временем выполнения - к примеру невовремя происходит вызов прерывания с каким-нить delayMicroseconds() внутри и готово дело. Несчастный жрет водку, плачет, попутно засерая окрестные форумы и тщетно пытается понять, почему температура 10 дней отдается нормальная, а потом хренак - и прыгает до небес. И, если у него ардуина не запустила газовый котел в экспедицию на Марс, то начинается замена сенсоров, перепайка, проверка неделями... Стоит эта круговерть одного лишнего вызова millis()?
Справедливости ради надо отметить, что DHT и прочие Далласы к классу аналоговых датчиков нихрена не относятся, но дальше, когда я буду виртуалить Read(), это может быть критично. Thanx.
Какие страсти у простейших! Обилие описаной хрени в коде означает наличие ошибок в ДНК, там миллис не поможет. И не на вредит тоже. По причине неспособности к высшей нервной деятельности.
Читай свой пост:
sadman41 пишет:
данном случае обращение к датчику будет происходить не раз в 2000 мс, а раз в 2000-<время на всякую фигню внутри if> мс. А если учесть, что внутри if() ты читаешь датчик, то он всяко будет опрашиваться чаще, чем раз в заданное время.
Выделено мной. Но ты требование "раз в 2000 мс" сам озвучил.
И смотри вывод скетча
now=2000delta=2000
Sensor polled after: 2000 ms
now=4000delta=2000
Sensor polled after: 1999 ms
now=6000delta=2000
Sensor polled after: 3000 ms
now=8000delta=2000
Sensor polled after: 999 ms
now=10000delta=2000
Sensor polled after: 2000 ms
now=12000delta=2000
Sensor polled after: 2999 ms
now=14000delta=2000
Sensor polled after: 1000 ms
now=16000delta=2000
Sensor polled after: 1999 ms
Это и называется опрос раз в 2000мсек, как ты и хотел. Период опроса такой 2000мсек. (если уж совсем точно то период не менее 2000, для в среднем 2000 пишем flasttime +=2000;) . Заметь, если разговор идет про "раз в заданное время", то это абсолютно не зависит от времени конверси, хоть 2, хоть 1000, хоть непредсказуемо (и даже дольше периода опроса, хоть эта мысль безумно сложна для тя). Период меряют от начала опроса до начала следующего опроса (или от конца - до конца, или от получения результата - до следующего результата, но между одинаковыми фазами процесса). А если нужен интервал времени от конца опроса до начала следующего равный 2000мсек то при длительности опроса 2 мс как у Семена период будет уже 2002 мсек. Тут уж просто надо определятся ему что он хочет, но при прочих равных место расположения flasttime = now; до или после опроса вобще ни на что не вличет. Возможно предполагается что её надо заменить на flasttime = millis(), тогда интервал времени от конца опроса до начала следующего будет конечно формироватся верно. Но вот беда! Период опроса будет опять таки не 2000 ;) Мало того, в случае "а if() появляется что-то с непредсказуемым временем выполнения" период опроса вобще станет непредсказуемым (да вобщем то и не периодическим, а с случайным). Потому так не делают. Делают опрос с фиксированым периодом, что очень упрощает последующую обработку.
ПС. Разберись с различием между "раз в 2000мсек" и "интервал времени от конца опроса до начала следующего". Почему второе вобще не важно, а первое должно быть константой позже поймеш, при матобработке введеного. Пока поверь так проще.
Я на этот счёт вапще не парилса, я не Громозека, мне пофиг, 400 капель у меня налито, или 402
Ну это зависит кто из этого стакана будет пить. Если это предстоит Вам, то Вы сами себе злобный Громозека.
Не дурно. указатель на функцию - правильно. Хотя наследованием в ООП правильней, но я чаще указатель на функцию пользую. Только зачем его вюеру передавать? Лучше у сенсора вместо/вместе с сохранением введеного сразу вызывать некую функцию-потребитель данных, переданую указателем в конструктор совершенно аналогично. И пусть она там дальше думает что делать с данными. Это уже не сенсора проблема. Мможет выводить или еще чего. Там тоже свои классы и указатели будут;)
Можна конечно и так, но я имел в виду не это. В конструктор сенсора добавте pDo D, но pDo с параметром как typedef void(*pDo)(int v). А из сенсоровского read() сразу и Do(analogRead(pin)); Вызов конструктора станет Cl_sensor Sensor(/*пин*/A0, eventA); Это оч. символично - в конструкторе первый параметр "откуда взять", а второй - "куда отправить".
Ну вот, теперь, когда ручка зажила (а не ходите, дети, грущиками работать, учитесь хорошо), морозяки кончились и зарядила снежная круговерть, пора наливать мне дешевого рома, в тоске о жарких странах, золотому песку и крутобёдрых мулатках, да продолжать наше повествование дальше...
Сейчас наш класс состоит из единственной функции, которая читает сенсор и нескольких полей, задающих признаки и состояние этого сенсора. Но, спросите вы, разве мы не можем просто написать эту функцию, без всяких классов, которая просто читает сенсор и отдает его значение не чаще определенного интервала времени? Зачем целый класс городить? Конечно же можем, щас мы даже так и сделаем. Обьявим, для начала, глобально, несколько переменных.
byte SensorPin; // номер пина, на котором "сидит" наш сенсор
unsigned long SensorLastTime; // время последнего к нему обращения
int SensorLastValue // последнее считанное с сенсора значение
unsigned long SensorReadInterval; // интервал, чаще которого, его не нужно читать
да и напишем, сопсно, функцию
int ReadSensor(void)
{
unsigned long now = millis(); // прочитаем время на сейчас
// Если с момента последнего обращения прошло меньше SensorReadInterval секунд, просто отдадим
// то, что прочитали последний раз, не дергая пин лишний раз
if (now - SensorLastTime < SensorReadInterval) return SensorLastValue;
// Ну а уж если времени прошло больше, куда деваца, надо читать
int result = 0;
for (byte i=0; i<16; i++) result += analogRead(SensorPin);
SensorLastTime = now; // запомним время, когда мы последний раз читали
SensorLastValue = result >> 4; // и запомним, что прочитали при этом
return SensorLastValue; // ну и то, что прочитали, то и отдадим.
}
такой подход тоже имеет право на жизнь, но вот не нравятся мне чота 4 переменные на каждый сенсор, болтающиеся в глобальных данных. Запортить их может любая другая функция этого (да и не только этого) модуля и смотри потом до дыр в глазах, кто и в какой момент это сделал. По-хорошему надо бы эти глобальные переменные спрятать. А куда их можно спрятать? Тут на ум сразу приходят статические переменные функций. Переменные, обьявленные в функциях как static, не теряют (хранят) свои значения между вызовами функции. Непонятно? Ну вот представим, что нам нужно каждый день ходить в волшебный лес и кормить там тигра. Лес - это у нас функция, а тигр - переменная (пока обычная) в этой функции. Если мы напишем так:
void Forest(void)
{
int Tiger;
Serial.println(Tiger);
}
то в сериал выведется любое случайное значение, которое осталось валяться на стеке. То есть при входе в лес, тигр может находиться где угодно, местоположение его не определено, и нам придется бродить полдня по лесу с тяжелым рюкзаком корма, искать его и плакать. Мало того, при выходе из леса тигр вапще исчезает (ну лес то волшебный), и появляется в случайном месте только когда мы входим в лес. Поэтому добрые люди сразу же, при обьявлении переменной присваивают ей значение.
int Tiger = 0;
ну слава Богу, теперь, как только мы входим в лес, тигр уже ждет нас возле дерева № 0, по крайней мере бегать по всему лесу уже не надо. Но вот беда, дерево №0 находится в таком заросшем овраге, что можно все штаны порвать, пока до него доберешься. Поэтому мы берем тигра, и как чумадан, относим его в более удобное место, например к дереву №10 (Tiger = 10). Со спокойной душой кормим тигра и уходим до завтра. А завтра что? Правильно, тигр (глупое жывотное, прям как мой кот) ждет нас опять возле дерева №0, скатина такая. Опять надо продираться через чертополох, оставляя клочки штанов на ветках. Плахая киса, не запомнил, куда мы его в прошлый раз притащили. И в следующий раз мы уже входим в лес вооруженные толстой, брутальной веревкой, к которой блестящими заклепками присобачена ржавая бирка, на которой нацарапано волшебное слово static. В очередной раз обдирая штаны до трусов, извлекаем тигра из чертополоха и веревкой привязываем упирающегося тигра к более удобному нам дереву №10.
static int Tiger = 10;
всё, галупчег, мы победили. Теперь при входе в лес мы точно знаем что
1. Там есть тигр, которого скатина леший нам навязал кормить, пока он в отпуске.
2. Тигр конкретно ждет нас у дерева №10.
И мало того, если вдруг нам покажется, что возле дерева №10 слишком много комаров, мы легко можем привязать тигра к любому другому дереву (присвоить в этой функции переменной, обьявленной как static, любое другое значение), зная, что при следующем входе в лес он будет именно там, куда мы его привязали. Например, написав где-то дальше по тексту Tiger = 20; мы точно знаем, что завтра (при следующем входе в функцию) тигр будет ждать еду конкретно возле дерева №20. А можно при входе написать Tiger++ и перебрать все деревья в лесу, чтоб ему не скучно было в одном и том же месте сидеть. Применим, написанное, попутно немношко подсократив, ведь номер пина мы можем передавать в функцию как параметр.
int ReadSensor(byte pin)
{
static unsigned long lasttime = 0; // время последнего чтения
static int lastvalue = -1; // сигнал, что pin еще не читали
static int readinterval=2000; // интервал между чтениями 2000 мс (2 секунды)
unsigned long now = millis(); // прочитаем время на сейчас
// если датчик уже читали (lastvalue>=0) И время последнего чтения меньше чем readinterval
// то просто отдаём последнее прочитанное значение из lastvalue
if ((lastvalue>=0) && (now-lasttime<readInterval)) return lastvalue;
// Ну а уж если времени прошло больше, или датчик еще совсем не читали (lastvalue<0), то куда деваца, надо читать
int result = 0;
for (byte i=0; i<16; i++) result += analogRead(pin);
lasttime = now; // запомним время, когда мы последний раз читали
lastvalue = result >> 4; // и запомним, что прочитали при этом
return lastvalue; // ну и то, что прочитали, то и отдадим.
}
И ведь надо же, и так будет работать правильно. Можно, кстати, и интервал чтения передавать как параметр, тогда еще от одной переменной можно избавиться, от readinterval. Примерно так
#define PHOTOPIN A0 // пин, на котором сидит фоторезистор
#define PHOTOREADINTERVAL 2000UL // интервал чтения фоторезистора - 2 секунды
int ReadSensor(byte pin, unsigned long interval)
{
static unsigned long lasttime = 0; // время последнего чтения
static int lastvalue = -1; // сигнал, что pin еще не читали
unsigned long now = millis(); // прочитаем время на сейчас
// если датчик уже читали (lastvalue>=0) И время последнего чтения меньше чем readinterval
// то просто отдаём последнее прочитанное значение из lastvalue
if ((lastvalue>=0) && (now-lasttime<interval)) return lastvalue;
// Ну а уж если времени прошло больше, или датчик еще не читали, то куда деваца, надо читать
int result = 0;
for (byte i=0; i<16; i++) result += analogRead(pin);
lasttime = now; // запомним время, когда мы последний раз читали
lastvalue = result >> 4; // и запомним, что прочитали при этом
return lastvalue; // ну и то, что прочитали, то и отдадим.
}
void loop(void)
{
ReadSensor(PHOTOPIN,PHOTOREADINTERVAL);
}
с какой бы частотой не вызывался loop(), наш фотосенсор не изотрется до дыр от частого чтения. Можно на этом и остановиться, наверное? Ога, ровно до того момента, как нам захочется подключить второй аналоговый сенсор, например MQ2. А чо в этом такого, спросите вы, ведь функция для чтения у нас уже есть, знай передавай туда номер пина да желаемый интервал между чтениями и получай себе данные. Как бы не так. Смотрим.
#define PHOTOPIN A0 // пин, на котором сидит фоторезистор
#define PHOTOREADINTERVAL 2000UL // интервал чтения фоторезистора - 2 секунды
#define MQ2PIN A0 // пин, на котором сидит MQ2
#define MQ2READINTERVAL 5000UL // интервал чтения датчика дыма MQ2 - 5 секунд
И если счас просто взять и написать:
void loop(void)
{
int photovalue = ReadSensor(PHOTOPIN,PHOTOREADINTERVAL); // читаем фоторезистор
int mq2value = ReadSensor(MQ2PIN, MQ2READINTERVAL);
}
то давайте представим, а чему будет равно значение mq2value? Ну хотя бы примерно? Дак вот вовсе не тому, что мы прочитали с датчика MQ2, а то что мы прочитали с фотодатчика. Непонятно. А почему? А потому что в первый раз, когда мы обратились к ReadSensor, она сохранила в своих static переменных время последнего обращения к фотодатчику и последнее прочитанное из него значение. А когда мы в следующий раз зашли в эту же функцию, но уже с номером пина MQ2 и интервалом его чтения. то вот это:
if ((lastvalue>=0) && (now-lasttime<interval)) return lastvalue;
сказало нам, что мы читали датчик несколько микросекунд назад и неважно какой это был датчик, поэтому функция нам и вернула то, что она в последний раз прочитала с фотодатчика. Вот блин засада. Опять чешем репу. Канеш, тут сразу видится, по крайней мере, 2 простых решения, либо загромождать область глобальных данных четырьмя переменными для каждого датчика и вызывать 1 функцию, передавая ей их в качестве параметров, либо писать столько функций, сколько у нас в программе будет даччиков, скрывая ихие данные в статические переменные этих функций.
Первый путь - возможен, но некрасив, в конце концов область глобальных переменных вскоре превратится в "Войну и Мир", в которой мы и сами ничего не поймем, где что лежит и к чему относится. А так как переменные осмысленно мы называть не любим, а тщательное написание комментариев нам как серпом по тестикулам, то стабильная работа кода нам может только сница. А особенно если кто-то любит обьявлять глобальные массивы и не проверять выход за границы при обращении к нему, то всё, вэлкам на форум с воплями "ПАМАГИТИ!!! Разберитесь в моём гагнакоде. Яжнепрограммист!!!".
Второй путь приводит к бессмысленному раздуванию кода программы, аптамуш, для каждого датчика компилятор генерирует свою функцию. Она ничем не отличается от функции для другого датчика, кроме как набором исходных данных. Вот тебе и выбор, б-ть! Либо делать свои данные уязвимыми для всех ветров и массивов, либо писать программу, которая из-за избыточного кода даже в Мегу не влезеть, если даччиков станет хотя бы 5. Пойду рома налью от безысходности... Ан нет, пока подожду, я же тут за классы начал говорить, вдруг оне помогут? Так вот. Классы и были придуманы умными людями (а не все буржуины в психушках стандарты С++ выдумывают) как раз именно для такой концепции, во-первых скрыть свои лишние данные от посторонних, тем самым обезопасив их, а во-вторых не раздувать код одинаковыми функциями. И если мы напишем в своей программе
TSensor Photo(A0);
TSensor MQ2(A1);
во первых, все внутренние данные РАЗНЫХ сенсоров будут РАЗНЫМИ и недоступными ни одному злобному Буратине снаружи. А во-вторых, функция ReadSensor в коде программы будет только одна. Не одна для каждого датчика, а именно одна ДЛЯ ВСЕХ датчиков одного класса TSensor. Захотите датчик влажности подключить - да пожалуйста, обьявляйте еще одну переменную класса TSensor, настраивайте желаемый пин и интервал чтения и вперед, читайте и его тоже ЭТОЙ ЖЕ САМОЙ функцией, код самого класса не вырастет ни на байт. Вы хотите спросить, а как одна функция знает, для какого даччика её вызвали? Дак очень просто. Дело в том, что в функцию класса в качестве неявного параметра (мы про это не знаем и не видим, но это есть) передается указатель на конкретный объект, который ее вызвал. примерно так:
мы пишем
int Value = Photo.ReadSensor();
а компилятор неявно преобразует это в
int Value = Photo.ReadSensor(&Photo);
то есть, неявно передает функции адрес обьекта, который ее вызвал. А так как адреса у всех обьектов разные, то одна единственная функция может работать с размыми наборами данных для каждого этого объекта (то бишь датчика). Налицо сокращение кода, особенно, когда однотипных датчиков Over чем 9000.
Пока делаем выводы, я пойду себе налью себе рома, штоли. Ручка сётаки вава. Не ходите дети в грущики.
А в след. раз будем разбирать брррр... указатели на функции.
скорей всего пишу неправильно,пытался пример со счетчиком переделать,но рано наверное в дебри полез. С реальными объектами, кнопками, получалось под себя подстроить, со счетом никак пока
Не лезь глубоко в ООП, так как надо знать грубоко синтаксис и мехамизмы компиляции, много оперативы и производительности процессора. Но если все же хочется, то здесь уже надо и гугл в помощь и много пробовать на железе.
на железе то и пробую, плюс монитор порта. но когда кода получилось около 200 строк, и так баран, да еще и не могу адекватно анализировать, задумался о классах. лучше, проще, но тяжко
Подскажите какую нибудь литературу, с классами по Ардуине.
Я тебе открою секрет - никаких "классов для ардуино" - нет. В Ардуино используется полноценный C++. Все примеры, что ты находишь в учебнике про классы С++ - должны компилироваться и на ардуино. Если у тебя не компилируется - значит просто у тебя код с ошибками, он не будет компилироваться и на "обычном С++". например в линуксе
Я правильно понял, что по сути на cpp пишем асмовские инструкции для ардуино?
неправильно, АСМ-овских инструкций в большинстве ардуиновских скетчей и близко нет.
Редко-редко где-нибудь попадется кусок, написанный на асме - обычно не больше 10-20 строк. И только там. где нужна адская производительность и С не справляется.
Ты, в своей категоричности, тоже традиционен, и порой - без смысла ;) Отказ чего-либо принимать/понимать - не значит бессмысленность этого ;) Про первичный смысл разделения на заголовочные файлы и файлы с реализацией - я уже писал, если ты не заметил. Первичный смысл - это удобный и быстрый доступ к API, которое предоставляет тот или иной набор исходников: заметь, на несколько *.cpp-файлов может быть всего один *.h-файл ;)
Для меня (да и не для меня одного) - гораздо удобнее взглянуть на объявления, чтобы понять, как работать с этим добром. А уже при необходимости - в исходнички залезать. С#, кстати, отсутствием объявлений и вымораживает - в большой простыне хер соберёшь в кучку описание функционала, предоставленного исходником.
Ты, в своей категоричности, тоже традиционен, и порой - без смысла ;) Отказ чего-либо принимать/понимать - не значит бессмысленность этого ;)
ты не понимаешь - моя категоричность заключается исключительно в одном утверждении: не нужно кого-то чему-то учить методом копипаста примеров - без объяснения смысла действий.
*пример из жизни: чел - отличник по физике, победитель республиканских олимпиад по физике купил аудиокабеля для своей аппаратуры по 100$ за погонный метр. когда я ему сказал, что он-дыбил и у него по физике в школе была твёрдая тупая тройка - оказалось, что я жестоко неправ. когда ему на на его глазах за полчаса спаяли кабель себестоимостью 10$, то на слепом аудио-тесте - он оказался жестоко не прав. и, мы оба задумались.
ты не понимаешь - моя категоричность заключается исключительно в одном утверждении: не нужно кого-то чему-то учить методом копипаста примеров - без объяснения смысла действий.
Вот тут-то как раз я с тобой согласен на 100500%.
В условиях возможности возникновения явно "левых" значений, нужно не усреднять, а брать медиану:
Очень не всегда так. Зависит от распределения вероятностей. Общеприменимое (когда думать не хочется и исходных данных нет;) скорей статобработка - считаем СКО, три сигмы, отбрасываем что невписалось и снова считаем. Это для нормального распределения. Для каждого конкретного случая свой подход. Иногда и по первому большому значению нужно хватать кота и бежать, т.к. до конца цикла обработки уже некому будет решение принимать. (Пример - контролируем напряжение в цепи постоянного тока на предмет аварии при превышении порога допустим 24В, решили брать некое среднее, но в цепь попало 220В 50Гц, среднее 0, защита не сработала. А работали бы по мгновенному напряжении, то сработала бы.). Медианное среднее - один из вариантов, а их вобще много. Добавлю для расширения кругозора еще и геометрическое среднее и фильтр Кальмана.
ПС. Когда данные поступают поочередно и с интервалами, лучше сортировать их по мере поступления, поддерживая массив отсортированым все время.
Ну вот как с вами трезвому жить?
Написано ж для подрастающего детсада, зачем седовласые старцы пытаюца разводить разводить срач ради срача? Выяснять у кого магия сильнее? Дак все вы - уважаемые луди, каждый силён по-своему. Давайте лучше вашу магию, против двух бутылок моего срецтва, да поживем здесь мирно некоторое время?
Я решил, опишу последовательно создание законченного класса да и забуду здесь писать, как страшный сон. Лучше телепузиков посмотрю под маринованных ежей с воткой.
Ну вот как с вами трезвому жить?
Написано ж для подрастающего детсада, зачем седовласые старцы пытаюца разводить разводить срач ради срача? Выяснять у кого магия сильнее? Дак все вы - уважаемые луди, каждый силён по-своему. Давайте лучше вашу магию, против двух бутылок моего срецтва, да поживем здесь мирно некоторое время?
Я решил, опишу последовательно создание законченного класса да и забуду здесь писать, как страшный сон. Лучше телепузиков посмотрю под маринованных ежей с воткой.
так, нафига ты написал про инклюды кпп и ха, если должен был писать про классы?, классы, Карл!
классы для дуино и детей - это вот это:
class test {}; void setup() {} void loop() {}когда дети подрастут - научишь их, как этим более лучше пользоваться в разных позах.
Клапа, не злись, мы тя сиравно любим. Описание класса мне куда-то пхать надо? В проект? Дак нет, я щитаю надо учится сызмальства парадигме, что каждому классу - свое укрытие. Иначе, несчастный .ino файл догонит и перегонит Войну и Мир по размерам.
В след. серии будем размахивать Ржавой Секирой Ужоса, то есть millis()-а над головами незадачливых пользователей.
В проект?
шозанах - проект?
каждому классу - свое укрытие.
это будет отдельная тема про инклюды, где ты расскажешь, что любой кусок кода возможно хранить в отдельном файле.
Иначе, несчастный .ino файл догонит и перегонит Войну и Мир по размерам.
приехали. а, что, по твоему, инклюд файла класса делает, если не дописывает текст кода в ino перед компилляцией? ты кого собрался обмануть малым объёмом кода ino - детей или компиллятор?
дети будут понимать, почему 10 строк в ino не помещаются в контроллер.
void loop() { int i = MyPhoto.Read(); }unsigned long previous = 0; unsigned long interval = 2000; // 2 секунды void loop() { unsigned long now = millis(); int value; if (now - previous > interval) { value = MyPhoto.Read(); previous = now; } }// -------------------------- // .h файл #pragma once #include "Arduino.h" class TSensor { protected: byte fpin; // поле "номер пина" типа байт, скрытый от пользователя int fvalue; // последнее считанное с pin-а значение unsigned long flasttime; // время последнего обращения к датчику public: TSensor(byte apin); // конструктор, принимающий только номер пина TSensor(byte apin, bool apullup); // а этот еще и включает или нет подтягивающий резистор на pin int Read(); // читать данные с сенсора } // -------------------------- // капец .h файла //------------------------------------- // срр файл #include "TSensor.h" // перво-наперво подключаем свой же файл определений // чтоб знать, реализацию чего мы пишем // срр файл мы не включаем в проект через #include, // поэтому #pragma once писать не надо // Реализация нашего класса. // Конструктор. В конструкторе мы просто вызываем другой конструктор, более общий TSensor::TSensor(byte apin): TSensor(apin, false) {} // Конструктор. В конструкторе мы запоминаем номер пина, который мы ему передали в качестве // параметра во внутреннем поле нашего класса и настраиваем его на вход с подтяжкой или без TSensor::TSensor(byte apin, bool apullup) { fpin = apin; if (apullup) pinMode(fpin,INPUT_PULLUP); else pinMode(fpin, INPUT); fvalue = 0; // инициализация ручками flasttime = 0; } int TSensor::Read(void) { unsigned long now = millis(); // прочитаем время на сейчас // Если с момента последнего обращения прошло меньше 2 секунд, просто отдадим // наружу, то что прочитали последний раз, не дергая пин лишний раз if (now - flasttime < 2000) return fvalue; // Ну а уж если времени прошло больше, куда деваца, надо читать int result = 0; for (byte i=0; i<16; i++) result += analogRead(fpin); flasttime = now; // запомним время, когда мы последний раз читали fvalue = result >> 4; // и запомним, что прочитали при этом return fvalue; // ну и то, что прочитали, то и отдадим. } // капец cpp файла //--------------------------------------// -------------------------- // .h файл #pragma once #include "Arduino.h" class TSensor { protected: byte fpin; // поле "номер пина" типа байт, скрытый от пользователя int fvalue; // последнее считанное с pin-а значение unsigned long flasttime; // время последнего обращения к датчику public: unsigned long ReadInterval; // частота реального чтения даччика в миллисекундах TSensor(byte apin); // конструктор, принимающий только номер пина TSensor(byte apin, bool apullup); // а этот еще и включает или нет подтягивающий резистор на pin int Read(); // читать данные с сенсора } // -------------------------- // капец .h файла //------------------------------------- // срр файл #include "TSensor.h" // перво-наперво подключаем свой же файл определений // чтоб знать, реализацию чего мы пишем // срр файл мы не включаем в проект через #include, // поэтому #pragma once писать не надо // Реализация нашего класса. // Конструктор. В конструкторе мы просто вызываем другой конструктор, более общий TSensor::TSensor(byte apin): TSensor(apin, false) {} // Конструктор. В конструкторе мы запоминаем номер пина, который мы ему передали в качестве // параметра во внутреннем поле нашего класса и настраиваем его на вход с подтяжкой или без TSensor::TSensor(byte apin, bool apullup) { fpin = apin; if (apullup) pinMode(fpin,INPUT_PULLUP); else pinMode(fpin, INPUT); fvalue = 0; flasttime = 0; ReadInterval = 2000; // по умолчанию чтение с pin-а раз в две секунды } int TSensor::Read(void) { unsigned long now = millis(); // прочитаем время на сейчас // Если с момента последнего обращения прошло меньше ReadInterval миллисекунд, просто отдадим // наружу, то что прочитали последний раз, не дергая пин лишний раз if (now - flasttime < ReadInterval) return fvalue; // Ну а уж если времени прошло больше, куда деваца, надо читать int result = 0; for (byte i=0; i<16; i++) result += analogRead(fpin); flasttime = now; // запомним время, когда мы последний раз читали fvalue = result >> 4; // и запомним, что прочитали при этом return fvalue; // ну и то, что прочитали, то и отдадим. } // капец cpp файла //--------------------------------------//-------------------------------------- // Файл Test.ino #include "TSensor.h" // ОБЪЯВЛЕНИЕ TSensor MyPhoto(A0, true); // обьявление и вызов конструктора для пина А0 с подтяжкой void setup() { // НАСТРОЙКА MyPhoto.ReadInterval = 5000; // читать не чаще раза в 5 сек. } void loop() { // ИСПОЛЬЗОВАНИЕ int value = MyPhoto.Read(); Serial.print("Value = "); Serial.println(value); delay(1000); } // капец файла Test.ino //--------------------------------------правильно, красиво, спасибо, но для небольших проектов сложно и много писать :)
ЗЫ. С молодости не люблю классы....бр.....бяка :)
ЗЫЫ. Вспоминаю соседа по общаге в середине 90х, попросили его на Turbo Pascal написать какую то примитивную лабу из пары действий, так он умудрился из программы длиной максимум строк 20 написать целый проект через классы строк на 100 минимум, делающий то же самое. Естественно вопрошающий не сдал, т.к. не смог объяснить как это все работает.
правильно, красиво, спасибо, но для небольших проектов сложно и много писать :)
ЗЫ. С молодости не люблю классы....бр.....бяка :)
ЗЫЫ. Вспоминаю соседа по общаге в середине 90х, попросили его на Turbo Pascal написать какую то примитивную лабу из пары действий, так он умудрился из программы длиной максимум строк 20 написать целый проект через классы строк на 100 минимум, делающий то же самое. Естественно вопрошающий не сдал, т.к. не смог объяснить как это все работает.
Ну так вот - еще одна польза от применения классов.
DetSimen, вообще-то есть возможность задавать функциям параметры по умолчанию. Так что пример с двумя конструкторами явно притянут.
Ну и идеологически неверно открывать поля, раз уж ООП, значит - ООП: следовало бы внести период опроса в вызов конструктора необязательным параметром. Тем более, что минимальный период, как правило, определяется дэйташитом и крайне маловероятно, что кому-то может потребоваться менять его в процессе выполнения.
DetSimen, вообще-то есть возможность задавать функциям параметры по умолчанию. Так что пример с двумя конструкторами явно притянут.
Ну и идеологически неверно открывать поля, раз уж ООП, значит - ООП: следовало бы внести период опроса в вызов конструктора необязательным параметром. Тем более, что минимальный период, как правило, определяется дэйташитом и крайне маловероятно, что кому-то может потребоваться менять его в процессе выполнения.
естесственно, это будет написано дальше, в секции про устойчивость класса.
int TSensor::Read(void) { unsigned long now = millis(); // прочитаем время на сейчас if (now - flasttime < 2000) return fvalue; ... flasttime = now; // запомним время, когда мы последний раз читали .. }Семен, тебя квон покусал что ли - на millis() экономишь...
В данном случае обращение к датчику будет происходить не раз в 2000 мс, а раз в 2000-<время на всякую фигню внутри if> мс. А если учесть, что внутри if() ты читаешь датчик, то он всяко будет опрашиваться чаще, чем раз в заданное время. Конечно это, скорее всего, будет быть снивелированно другими операциям вне процедуры чтения, но ржавое ружьё иногда и палит посреди ночи.
16 вызовов analogRead() на 328 Меге 16 Мгц выполняются ~2 мс. Критичненько? Я же не звездолеты строю и не осциллографы реального времени, где за каждый такт надабароца, я такого пока и не осилю. Мои поделки простые, как я. А в них и классы простые.
Да што ты заводишься сразу. Датчики-то разные бывают. Просто нужно запоминать время после окончания чтения, а не до него. Один лишний вызов millis(), а пользы от него - как от банки рассола на утро.
Я не завожусь, не подумай плахова. :)
Это же тогда в тексте надо будет слова писать про целесообразность еще одного такого вызова. А я хорошо умею только лечь и лежать изредка переворачиваясь, чтоб не отлежать лежало. Выходной же. Дальше писать буду на работе.
int TSensor::Read(void) { unsigned long now = millis(); // прочитаем время на сейчас if (now - flasttime < 2000) return fvalue; ... flasttime = now; // запомним время, когда мы последний раз читали .. }Семен, тебя квон покусал что ли - на millis() экономишь...
В данном случае обращение к датчику будет происходить не раз в 2000 мс, а раз в 2000-<время на всякую фигню внутри if> мс. А если учесть, что внутри if() ты читаешь датчик, то он всяко будет опрашиваться чаще, чем раз в заданное время. Конечно это, скорее всего, будет быть снивелированно другими операциям вне процедуры чтения, но ржавое ружьё иногда и палит посреди ночи.
Вы фигню писать изволили. Как раз приведеный код обеспечит время между чтениями не менее 2000мс. Т.е. равно и более 2 сек. Для обеспечения времени в среднем 2000 пишем flasttime += 2000;. И уж совершенно пофиг до или после чтения. Про экономию: а чё не так?! Экономить - не транжирить, к разорению не ведет. Я бы для экономии еще лонгинт убрал везде, там и инта с головой. И миллис бы один раз в начале лопа вызывал, сохранял бы и передавал бы всем заинтересованым.
Вы фигню писать изволили. Как раз приведеный код обеспечит время между чтениями не менее 2000мс. Т.е. равно и более 2 сек. Для обеспечения времени в среднем 2000 пишем flasttime += 2000;
Прогоните:
void setup() { Serial.begin(9600); } void loop() { uint8_t processSomething = 0; uint32_t now, flasttime, sensorPrevReadingTime, sensorRestTime; flasttime = sensorPrevReadingTime = millis(); while (true) { now = millis(); // прочитаем время на сейчас if (now - flasttime < 2000) continue; // return fvalue; processSomething++; if (3 <= processSomething) { delay(1000); //Serial.print("Process something"); processSomething = 0; } // int result = 0; sensorRestTime = millis() - sensorPrevReadingTime; Serial.print("Sensor polled after: "); Serial.print(sensorRestTime); Serial.println(" ms"); sensorPrevReadingTime = millis(); flasttime = now; // запомним время, когда мы последний раз читали //flasttime = millis(); // fvalue = result >> 4; // и запомним, что прочитали при этом // return fvalue; // ну и то, что прочитали, то и отдадим. } }sadman, я то понимаю, что ты прав, я же запоминаю now ДО чтения, а у Логика с логикой проблемы. Но он это никада не признает.
Прогоните:
Люблю конструктивный подход !
Прогнал.
void setup() { Serial.begin(9600); } void loop() { uint8_t processSomething = 0; uint32_t now, sensorPrevReadingTime, sensorRestTime; int flasttime; flasttime=sensorPrevReadingTime = millis(); while (true) { int now = millis(); // прочитаем время на сейчас if (now - flasttime < 2000) continue; // return fvalue; Serial.print("now="); Serial.print(now); Serial.print("delta="); Serial.println(now - flasttime); processSomething++; if (3 <= processSomething) { delay(1000); //Serial.print("Process something"); processSomething = 0; } // int result = 0; sensorRestTime = millis() - sensorPrevReadingTime; Serial.print("Sensor polled after: "); Serial.print(sensorRestTime); Serial.println(" ms"); sensorPrevReadingTime = millis(); flasttime = now; // запомним время, когда мы последний раз читали //flasttime = millis(); // fvalue = result >> 4; // и запомним, что прочитали при этом // return fvalue; // ну и то, что прочитали, то и отдадим. } }sadman, я то понимаю, что ты прав, я же запоминаю now ДО чтения, а у Логика с логикой проблемы. Но он это никада не признает.
Не чувак, у меня проблема с тупыми мнящими из себя фигзнает что, вместо того чтоб учится. См пост выше.
ПС. Неужели трудно осилить что
и
обеспечат на выходе одинаковое А при том же В на входе.
Время чтения 16 раз analogRead() и взятия среднего - 2 мс. значить, время опроса самого датчика
2000 мс - 2 мс. = 1998 мс.
Время чтения 16 раз analogRead() и взятия среднего - 2 мс. значить, время опроса самого датчика
2000 мс - 2 мс. = 1998 мс.
Нет конечно. Просто взятие среднего завершится на 2мс позже чем сработало условие завершения интервала. И так каждый цикл. Сработало условие ровно через 2сек, пошол опрос рсачет среднего и завершился через 2мс. И между этими завершениями сново будет 2 сек. В первый коде время скачет т.к. делей еще и под ифом, иногда в цикле он есть, иногда нет. Вы конечно дико удивитесь, но даже в таком случае среднее время опроса всеравно останется 2 сек )))
Коментируем if (3 <= processSomething) и о чудо!
Я и он про то, что время между последним analogRead() прошлого цикла и первым analogRead() текущего цикла = 2000 - 2. Значит пин начинает читаться чаще, чем 2000 мс. Только и всего.
Если сделать так
53intTSensor::Read(void)54{5556unsignedlongnow = millis();// прочитаем время на сейчас5758// Если с момента последнего обращения прошло меньше ReadInterval миллисекунд, просто отдадим59// наружу, то что прочитали последний раз, не дергая пин лишний раз6061if(now - flasttime < ReadInterval)returnfvalue;6263// Ну а уж если времени прошло больше, куда деваца, надо читать6465intresult = 0;6667for(bytei=0; i<16; i++) result += analogRead(fpin);6869flasttime = millis();// запомним время, когда мы последний раз читали ИЗМЕНЕНО ЗДЕСЬ70fvalue = result >> 4;// и запомним, что прочитали при этом7172returnfvalue;// ну и то, что прочитали, то и отдадим.73}То точно пройдет время НЕ МЕНЬШЕ 2000 мс между реальными обращением к пину.
И это. Извини, если обидел, я так больше не буду. Сонный был.
Я и он про то, что время между последним analogRead() прошлого цикла и первым analogRead() текущего цикла = 2000 - 2. Значит пин начинает читаться чаще, чем 2000 мс. Только и всего.
.....
То точно пройдет время НЕ МЕНЬШЕ 2000 мс между реальными обращением к пину.
И это. Извини, если обидел, я так больше не буду. Сонный был.
Та какие там обиды, проехали.
А я в #69 писал про два случая: НЕ МЕНЬШЕ и В СРЕДНЕМ. Это уже кому когда где что надо. Еще обращу таки внимание что в примерах выще int вместо long int прописан. Экономия по мелочам, тут 100гр, там 100гр, а в итоге скетч влазит в память.
А на вашем месте я бы про эти 2мсек чтения обработки вобще бы не вспоминал на фонее периода 2000. Дело в том что в большом скетче с прерываниями и крупными кусками кода обеспечить прокрутку лопа не реже 1мсек, чтоб сформировать точно 2000, очень сложно. Период будет плясать +-5мсек (и это еще очень хороше, может и 10 и 20 а с кривыми руками и 200 и более). На этом фоне те 2мсек не заметны. Если нужна реально высокая точность - прерывания таймера помогут.
Я на этот счёт вапще не парилса, я не Громозека, мне пофиг, 400 капель у меня налито, или 402
Я на этот счёт вапще не парилса, я не Громозека, мне пофиг, 400 капель у меня налито, или 402
/**/ unsigned long mill; // переменная под millis() typedef void (*pDo)() ;// тип -функция обработчик //-------------------------------- // класс сенсор class Cl_sensor { protected: const byte pin; const unsigned long inteval = 2000; unsigned long past; int val; public: /*конструктор*/ Cl_sensor(byte p): pin(p) {} /*инициализация-вставить в setup()*/ void init() { val = analogRead(pin); past = mill; } /*работа-вставить в loop()*/ void run() { } /*прочитать значение с сенсора*/ int read() { if (mill - past < inteval) return val; past = mill; return val = analogRead(pin); } }; //-------------------------------- // класс вьювер class Cl_viev { protected: pDo Do;//обработчик const unsigned long interval = 500; unsigned long past; public: /*конструктор*/ Cl_viev(pDo D): Do(D) {} /*инициализация-вставить в setup()*/ void init() { Do(); } /*работа-вставить в loop()*/ void run() { if (mill - past < interval) return; past = mill; Do(); } }; //---Компоновка----------------------------- Cl_sensor Sensor(/*пин*/A0); void DoViev() { Serial.println(Sensor.read()); } Cl_viev Viev(/*обработчик*/DoViev); //---main----------------------------- void setup() { Serial.begin(9600); Viev.init(); Sensor.init(); } void loop() { mill = millis(); Viev.run(); Sensor.run(); } /*Скетч использует 2104 байт (6%) памяти устройства. Всего доступно 32256 байт. Глобальные переменные используют 211 байт (10%) динамической памяти, оставляя 1837 байт для локальных переменных. Максимум: 2048 байт. */Я и он про то, что время между последним analogRead() прошлого цикла и первым analogRead() текущего цикла = 2000 - 2. Значит пин начинает читаться чаще, чем 2000 мс. Только и всего.
Я понял, что увидел в посте Logic - симптомы дислексии. Все слова он узнал, а смысла написанного не понял.
Кстати, мне ведь 2мс не жалко. Обидно будет, когда люди по такому алгоритму будут читать что-то типа DHT или датчика с долгим временем конверсии. Вроде все работает, потому что остальные операции нивелируют, затем какой-нибудь деб... новичок ставит рядом два вызова функции readSensor() - один для чтения в переменную, а второй - для вывода на дисплей, а в if() появляется что-то с непредсказуемым временем выполнения - к примеру невовремя происходит вызов прерывания с каким-нить delayMicroseconds() внутри и готово дело. Несчастный жрет водку, плачет, попутно засерая окрестные форумы и тщетно пытается понять, почему температура 10 дней отдается нормальная, а потом хренак - и прыгает до небес. И, если у него ардуина не запустила газовый котел в экспедицию на Марс, то начинается замена сенсоров, перепайка, проверка неделями... Стоит эта круговерть одного лишнего вызова millis()?
sadman, я тебя понял, исправлю завтра.
Справедливости ради надо отметить, что DHT и прочие Далласы к классу аналоговых датчиков нихрена не относятся, но дальше, когда я буду виртуалить Read(), это может быть критично. Thanx.
Тока срачица не нада.
Какие страсти у простейших! Обилие описаной хрени в коде означает наличие ошибок в ДНК, там миллис не поможет. И не на вредит тоже. По причине неспособности к высшей нервной деятельности.
Читай свой пост:
данном случае обращение к датчику будет происходить не раз в 2000 мс, а раз в 2000-<время на всякую фигню внутри if> мс. А если учесть, что внутри if() ты читаешь датчик, то он всяко будет опрашиваться чаще, чем раз в заданное время.
Выделено мной. Но ты требование "раз в 2000 мс" сам озвучил.
И смотри вывод скетча
now=2000delta=2000
Справедливости ради надо отметить, что DHT
Он про него вспомнил т.к. време конверсии большое, до 750мсек. Некоторые идиоты его делеем формируют. Вобщем обсуждение кода слабоумных там.
Я на этот счёт вапще не парилса, я не Громозека, мне пофиг, 400 капель у меня налито, или 402
Не дурно. указатель на функцию - правильно. Хотя наследованием в ООП правильней, но я чаще указатель на функцию пользую. Только зачем его вюеру передавать? Лучше у сенсора вместо/вместе с сохранением введеного сразу вызывать некую функцию-потребитель данных, переданую указателем в конструктор совершенно аналогично. И пусть она там дальше думает что делать с данными. Это уже не сенсора проблема. Мможет выводить или еще чего. Там тоже свои классы и указатели будут;)
у мня будет и указатель на функцию, и не одну. Но - завтра. Када я плюшевый, к компу не лезу. Я стакан я уже накатил. Чего и вам желаю, вместо срача.
Если завтра - то только через абстрактный метод принимаю ;)
ПС. Накачу, обязательно, но позже. Сейчас за руль, приеду - как раз к разгару срача, а потом и накочу сливяночки стакан и не один.
Я написал в свой традиционной системе событийного программирования. Но если бы к своему новому пониманию, то текст был бы таким
/**/ unsigned long mill; // переменная под millis() typedef void (*pDo)() ;// тип -событие //-------------------------------- // класс сенсор class Cl_sensor { protected: const byte pin; const unsigned long inteval = 2000; unsigned long past = 0; int val; public: /*конструктор*/ Cl_sensor(byte p): pin(p) {} /*прочитать значение с сенсора*/ int read() { if (mill - past < inteval) return val; past = mill; return val = analogRead(pin); } }; //-------------------------------- // класс вьювер class Cl_display { protected: pDo Do;//событие const unsigned long interval = 500; unsigned long past; public: /*конструктор*/ Cl_display(pDo D): Do(D) {} /*вывести на экран)*/ void viev() { if (mill - past < interval) return; past = mill; Do(); } }; //---Компоновка----------------------------- Cl_sensor Sensor(/*пин*/A0); void eventA() { Serial.println(Sensor.read()); } Cl_display display(/*событие*/eventA); //---main----------------------------- void setup() { Serial.begin(9600); } void loop() { mill = millis(); display.viev(); } /*Скетч использует 2048 байт (6%) памяти устройства. Всего доступно 32256 байт. Глобальные переменные используют 211 байт (10%) динамической памяти, оставляя 1837 байт для локальных переменных. Максимум: 2048 байт. */Можна конечно и так, но я имел в виду не это. В конструктор сенсора добавте pDo D, но pDo с параметром как
typedefvoid(*pDo)(int v). А из сенсоровского read() сразу и Do(analogRead(pin)); Вызов конструктора станетCl_sensor Sensor(/*пин*/A0, eventA);Это оч. символично - в конструкторе первый параметр "откуда взять", а второй - "куда отправить".А можна и еще интересней! ;)
int ReadSensor(void) { unsigned long now = millis(); // прочитаем время на сейчас // Если с момента последнего обращения прошло меньше SensorReadInterval секунд, просто отдадим // то, что прочитали последний раз, не дергая пин лишний раз if (now - SensorLastTime < SensorReadInterval) return SensorLastValue; // Ну а уж если времени прошло больше, куда деваца, надо читать int result = 0; for (byte i=0; i<16; i++) result += analogRead(SensorPin); SensorLastTime = now; // запомним время, когда мы последний раз читали SensorLastValue = result >> 4; // и запомним, что прочитали при этом return SensorLastValue; // ну и то, что прочитали, то и отдадим. }такой подход тоже имеет право на жизнь, но вот не нравятся мне чота 4 переменные на каждый сенсор, болтающиеся в глобальных данных. Запортить их может любая другая функция этого (да и не только этого) модуля и смотри потом до дыр в глазах, кто и в какой момент это сделал. По-хорошему надо бы эти глобальные переменные спрятать. А куда их можно спрятать? Тут на ум сразу приходят статические переменные функций. Переменные, обьявленные в функциях как static, не теряют (хранят) свои значения между вызовами функции. Непонятно? Ну вот представим, что нам нужно каждый день ходить в волшебный лес и кормить там тигра. Лес - это у нас функция, а тигр - переменная (пока обычная) в этой функции. Если мы напишем так:
void Forest(void) { int Tiger; Serial.println(Tiger); }int ReadSensor(byte pin) { static unsigned long lasttime = 0; // время последнего чтения static int lastvalue = -1; // сигнал, что pin еще не читали static int readinterval=2000; // интервал между чтениями 2000 мс (2 секунды) unsigned long now = millis(); // прочитаем время на сейчас // если датчик уже читали (lastvalue>=0) И время последнего чтения меньше чем readinterval // то просто отдаём последнее прочитанное значение из lastvalue if ((lastvalue>=0) && (now-lasttime<readInterval)) return lastvalue; // Ну а уж если времени прошло больше, или датчик еще совсем не читали (lastvalue<0), то куда деваца, надо читать int result = 0; for (byte i=0; i<16; i++) result += analogRead(pin); lasttime = now; // запомним время, когда мы последний раз читали lastvalue = result >> 4; // и запомним, что прочитали при этом return lastvalue; // ну и то, что прочитали, то и отдадим. }int ReadSensor(byte pin, unsigned long interval) { static unsigned long lasttime = 0; // время последнего чтения static int lastvalue = -1; // сигнал, что pin еще не читали unsigned long now = millis(); // прочитаем время на сейчас // если датчик уже читали (lastvalue>=0) И время последнего чтения меньше чем readinterval // то просто отдаём последнее прочитанное значение из lastvalue if ((lastvalue>=0) && (now-lasttime<interval)) return lastvalue; // Ну а уж если времени прошло больше, или датчик еще не читали, то куда деваца, надо читать int result = 0; for (byte i=0; i<16; i++) result += analogRead(pin); lasttime = now; // запомним время, когда мы последний раз читали lastvalue = result >> 4; // и запомним, что прочитали при этом return lastvalue; // ну и то, что прочитали, то и отдадим. } void loop(void) { ReadSensor(PHOTOPIN,PHOTOREADINTERVAL); }с какой бы частотой не вызывался loop(), наш фотосенсор не изотрется до дыр от частого чтения. Можно на этом и остановиться, наверное? Ога, ровно до того момента, как нам захочется подключить второй аналоговый сенсор, например MQ2. А чо в этом такого, спросите вы, ведь функция для чтения у нас уже есть, знай передавай туда номер пина да желаемый интервал между чтениями и получай себе данные. Как бы не так. Смотрим.
void loop(void) { int photovalue = ReadSensor(PHOTOPIN,PHOTOREADINTERVAL); // читаем фоторезистор int mq2value = ReadSensor(MQ2PIN, MQ2READINTERVAL); }Модератору, если позволите продолжать, то просьба перенести эту графоманию в "Отвлеченные темы".
Подскажите какую нибудь литературу, с классами по Ардуине.
В гугле видать забанили меня или искать не умею, все с готовыми скетчами нахожу,читаю Лафоре, но чистый С++ не компилируется
NalisniyRoman, заканчивайте искать в твитере и читайте основателя С++ http://8361.ru/6sem/books/Straustrup-Yazyk_programmirovaniya_c.pdf
Ну и мои зарисовки http://arduino.ru/forum/programmirovanie/klassy-arduino-po-qwone-dlya-chainikov
читаю Лафоре, но чистый С++ не компилируется
Это как это?
скорей всего пишу неправильно,пытался пример со счетчиком переделать,но рано наверное в дебри полез. С реальными объектами, кнопками, получалось под себя подстроить, со счетом никак пока
Не лезь глубоко в ООП, так как надо знать грубоко синтаксис и мехамизмы компиляции, много оперативы и производительности процессора. Но если все же хочется, то здесь уже надо и гугл в помощь и много пробовать на железе.
на железе то и пробую, плюс монитор порта. но когда кода получилось около 200 строк, и так баран, да еще и не могу адекватно анализировать, задумался о классах. лучше, проще, но тяжко
Подскажите какую нибудь литературу, с классами по Ардуине.
Я тебе открою секрет - никаких "классов для ардуино" - нет. В Ардуино используется полноценный C++. Все примеры, что ты находишь в учебнике про классы С++ - должны компилироваться и на ардуино. Если у тебя не компилируется - значит просто у тебя код с ошибками, он не будет компилироваться и на "обычном С++". например в линуксе
единственное, в книшках часто любят примеры писАть, используя std::какаянить хня, дак вот эта вот какаянить хня на Ардуинах не работает, памяти мало.
Я правильно понял, что по сути на cpp пишем асмовские инструкции для ардуино?
неправильно, АСМ-овских инструкций в большинстве ардуиновских скетчей и близко нет.
Редко-редко где-нибудь попадется кусок, написанный на асме - обычно не больше 10-20 строк. И только там. где нужна адская производительность и С не справляется.