Go Down

Topic: Armazenar valores maior que 2000000 (dois milhões)  (Read 2416 times) previous topic - next topic

omecatron

Olá a todos, preciso de uma ajuda. Fiz um código que gera pulsos com frequências de até 22Hz, com dois tipos de modo: contínuo e pulsado. Estou usando um display 20x4, um Arduíno Mega e um teclado com 7 botões no qual utiliza apenas um pino analógico. Até aqui tudo bem, a cada sessão é executado aproximadamente 2500 pulsos isso várias vezes por dia, o equipamentos já está funcionando, com execução da primeira linha do LCD que não está operado.

Esta linha soma os valores dos pulsos aplicado em cada sessão com os valores de pulsos armazenados na EMPROM.

No entanto não sei como escrever um valor tão grande na EMPROM. Sendo que o limite por mim definido seria entre 2 a 3 milhões de pulsos.

O escopo do código está todo bagunçado, mas sabendo o limite de gravação fiz o seguinte: o valor só é gravado na EEPROM quando pressionar o botão desliga do aparelho.


Então minha dúvida principal seria como gravar dados tão grande na EMPROM e posteriormente ler esse valor.

Logo mais posto meu código.

giova014

De acordo com a referência da biblioteca EEPROM, você pode usar os métodos EEPROM.put() e EEPROM.get() para salvar e recuperar qualquer tipo de variável na EEPROM.

Para número maiores que dois milhões, pode declarar a variável com um dos seguintes tipos:
long -> Número de -2,147,483,648 até 2,147,483,647.
unsigned long -> Número de 0 até 4,294,967,295.

Um exemplo básico usando EEPROM e o tipo indicado:
Code: [Select]

#include <EEPROM.h>

unsigned long quantidadePulsos = 0; // Declara e inicializa a variável que armazena a quantidade de pulsos

const unsigned int PosicaoQuantidadePulsos = 0; // Posicao (byte) na EEPROM que a variavel quantidadePulsos sera armazenada

void setup() {
 Serial.begin(115200);
 Serial.println("--- INICIO ---");
 
 quantidadePulsos = 3543879;
 Serial.print("quantidadePulsos original = "); Serial.println(quantidadePulsos);
 EEPROM.put(PosicaoQuantidadePulsos,quantidadePulsos);
 
 quantidadePulsos = 2132332;
 Serial.print("quantidadePulsos depois de alterar = "); Serial.println(quantidadePulsos);
 
 EEPROM.get(PosicaoQuantidadePulsos,quantidadePulsos);
 Serial.print("quantidadePulsos depois de recuperar da EEPROM = "); Serial.println(quantidadePulsos);
 
 Serial.println("--- FIM ---");
}

void loop() {
 \\ NADA
}


Note que estes dois tipos ocupam 4 bytes, portanto depois de PosicaoQuantidadePulsos, a próxima posição usável é a 5.

Como observação final, perceba que coloquei diversos links, que em uma rápida busca você poderia ter encontrado, além de muitos outros resultados.
Arduino!!

bubulindo

Não sabendo a criticalidade do sistema, deixo apenas uma dica que pode ser interessante. Se conseguires detectar que o sistema perdeu energia e teres uma reserva (condensadores ou algo do género), aquando da perda de alimentação seria também uma boa altura para guardar os valores.

This... is a hobby.

omecatron

Muito obrigado giova014 e bubulindo, farei um teste de acordo com giova014,  no entanto poderia me explicar como funciona PosicaoQuantidadePulsos?

Porque no meu caso a quantidade de pulsos começa de zero e com o passar dos dias (cerca de 10 dias)  de uso semi constante esse valor estaria já em 500 mil.

No caso essa variável incrementa sozinha conforme o número de pulsos aumenta?

E bubulindo,  agradeço pela observação,  o equipamento será desligado apenas uma vez por dia, ou seja seria necessário 100 mil dias para não conseguir regravar mais. No entanto não pensei no caso de alguém desinformado puxar o cabo da tomada ou acabar a energia. Muito bem lembrado.

omecatron

#include <EEPROM.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

long a = 0;
long b = 9888;
int resto1 = 0;          // escrever em uma posição a segunda parte
int resto2 = 0;          // escrever em uma posição a segunda parte
int vr1 = 0;           // valor resto 1
int vr2 = 0;           // valor rest 2
int vodi = 0;          // valor original do inteiro
long valor_original = 0;
long inteiro = 0;

void setup()
{
  lcd.begin(20, 4);
}

void loop()

{
  int x = analogRead (0);

  if (x >= 500 && x <= 530) // reset
  {
    EEPROM.write(0, 0);
    EEPROM.write(1, 0);
    EEPROM.write(2, 0);
  }

  if (x >= 800 && x <= 830) // pedal
  {
    a ++;
    lcd.setCursor(0, 0);
    lcd.print(a);
    resto1 = (a % b) / 100;          // escrever em uma posição a segunda parte
    resto2 = (a % b) % 100;          // escrever em uma posição a segunda parte
    inteiro = a / b;
  }

  if (valor_original <= 0 && x >= 300 && x <= 400) // desliga
  {
    lcd.clear();
    EEPROM.write(0, a / b);         // Primeira parte guarda o inteiro de A
    EEPROM.write(1, resto1);        // Primeira parte do resto de A
    EEPROM.write(2, resto2);        // Segunda parte do resto de A
  }

  if (valor_original >= 1 && x >= 300 && x <= 400) // desliga
  {
    EEPROM.write(0, vodi + (a / b));
    EEPROM.write(1, vr1 + resto1);
    EEPROM.write(2, vr2 + resto2);
  }
 
  vodi = EEPROM.read(0);         // valor original do inteiro
  vr1 = EEPROM.read(1);           // valor resto 1
  vr2 = EEPROM.read(2);           // valor rest 2
  valor_original = (vodi * b) + ((vr1 * 100) + vr2);
  lcd.setCursor(0, 3);
  lcd.print(valor_original);

}

omecatron

Essa parte do código funciona no entanto não como deveria. Eu divido o número de pulsos por um valor e encontro seu inteiro e módulo, até aí tudo bem.

Se não tiver nada na memória ele faz a primeira gravação dos pulsos e desliga a máquina, mas se a memória já conter uma valor o programa pega esse valor e e soma com os pulsos aplicados que ainda não foram gravados na EEPROM.

Como eu disse a programação funciona,  mas não 100%, quando vou fazer o teste ele consegue somar próximo de 12 mil pulsos. Alguém pode me dar uma luz?

giova014

Você testou e entendeu o código que forneci?

Quote
... no entanto poderia me explicar como funciona PosicaoQuantidadePulsos?
Leia o comentário na linha no código que postei:
Code: [Select]
const unsigned int PosicaoQuantidadePulsos = 0; // Posicao (byte) na EEPROM que a variavel quantidadePulsos sera armazenada

Quote
Porque no meu caso a quantidade de pulsos começa de zero e com o passar dos dias (cerca de 10 dias)  de uso semi constante esse valor estaria já em 500 mil.

No caso essa variável incrementa sozinha conforme o número de pulsos aumenta?
Perceba que no começo da linha há a palavra "const", que significa que está variável é constante e não pode ser mudada, pois se você está usando um endereço na EEPROM para gravar a variável não pode simplesmente mudar para outro endereço, não faz sentido.

Quote
Essa parte do código funciona no entanto não como deveria. Eu divido o número de pulsos por um valor e encontro seu inteiro e módulo, até aí tudo bem.
Por que faz essa divisão se ela não é necessária?
Mostrei os métodos EEPROM.get() e EEPROM.put() pois eles são apropriados para salvar qualquer tipo de variável sem se preocupar com tratamento de bytes, pois acabam acontecendo erros, como no seu caso.
Troque os métodos EEPROM.read() e EEPROM.write() pelos que mostrei. Se quiser continuar usando EEPROM.write(), pelo menos o mude para EEPROM.update() para diminuir o gasto da EEPROM.

Quote
Como eu disse a programação funciona,  mas não 100%, quando vou fazer o teste ele consegue somar próximo de 12 mil pulsos. Alguém pode me dar uma luz?
Defina "mas não 100%".
Quais os resultados gerados?
Quais os resultados esperados?
Como se diferem?

O que são as variáveis "a" e "b" no seu programa? Use nomes mais descritivos, que ajudem a entender o programa.
Arduino!!

omecatron

Vamos por parte:

1 - Sim, testei o código, mas não entendi a variável PosicaoQuantidadePulsos. No caso um número maior que 1 milhão será armazenado em apenas uma posição da memória?

2 - Também li o comentário da linha.

3 - a parte que você mencionou "const" eu não compreendi pelo fato dos pulsos ocuparem várias casas.

4 - Eu havia feito a divisão para dividir o número em partes e poder guardá - lo desfragmentado e não sobrescrever a memória. E o código eu havia feito antes de ver seu comentário. Mas farei um novo teste baseado no seu exemplo.

5 - E em relação a EEPROM.get ( ) e EEPROM.put ( ), vamos supor que eu desligue o aparelho e religue, como eu iria imprimir no visor o valor armazenado usando essas funções? 

Tipo com EEPROM.write e EEPROM.read para eu imprimir o valor na memória eu faço assim:

lcd.setCursor (0,0);
lcd.print (EEPROM.read (x));

onde x pode ser qualquer posição da memória.

6 - E eu não preciso usar EEPROM.update pois eu só escrevo na memória o valor acumulado de pulsos quando desliga o aparelho.

7 - Não funciona 100% pelo fato de contar até 12 mil e zera.

8 - Eu esperava somar na memória mais de 2 milhões.

9 - se diferem quado chega na casa de 12 mil e zera tudo e começa a recontar.

10 - E por fim a variável "a" é minha variável que conta mais que 2 milhões de pulsos e "b" é o valor que eu uso na minha divisão para obter números menores.




omecatron

A minha variável "a" é incrementada cada vez que eu pressiono um determinado botão.

giova014

Muito boas as informações que deu.  :)

Ok, tentarei explicar melhor.

Nas minhas explicações irei chamar a variável "a" de "quantidadePulsos", para facilitar o entendimento.

A variável "quantidadePulsos" é do tipo "unsigned long" com tamanho 4 bytes em um Arduino Uno ou Mega.

Os métodos EEPROM.get() e EEPROM.put() são métodos já tratam este tamanho de 4 bytes quando lendo ou escrevendo na EEPROM.

Para a posição, poderia simplesmente usar um número constante 0 ou 1234, porém usar uma varável auxiliar ajuda a manter o código e alterar facilmente a posição.

O fato da ser "const" é porque na execução do programa não faz sentido a posição ser mudada, pois você "perderia" a variável.


Exemplo de como escrever na EEPROM, a variável "quantidadePulsos":
Code: [Select]
#include <EEPROM.h>

unsigned long quantidadePulsos = 0; // Declaração no escopo global

EEPROM.put(0,quantidadePulsos); //  Ao executar esta linha, a variável quantidadePulsos será armazenada na posição 0 (zero) e ocupará 4 bytes (posições 0 ~ 3)


Exemplo de como ler da EEPROM, a variável "quantidadePulsos":
Code: [Select]
#include <EEPROM.h>

unsigned long quantidadePulsos = 0; // Declaração no escopo global

EEPROM.get(0,quantidadePulsos); // Ao executar esta linha, a variável quantidadePulsos será carregada com os valores dos 4 bytes a partir da posição 0 (posição 0 ~ 3)


Note que em ambos os exemplos, as posições 0 ~ 3 estão ocupadas, portanto se fosse armazenar outra variável na EEPROM, deveria usar a posição 4.


Eu particularmente reservo a posição 0 da EEPROM para detectar se é a primeira vez que o programa é executado, pois a EEPROM vem inicializada com todos os bytes inicializada em 0xFF (hex).
Exemplo:
Code: [Select]
#include <EEPROM.h>

unsigned long quantidadePulsos = 0; // Declaração no escopo global

const unsigned long EEPROM_quantidadePulsos = 1; // Salva na posicao 1, pois a 0 ja esta sendo usada

void setup(){

...

if(EEPROM.read(0) == 0xFF){ //Verifica se é a primera inicialização
EEPROM.write(0, 0x00)

EEPROM.put(EEPROM_quantidadePulsos, quantidadePulsos); // Inicializa a EEPROM
}
else {
EEPROM.get(EEPROM_quantidadePulsos, quantidadePulsos); // Carrega da EEPROM
}

...

}

void loop(){
...
}


Espero tenha sido claro, senão traga as dúvidas.   :smiley-wink:
Arduino!!

omecatron

Muito boa sua explicação giova014.

 Me corrija se eu estiver errado, então para eu imprimir no lcd o valor armazenado ficaria assim:

lcd.print (EEPROM.get (0));

E isso?

Gostei de mais do seu método e irei usar ele sim e quando tiver tudo pronto postarei o código aqui.

giova014

#11
Aug 02, 2017, 04:37 pm Last Edit: Aug 02, 2017, 04:40 pm by giova014
Quote
Me corrija se eu estiver errado, então para eu imprimir no lcd o valor armazenado ficaria assim:

lcd.print (EEPROM.get (0));

E isso?
Quase! O método EEPROM.get() necessita de dois argumentos.
De forma rápida, seria assim (considerando posição 0):
Code: [Select]
lcd.print (EEPROM.get (0,quantidadePulsos));

Recomendo carregar da EEPROM apenas UMA vez no setup() e não a cada vez que irá imprimir no LCD.

Assim, acho que seu programa fica modular, pois não irá misturar a EEPROM com o código e portanto poderá removê-lo mais facilmente (quando for trocar de Arduino, por exemplo, alguns não suportam EEPROM).

O funcionamento de seu programa seria assim:
- No setup carrega a variável, ou inicializa a EEPROM se for primeira inicialização
- Em seu código que contém a lógica, manipule a variável (direto da RAM), mas não a toque na EEPROM
- Quando for desligar grave na EEPROM

Um simples lcd.print(quantidadePulsos) imprime no LCD.

Ficaria, por exemplo:
Code: [Select]
#include <EEPROM.h>

// Variáveis da EEPROM
unsigned long quantidadePulsos = 0;
const unsigned int EEPROM_quantidadePulsos = 1; // Salva na posicao 1, pois a 0 ja esta sendo usada

// Variáveis e objetos da lógica do seu programa
...

void setup(){
// Carrega a "quantidadePulsos" da EEPROM (ou inicializa) usando "EEPROM_quantidadePulsos"
// setup() da lógica do seu programa
...
}

void loop(){
// Quando for desligar, salve "quantidadePulsos" na EEPROM usando "EEPROM_quantidadePulsos"
// loop() da lógica do seu programa
...
lcd.print(quantidadePulsos); // Pode imprimir normalmente
}

Veja que as partes ficam separadas e bem identificadas.

Como observação, use "const unsigned int" para posições na EEPROM, que é um tipo de variável com capacidade suficiente.

Se precisar salvar varias variáveis na EEPROM, posso lhe indicar outro método de gerenciamento no código, pois este que mostrei é bem direto porém fica difícil gerenciar conforme aumenta a quantidade.

Teste!
Arduino!!

omecatron

Fiz um teste com o seguinte código:

#include <EEPROM.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

unsigned long a = 0;
const int pqp = 0;

void setup()
{
  lcd.begin(20, 4);
}

void loop()

{
  int x = analogRead (0);

  if (x >= 500 && x <= 530)           // verifica se reset foi pressionado
  {
  // e limpa a memória
  }

  if (x >= 800 && x <= 830)           // de pedal for pressionado
  {
    a ++;                             // incrementa quantidadePulsos
    lcd.setCursor(0, 0);
    lcd.print("a  :");
    lcd.setCursor(5, 0);
    lcd.print(a);
  }

  if (x >= 300 && x <= 400)           // se desliga for pressionado
  {
    delay(200);
    EEPROM.put(pqp, a);              // grava na memória o valor de "a"
  }

  /*************imprimi o valor contido na memória******************************/

  lcd.setCursor(0, 2);
  lcd.print("memoria:");
  lcd.setCursor(9, 2);
  lcd.print(EEPROM.get(pqp,a));
}


Deveria funcionar correto?

No entanto o valor não salva na memória,  quando reiniciei o Arduíno o número não estava lá.

giova014

Por favor, coloque seus códigos dentro de tags [ code][ /code] para facilitar a leitura e cópia.

Acho que faltou uma explicação de como funciona o método EEPROM.get().
Aceita dois argumentos, o primeiro é a posição e o segundo é a variável.
Note que essa função retorna a própria variável do segundo argumento.
Toda vez que você chama esse método, o valor da variável é SUBSTITUÍDO pelo que está na EEPROM.

Portanto, nesta linha:
Code: [Select]
lcd.print(EEPROM.get(pqp,a));
Você está sempre carregando o valor antigo, descartando as mudanças feitas em linhas anteriores.

Tem duas soluções, salvar a variável toda vez que modifica-la ou usar o método de forma diferente.
Na primeira opção, que não é seu caso, pois quer apenas quando desligar, a EEPROM seria gasta muito mais rápida pois seriam gravadas muitas vezes durante a execução do código.
Na segunda opção, coloque o método EEPROM.get() apenas no setup(). O problema desta segunda opção é que se a energia pode cair a qualquer hora, e você só salva quando desliga, então perderia pulsos.

A escolha é sua. Lembre-se que a EEPROM é caracterizada com 100.000 gravações, então avalie.

Modificando seu código de acordo com a primeira opção:
Code: [Select]
#include <EEPROM.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

unsigned long a = 0;
const int pqp = 0;

void setup()
{
  lcd.begin(20, 4);
}

void loop()

{
  int x = analogRead (0);

  if (x >= 500 && x <= 530)           // verifica se reset foi pressionado
  {
  // e limpa a memória
  }

  if (x >= 800 && x <= 830)           // de pedal for pressionado
  {
    a ++;                             // incrementa quantidadePulsos
EEPROM.put(pqp, a); // <----- LINHA ADICIONADA

    lcd.setCursor(0, 0);
    lcd.print("a  :");
    lcd.setCursor(5, 0);
    lcd.print(a);
  }

  if (x >= 300 && x <= 400)           // se desliga for pressionado
  {
    delay(200);
    // EEPROM.put(pqp, a);              // grava na memória o valor de "a" <----- NÃO PRECISA MAIS DESSA LINHA
  }

  /*************imprimi o valor contido na memória******************************/

  lcd.setCursor(0, 2);
  lcd.print("memoria:");
  lcd.setCursor(9, 2);
  lcd.print(EEPROM.get(pqp,a));
}


Já com a segunda opção:
Code: [Select]
#include <EEPROM.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

unsigned long a = 0;
const int pqp = 0;

void setup()
{
  lcd.begin(20, 4);
EEPROM.get(pqp,a); <----- LINHA ADICIONADA
}

void loop()

{
  int x = analogRead (0);

  if (x >= 500 && x <= 530)           // verifica se reset foi pressionado
  {
  // e limpa a memória
  }

  if (x >= 800 && x <= 830)           // de pedal for pressionado
  {
    a ++;                             // incrementa quantidadePulsos
    lcd.setCursor(0, 0);
    lcd.print("a  :");
    lcd.setCursor(5, 0);
    lcd.print(a);
  }

  if (x >= 300 && x <= 400)           // se desliga for pressionado
  {
    delay(200);
    EEPROM.put(pqp, a);              // grava na memória o valor de "a"
  }

  /*************imprimi o valor contido na memória******************************/

  lcd.setCursor(0, 2);
  lcd.print("memoria:");
  lcd.setCursor(9, 2);
  lcd.print(a); // <----- LINHA MODIFICADA
}


Veja as setas de observação que coloquei nos códigos.

Entenda e traga as dúvidas. :)

PS.: "pqp" é um nome estranho  :smiley-eek:
Arduino!!

omecatron

Muito obrigado pela ajuda,  giova014,  gostei de usar esse método,  bem mais prático que o método que eu estava a desenvolver.

Obs1: pqp não é nome estranho, kkkk
pqp = PosicaoQuantidadePulsos

Obs 2: o método que eu estava a trabalhar funcionou, no entanto grava só se for acima de 10000 agora. Tipo, só posso salvar na EEPROM se os pulsos estiverem acima de  10 mil, mas funciona. Depois posto os códigos.

Go Up