Умная розетка Redmond SkyPort 100S на МК nRF51822, моя реализация
Добавлено: 25 сен 2019, 07:16
Решил переписать для себя код, предложенный автором статьи , добавил отправку устройством качества связи, индикацию подключения к сети 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