Armazenar valores maior que 2000000 (dois milhões)

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.

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:

#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.

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.

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.

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

}

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?

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

... no entanto poderia me explicar como funciona PosicaoQuantidadePulsos?

Leia o comentário na linha no código que postei:

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

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.

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.

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.

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.

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

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

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":

#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":

#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:

#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. :wink:

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.

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):

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:

#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!

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á.

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:

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:

#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:

#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. :slight_smile:

PS.: "pqp" é um nome estranho :astonished:

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.

Muito obrigado giova014, implementei o código que você me ensinou ao meu código e funcionou do jeito que eu preciso. Estou fazendo as alterações finais e quando estiver pronto postarei aqui.

Só me ficou uma última dúvida, esse método eu consigo contabilizar números long, unsigned long normalmente ou poderá ocorrer erros?

Muito bom! :slight_smile:

Só me ficou uma última dúvida, esse método eu consigo contabilizar números long, unsigned long normalmente ou poderá ocorrer erros?

Esses métodos EEPROM.get() e EEPROM.put() funcionam para qualquer tipo de variável, só precisa ficar atento a quantos bytes esta variável ocupa.
Não vejo o porque daria problemas com número long ou unsigned long, portanto pode utilizá-los normalmente, sem preocupação.

Boa sorte!