Quantcast
Channel: AUTOMALABS
Viewing all 63 articles
Browse latest View live

ESP8266: Como enviar e-mail por uma conexão criptografada (SSL/TLS).

$
0
0

Em um post anterior eu mostrei como se enviava e-mail e adverti para o fato de que a conexão não era segura e as credenciais podiam ser interceptadas (além do fato óbvio do e-mail poder ser lido). Eu achei que enviar por uma conexão segura fosse muito complicado, mas descobri que não é. O problema é que usa muita RAM. O email sem criptografia te deixa com 46KB livres para o resto do seu programa enquanto este aqui te deixa com “apenas” 18KB. Lembre-se de que isso ainda é 9x a RAM total de um Arduino UNO.

Sobre fingerprints e validação de certificado

Se você usa um servidor de e-mail numa conta compartilhada, como é o caso da hostgator, o certificado não vai validar se você colocar o nome do seu domínio como servidor. Você tem que obter o nome correto do servidor de e-mail que atende a sua conta. Na hostgator isso pode ser visto no cPanel.

Por exemplo, meu servidor de e-mail normal seria: meudominio.com.br ou mail.meudominio.com.br mas para validar o certificado eu preciso colocar gator1234.hostgator.com.

O exemplo abaixo é uma adaptação simples do exemplo que não usa criptografia.

/*
 *  Envio de e-mail por SMTP usando conexão criptografada
 *  Jefferson Ryan - automalabs.com.br - Julho de 2017
 *  Testado em um ESP8266 - ESP-01 - IDE Arduino 1.8.1 e ESP8266 Core v2.3.0
 *  Mais informações em http://www.automalabs.com.br/?p=1595
 *  
 */
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

//======== Você precisa preencher estes dados ===========================================
const char* SSID = "";
const char* PASS = "";

char smtp_server[] = "";
const int smtp_port = 465; //465 é a porta segura no meu servidor. A sua pode ser outra.
const String mail_username="nome_do_usuario";
const String mail_password="senha";
const String mail_from="ESP8266 <usuario@servidor_remetente>";
const String mail_to="Jefferson <usuario@servidor_destino>";
const String mail_subject="Enviado pelo ESP8266";
const String mail_line1="Esta é a linha 1\n";
const String mail_line2="Esta é a linha 2";
const String mail_line3="Esta é a linha 3";

//Obtenha com a ajuda de https://www.grc.com/fingerprints.htm
//Precisará ser atualizado manualmente quando o certificado expirar
const char* fingerprint = "";
//=========================================================================================

bool construindoLinha=false;
const char* _b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Cria uma conexao SSL/TLS
WiFiClientSecure client;

void printFreeRAM(){
  Serial.print("RAM livre: ");
  Serial.print(ESP.getFreeHeap());
  Serial.println(" bytes.");
}

void a3_to_a4(unsigned char * a4, unsigned char * a3) {
  a4[0] = (a3[0] & 0xfc) >> 2;
  a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
  a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
  a4[3] = (a3[2] & 0x3f);
}

int base64_encode(char *output, const char *input, int inputLen) {
  int i = 0, j = 0;
  int encLen = 0;
  unsigned char a3[3];
  unsigned char a4[4];
  while(inputLen--) {
    a3[i++] = *(input++);
    if(i == 3) {
      a3_to_a4(a4, a3);
      for(i = 0; i < 4; i++) {
        output[encLen++] = _b64_alphabet[a4[i]];
      }
      i = 0;
    }
  }
  if(i) {
    for(j = i; j < 3; j++) {
      a3[j] = '\0';
    }
    a3_to_a4(a4, a3);
    for(j = 0; j < i + 1; j++) {
      output[encLen++] = _b64_alphabet[a4[j]];
    }
    while((i++ < 3)) {
      output[encLen++] = '=';
    }
  }
  output[encLen] = '\0';
  return encLen;
}

void setup()
{
  Serial.begin(115200);
 // Serial.setDebugOutput(true);
  delay(10);
  Serial.println("");
  Serial.println("");
  Serial.print("Conectando a: ");
  Serial.println(SSID);
  Serial.print("Com a senha: ");
  Serial.println(PASS);
  WiFi.begin(SSID, PASS);

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Conectado.");
  Serial.print("Endereco IP local: ");
  Serial.println(WiFi.localIP());
  printFreeRAM();
  byte ret = sendEmail();
  printFreeRAM();
}

void loop()
{
}

void SEND(String message)
{
if (!construindoLinha){Serial.print("EU: ");};  //Imprimo apenas no início de cada linha
Serial.print (message);
client.print (message);
construindoLinha=true;
  
}

void SENDln(String message)
{
if (!construindoLinha){Serial.print("EU: ");};  //Imprimo apenas no início de cada linha
Serial.println (message);
client.println (message);
construindoLinha=false;  
}

byte sendEmail()
{
  byte thisByte = 0;
  byte respCode;

  Serial.print("Conectando a: ");
  Serial.println(smtp_server);
  if (!client.connect(smtp_server, smtp_port)) {
    Serial.println("Falha na conexao.");
    return 0;
  }else
  {
    Serial.println("Conectado.");
  }

  if (client.verify(fingerprint,smtp_server)) {
    Serial.println("O certificado confere.");
  } else {
    Serial.println("O certificado nao confere.");
    //return 0; //Descomente esta linha para impedir o envio se o certificado estiver errado
  }


  if (!eRcv()) return 0;

  char* buf = (char*)malloc(100);

  SEND("EHLO ");
  SENDln(smtp_server); 
  if (!eRcv()) return 0;

  SENDln("auth login");
  if (!eRcv()) return 0;

  base64_encode(buf, mail_username.c_str(), mail_username.length());
  client.println(buf);  
  Serial.println(buf);
  if (!eRcv()) return 0;
  
  base64_encode(buf, mail_password.c_str(), mail_password.length());
  client.println(buf);   
  Serial.println(buf);
  if (!eRcv()) return 0;
  
  SEND("MAIL From: ");
  SENDln(mail_from);
  if (!eRcv()) return 0;
  
  SEND("RCPT To: ");
  SENDln(mail_to);
  if (!eRcv()) return 0;
  
  SENDln("DATA");
  if (!eRcv()) return 0;
  
  SEND("To: ");
  SENDln(mail_to);
  SEND("From: "); 
  SENDln(mail_from); //Este é o "nome para exibição"
  SEND("Subject: ");
  SENDln(mail_subject);
  SENDln(mail_line1);
  SENDln(mail_line2);
  SENDln(mail_line3);
  SENDln("."); //O email tem que terminar assim
  if (!eRcv()) return 0;

  SENDln("QUIT");
 
  if (!eRcv()) return 0;
  client.stop();
  Serial.println("Desconectado.");
  return 1;
}

byte eRcv()
{
  byte respCode;
  byte thisByte;
  int loopCount = 0;

  while (!client.available()) {
    delay(1);
    loopCount++;
    // Se nada for recebido em 10s, desiste.
    if (loopCount > 10000) {
      client.stop();
      Serial.println("\r\nTimeout");
      return 0;
    }
  }

  respCode = client.peek();
  while (client.available())
  {
    thisByte = client.read();
    Serial.write(thisByte);
  }

  if (respCode >= '4')
  {
    //  efail();
    return 0;
  }
  return 1;
}

Por alto, as diferenças são 4:

1)Acrescente: #include <WiFiClientSecure.h>

2)Acrescente: const char* fingerprint = “fingerprint_aqui”;

3)Mude: WiFiClient client; para WiFiClientSecure client; 

4)Acrescente a validação de certificado logo após a conexão:

if (client.verify(fingerprint,smtp_server)) {
    Serial.println("O certificado confere.");
  } else {
    Serial.println("O certificado nao confere.");
    //return 0; //Descomente esta linha para impedir o envio se o certificado estiver errado
  }

 


ESP8266: Como enviar email pelo GMAIL.

$
0
0

O processo é muito parecido com o usado para enviar email criptografado para outros provedores. Os detalhes a atentar são:

  1. Você precisa configurar a conta gmail para permitir o acesso de “aplicativos menos seguros”, por isso é recomendável que você crie e configure uma conta no gmail só para isso;
  2. O servidor é smtp.gmail.com e a porta é 465;
  3. Nos campos “MAIL from” e “RCPT to” você deve preencher apenas o email, assim:
    <usuario@servidor.com>
     . Do contrário dará “syntax error”.

Erros que você pode encontrar:

  1. “MAIL first” – Neste momento o servidor esperava o comando “MAIL from:”
  2. “RCPT first” – Neste momento o servidor esperava o comando “RCPT TO:”
  3. “Syntax error” – Remova o “nome” da caixa postal. Deixe apenas o endereço de email;

Por que “aplicativos menos seguros”? Existe modo de contornar isso?

Aparentemente, não.

O gmail não parece explicar em lugar algum o que faz o aplicativo ser seguro ou não no conceito da Google. A minha suspeita é que tenha algo a ver com data/hora (para validar certificados) por duas razões:

  • As apps “seguras” da google também recusam seu login se, por exemplo, a data/hora do seu celular Android estiver errada;
  • Quando você habilita o debug “Core +SSL” do ESP8266 é exibida uma mensagem: “pelase start sntp first!” assim que você faz uma conexão criptografada. O autor da biblioteca, Igor, diz que essa mensagem é do SDK da Expressif e não dele e você pode “ignorá-la com segurança“, mas SNTP significa Simple Network Time Protocol e se o SDK quer que você tenha o horário correto antes da conexão, alguma razão existe.

E eu não faço idéia de como fazer o protocolo de comunicação ser configurado com data/hora corretos nem se isso seria suficiente.  A Google possivelmente quer se certificar de que você está validando o certificado dela antes de transmitir, mas isso é só um palpite.

 

Como fazer o componente TwebBrowser usar/emular uma versão específica do IE.

$
0
0

Isso é para quem programa em C++, Delphi, etc e usa o componente twebbrowser para construir um programa baseado em navegador.

Como todos devem saber esse componente usa o engine do Internet Explorer instalado no Windows. O que você talvez não saiba é que por default esse engine quando é “embarcado” em um programa é configurado para entrar em um modo de compatibilidade onde personifica uma versão anterior do IE. Até mesmo no Windows 8.1 de 64bits com o IE11 instalado seu programa vai emular o IE7.

O resultado disso é que certas páginas que abrem sem qualquer problema no IE da máquina vão acusar erro no seu programa. Os mais comuns são erros de script como este:

ie_erro_script_wrongIEversion_automalabs.com.br

Para saber que versão do IE seu programa está emulando faça-o abrir uma página que diga isso, como http://www.useragentstring.com/

Método 1

Isso é configurável pelo Registro. Você precisa ir na chave:

HKEY_LOCAL_MACHINE (ou HKEY_CURRENT_USER)
SOFTWARE
Microsoft
Internet Explorer
Main
FeatureControl
FEATURE_BROWSER_EMULATION

E adicionar um item “NomedoSeuPrograma.exe” com um valor DWORD que corresponde à versão do IE que você quer que seu programa use.

A lista dos valores pode ser vista aqui. Em resumo (valores  em DECIMAL):

  • 11001 – IE 11 modo Edge
  • 11000 – IE11
  • 10001 – IE10 forçado – ignora !DOCTYPE
  • 10000 – IE10
  • 9999 – IE9 forçado – ignora !DOCTYPE
  • 9000 – IE9
  • 8888 – IE8 forçado – ignora !DOCTYPE
  • 8000 – IE8
  • 7000 – IE7

Se fizer a inclusão em HKEY_LOCAL_MACHINE isso val valer para todos os usuários da máquina, mas requer privilégios de administrador para isso. Já a inclusão em HKEY_CURRENT_USER vale apenas para o usuário atual mas seu programa pode mexer nessa chave sem precisar de privilégios.

Você não pode emular uma versão do IE superior à instalada na máquina.

Método 2

Você também pode definir isso via HTML. Não vai precisar mexer no Registro mas cada página precisará ser alterada. Obviamente só é viável quando todas as páginas que seu programa vai exibir são criadas por você.

Basta acrescentar algo assim em HEAD:

<meta http-equiv="X-UA-Compatible" content="IE=9" />

Isso instrui o mecanismo do IE a exibir o documento como se fosse o IE9

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

Isso instrui o mecanismo do IE a exibir o documento como se fosse o Edge

Mais uma vez, você não pode emular uma versão do IE superior à instalada na máquina.

Como criar um programa baseado em Chromium no Delphi 7.

$
0
0

Chromium é o nome da versão opensource do browser/navegador Google Chrome. Normalmente você não precisa disso porque o Delphi 7 já vem com tudo o que é necessário para criar um browser baseado no Internet Explorer, que é o suficiente em projetos de automação (o foco deste blog). Na maioria das vezes os erros no uso de twebbrowser são provocados pela falta de uma configuração. Mas se por alguma razão o Chromium for realmente necessário, o que vou explicar aqui será o suficiente para alguém que já programa em Delphi rodar sua primeira aplicação baseada no Chromium.

Você precisa de quatro coisas:

  • O Delphi 7, claro;
  • Um componente chamado CEF4Delphi;
  • Um conjunto de bibliotecas e outros arquivos do projeto Chromium Embedded Framework (CEF);
  • O Winrar ou o 7-zip para poder abrir o arquivo acima.

É preciso ter em mente que o componente é feito para uma versão específica do CEF. Você deve baixar o componente e a versão do CEF especificada. Guarde esses arquivos porque senão mais tarde você pode ter problemas para refazer sua aplicação.

  • Baixe o CEF4Delphi aqui. Clique em “Clone or Download”;
  • Na mesma página, siga o link para baixar o arquivo do CEF desejado. Eu usei o de 32bits.

A instalação do Delphi4CEF é quase como a de qualquer outro componente:

  • Crie uma pasta vazia chamada “dcu” dentro do diretório “Sources”;
  • Clique duas vezes em “Sources\CEF4Delphi_D7.dpk”, compile e instale. Uma nova aba “Chromium” deve aparecer na barra de componentes;
  • Adicione o diretório Sources do componente ao Library Path do Delphi;

No diretório “demos” você encontrará diversos exemplos de uso, mas para funcionar você ainda precisa fazer algumas coisas:

  • Apague todos os arquivos que não são requeridos em um projeto do Delphi7. No meu caso a presença de algum deles estava travando o Delphi logo ao abrir o projeto;
  • Apague os arquivos .res (serão recriados automaticamente) para evitar um erro de access violation  em RLINK32;
  • Comente a linha {$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE} no código-fonte.

A partir deste ponto você deve ser capaz de compilar o demo. Vai dar um erro “CEF Binaries Missing!” que significa que não pode achar o CEF, mas é normal. Enquanto o programa não compilar não adianta prosseguir. Releia o explicado acima.

O programa compilou? Agora abra o arquivo do CEF no Winrar ou 7-zip (é normal demorar a abrir) e extraia todo o conteúdo dos diretórios “Resources” e “Release” para o mesmo diretório do exemplo que você quer rodar. Você precisa fazer isso para cada um deles que você queira rodar. Sim, os arquivos vão ficar misturados com os arquivos do projeto.

Agora execute o programa de novo. O navegador baseado em Chromium deve abrir.

Se continuar dando o erro “CEF Binaries Missing!” verifique se o código-fonte especifica onde devem ser colocados os arquivos. Por exemplo, o demo “Geolocation” faz as seguintes especificações:

GlobalCEFApp.FrameworkDirPath     := 'cef';
  GlobalCEFApp.ResourcesDirPath     := 'cef';
  GlobalCEFApp.LocalesDirPath       := 'cef\locales';
  GlobalCEFApp.cache                := 'cef\cache';
  GlobalCEFApp.cookies              := 'cef\cookies';
  GlobalCEFApp.UserDataPath         := 'cef\User Data';

Você pode comentar essas linhas (em especial as três primeiras) ou colocar os arquivos nos diretórios certos.

 

Como criar um programa baseado em Chromium no Lazarus.

$
0
0

O Lazarus é mais ou menos uma tentativa de fazer uma versão opensource do Delphi.

Chromium é o nome da versão opensource do browser/navegador Google Chrome. Ao contrário do que acontece no Delphi, que já vem com tudo o que é necessário para criar um browser baseado no Internet Explorer, o Lazarus não vem com tal funcionalidade. O que vou explicar aqui será o suficiente para alguém que já programa em Delphi rodar sua primeira aplicação baseada no Chromium no Lazarus.

Você precisa de quatro coisas:

  • O Lazarus. Eu usei a versão 1.6.4 de 32 bits (em geral eu acho que versões de 64 bits só complicam as coisas);
  • Um componente chamado fpCEF3;
  • Um conjunto de bibliotecas e outros arquivos do projeto Chromium Embedded Framework (CEF);
  • O Winrar ou o 7-zip para poder abrir o arquivo acima.

É preciso ter em mente que o componente é feito para uma versão específica do CEF. Você deve baixar o componente e a versão do CEF feita para ele. Essa é possivelmente a parte mais complicada do processo e por isso eu vou detalhar aqui como eu fiz.

  • Vá na página de Releases do fpCEF3 e veja que versões estão disponíveis. Digamos que você queira fazer com a versão 3.3029, que hoje é a mais recente. Baixe o ZIP do source code dessa versão.
  • Vá na página de builds do CEF e procure pela build mais recente para o seu sistema operacional da versão que você escolheu. Note que nessa página existem seções para Linux, Windows e MAC OSX. 32 bits e 64 bits. Você deve baixar o aquivo “Standard” ou o “Minimal”. A build mais recente Standard para Windows de 32bits v3.3029 é esta. Você poderia usar a versão de 64 bits, mas enquanto está aprendendo é melhor limitar as complicações.

Baixar a build mais recente é importante porque o componente é configurado para a build mais recente. Não é frescura. Se você usar outra build pode ter problemas ao compilar ou rodar o programa.

Guarde esses arquivos porque senão mais tarde você pode ter problemas para refazer sua aplicação.

Instale o Lazarus;

OBS: No Lazarus a janela Mensagens é importante mas tem o hábito de desaparecer em segundo plano e deixar você sem saber o que está havendo. Sempre procure colocá-la em primeiro plano quando você achar que “algo deveria estar havendo” e você não está vendo nada.

Instale o componente. Isso é feito colocando a pasta “fpCEF3-x.xxxx” do componente dentro da pasta Components do Lazarus e depois clicando duas vezes em fpCEF3-x.xxxx\Component\cef3.lpk. Na janela que se abre clique em Compilar e aguarde aparecer “Sucesso” na janela de mensagens. Depois clique em Usar -> Instalar, em SIM no diálogo que vai aparecer e aguarde o Lazarus reiniciar.  Sim, ao contrário do Delphi o default do Lazarus requer reiniciar o editor ao instalar um componente. A partir desse ponto você deve ter uma aba “Chromium” na barra de componentes.

Descompacte a build do CEF. Você precisa do conteúdo das pastas “Release” e “Resources”;

O componente vem com projetos de exemplo. Para rodar um deles você precisa colocar na pasta do projeto os arquivos obtidos no passo anterior. Fica tudo misturado mesmo.

Como o Lazarus pode criar para várias plataformas ele pode “se perder” e querer compilar para Linux apesar de você estar no Windows. À esquerda do botão Executar existe o botão “Modo de Construção” (Build Mode) que controla isso. Certifique-se de estar no modo certo.

Problemas que podem acontecer:

Exceção ao rodar o programa pode acontecer ao usar a build errada do CEF ou porque o programa não está achando os arquivos.

Ao executar o navegador fica em branco: Experimente colocar um botão que ao ser clicado manda abrir o navegador no Google e clique nesse botão depois que o programa abrir completamente. Existe um problema de “timing” nos exemplos (que não ocorre nos exemplos do CEF4Delphi) que faz o programa ignorar a página que deveria abrir automaticamente. Se mesmo assim não funcionar a documentação diz que o CEF pode estar travando e talvez você precise de outra combinação de componente e build.

 

Como fazer o Delphi executar uma função Javascript em um TwebBrowser

$
0
0

Meu exemplo é um resumo desta excelente explicação de Paul Johnson aqui.

Fazer isso é muito simples. Muito mais simples que o oposto, que é fazer o seu programa em Delphi responder a eventos que ocorrem no TwebBrowser.

Digamos que você tenha um componente TwebBrowser no seu projeto Delphi nomeado webbrowser1 que esteja carregado com uma página que tem a função javascript exibirGrafico(). Para chamar essa função você só precisa fazer algo assim:

procedure TForm1.ExecutaJS();
var
  Doc: IHTMLDocument2;      
  HTMLWindow: IHTMLWindow2; 
  JSFn: string;             
begin
  // Obtem a referência ao documento corrente
  Doc := WebBrowser1.Document as IHTMLDocument2;
  if not Assigned(Doc) then
    Exit;

  // Obtem a janela pai do documento corrente
  HTMLWindow := Doc.parentWindow;
  if not Assigned(HTMLWindow) then
    Exit;

  // Executa o JavaScript
  try
    JSFn := 'exibirGrafico()';
    HTMLWindow.execScript(JSFn, 'JavaScript');
  except
    // Manipula a exceção caso não rode
    ShowMessage('Erro ao executar JavaScript');
  end;

end;

Executar “ExecutaJS” no seu programa irá chamar a função “exibirGrafico()” carregada no documento HTML.

Note que a maior parte do que você vê acima é manipulação de erros. Se você não se preocupar com isso a procedure se resume a isto:

procedure TForm1.ExecutaJS();
var
  Doc: IHTMLDocument2;      
  HTMLWindow: IHTMLWindow2; 
  JSFn: string;             
begin

  Doc := WebBrowser1.Document as IHTMLDocument2;
  HTMLWindow := Doc.parentWindow;
  JSFn := 'exibirGrafico()';
  HTMLWindow.execScript(JSFn, 'JavaScript');

end;

Só existe um inconveniente: você não pode receber o retorno da função. É possível contornar esse problema e o artigo de Paul explica como fazer, mas eu ainda não precisei dessa funcionalidade e por isso só vou abordar esse problema outro dia.

Note que o motivo número 1 para você se deparar com erros ao tentar executar o script é estar usando a configuração errada. O número 2 é esquecer que javascript é Case Sensitive. Isto é: exibirGrafico() e ExibirGrafico() são funções diferentes.

Como passar um parâmetro para a função.

Digamos que a função javascript exibirGrafico espere por um parâmetro texto que está no componente edit1. Você pode executá-la com algo assim:

JSFn := 'exibirGrafico(''' + edit1.Text + ''')';

 

ESP8266: O que fazer quando WiFi.hostname() não funciona

$
0
0

Primeiramente é preciso que fique claro que o hostname definido nesta função é o chamado “DHCP hostname” e praticamente só serve para definir o que aparece na listagem de “DHCP leases” do roteador. Você não vai pode usá-lo para dar um “ping” a não ser que seu servidor DHCP seja também um servidor DNS. O ESP8266 já tem um hostname padrão no formato ESP_XXXXXX onde XXXXX são os seis últimos caracteres do endereço MAC e você não precisa usar WiFi.hostname() se esse hostname for suficiente para você.

Requerimentos para que WiFi.hostname() funcione:

  • A função precisa ser executada antes de WiFi.begin();
  • Só funciona se o ESP8266 estiver configurado para obter IP por DHCP;
  • Você precisa reiniciar o roteador e depois obter um IP por DHCP para ver o novo nome aparecer na lista.

Testado com ESP8266 Arduino Core 2.3.0.

 

ESP8266: Problemas enlouquecedores provocados por alimentação

$
0
0

Se você acha que problemas de alimentação só podem levar a travamentos e resets vai ficar espantado. Estes são os problemas que podem ocorrer na falta de um capacitor eletrolítico de desacoplamento na linha de 3.3V, bem perto do chip:

  • SoftAP com funcionamento irregular, chegando a ser impossível usar. Fica aparecendo e desaparecendo da lista de APs detectados pelo celular;
  • No modo STA ou AP_STA não conecta a certos roteadores. Dá consistentemente erro de senha errada, apesar de consistentemente conectar a outros e funcionar por semanas sem problemas aparentes.

Problema detectado com diversos módulos ESP-01 usando o firmware ESP8266 Arduino Core. O mais louco é que o primeiro problema desaparece se você fizer um downgrade para a versão 2.0.0 do Core. Com as versões mais recentes 2.3.0 e 2.4.0-rc2 sempre se manifesta. Isso faz você achar que se trata de um bug de software quando se dá conta de que módulos programados dois anos atrás não manifestam o problema. Tirando esses problemas o módulo funciona aparentemente sem falhas.

Colocar um capacitor de 220uF x 10V (100uF podem bastar) entre os terminais +3.3V e GND do módulo resolve o problema


ESP8266: O tamanho do stack é fixo em 4KB

$
0
0

Isso pode pegar você de surpresa. Mesmo com 30KB ou mais de RAM livre seu programa vai dar stack overflow com resultados imprevisíveis se você usar mais de 4KB em alguma rotina ou sucessão de rotinas encadeadas, o que é muito fácil usando, por exemplo, manipulação de strings com ArduinoJSON.

Isso é definido em cores\esp8266\cont.h:

#define CONT_STACKSIZE 4096

ESP8266: Como obter o endereço IP do cliente HTTP

$
0
0

Essa informação é útil quando, por exemplo, você precisa determinar se o cliente está conectado pelo SoftAP, porque o endereço IP do cliente normalmente estará na faixa 192.168.4.x.

String IPremoto = WebServer.client().remoteIP().toString();
  Serial.print("IP remoto: ");
  Serial.println(IPremoto);

Só vai funcionar corretamente em uma função que manipule uma requisição do servidor, como as definidas por “WebServer.on()”. Em outras o resultado pode ser “0.0.0.0” ou o endereço IP de outro cliente.

Testado com ESP8266 Arduino Core 2.3.0.

 

ESP8266: Usar ESP Touch / SmartConfig é seguro?

$
0
0

Aparentemente, não.

Para quem não conhece, ESP Touch é o nome dado pela ESPRESSIF a um engenhoso método de comunicação de credenciais aparentemente copiado da Texas Instruments, que esta chama de SmartConfig (marca registrada Texas). Nesse método, um ESP8266 ainda não configurado, que não faz idéia de a qual rede se conectar e muito menos a senha, é colocado em “modo de monitoramento” e fica continuamente escutando o tráfego de todas as redes Wi-Fi a seu alcance (uma por vez, rapidamente).  Uma app rodando em um computador ou smartphone conectado a um roteador Wi-Fi o mais próximo do ESP8266 quanto possível transmite continuamente via pacotes UDP o SSID e a senha que você quer que o ESP8266 conheça.  Quando o ESP8266 finalmente esbarrar nessa informação ele se auto configura com o SSID e a senha coletados.

Nota: embora o protocolo permita que você transmita o SSID e a senha de um roteador diferente do qual está conectado, é recomendável que você se conecte ao mesmo roteador cuja senha quer dar ao dispositivo porque isso permite que a app detecte que o dispositivo se conectou (estarão no mesmo domínio de broadcast). Se o dispositivo se conectar a outro roteador provavelmente não será detectável. Por causa disso algumas apps nem te dão a opção de escolher o SSID.

Isso é possível mesmo numa rede criptografada porque a app codifica essa informação em um campo da transmissão Wi-Fi que qualquer um pode ver: o tamanho do pacote. É como usar código morse. O pacote continua criptografado, mas você pode ver o seu tamanho e esse tamanho corresponde a “uma letra” específica (não é exatamente assim, mas o princípio é esse).

Engenhoso e prático mas com um grande problema: qualquer outro dispositivo ou software compatível com SmartConfig ao alcance pode ver o mesmo tráfego e recuperar as credenciais de acesso. Aqui é explicado como se faz isso.

Aparentemente a Texas Instruments incluiu em versões mais recentes do Smart Config a capacidade de criptografar essa transmissão com AES. No caso a app e o dispositivo precisam ter combinado antes uma chave que vai ser usada para poder decodificar a transmissão (a app da Texas tem um campo para a chave, que vem em um QRCode no dispositivo). Essa é uma mudança trivial do ponto de vista do usuário, mas aparentemente não foi implementado no ESP8266. Segundo a documentação da biblioteca ESP8266 Arduino Core a sintaxe para iniciar o monitoramento é:

WiFi.beginSmartConfig()

E apenas isso. Eu ficaria mais à vontade para usar o recurso se a sintaxe fosse algo como

WiFi.beginSmartConfig(chave)

E você precisasse colocar a mesma chave na app, junto com a senha do roteador.

A propósito, outro problema do SmartConfig é que raramente funciona e um dos problemas (mas não o único) é que o ESP8266 suporta apenas redes de 2.4GHz e a app não consegue diferenciar uma rede de 2.4GHz de uma de 5GHz. Pior ainda: se existirem duas redes com o mesmo SSID o Android dá preferência a se conectar à rede de 5GHz. Você ou o cliente do seu produto podem não se dar conta disso e o SmartConfig não vai funcionar. Usando o método “tradicional” de configuração se conectando ao SoftAP, pelo menos você tem menos chances de errar porque a varredura do ESP8266 só mostra as redes de 2.4GHz. Eu recomendo que você esqueça o SmarConfig e use SoftAP com CaptivePortal para a configuração.

Apps Android para a criação de GUIs (HMI/MMI) de controle para seus projetos

$
0
0

Isso é basicamente um rascunho onde vou escrever minha opinião sobre algumas apps que testei.

O que me interessa:

  1. Poder criar uma ou mais telas (num tablet, uma tela pode bastar, mas num celular, nem sempre) com botões e indicadores a meu gosto;
  2. Não depender de internet, nem de servidores locais. Eu quero poder mandar mensagens diretamente para meus projetos, sem intermediários;
  3. Ter o controle sobre o conteúdo das mensagens, para poder criar meus próprios protocolos ou usar os que já uso.

NETIO – É uma app paga, mas é barata e atende os requisitos acima. Para cada botão na tela você pode definir um destino e mensagens distintos, logo numa mesma tela você pode mandar mensagens para diversos projetos. Você cria as telas em um editor online (não pode fazer no próprio celular), mas tudo é salvo em arquivos JSON que você pode baixar, guardar e editar em outros programas se quiser. Se o editor online desaparecer alguém da comunidade pode criar um editor offline, porque a app apesar de (para sua comodidade)  sincronizar via internet aceita que você coloque os arquivos manualmente na memória do telefone. Mas em anos a app e seu editor não evoluíram quase nada e NETIO está deixando a desejar em beleza e funcionalidade.

Para fazer múltipla seleção de elementos não clique, apenas segure SHIFT e mova o mouse sobre eles. Depois disso veja na aba seleção instruções para mover, dimensionar e apagar

A resolução definida no editor corresponde ao tamanho da janela que você vê, como parte do seu monitor. E não bate com a do dispositivo. Por exemplo, precisei definir 1050×1700 para o Samsung A5, que tem uma resolução de 720×1280.

Para aumentar as chances de que as páginas sejam exibidas corretamente em outros dispositivos, acrescente o atributo fittoscreen a cada página.

Não há opção de definir uma fonte no dispositivo ou uma relação entre a fonte no dispositivo e a do editor. Fontes muito pequenas no editor ficam muito grandes no aparelho, o que atrapalha muito o WYSIWYG.

Você pode especificar apenas arquivos PNG existentes no dispositivo, que não aparecem no editor, o que atrapalha muito o WYSIWYG.

VIRTUINO – Grande quantidade de opções de ícones para a GUI a tornam muito mais atraente que o NETIO, mas você não tem controle sobre o conteúdo das mensagens e por isso precisa usar as bibliotecas do Virtuino. Pior que isso, as opções padrão são muito engessadas, porque ele espera que cada comando seja para acionar ou ler um certo I/O no arduino. Você não pode, por exemplo mandar um código com um comando a ser interpretado, o que dificulta a criação de interfaces de controle remoto IR por Wi-Fi. Entretanto se o que você quer é justamente com alguns toques na tela definir que um botão x acione a saída y no arduino, Virtuino pode ser uma ótima escolha.

Tem suporte ao ESP8266 ESP-01, mas apenas como interface Wi-Fi, controlada por comandos AT (como vem de fábrica). Não tem suporte a programar um ESP-01 como se fosse um arduino.

O editor fica no próprio aparelho.

REMOTE XY – A versão free da app é bastante limitada e a paga custa hoje R$22. Editor online. Mais bonito que o NETIO mas com menos opções de elementos que o Virtuino. Não parece ter opção de fazer o download dos projetos. Não parece fazer proveito de telas grandes. Requer que você use um arduino e um módulo de comunicação (não permite usar um ESP-01 como se fosse um arduino). Elementos na tela são ligados diretamente ao I/O do arduino (você não pode definir mensagens).

Fundamentos das células de carga

$
0
0

Este texto está em rascunho

Princípio de funcionamento

Vamos começar lembrando como funciona um simples divisor de tensão

celulas_de_carga_divisores_automalabs.com.br

 

Nos três divisores acima a tensão é sempre a mesma: 6V. Com dois resistores de valor idêntico temos metade do valor da tensão no ponto A. Se experimentarmos aumentar o valor do resistor inferior a tensão medida aumenta como vemos no ponto B. Mas se em vez disso aumentarmos o valor do resistor superior a tensão medida diminui como vemos no ponto C.

Digamos que nós temos dois divisores de tensão cada um com uma resistência variável e que essas resistências variem ao mesmo tempo. Para medir o desequilíbrio entre os dois divisores isso não daria certo:

celulas_de_carga_divisores_2_automalabs.com.br

4V-4V = 0V

2V-2V = 0V

Note que a diferença entre as tensões dos pontos B e C é sempre zero. Para o medidor é como se nada estivesse mudando. Porém se invertemos a posição de uma das resistências variáveis nós passamos a poder medir o desequilíbrio:

celulas_de_carga_divisores_3_automalabs.com.br

B-C = 4V – 2V = 2V

A diferença de tensão se torna proporcional à variação das resistências

 

A medição desse desequilíbrio é o princípio da Ponte de Wheatstone. A medição de células de carga se baseia nesse princípio. A célula de carga é uma Ponte de Wheatstone (ou meia ponte) cujo valor dos resistores variáveis muda com a pressão exercida.  Uma particularidade muito importante é que ao contrário dos exemplos que usei onde a variação da resistência é facilmente mensurável, na célula de carga essa variação é muito pequena sendo que quando você a pressiona com os dedos é de apenas décimos de ohm, por isso é impraticável medir/testar com um ohmímetro.

Com a variação de resistência é muito pequena, a variação de tensão também é. Tão pequena que um Arduino não poderia medir diretamente (tipicamente varia menos de 1mV por kg). Para isso precisamos de um circuito chamado amplificador de instrumentação (um tipo de amplificador operacional) e a saída desse amplificador é conectada ao Arduino. Você está livre para escolher que amplificador de instrumentação usar mas o mais comumente usado em projetos com Arduino é o HX711, que além de ser amplificador especificamente desenhado para ler células de carga incorpora um conversor A/D de 24 bits. Uma resolução muito maior que a do Arduino, que é de 10 bits.

É também comum nas células de carga que a resistência que estamos considerando como fixa também varie, mas no sentido oposto. Ou seja: enquanto uma resistência aumenta de valor a outra diminui. Para simplificar as explicações neste texto eu vou considerar que uma resistência é sempre fixa.

 

As células de baixo custo, usadas em balanças de banheiro e vendidas para projetos de automação com Arduino costumam ter as seguintes características:

  • Apenas três fios, característico de meia ponte;
  • Dos dois resistores internos apenas um é variável (ativo, valor muda com o stress) e o outro é fixo, usado como referência. Vou considerar que o ativo seja o resistor ligado ao fio branco.

Como explicado acima, ao fazer as ligações você deve ter em mente que as resistências variáveis precisam ficar em lados opostos da ponte.

Esquemas de ligação

Nos esquemas a seguir considere o seguinte:

  • As células tem três fios nas cores vermelho, branco e preto;
  • Os círculos coloridos representam as cores dos respectivos fios;
  • Se você ligar tudo certinho e ainda assim ao pressionar uma das células não aparecer variação, inverta a polaridade de um (e apenas um) dos pares. Nos meus desenhos eu considero que ao aplicar stress na célula o valor da resistência ativa aumente (tração). Se o valor diminui (compressão) a polaridade é oposta ao representado nos meus desenhos.

Ligação de 2 células de 3 fios

celulas_de_carga_ligacoes_2x_1

Ligação de 4 células de 3 fios

Ao contrário da ligação com duas células aqui ligamos preto com preto e branco com branco. A idéia é sempre colocar os resistores ativos nos mesmos “braços” da ponte.

celulas_de_carga_ligacoes_4x_3_automalabs.com.br

 

Ligação de 1 célula de 3 fios

Esse modo não é recomendado, mas funciona. Você substitui uma das células de 3 fios por dois resistores de precisão do mesmo valor da resistência medida da célula que você vai usar. Como vai existir um resistor fixo no lugar onde deveria haver um variável, o valor da tensão entre S+ e S- é metade do do que haveria com duas células. Explicando de outra forma: se você fizer isso em uma balança, substituindo uma das células por resistores, o peso apresentado será metade do esperado.

A colocação de resistores também desbalanceia a ponte (em repouso a diferença entre S+ e S- é diferente do esperado) por isso é recomendado colocar um trimpot de precisão (multivoltas) de valor imediatamente superior no lugar onde deveria estar o resistor ativo da célula. Esse trimpot consegue balancear a ponte mas não tem nenhuma influência sobre o problema anterior.

celulas_de_carga_ligacoes_1x_1_automalabs.com.br

Módulo para leitura de células de carga HX711

$
0
0

modulo_hx711_DSC03192_640_automalabs.com.br

Datasheet do CI HX711

Se você precisa medir peso com o Arduino esse é o módulo geralmente requerido. Permite conectar até dois conjuntos de células de carga (duas balanças).

Trata-se essencialmente de um amplificador de instrumentação de duas entradas multiplexadas ligado a um conversor A/D de 24 bits o que permite uma resolução muito superior à que seria obtida ligando um amplificador de instrumentação diretamente a uma entrada analógica do Arduino, cujo conversor A/D é de 10 bits.

  • Alimentação de 2.7 a 5.5V;
  • Saída digital a dois fios;
  • Duas entradas multiplexadas. Você pode usar apenas a entrada A, se quiser;
  • Taxa de amostragem selecionável de 10 ou 80 amostras por segundo;
  • Ganho selecionável de 32, 64 e 128.

Este é o o diagrama / esquema do módulo:

hx711_module_diagram_2

É quase a mesma coisa que o diagrama típico apresentado no datasheet, com o acréscimo de C4, C5, C6, R8 e R9.

Note que existem duas referências de terra: GND e AGND

Como fazer a leitura

O valor do registrador de 24bits com o resultado da medição é lido serialmente dando 24 pulsos no pino SCK e coletando os bits no pino DT. Isso é realmente simples de fazer mesmo sem uma biblioteca, mas como elas existem vamos usá-las até porque torna o código mais fácil de entender.

A biblioteca de uso mais simples que conheço é a do chinês aguegu. Você precisa fornecer à biblioteca quatro informações:

  • qual o pino de clock;
  • qual o pino de dados;
  • o valor de offset;
  • o valor do ratio.

Você pode usar dois pinos quaisquer. A biblioteca usa nos exemplos os pinos A0 e A1 mas estes são usados no modo digital. Pinos analógicos não são requeridos.

O valor de offset é o valor que o HX711 fornece quando a balança está em repouso (seria o “zero” da balança). Para obtê-lo deixe a balança sem peso e rode o seguinte sketch:

#include "hx711.h"

Hx711 scale(A1, A0); //A1 = dout e A0=sclk

void setup() {

  Serial.begin(9600);

}

void loop() {

  Serial.print(scale.averageValue(1));
  delay(200);
}

Este é o valor do offset. É normal que seja um valor grande como 8447426. Note que esse valor é aproximadamente metade de 16777215 (o maior valor possível com 24 bits). Isso é esperado porque o hx711 também lê valores negativos. Agora coloque um peso conhecido na balança, como 1kg (1000g), e execute novamente o comando acima para obter o que a balança vê agora. Esse valor deve ser maior que o valor obtido de offset. Se não for, experimente inverter os fios S+ com S- e repita. Vamos chamar esse novo valor de “w”. Agora faça a seguinte conta:

ratio = (w - offset) / 1000

Agora preencha os valores obtidos no sketch abaixo:

#include "hx711.h"

Hx711 scale(A1, A0); //<- edite aqui

void setup() {
  Serial.begin(9600);
  scale.setOffset(offset); //<- edite aqui
  scale.setScale(ratio); //<- edite aqui
}

void loop() {
Serial.print(scale.averageValue(1));
Serial.print(" : "); &nbsp;
Serial.print(scale.getGram());
Serial.println('g');
  delay(200);
}

O programa de exemplo acima deverá agora dar o valor aproximado de qualquer peso medido, em gramas.

Nota: a biblioteca tem um bug que faz a execução ser muito lenta. Para corrigir, abra hx711.cpp e os exemplos e mude todas as referências a averageValue() para averageValue(1). Isso na prática anula a função averageValue, mas elimina o delay. É melhor você tirar sua própria média dos valores lidos no loop (sugiro uma média móvel) para evitar paradas desnecessárias na execução do programa.

Delphi 7: Erro RLINK32 Out Of Memory ao tentar trocar um ícone

$
0
0

Abra o ícone que você está tentando inserir no Icon Workshop ou outro editor de ícones e verifique se uma ou mais das imagens (um arquivo .ICO pode conter várias) não está em um formato comprimido como “PNG Compressed”. Se estiver, desmarque a opção, salve e recarregue o ícone no Delphi. Isso é suportado pelo Windows Vista em diante mas o Delphi 7 não sabe como manipular.


Delphi: Como atualizar a área de trabalho programaticamente

$
0
0

Se você estiver experimentando novos ícones para a sua aplicação pode ser enganado pelo fato do Windows manter um cache com os ícones apresentados no desktop e assim os ícones nos atalhos podem permanecer os mesmos até pelo menos o Windows ser reiniciado.

Para forçar o Windows a recarregar todos os ícones, acrescente shlobj na seção USES do form e dê o seguinte comando:

SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil);

Você deverá notar o desktop “piscar”.

Testado com Delphi 7 e Windows 8.1 64 bits.

 

Como criar um ícone transparente de alta resolução para uso no Delphi 7

$
0
0

Este tutorial requer que você saiba o básico de edição de imagens (selecionar, recortar, redimensionar, etc). Eu não vou entrar em muitos detalhes aqui.

Para tornar o fundo da imagem transparente

Para este tutorial eu vou usar a imagem de uma engrenagem que pode ser obtida aqui. Eu recortei antes de começar o tutorial o que não era necessário para mim, o que incluía a sombra.

Usando o Paint.Net (estou usando a versão 4.2.1), selecione a região a tornar transparente. Usando a ferramenta Varinha Mágica, por exemplo, você usa SHIFT+CLICK para selecionar mais de uma região e reduz a Tolerância (indicada pela seta) se o corte automático estiver invadindo uma região que não deve. No exemplo abaixo dei um clique para selecionar a região externa e outro para selecionar a região no centro da imagem:

PaintNet_MagicWand_automalabs.com.br

Tecle DEL para apagar a região selecionada. O Paint.Net já mostrará que está definido como transparente usando o padrão xadrez característico.

PaintNet_TransparentCog_automalabs.com.br

Agora salve como PNG.

Se você precisa criar uma transparência mais complexa, pode usar o plugin Cut Color

Uma alternativa online ao Paint.Net para a criação da transparência está aqui.

Converter o arquivo PNG para ICO usando o Icon Workshop

Obs.:

  • Neste tutorial eu estou usando o Icon Workshop 6.53;
  • É possível que o Icon Workshop tenha ferramentas embutidas para definir a transparência sem precisar de ferramentas extras, mas nesse momento eu não sei como fazer e por isso usei o Paint.Net;
  • Tenha me mente que um arquivo ICO é uma coleção de imagens. Ele pode conter apenas uma, mas também pode conter muitas, que são selecionadas pelo Windows de acordo com a necessidade.

Você precisa abrir o arquivo PNG no Icon Workshop (IW). Isso pode ser feito de diversas maneiras:

  • CTRL-G para a busca de mídia;
  • Clicando no botão Browse;
  • ou arrastando a imagem para a janela do IW.

iconWorkshop_ImageLoaded_automalabs.com.br
Na janela onde aparece a imagem, clique no ícone no canto superior esquerdo. Aquele que tem um “I”:

IconWorkshop_ConvertToIco

O IW te mostrará todas as opções de criação para Windows:

 

IconWorkshop_AsNewIcon_automalabs.com.br

Se você vai usar sua aplicação em um sistema do Windows Vista em diante, marque a caixa indicada pela primeira seta.

Se o PNG for salvo dentro do ICO (um arquivo ICO é uma coleção de imagens) como compressed PNG o Delphi não poderá usá-lo e poderá até não conseguir compilar mais, acusando erro RLINK32: Out Of Memory. Então você precisa desmarcar a caixa apontada pela seta inferior.

Clique em OK, a imagem aparecerá no IW agora como um conjunto de imagens:

IconWorkshop_IconePronto_automalabs.com.br

Atente para as miniaturas. Se alguma (geralmente a primeira) exibir um símbolo de compressão alguma coisa deu errado e a imagem ainda está como PNG Compressed. Clique com o botão direito sobre a miniatura e desmarque a opção indicada.

IconWorkshop_PNGCompressed_automalabs.com.br

Agora você já pode salvar o ícone e carregá-lo no Delphi 7.

 

Não consegue usar o Icon Workshop?

Existem ferramentas online como icoconvert.com que fazem a criação do ícone para você. Se você fizer o upload do PNG do nosso exemplo e escolher a opção “ICO for Windows 7, Windows 8, Vista and XP” o site te devolverá um arquivo ICO com 5 imagens (256×256, 48×48, 32×32, 24×24 e 16×16, todas RGB/A). Porém a primeira imagem está comprimida e o ICO será incompatível com a compilação no Delphi 7. Você poderá, entretanto, carregar esse ícone manualmente no arquivo EXE da aplicação usando o Resource Hacker.

Já se você escolher Custom sizes -> Multi sizes e selecionar apenas as opções 48×48, 32×32, 24×24 e 16×16 (abrir mão do ícone de 256×256) o resultado poderá ser compilado pelo Delphi 7.

ESP8266: error: call of overloaded ‘println(time_t (&)())’ is ambiguous

$
0
0

Esse erro pode acontecer por diversas razões, mas no meu caso foi porque eu estava trabalhando em um exemplo feito por outra pessoa onde o desenvolvedor criou uma variável local chamada “now”. Eu fiz uma modificação acidental que escondeu a declaração da variável e então em vez do compilador me avisar disso ele assumiu que todas as referências a “now” no código fossem as declaradas em bibliotecas como a Time.h, que tem variáveis com o mesmo nome. E ele não sabia qual usar.

O compilador não tem como adivinhar sua intenção nesse caso.

A solução foi consertar a declaração que eu havia apagado, mas melhor ainda foi substituir todas as ocorrências de “now” no código por “agora” para evitar acidentes como esse no futuro. Eu acho que nós brasileiros levamos uma pequena vantagem nesse caso por usarmos naturalmente nomes de variáveis em português, o que evita a colisão com os nomes usados em bibliotecas, que geralmente estão em inglês. Se o nome da variável não fosse “now” minha ocultação acidental da declaração teria apenas causado um erro facílimo de entender do tipo “não foi declarada”. Tenha em mente que se o compilador só tivesse encontrado uma alternativa e assim não tivesse ficado confuso e compilasse o código, eu estaria usando no meu programa o conteúdo da variável errada (a de uma biblioteca em vez da local) o que levaria a defeitos difíceis de debugar.

Introdução aos bancos de dados NoSQL

$
0
0

Nota do autor: Este texto foi originalmente escrito para um projeto de 73 páginas (inclui figuras e código-fonte) que fiz na faculdade intitulado BANCO DE DADOS ORIENTADO A DOCUMENTOS – UMA APLICAÇÂO PRÁTICA e seguiu alguns requerimentos burocráticos que eu apaguei e outros que mantive. O projeto completo inclui uma aplicação em Delphi para Windows e outra para Arduino/ESP8266 e será tudo detalhado em outros posts.

 

Quando pensamos hoje em banco de dados quase sempre ainda estamos pensando em um banco de dados relacional. Segundo pesquisa mensal da consultoria austríaca Solid IT, os
quatro SGBD (Sistema Gerenciador de Banco de Dados) mais populares são relacionais, o que corresponde a 75% do total.
db-engines_database_ranking_automalabs.com.br

O que pode surpreender muitos programadores que por não conhecerem as alternativas podem se espantar por esse número não ser 100%. Mas provavelmente todo programador deve ter se deparado com alguma situação em que seu SGBD (relacional) favorito gerava um incômodo que precisou ser contornado de alguma forma pouco ou nada elegante e que poderia ter sido resolvido com a troca do paradigma.

Mas antes de introduzir a alternativa é conveniente dar um exemplo do tipo de problema que ela resolve.

Considere um problema comum de IoT (Internet of Things – Internet das Coisas) onde você tem um sensor para acompanhar o desenvolvimento minuto a minuto de uma certa grandeza (temperatura, por exemplo) e deseja armazenar isso em um banco de dados para análise posterior. A informação mínima útil a armazenar seria a temperatura e o horário da medição. Uma representação tabular da informação armazenada ficaria assim:

Ordem Temperatura Horário
1 22,12 01/05/2020 15:10
2 23,45 01/05/2020 15:11
3 25,10 01/05/2020 15:12
4 26,13 01/05/2020 15:13

Mas digamos que em um momento qualquer no futuro você decidiu acrescentar mais um sensor. Em uma aplicação convencional usando um SGBD relacional, além de ter que atualizar todos os componentes do projeto que acessam o banco de dados é preciso alterar o próprio banco de dados mudando o seu schema (sua estrutura lógica) acrescentando no mínimo uma coluna (campo), e o resultado poderia ser algo assim:

Ordem Temperatura Horário Temperatura2
1 22,12 01/05/2020 15:10
2 23,45 01/05/2020 15:11
3 25,10 01/05/2020 15:12
4 26,13 01/05/2020 15:13
199 25,12 20/05/2020 11:00 28,10
200 26,13 20/05/2020 11:01 29,05
201 27,08 20/05/2020 11:02 30,12
202 29,10 20/05/2020 11:03 31,10

 

O fato de todos os registros anteriores à alteração ficarem vazios pode ou não ser um problema, mas o fato de ter que alterar o schema é. Além do mero incômodo de ser um passo adicional, normalmente alterar o schema requer downtime. Todos os acessos à tabela a modificar precisam ser interrompidos durante a operação. Em um sistema local isso pode significar apenas “pedir a todo mundo que saia do sistema”, mas à medida que aumenta a complexidade e a utilização do sistema, interromper o acesso aos dados mesmo que por um intervalo curto de tempo vai se tornando uma decisão mais difícil, impactando negativamente na escalabilidade. Sam Saffron, um dos criadores do popular software de código aberto de gerenciamento de listas de discussão Discourse, afirma que “uma das razões mais comuns para interrupções de serviço durante o deployment de uma aplicação é a alteração do schema do banco de dados”.

Agora digamos que você como desenvolvedor antecipa a necessidade futura de acrescentar sensores e pensando em evitar esse passo adicional e o downtime, decide criar a tabela já com os campos necessários para acomodar mais sensores. Mas quantos campos adicionais antecipar? Dois? Cinco? Dez? Cem? E se você precisar fazer alguma alteração que torne essa antecipação inútil? O nosso exemplo é propositalmente bem simples, mas existem outras situações similares bem mais complexas.

Nesse ponto é interessante avaliar os SGBD NoSQL.
Bancos de dados NoSQL

NoSQL é um paradigma de banco de dados que é modelado de uma forma diferente da tradicional representação tabular dos bancos relacionais. Esse tipo de banco de dados existe desde os anos 1960, mas apenas cerca de dez anos atrás foi o adotado o termo “NoSQL” para se referir ao paradigma.  Bancos NoSQL tem um foco em simplicidade de projeto e escalabilidade e suas estruturas de dados são diferentes das usadas em bancos relacionais fazendo com que algumas operações sejam mais rápidas em NoSQL.

Os requerimentos do nosso exemplo são a capacidade de mudar o schema facilmente e fazer buscas simples no banco de dados. O primeiro é uma característica dos bancos NoSQL mas os recursos de busca dependem do SGBD NoSQL escolhido.

Os SGBD NoSQL podem ser divididos em quatro grandes categorias:

  • Chave-Valor (key-value);
  • Armazenamento de Documentos (document-store);
  • Armazenamento de coluna ampla (wide colum stores);
  • Graph Database.

Novamente usando como referência  a DB-Engines, podemos ver que os modelos mais populares são chave-valor e armazenamento de documentos:

db-engines_database_ranking_automalabs.com.br

Por brevidade, vamos nos ater a analisar apenas esses dois modelos.

Chave-Valor
Nesse modelo o banco de dados usa um simples método chave-valor para armazenar os dados. É mais fácil entender esse método pensando que um dicionário é um exemplo de chave-valor: a chave sendo uma palavra e o valor sendo o significado. O nome do método pode ser enganador, levando você a pensar que o “valor” pode ser uma entidade apenas, mas não é bem assim. Abaixo temos um exemplo perfeitamente válido de armazenamento chave-valor:

Chave Valor
1 22.5,10/04/2020 12:35
2 23.6,10/04/2020 12:36
3 24.2,10/04/2020 12:37
4 25.1,10/04/2020 12:38

No exemplo acima, cada chave dá acesso a duas entidades: uma temperatura e o horário. Chave-valor não tem nenhum schema definido e você está livre para mais tarde ao descobrir que precisa colocar mais um sensor fazer algo assim:

Chave Valor
1 22.5, 10/04/2020 12:35
2 23.6, 10/04/2020 12:36
3 24.2, 10/04/2020 12:37
4 25.1, 10/04/2020 12:38
199 27.2, 20/05/2020 17:23, 28.9
200 28.4, 20/05/2020 17:24, 27.9
201 27.3, 20/05/2020 17:25, 26.5
202 26.2, 20/05/2020 17:26, 25.3

Como se pode ver, o valor do segundo sensor foi simplesmente acrescentado aos novos registros armazenados. Isso pode ser feito indefinidamente e não apenas com o valor de outros sensores. Você pode adicionar qualquer outra informação ao registro que queira, como notas. Você faz o seu schema e isso é transparente para o banco de dados.
Além disso, como a única coisa que define a chave é que é um valor que só pode ocorrer uma vez no banco de dados, você pode perfeitamente usar o horário como chave:

Chave Valor
10/04/2020 12:35 22.5
10/04/2020 12:36 23.6
10/04/2020 12:37 24.2
10/04/2020 12:38 25.1
20/05/2020 17:23 27.2, 28.9
20/05/2020 17:24 28.4, 27.9
20/05/2020 17:25 27.3, 26.5
20/05/2020 17:26 26.2, 25.3

Parece perfeito para a nossa aplicação, mas existe um problema. Segundo a IBM Cloud Education:

Em geral, armazenamentos key-value não tem linguagem de consulta. Eles simplesmente proporcionam um modo de armazenar, recuperar e atualizar dados usando simples comandos GET, PUT e DELETE. A simplicidade deste modelo torna um armazenamento key-value rápido, fácil de usar, escalável, portável e flexível.

Então com satisfazer o requerimento de poder fazer buscas no banco? De acordo com DB-Engines, o mais popular SGBD com suporte a chave-valor é o Redis:

db-engines_key-value_stores_ranking_automalabs.com.br

Este parece suportar buscas, mas após uma verificação superficial da documentação não conseguimos uma resposta clara de como fazer o tipo de busca que desejamos.

Armazenamento de Documentos

Esse nome pode dar a impressão inicial que essa categoria objetiva armazenar arquivos do Word, Excel, AutoCAD, etc. Os arquivos que nós geralmente chamaríamos de “documentos” em um computador. Mas não se trata disso. No contexto dos bancos de dados um documento pode ser algo tão simples quanto a seguinte linha de texto:

{“Temperatura1”:”22.4”}

De acordo com DB-Engines, o mais popular SGBD orientado a documentos é o MongoDB:db-engines_document_stores_ranking_automalabs.com.br

Em teoria você pode armazenar o documento em qualquer formato que queira, mas se você quer aproveitar as vantagens do NoSQL é prudente seguir formatos padronizados e cada SGBD tem suas preferências, sendo os mais comuns XML e JSON. Abaixo uma comparação simples dos dois formatos:

XML JSON
<temperatura1>22.4</temperatura1>

<horário>10/12/2019 10:23</horário>

{“temperatura1”:”22.4”;

“horário”:” 10/12/2019 10:23”}

Como se pode notar o formato JSON é mais “natural”, mais fácil de ler e menos “verboso”, isto é: requer menos texto para transmitir a mesma informação. E de fato o mais popular SGBD NoSQL, o MongoDB, optou pelo JSON. E acabamos optando por esse também.

Os documentos não seguem nenhum schema rígido, por isso você pode a qualquer tempo adicionar novos campos ao documento, como no exemplo abaixo:

JSON
{“temperatura1”:”22.4”;

“horário”:” 10/12/2019 10:23”}

{“temperatura1”:”23.7”;

“horário”:” 10/12/2019 10:24”}

{“temperatura1”:”23.7”;

“temperatura2”:”24.2”;

“horário”:” 10/12/2019 10:25”}

{“temperatura1”:”23.7”;

“temperatura2”:”24.8”;

“horário”:” 10/12/2019 10:25”}

 

No exemplo nós simplesmente acrescentamos um campo a mais: temperatura2, mas novos campos podem ser acrescentados indefinidamente. Quanto ao nosso requerimento de fazer buscas nos dados, a documentação do MongoDB nesse aspecto é muito mais fácil de entender que a do Redis.

É importante notar que, mesmo usando JSON, document-store desperdiça muito espaço em comparação com um banco de dados relacional ou mesmo o modelo chave-valor por causa da alta repetição de informação. Por exemplo, digamos que desejamos apenas armazenar o valor em ponto flutuante de dois sensores no formato: “12,34” e que esse valor seja armazenado como string ASCII (cinco bytes por valor armazenado). Vamos considerar um “id” para identificar o registro, também string, com dois bytes:

Banco Relacional:

Id Sensor1 Sensor2
01 23,34 23,45

Cada registro ocupa 12 bytes

Agora, o mesmo registro codificado como JSON:

{
“Id”=”01”
“Sensor1”:”23,34”;
“Sensor2”:”23,45”;
}
Como cada caractere precisa ser contado, a mesma quantidade de informação ocupa agora (ignorando caracteres de quebra de linha) 47 bytes por registro.  Essa diferença só piora à medida que mais valores precisarem ser armazenados em cada documento. Em muitas aplicações essa diferença é irrelevante diante das vantagens, mas precisa ser levada em consideração onde espaço é problemático, como nas aplicações embarcadas. Espaço não será problema na nossa demonstração, pois mesmo na versão gratuita, com 500MB de espaço, o serviço escolhido pode suportar nossos requerimentos básicos por mais de três anos.

Podemos reduzir essa desvantagem no uso de espaço usando um formato binário de codificação como o BSON, mas em alguns casos BSON pode ocupar ainda mais espaço que JSON porque, de acordo com o FAQ “…adiciona informação extra aos documentos, como o comprimento das strings e subobjetos” e tem a desvantagem de não ser uma codificação legível por humanos.

 

Suporte a relações

Apesar de NoSQL se referir especificamente a bancos de dados não relacionais achar que você não pode armazenar relações em um bancos de dados NoSQL é um engano. Segundo a MONGODB, Inc: “Um equívoco comum é achar que bancos de dados NoSQL ou bancos de dados não-relacionais não armazenam dados de relacionamento bem. Bancos de dados NoSQL podem armazenar dados de relacionamento – eles apenas o fazem diferentemente de como é feito pelos bancos de dados relacionais. 

Então não existe problema se futuramente nosso projeto necessitar de suporte a relações.

Diante do exposto, decidi pelo uso do modelo Armazenamento de Documentos com o MongoDB como SGBD.

Para demonstrar o uso de NoSQL irei usar um Arduino com dois sensores de temperatura publicando uma vez por minutos as temperaturas em um servidor NoSQL MongoDB Atlas e uma interface de usuário Windows onde se pode coletar e analisar esses dados. O projeto inteiro foi desenvolvido em três linguagens: javascript nos scripts Atlas, Delphi na aplicação Windows que fornece a interface gráfica com usuário e C++ no programa que roda no Arduino.

Nesta aplicação foi descartado o uso de um servidor de banco de dados NoSQL local porque reduziria sua utilidade. Dentre as opções online optamos pelo MongoDB por oferecer um serviço gratuito na forma do MongoDB Atlas. A única limitação dessa versão gratuita que afeta nossa demonstração é estar limitada a 500MB.

Próxima parte: Criando e configurando uma conta MongoDB Atlas

Tutorial MongoDB Atlas: conta, bancos e coleções

$
0
0

Neste tutorial vamos usar uma conta gratuita MongoDB Atlas, que é limitada a 500MB (04/2020) de dados.

Visite https://www.mongodb.com/cloud/atlas e clique em Start Free (1). Na próxima página você pode se registrar usando uma conta Google ou email, nome, sobrenome e senha (2). Essas
são as únicas informações pedidas e pelo menos até abril de 2020 não era pedida confirmação do email até a data deste trabalho. Preencha tudo como no exemplo e clique em Get Started Free (3):

mongodb_atlas_1_automalabs.com.br

Agora clique em Create a Cluster – FREE para criar uma conta gratuita

mongodb_atlas_10_automalabs.com.br

Na próxima tela você escolhe o provedor de nuvem e a localidade. O default (no caso abaixo, um servidor Amazon na Virgínia do Norte) é suficiente. Clique em Create Cluster.mongodb_atlas_10_automalabs.com.br

A seguir MongoDB Atlas vai instalar e configurar o MongoDB no provedor de nuvem escolhido. Esse processo pode levar alguns minutos e você deve esperar enquanto os avisos, em inglês, estiverem aparecendo na tela:

mongodb_atlas_10_automalabs.com.br

Quando os avisos sumirem você pode passar para o próximo passo, que é criar um usuário. Clique em Create your first database user.

mongodb_atlas_10_automalabs.com.br

Clique em Database Access:

mongodb_atlas_10_automalabs.com.br

Clique em Add New Database User:

mongodb_atlas_10_automalabs.com.br

Na próxima tela não altere as opções padrão. Insira um nome de usuário (1), senha (2) e clique em Add User.

mongodb_atlas_10_automalabs.com.br

Esse usuário vai ser criado com permissões para ler e escrever em qualquer banco de dados, mas não vai poder administrar o banco de dados. Isso é o bastante para nossa aplicação.

Agora clique em Whitelist your IP addres:

mongodb_atlas_10_automalabs.com.br

Clique em Network Access:

mongodb_atlas_10_automalabs.com.br

Clique em Add IP Address:

mongodb_atlas_10_automalabs.com.br

Você pode clicar em ADD CURRENT IP ADDRESS (1) para colocar o seu endereço IP externo público, mas a menos que você esteja em uma conexão que tem IP fixo esse IP vai mudar pelo menos toda vez que você reiniciar seu modem. Pelo menos durante a fase de aprendizagem é melhor clicar em ALLOW ACCESS FROM ANYWHERE (2), que vai dar acesso ao banco de dados de qualquer lugar (adiciona 0.0.0.0/0, como mostrado). Finalize clicando em Confirm (3).

mongodb_atlas_10_automalabs.com.br

O próximo passo da lista, Load Sample Data, é opcional, mas pode ser interessante. Ele carrega no banco de dados algumas coleções de documentos de exemplo que você pode usar enquanto está
aprendendo MongoDB. Ocupa mais da metade do espaço disponível no serviço gratuito, mas não vamos precisar de muito espaço neste projeto. De qualquer forma este tutorial vai prosseguir sem
esse passo opcional. Você terá outras oportunidades de carregar essas coleções.
Também não vamos precisar do passo Connect your Cluster, porque vamos usar o serviço Stitch. Então finalize o assistente clicando em No thanks:

 

mongodb_atlas_14_automalabs.com.br

Em seguida clique em Dismiss it:

mongodb_atlas_15_automalabs.com.br

 

Agora nós vamos criar uma coleção. Isso não é estritamente necessário porque, como veremos adiante, a coleção é automaticamente criada quando necessário. Mas mostraremos como é feito
assim mesmo. Em um banco de dados orientado a documentos uma coleção é o equivalente a uma tabela em um banco relacional.
Clique em Collections:

 

mongodb_atlas_10_automalabs.com.br

Clique em Add My Own Data:

mongodb_atlas_10_automalabs.com.br
Dê um nome para o banco de dados (1), um nome para a coleção (2) e clique em Create (3):

mongodb_atlas_10_automalabs.com.br

 

Para criar um novo banco de dados, clique em Create Database (1). Para criar uma nova coleção mova o mouse sobre o nome do banco de dados e clique no sinal + que aparece (2).

mongodb_atlas_10_automalabs.com.br

Pronto. Você já tem o necessário para criar o equivalente a bancos e tabelas no MongoDB. Em outros posts eu explicarei como inserir dados nesse banco usando um ESP8266 e como extrair dados dele usando o Delphi.

Viewing all 63 articles
Browse latest View live