/* Embarcatech: 28-01-2025 - atualizado em 04-02-2025
Autor: Marcio Barbosa
7 - Microcontroladores - Projeto Prático - Equipe Firmware
Projeto: Sistema de segurança para máquina de Serra de Fita de Bancada
Para realizar este experimento, foi necessário os seguintes componentes:
1 x Raspberry Pi Pico W ,
1 x HCSR04
1 x Buzzer
2 x Leds
2 x Resistores
3 x botões (verde - liga, azul - desliga e vermelho - emergência)
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include "hardware/irq.h"
// Definição dos pinos
#define TRIG_PIN 3 // Pino TRIG do HC-SR04
#define ECHO_PIN 4 // Pino ECHO do HC-SR04
#define LED_G 5 // LED Verde (segurança)
#define LED_R 6 // LED Vermelho (alerta)
#define BUZZER_PIN 7 // Buzzer para alarme
#define EMERGENCY_BTN 8 // Botão de emergência (Vermelho)
#define START_BTN 10 // Botão para ligar a máquina (Verde)
#define STOP_BTN 11 // Botão para desligar a máquina (Azul)
#define MOTOR_PIN 9 // Motor simulado com PWM
volatile bool flag_emergency = false; // Flag de emergência, testar o botão vermelho
volatile bool status_machine = false; // Estado da máquina - deligada (false) | ligada (true)
// Tratar todos os botões ## estava montando separado da erro por causa do tempo de acionamento do botão
void gpio_button(uint gpio, uint32_t events) {
if (gpio == EMERGENCY_BTN && (events & GPIO_IRQ_EDGE_FALL)) {
flag_emergency = true;
status_machine = false;
printf("## EMERGÊNCIA: Máquina desligada! ##\n");
}
else if (gpio == START_BTN && (events & GPIO_IRQ_EDGE_FALL)) {
if (!flag_emergency) {
status_machine = true;
printf("## Máquina LIGADA pelo operador ##!\n");
}
}
else if (gpio == STOP_BTN && (events & GPIO_IRQ_EDGE_FALL)) {
if (!flag_emergency) {
status_machine = false;
printf("## Máquina DESLIGADA pelo operador ##.\n");
}
}
}
/*
// Simulação da leitura do HC-SR04
float measure_distance() {
static int distance = 10;
distance += 10;
if (distance < 0 || distance > 100) { //coloquei para acionar a 100 cm, se ajustar aqui deve ajustar no fluxo abaixo
printf("ERRO: Leitura de distância inválida: %.2f cm!\n", distance);
printf("ERRO: Verifique as conexões e a alimentação do sensor HC-SR04. .\n");
return distance;
}
else{
distance = 100; // detectado problema com o sensor HC-SR04, desliga a máquina
printf("## Distância detectada: %d cm ##\n", distance);
return distance;
}
}
*/
// Função para medir a distancia com o sensor HC-SR04
//https://wokwi.com/projects/420357412143466497 -> funcao medir distance
float measure_distance() {
// Envia pulso de 10 microsegundos no pino TRIG
gpio_put(TRIG_PIN, 1);
sleep_us(10);
gpio_put(TRIG_PIN, 0);
// Aguarda o início do pulso de ECHO
while (gpio_get(ECHO_PIN) == 0) {
tight_loop_contents();
}
absolute_time_t start = get_absolute_time();
// Aguarda o fim do pulso de ECHO
while (gpio_get(ECHO_PIN) == 1) {
tight_loop_contents();
}
absolute_time_t end = get_absolute_time();
// Calcula a duração do pulso (em microsegundos)
//Distância em centímetros = Tempo / 58
uint32_t diff = absolute_time_diff_us(start, end);
// Conversão: 58 µs ≈ 1 cm (aproximadamente)
float distance_cm = diff / 58.0f;
printf("## Distância detectada: %.2f cm ##\n", distance_cm);
return distance_cm;
}
// Função para controlar o motor com PWM
void set_motor_speed(uint16_t duty_cycle) {
pwm_set_gpio_level(MOTOR_PIN, duty_cycle);
}
// Funções para controlar o buzzer via PWM
//Função para acionar
void buzzer_on() {
// Configure o BUZZER_PIN como PWM
gpio_set_function(BUZZER_PIN, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN);
// Configure para gerar 2 kHz com 50% de duty cycle.
// Exemplo: se o clock é 125MHz, com divisor 25, o clock do slice será 5MHz.
// Para 2 kHz, o wrap deve ser aproximadamente 2500 (5,000,000 / 2,000 = 2500).
pwm_set_clkdiv(slice_num, 25.0f);
pwm_set_wrap(slice_num, 2500);
pwm_set_chan_level(slice_num, pwm_gpio_to_channel(BUZZER_PIN), 1250);
pwm_set_enabled(slice_num, true);
}
//Função para desligar
void buzzer_off() {
// Desabilita o PWM no buzzer e o coloca em nível baixo.
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN);
pwm_set_enabled(slice_num, false);
gpio_set_function(BUZZER_PIN, GPIO_FUNC_SIO);
gpio_put(BUZZER_PIN, 0);
}
// Função para ativar o buzzer por um tempo definido (em segundos) --> vou manter mas não usei
void buzzer_for_seconds(uint32_t seconds) {
buzzer_on();
sleep_ms(seconds * 1000);
buzzer_off();
}
int main() {
stdio_init_all();
// Inicializa os pinos de saída
gpio_init(TRIG_PIN);
gpio_set_dir(TRIG_PIN, GPIO_OUT);
gpio_put(TRIG_PIN, 0);
gpio_init(ECHO_PIN);
gpio_set_dir(ECHO_PIN, GPIO_IN);
gpio_init(LED_G);
gpio_set_dir(LED_G, GPIO_OUT);
gpio_init(LED_R);
gpio_set_dir(LED_R, GPIO_OUT);
// Inicializa o pino do buzzer (inicialmente desligado)
gpio_init(BUZZER_PIN);
gpio_set_dir(BUZZER_PIN, GPIO_OUT);
// Inicializa os pinos dos botões e ativa os pull-ups
gpio_init(EMERGENCY_BTN);
gpio_set_dir(EMERGENCY_BTN, GPIO_IN);
gpio_pull_up(EMERGENCY_BTN);
gpio_init(START_BTN);
gpio_set_dir(START_BTN, GPIO_IN);
gpio_pull_up(START_BTN);
gpio_init(STOP_BTN);
gpio_set_dir(STOP_BTN, GPIO_IN);
gpio_pull_up(STOP_BTN);
// Registra um único callback para todos os pinos com interrupção
gpio_set_irq_enabled_with_callback(EMERGENCY_BTN, GPIO_IRQ_EDGE_FALL, true, &gpio_button);
gpio_set_irq_enabled(START_BTN, GPIO_IRQ_EDGE_FALL, true);
gpio_set_irq_enabled(STOP_BTN, GPIO_IRQ_EDGE_FALL, true);
// Configuração do PWM para o motor
gpio_set_function(MOTOR_PIN, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(MOTOR_PIN);
pwm_set_wrap(slice_num, 100);
pwm_set_enabled(slice_num, true);
while (1) {
if (flag_emergency) {
// Se a emergência for acionada, desliga tudo
gpio_put(LED_G, 0);
gpio_put(LED_R, 1);
buzzer_on();
sleep_ms(3000);
buzzer_off();
set_motor_speed(0);
printf("Máquina desligada por emergência! Pressione o botão verde para reiniciar.\n");
flag_emergency = false;
status_machine = false;
}
if (!status_machine) {
// Se a máquina estiver desligada, aguarda o start
set_motor_speed(0);
gpio_put(LED_G, 0);
gpio_put(LED_R, 1);
//gpio_put(BUZZER_PIN, 0);
//pwm_set_enabled(slice_buzzer, false);
buzzer_off();
printf("Máquina DESLIGADA. Pressione o Botão Verde para ligar.\n");
sleep_ms(1000);
continue;
}
// Se a máquina estiver ligada, realiza a medição da distância
float test_distance = measure_distance();
float distance = 100.0f; // defina a distancia para o sensor
//Além da distancia colocar permitir que a máquina funcione sem o operador por 1 min
if (test_distance > distance) {
printf("## Operador fora da zona segura. Iniciando tempo de retorno de 1 minuto... ##\n");
uint32_t start_time = to_ms_since_boot(get_absolute_time());
bool operator_returned = false;
while ((to_ms_since_boot(get_absolute_time()) - start_time) < 10000) { // 60.000 ms = 1 minuto
float new_distance = measure_distance();
if (new_distance <= distance) {
operator_returned = true;
printf("## Operador retornou à zona segura. ##\n");
break;
}
sleep_ms(1000); // Verifica a cada 1 segundo
}
if (!operator_returned) {
// Alerta: operador ausente, desligando a máquina
gpio_put(LED_G, 0);
gpio_put(LED_R, 1);
printf("ALERTA: Operador ausente! Máquina será desligada.\n");
// Reduz gradualmente a velocidade do motor antes de desligar
for (int speed = 100; speed >= 0; speed -= 10) {
set_motor_speed(speed);
printf("Reduzindo motor: %d%%\n", speed);
sleep_ms(500);
}
// Após desaceleração, desliga a máquina
buzzer_on();
sleep_ms(2000);
status_machine = false;
printf("Máquina desligada automaticamente por ausência do operador.\n");
buzzer_off();
}
} else {
// Operação normal
gpio_put(LED_G, 1);
gpio_put(LED_R, 0);
//gpio_put(BUZZER_PIN, 0);
// pwm_set_enabled(slice_buzzer, false);
buzzer_off();
set_motor_speed(100);
printf("SEGURO: Máquina operando normalmente. Distância = %.2f cm.\n", test_distance);
}
sleep_ms(2000);
}
return 0;
}