Страница 1 из 1

Умный цоколь REDMOND SkySocket 202S с функцией диммера

Добавлено: 03 окт 2019, 14:39
Mikhail72
Также как и в соседней теме решил переписать для себя код, предложенный автором статьи, добавил функцию диммера, отправку устройством качества связи, вывод лога в контроллер при настройке устройства, сохранение настроек в еепром.
Скетч
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
presentationSkySocket.jpg
presentationSkySocket.jpg (331.37 КБ) 20523 просмотра
Инструкция как настраивать цоколь для работы с диммируемыми лампами (я брал лампу-накаливания):
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. Для прошивки выбирать либо внутренний генератор, либо синтезированный
nRF51.jpg
nRF51.jpg (9.04 КБ) 20508 просмотров

Re: Умный цоколь REDMOND SkySocket 202S с функцией диммера

Добавлено: 28 фев 2021, 15:13
petrosetrov
Можете рассказать подробнее об этой штуке?

Re: Умный цоколь REDMOND SkySocket 202S с функцией диммера

Добавлено: 28 фев 2021, 15:13
petrosetrov
А то я первый раз о чем-то подобном читаю