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

Умная розетка Redmond SkyPort 100S на МК nRF51822, моя реализация

Добавлено: 25 сен 2019, 07:16
Mikhail72
Решил переписать для себя код, предложенный автором статьи , добавил отправку устройством качества связи, индикацию подключения к сети MySensors, выбор начального состояния реле, вывод лога в контроллер при настройке устройства, сохранение настроек в еепром. А поводом для написания стало то, что у автора вышеприведённой статьи используется переключение реле при входящем сообщении, а если качество связи низкое, то непонятно в каком состоянии реле после отправки сообщения на устройство, т.к. не всегда приходит подтверждающий ответ.
Скетч
Show

Код: Выделить всё

/*
 * Скетч для розетки Redmond на МК nRF51822
 */

#define BUTTON_PIN 0                      // Пин кнопки
#define RED_LED_PIN 1                     // Пин красного светодиода
#define GREEN_LED_PIN 2                   // Пин зелёного светодиода
#define RELAY_PIN 3                       // Пин реле
#define RELAY_ID 0                        // Сенсор реле
#define TRANSPORT_SIGNAL_REPORT_ID 1      // Сенсор качества связи
#define SETTINGS_ID 2                     // Сенсор настроек

#define MY_REPEATER_FEATURE               // Устройство является репитером
#define MY_TRANSPORT_WAIT_READY_MS 5000   // Если парент не найден, через 5 сек. после включения в сеть запускаем цикл 
#define MY_RADIO_NRF5_ESB                 // Используем радио nrf5
#define MY_RF24_PA_LEVEL (NRF5_PA_MAX)    // На максимальной мощности

#include <MySensors.h>                    // Подключаем библиотеку

bool status_ = true;                      // Статус реле
bool flag = false;                        // Флаг нажания на кнопку
bool led;                                 // Статус светодиода
bool _h_or_m_;                            // Флаг
byte time_send;                           // Время отправки 
unsigned long timer_;                     // Таймер 1
unsigned long timer1_;                    // Таймер 2
unsigned long t_s;                        // Таймер 3
byte number;                              // Индекс массива
char _command[]={'S','s','R','r','P','T','U'}; // Массив для выбора отправляемого параметра качества связи

MyMessage msg_1(RELAY_ID, V_STATUS);      // Контейнеры для отправляемых данных
MyMessage msg_2(TRANSPORT_SIGNAL_REPORT_ID, V_VAR1);
MyMessage msg_3(SETTINGS_ID, V_VAR1);

void preHwInit() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);      // Инициализация пинов
  pinMode(RED_LED_PIN, OUTPUT);
  pinMode(GREEN_LED_PIN, OUTPUT);
  pinMode(RELAY_PIN, OUTPUT);
  status_ = loadState(1);                 // Загрузка занных из еепром
  number = loadState(0);
  time_send = loadState(2);
  _h_or_m_ = loadState(3);
}

void before()
{
 digitalWrite(RED_LED_PIN, status_);      // Устанавливаем состояние светодиода, в зависимости от настройки
}

void presentation()                       // Презентация сенсоров
{
  sendSketchInfo("REDMOND_nRF51", "1.0");
  wait(50);
  present(RELAY_ID, S_BINARY, "RELAY");
  wait(50);
  present(TRANSPORT_SIGNAL_REPORT_ID, S_CUSTOM, "RSSI");
  wait(50);
  present(SETTINGS_ID, S_CUSTOM, "Settings");
}

void setup()
{
attachInterrupt(BUTTON_PIN, _int, RISING); // Включаем прерывание при отпускании кнопки
timer_ = millis();                         // Сохраняем время
timer1_ = millis();
digitalWrite(RELAY_PIN, status_);          // Устанавливаем состояние реле, в зависимости от настройки
send(msg_1.set(status_));                  // Отправляем статус реле на контроллер
wait(20);
send(msg_2.setType(V_VAR1).set((String(transportSignalReport(_command[number]), DEC)).c_str()));
}                                          // Отправляем данные о качестве связи

void loop()
{             // В зависимости от флага время, отправка качества связи измеряется в часах или минутах
if(_h_or_m_) t_s = time_send * 3600000; else t_s = time_send * 60000;
if(millis() - timer_ >= t_s) { //Если установленное время отправки совпало
  timer_ = millis();           //Сбрасываем таймер и отправляем сообщение о качестве связи
  send(msg_2.setType(V_VAR1).set((String(transportSignalReport(_command[number]), DEC)).c_str()));
}
if(flag){                                  // Если была нажата кнопка
  delay(100);                              // Ждём
  flag = false;                            // Снимаем флаг нажатия
  status_ = !status_;                      // Инвертируем статус
  digitalWrite(RELAY_PIN, status_);        // Переключаем реле
  digitalWrite(RED_LED_PIN, status_);      // И светодиод
  send(msg_1.set(status_));                // Отправляем статус реле и качество связи
  send(msg_2.set((String(transportSignalReport(_command[number]), DEC)).c_str()));
  attachInterrupt(BUTTON_PIN, _int, RISING);// Включаем прерывание 
}
if (_transportConfig.parentNodeId != AUTO){ // Если парент найден
   if((millis() - timer1_ >= 20000)){       // То раз в 20 сек.
     digitalWrite(GREEN_LED_PIN, HIGH);     // 
     wait(30);                              // Моргаем светодиодом
     digitalWrite(GREEN_LED_PIN, LOW);      
     timer1_ = millis();                    // Сбрасываем таймер
     }
   } else {                                 // Если парент не найден
       if((millis() - timer1_ >= 200)){     // Мигаем светодиодом
         led = !led;                        // Сигнализируя о поиске
         digitalWrite(GREEN_LED_PIN, led);
         timer1_ = millis();
         }
     
      }
}
void receive(const MyMessage & message) {  //Обработчик входящих сообщений

/*
 * Отправкой сообщения на сенсор TRANSPORT_SIGNAL_REPORT_ID типом V_VAR1 числа от 0 до 6 
 * выбирается какие данные о качестве связи будет отправлять модуль переодически и при 
 * переключении реле. В подтверждение выбора вернётся текстовая строка, описывающая выбор.
 */

if ((message.sensor == TRANSPORT_SIGNAL_REPORT_ID)&&(message.type ==  V_VAR1)){ 
  saveState(0, message.getByte());
  number =  message.getByte();
  
  switch (number) {
  case 0:
    send(msg_2.setType(V_TEXT).set(String("SNR of incoming message").c_str()));
    break;
  case 1:
   send(msg_2.setType(V_TEXT).set(String("SNR of incoming ACK message").c_str()));
    break;
  case 2:
   send(msg_2.setType(V_TEXT).set(String("RSSI of incoming message").c_str())); 
    break;
  case 3:
    send(msg_2.setType(V_TEXT).set(String("RSSI of incoming ACK message").c_str())); 
    break;
  case 4:
    send(msg_2.setType(V_TEXT).set(String("TX powerlevel in %").c_str())); 
    break;
  case 5:
    send(msg_2.setType(V_TEXT).set(String("TX powerlevel in dBm").c_str())); 
    break;
  case 6:
    send(msg_2.setType(V_TEXT).set(String("Uplink quality").c_str())); 
    break;
  default:
    send(msg_2.setType(V_TEXT).set(String("Send from 0 to 6").c_str())); 
    break;
  }
  wait(20);
  // Отправка данных о качестве связи
  send(msg_2.setType(V_VAR1).set((String(transportSignalReport(_command[number]), DEC)).c_str()));
}

/*
 * Для настройки начального статуса реле при включении устройства в сеть, отправляем сообщение
 * содержащее 1 или 0 на сенсор SETTINGS_ID типа V_VAR1, в подтверждение получаем текстовое сообщение
 * при установке в розетку устройство включается или остается выключенным.
 */
 
if ((message.sensor == SETTINGS_ID)&&(message.type ==  V_VAR1)){
  saveState(1, message.getBool());
  wait(100);
  if(message.getBool()){
  send(msg_3.setType(V_VAR1).set(String("When connected, ON").c_str()));
  } else send(msg_3.setType(V_VAR1).set(String("When connected, OFF").c_str()));
}

/*
 * Для установки таймера переодической отправки данных, отправляем сообщение на сенсор SETTINGS_ID типа V_VAR2
 * одержащее символы h или m, для выбота единицы измерения таймера в часах или минутах.
 */
 
if ((message.sensor == SETTINGS_ID)&&(message.type ==  V_VAR2)){
  String h_or_m = message.getString();
  if((h_or_m).equalsIgnoreCase(String("h"))){
    _h_or_m_ = true;
    saveState(3, _h_or_m_);
    send(msg_3.setType(V_VAR2).set(String("Time_send_RSSI_in_hours").c_str()));
  } else send(msg_3.setType(V_VAR2).set(String("Send_h_or_m").c_str()));
  if((h_or_m).equalsIgnoreCase(String("m"))){
    _h_or_m_ = false;
    saveState(3, _h_or_m_);
    send(msg_3.setType(V_VAR2).set(String("Time_send_RSSI_in_minutes").c_str()));
  } else send(msg_3.setType(V_VAR2).set(String("Send_h_or_m").c_str()));
    
}

/*
 * Значение таймера задаётся отправкой сообщения на сенсор SETTINGS_ID типа V_VAR3, содержащее число от 1 до 255
 */

if ((message.sensor == SETTINGS_ID)&&(message.type ==  V_VAR3)){
  time_send = message.getByte();
  saveState(2, time_send);
  if(_h_or_m_) send(msg_3.setType(V_VAR3).set(String(String("Time_send_RSSI_")+String(time_send)+String(", h.")).c_str()));
  else send(msg_3.setType(V_VAR3).set(String(String("Time_send_RSSI_")+String(time_send)+String(", m.")).c_str()));
 }


/*
 * Сообщение переключающее статус реле и светодиода индикации.
 */
 
if ((message.sensor == RELAY_ID)&&(message.type ==  V_STATUS)){
  status_ = message.getBool();
  digitalWrite(RELAY_PIN, status_);
  digitalWrite(RED_LED_PIN, status_);
  }
}

void _int(){                 // Обработчик прерывания
detachInterrupt(BUTTON_PIN); // Отключаем прерывание
flag = true;                 // Ставим флаг нажатия
}
Лог
Show
presentation.jpg
presentation.jpg (214.36 КБ) 18095 просмотров

Re: Умная розетка Redmond SkyPort 100S на МК nRF51822, моя реализация

Добавлено: 07 июн 2020, 16:19
Berk
урл той статьи - https://habr.com/ru/post/450860/, ..у топикастера битая ссылка