#define fadePin1 10 //пин управления MOSFET транзистором
#define fadePin2 9 //пин управления MOSFET транзистором
#define fadePin3 6 //пин управления MOSFET транзистором
int calibrationTime = 30; //Время калибровки датчика (10-60 сек. по даташиту)
int pirPin1 = 2; //пин подключения управляющего сигнала PIR датчика1
int pirPin2 = 4; //пин подключения управляющего сигнала PIR датчика2
int light; //переменная для хранения состояния света (вкл/выкл)
void setup(){
Serial.begin(9600);
pinMode(pirPin1, INPUT); //настариваем 2 пин как вход для сигналов с датчика
pinMode(pirPin2, INPUT); //настариваем 4 пин как вход для сигналов с датчика
pinMode(fadePin1, OUTPUT); // пины на выход, для управления транзисотором
pinMode(fadePin2, OUTPUT);
pinMode(fadePin3, OUTPUT);
light = 0; //устанаваливаем переменную для первого включения света
Serial.print("Calibrating"); //дадим датчику время на калибровку
for(int i = 0; i < calibrationTime; i++)
{
Serial.print(".");
delay(1000);
}
Serial.println(" done");
Serial.println("SENSOR ACTIVE");
delay(50);
}
uint8_t fades[] = {fadePin1, fadePin2, fadePin3};
int isGo = 0; // порядок включения ламп
void fadeLED(boolean state, int is_go)
{
int num=0, max=0;
if( is_go == 1 ){ num=0; max=3; }
if( is_go == -1){ num=2; max=-1; }
while( num != max ){
for(int i = 255*(1-state); state ? i<=255 : i>=0; state ? i++ : i--)
{
analogWrite(fades[num], i);
Serial.println(i); // передаем данные в порт
delay(10);
}
num += is_go;
}
}
void loop()
{
if( ! light ) // сначала проверяем это!
{
if( digitalRead(pirPin1) ){ isGo = 1; }
if( digitalRead(pirPin2) ){ isGo = -1; }
fadeLED( HIGH, isGo ); // включаем лампЫ
delay(5000);
light = 1;
// isGo = -isGo; // это если надо выключать в обратном порядке.
} else {
fadeLED( LOW, isGo );
light = 0;
isGo = 0;
}
}
работает, как и хотел! Спасибо! Вот только одно НО, после выключения, какая то задержка что ли, датчики не реагируют вообще секунд 5-7( почему?
Вернемся чуть назад! все-таки дело видимо в скетче, не датчики тут виноваты! Заливаю верхний, отклик после 5-7 сек, заливаю этот
#define fadePin1 10 //пин управления MOSFET транзистором
#define fadePin2 9 //пин управления MOSFET транзистором
#define fadePin3 6 //пин управления MOSFET транзистором
int calibrationTime = 30; //Время калибровки датчика (10-60 сек. по даташиту)
int pirPin1 = 2; //пин подключения управляющего сигнала PIR датчика1
int pirPin2 = 4; //пин подключения управляющего сигнала PIR датчика2
int light; //переменная для хранения состояния света (вкл/выкл)
void setup(){
Serial.begin(9600);
pinMode(pirPin1, INPUT); //настариваем 2 пин как вход для сигналов с датчика
pinMode(pirPin2, INPUT); //настариваем 4 пин как вход для сигналов с датчика
pinMode(fadePin1, OUTPUT); // пины на выход, для управления транзисотором
pinMode(fadePin2, OUTPUT);
pinMode(fadePin3, OUTPUT);
light = 0; //устанаваливаем переменную для первого включения света
Serial.print("Calibrating"); //дадим датчику время на калибровку
for(int i = 0; i < calibrationTime; i++)
{
Serial.print(".");
delay(1000);
}
Serial.println(" done");
Serial.println("SENSOR ACTIVE");
delay(50);
}
void fadeLED(byte pin, boolean state)
{for(int i = 255*(1-state); state ? i<=255 : i>=0; state ? i++ : i--)
{
analogWrite(pin, i);
Serial.println(i); // передаем данные в порт
delay(10);
}
}
void loop()
{
if(digitalRead(pirPin1)) // если есть движение
{ if(!light) // и если свет не был включен
{ fadeLED(fadePin1, HIGH);
fadeLED(fadePin2, HIGH);
fadeLED(fadePin3, HIGH);
} // включаем по очереди
delay(5000); // пауза после полного открытия всех транзисторов
light = 1; // передаем значение переменной, что свет включен
}
if(digitalRead(pirPin2)) // если есть движение
{ if(!light) // и если свет не был включен
{ fadeLED(fadePin3, HIGH);
fadeLED(fadePin2, HIGH);
fadeLED(fadePin1, HIGH);
} // включаем по очереди
delay(5000); // пауза после полного открытия всех транзисторов
light = 1; // передаем значение переменной, что свет включен
}
else // иначе
{
if(light) // и если свет включен
{
fadeLED(fadePin1, LOW);
fadeLED(fadePin2, LOW);
fadeLED(fadePin3, LOW);
} // выключаем по очереди
light = 0; // передаем значение переменной, что свет выключен
}
}
Все работает сразу, ни какой задержки! Почему так происходит?
Задержка должна отрабатывать в обоих случаях. Она там у вас прописана в delay(5000) - 5сек "как с куста". Пока этот оператор выполняется - МК просто тупит и ни на что не реагирует. Мы с этим ещё разберемся чуть позже.
Кстати, Logik вам предложил несколько иной алгоритм и в нем есть свои особенности, смотрите откомментирую:
byte LeftLED=0;
byte RigthLED=0;
int OldTime;
#define TIME_WAVE 5000 //5 sec
#define Fotodiod digitalRead(8)
#define PIR_left digitalRead(9)
#define PIR_rigth digitalRead(10)
#define OutLED(a) PORTD=a
void loop(void)
{
int Time=millis(); // Заходя каждый раз сюда - запоминаем время захода (мсек.)
if(LeftLED || RigthLED) // чтото светит, датчик света не интересен - он блоке else! даже не смотрим!
{
if(Time-OldTime>TIME_WAVE) // таймер сработал - прошла фикс. пауза (TIME_WAVE) с прошлого захода внутрь)
{
OldTime=Time; // фиксируем время захода для отсчета следующей паузы
if(LeftLED==0xff) // светится все
LeftLED=0x7f; // .. выключаем старший бит (предположительно левую лампу - см. соединения!)
else // не всё (где-то бит уже выключен!):
LeftLED=(LeftLED>>1) | (LeftLED & 0x80); // .. сдвигаем картинку вправо и добавляем включение крайнего левого бита
if(RigthLED==0xff) //светится все (аналогично, только справа-налево. ничего тут НЕ тушим - просто бегаем справа-налево!)
RigthLED=0xfe; // тушим первую (правую)
else
RigthLED=(RigthLED<<1) | (RigthLED & 1); //сдвигаем с заполнением нового разряда старым значением (нет заполняем 1 - включаем)
}
}
else //ничего не светится, проверяем датчик света И ТОЛЬКО КОГДА НИЧЕГО НЕ СВЕТИТСЯ!
if(Fotodiod) //светло
return;
//-- этот блок работает независимо от предыдущего:
if(!LeftLED) //слева не стартовали
if(PIR_left) //проверим левый датчик
{
LeftLED=0x80; //старт слева
OldTime=Time; //таймер актуализируем бесполезно, если не установлено в setup()! назаданное значение ==0, но тут даже не важно.
}
if(!RigthLED)
if(PIR_rigth)
{
RigthLED=1;
OldTime=Time;
}
OutLED(RigthLED | LeftLED); // включаем всё что накрутили и сразу и мгновенно.
}
Отличия:
1. Скетч работает с цифровым выводом, а не ШИМ. То есть яркость ламп устанавливается мгновенно, а не плавно.
2. Скетч работает с байтом, предполагая поключение реле ламп строго по месту: левый (старший бит) байта порта управления должен соответствовать крайней левой лампе и тд. Младший бит - крайней правой. Не у всех Ардуин и далеко не все пины расположены "подряд" от одного регистра-порта вывода .. смотреть даташит на вашу дуньку и её распиновку тщательно. Впрочем, у вас похоже УНО - там кажется такие есть.
3. Бит, у которого стоит 0 - лампа выключена. И такой - один на каждый датчик. То есть, как только датчик сработал, программа будет "крутить" - выключать по одной лампе "по кругу", оставляя остальные включенными постоянно. Если сработал второй датчик, то ещё одна лампа будет бегать в обратном направлении.
4. Заданная пауза - это "скорость" переключения ламп - скорость кругового бега.
5. Однажды сработавший датчик запустит процесс, а код его остановки тут отсутствует "напрочь". То есть выключать лампы тут - банально нечем.
6. Если процесс мигания вкруговую запущен, то датчик освещенности - уже не играет роли и НЕ проверяется вовсе, ибо стоит в блоке else.
Как интересное решение - полезно. Может вам понравится. Заодно освоите один простой момент: все пины соттветствуют какому-то регистру вывода и собраны побайтно .. но разбросаны по ногам платы .. :)
Да, ещё немаловажный момент: скетч рассчитан из предположения ровно 8 ламп. Если ламп меньше (у вас кажется 6), то будет пауза на время переключения несуществующих ламп, в течение которой все имеющиеся лампы будут гореть одновременно.
Если нужно гасить все - обнуляем LeftLED и RigthLED.
Если меньше 8 ламп правим условия if(LeftLED==0xff) на if(LeftLED==0xfс) (для 6-и) и аналогично RigthLED для.
ШИМ и произвольный порядок пинов - развиваем OutLED в функцию делающую нужное.
Кстати можно исключить горение нескольких ламп, т.е. горит одна сопровождающая движущегося, тогда из LeftLED=(LeftLED>>1) | (LeftLED & 0x80); делаем LeftLED=(LeftLED>>1) условия if(LeftLED==0xff) на if(LeftLED==0x04) (для 6-и). И аналогично RigthLED.
Аналогично делается горение нескольких (2-3 ламп) сопровождающих движущегося.
ну вот, пришел Logik и все опошлил битовыми операциями. Теперь точно никто не сможет разобраться в скетче :)
Зря Вы так :) вообще битовые операции самое то, я даже банальное сложение чисел иногда на них делаю :) сейчас то-же увлекся переписанием своего протокола под битовые операции, тем самым сократил заголовок хедера на 3 байта (с 11 до 8), то есть скорость малых пакетов возрасла почти на 20%....
То же самое и с внутренним массивом, нужно мне запомнить состояния 255 устройств, с битовыми операциями я обошелся в 32 байта ОЗУ
Битовые операции - самые быстрые (большенство занимает 1 такт, правда это не не включая пересылку в регистр). Единственное чего я в себе не как не могу побороть - это излишества скобок при работе с ними, да я знаю про приоритеты выполнения, но все равно ставлю скобки...
Logik, перечитайте то ТЗ, которое составлено и утверждено вместе с автором темы. Оно - иное, и цель там другая. Ваш код безусловно интересен как знакомство с битовыми операциями и неблокирующей работой интервальных автоматов .. но тут задача пока несколько иная. И первая часть задачи - построить верный алгоритм, а не код программы.
Кодировать программу по готовому и правильному алгоритму, можно даже в сильно нетрезвом виде и даже вовсе мало что смысля в конкретном языке, тупо смотря похожие примеры и выдирая из них готовые куски. А вот составить алгоритм, в точности соответствующий задаче - это и есть ПРОГРАММИРОВАНИЕ. И конкретный язык или реализация - тут вовсе не при чем.
Пока автор осваивает эту часть - алгоритмирование. :)
Задержка должна отрабатывать в обоих случаях. Она там у вас прописана в delay(5000) - 5сек "как с куста". Пока этот оператор выполняется - МК просто тупит и ни на что не реагирует. Мы с этим ещё разберемся чуть позже.
Так она же прописана между включить и выключить, а не перед (движение-включить) а она то как раз там и появляется(
Как-то странно. Задержка в ОБОИХ скетчах прописана в одном и том же месте: сразу после включения ламп. В этой части между ними никакой разницы нет. То есть должно быть так: сработал датчик - пошли загораться лампы, зажглись - ждем 5 сек и после этого лампы выключаются. Никакой разницы.
Разница проявляется только в порядке обработки датчиков: в первом скетче они опрашиваются практически одновременно и сразу запускается процесс включения-задержки, а после задержки, при следующем вхождении в loop(), начинается процесс выключения ламп БЕЗ опроса датчиков, но и без задержек. Но пока идет выключение - тоже ничего не опрашивается.
, а во втором сначала опрашивается один .. и если ОН сработал, то начинается его процесс включения - задержки, и только потом опрашивается второй датчик и если он сработал - то начинается его процесс включения-задержки. А процесс выключения начинается ТОЛЬКО в случае НЕсрабатывания второго датчика (его блок else!).
Время на включение/выключение: 10мсек * 256 * 3 = 2.56 * 3 = 7.5секунд. Это то время, в течение которого оба варианта тупят и тоже ничего не делают как и при delay(5сек). Возможно что Вы обнаруживаете эту задержку.
Практически нет. Опрос датчиков визуально проходит практически мгновенно: просто МК смотрит есть на входе лог. 1 или там лог.0 и всё. Времени на этот процесс затрачивается в несколько микросекунд.
Порт - это внутренний регистр ввода-вывода (смотря какой), где собрано 8 ножек. К нумерации ножек платы имеет практически никакое соотношение, что и представляет собой определенное неудобство в програмировании. Как было удобно - так и развели.
Давайте вернемся к нашему алгоритму. Предлагаю начать с включения-выключения ламп "по 1шт за 1 вызов loop()"
У вас есть функция fadeLed() которая делает процесс плавным, но при этом несколько "тупит". Давайте поправим её так, чтобы в этом процессе устранить delay(), хоть он и краток (но его много раз дергаем)..
// у нас был список ножек ламп .. добавляем к нему список текущих яркостей, и для этого опишем структуру "лампа"
typedef struct{
uint8_t pin;
uint8_t bright;
} Led;
// теперь создадим список ламп, заодно и введем константу "всего ламп"
#define MAX_LED 3
Led fades[MAX_LED] = {{fadePin1, 0}, {fadePin2, 0}, {fadePin3, 0}};
// Функцию изменим так, чтобы она получала номер лампы и направление включать-выключать
// @param state: +1 -- включение; -1 -- выключение
// сразу формируем указатель на текущую лампу, это лучше чем смотреть в массив каждый раз
// как и положено, функция ничего не проверяет. Верный вызов - проблема вызывающего!
void fadeLED(uint8_t lamp, uint8_t state)
{
Led * ptr = &(fades[lamp]);
ptr->bright += state;
analogWrite( ptr->pin, ptr->bright);
Serial.println(i); // передаем данные в порт
}
Arhat109-2, прокомментируйте строки 17-20, для нас это джунгли :) Зачем нужно было водить структуру?
я строчку 17 понимаю как некий трюк позволяющий обращатся к одним данным используя разные типы без приведения типов, что-то похожее на record в паскале, хотя может я и не прав совсем :)
Структура типа Led описывает каждую лампу как явление из 2-х сущностей: номера управляющего пина и текущего уровня яркости свечения (ШИМ). Это необходимо, чтобы отвязать код, управляющий яркостью от "цикла включения" в виде оператора for. Цикл - пробегал все значения яркости "за раз", и для достижения требуемой плавности в него вводится задержка. Небольшая на каждый шаг, но вполне ощутимая в "итого": 2.56сек на каждую лампу.
Введя структуру, теперь можно помнить состояние ШИМ (яркости) между вызовами функции, и тем самым оставить функции задачу однократного изменения яркости, а "цикл плавного изменения" вынести наружу, и связать его с вызовами loop(): на каждый вызов loop() делаем ровно один шаг изменения яроксти (если это требуется).
Строки 17-20 как раз и делают такой шаг 1 изменения ШИМ:
17 - вычисляем место хранения структуры для заданной лампы lamp. Это чтобы далее в коде не указывать "fades[lamp]." Можно и не вводить, в таком простом коде компилятор и сам это сделает за вас ..
19 - поле структуры "яркость" изменяем на величину заданного шага: +1 - яркость увеличивается; -1 - уменьшается.
20 - собственно изменяем яркость на том пине, номер которого указан в поле pin структуры, на то значение, которое только что изменили.
Просто записи fades[lamp].pin и ptr->pin эквиваленты. Если мы обращаемся к полю структуры через указатель, то используем ->, а если по имени элемента/объекта, то "." .. не думаю что это непонятно: описание языка и только.
строка 17 читается так: заведи временную (регистровую) переменную, которая есть адрес структуры типа Led И запиши в него АДРЕС начала элемента массива с номером lamp.
То есть тип данных указателя указан ЯВНО: Led * == "указатель на Led" (а не куда попало). Куда попало будет "void *" :)
Компилятор в любом случае, формирует такой регистровый указатель. Он по иному ваще не умеет адресовать ячейки массивов в памяти .. (вычисляемые места хранения). Просто, если везде указывать fades[lamp].XXX то момент формирования указателя будет выбран компилятором .. и возможно что не единожды, а перед КАЖДЫМ оператором, что может оказаться дорого (в среднем 10-14байт кода на каждый раз).
А так, мы ему явно сообщаем, что заведи 1 раз и пользуйся. И "будь добр" не потерять его, он тебе пригодится более чем в одном операторе.. :)
конечно жеж. 16Мгц - это ну очень быстро. Поэтому вызывать такую функцию внутри loop() надо "не часто", а согласно той самой задержке в 10 миллисекунд. Давайте её и заведем:
typedef struct{
uint8_t pin;
uint8_t bright;
} Led;
#define MAX_LED 3
Led fades[MAX_LED] = {{fadePin1, 0}, {fadePin2, 0}, {fadePin3, 0}};
// Функция изменения яркости на 1 шаг state за 1 вызов
// @param state: +1 -- включение; -1 -- выключение
void fadeLED(uint8_t lamp, uint8_t state)
{
Led * ptr = &(fades[lamp]); // врем. лок. указатель на структуру "лампа"
ptr->bright += state; // изменяем яркость
analogWrite( ptr->pin, ptr->bright); // устанавливаем на выходе
Serial.println(i); // передаем данные в порт для отладки
}
#define WAIT_1 10 // 10мсек - пауза между изменениями яркости
uint32_t startedAt = 0; // тут будем хранить время последнего запуска
void loop()
{
// при каждом входе в loop() проверяем датчики и можем делать что-то ещё
if( digitalRead(pirPin1)==1 ){ ...}
...
// а тут изменяем яркость лампы с номером lamp:
if(
millis() - startedAt >= WAIT_1 // пора изменять яркость?
&& (
(state==1 && fades[lamp].bright < 255) // включаем И есть куда?
||
(state==-1 && fades[lamp].bright > 0) // или вЫключаем И есть куда?
)
){
startedAt = millis(); // новое время для отсчета следующей паузы
fadeLed(lamp, state); // изменяем яркость на state.
}
}
Сложный набор условий расписал так, чтобы было понятней.
Собственно первое условие в этом IF определяется интервалом WAIT_1. И пока не пройдет времени больше чем в нем указано, дальнейшие проверки выполняться НЕ будут. Он установлен в 10мсек. соответственно, яркость ламп изменяется не быстрее чем раз в 10 миллисекунд. Что и требовалось. Или нет? :)
Нет. Просто решается уже несколько иная задача: создание полноценного скетча по поставленному ТЗ. Как понимаю, автор собирается это реализовать на своей улице. Надо чтобы оно не только "как-то" работало, а хорошо работало И было понятно автору. Постепенно.. осваиваем приемы программирования.
Аналогичным способом мы можем избавится и от тупления в 5 секунд в виде паузы между включением и выключением ламп. Вам для этого понадобится завести ещё одну переменную, подобную startedAt и хранить в ней начало паузы с длительностью 5 сек.
Можно попробовать объединить в один скетч то что получается:
#define fadePin1 10 //пин управления MOSFET транзистором
#define fadePin2 9 //пин управления MOSFET транзистором
#define fadePin3 6 //пин управления MOSFET транзистором
int calibrationTime = 30; //Время калибровки датчика (10-60 сек. по даташиту)
int pirPin1 = 2; //пин подключения управляющего сигнала PIR датчика1
int pirPin2 = 4; //пин подключения управляющего сигнала PIR датчика2
int light; //переменная для хранения состояния света (вкл/выкл)
void setup()
{
Serial.begin(9600);
pinMode(pirPin1, INPUT); //настариваем 2 пин как вход для сигналов с датчика
pinMode(pirPin2, INPUT); //настариваем 4 пин как вход для сигналов с датчика
pinMode(fadePin1, OUTPUT); // пины на выход, для управления транзисотором
pinMode(fadePin2, OUTPUT);
pinMode(fadePin3, OUTPUT);
light = 0; //устанаваливаем переменную для первого включения света
Serial.print("Calibrating"); //дадим датчику время на калибровку
for(int i = 0; i < calibrationTime; i++)
{
Serial.print(".");
delay(1000);
}
Serial.println(" done");
Serial.println("SENSOR ACTIVE");
delay(50);
}
// структура - описание понятия "Лампа" - это пин и текущая яркость:
typedef struct{
uint8_t pin;
uint8_t bright;
} Led;
// теперь создадим список ламп, заодно и введем константу "всего ламп"
#define MAX_LED 3
Led fades[MAX_LED] = {{fadePin1, 0}, {fadePin2, 0}, {fadePin3, 0}};
#define WAIT_1 10 // 10мсек - пауза между изменениями яркости
uint32_t startedAt = 0; // тут будем хранить время последнего запуска fadeLed
#define WAIT_PAUSE = 5000
uint32_t startedPause = 0; // тут будем вести отсчет паузы до выключения
int8_t isGo1 = 0; // режим включения/выключения от левого датчика
uint8_t lamp1 = 0; // номер текущей лампы управления левым датчиком
uint8_t max1 = 0; // последний номер режимов левого датчика
int8_t isGo2 = 0; // режим включения/выключения от правого датчика
uint8_t lamp2 = 0; // номер текущей лампы управления правого датчиком
uint8_t max2 = 0; // последний номер режимов правого датчика
/**
* Функция изменения яркости на 1 шаг state РАЗ в 10 миллисекунд
* @param state: +1 -- включение; -1 -- выключение
*/
void fadeLED(uint8_t lamp, uint8_t state)
{
Led * ptr = &(fades[lamp]); // врем. лок. указатель на структуру "лампа"
if(
millis() - startedAt >= WAIT_1 // пора изменять яркость?
&& (
(state==1 && ptr->bright < 255) // включаем И есть куда?
||
(state==-1 && ptr->bright > 0) // или вЫключаем И есть куда?
)
){
startedAt = millis(); // новое время для отсчета следующей паузы
ptr->bright += state; // изменяем яркость
analogWrite( ptr->pin, ptr->bright); // устанавливаем на выходе
Serial.println(i); // передаем данные в порт для отладки
}
}
//========== loop() ========
void loop()
{
// п.1. Если светло - выходим.
if( isDayNow() ) return;
// п.2: проверяем датчики движения и запускаем включение если надо:
if( digitalRead(pirPin1) ){ isGo1 = 1; lamp1=0; max1 = MAX_LED; }
if( digitalRead(pirPin2) ){ isGo2 = 1; lamp1=MAX_LED-1; max1 = -1; }
// п.3. поштучно включаем (п.5 - выключаем) лампы, если левый датчик сработал (2-й датчик точно также можно ниже .. не показан):
if(
(isGo1 == 1) // если включаем лампы, то сразу
|| (
(isGo1 == -1) // а вот если вЫключаем, то
&& (millis() - startedPause >= WAIT_PAUSE) // по истечению паузы
)
){
// 3.1. проверяем лампа уже горит? переходим к следующей:
if( isGo1==1 && fades[lamp1].bright == 255 ){ lamp1++; }
// 5.1. или лампа уже выключена? переходим к следующей:
else if( isGo == -1 && fades[lamp1].bright == 0 ){ lamp--; }
else{
// 3.2. + 5.2 увеличиваем/уменьшаем яркость на 1 каждые 10мсек:
fadeLED(lamp1, isGo);
}
// 3.3., 5.3 проверяем лампы кончились? включаем паузу:
if( lamp1 == max1 ){
if( isGo1 == 1){ // а мы лампы включали?
startedPause = millis(); // .. время паузы "пошло".
isGo1 = -1; // .. после паузы будем выключать
lamp1=MAX_LED-1; // .. в обратном порядке
max = -1;
}else if( isGo == -1 ){ // нет, мы их выключали
isGo = 0; // .. ну и ладушки, всё погасло.
}
}
}
}
Я вам даже прописал примерные номера икон согласно вашей ДРАКОН-схеме, если вы её конечно же отрисовали .. заметьте что п.3 и п.5 - включение и выключение отличаются "деталями". Поэтому я их объединил насколько это возможно.
Точно также можно объединить и работу двух дачтиков, поскольку она практически "идентична". А можно отдублировать ниже код для второго датчика п3-п.5.
функция isDayNow() тут отсутствует. Это проверка датчика освещенности. Для отладки пока нет самого датчика можно сделать заглушку типа uint8_t isDayNow(void){ return 0; }
Ну, как обычно. При слиянии скетчей без проверочной компиляции остается много огрехов переименований и пр. ерунды. Будем считать, что найти все очепятки было "домашним заданием". :)
Вот вроде бы компилирующийся вариант:
#define fadePin1 10 // пин управления MOSFET транзистором
#define fadePin2 9 // пин управления MOSFET транзистором
#define fadePin3 6 // пин управления MOSFET транзистором
#define pirPin1 2 // пин подключения управляющего сигнала PIR датчика1
#define pirPin2 4 // пин подключения управляющего сигнала PIR датчика2
int calibrationTime = 30; // Время калибровки датчика (10-60 сек. по даташиту)
void setup()
{
pinMode(pirPin1, INPUT); // настариваем 2 пин как вход для сигналов с датчика
pinMode(pirPin2, INPUT); // настариваем 4 пин как вход для сигналов с датчика
pinMode(fadePin1, OUTPUT); // пины на выход, для управления транзисотором
pinMode(fadePin2, OUTPUT);
pinMode(fadePin3, OUTPUT);
Serial.begin(9600);
Serial.print("Calibrating"); //дадим датчику время на калибровку
for(int i = 0; i < calibrationTime; i++)
{
Serial.print(".");
delay(1000);
}
Serial.println(" done");
Serial.println("SENSOR ACTIVE");
delay(50);
}
// структура - описание понятия "Лампа" - это пин и текущая яркость:
typedef struct{
uint8_t pin;
uint8_t bright;
} Led;
// теперь создадим список ламп, заодно и введем константу "всего ламп"
#define MAX_LED 3
Led fades[MAX_LED] = {{fadePin1, 0}, {fadePin2, 0}, {fadePin3, 0}};
#define WAIT_1 10 // 10мсек - пауза между изменениями яркости
uint32_t startedAt = 0; // тут будем хранить время последнего запуска fadeLed
#define WAIT_PAUSE 5000
uint32_t startedPause = 0; // тут будем вести отсчет паузы до выключения
int8_t isGo1 = 0; // режим включения/выключения от левого датчика
uint8_t lamp1 = 0; // номер текущей лампы управления левым датчиком
uint8_t max1 = 0; // последний номер режимов левого датчика
int8_t isGo2 = 0; // режим включения/выключения от правого датчика
uint8_t lamp2 = 0; // номер текущей лампы управления правого датчиком
uint8_t max2 = 0; // последний номер режимов правого датчика
uint8_t isDayNow(void){ return 0; }
/**
* Функция изменения яркости на 1 шаг state РАЗ в 10 миллисекунд
* @param state: +1 -- включение; -1 -- выключение
*/
void fadeLED(uint8_t lamp, uint8_t state)
{
Led * ptr = &(fades[lamp]); // врем. лок. указатель на структуру "лампа"
if(
millis() - startedAt >= WAIT_1 // пора изменять яркость?
&& (
(state==1 && ptr->bright < 255) // включаем И есть куда?
||
(state==-1 && ptr->bright > 0) // или вЫключаем И есть куда?
)
){
startedAt = millis(); // новое время для отсчета следующей паузы
ptr->bright += state; // изменяем яркость
analogWrite( ptr->pin, ptr->bright); // устанавливаем на выходе
Serial.println(ptr->bright); // передаем данные в порт для отладки
}
}
//========== loop() ========
void loop()
{
// п.1. Если светло - выходим.
if( isDayNow() ) return;
// п.2: проверяем датчики движения и запускаем включение если надо:
if( digitalRead(pirPin1) ){ isGo1 = 1; lamp1=0; max1 = MAX_LED; }
if( digitalRead(pirPin2) ){ isGo2 = 1; lamp1=MAX_LED-1; max1 = -1; }
// п.3. поштучно включаем (п.5 - выключаем) лампы, если левый датчик сработал (2-й датчик точно также можно ниже .. не показан):
if(
(isGo1 == 1) // если включаем лампы, то сразу
|| (
(isGo1 == -1) // а вот если вЫключаем, то
&& (millis() - startedPause >= WAIT_PAUSE) // по истечению паузы
)
){
// 3.1. проверяем лампа уже горит? переходим к следующей:
if( isGo1==1 && fades[lamp1].bright == 255 ){ lamp1++; }
// 5.1. или лампа уже выключена? переходим к следующей:
else if( isGo1 == -1 && fades[lamp1].bright == 0 ){ lamp1--; }
else{
// 3.2. + 5.2 увеличиваем/уменьшаем яркость на 1 каждые 10мсек:
fadeLED(lamp1, isGo1);
}
// 3.3., 5.3 проверяем лампы кончились? включаем паузу:
if( lamp1 == max1 ){
if( isGo1 == 1){ // а мы лампы включали?
startedPause = millis(); // .. время паузы "пошло".
isGo1 = -1; // .. после паузы будем выключать
lamp1=MAX_LED-1; // .. в обратном порядке
max1 = -1;
}else if( isGo1 == -1 ){ // нет, мы их выключали
isGo1 = 0; // .. ну и ладушки, всё погасло.
}
}
}
}
Но, опять же: вопрос проверки работоспособности и отладки - на ваших плечах. Заодно и разберетесь. :)
Ещё есть смысл обозначить значение в кностанте WAIT_PAUSE и WAIT_1 как 10UL и 5000UL соответственно. Чиселки должны быть длинные и беззнаковые. Эти проверки периодов времени работают нормально только на беззнаковой арифметике. Точнее работают в любом случае, но переполнение нормально переживают только в беззнаковом виде... поэтому и результат millis() и переменная и .. константа должны быть объявлены исключительно как беззнаковые. :)
В мониторе отключаете галочку "скроллировать" или как она там .. возвращаетесь в начало странички и копипастите сюда солидный кусос под видом кода программы. :)
Спасибо!
малость понятно, но еще не совсем
это зачем, куда и почему?
Я уж привык к своему коду, и смотрю на ваш, как баран на те ворота)))))
ну вот, пришел Logik и все опошлил битовыми операциями. Теперь точно никто не сможет разобраться в скетче :)
Не, ну первые 8 строк я понял)))))
Спасибо!
малость понятно, но еще не совсем
это зачем, куда и почему?
Я уж привык к своему коду, и смотрю на ваш, как баран на те ворота)))))
Это прямой вывод в порт, чтоб не выводить каждый битик, выводим сразу байтом.
работает, как и хотел! Спасибо! Вот только одно НО, после выключения, какая то задержка что ли, датчики не реагируют вообще секунд 5-7( почему?
Вернемся чуть назад! все-таки дело видимо в скетче, не датчики тут виноваты! Заливаю верхний, отклик после 5-7 сек, заливаю этот
Все работает сразу, ни какой задержки! Почему так происходит?
Задержка должна отрабатывать в обоих случаях. Она там у вас прописана в delay(5000) - 5сек "как с куста". Пока этот оператор выполняется - МК просто тупит и ни на что не реагирует. Мы с этим ещё разберемся чуть позже.
Кстати, Logik вам предложил несколько иной алгоритм и в нем есть свои особенности, смотрите откомментирую:
Отличия:
1. Скетч работает с цифровым выводом, а не ШИМ. То есть яркость ламп устанавливается мгновенно, а не плавно.
2. Скетч работает с байтом, предполагая поключение реле ламп строго по месту: левый (старший бит) байта порта управления должен соответствовать крайней левой лампе и тд. Младший бит - крайней правой. Не у всех Ардуин и далеко не все пины расположены "подряд" от одного регистра-порта вывода .. смотреть даташит на вашу дуньку и её распиновку тщательно. Впрочем, у вас похоже УНО - там кажется такие есть.
3. Бит, у которого стоит 0 - лампа выключена. И такой - один на каждый датчик. То есть, как только датчик сработал, программа будет "крутить" - выключать по одной лампе "по кругу", оставляя остальные включенными постоянно. Если сработал второй датчик, то ещё одна лампа будет бегать в обратном направлении.
4. Заданная пауза - это "скорость" переключения ламп - скорость кругового бега.
5. Однажды сработавший датчик запустит процесс, а код его остановки тут отсутствует "напрочь". То есть выключать лампы тут - банально нечем.
6. Если процесс мигания вкруговую запущен, то датчик освещенности - уже не играет роли и НЕ проверяется вовсе, ибо стоит в блоке else.
Как интересное решение - полезно. Может вам понравится. Заодно освоите один простой момент: все пины соттветствуют какому-то регистру вывода и собраны побайтно .. но разбросаны по ногам платы .. :)
Но, мне кажется что ваше ТЗ было вовсе иным.
Да, ещё немаловажный момент: скетч рассчитан из предположения ровно 8 ламп. Если ламп меньше (у вас кажется 6), то будет пауза на время переключения несуществующих ламп, в течение которой все имеющиеся лампы будут гореть одновременно.
В общем, описали верно.
Если нужно гасить все - обнуляем LeftLED и RigthLED.
Если меньше 8 ламп правим условия
if
(LeftLED==0xff)
наif
(LeftLED==0xfс) (
для 6-и) и аналогично RigthLED для.ШИМ и произвольный порядок пинов - развиваем OutLED в функцию делающую нужное.
Кстати можно исключить горение нескольких ламп, т.е. горит одна сопровождающая движущегося, тогда из LeftLED=(LeftLED>>1) | (LeftLED & 0x80); делаем LeftLED=(LeftLED>>1) условия
if
(LeftLED==0xff)
наif
(LeftLED==0x04) (
для 6-и) . И аналогично RigthLED.Аналогично делается горение нескольких (2-3 ламп) сопровождающих движущегося.
В общем есть простор для творчества.
ну вот, пришел Logik и все опошлил битовыми операциями. Теперь точно никто не сможет разобраться в скетче :)
Зря Вы так :) вообще битовые операции самое то, я даже банальное сложение чисел иногда на них делаю :) сейчас то-же увлекся переписанием своего протокола под битовые операции, тем самым сократил заголовок хедера на 3 байта (с 11 до 8), то есть скорость малых пакетов возрасла почти на 20%....
То же самое и с внутренним массивом, нужно мне запомнить состояния 255 устройств, с битовыми операциями я обошелся в 32 байта ОЗУ
Битовые операции - самые быстрые (большенство занимает 1 такт, правда это не не включая пересылку в регистр). Единственное чего я в себе не как не могу побороть - это излишества скобок при работе с ними, да я знаю про приоритеты выполнения, но все равно ставлю скобки...
Logik, перечитайте то ТЗ, которое составлено и утверждено вместе с автором темы. Оно - иное, и цель там другая. Ваш код безусловно интересен как знакомство с битовыми операциями и неблокирующей работой интервальных автоматов .. но тут задача пока несколько иная. И первая часть задачи - построить верный алгоритм, а не код программы.
Кодировать программу по готовому и правильному алгоритму, можно даже в сильно нетрезвом виде и даже вовсе мало что смысля в конкретном языке, тупо смотря похожие примеры и выдирая из них готовые куски. А вот составить алгоритм, в точности соответствующий задаче - это и есть ПРОГРАММИРОВАНИЕ. И конкретный язык или реализация - тут вовсе не при чем.
Пока автор осваивает эту часть - алгоритмирование. :)
Задержка должна отрабатывать в обоих случаях. Она там у вас прописана в delay(5000) - 5сек "как с куста". Пока этот оператор выполняется - МК просто тупит и ни на что не реагирует. Мы с этим ещё разберемся чуть позже.
Так она же прописана между включить и выключить, а не перед (движение-включить) а она то как раз там и появляется(
Как-то странно. Задержка в ОБОИХ скетчах прописана в одном и том же месте: сразу после включения ламп. В этой части между ними никакой разницы нет. То есть должно быть так: сработал датчик - пошли загораться лампы, зажглись - ждем 5 сек и после этого лампы выключаются. Никакой разницы.
Разница проявляется только в порядке обработки датчиков: в первом скетче они опрашиваются практически одновременно и сразу запускается процесс включения-задержки, а после задержки, при следующем вхождении в loop(), начинается процесс выключения ламп БЕЗ опроса датчиков, но и без задержек. Но пока идет выключение - тоже ничего не опрашивается.
, а во втором сначала опрашивается один .. и если ОН сработал, то начинается его процесс включения - задержки, и только потом опрашивается второй датчик и если он сработал - то начинается его процесс включения-задержки. А процесс выключения начинается ТОЛЬКО в случае НЕсрабатывания второго датчика (его блок else!).
Время на включение/выключение: 10мсек * 256 * 3 = 2.56 * 3 = 7.5секунд. Это то время, в течение которого оба варианта тупят и тоже ничего не делают как и при delay(5сек). Возможно что Вы обнаруживаете эту задержку.
вот, чтобы такого не было я и предлагаю вам вернуться к построению алгоритма "без задержек". :)
Спасибо!
малость понятно, но еще не совсем
это зачем, куда и почему?
Я уж привык к своему коду, и смотрю на ваш, как баран на те ворота)))))
Это прямой вывод в порт, чтоб не выводить каждый битик, выводим сразу байтом.
Порт - это пины ардуино по порядку, так как они есть(D2,D3,D4 и т.д) я правилно понял?
Я так думаю эта задержка связана как раз с опросом двух датчиков по отдельности! Какое то время все таки нужно для вычислений этого!?
Практически нет. Опрос датчиков визуально проходит практически мгновенно: просто МК смотрит есть на входе лог. 1 или там лог.0 и всё. Времени на этот процесс затрачивается в несколько микросекунд.
Порт - это внутренний регистр ввода-вывода (смотря какой), где собрано 8 ножек. К нумерации ножек платы имеет практически никакое соотношение, что и представляет собой определенное неудобство в програмировании. Как было удобно - так и развели.
Ну так понятнее)
Пока автор осваивает эту часть - алгоритмирование. :)
Вот тут прям у точку!:)
Давайте вернемся к нашему алгоритму. Предлагаю начать с включения-выключения ламп "по 1шт за 1 вызов loop()"
У вас есть функция fadeLed() которая делает процесс плавным, но при этом несколько "тупит". Давайте поправим её так, чтобы в этом процессе устранить delay(), хоть он и краток (но его много раз дергаем)..
вопрос: Как теперь вызывать "это чудо"? :)
Ваши предложения?
Arhat109-2, прокомментируйте строки 17-20, для нас это джунгли :) Зачем нужно было водить структуру?
Arhat109-2, прокомментируйте строки 17-20, для нас это джунгли :) Зачем нужно было водить структуру?
я строчку 17 понимаю как некий трюк позволяющий обращатся к одним данным используя разные типы без приведения типов, что-то похожее на record в паскале, хотя может я и не прав совсем :)
Структура типа Led описывает каждую лампу как явление из 2-х сущностей: номера управляющего пина и текущего уровня яркости свечения (ШИМ). Это необходимо, чтобы отвязать код, управляющий яркостью от "цикла включения" в виде оператора for. Цикл - пробегал все значения яркости "за раз", и для достижения требуемой плавности в него вводится задержка. Небольшая на каждый шаг, но вполне ощутимая в "итого": 2.56сек на каждую лампу.
Введя структуру, теперь можно помнить состояние ШИМ (яркости) между вызовами функции, и тем самым оставить функции задачу однократного изменения яркости, а "цикл плавного изменения" вынести наружу, и связать его с вызовами loop(): на каждый вызов loop() делаем ровно один шаг изменения яроксти (если это требуется).
Строки 17-20 как раз и делают такой шаг 1 изменения ШИМ:
17 - вычисляем место хранения структуры для заданной лампы lamp. Это чтобы далее в коде не указывать "fades[lamp]." Можно и не вводить, в таком простом коде компилятор и сам это сделает за вас ..
19 - поле структуры "яркость" изменяем на величину заданного шага: +1 - яркость увеличивается; -1 - уменьшается.
20 - собственно изменяем яркость на том пине, номер которого указан в поле pin структуры, на то значение, которое только что изменили.
Просто записи fades[lamp].pin и ptr->pin эквиваленты. Если мы обращаемся к полю структуры через указатель, то используем ->, а если по имени элемента/объекта, то "." .. не думаю что это непонятно: описание языка и только.
строка 17 читается так: заведи временную (регистровую) переменную, которая есть адрес структуры типа Led И запиши в него АДРЕС начала элемента массива с номером lamp.
То есть тип данных указателя указан ЯВНО: Led * == "указатель на Led" (а не куда попало). Куда попало будет "void *" :)
Компилятор в любом случае, формирует такой регистровый указатель. Он по иному ваще не умеет адресовать ячейки массивов в памяти .. (вычисляемые места хранения). Просто, если везде указывать fades[lamp].XXX то момент формирования указателя будет выбран компилятором .. и возможно что не единожды, а перед КАЖДЫМ оператором, что может оказаться дорого (в среднем 10-14байт кода на каждый раз).
А так, мы ему явно сообщаем, что заведи 1 раз и пользуйся. И "будь добр" не потерять его, он тебе пригодится более чем в одном операторе.. :)
Продолжаем? Так как можно вызвать теперь эту функцию из loop() чтобы лампа включалась-выключалась по 1 шагу на вызов? :)
Хорошо. Если вызывать так, что будет?
Сколько раз надо зайти в loop() чтобы увеличить яркость с 0 до 255? :)
А если предварительно напишем так:
то будет ли опрашиваться датчик "одновременно" с изменением яркости? Не это ли нам и требуется? :)
Arhat109-2, delay() выкинул, добавляй millis() иначе глазом не успеешь моргнуть, как яркость увеличится с нуля до максимума.
Оёёй, пока пытаюсь вникнуть!)))
конечно жеж. 16Мгц - это ну очень быстро. Поэтому вызывать такую функцию внутри loop() надо "не часто", а согласно той самой задержке в 10 миллисекунд. Давайте её и заведем:
Сложный набор условий расписал так, чтобы было понятней.
Собственно первое условие в этом IF определяется интервалом WAIT_1. И пока не пройдет времени больше чем в нем указано, дальнейшие проверки выполняться НЕ будут. Он установлен в 10мсек. соответственно, яркость ламп изменяется не быстрее чем раз в 10 миллисекунд. Что и требовалось. Или нет? :)
мозголомы...
мозголомы...
Ну что Вы, как раз сообразно названию темы всё ))).
Нет. Просто решается уже несколько иная задача: создание полноценного скетча по поставленному ТЗ. Как понимаю, автор собирается это реализовать на своей улице. Надо чтобы оно не только "как-то" работало, а хорошо работало И было понятно автору. Постепенно.. осваиваем приемы программирования.
Продолжаем.
Аналогичным способом мы можем избавится и от тупления в 5 секунд в виде паузы между включением и выключением ламп. Вам для этого понадобится завести ещё одну переменную, подобную startedAt и хранить в ней начало паузы с длительностью 5 сек.
Можно попробовать объединить в один скетч то что получается:
Я вам даже прописал примерные номера икон согласно вашей ДРАКОН-схеме, если вы её конечно же отрисовали .. заметьте что п.3 и п.5 - включение и выключение отличаются "деталями". Поэтому я их объединил насколько это возможно.
Точно также можно объединить и работу двух дачтиков, поскольку она практически "идентична". А можно отдублировать ниже код для второго датчика п3-п.5.
функция isDayNow() тут отсутствует. Это проверка датчика освещенности. Для отладки пока нет самого датчика можно сделать заглушку типа uint8_t isDayNow(void){ return 0; }
Постепенно.. осваиваем приемы программирования.
Ща такое ощущение, что я в (-)9 классе, как минимум :(
а вот тут рагается, есть еще кое где, но то вроде устранил?!
в строке 46 знак = не нужен.
Ну, как обычно. При слиянии скетчей без проверочной компиляции остается много огрехов переименований и пр. ерунды. Будем считать, что найти все очепятки было "домашним заданием". :)
Вот вроде бы компилирующийся вариант:
Но, опять же: вопрос проверки работоспособности и отладки - на ваших плечах. Заодно и разберетесь. :)
Будем считать, что найти все очепятки было "домашним заданием". :)
Но, опять же: вопрос проверки работоспособности и отладки - на ваших плечах. Заодно и разберетесь. :)
Ну с ним я справился)), нашел косяки) Во я какой,а)))))
а вот второе, большой ?????)
1. калибровка датчика - норма
2. датчик сработал(движение) - включаем лампы(норма)
3. а вот тут начинается весело; Зажигаем плавно №6, №10, №9 (должно же быть по другому 10,9,6 или 6,9,10)
4. и НЕ тухнем, вообще
5. следующее движение - лампы горять, а мы еще чего то там зажигаем (2 раза шим, все 3 лампы горят в этот момент)
6. и зачем то опять проходим калибровку
7. еще сработка датчика - резко тушим и сразу плавно зажигаем в том же порядке(причем другие 2 горят постоянно)
8. опять проходим калибровку
И преход в п.5
Ну, уже успех. :)
Далее, как обычно втыкиваем отладку во все подходящие и ключевые места:
Например так. Заодно растягиваем паузы и смотрим чего происходит. :)
повтор калибровки возможен только в одном случае: перезагрузка МК заново.
ну значит чего то его перезагружает!)
Ещё есть смысл обозначить значение в кностанте WAIT_PAUSE и WAIT_1 как 10UL и 5000UL соответственно. Чиселки должны быть длинные и беззнаковые. Эти проверки периодов времени работают нормально только на беззнаковой арифметике. Точнее работают в любом случае, но переполнение нормально переживают только в беззнаковом виде... поэтому и результат millis() и переменная и .. константа должны быть объявлены исключительно как беззнаковые. :)
Показывайте вывод сериалов .. будем смотреть где ошибка.
ругается в 92 и 142 строчках, но разобрался)
Показывайте вывод сериалов .. будем смотреть где ошибка.
так там их МОРЕ, как их сюда то показать?
В мониторе отключаете галочку "скроллировать" или как она там .. возвращаетесь в начало странички и копипастите сюда солидный кусос под видом кода программы. :)