Приветствую всех читателей блога Mysku!!! В сегодняшней статье хочу поделится с вами новым проектом. На этот раз это сенсорный выключатель со стеклянной панелью. Устройство компактное, размерами 42х42мм(стандартные стеклянные панель имеет размеры 80х80мм). История этого устройства началась давно, около года назад.
Превые варианты были на микроконтролере atmega328, но в итоге все закончилось микроконтролером nRF52832.
Сенсорная часть устройства работает на микросхемах TTP223. Оба сенсора обслуживает одно прерывание. Питание от батарейки CR2477, через повышающий преобразователь на микросхеме TPS610981.
В устройстве реализована схема отключения питания на полевых транзисторах. После нажатия на кнопку микроконтролер сам перехватывает управление питанием и далее кнопка может использоватся для сервисных режимов(в моем случает это сопряжение с другими устройствами, отключение питания и сброс к заводским установкам(factory reset)).
Присутствуют 2 rgb светодиода для индикаций состояний и сервисных режимов. Так же добавлен пьезоизлучатель для имитации клика при прикосновении к сенсорным кнопкам и звуковой индикации сервичных режимов. Светодиоды и пьезоизлучатель можно включать и отключать по желанию пользователя. Делается это через контролер умного дома, отправкой команд на технические сенсоры, так же реализована возможность изменения пользователем интервалов отправки заряда батареи и уровня сигнала так же через контролер умного дома. В моем случае это МАЖОРДОМО.
Потребление в режиме передачи 7мА(250кбит, 10мс), потребление во сне 40мкА, потребление в выключенном состоянии менее 1мкА(=потреблению повышающего преобразователя в «холостом» режиме). Выведен rx, tx, swd разьем для програмирования. Используется миниатюрный разьем 2х3p с шагом 1.27. Для програмирования изготовлен специальный переходник.
Как и всегда в основе работы устройства лежит протокол
MySensors. Данный сенсорный выключатель планируется применять в системе управления рулонными шторами. Но в целом применение ограничено только вашей фантазией. Например уже сейчас сын(7 лет) сделал 3 заказа на версии выключателя: для включения и выключения света в туалете с ванной(крепится будет невысоко от пола), для включения света в длинном и темном коридоре при путешествии в туалет с ванной и еще один как прикроватный, для быстрого включения света в своей комнате чтобы монстры разбежались.
Корпус по традиции печатался на SLA принтере, устройство миниатюрное, корпус получился небольшой, применение даной технологии печати оправдано.
Посмотреть отпечатанную модель
В корпус и крышку батарейного отсека вклеены магнитики.
Видосики с тестами данного устрройства:
Для желающих повторить:
Код тестовой программы выключателя в системе управления рулонными шторами для Arduino IDE
int8_t timer_status = 0;
boolean sens_flag1 = 0;
boolean sens_flag2 = 0;
boolean switch_a = 0;
boolean switch_b = 0;
uint16_t temp;
float vcc;
int battery;
int old_battery;
uint32_t oldmillis;
uint32_t newmillis;
uint32_t interrupt_time;
uint32_t SLEEP_TIME = 7000;
uint32_t SLEEP_TIME_W;
uint32_t SLEEP_TIME_W2;
int NrfRSSI;
uint16_t NrfRSSI2;
boolean wait_off;
#define MY_DEBUG
#define MY_RADIO_NRF5_ESB
#define MY_PASSIVE_NODE
#define MY_NODE_ID 120
#define MY_PARENT_NODE_ID 0
#define MY_PARENT_NODE_IS_STATIC
#define MY_TRANSPORT_UPLINK_CHECK_DISABLED
#define POWER_CHILD_ID 110
#define UP_POWER_SWITCH_ID 1
#define DOWN_POWER_SWITCH_ID 2
#define CHILD_ID_nRF52_RSSI_RX 3
#define BAT_COOF 0.0092957746478873
#define BAT_MIN 200
#define BAT_MAX 290
#include <MySensors.h>
MyMessage upMsg(UP_POWER_SWITCH_ID, V_STATUS);
MyMessage downMsg(DOWN_POWER_SWITCH_ID, V_STATUS);
MyMessage powerMsg(POWER_CHILD_ID, V_VAR1);
MyMessage msgRF52RssiReceiv(CHILD_ID_nRF52_RSSI_RX, V_VAR1);
void preHwInit() {
//delay(1000);
pinMode(31, OUTPUT);
digitalWrite(31, HIGH);
delay(3000);
pinMode(3, INPUT);
pinMode(25, OUTPUT);
pinMode(26, OUTPUT);
pinMode(27, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(28, OUTPUT); // bizzer
pinMode(2, INPUT);
pinMode(9, INPUT);
pinMode(10, INPUT);
pinMode(29, INPUT);
digitalWrite(28, LOW); // off bizzer
digitalWrite(27, HIGH);
digitalWrite(26, HIGH);
digitalWrite(25, HIGH);
digitalWrite(6, HIGH);
digitalWrite(7, HIGH);
digitalWrite(8, HIGH);
}
void before()
{
//digitalWrite(31, HIGH);
NRF_POWER->DCDCEN = 1;
//NRF_UART0->ENABLE = 0;
analogReadResolution(12);
disableNfc();
turnOffAdc();
//wait(2000);
digitalWrite(25, LOW);
digitalWrite(6, LOW);
wait(200);
digitalWrite(25, HIGH);
digitalWrite(6, HIGH);
wait(100);
playSound0();
wait(100);
digitalWrite(25, LOW);
digitalWrite(6, LOW);
wait(200);
digitalWrite(25, HIGH);
digitalWrite(6, HIGH);
wait(3000);
digitalWrite(27, LOW);
digitalWrite(8, LOW);
wait(200);
digitalWrite(27, HIGH);
digitalWrite(8, HIGH);
wait(400);
digitalWrite(6, LOW);
digitalWrite(25, LOW);
wait(200);
digitalWrite(6, HIGH);
digitalWrite(25, HIGH);
wait(400);
digitalWrite(26, LOW);
digitalWrite(7, LOW);
wait(200);
digitalWrite(26, HIGH);
digitalWrite(7, HIGH);
wait(1000);
digitalWrite(26, LOW);
digitalWrite(7, LOW);
}
void setup()
{
digitalWrite(26, HIGH);
digitalWrite(7, HIGH);
wait(50);
playSound();
wait(2000);
readBatLev();
wait(200);
SLEEP_TIME_W = SLEEP_TIME;
}
void presentation()
{
sendSketchInfo(«Power on|off Node», «1.0»);
wait(100);
present(POWER_CHILD_ID, S_CUSTOM, «BATTERY DATA»);
wait(100);
present(UP_POWER_SWITCH_ID, S_BINARY, «UP SWITCH»);
wait(100);
present(DOWN_POWER_SWITCH_ID, S_BINARY, «DOWN SWITCH»);
}
void loop()
{
if (sens_flag1 == 0 && sens_flag2 == 0) {
if (switch_a == 0 && switch_b == 0) {
timer_status = sleep(digitalPinToInterrupt(2), RISING, digitalPinToInterrupt(3), RISING, 3600000, false);
wait_off = 1;
} else {
//oldmillis = millis();
timer_status = sleep(digitalPinToInterrupt(2), RISING, digitalPinToInterrupt(3), RISING, SLEEP_TIME_W, false);
wait_off = 0;
}
}
if (timer_status == 3) {
wait(100);
digitalWrite(27, LOW);
digitalWrite(8, LOW);
wait(2000);
digitalWrite(27, HIGH);
digitalWrite(8, HIGH);
wait(100);
digitalWrite(31, LOW);
}
if (timer_status == 2) {
if (digitalRead(9) == HIGH && sens_flag1 == 0 && switch_b == 0) {
sens_flag1 = 1;
if (switch_a == 0) {
oldmillis = millis();
SLEEP_TIME_W = SLEEP_TIME;
switch_a = 1;
send(upMsg.set(switch_a));
//wait(200);
digitalWrite(6, LOW);
wait(10);
playSound1();
wait(20);
playSound2();
wait(50);
} else {
switch_a = 0;
send(upMsg.set(switch_a));
//wait(200);
digitalWrite(6, HIGH);
wait(10);
playSound2();
wait(20);
playSound1();
wait(50);
}
//sleep_not_pasible = 1;
//digitalWrite(25, HIGH);
//wait(100);
}
if (digitalRead(10) == HIGH && sens_flag2 == 0 && switch_a == 0) {
sens_flag2 = 1;
if (switch_b == 0) {
oldmillis = millis();
SLEEP_TIME_W = SLEEP_TIME;
switch_b = 1;
send(downMsg.set(switch_b));
//wait(200);
digitalWrite(25, LOW);
wait(10);
playSound1();
wait(20);
playSound2();
wait(50);
} else {
switch_b = 0;
send(downMsg.set(switch_b));
//wait(200);
digitalWrite(25, HIGH);
wait(10);
playSound2();
wait(20);
playSound1();
wait(50);
}
//sleep_not_pasible = 1;
//digitalWrite(6, HIGH);
//wait(100);
}
if (digitalRead(9) == LOW && sens_flag1 == 1) {
sens_flag1 = 0;
//digitalWrite(6, HIGH);
//playSound2();
//wait(50);
}
if (digitalRead(10) == LOW && sens_flag2 == 1) {
sens_flag2 = 0;
//digitalWrite(25, HIGH);
//playSound2();
//wait(50);
}
if (switch_a == 1 || switch_b == 1) {
if (wait_off == 0) {
newmillis = millis();
wait(10);
SLEEP_TIME_W2 = SLEEP_TIME_W;
wait(10);
interrupt_time = newmillis — oldmillis;
wait(10);
SLEEP_TIME_W = SLEEP_TIME_W2 — interrupt_time;
wait(10);
Serial.print(«WAS IN A SLEEP: „);
Serial.print(newmillis — oldmillis);
Serial.println(“ MILLISECONDS»);
if (SLEEP_TIME_W < 1000) {
if (switch_a == 1) {
switch_a = 0;
digitalWrite(6, HIGH);
//wait(10);
//playSound2();
//wait(20);
//playSound1();
//wait(50);
//send(upMsg.set(switch_a));
//wait(200);
}
if (switch_b == 1) {
switch_b = 0;
digitalWrite(25, HIGH);
//wait(10);
//playSound2();
//wait(20);
//playSound1();
//wait(50);
//send(downMsg.set(switch_b));
//wait(200);
}
SLEEP_TIME_W = SLEEP_TIME;
wait(50);
}
Serial.println(SLEEP_TIME);
Serial.println(SLEEP_TIME_W);
Serial.println(SLEEP_TIME_W2);
Serial.print(«GO TO SLEEP FOR: „);
Serial.print(SLEEP_TIME_W);
Serial.println(“ MILLISECONDS»);
}
oldmillis = millis();
}
}
if (timer_status == -1) {
if (switch_a == 1 || switch_b == 1) {
if (switch_a == 1) {
switch_a = 0;
digitalWrite(6, HIGH);
wait(10);
playSound2();
wait(20);
playSound1();
wait(50);
send(upMsg.set(switch_a));
wait(200);
}
if (switch_b == 1) {
switch_b = 0;
digitalWrite(25, HIGH);
wait(10);
playSound2();
wait(20);
playSound1();
wait(50);
send(downMsg.set(switch_b));
wait(200);
}
} else {
readBatLev();
}
}
}
void disableNfc() {
NRF_NFCT->TASKS_DISABLE = 1;
NRF_NVMC->CONFIG = 1;
NRF_UICR->NFCPINS = 0;
NRF_NVMC->CONFIG = 0;
}
void turnOffAdc() {
if (NRF_SAADC->ENABLE) {
NRF_SAADC->TASKS_STOP = 1;
while (NRF_SAADC->EVENTS_STOPPED) {}
NRF_SAADC->ENABLE = 0;
while (NRF_SAADC->ENABLE) {}
}
}
void myTone(uint32_t j, uint32_t k) { // Определяем функцию myTone
j = 500000 / j; // Меняем значение переменной j на время одного полупериода в мкс
k += millis(); // Меняем значение переменной к на время завершения вывода сигнала
while (k > millis()) { // Выводим сигнал, пока не истечёт указанное время
digitalWrite(28, HIGH); delayMicroseconds(j); // Устанавливаем на выходе i уровень логической «1» на время j
digitalWrite(28, LOW ); delayMicroseconds(j); // Устанавливаем на выходе i уровень логического «0» на время j
}
}
void playSound0() {
//wait(500);
myTone(1300, 50); // Выводим звуковой сигнал длительностью 0,1 сек с частотой 2048 Гц
wait(20); // Ждём 0,1 сек
myTone(1300, 50);
wait(50);
}
void playSound() {
//wait(500);
myTone(700, 30); // Выводим звуковой сигнал длительностью 0,1 сек с частотой 2048 Гц
wait(10); // Ждём 0,1 сек
myTone(700, 30);
wait(10);
myTone(700, 30);
wait(50); // Ждём 0,1 сек
//myTone(500, 30);
//wait(500);
}
void playSound1() {
//wait(500);
myTone(200, 10); // Выводим звуковой сигнал длительностью 0,1 сек с частотой 2048 Гц
wait(10); // Ждём 0,1 сек
myTone(400, 5);
wait(30); // Ждём 0,1 сек
//myTone(500, 30);
//wait(500);
}
void playSound2() {
//wait(500);
myTone(400, 10); // Выводим звуковой сигнал длительностью 0,1 сек с частотой 2048 Гц
wait(10); // Ждём 0,1 сек
myTone(200, 5);
wait(30); // Ждём 0,1 сек
//myTone(500, 30);
//wait(500);
}
void readBatLev() {
//NRF5_ESB_startListening();
wait(200);
temp = analogRead(29);
vcc = temp * 0.0033 * 100;
battery = map((int)vcc, BAT_MIN, BAT_MAX, 0, 100);
if (battery < 0) {
battery = 0;
}
if (battery > 100) {
battery = 100;
}
sendBatteryLevel(battery);
wait(200);
send(powerMsg.set(temp));
wait(200);
NrfRSSI = transportGetReceivingRSSI();
NrfRSSI2 = map(NrfRSSI, -85, -40, 0, 100);
if (NrfRSSI2 < 0) {
NrfRSSI2 = 0;
}
if (NrfRSSI2 > 100) {
NrfRSSI2 = 100;
}
send(msgRF52RssiReceiv.set(NrfRSSI2));
wait(200);
}
Все комплектующие покупались на АЛИ, приведу ссылки на основные компоненты:
Стеклянная мини панель —
ссылка
Микроконтролер nRF52832 —
ссылка
Сенсорная микросхема TTP223 —
ссылка
Файлы корпуса в stl —
гугл драйв
Gerber файлы печатной платы —
гугл драйв
На все вопросы по данной разработке, помощь в освоении MySensors обращайтесь в чат
@mysensors_rus
На какой срок хватает батареи? Или я пропустил?
Сенсорные хороши при нормальном питании:
Почему спойлер в комментах не работает?
Вот точный аналог по функционалу, все в инфраструктуре mysensors, только я domoticz использую:
ЗЫ спойлер не работает, не раскрывается (
Так не раскрывается или не создается? Для открытия спойлера после добавления комментария обнови страницу.
Пойду ка я приму увеличитель мозга…
NRF52832 за 150р при покупке некоего количества. А дальше уже от вашего скилла все зависит, большинство вообще не поймет о чем этот топик :-)
А то пристал там к автору один «разработчик» с ЧСВ over 8000 что TP223 не нужна можно ножки заряжать/разряжать, noolite лучше и.т.п.
Только он забыл написать, что дрыгать ногой с площадкой и высокоомным резистором — это в разы больше расходовать энергию.
Сам сейчас допиливаю сенсор на pic18. Да, нажатие можно отслеживать и через 70мм диэлектрика (дерево/бетон — ему пофиг), но путем неимоверных страданий мк :) А noolite который на pic12, ЕМНИП, без обратной связи. Это я считаю вообще не вариант. Продавать это должно быть стыдно.
Так что жму руку, всё у вас норм :)
да и вызывает вопросы:
что за сервисные режимы?
Что за технические сенсоры?
Какие команды поддерживает сенсор?
может ест подробное описание на hardware.io?