Firmware del contador Endesa / Enel con telegestión

En la entrada anterior está desglosado a grandes rasgos el contador. Existen varios dispositivos en el contador que almacenan información. El principal es la memoria flash del microcontrolador principal (ARM STM32F101RCT6). Éste dispone de 512Kb de memoria flash, es decir memoria de programa. Esta memoria almacena el código que ejecuta el microcontrolador, y en principio no es editable en tiempo de ejecución. Es programable a efectos prácticos, un número de veces ilimitado. El contador, como se puede ver en la imagen en la entrada anterior, dispone de un puerto de programación JTAG. Es por ello que el microcontrolador, se sirve de una memoria externa. En este caso una clásica EEPROM 24512, mediante interfaz I2C. En esta entrada, me centraré en la lectura de la memoria en cuestión. No obstante en el circuito, existe una memoria adicional descrita en la entrada anterior, usada para almacenar información relativa a la comunicación PLC, objeto que hoy no nos ocupa.

Lectura EEPROM contador

Detalle de la lectura EEPROM

El primer paso para leer la EEPROM es desoldarla. Pese a que se puede leer, directamente conectada al contador, para las pruebas posteriores es preferible extraerla, asi se puede manejar con comodidad, y disponer de la propia alimentación. Para desoldar la EEPROM he utilizado una estación de aire caliente por comodidad, aunque perfectamente podría haber utilizado el soldador de tipo lápiz.

Punta de la pistola

Punta de la pistola

Detalle de la temperatura

Detalle de la temperatura

Desoldar la memoria es un proceso rápido. Así queda la placa después de extraer la memoria.

Lugar donde va soldada la EEPROM

Lugar donde va soldada la EEPROM

Una vez extraída la memoria debe conectarse a un lector. El encapsulado es un SO8, así que haría falta algún tipo de adaptador. Sino, siempre se puede recurrir al adaptador del pobre, esto es, soldar directamente cables en los pines de la memoria. Dado que el encapsulado tiene un espaciado de pines considerable, es una tarea relativamente sencilla.

EEPROM con cables soldados

EEPROM con cables soldados

Pinout EEPROM 24512

Pinout EEPROM 24512

Básicamente todo el lado izquierdo (pines 1 a 4) va conectado a GND. Vcc a +3.3V y SCL y SDA al lector. Por seguridad, he conectado WC (Write control) a +3.3V para evitar la escritura accidental.

Por curiosidad, si intentamos encender el contador sin la memoria EEPROM, en la pantalla muestra un mensaje de un BOOTLOADER y acto seguido muestra un error. Según la publicidad que Endesa está haciendo del contador, el firmware del mismo será actualizable vía PLC. No obstante, no es posible actualizar en tiempo de ejecución la memoria flash del contador. Por consiguiente, toda información relacionada con el firmware, y múltiples parámetros sujetos al cambio, incluso la propia calibración del aparato estarán almacenados en la memoria EEPROM. Quizá por ese motivo hayan escogido una memoria de capacidad considerable (512 Kbit).

Ya que no dispongo de ningún lector de EEPROM, he desempolvado el Arduino. Aunque arduino trabaja a 5V, según el datasheet de la memoria, soporta hasta 5.5V, con lo que no habrá problema. Tras conectarlo todo al ordenador, y escribir un sencillo programa, comienza a leer la memoria.

#include <Wire.h>

void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
    int rdata = data;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.write(rdata);
    Wire.endTransmission();
  }

  void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddresspage >> 8)); // MSB
    Wire.write((int)(eeaddresspage & 0xFF)); // LSB
    byte c;
    for ( c = 0; c < length; c++)
      Wire.write(data[c]);
    Wire.endTransmission();
  }

  byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
    byte rdata = 0xFF;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,1);
    if (Wire.available()) rdata = Wire.read();
    return rdata;
  }

  void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,length);
    int c = 0;
    for ( c = 0; c < length; c++ )
      if (Wire.available()) buffer[c] = Wire.read();
  }

  void PrintHex8(uint8_t *data, uint8_t length) //imprime los HEX con los ceros
{
        //Serial.print("0x");
        for (int i=0; i<length; i++) {
          if (data[i]<0x10) {Serial.print("0");}
          Serial.print(data[i],HEX);
          Serial.print(" ");
        }
}

void PrintHex16(uint16_t *data, uint8_t length) //imprime los HEX con los ceros
{
        Serial.print("0x");
        for (int i=0; i<length; i++)
        {
          uint8_t MSB=byte(data[i]>>8);
          uint8_t LSB=byte(data[i]);

          if (MSB<0x10) {Serial.print("0");} Serial.print(MSB,HEX); Serial.print(" ");
          if (LSB<0x10) {Serial.print("0");} Serial.print(LSB,HEX); Serial.print(" ");
        }
}

  void setup()
  {
    //char somedata[] = "mi mama me mima";
    Wire.begin(); // initialise the connection
    Serial.begin(115200);
    //i2c_eeprom_write_page(0x50, 0, (byte *)somedata, sizeof(somedata));

  }

  void loop()
  {
    uint8_t data;
    unsigned int count = 16;
    for(unsigned int y=0; y<65600; y++) {
      if(count == 16) {
        Serial.println("");
        Serial.print(y,HEX);
        Serial.print(":\t");
        count=0;
      }
       delay(5);
      data = i2c_eeprom_read_byte(0x50, y); /
      delay(5);
      Serial.print(" ");
      PrintHex8(&data,1);
      count++;

    }
  }
Montaje con el orden que me caracteriza

Montaje con el orden que me caracteriza

Finalmente obtenemos el código que contiene la memoria. En un principio, lo he copiado en hexadecimal, en un archivo de texto. A la izquierda, las direcciones de memoria. Cada pareja de caracteres representa un byte. Dada la extensión del texto, podéis consultar el archivo entero en este enlace.

0: 08 00 C7 FF 00 00 92 0E 03 0F 06 18 7D 1F 7A 15
10: C0 A2 04 16 06 86 31 31 4E 44 45 45 41 41 55 38
20: 30 38 33 30 B7 19 01 08 C4 02 01 08 00 00 00 01
30: 00 00 00 00 00 03 17 02 E9 5F 42 7F 3B B6 B2 87
40: F3 96 00 00 00 00 7A 53 79 38 8B 3F 22 2F E3 F6
50: 8A DF 00 00 00 00 06 00 00 00 00 00 00 00 00 00
60: 00 00 00 00 00 00 00 00 50 03 E8 05 DC 00 FA FF
70: 37 00 00 00 00 00 00 00 00 00 00 00 87 07 E2 8A
80: 03 05 03 05 01 03 00 00 00 00 00 00 37 30 33 31
90: 30 4C 37 30 00 00 00 00 32 30 AB 15 4D 23 40 40
A0: A1 5A 72 5C B3 48 58 9A 23 68 00 00 00 00 00 00
B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
F0: 00 00 00 00 00 00 00 00 00 00 00 00 DB AC 85 83
100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
...

Como curiosidad, a la vista queda que la memoria está eminentemente vacía. En las próximas entradas, comenzaré con la interpretación del código  y con pruebas adicionales. Cabe decir que esta lectura corresponde al contador que no está programado, es decir, aun no se ha introducido información relativa al suministro o a la tarificación. Además en una de las pruebas desconecté la pila, perdiendo la información relativa a la hora, entre otras cosas.

EDIT: Por motivos legales he tenido que eliminar el contenido de la EEPROM.

EDIT: Actualización