/*
 SetEEprom:
 Sencillo programa para borrar y leer o escribir la EEprom del ATMEGA328
 Manualde uso: SetEEprom.txt
 
 Hardware: Arduino UNO
 
 (c) Heli Tejedor, helitp@gmail.com, Mayo 2017
 
 Este Software se distribuye bajo licencia
 Creative Commons 3.0 Reconocimiento, No Comercial, Compartir Igual (CC BY-NC-SA 3.0)
 http://creativecommons.org/licenses/by-nc-sa/3.0/deed.es_ES
 http://creativecommons.org/licenses/by-nc-sa/3.0/
 
 Puerto serie configurado a 115200 bauds 
*/

#include <EEPROM.h>                     // Para manejar la EEprom       

#define VERSION "Set EEprom V 0.1"             // Version 
#define AUTHOR  " (c) Heli Tejedor, Mayo 2017" // Autor 

 // Variables y datos para comunicaciones serie
#define MAXBYTES 64                     // Maximo 64 bytes y 64*2 caracteres
#define MAXBUFF 24+(MAXBYTES*2)         // Para poder hacer un Write EEprom de MAXBYTES
char MySerialBuffer[MAXBUFF];           // Buffer para comunicaciones serie
byte MySerialIndex = 0;                 // Indice para introducir datos

   // Variables para ParseSerialCommand 
char *SavePtr;   
const char Space[] = " \n\r";

// ===============================================================================
char GetHex (char *c)
{
  byte D = *c - '0';
  if (D > 9) D -= 7;
  c++;
  byte E = *c - '0'; 
  if (E > 9) E -= 7;
  return (D<<4|E); 
}

// ===============================================================================
void ParseHEX (uint8_t* Addr, word Len, bool Write)
{
  if (Write)
  {
    while (Len--)
    {
      eeprom_write_byte(Addr++, GetHex(SavePtr));
      SavePtr+=2;
    } 
  }
  else
  {
    while (Len--)
    {
      byte Data = eeprom_read_byte(Addr++);
      if (Data < 0x10) Serial.print('0');
      Serial.print(Data, HEX);
    } 
    Serial.println();  
  }
}

// ===============================================================================
void ParseString (uint8_t* Addr, byte Len, bool Write)
{
  if (Write)
  {
    while (Len)
    {
       Len--;
       eeprom_write_byte(Addr++, *SavePtr);
       if (*SavePtr++ == '\0') break;                // Termina con fin cadena
    }
    while (Len--) eeprom_write_byte(Addr++ , '\0');  // Rellena
  }
  else
  {
    while (Len--)
    {
      byte Data = eeprom_read_byte(Addr++);
      if (Data == '\0') break;                      // Acabar
      Serial.write((char)Data);     
    }
    Serial.println();  
  } 
}

// ===============================================================================
void ParseDEC (uint8_t* Addr, bool Write)
{
  if (Write)
    eeprom_write_byte(Addr, atoi(SavePtr) );             
  else 
    Serial.println (eeprom_read_byte(Addr));
}

// ===============================================================================
// Procesa los mensajes serie recibidos para configuracion 
// ===============================================================================
void ParseSerialCommand (void)
{
  bool WriteMode = false;
  char *Token;
  
  MySerialBuffer[MySerialIndex] = '\0'; // Convierte el bufer en cadena   
  MySerialIndex = 0;

    // Debug
  Serial.println(MySerialBuffer); 
  
    // Comprobar el password 
  Token = strtok_r(MySerialBuffer, Space, &SavePtr); 
   
    // Obtiene el primer token: Read o Write y pone flags adecuados 
  if (strcmp (Token, "Write") == 0) WriteMode = true;
  else if (strcmp (Token, "Read") != 0)   
         { Serial.println(F("Read or Write?")); return; }

    // Obtiene el Second Token: comando
  Token = strtok_r(NULL, Space, &SavePtr);
  if (strcmp (Token, "Version") == 0)
  {
     if (WriteMode); // Reservado para funciones futuras
     else Serial.println(VERSION);
  }
  else if (strcmp (Token, "Init") == 0)
  {
    if (!WriteMode) return;
    if (strcmp (strtok_r(NULL, Space, &SavePtr), "EEprom") == 0)
    {
      uint8_t* Addr = 0;
      while (Addr < (uint8_t*)1024)           // 1024 = tamaño de la eeprom del atmega328
        eeprom_write_byte(Addr++, 0xFF);  
      Serial.print(F("EEprom iniciada a 0xFF: "));
    }
  }    
    // Los siguientes comandos necesitan Src 
  int Src = atoi(strtok_r(NULL, Space, &SavePtr));
  if (strcmp (Token, "ByteH") == 0) 
  {  
    ParseHEX (Src, 1, WriteMode);  
  }    
  else if (strcmp (Token, "ByteD") == 0) 
  {  
    ParseDEC (Src, WriteMode); 
  }
    // Los siguientes comandos necesitan tambien Len 
  int Len = atoi(strtok_r(NULL, Space, &SavePtr));    
  if (strcmp (Token, "EEprom") == 0) 
  {   
    if (Len > MAXBYTES) { Serial.print(F("Demasiados bytes!")); return; }
    ParseHEX ((uint8_t*)Src, Len, WriteMode);      
  }
  else if (strcmp (Token, "String") == 0)      
  {   
    if (Len > MAXBYTES*2) { Serial.print(F("Demasiados bytes!")); return; }    
    ParseString (Src, Len, WriteMode);        
  } 
  else 
  {
    Serial.println(F("Funcion?")); 
    return;
  }
  Serial.println(F("OK"));
}
      
// ===============================================================================
void setup() 
{
  // Inicia el puerto serie a velocidad rapida
  Serial.begin(115200);
  Serial.print(F(VERSION));
  Serial.println(F(AUTHOR));  
}

// ===============================================================================
void loop() 
{                                    
    // Lee datos serie y los salva en buffer, con LF o CR los procesa 
  byte NumC = Serial.available();
  while (NumC--)
  {
    char Leido = (char)Serial.read();
    if (MySerialIndex < MAXBUFF)
    {     
      if (Leido == '\n' || Leido == '\r') 
      { if (MySerialIndex > 0 ) ParseSerialCommand(); }     
      else MySerialBuffer[MySerialIndex++] = Leido;     
    }
    else MySerialIndex = 0;
  }
}



