Projetos

BOOTSEL da Raspberry Pi Pico no Arduino IDE

Eletrogate 25 de julho de 2023

Introdução

Neste tutorial, será mostrado como ler o botão BOOTSEL da placa Raspberry Pi Pico e utilizar essa informação para controlar o comportamento de um LED externo. Além disso, vamos explorar como implementar diferentes funções para um único botão. O botão BOOTSEL na Raspberry Pi Pico desempenha um papel importante ao iniciar o dispositivo em diferentes modos de inicialização. No entanto, também podemos aproveitar esse botão para executar ações personalizadas no nosso programa, com a vantagem de não precisar adicionar um botão externo em uma protoboard.

Além de mostrar como ler o botão BOOTSEL, será mostrado como dar duas funções a um mesmo botão. Este botão será o BOOTSEL, da palca Raspberry Pi Pico. Estas duas funções irão controlar um LED externo de modos difrerentes.


Materiais Utilizados no Projeto BOOTSEL da Raspberry Pi Pico no Arduino IDE

Para este post, é utilizado o seguinte material:


Como Ler o Botão BOOTSEL

Para realizar a leitura do botão BOOTSEL da placa Raspberry Pi Pico, basta usar a variável BOOTSEL. Essa variável é definida no arquivo bootsel.cpp. Para usar a variável BOOTSEL, deve-se usar o pacote de placas Arduino-Pico. Caso ainda não o tenha instalado, veja como fazer o processo de instalação do pacote de placas no post Raspberry Pi Pico com Arduino IDE + Exemplos do blog Eletrogate.

Veja um exemplo de uso da variável BOOTSEL:

/******************************************************************************
      Lendo o botão BOOTSEL da placa Raspberry Pi Pico no Framework Arduino
                      Sketch Como ler o botão BOOTSEL

                       Criado em 10 de Junho de 2023
                por Michel Galvão (https://micsg.com.br)

              Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
                            https://www.eletrogate.com/
      Confira mais detalhes deste sketch em https://blog.eletrogate.com/
******************************************************************************/

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);  // Define o pino do LED_BUILTIN como saída
}

void loop() {
  if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado

    digitalWriteFast(LED_BUILTIN, HIGH);  // Define o pino LED_BUILTIN como HIGH, acendendo o LED
    while (BOOTSEL);                      // Aguarda o botão BOOTSEL ser solto
    digitalWriteFast(LED_BUILTIN, LOW);  // Define o pino LED_BUILTIN como LOW, apagando o LED
  }
}

Ao utilizar a variável BOOTSEL em uma condição, como no exemplo de código acima, o programa, internamente, chama a função get_bootsel_button() para obter o estado do botão BOOTSEL. Essa função desabilita as interrupções, verifica o estado do pino GPIO associado ao botão BOOTSEL, habilita novamente as interrupções e retorna true se o botão estiver pressionado ou false, caso contrário.

No código acima, na função loop(), a condição if (BOOTSEL) verifica se o botão BOOTSEL está pressionado. Se essa condição for verdadeira, ou seja, o botão estiver pressionado, o programa acende o LED onboard utilizando a função digitalWrite(), passando como parâmetros o pino do LED (no caso, LED_BUILTIN) e o estado no qual deseja que o pino fique (no caso, HIGH). Em seguida, o programa entra em um loop while que aguarda até que o botão BOOTSEL seja liberado, ou seja, até que o estado de BOOTSEL se torne falso. Ao sair do loop while, o que significa que o botão foi solto, o LED é apagado pela função digitalWrite(), passando como parâmetros o pino do LED (no caso, LED_BUILTIN) e o estado no qual deseja que o pino fique (no caso, LOW).

Essa abordagem permite ao programa detectar quando o botão BOOTSEL é pressionado e tomar medidas com base nessa informação. É importante mencionar que as informações sobre a implementação do BOOTSEL são baseadas no arquivo bootsel.cpp, incluído no core do pacote de placas Arduino-Pico da placa Raspberry Pi Pico, que contém a lógica de leitura e verificação do estado do botão BOOTSEL na placa Raspberry Pi Pico. Veja mais detalhes sobre este arquivo em https://github.com/earlephilhower/arduino-pico/blob/master/cores/rp2040/Bootsel.cpp.


Exemplo de Projeto Utilizando o Botão BOOTSEL

Como exemplo de projeto usando o botão BOOTSEL, será mostrado como fazer com que um botão tenha mais de uma função. As duas funções que estarão atribuídas ao botão BOOTSEL são as seguintes:

  • Função básica:
    • Ao pressionar o botão, o LED é aceso apenas durante o tempo em que o botão ficar pressionado;
    • Ao soltar o botão, o LED é apagado;
    • Ao pressionar o botão e soltar rapidamente duas vezes, alterna-se para a função alternativa.
  • Função alternativa:
    • Ao clicar uma vez rapidamente e soltar, o LED é aceso e permanece aceso continuamente, mesmo após soltar o botão;
    • Ao clicar outra vez rapidamente e soltar, o LED é apagado e permanece apagado até outro clique rápido.
    • Ao pressionar novamente o botão por um longo período, alterna-se novamente para a função básica.

A transição entre as funções será feita utilizando uma lógica de programação que verifica os padrões de pressionar e soltar o botão BOOTSEL. Veja a descrição do fluxo básico de como será implementado no projeto:

  1. Inicialmente, o programa estará na função básica, onde o LED é aceso apenas enquanto o botão está pressionado;
  2. O programa irá monitorar continuamente o estado do botão BOOTSEL para detectar quando ele é pressionado e solto;
  3. Quando o botão é pressionado e solto rapidamente duas vezes seguidas, o programa reconhece esse padrão como uma solicitação para alternar para a função alternativa. Para detectar este padrão de pressionar e soltar o botão rapidamente duas vezes, será utilizado variáveis de controle de tempo e a função millis() para medir a diferença entre os tempos de soltar e pressionar o botão. Após reconhecer o padrão de duplo clique, será utilizado uma variável de estado com uma estrutura de condição para verificar se é necessário mudar para a função alternativa;
  4. Ao entrar na função alternativa, o programa mantém um estado (HIGH ou LOW) em uma variável que controla o comportamento do LED;
  5. Quando o botão é pressionado e solto rapidamente enquanto estiver na função alternativa, o valor da variável de controle do LED é alterado para HIGH (ligado) ou LOW (desligado), dependendo do estado atual do LED;
  6. O programa continua monitorando o botão na função alternativa e mantém o estado do LED (seja ligado ou desligado) até que o botão seja pressionado e solto novamente rapidamente;
  7. Se o botão for pressionado por um longo período (1000 milissegundos) enquanto na função alternativa, o programa reconhece isso como uma solicitação para retornar à função básica. Para detectar o pressionamento longo do botão na função alternativa, será utilizado a função millis() novamente para verificar se o botão foi mantido pressionado por um período específico.
    Ao reconhecer o pressionamento longo, será alterada a variável de estado para retornar à função básica.

Hardware do Projeto

Veja abaixo o esquemático do projeto:


Software do Projeto

O Sketch do projeto é o abaixo:

/******************************************************************************
      Lendo o botão BOOTSEL da placa Raspberry Pi Pico no Framework Arduino
                        Sketch do Projeto de Exemplo

                       Criado em 12 de Junho de 2023
                por Michel Galvão (https://micsg.com.br)

              Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
                            https://www.eletrogate.com/
      Confira mais detalhes deste sketch em https://blog.eletrogate.com/
******************************************************************************/

bool funcaoBasicaEhAtual = true;     // variável indicando se a função básica está ativa (true) ou se a função alternativa está ativa (false)
bool statusLed = LOW;                // controla o estado do LED (HIGH ou LOW)
unsigned long timer = 0;             // Timer para controle de tempo
const uint8_t pinLed = 0;  // Define a constante pinLed como o valor 0, representando o número do pino digital conectado ao LED.

void setup() {
  Serial.begin(115200);  // Inicia a comunicação serial com uma taxa de baud rate de 115200.
  //                    Lembre-se que esta taxa é ignorada, pois a Serial na Raspberry Pi Pico é baseada em USB.
  pinMode(pinLed, OUTPUT);  // Configura o pino do LED como saída
}

void loop() {

  if (funcaoBasicaEhAtual) {  // Verifica se a função básica está ativa

    if (BOOTSEL) {  // Verifica se o botão BOOTSEL está pressionado

      Serial.println("Primeiro clique detectado");    // Informa que foi detectado o primeiro clique
      statusLed = HIGH;                               // Define a variável de controle do estado do LED como HIGH (ligado)
      while (BOOTSEL) {}                              // Aguarda até que o BOOTSEL deixe de ser pressionado
      statusLed = LOW;                                // Define a variável de controle do estado do LED como LOW (desligado)
      timer = millis();                               // Registra o valor atual do tempo em milissegundos, depois de soltar o botão
      while (!BOOTSEL && millis() - timer <= 100) {}  // Aguarda até que o BOOTSEL seja pressionado novamente ou até que tenha
      //                                                  passado 100 milissegundos desde o evento de soltar o botão.

      if (BOOTSEL) {  // Verifica se o botão BOOTSEL está pressionado

        Serial.println("Segundo clique detectado");       // Informa que foi detectado o segundo clique
        while (BOOTSEL) {}                                // Aguarda até que o BOOTSEL deixe de ser pressionado
        funcaoBasicaEhAtual = false;                      // Define a função alternativa como ativa, e desativa a básica (false)
        Serial.println("Troca para função alternativa");  // Informa que está trocando para a função alternativa

      } else {

        Serial.println("Continua na função básica");  // Informa que permanecerá funcionando na função básica
      }

      Serial.println();
    }
  } else {  // Caso contrário, se a função básica não está ativa e sim a alternativa

    if (BOOTSEL) {  // Verifica se o botão BOOTSEL está pressionado

      statusLed = !digitalRead(pinLed);  // Define a variável de controle do estado do LED como o inverso da leitura do valor
      //                                          atual do pino pinLed, ou seja, inverte o estado do LED.
      timer = millis();   // Registra o valor atual do tempo em milissegundos, após o botão estar pressionado
      while (BOOTSEL) {}  // Aguarda até que o BOOTSEL deixe de ser pressionado

      if (millis() - timer >= 1000) {  // Verifica se o tempo decorrido desde o botão estar pressionado é igual ou superior a 1000 milissegundos (1 segundo)

        Serial.println("Troca para função básica");  // Informa que está trocando para a função básica
        funcaoBasicaEhAtual = true;                  // Define a função básica como ativa, e desativa a alternativa (true)
        statusLed = LOW;                             // Define a variável de controle do estado do LED como LOW (desligado)

      } else {  // Caso contrário, se o tempo decorrido desde o botão estar pressionado for menor que 1000 milissegundos, ...

        Serial.println("Continua na função alternativa");  // Informa que permanecerá funcionando na função alternativa
      }
    }
  }
}

void loop1() {
  digitalWrite(pinLed, statusLed);  // Define o estado do LED com base na variável statusLed
  delay(1);                         // Aguarda 1 milissegundo antes de continuar a execução do loop1
}

Explicação do Sketch

No início do sketch, algumas variáveis e constantes são declaradas. A variável booleana funcaoBasicaEhAtual indica se a função básica está ativa (true) ou se a função alternativa está ativa (false). A variável booleana statusLed é responsável por controlar o estado do LED (HIGH ou LOW), indicando se está ligado ou desligado, respectivamente. A variável timer é um temporizador utilizado para controlar o tempo decorrido. Além disso, é definida uma constante pinLed que representa o número do pino digital conectado ao LED que é o pino 0 (zero).

bool funcaoBasicaEhAtual = true;     // variável indicando se a função básica está ativa (true) ou se a função alternativa está ativa (false)
bool statusLed = LOW;                // controla o estado do LED (HIGH ou LOW)
unsigned long timer = 0;             // Timer para controle de tempo
const uint8_t pinLed = 0;  // Define a constante pinLed como o valor 0, representando o número do pino digital conectado ao LED.

A função setup() é responsável pela configuração inicial do sketch. Nesse caso, a função Serial.begin(115200) é chamada para iniciar a comunicação serial com uma taxa de baud rate de 115200. No entanto, é importante destacar que essa taxa é ignorada, pois a comunicação serial na Raspberry Pi Pico é baseada em USB. Em seguida, a função pinMode(pinLed, OUTPUT) é utilizada para configurar o pino do LED como saída.

void setup() {
  Serial.begin(115200);  // Inicia a comunicação serial com uma taxa de baud rate de 115200.
  //                    Lembre-se que esta taxa é ignorada, pois a Serial na Raspberry Pi Pico é baseada em USB.
  pinMode(pinLed, OUTPUT);  // Configura o pino do LED como saída
}

O loop principal do sketch é executado na função loop(). Ele é dividido em duas partes: função básica e função alternativa. Para que o Raspberry Pi Pico saiba qual função executar para o botão, é feita uma verificação na variável funcaoBasicaEhAtual com a estrutura de condição if. Caso possuir o valor true, a função básica é executada. Caso contrário, a função alternativa é executada.

void loop() {

  if (funcaoBasicaEhAtual) {  // Verifica se a função básica está ativa

    // Código aqui que será executado quando a função básica está ativa
    // ...

  } else {  // Caso contrário, se a função básica não está ativa e sim a alternativa

    // Código aqui que será executado quando a função alternativa está ativa
    // ...

  }
}

Na função básica, o sketch verifica se o botão BOOTSEL está pressionado. Se sim, inicia-se o processo de detecção de cliques. Primeiro, é exibida a mensagem "Primeiro clique detectado" utilizando a função Serial.println(). Em seguida, a variável statusLed é definida como HIGH, o que significa que o LED será ligado. O programa entra em um loop while() enquanto o botão BOOTSEL estiver pressionado, aguardando até que seja solto. Após o botão ser solto, a variável statusLed é definida como LOW, desligando o LED. O valor atual do tempo em milissegundos é registrado na variável timer, utilizando a função millis(). Em seguida, o programa entra em outro loop enquanto o botão BOOTSEL não estiver pressionado e o tempo decorrido (obtido pela diferença entre o valor atual de millis() e o valor registrado em timer) for menor ou igual a 100 milissegundos. Esse segundo loop serve para aguardar até que o botão seja pressionado novamente ou até que tenham se passado 100 milissegundos desde o momento em que o botão foi solto. Se após estes 100 milissegundos passarem e o botão não for pressionado, o programa não mudará de função (ele exibirá a mensagem "Continua na função básica" utilizando a função Serial.println()).

Se, dentro desse segundo loop, o botão BOOTSEL for pressionado novamente, é exibida a mensagem "Segundo clique detectado" utilizando a função Serial.println(). O programa entra em um novo loop enquanto o botão BOOTSEL estiver pressionado, aguardando até que seja solto novamente. Após o botão ser solto pela segunda vez, a variável funcaoBasicaEhAtual é definida como false, indicando que a função alternativa está ativa. Uma mensagem informativa "Troca para função alternativa" é exibida utilizando a função Serial.println().

if (BOOTSEL) {  // Verifica se o botão BOOTSEL está pressionado

      Serial.println("Primeiro clique detectado");    // Informa que foi detectado o primeiro clique
      statusLed = HIGH;                               // Define a variável de controle do estado do LED como HIGH (ligado)
      while (BOOTSEL) {}                              // Aguarda até que o BOOTSEL deixe de ser pressionado
      statusLed = LOW;                                // Define a variável de controle do estado do LED como LOW (desligado)
      timer = millis();                               // Registra o valor atual do tempo em milissegundos, depois de soltar o botão
      while (!BOOTSEL && millis() - timer <= 100) {}  // Aguarda até que o BOOTSEL seja pressionado novamente ou até que tenha
      //                                                  passado 100 milissegundos desde o evento de soltar o botão.

      if (BOOTSEL) {  // Verifica se o botão BOOTSEL está pressionado

        Serial.println("Segundo clique detectado");       // Informa que foi detectado o segundo clique
        while (BOOTSEL) {}                                // Aguarda até que o BOOTSEL deixe de ser pressionado
        funcaoBasicaEhAtual = false;                      // Define a função alternativa como ativa, e desativa a básica (false)
        Serial.println("Troca para função alternativa");  // Informa que está trocando para a função alternativa

      } else {

        Serial.println("Continua na função básica");  // Informa que permanecerá funcionando na função básica
      }

      Serial.println();
    }

Na função alternativa, o sketch verifica se o botão BOOTSEL está pressionado. Caso o botão BOOTSEL seja pressionado, o programa executa uma sequência de ações. Primeiro, o estado do LED é invertido utilizando a função digitalRead(pinLed), onde o parâmetro pinLed é o número do pino digital conectado ao LED, e o resultado é armazenado na variável statusLed. Em seguida, o valor atual do tempo em milissegundos é registrado na variável timer. O programa entra em um loop enquanto o botão BOOTSEL não for solto, aguardando até que isso aconteça.

Após o botão BOOTSEL ser solto, o programa verifica se o tempo decorrido desde o momento em que o botão foi pressionado é maior ou igual a 1000 milissegundos (1 segundo). Se essa condição for satisfeita, significa que houve uma troca para a função básica. Nesse caso, é exibida a mensagem "Troca para função básica" utilizando a função Serial.println(). A variável funcaoBasicaEhAtual é definida como true, indicando que a função básica está ativa. Além disso, a variável statusLed é definida como LOW, desligando o LED.

Por outro lado, se o tempo decorrido for menor que 1000 milissegundos, significa que o botão BOOTSEL foi pressionado apenas por um curto período. Nesse caso, o programa continua na função alternativa. A mensagem "Continua na função alternativa" é exibida utilizando a função Serial.println().

if (BOOTSEL) {  // Verifica se o botão BOOTSEL está pressionado

      statusLed = !digitalRead(pinLed);  // Define a variável de controle do estado do LED como o inverso da leitura do valor
      //                                          atual do pino pinLed, ou seja, inverte o estado do LED.
      timer = millis();   // Registra o valor atual do tempo em milissegundos, após o botão estar pressionado
      while (BOOTSEL) {}  // Aguarda até que o BOOTSEL deixe de ser pressionado

      if (millis() - timer >= 1000) {  // Verifica se o tempo decorrido desde o botão estar pressionado é igual ou superior a 1000 milissegundos (1 segundo)

        Serial.println("Troca para função básica");  // Informa que está trocando para a função básica
        funcaoBasicaEhAtual = true;                  // Define a função básica como ativa, e desativa a alternativa (true)
        statusLed = LOW;                             // Define a variável de controle do estado do LED como LOW (desligado)

      } else {  // Caso contrário, se o tempo decorrido desde o botão estar pressionado for menor que 1000 milissegundos, ...

        Serial.println("Continua na função alternativa");  // Informa que permanecerá funcionando na função alternativa
      }
    }

Além do loop principal (loop()), executado no núcleo 1 da placa Raspberry Pi Pico, o sketch também possui uma função chamada loop1(). Essa função é executada no núcleo 2 da placa Raspberry Pi Pico repetidamente (saiba mais sobre dual-core da placa Raspberry Pi Pico no post Raspberry Pi Pico com Arduino IDE + Exemplos). Neste segundo loop, é feita a atualização de estado do LED com base no valor atual da variável statusLed utilizando a função digitalWrite(pinLed, statusLed), onde o parâmetro pinLed é o número do pino digital conectado ao LED e o parâmetro statusLed é a variável que controla o estado do LED (HIGH ou LOW). Em seguida, a função delay(1) é utilizada para aguardar 1 milissegundo antes de continuar a execução do loop.

void loop1() {
  digitalWrite(pinLed, statusLed);  // Define o estado do LED com base na variável statusLed
  delay(1);                         // Aguarda 1 milissegundo antes de continuar a execução do loop1
}

Demonstração do Funcionamento do Projeto

Veja, no vídeo abaixo, a demonstração de funcionamento do projeto:


Conclusão

Aproveite a vantagem de utilizar o botão BOOTSEL da placa Raspberry Pi Pico para o controle de dispositivos, eliminando a necessidade de um botão externo na protoboard. Com a leitura e interpretação do estado do botão BOOTSEL, você poderá implementar diversos projetos facilmente.

Gostaríamos de saber se você curtiu este post! Por favor, avalie e deixe um comentário sobre o conteúdo. E não esqueça de nos seguir no Instagram e nos marcar quando fizer algum projeto nosso: @eletrogate.

Até a próxima!


Sobre o Autor


Michel Galvão

Graduando em Engenharia de Software pela UniCV de Maringá/PR. Tem experiência em Automação Residencial e Agrícola. É um Eletrogater Expert. Tem como Hobby Sistemas Embarcados e IoT. É desenvolvedor de software de Sistemas MicSG. Site: https://micsg.com.br/


Eletrogate

25 de julho de 2023

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!