Rodando Testes Unitários com Wine no Linux

Muitas vezes desejamos compilar e executar testes unitários para a plataforma Windows. No entanto, reiniciar sua máquina e dar boot no Windows só para testar fica meio na contramão.

Por isso podemos instalar o MinGW e o Wine, para podermos compilar para a plataforma Windows e para executar os testes unitários, respectivamente, sem sair do conforto do Linux.

Para instalar o MinGW e o Wine:

# MinGW - Windows 32-bit:
sudo apt install gcc-mingw-w64-i686 g++-mingw-w64-i686

# MinGW - Windows 64-bit:
sudo apt install gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64

# Wine
sudo apt install wine

Para compilar para a plataforma Windows usamos os compiladores MinGW para C ou C++, para 32-bit ou 64-bit.

Compilador \ Windows32-bit64-bit
C (GCC)i686-w64-mingw32-gccx86_64-w64-mingw32-gcc
C++ (G++)i686-w64-mingw32-g++x86_64-w64-mingw32-g++
Compiladores MinGW para C/C++ em 32-bit ou 64-bit.

Por exemplo, para compilar os arquivos test_unit.c e unit.c e para Windows 32-bit usamos:

i686-w64-mingw32-gcc test_unit.c unit.c -o test.exe

Então, para rodar o executável do próprio Linux, usando o Wine, usamos:

wine test.exe

Conclusão

Podemos executar testes unitários da plataforma Windows (32-bit ou 64-bit), sem sair do Linux, usando o Wine.

Para rodar um executável qualquer com o Wine, no entanto, pode ser necessário fornecer DLLs (bibliotecas). Porém, a princípio, um teste unitário não deve depender de nada externo e, portanto, não deveria cair neste problema, pois todas as dependências devem ser fakes (falsos) ou mocks (imitadores).

Se o seu teste PRECISA de alguma DLL, ele provavelmente é um teste de integração, não um teste unitário.

Qual é motivo do reset do microcontrolador STM32F

No post anterior vê-se como configurar os dois Watchdogs do microcontrolador STM32F.

Porém, é possível saber qual o motivo que causou o reset? O microcontrolador ligou? Foi pressionado o botão de reset? Ou foi algum dos Watchdogs?

A resposta é: sim, é possível determinar a causa do reset.

Para determinar o motivo do reset, basta avaliar o valor lido no registrador RCC_CSR. Os seis bits mais significativos são utilizados para determinar a fonte do reset:

BitValorMotivo
310x80000000Low-power reset
Reset de baixo-consumo
300x40000000Window watchdog
Watchdog de janela
290x20000000Independent watchdog
Watchdog independente
280x10000000Software reset
Reset por software
270x08000000POR/PDR reset
Reset de energização
260x04000000Pin reset
Reset pelo pino

Abaixo segue o código que lê a fonte do reset, limpa o registrador e determina a fonte, executando blocos de acordo. Com isso é possível determinar tomar ações diferenciadas para cada um dos casos. Note que é usada a operação lógica & (E bit-a-bit).

Para obter apenas o motivo do último reset é necessário realizar a limpeza do registrador. Isso é necessário porque quando ocorre um reset, os bits do registrador não são limpados para zero, mas apenas setados para um, fazendo-os acumular cada uma das fontes de reset que ocorreram.

  uint32_t reset_source = 0;

  // Lê a fonte do reset
  reset_source = RCC->CSR;

  // Limpa o registrador
  __HAL_RCC_CLEAR_RESET_FLAGS();

  if (reset_source & RCC_CSR_LPWRRSTF) {
    // 0x80000000 Low-power reset
  }

  if (reset_source & RCC_CSR_WWDGRSTF) {
    // 0x40000000 Window watchdog reset
  }

  if (reset_source & RCC_CSR_IWDGRSTF) {
    // 0x20000000 Independent watchdog reset
  }

  if (reset_source & RCC_CSR_SFTRSTF) {
    // 0x10000000 Software reset
  }

  if (reset_source & RCC_CSR_PORRSTF) {
    // 0x08000000 POR/PDR reset
  }

  if (reset_source & RCC_CSR_PINRSTF) {
    // 0x04000000 Pin reset
  }

Veja abaixo dois casos:

  • Quando o debugger grava e inicia o debug do programa, obtém-se o valor 0x14000000, indicando que houve dois resets:
    • Reset por software; e
    • Reset pelo pino.
  • Quando o reset é causado ao pressionar o pino de reset, obtém-se o valor 0x04000000.

Conclusão

Portanto, podemos verificar qual a fonte de um reset, ou seja, qual o seu motivo. Dessa forma, podemos determinar se houve um reset por algum Watchdog, pino, energização, etc. Com isso podemos ter uma dica do que pode ter acontecido logo antes do reset.

Os Dois Watchdogs do microcontrolador STM32F

No post anterior foi visto como configurar o clock do microcontrolador STM32F para rodar a 72 MHz, baseado no cristal externo.

Neste post serão explicados pra que são e como configurar os dois Watchdogs do STM32F (IWDG e WWDG).

O que é um Watchdog

O Watchdog (cão de guarda) é um periférico interno do microcontrolador que serve para identificar mal funcionamento do programa e reiniciar o sistema a partir de um reset.

É preciso recarregar/alimentar (reload/feed) periodicamente o Watchdog para que ele não cause um reset.

Então, quando um programa entra em um laço infinito ou entra em algum estado inconsistente que evita que o Watchdog seja adequadamente recarregado, o sistema vai resetar em um tempo máximo estipulado pela configuração do Watchdog.

IWDG – Independent Watchdog

O IWDG (Watchdog Independente) é chamado assim porque ele utiliza uma fonte de clock independente do clock do sistema.

Para habilitar o IWDG:

  • Abra o arquivo IOC do projeto, entrar em “Pinout & Configuration > IWDG” e então habilitá-lo em “Activated”.
  • O valor do prescaler (divisor de clock) e o valor de reload (recarregamento) podem ser configurados para se obter um tempo máximo de reset com precisão.
  • Note que o clock usado pelo IWDG vem de um oscilador RC interno de 40 kHz, o que pode ser visto em “Clock Configuration”.

O IWDG pode ser recarregado a qualquer momento, independente do valor do seu contador. Ele vai causar um reset quando seu contador chegar até zero.

Com um clock de 40 kHz, prescaler igual a 16 e valor de recarregamento 4095, o IWDG causará um reset em de 1,6 segundos.

T = 16*(4095+1)/40000

Para evitar isso, chamamos a função que reseta o IWDG no fim do laço while(1){…}, o qual demora 1.0 segundo para ser executado:

int main(void) {
  // ...
  while (1)
  {
    // ...

    // Recarrega o IWDG
    HAL_IWDG_Refresh(&hiwdg);

    // ...
  }
   // ...
}

Note que, para poder debugar o microcontrolador com o IWDG ativo, é necessário configurá-lo para que pare a contagem quando o processador pára em um breakpoint.

Na função MX_IWDG_Init() adicione as linhas a seguir:

static void MX_IWDG_Init(void)
{
  // ...

  // Necessário para parar o IWDG quando
  // o programa para em um breakpoint
  __HAL_DBGMCU_FREEZE_IWDG();

  // ...
}

WWDG – Window Watchdog

O WWDG (Watchdog de Janela) é chamado assim porque com ele é possível configurar uma janela de tempo onde ele deve ser recarregado, de forma a não gerar um reset do microcontrolador. Se recarregar antes do tempo ou depois do tempo o reset ocorre.

Em outras palavras, o WWDG pode ser configurado para exigir um tempo mínimo e máximo entre os recarregamentos, causando um reset caso seja recarregado antes do tempo mínimo ou caso não seja recarregado no tempo máximo.

Este Watchdog funciona de forma um pouco diferente:

  • Você configura um valor de recarregamento, por exemplo 127 (0x7F) a partir do qual o contador é decrementado. Se o valor do contador é reduzido a 63 (0x3F) ocorre o reset.
  • Você também configura um valor de janela, o valor a partir do qual é permitido recarregar o WWDG. Se, quando recarregado, o contador é maior que este valor, o WWDG causa um reset. Se é menor ou igual, o WWDG é recarregado com sucesso, sem gerar um reset.
    • Se você configurar este valor como sendo igual ao valor de recarregamento, o WWDG funciona como um Watchgod normal, sem o tempo mínimo.
  • Você pode habilitar a interrução do WWDG, que ocorre quando o o valor do contador é reduzido a 64 (0x40).

Para habilitar o WWDG:

  • Abra o arquivo IOC do projeto, entrar em “Pinout & Configuration > WWDG” e então habilitá-lo em “Activated”.
  • Configure em “prescaler” o valor do divisor de clock.
  • Configure em “window value” o valor da janela (início da janela).
  • Configure em “down-counter” o valor do recarregamento (início da contagem).
  • Habilite a interrupção do WWDG e, na aba “NVIC Settings” habilite a interrupção.

Com um clock de 72 MHz, portanto 36 MHz em APB1, prescaler igual a 8 e valor de recarregamento igual a 127, o WWDG causará um reset em de 58,2 milissegundos após o o recarregamento anterior.

T1 = 8*4096*(127-63)/36000000

Com o valor da janela igual a 64, a janela de recarregamento inicia em 57,3 milissegundos após o recarregamento anterior.

T2 = 8*4096*(127-64)/36000000

Dessa forma há uma janela de tempo de 0,091 milissegundos para que o recarregamento ocorra: depois de T2 e antes de T1.

ΔT = T1-T2 = 8*4096*(64-63)/36000000

+--------------------------+
| Recarrega          Reset |
| |-------------|----|     |
| T0            T2   T1    |
|                <-->      |
|                Janela    |
+--------------------------+

Para evitar isso, no arquivo main.c (ou em qualquer arquivo) criamos a função HAL_WWDG_EarlyWakeupCallback(), a qual chama a função de reset do WWDG.

Essa função criada é automaticamente chamada pelo tratamento da interrupção do WWDG.

void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg) {
  // Recarrega o WWDG
  HAL_WWDG_Refresh(hwwdg);
}

De forma semelhante ao IWDG, para que seja possível debugar o microcontrolador com o WWDG ativo, é necessário configurá-lo para que pare a contagem quando a execução é pausada em um breakpoint.

Na função MX_WWDG_Init() adicione as linhas a seguir:

static void MX_WWDG_Init(void)
{
  // ...

  // Necessário para parar o WWDG quando
  // o programa para em um breakpoint
  __HAL_DBGMCU_FREEZE_WWDG();

  // ...
}

Por que dois Watchdogs?

Você pode se perguntar porque seriam necessários dois Watchdogs?

Caso o clock principal do sistema falhe, o que pode acontecer por exemplo escrevendo indevidamente em certos registradores, o WWDG vai parar de contar e, portanto, não causará reset. Nesse caso o IWDG entrará em ação, reiniciando o microcontrolador.

Além disso, como foi feito neste exemplo, ambos podem cuidar de dois escopos diferentes:

  • IWDG pode ser recarregado a cada 1 segundo, garantindo que o laço de execução principal está sendo executado; e
  • WWDG pode ser recarregado pela sua interrupção, garantindo que as interrupções não ficam desabilitadas por períodos muito longos.

Conclusão

Assim, com o uso de ambos os Watchdogs do STM32F, podemos obter uma garantia de que o programa está operando de forma correta: executando todas as suas tarefas e mantendo interrupções habilitadas.

Configurando Clock do microcontrolador STM32F

No post anterior foi explicado como configurar um projeto no STM32CubeIDE e debugar o microcontrolador STM32F usando o debugger ST-LINK V2.

Neste post será visto como configurar o clock do microcontrolador para rodar com uma frequência de 72 MHz, utilizando o cristal de 8MHz que vem na placa de desenvolvimento. Na foto este cristal está logo à direita do microcontrolador.

Por padrão o clock do microcontrolador STM32F vem de um oscilador RC interno de 8MHz, calibrado em fábrica. Porém, de acordo com o datasheet, sua frequência pode variar até 2,5 %, o que facilmente se torna problemático em sistemas que fazem uso de comunicação ou de alguma outra forma dependem da precisão do tempo.

Um cristal, por sua vez possui uma variação muito menor na frequência de operação, por exemplo 100 ppm, ou seja, 0,01 %.

Configurando o clock para 72 MHz

Para habilitar e configurar o cristal oscilador:

  • Abra o arquivo IOC do projeto.
  • Habilite o cristal: entre em “Pinout & Configuration > System Core > RCC” e habilite o cristal “Crystal/Ceramic Resonator”.
    • Se necessário, ajuste a tensão de operação, que neste caso é 3,3 V.
  • Configure o clock: entre em “Clock Configuration” e ajuste:
    • “PLL Source Mux” para HSE.
    • “PLL Mul” para x9 (multiplica 8 MHz do cristal por 9).
    • “System Clock Mux” para PLLCLK.
    • “APB1 Prescaler para /2 (divide o clock de 72 MHz por 2).
  • Caso alguma configuração do clock estiver inadequada (fora da especificação do fabricante), os pontos onde há erros são destacados pela interface gráfica.
  • Agora basta salvar e confirmar que quer gerar o código.

Comparando os clocks de 8 MHz e de 72 MHz

Para comparar o clock anterior com o atual, pode-se usar o bloco de código abaixo, colocado logo antes do laço while(1){…}.

  // Conta quanto incrementos faz em 1 segundo
  // Clock 72 MHz: Cristal externo
  {
    uint32_t wait = 1000; // 1 segundo
    uint32_t count = 0;
    uint32_t tickstart = HAL_GetTick();

    while ((HAL_GetTick() - tickstart) < wait)
    {
        count++;
    }

    count = 0;
  }

Este código conta quantas vezes a variável count é incrementada enquanto se espera o delay de 1 segundo. O código foi adaptado da função HAL_Delay().

Executando para cada uma das configurações temos o seguinte resultado:

OsciladorFrequênciaIncrementosProporção
RC interno8 MHz274.0301 x
Cristal externo72 MHz1.892.9686.9 x

Dessa forma, rodando a 72 MHz o contador é incrementado 6.9 vezes mais rápido do que quando rodando a 8 MHz. Revelando um ganho em velocidade, porém menor do que as 9.0 vezes que o clock foi acelerado.

Conclusão

Dessa forma, configurando o clock em 72 MHz usando o cristal externo de 8 MHz, podemos acelerar o processamento e aumentar a precisão temporal do microcontrolador.

Portanto, podemos fazer o LED piscar com mais precisão e fazer mais trabalho entre cada uma das piscadas.

Debugando microcontrolador STM32F

Esta semana chegou em minhas mãos uma pequena placa de desenvolvimento e o seu gravador.

Esta placa possui o microcontrolador STM32F103C8T6, um ARM Cortex-M3 que roda em até 72 MHz, com 64 kB de flash (programa), 20 kB de RAM (variáveis) e diversas interfaces de comunicação.

Criando um projeto

Para programar vamos usar a STM32CubeIDE, uma IDE focada em microcontroladores fornecida pela STM. Vamos criar o projeto:

  • Crie o projeto
    • Menu File > New > STM32 Project.
    • Menu Arquivo > Novo > Projeto STM 32.
  • Em “Part Number” (à esquerda) digite o código do microcontrolador: STM32F103C8. Selecione na lista e avance (Next).
  • Dê um nome ao projeto: Blink (piscar). Finalize (Finish).
  • Abrirá este arquivo Blink.ioc, que contém as configurações do microcontrolador para geração automática do código do hardware.
  • Configure a interface de debug: à esquerda entre em SYS, selecione “Serial Wire” em Debug e selecione “SysTick” em Timebase Source (fonte de temporização). Note que dois pinos são marcados em verde, pois são usados pela interface de debug.
  • Na placa, o LED é indicado como PC13, então configuramos este como saída (GPIO_Output).
  • Agora basta salvar e confirmar que deseja gerar o código.

Programando para o LED piscar

Para fazer o LED piscar é muito simples. Usamos duas funções:

  • HAL_GPIO_WritePin(), para alterar o valor o IO que controla o LED; e
  • HAL_Delay(), para aguardar o tempo passar.

O arquivo com a função main() se encontra em Core/Src/main.c. Este é um dos arquivos gerados automaticamente pela IDE.

Note que você pode modificar o código entre os comentários de início (USER CODE BEGIN XXX) e fim (USER CODE END XXX).

A IDE usa estes comentários para saber onde inserir o código gerado automaticamente. O que estiver fora das regiões permitidas será eliminado na próxima vez que o arquivo Blink.ioc for editado e o código for gerado novamente.

Encontre o laço infinito [while(1){…}] e adicione as chamadas de função para aguardar (delay) e escrever no pino (write pin).

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    // Desligar LED
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
    HAL_Delay(500);

    // Ligar LED
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
    HAL_Delay(500);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

Configurando o debug

Para configurar o debug basta seguir os passos abaixo:

  • Primeiro é necessário compilar o projeto.
    • Menu Project > Build Project
    • Menu Projeto > Compilar Projeto
  • Abra as configurações de debug
    • Menu Run > Debug Configurations…
    • Menu Executar > Configurações de Debug/Depuração…
  • Selecione a opção “STM32 Cortex-M C/C++ Application”, clique nela com o botão contrário e crie uma nova configuração (New Configuration).
  • A configuração de debug deve se parecer com as duas da imagem. Abas: Main e Debugger.
  • Inicie o debugger clicando em “Debug”.
  • O programa vai parar no início da função main().
  • Então é possível usar:
    • “Step Into (F5)” para prosseguir passo a passo, entrando nas funções chamadas;
    • “Step Over (F6)” para prosseguir passo a passo, sem entrar nas funções chamadas;
    • “Step Return (F7)” para parar no retorno da função atual; e
    • “Resume (F8)” para continuar, parando no próximo breakpoint.
  • Para criar breakpoints basta dar um clique duplo no número da linha.

Conclusão

Agora é só se divertir com a placa de desenvolvimento: configure os pinos como entrada ou saída, escreva e leia eles.

Como Revisar seu Próprio Texto

Quando escrevemos um texto, é comum que o modifiquemos constantemente, até que as diversas estruturas estejam adequadas.

Primeiramente temos a estrutura do texto: os capítulos e as seções. Estes devem seguir uma hierarquia e uma ordem lógica, preferencialmente sequencial.

Em segundo lugar temos a estrutura dos parágrafos, dentro dos capítulos e das seções. Estes devem ser organizados de forma lógica e estritamente sequencial.

Em terceiro lugar temos a estrutura das frases dentro dos parágrafos. Neste ponto, expressamos uma ideia de forma clara, seguindo as regras gramaticais.

Em geral, a última estrutura sofre uma quantidade de modificações muito maior do que a primeira e, por isso, nela ocorrem a maioria dos erros. É comum que se reorganize uma frase, para escrevê-la melhor e de forma mais clara, e acabar por introduzir erros, por exemplo, artigos (o, a, os, as) discordantes com respeito ao gênero ou número e preposições inconsistentes.

No entanto, ao mesmo tempo que escrevemos e reorganizamos um parágrafo, nos acostumamos com sua estrutura e suas palavras-chave e acabamos por não avaliar com perícia diversos detalhes, como esses que acabamos de mencionar, os quais são extremamente importantes para uma leitura clara e prazerosa.

Diz-se que, ao escrever, estamos “viciados no texto”. Estamos tão acostumados com o ele que não o lemos mais com detalhe. Então, ao revisar o texto, mesmo quando focamos em ler cada palavra, com muita calma e analisando tudo, não é raro que algo passe despercebido.

No entanto, note que estamos “viciados” ao ler o texto, e não ao escutá-lo. É muito mais difícil enganar aos ouvidos do que aos olhos.

Então para revisar um texto, basta pedir que alguém o leia para você, em voz alta. Enquanto ouvimos o texto, fazemos sua leitura e anotamos as inconsistências. Dessa forma, é possível perceber muito mais do texto do que apenas lendo.

Mas quem faria um trabalho desses, de ler um texto em voz alta? Dez páginas, vinte paginas, cem páginas?

O Google Tradutor faria: selecione a língua, colote o texto e clique no botão “Ouvir”.

Linux: Áudio do computador em videoconferência

Para apresentar a defesa da minha dissertação de mestrado, eu precisava tocar executar um vídeo com áudio no computador e a audiência precisava escutá-lo.

Uma saída era, na hora do vídeo, colocar o meu fone de ouvido bem perto do microfone, de forma que fosse transmitido pela videoconferência. Fazendo isso, no entanto, perde-se muito na qualidade do áudio.

Com um pouco de pesquisa descobri que existe uma solução melhor:

  • Criar um canal de áudio Virtual-1, que recebe o áudio das aplicações compartilhadas com a videoconferência. Este canal é ouvido por mim e pela audiência.
  • Direcionar a saída de áudio Virtual-1 para meus fones de ouvido, de forma que eu possa ouvir os programas compartilhados.
  • Criar um canal de áudio Virtual-2, que recebe o Microfone no qual eu falo e a saída de áudio Virtual-1, com os áudios compartilhados. Este canal é transmitido para a audiência.
  • Direcionar a saída de áudio Virtual-2 para um microfone virtual, chamado Virtual-3, que pode ser utilizado pela plataforma de videoconferência Google Meet.

Dessa forma, em vez de enviar apenas o meu microfone para a videoconferência, envio a saída Virtual-3 que também contempla os programas compartilhados.

O sistema em uso

No meu caso, estava utilizando o Ubuntu Linux 18.04. Para configurar o computador dessa forma, fiz uso dos dois scripts a seguir:

  • O primeiro configura o Pulse Audio para criar esses dispositivos de entrada e saída de áudio; e
  • O segundo desfaz essa configuração.

pulse_setup.sh

#!/bin/bash
# This script sets up pulseaudio virtual devices
# The following variables must be set to the names of your own microphone and speakers devices
# You can find their names with the following commands :
# pacmd list-sources
# pacmd list-sinks
# Use pavucontrol to make tests for your setup and to make the runtime configuration
# Route your audio source to virtual1
# Record your sound (videoconference) from virtual2.monitor

# Unload
./pulse_unload.sh

set -e

MICROPHONE=${MICROPHONE:-"alsa_input.pci-0000_00_1b.0.analog-stereo"}
SPEAKERS=${SPEAKERS:-"alsa_output.pci-0000_00_1b.0.analog-stereo"}
#SPEAKERS=${SPEAKERS:-"alsa_output.pci-0000_00_03.0.hdmi-stereo"}

module_file="/tmp/pulseaudio_module_list.txt"

if ! pacmd list-sources | grep -P "^\s+name: <${MICROPHONE}>" >/dev/null; then
  echo "ERROR: Microphone (source) \"${MICROPHONE}\" was not found" >&2
  exit 1
fi

if ! pacmd list-sinks | grep -P "^\s+name: <${SPEAKERS}>" >/dev/null; then
  echo "ERROR: Speaker (sink) \"${SPEAKERS}\" was not found" >&2
  exit 1
fi

# Create the null sinks
# virtual1 gets your audio sources (mplayer ...) that you want to hear and share
# virtual2 gets all the audio you want to share (virtual1 + micro)
pactl load-module module-null-sink sink_name=virtual1 sink_properties=device.description="Compartilhar-Audio" | tee -a "${module_file}"
pactl load-module module-null-sink sink_name=virtual2 sink_properties=device.description="Intermediario" | tee -a "${module_file}"

# Now create the loopback devices, all arguments are optional and can be configured with pavucontrol
pactl load-module module-loopback source=virtual1.monitor sink="${SPEAKERS}" latency_msec=1 | tee -a "${module_file}"
pactl load-module module-loopback source=virtual1.monitor sink=virtual2 latency_msec=1 | tee -a "${module_file}"
pactl load-module module-remap-source source_name=virtual3 source_properties=device.description="Vmic" master=virtual2.monitor | tee -a "${module_file}"
pactl load-module module-loopback source="${MICROPHONE}" sink=virtual2 latency_msec=1 | tee -a "${module_file}"

# Make the default sink back to speakers
pactl set-default-sink "${SPEAKERS}"
pacmd set-default-source "virtual3"

pulse_unload.sh

#!/bin/bash
set -e
module_file="/tmp/pulseaudio_module_list.txt"
if [ ! -f "${module_file}" ]; then
  echo "ERROR: file ${module_file} doesn't exist" >&2
  exit 1
fi
while read -r module; do
  if [[ "${module}" =~ ^[0-9]+$ ]]; then
    pacmd unload-module "${module}"
  else
    echo "ERROR: file ${module_file} is not correctly formated" >&2
    exit 1
  fi
done < "${module_file}"
rm "${module_file}"

Usando OBS para apresentação em videoconferência

Para defender minha dissertação de mestrado, a banca foi realizada à distância, através de uma videoconferência pelo Google Meet.

Para dar mais flexibilidade para a apresentação, utilizei o software OBS para gerar a parte gráfica e para realizar a gravação da aprestação.

Vejamos a seguir os passos básicos para fazer isso.

O tamanho da tela

A primeira coisa a fazer é definir o tamanho da tela.

Entre em “Menu Arquivo > Configurações > Vídeo” e selecione a “Resolução de base (tela)”, ou seja, a resolução de onde será projetado, e a “Resolução de saída (escala)”, ou seja, a resolução da gravação. E clique OK.

É comum utilizar resoluções de proporção 16×9, por exemplo 1280×720 ou 1920×1080.

Existem várias outras configurações do OBS, mas vamos focar apenas no necessário para uma apresentação por videoconferência.

Criando a cena da apresentação

Com o OBS foi criada uma ‘cena’ que contem a apresentação de slides e a câmera.

A cena foi criada clicando no botão mais (+) da interface de cenas.

Capturando a câmera

Para capturar a câmera, clique no botão mais (+) da interface de fontes. No menu selecione “Dispositivo de captura de vídeo” e na janela da interface selecione “Criar nova > OK”. Então escolha a câmera e, se desejar, pode até ajustar sua resolução. Clique em OK e agora basta ajustar o tamanho e a posição da câmera.

Capturando a tela

Quando a apresentação é realizada em tela cheia, temos que capturar a tela em que ela é apresentada.

A tela teve ser capturada, clicando no botão mais (+) da interface de fontes. No menu selecione “Captura de tela” e na janela da interface selecione “Criar nova > OK”. Então selecione a tela da apresentação. É possível recortar bordas, se necessário. Clique em OK e agora basta ajustar o tamanho e a posição da captura de tela.

Capturando uma janela

No meu caso, em vez de capturar a tela, eu capturei apenas a janela da apresentação, porque eu precisava mexer ao mesmo em outras janelas e não queria que elas aparecessem para a audiência.

A apresentação de slides em PDF foi aberta maximizada, com opção de zoom para “ajustar à página”, de forma que fique com altura ou largura máxima, sem exceder a tela. Isso tem o objetivo de manter os slides com tamanho fixo, que será útil na hora de cortar a interface.

Então a apresentação foi adicionada à cena, clicando no botão (+) da interface de fontes. No menu foi selecionado “Captura de janela” e na janela da interface foi selecionado “Criar nova > OK”. Então foi selecionada a janela da apresentação de slides e foram recortadas as bordas desnecessárias. Então clica-se em OK. Agora basta ajustar o tamanho e a posição da janela da forma que desejar.

Projetando em uma janela

Agora que a cena está finalizada, precisamos projetar ela, de forma que possa ser compartilhada na videoconferência.

Quem não tem uma segunda tela, pode projetar em uma janela e compartilhá-la.

No OBS, abra a prévia em uma janela [Botão contrário > Projetor em janela (prévia)] e ajuste a prévia em um tamanho adequado.

Agora, no Google Meet, compartilhe a prévia [Apresentar Agora > Uma janela > Projetor em janela (prévia) > Compartilhar].

Ao compartilhar a janela, você tem a desvantagem de que, se a janela for fechada, seu compartilhamento é finalizado, tendo que ser reiniciado.

Projetando em uma segunda tela

Já quem tem uma segunda tela, pode projetar e compartilhar nesta. A vantagem é que se pode ver o resultado sem precisar ficar trocando de janelas.

No OBS, abra a prévia em tela cheia [Botão contrário > Projetor em tela cheia (prévia) > Monitor X].

Agora, no Google Meet, compartilhe a prévia, o que pode ser feito de duas formas: compartilhando a tela onde a prévia é projetada; ou compartilhando a janela invisível dessa prévia (semelhante acima).

Para compartilhar a tela use [Apresentar Agora > A tela inteira > Tela X > Compartilhar].

Para compartilhar a janela use [Botão contrário > Projetor em tela cheia (prévia) > Compartilhar].

Ao compartilhar a tela, você tem a vantagem de poder arrastar qualquer janela para esta segunda tela e exibi-la durante sua apresentação. Tudo o que estiver na segunda tela vai ser compartilhado.

Identicamente ao anterior, ao compartilhar a janela, você tem a desvantagem de que, se a janela for fechada, seu compartilhamento é finalizado, tendo que ser reiniciado.

Gravando a apresentação

Para gravar a apresentação, basta clicar em “Iniciar gravação” na interface de controles. Obviamente a resolução escolhida impactará no tamanho do arquivo da gravação.

Conclusão

Com isso tudo, agora sabemos como criar uma cena no OBS, gravá-la, projetá-la e compartilhá-la em uma videoconferência.

Foi dado exemplo com o Google Meet, porém imagino que fazer o mesmo com outras plataformas, como Zoom ou Teams, seja tão simples quanto nesse exemplo.

Gráficos vetoriais em PDF

Eu estava usando o LibreOffice Calc para criar gráficos para minha dissertação, porém não estava muito feliz com a conversão para imagem (PNG e JPEG). A solução é utilizar uma opção de exportação para e obter gráficos vetoriais em PDF, que pode ser aproximado sem perda de qualidade.

Criando os gráficos

A Planilha1 vamos deixar para os gráficos. As tabelas e cálculos ficam nas demais planilhas.

Na Planilha2 vamos criar duas tabelas: uma para o gráfico de uma reta e outra para parábola. Eixo X nos limites desejados e eixo Y calculado a partir do eixo X. A partir disso criamos dois gráficos: da reta e da parábola.

Veja que os gráficos possuem dimensão padrão de 16 cm x 9cm. No gráfico, clique com o botão contrário e selecione “Posição e Tamanho”.

Formatando a página

De volta à Planilha1, vamos formatar a página para que os gráficos caibam exatamente na página.

Selecione a Planilha1, entre em Menu Formatar > Página.

Na aba Página, configure Largura 16,1 cm, Altura 9,1 cm e as quatro margens 0 cm.

Na aba Cabeçalho, desative o cabeçalho. Na aba Rodapé, desative o rodapé.

Clique em OK para finalizar. O LibreOffice vai reclamar sobre as margens e impressão, aceite clicando em Sim.

Agora cada página possui tamanho 16,1 cm x 9,1 cm, um pouco maior que os gráficos, e não possui margens, cabeçalho ou rodapé para roubar espaço.

Organizando os gráficos

Agora recortamos os gráficos criados nas outras planilhas e colamos na Planilha1, um gráfico em cada página. Isso vai colocar um gráfico em cada página do PDF.

Para ver as divisões das páginas entre no menu Exibir > Quebras de página. É possível ajustar a altura e largura das células. Recomendo quatro colunas de 4 cm e três linhas de 3 cm.

Exportanto para PDF

Agora, para gerar o PDF com os gráficos, selecione a Planilha1, entre no menu Arquivo > Exportar como PDF, configure para imprimir seleção/planilhas selecionadas e clique em exportar.

Agora temos um arquivo com vários gráficos dentro dele. Ter menos arquivos é ótimo!

Importando no LaTeX

Agora basta importar os gráficos no seu documento LaTex:

\includegraphics[width=0.7\textwidth,page=1]{fig/Graficos.pdf}

Basta indicar qual a página que quer incluir com a opção page=?.

Também é possível recortar margens do gráfico importado usando as opções trim (ajustar as margens) e clip (recortar fora das margens):

\includegraphics[width=0.7\textwidth,page=2,trim=0.5cm 0.5cm 0.5cm 0.5cm,clip=true]{fig/Graficos.pdf}

Os comprimentos da opção trim são: esquerda, embaixo, direita e topo.

Então está feito!

Desta forma é possível criar gráficos vetoriais em PDF de qualidade utilizando o LibreOffice Calc, para inclusão em documentos LaTeX.

Alem dos gráficos vetoriais maravilhosos,outra vantagem é poder agrupar vários gráficos em um único arquivo PDF, ficando mais fácil organizar a pasta de imagens.

Autenticação Criptográfica

Resumão

MAC significa Código de Autenticação de Mensagem (Message Authentication Code). Este código é enviado junto com a mensagem, para provar sua integridade e autenticidade.

Integridade: Se algum bit da mensagem for alterado o MAC fica inválido. Semelhante a enviar a mensagem junto com sua hash.

Autenticidade: Apenas quem possui a chave criptográfica K é capaz de criar e verificar uma mensagem autenticada com esta chave.

A partir da chave e da mensagem criamos o código de autenticação, também chamado de TAG de autenticação. Enviamos o par (MENSAGEM, TAG) e quem recebe este par pode verificar a integridade e autenticidade da mensagem usando a chave K.

A chave K e a tag de autenticação precisam ambas ter comprimentos de acordo com o nível de segurança da aplicação.

Autenticação HMAC

A primeira opção é HMAC, que usa uma Função Hash para criar o código de autenticação. Eis o algoritmo:

se (comprimento(K) > Hash.Tamanho_Bloco) { K = Hash(K) }
enquanto (comprimento(K) < Hash.Tamanho_Bloco) { K = K || 00 }
K1 = K XOR 5C…5C
K2 = K XOR 36…36
TEMP = Hash(K2 || MENSAGEM)
TAG = Hash(K1 || TEMP)

Usamos uma chave para criar duas chaves diferentes. Cada uma é usada em uma etapa: criar uma hash da mensagem (TEMP) e tag de autenticação (TAG). Note que estamos utilizando o símbolo || com o significado de concatenação.

Antes de gerar K1 e K2, a chave K deve ter o tamanho do bloco da função hash. Este preenchimento do bloco permite pré-computar Hash(K1) e Hash(K2), acelerando a autenticação.

Se o comprimento da chave K for maior que o bloco de entrada da função hash (64 bytes/512 bits para SHA-256 ou 128 bytes/1024 bits SHA-512), primeiro realizamos a hash da chave.

Então, se o comprimento da cahve K for menor que o tamanho do bloco de entrada da função hash adicionamos zeros até completar o tamanho.

Podemos usar qualquer função hash com HMAC.

TAG =HMAC-SHA256(K, MENSAGEM)
TAG =HMAC-SHA512(K, MENSAGEM)

Obviamente vamos utilizar hashes que são criptograficamente seguras, por exemplo SHA2 e SHA3, e com comprimentos de saída adequados para o nível de segurança exigido pela aplicação. A chave K também precisa ser longa o suficiente para ser segura.

Autenticação KMAC

KMAC é uma extensão do padrão SHA3 usando XOFs (funções hash com saída de comprimento variável) baseada na esponja criptográfica Keccak.

Uma vantagem clara de usar KMAC, é que permite diferentes comprimentos de saída, gerando TAGs completamente diferentes para cada comprimento de saída.

KMAC é mais flexível e complicada que HMAC. Não vamos entrar em detalhes.

Podemos escolher entre os níveis de segurança máximos de 128 e 256 bits.

TAG =KMAC-128(K, MENSAGEM, COMPRIMENTO_TAG)
TAG =KMAC-256(K, MENSAGEM, COMPRIMENTO_TAG)

A chave K e a TAG precisam ter comprimentos compatíveis com o nível de segurança da aplicação e a função KMAC também precisa ser escolhida adequadamente. Ex: chave 256 bits e tag 256 bits só fazem sentido com KMAC-256.

Autenticação CBC-MAC

É possível também usar o sistema de autenticação CBC-MAC, que usa cifras em blocos como AES em modo CBC.

CBC-MAC possui características semelhantes a HMAC. Usa duas chaves diferentes K1 e K2, geradas a partir de uma chave mestre K. Primeiro calculamos Cn, o último bloco do modo CBC com a chave K1. Então criptografamos este bloco com a chave K2, gerando a tag de autenticação.

K1 = K XOR 5C…5C
K2 = K XOR 36…36
C0 = EK1(IV)
Ci = EK1(Pi XOR Ci-1)
Cn = EK1(Pn XOR Cn-1)
TAG = EK2(Cn)

A tag de autenticação tem o mesmo comprimento do bloco da cifra, 128 bits no caso de AES.

TAG = CBC-MAC-AES(K, MENSAGEM)

Porque não usar HMAC ou KMAC sempre? Em sistemas embarcados com pouca memória de programa, por exemplo, pode ser inviável implementar uma cifra (como AES-128) e também uma hash (como SHA2 ou SHA3). Então usa-se a cifra para ambos criptografia e autenticação.

Vetor de inicialização e MACs

Note que no modo CBC-MAC, deixamos explícito a existência de um vetor de inicialização (IV).

Este é um nonce, um número utilizado apenas uma vez com a chave K. Ele pode ser aleatório ou um contador incrementado a cada mensagem.

O nonce serve para que duas mensagens tenham tags diferentes, mesmo se o conteúdo seja idêntico.

HMAC e KMAC também podem utilizar um IV, basta colocá-lo logo antes da mensagem:

MENSAGEM = IV || MENSAGEM

Isso é semelhante ao CBC-MAC, que coloca o IV no bloco zero, antes da mensagem.

Se considerarmos os IV como sendo parte da mensagem, sua função é de fazer com que sempre tenhamos uma mensagem diferente sendo autenticada, gerando sempre uma tag diferente.

Note que o vetor de inicialização (IV) é enviado junto com a mensagem, ou seja, não é secreto. Em alguns casos ele não é transmitido, se pode ser identificado pelo contexto.

Links Externos

Wikipedia HMAC

HMAC – FIPS PUB 198-1 – The Keyed-Hash Message Authentication Code

KMAC – NIST 800-185 – SHA-3 Derived Functions

Wikipedia CBC-MAC

CBC-MAC – FIPS PUB 113 – Computer Data Authentication

Leia mais:

Veja mais posts na Categoria Criptografia!