Projetos

Gerenciador de Autenticação com RaspPi Pico como Teclado USB

Eletrogate 27 de fevereiro de 2024

Introdução

Neste tutorial, exploraremos uma aplicação para a placa Raspberry Pi Pico: utilizar a RP Pico para simular um teclado USB para interação com um computador.

Para isso, será utilizada a biblioteca Keyboard.h, que permite ao RP Pico enviar pressionamentos de tecla para um computador conectado através da porta USB nativa de seu computador. Também será demonstrado como a placa RP Pico pode detectar e interagir com as funções Num Lock, Caps Lock e Scroll Lock de um computador hospedeiro.

Por fim, será demonstrado como criar um Gerenciador de Autenticação com a placa RP Pico. Ao integrar três push buttons à placa Raspberry Pi Pico, será possibilitado a execução de ações específicas no sistema operacional Windows 11 (SO do Computador). Ao pressionar o botão LOGIN, a senha armazenada na memória FLASH da RP Pico será enviada para permitir o acesso à sessão do Windows. Para evitar equívocos causados pelo Caps Lock, será implementada uma função que formata corretamente a senha, independentemente do estado do Caps Lock.

Outras funcionalidades incluirão o botão LOGOUT, que enviará um comando para realizar o logout no Windows, e o botão BLOQUEAR, que permitirá bloquear o sistema, exigindo que o usuário faça login novamente para acessar a sessão atual do Windows.


Materiais Necessários para o Projeto Gerenciador de Autenticação com RaspPi Pico como Teclado USB

Neste post será necessário os seguintes materiais: 


Configurando IDE Arduino para a RP Pico

Para utilizar a placa Raspberry Pi Pico deve-se instalar o núcleo Arduino Pico na IDE Arduino. Para isso, siga os passos abaixo:

  1. Abra a IDE Arduino e navegue no menu Arquivo ➜ Preferências;
  2. Clique no botão de adicionar URL na seção URLs Adicionais para Gerenciador de Placas para adicionar o seguinte URL: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json e em seguida clique em OK para fechar a janela.
  3. Clique no botão OK da janela Preferências para salvar as alterações.

A biblioteca Keyboard.h

Para que a placa Raspberry Pi Pico possa enviar comandos para o computador, é necessário utilizar a biblioteca Keyboard.h. Esta biblioteca permite enviar pressionamentos de tecla para um computador conectado através da porta USB do RP Pico.

Para programar a placa RP Pico, será utilizado o núcleo Arduino Pico desenvolvido por Earle F. Philhower, III (earlephilhower). Este núcleo possui dois stacks USB. As stacks USB são implementações de protocolos e drivers USB que possibilita a comunicação entre dispositivos Arduino e outros dispositivos por meio da interface USB (Universal Serial Bus: Porta Serial Universal). As duas stacks USB existentes no núcleo do Arduino Pico são: Arduino e Adafruit TinyUSB. A stack Arduino (Pico SDK) é a versão mais simples e a stack Adafruit TinyUSB é a mais completa.

A escolha entre essas duas pilhas USB pode ser feita através do menu da Arduino IDE. Para isso,na Arduino IDE, navegue no menu Ferramentas ➜ USB Stack. Então, selecione a opção pico-SDK (stack Arduino) ou a opção Adafruit TinyUSB.

Neste post, estaremos utilizando a Stack Arduino (Pico SDK), pois a mesma fornece as funcionalidades necessárias.

Para a utilização da biblioteca Keyboard.h não é necessário nenhuma instalação de biblioteca, apenas é necessário ter o núcleo Arduino-Pico (de earlephilhower) instalado em sua IDE Arduino. Para instruções de como instalar este núcleo, confira o tópico anterior Configurando IDE Arduino para a RP Pico.

Caracteres ASCII

Nas funções da biblioteca Keyboard.h em que ocorre o pressionamento de tecla, pode-se utilizar caracteres ASCII. Estes caracteres podem ser expressos em um inteiro, em um caractere, em um hexadecimal ou em um binário. Veja na tabela abaixo os caracteres ASCII compatíveis:

*Caracteres ASCII azuis são chamados de caracteres de controle, códigos de controle ou caracteres não imprimíveis; Caracteres ASCII vermelhos são caracteres visíveis/imprimíveis que incluem letras maiúsculas, letras minúsculas, números e pontuação básica; Códigos Hexadecimais devem ser precedidos de ‘0x’; Códigos Binários devem ser precedidos de ‘0b’.

Modificadores de teclado e teclas especiais

Assim como ocorre com os caracteres ASCII, também se pode utilizar pressionamentos de teclas modificadoras de teclado e teclas especiais. Confira abaixo as teclas modificadoras e teclas especiais:

Principais funções da biblioteca Keyboard.h

Nota: Quando a placa RP Pico usa comandos de pressionamentos de tecla, é assumido o controle do teclado de seu Computador. Para controlar quando o pressionamento ocorre, é utilizado o botão físico BOOTSEL da placa RP Pico. Para detalhes de como efetuar a leitura do botão BOOTSEL da placa Raspberry Pi Pico, consulte o artigo BOOTSEL da Raspberry Pi Pico no Arduino IDE daqui do blog Eletrogate:

BOOTSEL da Raspberry Pi Pico no Arduino IDE

  • void begin(const uint8_t *layout = KeyboardLayout_en_US): Inicia a emulação de um teclado conectado a um computador. Esta função inicia o teclado com o layout fornecido. O layout padrão é o teclado em inglês dos Estados Unidos (en_US).
    • Parâmetros da função:
      • layout: representa o layout do teclado. O layout padrão é KeyboardLayout_en_US. a biblioteca oferece suporte aos seguintes layouts de teclado nacionais:
        • KeyboardLayout_da_DK:Dinamarca;
        • KeyboardLayout_de_DE: Alemanha;
        • KeyboardLayout_en_US: EUA;
        • KeyboardLayout_es_ES: Espanha;
        • KeyboardLayout_fr_FR: França;
        • KeyboardLayout_it_IT: Itália;
        • KeyboardLayout_pt_PT: Portugal;
        • KeyboardLayout_sv_SE: Suécia;
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
          Keyboard.print("ESP32!"); // Envia uma série de pressionamentos de caracteres
      
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • void end(void): Encerra a emulação do teclado, desligando a comunicação com o computador ao qual o teclado está conectado.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {}
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
          Keyboard.begin();  // Inicia a comunicação com o teclado
          Keyboard.print("Teste!");  // Envia uma série de pressionamentos de caracteres
          Keyboard.end();  // Finaliza a comunicação com o teclado
      
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • size_t write(uint8_t k): Envia um pressionamento de tecla para um computador conectado. Isso é semelhante a pressionar e soltar uma tecla do teclado. Somente caracteres ASCII presentes no teclado são suportados, além de  modificadores de teclado adicionais e teclas especiais (como CTRL, SHIFT, ALT, entre outras).
    • Parâmetros da função:
    • Retorno: O número de bytes enviados, que será 1 nesta função.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
          Keyboard.write(65);         // pressiona e solta a tecla com código ASCII decimal 65 - letra A
          Keyboard.write(0x42);       // pressiona e solta a tecla com código ASCII hexadecimal 42 - letra B
          Keyboard.write(0b1000011);  // pressiona e solta a tecla com código ASCII binário 1000011 - letra C    
          Keyboard.write('D');        // pressiona e solta a tecla de caractere ASCII 'D'
      
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • size_t write(const uint8_t *buffer, size_t size): Envia uma série de pressionamentos de teclas para um computador conectado. Isso é semelhante a pressionar e soltar sequencialmente várias teclas do teclado. Somente caracteres ASCII presentes no teclado são suportados, além de  modificadores de teclado adicionais e teclas especiais (como CTRL, SHIFT, ALT, entre outras).
    • Parâmetros da função:
      •  buffer: Um ponteiro para o array de bytes (buffer) contendo as teclas a serem pressionadas, com cada byte representando a tecla a ser pressionada e solta. Este byte pode ser um inteiro, um caractere, um hexadecimal ou um binário. Confira a tabela de Caracteres ASCII  e a tabela de Modificadores de teclado e teclas especiais;
      • size: O tamanho do buffer, indicando a quantidade de teclas a serem pressionadas.
    • Retorno: O número de bytes escritos, que será igual a size nesta função.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
          uint8_t keyBuffer[] = { 'c', 'a', 'l', 'c', 'u', 'l', 'a', 'd', 'o', 'r', 'a' , '\n'}; // Define um buffer com as teclas a serem pressionadas
          size_t tamanhoBuffer = sizeof(keyBuffer)/sizeof(uint8_t); // Calcula o tamanho do buffer em bytes
          Keyboard.write(keyBuffer,tamanhoBuffer);         // pressiona e solta cada tecla do buffer keyBuffer formando a palavra 'calculadora' com uma quebra de linha
      
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • size_t press(uint8_t k): Simula a pressionamento (como se uma tecla fosse pressionada e mantida pressionada no teclado) de uma tecla no Computador conectado. Está função é útil ao usar teclas modificadoras (como SHIFT, CTRL, ALT, F1, F2, WIN, TAB, entre outros).
    • Parâmetros da função:
    • Retorno: O número de bytes escritos, que será 1 nesta função.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
      
          //Série de pressionamentos para abrir o Gerenciador de Tarefas do Windows (Ctrl + Shift + Esc)
          Keyboard.press(KEY_LEFT_CTRL);  // pressiona e mantém pressionada a tecla com chave KEY_LEFT_CTRL (tecla Ctrl esquerda)
          Keyboard.press(KEY_LEFT_SHIFT); // pressiona e mantém pressionada a tecla com chave KEY_LEFT_SHIFT (tecla Shift esquerda)
          Keyboard.press(KEY_ESC);        // pressiona e mantém pressionada a tecla com chave KEY_ESC (tecla Esc)
          delay(100); // atraso de 100 milissegundos
          Keyboard.releaseAll(); // solta todas as teclas pressionadas
          
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • size_t release(uint8_t k): Simula a liberação da tecla especificada que estava mantida pressionada no Computador conectado.
    • Parâmetros da função:
    • Retorno: O número de bytes escritos, que será 1 nesta função.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
      
          //Série de pressionamentos para colar a última coisa que estiver na área de trânsferência
          Keyboard.press(KEY_LEFT_CTRL);  // pressiona e mantém pressionada a tecla com chave KEY_LEFT_CTRL (tecla Ctrl esquerda)
          Keyboard.press('v'); // pressiona e mantém pressionada a tecla com caractere ASCII 'v' (tecla v minúsculo)
          delay(100); // atraso de 100 milissegundos
          Keyboard.release(KEY_LEFT_CTRL);   // solta a tecla pressionada especificada
          Keyboard.release('v');  // solta a tecla pressionada especificada
          
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • void releaseAll(void): Simula a liberação de todas as teclas que foram previamente pressionadas no teclado do Computador conectado.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
      
          //Série de pressionamentos para abrir o histórico da área de transferência
          Keyboard.press(KEY_LEFT_GUI);  // pressiona e mantém pressionada a tecla com chave KEY_LEFT_GUI (tecla Windows esquerda)
          Keyboard.press('v'); // pressiona e mantém pressionada a tecla com caractere ASCII 'v' (tecla v minúsculo)
          delay(100); // atraso de 100 milissegundos
          Keyboard.releaseAll();   // solta todas as teclas pressionadas
          
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • size_t consumerPress(uint16_t k): Simula o pressionamento de uma tecla de controle multimídia, como play, pause, volume up, volume down, entre outras, no Computador conectado.
    • Parâmetros da Função:
    • Retorno: O número de bytes escritos, que será 1 nesta função.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
      
          //Série de pressionamentos para deixar o sistema de áudio do Windows Mudo ou 'desmutado'
          Keyboard.consumerPress(KEY_MUTE);  // pressiona e mantém pressionada a tecla com chave KEY_MUTE (tecla mudo)
          delay(100); // atraso de 100 milissegundos
          Keyboard.consumerRelease();   // solta todas as teclas pressionadas de controle de mídia
          
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • size_t consumerRelease(): Simula a liberação das teclas de controle multimídia no Computador conectado.
    • Retorno: O número de bytes escritos, que será 1 nesta função.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
      
          //Série de pressionamentos para pausar/dar play na mídia atual que está tocando no Windows
          Keyboard.consumerPress(KEY_PLAY_PAUSE);  // pressiona e mantém pressionada a tecla com chave KEY_PLAY_PAUSE (tecla play / pause)
          delay(100); // atraso de 100 milissegundos
          Keyboard.consumerRelease();   // solta todas as teclas de controle de mídia pressionadas
          
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • size_t Print::print(const char str[]): Envia um ou mais pressionamentos de tecla para um computador conectado.
    • Parâmetros da Função:
      • str: Um char, int ou uma string representando as teclas a serem enviadas.
    • Retorno: o número de pressionamentos de teclas enviados.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
      
          Keyboard.print("abc");  // Envia uma série de pressionamentos de caracteres
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }
  • size_t Print::println(const char c[]): Envia um ou mais pressionamentos de tecla para um computador conectado, seguido por um pressionamento na tecla Enter.
    • Parâmetros da Função:
      • c: Um char, int ou uma string representando as teclas a serem enviadas.
    • Retorno: o número de pressionamentos de teclas enviados.
    • Exemplo:
      #include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado
      
      void setup() {
        Keyboard.begin();  // Inicia a comunicação com o teclado
      }
      
      void loop() {
      
        if (BOOTSEL) {  // Verifica se o botão BOOTSEL foi pressionado
      
          Keyboard.println("xyz");  // Envia uma série de pressionamentos de caracteres
          while (BOOTSEL) {}  // Aguarda o usuário soltar o botão BOOTSEL
        }
      
        delay(10); // Atraso de 10 milissegundos
      
      }

Detectando status de Num Lock, Caps Lock e Scroll Lock do Computador

Para a placa Raspberry Pi Pico detectar quando o computador estiver com as funções Num Lock, Caps Lock e Scroll Lock ativas é utilizada uma função callback na programação da placa. Esta função é a onLed. Veja abaixo a sintaxe dela:

void onLED(LedCallbackFcn fcn, void *cbData = nullptr):

Ela possui os seguintes parãmetros:

  • fcn: parâmetro do tipo LedCallbackFcn que aceita uma função de callback cuja assinatura é definida como typedef void(*LedCallbackFcn)(bool numlock, bool capslock, bool scrolllock, bool compose, bool kana, void *cbData);. Os parâmetros desta assinatura da callback são:
    • numlock: parâmetro booleano que indica se a função Num Lock está ativa ou não (ao estar ativada trava as funções no teclado numérico do computador).
    • capslock: parâmetro booleano que indica se a função Caps Lock está ativa ou não (ao estar ativada deixa as letras em maiúsculas).
    • scrolllock: parâmetro booleano que indica se a função Scroll Lock está ativa ou não (ao estar ativada bloqueia a rolagem da página).
    • compose: parâmetro booleano que indica o estado da função Compose ou não (ao estar ativada permite a criação de caracteres especiais, veja mais sobre em Compose key – Wikipedia).
    • kana: parâmetro booleano que indica o estado da função Kana ou não (ao estar ativada permite a inserção de caracteres em sistemas de escrita japonesa, veja mais sobre em Japanese input method – Wikipedia).
    • cbData: parãmetro que permite que dados adicionais possam ser passados para a função de callback.
  • cbData : parãmetro OPCIONAL que permite que dados adicionais possam ser passados para a função de callback.

Exemplo de Utilização

Veja o sketch abaixo que exibe no monitor serial o status atual de Num Lock, Caps Lock e Scroll Lock:

#include <Keyboard.h>  // Inclui a biblioteca para o uso da emulação de teclado

bool nl, cl, sl; // variáveis globais que armazenam o status atual de Num Lock, Caps Lock e Scroll Lock

void ledCallBack(bool numlock, bool capslock, bool scrolllock, bool compose, bool kana, void *cbData) {
  nl = numlock;     // Atualiza o status de Num Lock na variável global
  cl = capslock;    // Atualiza o status de Caps Lock na variável global
  sl = scrolllock;  // Atualiza o status de Scroll Lock na variável global
}

void setup() {
  Serial.begin(115200); // configura o monitor serial em 115200 de baudrate
  Keyboard.onLED(ledCallBack); // Associa a função de callback para monitorar os estados das teclas
  Keyboard.begin();  // Inicia a comunicação com o teclado
}

void loop() {
  Serial.println("--------------------------------------------\n");
  Serial.println("Num Lock \t Caps Lock \t Scroll Lock");
  Serial.print("   ");
  Serial.print(nl);   // Exibe o status atual de Num Lock
  Serial.print("\t\t     ");
  Serial.print(cl);   // Exibe o status atual de Caps Lock
  Serial.print("\t\t      ");
  Serial.println(sl); // Exibe o status atual de Scroll Lock
  Serial.println("--------------------------------------------\n");
  delay(100);
}

Ao sketch ser executado na placa RP Pico e ao ser apertada  as teclas Num Lock, Caps Lock ou Scroll Lock do computador, o monitor serial exibe:


Formatando Strings garantindo consistência independente do Caps Lock

Quando uma String é passada como parâmetro para a função print da biblioteca Keyboard.h e o computador de destino estiver com a função Caps Lock ativada, a String pode ser recebida incorretamente. Veja o exemplo:

#include <Keyboard.h>   // Inclui a biblioteca para o uso da emulação de teclado

void setup() {
  Keyboard.begin();  // Inicia a comunicação com o teclado

}

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

    Keyboard.println("ABCdef"); // Envia uma série de pressionamentos de caracteres
    while (BOOTSEL);// Aguarda o usuário soltar o botão BOOTSEL
  }
  delay(10); // atraso de 10 milissegundos no loop principal
}

Neste código anterior, ao pressionar o botão BOOTSEL seria enviado para o computador:

  • Computador com a função Caps Lock desativada: ABCdef
  • Computador com a função Caps Lock ativada: abcDEF

Como visto, quando a função Caps Lock estiver ativada, os caracteres maiúsculos são recebidos como minúsculos e os caracteres minúsculos são recebidos como maiúsculos.

Para resolver isso, basta formatar adequadamente a string antes de enviar para o computador de acordo com o status da função Caps Lock no computador. Para formatar a string, foi criada a função formatarString:

// Função para formatar a string, levando em consideração o estado do Caps Lock .
// Lógica de funcionamento:
//  - se a função Caps Lock do computador estiver acionada: 
//      -> os caracteres da string que forem maiúsculos serão transformados em minúsculos,
//         e os caracteres da string que forem minúsculos serão transformados em maiúsculos;
//
//  - se a função Caps Lock do computador NÃO estiver acionada:
//      -> os caracteres da string que forem maiúsculos permanecerão maiúsculos, 
//         e os caracteres da string que forem minúsculos permanecerão minúsculos.
//
String formatarString(String string, bool capsLockAtivo) {
  if (capsLockAtivo) { // Verifica se o Caps Lock está ativado
    
    for (int i = 0; i < string.length(); i++) { // Percorre cada caractere da string
      
      if (isAlpha(string[i])) { // Verifica se o caractere é uma letra
        
        if (isUpperCase(string[i])) { // Verifica se o caractere é maiúsculo
          string[i] = toLowerCase(string[i]); // Converte para minúsculo
        } else { // se não, caso o caractere seja minúsculo, ...
          string[i] = toUpperCase(string[i]); // Converte para maiúsculo
        }
        
      }
      
    }
    
  }
  return string; // Retorna a string formatada
}

Para utilizar esta função no sketch, basta informar nos parâmetros a string a ser formatada e o status da função Caps Lock no computador. Veja o Exemplo:

#include <Keyboard.h>   // Inclui a biblioteca para o uso da emulação de teclado

bool cl; // variável global que armazena o status atual de Caps Lock

void ledCallBack(bool numlock, bool capslock, bool scrolllock, bool compose, bool kana, void *cbData) {
  cl = capslock;    // Atualiza o status de Caps Lock na variável global
}

void setup() {
  Keyboard.onLED(ledCallBack); // Associa a função de callback para monitorar os estados das teclas
  Keyboard.begin();  // Inicia a comunicação com o teclado
}

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

    Keyboard.println(formatarString("ABCdef", cl)); // Envia uma série de pressionamentos de caracteres, com a string sendo formatada adequadamente
    while (BOOTSEL);// Aguarda o usuário soltar o botão BOOTSEL
  }
  delay(10); // atraso de 10 milissegundos no loop principal
}

// Função para formatar a string, levando em consideração o estado do Caps Lock .
// Lógica de funcionamento:
//  - se a função Caps Lock do computador estiver acionada: 
//      -> os caracteres da string que forem maiúsculos serão transformados em minúsculos,
//         e os caracteres da string que forem minúsculos serão transformados em maiúsculos;
//
//  - se a função Caps Lock do computador NÃO estiver acionada:
//      -> os caracteres da string que forem maiúsculos permanecerão maiúsculos, 
//         e os caracteres da string que forem minúsculos permanecerão minúsculos.
//
String formatarString(String string, bool capsLockAtivo) {
  if (capsLockAtivo) { // Verifica se o Caps Lock está ativado
    
    for (int i = 0; i < string.length(); i++) { // Percorre cada caractere da string
      
      if (isAlpha(string[i])) { // Verifica se o caractere é uma letra
        
        if (isUpperCase(string[i])) { // Verifica se o caractere é maiúsculo
          string[i] = toLowerCase(string[i]); // Converte para minúsculo
        } else { // se não, caso o caractere seja minúsculo, ...
          string[i] = toUpperCase(string[i]); // Converte para maiúsculo
        }
        
      }
      
    }
    
  }
  return string; // Retorna a string formatada
}

Agora, neste código, ao pressionar o botão BOOTSEL seria enviado para o computador:

  • Computador com a função Caps Lock desativada: ABCdef
  • Computador com a função Caps Lock ativada: ABCdef

Como visto, não existe mais o problema anterior de “inversão” maiúsculo/minúsculo.


Projeto: Gerenciador de Autenticação com RP Pico

Agora será mostrado como criar um Gerenciador de Autenticação com a placa Raspberry Pi Pico. Ao se clicar em um dos três pushbuttons conectados à placa RP Pico, será enviado ao computador com Sistema Operacional Windows 11 uma determinada ação:

  • Ao clicar no push button LOGIN: será enviado ao PC a senha armazenada na memória FLASH para que se possa fazer login e entrar em uma sessão no Windows. Para evitar erros de senha errada quando o PC estiver com a função Caps Lock ativada, será utilizada uma função que formata corretamente a senha (String) independentemente de a função Caps Lock estiver ativada ou não;
  • Ao clicar no push button LOGOUT: será enviado ao PC um comando para que se possa fazer logout e sair da sessão do Windows;
  • Ao clicar no push button BLOQUEAR: será enviado ao PC um comando para que se possa bloquear o Windows e que o mesmo possa requisitar ao usuário fazer login novamente para entrar na sessão atual do Windows.

Esquemático do Projeto

Monte o circuito do projeto de acordo com o seguinte esquemático:


Sketch do Projeto

Clique aqui para fazer o download do sketch completo.

Antes de prosseguir, modifique o valor das seguintes variáveis no sketch:

  • Arquivo credencial.h
    • senhaWindows: substitua SENHA_WINDOWS pela sua senha de autenticação no Windows 11;

Em seguida, faça o upload do sketch para a placa RP Pico.

Veja abaixo o sketch (contendo os arquivos gerenciador_autenticacao_rp_pico.ino e credencial.h):

/******************************************************************************
                      Simule um Teclado com Raspberry Pi Pico
                                          +
                      Gerenciador de Autenticação com RP Pico

                        Criado em 05 de Fevereiro de 2024
                     por Michel Galvão (https://micsg.com.br)

                              Sketch Principal

              Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
                          https://www.eletrogate.com/
******************************************************************************/

#include <Keyboard.h>   // Inclui a biblioteca para o uso da emulação de teclado
#include "credencial.h" // Inclui o arquivo credencial.h que armazena a senha do Computador Windows

#define pinLogin 17 // Define o pino do botão para acionar a função login
#define pinLogout 16// Define o pino do botão para acionar a função logout
#define pinBloquear 15// Define o pino do botão para acionar a função bloqueio

bool cl; // variável global que armazena o status atual de Caps Lock

void ledCallBack(bool numlock, bool capslock, bool scrolllock, bool compose, bool kana, void *cbData) {
  cl = capslock;    // Atualiza o status de Caps Lock na variável global
}

void setup() {
  Serial.begin(115200); // configura o monitor serial em 115200 de baudrate
  Keyboard.onLED(ledCallBack); // Associa a função de callback para monitorar os estados das teclas
  Keyboard.begin();  // Inicia a comunicação com o teclado
  pinMode(pinLogin, INPUT_PULLUP);// Configura o pino do botão login como entrada com resistor de pull-up interno
  pinMode(pinLogout, INPUT_PULLUP);// Configura o pino do botão logout como entrada com resistor de pull-up interno
  pinMode(pinBloquear, INPUT_PULLUP);// Configura o pino do botão bloqueio como entrada com resistor de pull-up interno
}

void loop() {

  if (!digitalRead(pinLogin)) { // se a leitura do botão login for igual à 0 (LOW), ...

    // Estrutura de controle para esperar o usuário soltar o botão Login
    do {  // Início do bloco 'do'
      delay(100); // Aguarda por 100 milissegundos
    }
    while (!digitalRead(pinLogin));// Continua repetindo o loop enquanto o botão de login estiver em nível lógico baixo (pressionado)

    Serial.println("Login acionado"); // Informa pelo Monitor Serial que a Função Login será executada
    Keyboard.write(KEY_RETURN); // pressiona e solta a tecla ENTER
    delay(500); // pausa o programa por 350 milissegundos
    Keyboard.print(formatarString(senhaWindows, cl)); // Envia uma série de pressionamentos dos caracteres da variável contendo a senha de login do Windows 11, com a senha sendo formatada adequadamente
    delay(150); // pausa o programa por 150 milissegundos
    Keyboard.write(KEY_RETURN); // pressiona e solta a tecla ENTER
  }

  if (!digitalRead(pinLogout)) { // se a leitura do botão logout for igual à 0 (LOW), ...

    // Estrutura de controle para esperar o usuário soltar o botão Logout
    do {  // Início do bloco 'do'
      delay(100); // Aguarda por 100 milissegundos
    }
    while (!digitalRead(pinLogout));  // Continua repetindo o loop enquanto o botão de login estiver em nível lógico baixo (pressionado)

    Serial.println("Logout acionado"); // Informa pelo Monitor Serial que a Função Logout será executada
    Keyboard.press(KEY_LEFT_CTRL);  // pressiona e mantém pressionada a tecla com chave KEY_LEFT_CTRL (tecla Ctrl esquerda)
    Keyboard.press(KEY_LEFT_ALT); // pressiona e mantém pressionada a tecla com chave KEY_LEFT_ALT (tecla Alt)
    Keyboard.press(KEY_DELETE); // pressiona e mantém pressionada a tecla com chave KEY_DELETE (tecla Delete)
    Keyboard.releaseAll();  // solta todas as teclas pressionadas
    delay(500); // pausa o programa por 350 milissegundos
    Keyboard.write(KEY_LEFT_ALT); // pressiona e solta a tecla ALT
    delay(150); // pausa o programa por 100 milissegundos
    Keyboard.write('s');  // pressiona e solta a tecla de caractere ASCII 's' (para fazer logout no Windows 11)
  }

  if (!digitalRead(pinBloquear)) { // se a leitura do botão bloquear for igual à 0 (LOW), ...

    // Estrutura de controle para esperar o usuário soltar o botão Bloquear
    do {  // Início do bloco 'do'
      delay(100); // Aguarda por 100 milissegundos
    }
    while (!digitalRead(pinBloquear));  // Continua repetindo o loop enquanto o botão de bloqueio estiver em nível lógico baixo (pressionado)

    Serial.println("Bloqueio acionado"); // Informa pelo Monitor Serial que a Função Bloquear será executada
    Keyboard.press(KEY_LEFT_CTRL);  // pressiona e mantém pressionada a tecla com chave KEY_LEFT_CTRL (tecla Ctrl esquerda)
    Keyboard.press(KEY_LEFT_ALT); // pressiona e mantém pressionada a tecla com chave KEY_LEFT_ALT (tecla Alt)
    Keyboard.press(KEY_DELETE); // pressiona e mantém pressionada a tecla com chave KEY_DELETE (tecla Delete)
    Keyboard.releaseAll();  // solta todas as teclas pressionadas
    delay(500); // pausa o programa por 350 milissegundos
    Keyboard.write(KEY_LEFT_ALT); // pressiona e solta a tecla ALT
    delay(150); // pausa o programa por 100 milissegundos
    Keyboard.write('b');  // pressiona e solta a tecla de caractere ASCII 'b' (para bloquear o Windows 11)
  }

  delay(10); // atraso de 10 milissegundos no loop principal
}

// Função para formatar a string, levando em consideração o estado do Caps Lock .
// Lógica de funcionamento:
//  - se a função Caps Lock do computador estiver acionada: 
//      -> os caracteres da string que forem maiúsculos serão transformados em minúsculos,
//         e os caracteres da string que forem minúsculos serão transformados em maiúsculos;
//
//  - se a função Caps Lock do computador NÃO estiver acionada:
//      -> os caracteres da string que forem maiúsculos permanecerão maiúsculos, 
//         e os caracteres da string que forem minúsculos permanecerão minúsculos.
//
String formatarString(String string, bool capsLockAtivo) {
  if (capsLockAtivo) { // Verifica se o Caps Lock está ativado
    
    for (int i = 0; i < string.length(); i++) { // Percorre cada caractere da string
      
      if (isAlpha(string[i])) { // Verifica se o caractere é uma letra
        
        if (isUpperCase(string[i])) { // Verifica se o caractere é maiúsculo
          string[i] = toLowerCase(string[i]); // Converte para minúsculo
        } else { // se não, caso o caractere seja minúsculo, ...
          string[i] = toUpperCase(string[i]); // Converte para maiúsculo
        }
        
      }
      
    }
    
  }
  return string; // Retorna a string formatada
}
/******************************************************************************
                      Simule um Teclado com Raspberry Pi Pico
                                          +
                      Gerenciador de Autenticação com RP Pico

                        Criado em 05 de Fevereiro de 2024
                     por Michel Galvão (https://micsg.com.br)

                                    Credencial

              Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
                          https://www.eletrogate.com/
******************************************************************************/

const char *senhaWindows = "SENHA_WINDOWS"; // variável que armazena a senha de autenticação do Windows

Explicação do Funcionamento

Aqui está um resumo explicativo com base no código:

  1. Bibliotecas e Definições:
    • O código utiliza a biblioteca Keyboard.h para emular um teclado.
    • Há uma inclusão do arquivo credencial.h para obter a senha do Windows.
  2. Definição de Pinos:
    • pinLogin, pinLogout, e pinBloquear são definidos como os pinos dos botões para acionar as funções de login, logout e bloqueio, respectivamente.
  3. Configuração Inicial no Setup:
    • Inicialização da comunicação serial.
    • Inicialização da emulação do teclado.
    • Configuração dos pinos dos botões como entradas com resistor de pull-up interno.
  4. Loop Principal:
    • O loop principal contém três blocos condicionais para cada função: login, logout e bloqueio.
    • Cada bloco verifica se o botão correspondente foi pressionado e, em seguida, executa as ações associadas.
  5. Ações de Login, Logout e Bloqueio:
    • A ação de login envolve pressionar Enter, enviar a senha do Windows (devidamente formatada com a função formatarString, por causa do problema de “inversão” de caractere Maiúsculo/minúsculo) e, em seguida, pressionar Enter novamente.
    • A ação de logout envolve pressionar simultaneamente as teclas Ctrl, Alt e Delete, seguido pelo pressionamento da tecla Alt e o envio da tecla ‘s’ para fazer o logout no Windows.
    • A ação de bloqueio é semelhante ao logout, mas envia a tecla ‘b’ para bloquear o Windows.
  6. Arquivo credencial.h:
    • O arquivo contém a definição da senha do Windows, armazenada na variável senhaWindows.

Demonstração de Funcionamento do Projeto

Veja no vídeo abaixo o funcionamento do projeto:


Conclusão

Em um passo além do projeto original, pode-se utilizar um Módulo Leitor Biométrico (Impressão Digital) – DY50 ou um Kit Módulo RFID Mfrc522 13.56 Mhz para aprimorar ainda mais a autenticação no Windows e permitir ao usuário que, facilmente, possa se autenticar utilizando sua digital, um cartão RFID ou um chaveiro RFID.

Curtiu o post? Avalie e deixe um comentário! Siga-nos também no Instagram e nos marque 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

27 de fevereiro de 2024

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!