Умный цоколь REDMOND SkySocket 202S с функцией диммера
Добавлено: 03 окт 2019, 14:39
Также как и в соседней теме решил переписать для себя код, предложенный автором статьи, добавил функцию диммера, отправку устройством качества связи, вывод лога в контроллер при настройке устройства, сохранение настроек в еепром.
Инструкция как настраивать цоколь для работы с диммируемыми лампами (я брал лампу-накаливания):
1. Отправить на сенсор 3, тип V_VAR1 цифру 2
2. Отправить на сенсор 4, тип V_VAR1 цифру 96
3. Отправить на сенсор 2, тип V_VAR1 цифру 1
4. Отправить на сенсор 1, тип V_PERCENTAGE цифру 1
5. Если лампа будет мигать, убавить на единицу цифру из п.2, т.е. отправить 95 и повторить п.4.
6. Либо попробовать добавить единицу к цифре из п.2, т.е. отправить 97 и повторить п.4, если замигает вернутся назад как в п.5.
Отправка на сенсор 0, тип V_STATUS, цифры 1 или 0, включает на полную или выключает лампочку. Нажатие на кнопку проделывает тоже самое, если лампа горела выключает, если не горела - включает на полную.
P.S. Для прошивки выбирать либо внутренний генератор, либо синтезированный
Скетч
Show
Код: Выделить всё
#define MY_REPEATER_FEATURE // Включаем функцию репитера
#define MY_TRANSPORT_WAIT_READY_MS 2000 // Запускаем через 2 сек, если нет сети
#define MY_RADIO_NRF5_ESB
#define MY_RF24_PA_LEVEL (NRF5_PA_MAX)
#define BUTTON_PIN 27 // Обзываем пины
#define BIZZER_PIN 26
#define ZERO_CROSS_PIN 0
#define RELAY_PIN 16
#define RELAY_ID 0 // Сенсоры
#define DIMMER_ID 1
#define TYPE_LAMP_ID 2
#define TRANSPORT_SIGNAL_REPORT_ID 3
#define TIME_TO_ZERO_ID 4
#include <MySensors.h>
MyMessage msg_0(RELAY_ID, V_STATUS); // Контейнеры для сенсоров
MyMessage msg_1(DIMMER_ID, V_PERCENTAGE);
MyMessage msg_2(TYPE_LAMP_ID, V_VAR1);
MyMessage msg_3(TRANSPORT_SIGNAL_REPORT_ID, V_VAR1);
MyMessage msg_4(TIME_TO_ZERO_ID, V_VAR1);
bool _button_ = false; // Декларация флагов и переменных
bool dimmable = false;
bool New_ = false;
byte brightness_ = 1;
bool iswitch = true;
byte time_to_zero = 96; // Время до истинного перехода через ноль (9600 мкСек)
byte number; // Индекс массива
char _command[]={'S','s','R','r','P','T','U'}; // Массив для выбора отправляемого параметра качества связи
void preHwInit() { // Настройка пинов
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BIZZER_PIN, OUTPUT);
pinMode(ZERO_CROSS_PIN, INPUT);
pinMode(RELAY_PIN, OUTPUT);
}
void myTone(uint32_t _pin, uint16_t _frequency, uint16_t _duration) { //Функция пищалки (работает не на всех частотах, т.к. 16bit TIMER2)
bool beep = true;
NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Enabled; // Сбросить таймер2 при совпадении
NRF_TIMER2->CC[0] = 500000/_frequency; // Устанавливаем до скольки считать
NRF_TIMER2->EVENTS_COMPARE[0] = 0; // Сбрасываем событие
NRF_TIMER2->TASKS_START = 1; // Запускаем таймер
for(int i=0; i < (_duration*_frequency/500); i++){ // цикл длительностью _duration, мс
while(NRF_TIMER2->EVENTS_COMPARE[0] == 0); // Ждём событие
NRF_TIMER2->EVENTS_COMPARE[0] = 0; // Сбрасываем событие
digitalWrite(_pin, beep); // Переключаем пин пищалки
beep = !beep; // Инвертируем переменную
}
digitalWrite(_pin, LOW); // Выключаем пин пищалки на всякий
NRF_TIMER2->EVENTS_COMPARE[0] = 0; // Сбрасываем событие на всякий
NRF_TIMER2->TASKS_STOP = 1; // Останавливаем таймер
}
void before() { // Включаем лампу при вкючении устройства в сеть
while (digitalRead(ZERO_CROSS_PIN)!=0); // Ждём импульс, от датчика zero_cross
wait(10); // Ждём перехода через ноль
digitalWrite(RELAY_PIN, iswitch); // Включаем лампу
}
void _button(){ // Обработчик прерывание по кнопке
detachInterrupt(ZERO_CROSS_PIN); // Отключаем прерывание zero_cross
detachInterrupt(BUTTON_PIN); // Отключаем прерывание по кнопке
NRF_TIMER1->TASKS_STOP = 1; // Останавливаем таймер1
iswitch = !iswitch; // Инвертируем переменную
while (digitalRead(ZERO_CROSS_PIN)!=0); // Ждём импульс, от датчика zero_cross
wait(10); // Ждём перехода через ноль
digitalWrite(RELAY_PIN, iswitch); // Переключаем лампу
_button_ = true; // Флаг что было прерывание
}
void setup() {
number = loadState(TRANSPORT_SIGNAL_REPORT_ID); // Загружаем из настройки
dimmable = loadState(TYPE_LAMP_ID); // из EEPROM
time_to_zero = loadState(TIME_TO_ZERO_ID);
NRF_TIMER1->CC[1] = 10000; // Установить период счёта таймера1 10мс
NRF_TIMER1->EVENTS_COMPARE[0] = 0; // Сбросить события таймера1, на всякий
NRF_TIMER1->EVENTS_COMPARE[1] = 0;
myTone(BIZZER_PIN, 500, 150); // Пикаем
send(msg_0.set(iswitch)); // Отправляем статус Лампы
myTone(BIZZER_PIN, 1000, 100); // Пикаем
send(msg_3.setType(V_VAR1).set((String(transportSignalReport(_command[number]), DEC)).c_str()));// Отправяем качество связи
attachInterrupt(BUTTON_PIN, _button, RISING); // Активиуем прерывание на кнопке
}
void loop() {
if(dimmable){ // Если лампа диммируемая
if(New_){ // Если новые данные яркости
if((brightness_ >= 1)&&(brightness_ <= 99)){ // Проверяем в каком интервале желаемый уровень яркости
myTone(BIZZER_PIN, 500, 150); // Пик
myTone(BIZZER_PIN, 1000, 100); // Пик
iswitch = 1; // Устанавливаем статус лампы
send(msg_0.set(iswitch)); // И отправляем его
NRF_TIMER2->SHORTS = (TIMER_SHORTS_COMPARE1_STOP_Enabled << TIMER_SHORTS_COMPARE1_STOP_Pos | // Остановить и
TIMER_SHORTS_COMPARE1_CLEAR_Enabled << TIMER_SHORTS_COMPARE1_CLEAR_Pos); // Сбросить Таймер2 при совпадении
NRF_TIMER2->CC[1] = time_to_zero*100; // Установить период счёта таймера2 9600 мкс
NRF_TIMER2->EVENTS_COMPARE[1] = 0; // Сбросить события таймера2, на всякий
NRF_TIMER1->TASKS_START = 1; // Запускаем таймер1
attachInterrupt(ZERO_CROSS_PIN, zero_cross, FALLING);// Активируем прерывание на пине Zero_Cross
} else if(brightness_ > 99){ // Если яркость ниже 1%
detachInterrupt(ZERO_CROSS_PIN); // Деактивируем прерывание Zero_Cross
NRF_TIMER1->TASKS_STOP = 1; // Останавливаем таймер1
iswitch = 0; // Устанавливаем статус лампы
digitalWrite(RELAY_PIN, iswitch); // Выключаем её
myTone(BIZZER_PIN, 500, 150); // Пик
myTone(BIZZER_PIN, 1000, 100); // Пик
send(msg_0.set(iswitch)); // Отправляем статус лампы
} else if(brightness_ < 1){ // Если яркость больше 99%
detachInterrupt(ZERO_CROSS_PIN); // Отключаем прерывание на Zero_Cross
NRF_TIMER1->TASKS_STOP = 1; // Останавливаем таймер1
iswitch = 1; // Устанавливаем статус лампы
digitalWrite(RELAY_PIN, iswitch); // Включаем ее
myTone(BIZZER_PIN, 500, 150); // Пик
myTone(BIZZER_PIN, 1000, 100); // Пик
send(msg_0.set(iswitch)); // Отправляем статус лампы
}
New_ = 0; // Сбрасываем флаг новых данных яркости
}
if(NRF_TIMER1->EVENTS_COMPARE[0]){ // Если Таймер1 отсчиал задынный период brightness_ * 100
digitalWrite(RELAY_PIN, HIGH); // Включить реле
NRF_TIMER1->EVENTS_COMPARE[0] = 0; // Сбросить флаг события TIMER1 CC[0]
}
if(NRF_TIMER1->EVENTS_COMPARE[1]){ // Если Таймер1 отсчиал задынный период в 10 мс
digitalWrite(RELAY_PIN, LOW); // Выключить реле
NRF_TIMER1->TASKS_CLEAR = 1; // Очищаем таймер1
NRF_TIMER1->EVENTS_COMPARE[1] = 0; // Сбросить флаг события TIMER1 CC[1]
}
if(NRF_TIMER2->EVENTS_COMPARE[1]){ // Если Таймер2 отсчитал задынный период в 9600 мкс
digitalWrite(RELAY_PIN, LOW); // Выключить реле
NRF_TIMER1->TASKS_CLEAR = 1; // Очищаем таймер1
NRF_TIMER2->EVENTS_COMPARE[1] = 0; // Сбросить флаг события TIMER2 CC[1]
}
}
if(_button_){ // Если была нажата кнопка
myTone(BIZZER_PIN, 500, 150); // Пик
send(msg_0.set(iswitch)); // Отправляем статус лампы
myTone(BIZZER_PIN, 1000, 100); // Пик
_button_ = false; // Сбрасываем флаг
attachInterrupt(BUTTON_PIN, _button, RISING);// Активиуем прерывание на кнопке
}
}
void zero_cross(){ // При переходе через ноль
NRF_TIMER2->TASKS_START = 1; // Запускаем таймер2
}
void receive(const MyMessage &message){ // Обработчик входящих сообщений
if((message.sensor == RELAY_ID)&&(message.type == V_STATUS)){ // Если сообщение для лампы
iswitch = message.getBool(); // Записываем в переменную желаемый статус лампы
detachInterrupt(ZERO_CROSS_PIN); // Отключаем прерывание zero_cross
NRF_TIMER1->TASKS_STOP = 1; // Останавливаем таймер1
while (digitalRead(ZERO_CROSS_PIN)!=0); // Ждём импульс, от датчика zero_cross
wait(10); // Ждём перехода через ноль
digitalWrite(RELAY_PIN, iswitch); // Включаем или выключаем лампу
send(msg_0.set(iswitch)); // Отправляем статус
myTone(BIZZER_PIN, 500, 150); // Пик
send(msg_3.setType(V_VAR1).set((String(transportSignalReport(_command[number]), DEC)).c_str())); // Отправляем данные о качестве связи
myTone(BIZZER_PIN, 1000, 100); // Пик
}
if((message.sensor == DIMMER_ID)&&(message.type == V_PERCENTAGE)){ // Если сообщение для установки яркости
brightness_ = map(message.getByte(), 0, 100, 100, 0); // Преобразуем в данные для Таймера1
wait(20); // Ждём
send(msg_1.set(message.getByte())); // Отправляем полученные данные для подтверждения
NRF_TIMER1->CC[0] = brightness_ * 100; // Установить период счёта таймера1
New_ = 1; // Флаг новых данных
}
if((message.sensor == TYPE_LAMP_ID)&&(message.type == V_VAR1)){ // Если сообщение о типе лампы
switch (message.getByte()){ // Проверяем их на правильность
case 0: // Если пришёл 0
dimmable = false; // Устанавливаем флаг что лампа не диммируемая
saveState(TYPE_LAMP_ID, dimmable); // Сохраняем в ЕЕПРОМ
send(msg_2.setType(V_TEXT).set(String("Standard bulb").c_str())); // Отправляем сообщение о выбранном типе
break;
case 1: // Если 1
dimmable = true; // Устанавливаем флаг что лампа диммируемая
saveState(TYPE_LAMP_ID, dimmable); // Сохраняем в ЕЕПРОМ
send(msg_2.setType(V_TEXT).set(String("Dimmable lamp").c_str())); // Отправляем сообщение о выбранном типе
break;
default: // Если сообщение отличное от 0 и 1
wait(20); // Ждём
send(msg_2.setType(V_TEXT).set(String("Send 1 or 0").c_str())); // Отправляем сообщение об ошибке
break;
}
wait(20); // Ждём
// Отправляем данные о качестве связи
send(msg_3.setType(V_VAR1).set((String(transportSignalReport(_command[number]), DEC)).c_str()));
}
if((message.sensor == TRANSPORT_SIGNAL_REPORT_ID)&&(message.type == V_VAR1)){ // Если сообщение для выбора типа данных о качестве связи
saveState(TRANSPORT_SIGNAL_REPORT_ID, message.getByte()); // Сохраняем в ЕЕПРОМ
number = message.getByte(); // Записываем в переменную
switch (number) { // В зависимости от индекса
case 0: //
send(msg_3.setType(V_TEXT).set(String("SNR of incoming message").c_str())); // Отправляем сообщение о типе получаемых данных
break;
case 1:
send(msg_3.setType(V_TEXT).set(String("SNR of incoming ACK message").c_str()));
break;
case 2:
send(msg_3.setType(V_TEXT).set(String("RSSI of incoming message").c_str()));
break;
case 3:
send(msg_3.setType(V_TEXT).set(String("RSSI of incoming ACK message").c_str()));
break;
case 4:
send(msg_3.setType(V_TEXT).set(String("TX powerlevel in %").c_str()));
break;
case 5:
send(msg_3.setType(V_TEXT).set(String("TX powerlevel in dBm").c_str()));
break;
case 6:
send(msg_3.setType(V_TEXT).set(String("Uplink quality").c_str()));
break;
default:
send(msg_3.setType(V_TEXT).set(String("Send from 0 to 6").c_str()));
break;
}
wait(20);
// Отправка данных о качестве связи
send(msg_3.setType(V_VAR1).set((String(transportSignalReport(_command[number]), DEC)).c_str()));
}
if((message.sensor == TIME_TO_ZERO_ID)&&(message.type == V_VAR1)){ // Если данные для настройки истинного перехода через ноль
saveState(TIME_TO_ZERO_ID, message.getByte()); // Сохраняем в ЕЕПРОМ
time_to_zero = message.getByte(); // Записываем в переменную
wait(20); // Ждем и отправляем сообщение для дебага
send(msg_4.setType(V_TEXT).set(String(String("Time_to_zero ")+String(time_to_zero)+String("00uS")).c_str()));
}
}
void presentation() // Презентация сенсоров
{
sendSketchInfo("REDMOND nRF51", "2.0");
wait (20);
present (RELAY_ID, S_BINARY, "On/Off");
wait (20);
present (DIMMER_ID, S_DIMMER, "Brightness");
wait (20);
present (TYPE_LAMP_ID, S_CUSTOM, "Dimmable?");
wait (20);
present (TRANSPORT_SIGNAL_REPORT_ID, S_CUSTOM, "RSSI");
wait (20);
present (TIME_TO_ZERO_ID, S_CUSTOM, "Time to zero");
}
Лог
Show
1. Отправить на сенсор 3, тип V_VAR1 цифру 2
2. Отправить на сенсор 4, тип V_VAR1 цифру 96
3. Отправить на сенсор 2, тип V_VAR1 цифру 1
4. Отправить на сенсор 1, тип V_PERCENTAGE цифру 1
5. Если лампа будет мигать, убавить на единицу цифру из п.2, т.е. отправить 95 и повторить п.4.
6. Либо попробовать добавить единицу к цифре из п.2, т.е. отправить 97 и повторить п.4, если замигает вернутся назад как в п.5.
Отправка на сенсор 0, тип V_STATUS, цифры 1 или 0, включает на полную или выключает лампочку. Нажатие на кнопку проделывает тоже самое, если лампа горела выключает, если не горела - включает на полную.
P.S. Для прошивки выбирать либо внутренний генератор, либо синтезированный