Publicidade:

quarta-feira, 22 de julho de 2015

Arduino - Voltímetro com Display de 7 Segmentos

No post anterior (aqui) falei sobre como mostrar números com casas decimais em displays de 7 segmentos. Aproveitando esse mesmo exemplo, fiz um voltímetro, onde é possível ler a tensão de uma fonte externa e mostrar seu valor nos displays.

Vídeo:



código-fonte:

/************************************************************************************************************
**********************************display de 7 segmentos*****************************************************
************************************************************************************************************/
const int  d7seg_pin_segments[] = {6,7,8,9,10,11,12,13};   //pinos para os segmentos:  --> a b c d e f g .
const int  d7seg_pin_enable[]   = {2,3,4,5};               //pinos para habilitar os displays
const byte d7seg_digits[]       = {B11111100, B01100000, B11011010, B11110010, B01100110, 
                                   B10110110, B10111110, B11100000, B11111110, B11110110};  //Babcdefg.  0--9

void d7seg_write(int digit, int pos, boolean point=false) {
  digitalWrite(d7seg_pin_enable[pos], HIGH);
  for(int i=0;i<7;i++) { digitalWrite(d7seg_pin_segments[i],   d7seg_digits[digit] & (1 << (7-i))    ); }
  digitalWrite(d7seg_pin_segments[7], point);
  delay(1);  //alterar aqui pro valor mais adequado
  digitalWrite(d7seg_pin_enable[pos], LOW);
}

void d7seg_write_number(float f, int decimals=0) {
  f = (f+0.000001) * pow(10, decimals);
  for(int i=0; i<sizeof(d7seg_pin_enable)/sizeof(int); i++) {  
    d7seg_write( (unsigned int)(f/pow(10, i)) % 10, i,  (i!=0)&&(decimals==i) );  
  }
}
/************************************************************************************************************
**********************************fim display de 7 segmentos*************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************voltimetro*****************************************************************
************************************************************************************************************/
const unsigned long r1   = 1000000;  //resistor de 1M
const unsigned long r2   = 100000;   //resistor de 100K
const unsigned int  aRef = 5;        //referencia de 5v
float               tensao = 0;      //tensao lida
unsigned long       millis_ref = 0;
const unsigned long time_refresh = 500; //faz nova leitura a cada 500 ms

float get_voltage(int pin){
  return (analogRead(pin) * aRef) / 1023.0 * ( (r1+r2)/r2 );
}

void show_voltage(){
  if ( (millis()-millis_ref) > time_refresh ) { //intervalo de tempo pra atualizar a leitura. 
    tensao = get_voltage(A0); //calculo da tensão lida
    millis_ref = millis();
  }
  float f = tensao;
  d7seg_write_number(f, f>=1000 ? 0 : (f>=100 ? 1 : (f>=10 ? 2 : 3) ) );  //de acordo com o numero, mostra 0, 1, 2 ou 3 casas decimais
}
/************************************************************************************************************
**********************************fim voltimetro*************************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************setup/loop*****************************************************************
************************************************************************************************************/
void setup(){
  for(int i=2; i<=13; i++) { pinMode(i, OUTPUT); }
}  

void loop() {
  show_voltage();
}

/************************************************************************************************************
**********************************fim setup/loop*************************************************************
************************************************************************************************************/




Referência: http://br-arduino.org/2015/06/voltimetro-com-arduino-como-montar-programar-e-calibrar.html

terça-feira, 21 de julho de 2015

Arduino - Display de 7 Segmentos com Casas Decimais

Nesse post vou mostrar um exemplos simples e funcional de como mostrar números com casas decimais em displays de 7 segmentos diretamente com o Arduino, sem nenhum drive entre o Arduino e os displays.

O Exemplo mostrado no vídeo possui 4 displays em série, mas nada impede que sejam adicionados outros, bastando apenas modificar no código, a constante d7seg_pin_enable, onde deve ser informado os pinos a mais utilizados. No exemplo, foram utilizados os pinos, 2, 3, 4 e 5. E fisicamente, devem ser incluídos os componentes (displays, transistores e resistores) adicionais no circuito, seguindo o esquema abaixo.

Da mesma maneira que é possível incluir mais, pode-se também utilizar esse código, com menos displays, 3, 2 ou 1.

Os dígitos do display (números de zero a nove) são definidos através da constante d7seg_digits, no qual foram necessários apenas 10 bytes, sendo um byte para cada dígito que pode ser representado no em cada um dos displays.

Como um byte tem oito bits, foi possível definir um bit para cada segmento do display. Em termos de economia de memória essa é uma excelente tática, ao contrário de vários outros exemplos que podem ser encontrados na internet, os quais muitas vezes, não possuem os devidos cuidados na hora de definir os tipos corretos de dados para representar cada um dos dígitos. O único inconveniente ao utilizar bytes para representar dígitos, é a "dificuldade" que o programador menos experiente pode sentir ao tentar ler um determinado bit (segmento) na hora de ligar/desligar o display. O segredo para isso, é utilizar operadores de bitwise, o qual podemos ver nessa parte do código:

d7seg_digits[digit] & (1 << (7-i))

onde o "7-i", indica qual bit do byte que será lido.



No exemplo do vídeo tem um potenciômetro ligado ao pino A0. o valor lido no A0, que por padrão retorna um valor entre 0 e 1023, é convertido pra uma escala que vai entre 0 e 2000. Quando o valor a ser mostrado é maior que 1000, nenhuma casa decimal é mostrada. Já quando o número está entre 100 e 999 o valor a ser mostrado no display terá uma casa decimal, já para valores inferiores a 99, o valor aparece com 2 casas decimais. Isso foi feito pra não "perder" dígitos no caso de valores pequenos tendo uma maior precisão do valor lido em caso de números menores.





const int  d7seg_pin_segments[] = {6,7,8,9,10,11,12,13};   //pinos para os segmentos:  --> a b c d e f g .
const int  d7seg_pin_enable[]   = {2,3,4,5};               //pinos para habilitar os displays
const byte d7seg_digits[]       = {B11111100, B01100000, B11011010, B11110010, B01100110, 
                                   B10110110, B10111110, B11100000, B11111110, B11110110};  //Babcdefg.  0--9

void d7seg_write(int digit, int pos, boolean point=false) {
  digitalWrite(d7seg_pin_enable[pos], HIGH);
  for(int i=0;i<7;i++) { digitalWrite(d7seg_pin_segments[i],   d7seg_digits[digit] & (1 << (7-i))    ); }
  digitalWrite(d7seg_pin_segments[7], point);
  delay(1);  //alterar aqui pro valor mais adequado
  digitalWrite(d7seg_pin_enable[pos], LOW);
}

void d7seg_write_number(float f, int decimals=0) {
  f = (f+0.000001) * pow(10, decimals);
  for(int i=0; i<sizeof(d7seg_pin_enable)/sizeof(int); i++) {  
    d7seg_write( (unsigned int)(f/pow(10, i)) % 10, i,  (i!=0)&&(decimals==i) );  
  }
}

void setup(){
  for(int i=2; i<=13; i++) { pinMode(i, OUTPUT); }
}

float               f = 0;      //tensao lida
unsigned long       millis_ref = 0;
const unsigned long time_refresh = 500; //faz nova leitura a cada 500 ms

void loop() {
  
  if ( (millis()-millis_ref) > time_refresh ) { //intervalo de tempo pra atualizar a leitura. 
    f = ((analogRead(A0)) / 1023.0 * 2000.0);
    millis_ref = millis();
  }
  
  d7seg_write_number(f, f>=1000 ? 0 : (f>=100 ? 1 : 2) );  //de acordo com o numero, mostra 0, 1 ou 2 casas decimais

}

quarta-feira, 17 de junho de 2015

Arduino - Ethernet Shield + SDCard + Arquivos


Nesse vídeo mostro como incluir imagens, javascript, e outros tipos de arquivos e mídias na sua aplicação web baseada no Ethernet Shield W5100


31/10/2016 - Observação
Erro de login

Várias pessoas têm me relatado problemas no login, e eu não havia conseguido achar o problema, mas o Rodrigo (nos comentários abaixo) encontrou o problema e postou a solução. Vou manter o código original, e se quem tiver erros, siga o que o Rodrigo recomendou:

//Descobri o problema e mudei o meu código. 
//A função base64_encode não retorna corretamente o primeiro byte codificado, 
//por isso a função validar_usuario sempre recebia a string "out" errada. 
//Tive que trocar a função base64_encode para uma que retornasse o valor 
//inteiro de "out" codificado. Dai minha função validar_usuario ficou assim:

boolean validar_usuario(char * linebuf) {
  char usuario_senha[] = "admin:admin"; //"admin:admin";
  byte t = strlen(usuario_senha);
  int tamanhoEnc = (((t-1) / 3) + 1) * 4; //tamanho da string codificada
  char *out = base64_encode(usuario_senha, t);
  char out2[tamanhoEnc];

  for (t=0; t<(tamanhoEnc); t++) {
    out2[t] = linebuf[21+t];
  }
  return (strstr(out2, out)>0);
} 


Entenda o Mime Types:
https://pt.wikipedia.org/wiki/MIME

Lista de todos (acredito eu) os Mime Types de Arquivos:
http://www.sitepoint.com/web-foundations/mime-types-complete-list/



Para rodar a aplicação, copie os seguintes arquivos para dentro do SDCard



atmel.pdf

Qualquer arquivo PDF chamado atmel.pdf (cuidado com o tamanho do arquivo)

favicon.ico

qualquer arquivo do tipo ico, chamado favicon.ico

uno.jpg

qualquer imagem do tipo jpg, chamada uno.jpg


os demais arquivos, devem ser conforme mostrados abaixo


HTML - index.htm

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" type="text/css" href="/css.css">
  <script src="/js.js"></script>
</head>
<body onload="init();">
  <h1>ARDUINO - ETHERNET SHIELD + SDCARD + ARQUIVOS</h1>
  <br>
  <img src="/uno.jpg" />
  <br>
  <a href="/atmel.pdf">Download datasheet</a> | <a href='/logoff'>Logoff</a>
  <br>
</body>
</html>


Javascript - js.js

function init(){
  window.alert("hello world!");
}



CSS - css.css

body {
    background-color: #FAEBD7;
}
h1 {
    margin-left: 40px;
} 




Sketch - código-fonte:

/*
 Web Server - HTTP Autentication
 Baseado na versão de exemplo do Arduino.
 
   - exemplo que mostra como incluir html, css, 
   javascript e imagem em arquivos no sdcard e também
   como fazer download de um arquivo pdf do sdcard.
  
   by Fabiano A. Arndt (fabianoallex)
*/

/**********************************************************************************
************************************BIBLIOTECAS************************************
**********************************************************************************/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

/**********************************************************************************
********************************FIM BIBLIOTECAS************************************
**********************************************************************************/

/**********************************************************************************
*************************ROTINAS USUARIO E SENHA***********************************
***********************************************************************************/

boolean validar_usuario(char * linebuf) {
  /*
  nesse exemplo o usuario e senha estão definidos dentro do código fonte.
  mas o usuário e senha poderiam ser autenticados de diversas maneiras,
  lendo os dados de um servidor web, arquivo texto, etc, bastando apenas atribuir
  o valor lido para a variável usuario_senha. 
  */
  
  char usuario_senha[] = "admin:admin";                           //usuario e senha para acessar a pagina
  int t = strlen(usuario_senha);
  
  int tamanhoEnc = (((t-1) / 3) + 1) * 4;   //tamanho da string codificada
  char out[tamanhoEnc];
  base64_encode(out, usuario_senha, t+1 );
  
  //---desconta é usado pra eliminar os caracteres '='
  int desconta = 0;
  if ((t%3) == 1) { desconta = 2; }
  if ((t%3) == 2) { desconta = 1; }
  
  char out2[tamanhoEnc-desconta];
  
  byte i;
  for (i=0; i<(tamanhoEnc-desconta);i++){ out2[i] = out[i]; }
  out2[i] = '\0';
  
  return ( strstr(linebuf, out2)>0 );
}

/**********************************************************************************
*************************FIM ROTINA USUARIO E SENHA********************************
***********************************************************************************/

/****************************************************************************************
 ****************************SD CARD INICIO***********************************************
 ****************************************************************************************/

#define PIN_SD_CARD 4
 
boolean iniciar_sd_card() {
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
   
  if (!SD.begin(PIN_SD_CARD)) { return false; }
   
  return true;
}

 
/****************************************************************************************
 ****************************SD CARD FIM**************************************************
 ****************************************************************************************/
 

/**********************************************************************************
***********************************PAGINAS HTML************************************
***********************************************************************************/
EthernetServer * server;

void write_from_file(EthernetClient &client, char * filename){
  File webFile = SD.open(filename);
  if (webFile) {
    while(webFile.available()) {
      client.write(webFile.read()); 
    }
    webFile.close();
  } else {
    Serial.print("Erro SD CARD: ");
    Serial.println(filename);
  }
}

void iniciar_ethernet_01(){
  byte ip[4]      = {192,168,200,25};                    //definir aqui o ip
  byte gateway[4] = {192,168,200,254};
  byte subnet[4]  = {255,255,255,0};
  byte mac[6]     = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  int  porta      = 80;

  server = new EthernetServer(porta);

  Ethernet.begin(mac, ip, gateway, subnet);         //caso necessario gateway utilizar essa linha
  server->begin();
}

void iniciar_ethernet_02(){
  byte ip[4]      = {192,168,200,25};                    //definir aqui o ip
  byte mac[6]     = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  int  porta      = 80;

  server = new EthernetServer(porta);

  Ethernet.begin(mac, ip);
  server->begin();
}

void iniciar_ethernet() {              //escolher apenas um dos dois modos de iniciar o ethernet shield
  iniciar_ethernet_01();               //inicia com ip, gateway, mascara e porta
  //iniciar_ethernet_02();             //inicia só com ip e porta
}

void html_cab_200_ok(EthernetClient &client){
  
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/html\n"
                   "Connection: keep-alive\n\n"));
}


void html_logoff(EthernetClient &client){
    client.print(F(
                 "HTTP/1.1 401 Authorization Required\n"  
                 "Content-Type: text/html\n"  
                 "Connnection: close\n\n"  
                 "<!DOCTYPE HTML>\n"  
                 "<html><head><title>Logoff</title>\n"  
                 "<script>document.execCommand('ClearAuthenticationCache', 'false');</script>"  //IE logoff
                 "<script>try {"                                                                //mozila logoff
                 "   var agt=navigator.userAgent.toLowerCase();"
                 "   if (agt.indexOf(\"msie\") != -1) { document.execCommand(\"ClearAuthenticationCache\"); }"
                 "   else {"
                 "     var xmlhttp = createXMLObject();"
                 "      xmlhttp.open(\"GET\",\"URL\",true,\"logout\",\"logout\");"
                 "     xmlhttp.send(\"\");"
                 "     xmlhttp.abort();"
                 "   }"
                 " } catch(e) {"
                 "   alert(\"erro ao fazer logoff\");"
                 " }"
                 "function createXMLObject() {"
                 "  try {if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();} else if (window.ActiveXObject) {xmlhttp=new ActiveXObject(\"Microsoft.XMLHTTP\");}} catch (e) {xmlhttp=false;}"
                 "  return xmlhttp;"
                 "}</script>"
                 "</head><body><h1>Voce nao esta mais conectado</h1></body></html>\n"));
}

void html_autenticar(EthernetClient &client) {
  client.print(F("HTTP/1.1 401 Authorization Required\n"  
               "WWW-Authenticate: Basic realm=\"Area Restrita\"\n"  
               "Content-Type: text/html\n"  
               "Connnection: close\n\n"  
               "<!DOCTYPE HTML>\n"  
               "<html><head><title>Error</title>\n"  
               "</head><body><h1>401 Acesso nao autorizado</h1></body></html>\n"));
}

void html_autenticado(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/html\n"
                   "Connection: keep-alive\n"));

  write_from_file(client, filename);
}


void js_file(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/javascript\n"
                   "Connection: keep-alive\n"));

  write_from_file(client, filename);
}

void css_file(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/css\n"
                   "Connection: keep-alive\n"));

  write_from_file(client, filename);
}

void favicon_file(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: image/x-icon\n"
                   "Connection: keep-alive\n"));

  write_from_file(client, filename);
}


void pdf_file_download(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: application/download\n"
                   "Connection: close\n"));
    
  write_from_file(client, filename);
}

void jpg_file(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: file/jpg\n"
                   "Connection: close\n"));
    
  write_from_file(client, filename);
}


void exec_ethernet() {
  EthernetClient client = server->available();
  if (client) {
    char linebuf[80];
    memset(linebuf, 0, sizeof(linebuf));
    
    int     charCount          = 0;
    boolean autenticado        = false;
    boolean currentLineIsBlank = true;
    boolean logoff             = false;
    boolean indCss             = false;
    boolean indJs              = false;
    boolean indPdfDataSheet    = false;
    boolean indJpgUno          = false;
    boolean indFavicon         = false;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        linebuf[charCount] = c;
        
        if (charCount<sizeof(linebuf)-1) { charCount++; }
        Serial.write(c);
        
        if (c == '\n' && currentLineIsBlank) {
          if (autenticado && !logoff ) {
            if (indJs){
              js_file(client, "js.js");                          //js file
            } else if(indCss){
              css_file(client, "css.css");                       //css file
            } else if(indPdfDataSheet){
              pdf_file_download(client, "atmel.pdf");        //datasheet download
            } else if(indJpgUno){
              jpg_file(client, "uno.jpg");                       //jpg file
            } else if(indFavicon){
              jpg_file(client, "favicon.ico");                   //icone do browser
            } else {
              html_autenticado(client, "index.htm");             //página inicial
            }
          } else {
            logoff ? html_logoff(client) : html_autenticar(client);
          }
          break;
        }
        if (c == '\n') { 
          currentLineIsBlank = true;               
          
          if (strstr(linebuf, "GET /logoff"         )>0 ) { logoff = true; }
          if (strstr(linebuf, "Authorization: Basic")>0 ) { if ( validar_usuario(linebuf) )   {  autenticado = true;   } }  
          if (strstr(linebuf, "GET /css.css"        )>0 ) { indCss = true; }
          if (strstr(linebuf, "GET /js.js"          )>0 ) { indJs = true; }
          if (strstr(linebuf, "GET /atmel.pdf"      )>0 ) { indPdfDataSheet = true; }
          if (strstr(linebuf, "GET /uno.jpg"        )>0 ) { indJpgUno = true; }
          if (strstr(linebuf, "GET /favicon.ico"    )>0 ) { indFavicon = true; }
          
          
          memset(linebuf, 0, sizeof(linebuf));
          charCount = 0;
        } else if (c != '\r') {
          currentLineIsBlank = false;    // you've gotten a character on the current line
        }
      }
    }
    
    delay(1);           
    client.stop();      
  }
}
/**********************************************************************************
**************************************** FIM PAGINAS HTML**************************
***********************************************************************************/


/**********************************************************************************
*************************BASE 64 CODE/DECODE DO USUARIO E SENHA********************
***********************************************************************************/

void a3_to_a4(unsigned char * a4, unsigned char * a3);
void a4_to_a3(unsigned char * a3, unsigned char * a4);
unsigned char b64_lookup(char c);

int base64_encode(char *output, char *input, int inputLen) {
  const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  int i=0, j=0, encLen=0;
  unsigned char a3[3], 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;
}

/*
int base64_decode(char * output, char * input, int inputLen) {
  int i = 0, j = 0;
  int decLen = 0;
  unsigned char a3[3];
  unsigned char a4[4];
  while (inputLen--) {
    if (*input == '=') { break; }
    a4[i++] = *(input++);
    if (i == 4) {
      for (i = 0; i <4; i++)  { a4[i] = b64_lookup(a4[i]); }
      a4_to_a3(a3,a4);
      for (i = 0; i < 3; i++) { output[decLen++] = a3[i];  }
      i = 0;
    }
  }

  if (i) {
    for (j=i; j<4; j++)   { a4[j] = '\0';             }
    for (j=0; j<4; j++)   { a4[j] = b64_lookup(a4[j]);}
    a4_to_a3(a3,a4);
    for (j=0; j<i-1; j++) { output[decLen++] = a3[j]; }
  }
  output[decLen] = '\0';
  return decLen;
}
*/

//int base64_enc_len(int plainLen) {
//  int n = plainLen;
//  return (n + 2 - ((n + 2) % 3)) / 3 * 4;
//}

//int base64_dec_len(char * input, int inputLen) {
//  int i = 0;
//  int numEq = 0;
//  for(i = inputLen - 1; input[i] == '='; i--) { numEq++; }
//  return ((6 * inputLen) / 8) - numEq;
//}

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);
}

//void a4_to_a3(unsigned char * a3, unsigned char * a4) {
//  a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
//  a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
//  a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
//}

unsigned char b64_lookup(char c) {
  if(c >='A' && c <='Z') return c - 'A';
  if(c >='a' && c <='z') return c - 71;
  if(c >='0' && c <='9') return c + 4;
  if(c == '+') return 62;
  if(c == '/') return 63;
  return -1;
}

/**********************************************************************************
*************************FIM BASE 64 CODE/DECODE DO USUARIO E SENHA****************
***********************************************************************************/

/**********************************************************************************
**************************************** VOID / LOOP ******************************
***********************************************************************************/

void setup() {
  Serial.begin(9600);
  iniciar_sd_card();
  iniciar_ethernet();
}

void loop() {
  exec_ethernet();
}

/**********************************************************************************
*************************************FIM VOID / LOOP*******************************
***********************************************************************************/