Quero partilhar um projeto que estive a desenvolver nestas ultimas semanas que consiste numa campainha sem fios e que reporta para um esp32 com MQTT com um módulo lora RFM95.
Material que usei:
1- Arduino mini pro 3.3v. Tem de ser a versão de 3.3v 8Mhz para não danificar o modulo lora.
1 - Xiao Esp32c3
2 - Modulos Lora RFM95 - 868Mhz.
1 - Pcb para a placa que vai ser de emissor.
1 - Controlador de carga para uma pilha de litio de 3.7v.
1 - Conversor DC-DC para 3.3v para alimentar o arduino.
1 - Reed switch
A alimentação do arduino vai ser feita através de um painel solar de 5v e de uma pilha de litio de 3.7v. Deixo abaixo uma imagem para perceberem a ideia.
Depois de ter encomendado o pcb para o arduino é que descobri que também existia o pcb para o esp32c3. Já fiz a encomenda desse pcb e assim que ele chegar atualizo este post.
1- Pcb para o receptor ESP32C3 .
O código que vou partilhar abaixo não foi testado com o PCB do ESP32C3.
Pinout usado para ligar o ESP32C3 ao modulo lora RFM95:
RFM - XIAO ESP32C3
DIO0 - GPIO2
RST - GPIO3
NSS - GPIO7
SCK - SCK
MOSI - MOSI
MISO - MISO
3.3v - 3.3v
GND - GND
O buzzer/altifalante liga ao GPIO4 e ao GND do esp.
Arduino:
O botão da campainha liga ao GPIO3 e ao GND do arduino.
O reed switch liga ao GPIO4 e ao GND do arduino.
Código para o arduino mini pro:
Devem alterar a linha de Identificador único tanto no código do emissor como no código do receptor.
Ele é utilizado para assegurar que apenas dispositivos emparelhados comuniquem entre si,
garantindo a correta transmissão de mensagens.
Update ao codigo do emissor - 18/06/2024
Acrescentei um led e um besouro ao emissor e de forma a poupar bateria, o reed switch funciona com um botão. Assim sóé enviado a notificação de correio quando o reed switch está aberto.
#include <SPI.h>
#include <LoRa.h>
// Pinos configurados conforme a breakout board DIYcon v2.03
#define SS 10
#define RST 9
#define DIO0 2
// Identificador único
const String identificador = "campainha1";
// Pinos para o botão da campainha, LED, buzzer e botão de correio
#define BUTTON_PIN 3
#define MAIL_BUTTON_PIN 4
#define LED_PIN 5
#define BUZZER_PIN 6
void setup() {
Serial.begin(9600);
while (!Serial);
// Inicializa os pinos conforme a breakout board
LoRa.setPins(SS, RST, DIO0);
Serial.println("LoRa Transmitter");
if (!LoRa.begin(868E6)) {
Serial.println("Starting LoRa failed!");
while (1);
}
// Configura os pinos dos botões como entrada com pull-up
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(MAIL_BUTTON_PIN, INPUT_PULLUP);
// Configura os pinos do LED e do buzzer como saída
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Desliga o LED e o buzzer inicialmente
digitalWrite(LED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}
void loop() {
// Verifica se o botão da campainha foi pressionado
if (digitalRead(BUTTON_PIN) == LOW) {
Serial.println("Button pressed");
// Acende o LED
digitalWrite(LED_PIN, HIGH);
// Mantém o LED ligado por 2 segundos e o buzzer apitando enquanto o botão estiver pressionado
unsigned long startTime = millis();
while (millis() - startTime < 2000) {
if (digitalRead(BUTTON_PIN) == LOW) {
tone(BUZZER_PIN, 1000); // Frequência de 1000 Hz
} else {
noTone(BUZZER_PIN);
}
sendMessage(identificador + "Buzzer");
delay(100); // Pequeno atraso para evitar saturação da rede
}
// Assegura que o buzzer está desligado
noTone(BUZZER_PIN);
// Desliga o LED após 2 segundos
digitalWrite(LED_PIN, LOW);
// Debounce delay para evitar múltiplas detecções
delay(500);
}
// Verifica se o botão de correio foi pressionado
if (digitalRead(MAIL_BUTTON_PIN) == LOW) {
Serial.println("Mail button pressed");
sendMessage(identificador + "Mail");
delay(500); // Debounce delay para evitar múltiplas detecções
}
delay(100);
}
void sendMessage(String message) {
Serial.print("Sending: ");
Serial.println(message);
LoRa.beginPacket();
LoRa.print(message);
LoRa.endPacket();
}
Código para o esp32c3 com MQTT ( versão campainha e caixa de correio) :
Nota: O nomes e senhas partilhados nos códigos foram inventados de forma a apresentar o código como ele deve ficar após ser editado com os vossos dados.
const char* telegramBotToken = "1125383575:AAGl22YyQtxyzpauvufYlMLiENbVAEudw"; // Substitua pelo token do seu bot (
const char* telegramChatID = "-419345390"; // Substitua pelo chat ID do seu bot
Código para o esp32c3 com MQTT ( versão campainha, caixa de correio e notificação via telegram que tem correio) :
Nota: O nomes e senhas partilhados nos códigos foram inventados de forma a apresentar o código como ele deve ficar após ser editado com os vossos dados.
Update 09/08/2024: O esp emite um beep assim que se liga ao wifi e emite outro beep quando se liga ao servidor mqtt.
#include <LoRa.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <UniversalTelegramBot.h>
#include <WiFiClientSecure.h>
// Pinos
const int buzzerPin = 4; // Pino do buzzer
const int loraSS = 7;
const int loraRST = 3;
const int loraDI0 = 2;
// Identificador único esperado
const String identificador = "campainha1";
// WiFi
const char* ssid = "Vodafone-FUB0Y";
const char* password = "A73737rocas70d0";
// MQTT
const char* mqtt_server = "192.168.1.xxx";
const int mqtt_port = 1883;
const char* mqtt_command_topic = "cmnd/campainha/POWER";
const char* mqtt_state_topic = "stat/campainha/POWER";
const char* mqtt_mail_topic = "stat/correio/POWER";
const char* mqtt_user = "userxyx";
const char* mqtt_password = "passwxyz";
const char* mqtt_client_name = "campainha"; // Nome do cliente MQTT
// Telegram
const char* telegramBotToken = "1125383575:AAGl22YyQtxyzpauvufYlMLiENbVAEudw"; // Substitua pelo token do seu bot (
const char* telegramChatID = "-419345390"; // Substitua pelo chat ID do seu bot
WiFiClient espClient;
WiFiClientSecure secured_client;
PubSubClient client(espClient);
UniversalTelegramBot bot(telegramBotToken, secured_client);
// Flags para controlar o envio de mensagens MQTT e Telegram
volatile bool buzzerEvent = false;
volatile bool mailEvent = false;
volatile bool newMailReceived = false; // Flag para verificar nova mensagem de correio
void setup() {
pinMode(buzzerPin, OUTPUT);
Serial.begin(115200);
LoRa.setPins(loraSS, loraRST, loraDI0);
// Inicializa o LoRa
if (!LoRa.begin(868E6)) { // Frequência ajustada para 868 MHz
Serial.println("Erro LoRa");
while (1);
}
Serial.println("LoRa inicializado");
// Conecta-se à rede Wi-Fi
Serial.println();
Serial.println();
Serial.print("Conectando à rede ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("Conectado à rede WiFi");
Serial.println("Endereço IP: ");
Serial.println(WiFi.localIP());
// Configura o servidor MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
// Configura o cliente seguro para o Telegram
secured_client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Certificado root para o Telegram
// Tenta se conectar ao servidor MQTT
reconnect();
Serial.println("Campainha");
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
if (buzzerEvent) {
if (client.publish(mqtt_state_topic, "ON")) {
Serial.println("Mensagem MQTT 'ON' enviada com sucesso");
buzzerEvent = false;
delay(3000); // O buzzer toca por 3 segundos
client.publish(mqtt_state_topic, "OFF");
Serial.println("Campainha");
} else {
Serial.println("Falha ao enviar mensagem MQTT 'ON'");
}
}
if (mailEvent && newMailReceived) {
if (client.publish(mqtt_mail_topic, "ON")) {
Serial.println("Mensagem MQTT 'ON' para correio enviada com sucesso");
mailEvent = false;
newMailReceived = false;
delay(5000); // Mostra a mensagem por 5 segundos
client.publish(mqtt_mail_topic, "OFF");
Serial.println("Correio");
// Envia mensagem pelo Telegram
String message = "Você tem correio!";
if (bot.sendMessage(telegramChatID, message, "")) {
Serial.println("Mensagem Telegram enviada com sucesso");
} else {
Serial.println("Falha ao enviar mensagem Telegram");
}
} else {
Serial.println("Falha ao enviar mensagem MQTT 'ON' para correio");
}
}
int packetSize = LoRa.parsePacket();
if (packetSize) {
String incoming = "";
while (LoRa.available()) {
incoming += (char)LoRa.read();
}
Serial.print("Mensagem LoRa recebida: ");
Serial.println(incoming);
// Verificar se a mensagem recebida contém o identificador correto
if (incoming.startsWith(identificador)) {
// Verificar se a mensagem recebida é um sinal para tocar a campainha
if (incoming.endsWith("Buzzer")) {
playRedmiMelody();
buzzerEvent = true;
}
// Verificar se a mensagem recebida é um sinal de correio chegou
if (incoming.endsWith("Mail")) {
Serial.println("Correio Chegou!");
mailEvent = true;
newMailReceived = true; // Setar flag de nova mensagem de correio
}
}
}
}
void reconnect() {
// Loop até estar conectado ao WiFi
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Conectar ao servidor MQTT
while (!client.connected()) {
Serial.print("Conectando ao servidor MQTT...");
if (client.connect(mqtt_client_name, mqtt_user, mqtt_password)) {
Serial.println("Conectado");
// Subscrever ao tópico de comando
client.subscribe(mqtt_command_topic);
} else {
Serial.print("Falha ao conectar, rc=");
Serial.print(client.state());
Serial.println(" Tentando novamente em 5 segundos");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
message.trim();
Serial.print("Mensagem recebida [");
Serial.print(topic);
Serial.print("]: ");
Serial.println(message);
if (String(topic) == mqtt_command_topic) {
if (message.equalsIgnoreCase("on")) {
playRedmiMelody();
client.publish(mqtt_state_topic, "ON");
} else if (message.equalsIgnoreCase("off")) {
noTone(buzzerPin);
Serial.println("Campainha");
client.publish(mqtt_state_topic, "OFF");
}
}
}
void playRedmiMelody() {
// Notas e durações para um toque mais complexo
int melody[] = {
659, 659, 523, 659, 784, 392,
523, 392, 330, 440, 494, 466, 440, 392, 659, 784, 880,
698, 784, 659, 523, 587, 494
};
int noteDurations[] = {
125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
125, 125, 125, 125, 125, 125, 125, 125, 125, 125
};
Serial.println("A Tocar...");
for (int i = 0; i < sizeof(melody) / sizeof(int); i++) {
tone(buzzerPin, melody[i], noteDurations[i]);
delay(noteDurations[i] * 1.30);
noTone(buzzerPin);
}
}
Partilho tambem a automação que fiz para notificar pelo HA que temos correio.
configuration.yaml
mqtt:
binary_sensor:
- name: "Campainha"
state_topic: "stat/campainha/POWER"
device_class: sound
qos: 0
unique_id: "campainha_sensor"
- name: "Correio"
state_topic: "stat/correio/POWER"
device_class: sound
qos: 0
unique_id: "campainha_correio"
campainha.yaml
automation:
- alias: Notificação de Campainha
trigger:
platform: state
entity_id: binary_sensor.campainha
to: 'on'
action:
service: notify.notify
data:
message: "A campainha está a tocar!"
- alias: Notificação de Correio
trigger:
platform: state
entity_id: binary_sensor.correio
to: 'on'
action:
service: notify.notify
data:
message: "Chegou correio"
Automação em NODE RED :
Alexa avisa que estão a tocar à campainha.
[{"id":"4322e1fcbbf89b87","type":"api-call-service","z":"af740ccdec97ba89","name":"","server":"79fd2272.3f2bdc","version":5,"debugenabled":false,"domain":"media_player","service":"volume_set","areaId":[],"deviceId":[],"entityId":["media_player.echo_dot_escritorio"],"data":"{\"volume_level\":\"0.17\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":610,"y":2120,"wires":[["ecabb79ad1399bfc"]]},{"id":"ecabb79ad1399bfc","type":"api-call-service","z":"af740ccdec97ba89","name":"Campainha","server":"79fd2272.3f2bdc","version":5,"debugenabled":false,"domain":"notify","service":"alexa_media_echo_dot_escritorio","areaId":[],"deviceId":[],"entityId":[],"data":"{\"data\":{\"type\":\"announce\",\"method\":\"speak\"},\"message\":\"<voice name='Ines'><lang xml:lang='pt-PT'><amazon:auto-breaths frequency='low' volume='soft' duration='x-short'> Estão a tocar à campainha </amazon:auto-breaths></lang></voice>\",\"target\":[\"media_player.echo_dot_escritorio\"]}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"data"}],"queue":"none","x":870,"y":2120,"wires":[[]]},{"id":"52b744f93c1921e0","type":"switch","z":"af740ccdec97ba89","name":"códigos bridge","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":340,"y":2120,"wires":[["4322e1fcbbf89b87"]]},{"id":"c755ea95d74711a6","type":"server-state-changed","z":"af740ccdec97ba89","name":"Sensores","server":"79fd2272.3f2bdc","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"binary_sensor.campainha","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":140,"y":2120,"wires":[["52b744f93c1921e0"],[]]},{"id":"79fd2272.3f2bdc","type":"server","name":"Home Assistant :)","addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":"","statusSeparator":"","enableGlobalContextStore":false}]
Avisa que chegou correio pelo telegram.
[{"id":"79ce4183a8758d12","type":"telegrambot-notify","z":"34b3a010.928f3","name":"Correio","bot":"30ccc6d5795ddf03","chatId":"419345390","message":"","parseMode":"","x":800,"y":1600,"wires":[]},{"id":"6f7313167ab21a37","type":"server-state-changed","z":"34b3a010.928f3","name":"Sensor da caixa de correio","server":"79fd2272.3f2bdc","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"binary_sensor.correio","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"off","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"for":"2","forType":"num","forUnits":"seconds","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":270,"y":1600,"wires":[["1c223ed8291de5b3"],[]]},{"id":"1c223ed8291de5b3","type":"api-render-template","z":"34b3a010.928f3","name":"Prepare Message","server":"79fd2272.3f2bdc","version":0,"template":"Chegou a encomenda da Mauser.","resultsLocation":"payload","resultsLocationType":"msg","templateLocation":"template","templateLocationType":"msg","x":530,"y":1600,"wires":[["79ce4183a8758d12"]]},{"id":"30ccc3d5795ddf03","type":"telegrambot-config","botname":"@poisebebe_bot","usernames":"madmax","chatIds":"https://t.me/+IBklXAOGGrhiMDVk","pollInterval":"300"},{"id":"79fd2272.3f2bdc","type":"server","name":"Home Assistant :)","addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":"","statusSeparator":"","enableGlobalContextStore":false}]
Espero que não me tenha esquecido de partilhar nada .
Agradecimentos ao chatgpt pela paciencia que tem tido comigo e ao @RodolfoVieira pela ajuda que deu.