PROGMEM - Resolvido por meio de dicas

Olá,

Estou apanhando do Arduino ultimamente.
Estourei a memoria do Arduino fazendo alguns FOR.
Me indicaram usar o PROGMEM e até aí tudo bem. Declarei arrays nas novas variáveis e estou lendo elas direitinho.
Meu problema está sendo dar um update em um dos valores de um array.

É possível isso com o PROGMEM?
Exemplo:

Declarei no código um array com 8 posições:

prog_uint16_t buttonStateOSCLigths1 PROGMEM = {0,0,10,0,0,0,0,0};

Quero mudar o terceiro valor para 15.
Estou tentando isso mas não surte efeito:

pgm_read_word(buttonStateOSCLigths1 + 2)==0;

Quando mando printar o resultado na tela, ele me exibe 10. Ou seja, ele não mudou.

Serial.print(pgm_read_word_near(buttonStateOSCLigths1 + 2));

Alguém saberia me dizer qual comando poderia usar para fazer isso?

Obrigado,
Felipe.

Felipe, não tenho muita experiencia nisto, mas vou arriscar um palpite.
O comando criou uma tabela na area de programa , ou seja, a tabela fica na flash não na SRAM, portanto não é alteravel durante a execução do programa.

É exactamente o que o valter disse...

Muito obrigado amigos.

Sou novo no Arduino e está sendo difícil programar em baixíssimo nível.
Imaginei que fosse algo do gênero.

Então, para manipular valores de um Array não vai ter muito jeito. Terei que usar o SRAM certo? Ou teria outra solução?
Pergunto pois hoje meu projeto já roda com arrays tipo int

int buttonStateOSCLigths1[10];
int buttonStateOSCLigths2[10];
int buttonStateOSCLigths3[10];
int buttonStateOSCLigths4[10];

São 60 lâmpadas que manipulo valores 0 e 100(totalmente apagada e totalmente acesa).
Uso tb uma interface OSC para acender e apagar lâmpadas pelo celular.
Tudo funciona bem por alguns dias, depois o Arduino para de responder e acredito que seja o estouro de memória.
E quando peço para fazer um Loop para pegar todos os valores e enviá-los para interface OSC afim de atualizar o celular com as lâmpadas que estão acesas e apagas, esse tempo de alguns dias diminui drasticamente para 1 ou 2 dias.

E por esse motivo resolvi estudar o uso da memória flash.

Ps.: Essas lâmpadas são as lâmpadas da minha sala, ou seja, minha esposa fica extremamente chateada quando a coisa não funciona...rs

Desde já agradeço imensamente o pronto atendimento dos colegas.

Abraço,
Felipe.

LOL

Baixissimo nível? Estás a usar C e C++… Que é que chamas a Assembly?

hehehehe Não a linguagem, a programação.
Estou acostumado a criar programas que rodam em servidores. O que está difícil é lidar com as limitações do Arduino.

Ja agora amigo coloca ai o teu código estou curioso para saber onde é que rebentas com a RAM .
Posso já estar a falar baboseiras mas ja pensaste que o podes estar a implementar da forma errada?

Hugo, sempre existe a possibilidade de estar implementando de forma errada.

Deixei apenas a parte onde utilizo os arrays.

Separei o código por Tabs para ficar mais fácil dar manutenção.
E como o post ficou grande, vou separá-lo em 2 pois passou de 9500 caracteres.

Tab principal:

#include <SPI.h> //inclui bibliotecas necessarias para Ethernet
#include <Ethernet.h> //inclui bibliotecas necessarias para Ethernet
#include <OneWire.h> //inclui bibliotecas necessarias para Ethernet

#include <ArdOSC.h>
#include <XBee.h>    //inclui bibliotecas necessarias do xBee
#include <SoftwareSerial.h>    //inclui bibliotecas necessarias para comunicação com placas Dimmer8CH

int delayDimmer = 300; //Delay entre os acionamentos de diferentes canais - 300 é o ideal mas vale testes com 200ms
int buttOnOff = 0; //Variável verificadora para botão está ON ou OFF
int a = 1;
int physicalButtonState[20];
int count[20];

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  // Try to replace with DS MAC
byte centralIP[] = {10,0,1,82};
byte destIPiPad[] = {10,0,1,15};
byte destIPiPadMini[] = {10,0,1,18};
byte destIPiPhone[] = {10,0,1,8};

int serverPort = 8000;
int destPort = 9000;
int state = 0;

OSCServer server;
OSCClient client;
IPAddress ip(centralIP);
int programButtonOSC;

String buttonOSCgetIp;
String buttonOSCCenas[10];
String buttonOSCLigths1[10];
String buttonOSCLigths2[10];

int buttonStateOSCCenas[10];
int buttonStateOSCLigths1[10];
int buttonStateOSCLigths2[10];

String origemEnviaMsgOSC;
//---------------------------------------------FIM Variaveis OSC---------------------------------------------

void setup(){
  Serial.begin(19200);
  Serial.println("Serial ready!");

  Ethernet.begin(mac, ip);
  Serial.print( "Ethernet MAC:" ); //Print MACAddress na tela
  for(int i = 0; i < 6; i++ )
  {
    Serial.write( ' ' );
    Serial.print( mac[i], HEX );
  }
  Serial.println();
  Serial.print( "My IP address: " ); //Print IP na tela
  Serial.println( Ethernet.localIP() );

  Serial.println("Ethernet ready!");
  server.begin(serverPort);
  Serial.println("OSC ready!");
  Serial.println( "Online and ready!" );
  server.begin(serverPort);
  //-------------------------------------------Comandos-------------------------------------------------  
  server.addCallback("/home/button0",&button0);
  server.addCallback("/home/button100",&button100);
  //----------------------------------------------Cenas-------------------------------------------------  
  server.addCallback("/home/button1",&button1_1);
  server.addCallback("/home/button2",&button1_2);
  server.addCallback("/home/button3",&button1_3);
  server.addCallback("/home/button4",&button1_4);
  server.addCallback("/home/button5",&button1_5);
  server.addCallback("/home/button6",&button1_6);
  //-------------------------------------------Sequencias------------------------------------------------  
  server.addCallback("/lights/button1",&button2_1);
  server.addCallback("/lights/button2",&button2_2);
  server.addCallback("/lights/button3",&button2_3);
  server.addCallback("/lights/button4",&button2_4);
  server.addCallback("/lights/button5",&button2_5);
  server.addCallback("/lights/button6",&button2_6);
  server.addCallback("/lights/button7",&button2_7);
  server.addCallback("/lights/button8",&button2_8);
  server.addCallback("/lights/button9",&button2_9);
  server.addCallback("/lights/button10",&button2_10);
  server.addCallback("/lights/button11",&button2_11);
  server.addCallback("/lights/button12",&button2_12);
  server.addCallback("/lights/button13",&button2_13);
  server.addCallback("/lights/button14",&button2_14);
  server.addCallback("/lights/button15",&button2_15);
  server.addCallback("/lights/button16",&button2_16);

  OSCButtonConfig();
}
void loop(){
  if(server.aviableCheck()>0){   //Verifica se OSC esta ativo
  }
}

Tab OSCConfigs:

void OSCButtonConfig(){

//Variáveis que armazenam os botões OSC para envio de msg  
   buttonOSCCenas[0] = ("/home/button0");  

   buttonOSCCenas[1] = ("/home/button1");
   buttonOSCCenas[2] = ("/home/button2");
   buttonOSCCenas[3] = ("/home/button3");
   buttonOSCCenas[4] = ("/home/button4");
   buttonOSCCenas[5] = ("/home/button5");
   buttonOSCCenas[6] = ("/home/button6");
   buttonOSCCenas[7] = ("/home/label10"); 
   
   buttonOSCLigths1[1] = ("/lights/button1"); 
   buttonOSCLigths1[2] = ("/lights/button2"); 
   buttonOSCLigths1[3] = ("/lights/button3"); 
   buttonOSCLigths1[4] = ("/lights/button4"); 
   buttonOSCLigths1[5] = ("/lights/button5"); 
   buttonOSCLigths1[6] = ("/lights/button6"); 
   buttonOSCLigths1[7] = ("/lights/button7"); 
   buttonOSCLigths1[8] = (""); 
   
   buttonOSCLigths2[1] = ("/lights/button9"); 
   buttonOSCLigths2[2] = ("/lights/button10"); 
   buttonOSCLigths2[3] = (""); 
   buttonOSCLigths2[4] = ("/lights/button12"); 
   buttonOSCLigths2[5] = ("/lights/button13"); 
   buttonOSCLigths2[6] = ("/lights/button14"); 
   buttonOSCLigths2[7] = ("/lights/button8"); 
   buttonOSCLigths2[8] = ("/lights/button11"); 
  
   buttonOSCCenas[99] = ("/lights/ip");
   buttonOSCCenas[100] = ("/home/button100");

}

Tab OSC_RX:

void button0(OSCMessage *_mes){
    refreshOSC(1,(int)_mes->getArgFloat(0));
    delay(delayDimmer);
    refreshOSC(2,(int)_mes->getArgFloat(0));
    delay(delayDimmer);
    refreshOSC(3,(int)_mes->getArgFloat(0));
}
void button100(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
 //String sourceIp = (byte)_mes->getIpAddress();  
  //Serial.println(sourceIp);
}


void button1_1(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(0,2,1,value);
}
void button1_2(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(0,2,2,value);
}
void button1_3(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(0,2,3,value);
}
void button1_4(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(0,2,4,value);
}
void button1_5(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(0,2,5,value);
}
void button1_6(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(0,2,6,value);
}




void button2_1(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(1,1,1,value);
}
void button2_2(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(1,1,2,value);
}
void button2_3(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(1,1,3,value);
}
void button2_4(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(1,1,4,value);
}
void button2_5(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(1,1,5,value);
}
void button2_6(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(1,1,6,value);
}
void button2_7(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(1,1,7,value);
}
void button2_8(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(2,1,7,value); //Saida Queimada
}
void button2_9(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(2,1,1,value);
}
void button2_10(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(2,1,2,value);
}
void button2_11(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(2,1,8,value); //Saida Queimada
}
void button2_12(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(2,1,4,value);
}
void button2_13(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(2,1,5,value);
}
void button2_14(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  OSC(2,1,6,value);
}
void button2_15(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  //OSC(2,1,7,value);
}
void button2_16(OSCMessage *_mes){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100); //converte valores (int)_mes->getArgFloat(0) de 0 e 1 para 0 e 100
  //OSC(2,1,8,value);
}

Tab sendRemoteAtComand:

void OSC(int origem, int forma, int button, int buttonState){
// origem = (1/2) numero da placa Dimmer  - (0) Cenas
// forma = grupo/sequencia (1/2)
  
if(origem==0){
origemEnviaMsgOSC = buttonOSCCenas[button];
buttonStateOSCCenas[button] = buttonState;
}
if(origem==1){
origemEnviaMsgOSC = buttonOSCLigths1[button];
buttonStateOSCLigths1[button] = buttonState;
}
if(origem==2){
origemEnviaMsgOSC = buttonOSCLigths2[button];
buttonStateOSCLigths2[button] = buttonState;
}
      OSCMessage txMes;
      txMes.setAddress(destIPiPad,destPort);
      txMes.beginMessage((origemEnviaMsgOSC).c_str());
      txMes.addArgFloat(buttonState);
      client.send(&txMes);
      txMes.flush(); 

      OSCMessage txMes1;
      txMes1.setAddress(destIPiPadMini,destPort);
      txMes1.beginMessage((origemEnviaMsgOSC).c_str());
      txMes1.addArgFloat(buttonState);
      client.send(&txMes1);
      txMes1.flush(); 

      OSCMessage txMes2;
      txMes2.setAddress(destIPiPhone,destPort);
      txMes2.beginMessage((origemEnviaMsgOSC).c_str());
      txMes2.addArgFloat(buttonState);
      client.send(&txMes2);
      txMes2.flush(); 
}

void refreshOSC(int origem, int start){
// origem = numero da placa Dimmer (1/2)

  if(origem==1){
      for (int i=1; i <= 10; i++){
        OSCMessage txMes1;
        txMes1.setAddress(destIPiPadMini,destPort);
        txMes1.beginMessage((buttonOSCCenas[i]).c_str());
        txMes1.addArgFloat(buttonStateOSCCenas[i]);
        client.send(&txMes1);
        txMes1.flush(); 
      }   
      for (int i=1; i <= 10; i++){
        OSCMessage txMes;
        txMes.setAddress(destIPiPad,destPort);
        txMes.beginMessage((buttonOSCCenas[i]).c_str());
        txMes.addArgFloat(buttonStateOSCCenas[i]);
        client.send(&txMes);
        txMes.flush(); 
      }   
      for (int i=1; i <= 10; i++){
        OSCMessage txMes2;
        txMes2.setAddress(destIPiPhone,destPort);
        txMes2.beginMessage((buttonOSCCenas[i]).c_str());
        txMes2.addArgFloat(buttonStateOSCCenas[i]);
        client.send(&txMes2);
        txMes2.flush(); 
      }   
  }
  if(origem==2){
      for (int i=1; i <= 10; i++){
        OSCMessage txMes1;
        txMes1.setAddress(destIPiPadMini,destPort);
        txMes1.beginMessage((buttonOSCLigths1[i]).c_str());
        txMes1.addArgFloat(buttonStateOSCLigths1[i]);
        client.send(&txMes1);
        txMes1.flush(); 
      }   
      for (int i=1; i <= 10; i++){
        OSCMessage txMes;
        txMes.setAddress(destIPiPad,destPort);
        txMes.beginMessage((buttonOSCLigths1[i]).c_str());
        txMes.addArgFloat(buttonStateOSCLigths1[i]);
        client.send(&txMes);
        txMes.flush(); 
      }   
      for (int i=1; i <= 10; i++){
        OSCMessage txMes2;
        txMes2.setAddress(destIPiPhone,destPort);
        txMes2.beginMessage((buttonOSCLigths1[i]).c_str());
        txMes2.addArgFloat(buttonStateOSCLigths1[i]);
        client.send(&txMes2);
        txMes2.flush(); 
      }   
  }
  if(origem==3){
      for (int i=1; i <= 10; i++){
        OSCMessage txMes1;
        txMes1.setAddress(destIPiPadMini,destPort);
        txMes1.beginMessage((buttonOSCLigths2[i]).c_str());
        txMes1.addArgFloat(buttonStateOSCLigths2[i]);
        client.send(&txMes1);
        txMes1.flush(); 
      }   
      for (int i=1; i <= 10; i++){
        OSCMessage txMes;
        txMes.setAddress(destIPiPad,destPort);
        txMes.beginMessage((buttonOSCLigths2[i]).c_str());
        txMes.addArgFloat(buttonStateOSCLigths2[i]);
        client.send(&txMes);
        txMes.flush(); 
      }   
      for (int i=1; i <= 10; i++){
        OSCMessage txMes2;
        txMes2.setAddress(destIPiPhone,destPort);
        txMes2.beginMessage((buttonOSCLigths2[i]).c_str());
        txMes2.addArgFloat(buttonStateOSCLigths2[i]);
        client.send(&txMes2);
        txMes2.flush(); 
      }   
  }       
}

Esqueci de um detalhe importante... possuo um Arduino Mega

Mais precisamente esse:

Bom amigo olhando para o teu codigo vejo que abusas das Strings.Elas comem muita RAM e tu usas las com fartura.
Depois outra coisa que reparo é que tens muitas Strings que basicamente dizem o mesmo mudando apenas os ultimos caracteres.Nao lendo o teu codigo todo parece me que apenas usas as Strings sem alteraçao do seu conteudo o que se assim for podes armazenalas na flash e nao na SRAM usando o PROGMEM

buttonOSCCenas[1] = ("/home/button1");
buttonOSCCenas[2] = ("/home/button2");
buttonOSCCenas[3] = ("/home/button3");
buttonOSCCenas[4] = ("/home/button4");
buttonOSCCenas[5] = ("/home/button5");
buttonOSCCenas[6] = ("/home/button6");
buttonOSCCenas[7] = ("/home/label10");

porque nao antes algo assim:

prog_uchar cena1[] PROGMEM  = {"/home/button1"};
prog_uchar cena2[] PROGMEM  = {"/home/button2"};
prog_uchar cena3[] PROGMEM  = {"/home/button3"};
prog_uchar cena4[] PROGMEM  = {"/home/button4"};

Aqui vão alguns comentários...
A tua placa é mais concretamente um EtherMega... eu estive para comprar uma, mas achei um roubo. Quanto pagaste de portes por ela? (isto não é comentário... é curiosidade...)

Não deves olhar para um microcontrolador como sendo diferente dum computador. Tu quando escreves um programa, também não pensas se vais usar a memória RAM. Aqui é igual nesse ponto.
A diferenca é que em microcontroladores a memória é muito pequena e como tal tens de olhar sempre ao que estás a criar nas tuas variáveis.

O C permite variáveis unsigned char ou char que ocupam 8 bits... um int ocupa 16 (e dá mais trabalho a processar). Aqueles arrays que criaste precisam necessariamente de ser ints? Se puderem ser chars poupas logo imenso... eu reparo que não existe uma única variável char ou unsigned char no teu código... e tenho a certeza que muitas delas não precisam de ser ints.

Se não pretendes mudar a configuracão do teu módulo IP, podes definir essas variáveis como const. Isso faz com que deixem de estar em memória e passem a estar na memória de programa. O mesmo para configuracão de delays ou pinos de entrada/saída.

O uso de Strings é a causa mais provável dos problemas que estás a ver... num microprocessador tão limitado o objecto String só funciona para acender LEDs. Sendo assim... Arrays de Strings é pedir sarilhos.

Tens imensas strings literais que, julgo eu, podem ser também metidas para a memória de programa... pesquisa por isso no site do Arduino. Como tens um Mega, aumentas o tamanho do programa, mas a memória fica mais livre.

No tue ficheiro OSC_RX... tens imensas funcões iguais... alguma necessidade para isso? Não poderias alterar a funcão para isto:

void button1_1(OSCMessage *_mes, unsigned char var1, unsigned char var2, unsigned char var3){
  int value=map((int)_mes->getArgFloat(0),0,1,0,100);
  OSC(var1,var2,var3,value);
}

É uma funcão em vez de 22... e julgo que a maior parte dessas vars pode ficar guardada numa matriz em memória de programa.

Quanto a isto:

if(origem==0){
origemEnviaMsgOSC = buttonOSCCenas[button];
buttonStateOSCCenas[button] = buttonState;
}
if(origem==1){
origemEnviaMsgOSC = buttonOSCLigths1[button];
buttonStateOSCLigths1[button] = buttonState;
}
if(origem==2){
origemEnviaMsgOSC = buttonOSCLigths2[button];
buttonStateOSCLigths2[button] = buttonState;
}

Certamente que já ouviste falar do switch... aqui ficava a matar. :wink:

Não sei mesmo ao certo o que estoura a memória... mas noto que prestaste pouca atencão à poupanca de RAM no programa. Eu aponto o dedo às Strings que tens no programa... mas como não é simples de alterar o programa podes comecar a ver onde poupar memória nos outros pontos que te referi.

Sempre que fores criar uma variável, em vez de só criar, pára e questiona-te: "Até quanto é que esta variável tem de contar??" ou se for um array "qual é o máximo que eu vou permitir meter aqui dentro?"

Após algum tempo, vais ver que isto se torna natural e deixa de ser uma imposicão.
Eu sempre supus que os programas que rodam em servidores deviam ter as mesmas precaucões... :stuck_out_tongue:

Olá bubulindo,

Sim, é um EtherMega e o preço da placa está no link logo abaixo da imagem.
Preferi a compra de uma placa com Ethernet integrada do que usar um Mega e um Shield Ethernet. Economiza espaço.

Muito obrigado pelas dicas.
Com certeza irão ajudar e já começarei a implementar todas elas.

Quanto a qualidade do código, ele foi feito apenas para funcionar. Não tive tempo de refactorar o código para ficar limpo.
A história é meio longa mas basicamente tive 4 meses de aprendizado, entre não saber absolutamente nada de Arduino, passando por comprar um kit starter até chegar nisso:

Somente agora estou refazendo o código para ficar limpo.

O importante para mim foi a dica de com quais tipos de variáveis o Arduino trabalha melhor. Nunca olhei para o processamento dele e por isso o post pedindo ajuda.

Hugo, tks a lot!!!

Mais uma vez muito obrigado pelas dicas,
Felipe.