I have a program running in a Nano. The program behavior is defined by a structure stored in EEPROM. I'd like to read that EEPROM data, but I don't want to compile and download a new program, because that will wipe the existing software. Is there a way to non-destructively read the EEPROM from an external device, be it via the IDE or otherwise?
(I'm not providing code, nor a schematic, as they're not germane to the question.)
Do you have the existing source code? Can you just add something to that to view the EEPROM data?
Hi,
"Is it possible to use ISP to read and write parts of (or whole of) EEPROM, without touching the program stored in flash, or any of the fuse-bits etc."
AVRDUDE has that capablity, however such AVRDUDE commands are not supported via the arduino IDE but rather using command line instructions.
Ref:
" Can EEPROM be written via ISP without overwriting/impacting flash - #2 by retrolefty "
presumably you know that the EEPROM is not reset when code is reprogrammed
only if your software erases it.
i'm working on a project where configuration information is stored in EEPROM. the program has utilities to writing and editing that configuration information in EEPROM.
perhaps the code needs to check if the EEPROM data is already written before overwriting it
@gcjr ,
I don't know if I got it right, but it looks like the OP wants to read the EEPROM but without having to reload or erase the current code.
Correct @camsysca ?
@ruilviana That's correct. Two issues; I'm not positive of the configuration of that node (yeah, I know, config management...), but it acts like a certain version of my code... and, there's a number being stored to EEPROM that, in retrospect, I'd like access to, but the nodes don't have a query for it. For those "in the know", this is a classic CMRI node, so the standard protocol, which this node has, is closed. I already have begun to extend the CMRI feature set for my own purposes, but I hadn't gotten to the point of adding EEPROM queries.
Anyway, @ruilviana, I'll follow up on the post you linked to if I decide I need the information badly enough. Thanks.
i'm working on a Computer Model RR Interface (C/MRI) myself, where the EEPROM contains configuration information for each node and may need to change as the code develops.
as mentioned, the EEPROM is no erased and new code is loaded.
the code needs to support capabilities to edit the EEPROM. this means initialize it the first time as well as change it later.
i'm using TLVs to store values. this allows
- values of varying length
- a search for a specific type, skipping entries knowing their length
- deleting records by simply setting their type to zero indicating no longer used.
function include adding and deleting new entries and listing the existing entries. more code than i expected. also handles corrupted entries
// eeprom routines
#include <Arduino.h>
#include <EEPROM.h>
#include "ee.h"
#include "node.h"
#include "pcRead.h"
#include "signals.h"
int dbgEE;
// -----------------------------------------------------------------------------
#define StrSize 30
TwrSym twr = (TwrSym) 99;
int board;
char ap0 [StrSize];
char ap1 [StrSize];
char name [StrSize];
// -------------------------------------
#define EEPROM_SIZE 100
enum ID {
ID_END = 0xFF,
ID_NAME = 10,
ID_SSID = 11,
ID_PASSWORD = 12,
ID_MASTER = 13,
ID_AP0 = 14,
ID_AP1 = 15,
ID_BOARD = 16,
ID_TWR = 17,
ID_UNKNOWN = 255,
ID_IGNORE = 0,
};
enum EE_TYPE {
EE_STR,
EE_INT,
EE_NUL,
};
// -------------------------------------
struct TLV {
byte id;
byte type;
void *pVar;
const char *label;
}
tlvs [] = {
{ ID_UNKNOWN, EE_STR, 0, "unknown" },
// { ID_NAME, EE_STR, name, "name" },
// { ID_SSID, EE_STR, name, "ssid" },
// { ID_PASSWORD, EE_STR, name, "password" },
// { ID_MASTER, EE_STR, name, "master" },
{ ID_AP0, EE_STR, ap0, "ap0" },
{ ID_AP1, EE_STR, ap1, "ap1" },
{ ID_BOARD, EE_INT, &board, "board" },
// { ID_HOST, EE_STR, name, "host" },
{ ID_TWR, EE_INT, &twr, "twr" },
{ ID_IGNORE, EE_NUL, 0, "ignore" },
};
#define NtlvStr (sizeof(tlvs)/sizeof(TLV))
// -----------------------------------------------------------------------------
static void _eeVars (void);
// -------------------------------------
#if 0
static const char *
_eeIdStr (
byte id )
{
TLV *t = tlvs;
for (unsigned n = 0; n < NtlvStr; n++, t++) {
if (t->id == id)
return t->label;
}
return "";
}
#endif
// -------------------------------------
// search for ID in table and return entry ptr
static TLV *
_eeTlv (
byte id )
{
if (dbgEE)
printf ("%s: %3d\n", __func__, id);
TLV *t = tlvs;
for (unsigned n = 0; n < NtlvStr; n++, t++) {
if (dbgEE)
printf (" %s: %3d %s\n", __func__, t->id, t->label);
if (t->id == id)
return t;
}
t = tlvs;
if (dbgEE)
printf (" %s: %3d %s\n", __func__, t->id, t->label);
return t;
}
// -------------------------------------
// print TLV code/label from table
static void
_eeCodes (void)
{
TLV *t = tlvs;
for (unsigned n = 0; n < NtlvStr; n++, t++)
printf (" %s: %3d %s\n", __func__, t->id, t->label);
}
// -------------------------------------
// search for ID in EEprom and return addr
static int
_eeAddr (
byte type)
{
for (int addr = 0; addr < EEPROM_SIZE; ) {
byte id = EEPROM.read (addr);
if (dbgEE)
printf (" %s: 0x%02x 0x%02x\n", __func__, type, id);
if (id == ID_END || id == type)
return addr;
addr += EEPROM.read (++addr)+1;
}
return ID_END;
}
// -------------------------------------
// set 1st EEprom byte to 0xFF, effectively erasing it
static void
_eeClear (void)
{
printf ("%s:\n", __func__);
EEPROM.write (0, ID_END);
EEPROM.commit ();
};
// -------------------------------------
static void
_eeDelete (
byte type)
{
printf ("%s: %d\n", __func__, type);
int addr = _eeAddr (type);
if (ID_END == EEPROM.read (addr)) {
printf (" %s: %d NOT_FOUND\n", __func__, type);
}
EEPROM.write (addr, ID_IGNORE);
EEPROM.commit ();
}
// -------------------------------------
// prepare EE for use
static void _eeLoad ();
void
eeInit (void)
{
printf ("%s:\n", __func__);
EEPROM.begin (EEPROM_SIZE);
_eeLoad ();
}
// -------------------------------------
static void
_eeList (void)
{
printf ("%s:\n", __func__);
for (int addr = 0; addr < EEPROM_SIZE; ) {
int adr = addr;
byte id = EEPROM.read (addr++);
int len = EEPROM.read (addr++);
if (id == ID_END)
goto done;
TLV *t = _eeTlv (id);
printf (" %s: 0x%03x %3d %3d", __func__, adr, id, len);
if (ID_UNKNOWN == t->id) {
printf (" unknown - abort");
goto done;
}
printf (" %s\n", t->label);
addr += len;
}
done:
printf ("\n");
return;
}
// -------------------------------------
// read all TLVs from EEprom
static void
_eeLoad (void)
{
printf ("%s:\n", __func__);
for (int addr = 0; addr < EEPROM_SIZE; ) {
byte id = EEPROM.read (addr++);
int len = EEPROM.read (addr++);
if (ID_END == id)
break;
TLV *t = _eeTlv (id);
printf (" %s: %3d %3d", __func__, id, len);
if (ID_UNKNOWN == t->id) {
printf (" unknown - abort\n");
return;
}
if (ID_IGNORE == t->id) {
printf (" %s\n", t->label);
addr += len;
continue;
}
if (EE_INT == t->type) {
int val = EEPROM.read (addr++);
* (int*) t->pVar = val;
printf (" %d", val);
}
else if (EE_STR == t->type) {
if (StrSize < len) {
printf (" len %d > %d - abort\n", len, StrSize);
return;
}
char *buf = (char*) t->pVar;
for (int i = 0; i < len; i++)
buf [i] = EEPROM.read (addr++);
printf (" %s", buf);
}
printf ("\n");
}
#if 0
_eeVars ();
#endif
}
// -------------------------------------
bool
_eeRead (
byte type,
char *buf,
int bufSize )
{
if (dbgEE)
printf ("%s:\n", __func__);
int addr = _eeAddr (type);
if (ID_END == EEPROM.read (addr)) {
printf (" %s: %d NOT_FOUND\n", __func__, type);
return false;
}
int len = EEPROM.read (++addr);
if (len > (bufSize - 1))
len = bufSize -1;
for (int n = 0; n < len; n++)
buf [n] = EEPROM.read (++addr);
buf [bufSize-1] = '\0';
printf (" %s: %d %s\n", __func__, type, buf);
return true;
}
// -------------------------------------
// hex dump of EEprom
void
_eeScan (void)
{
printf ("%s:", __func__);
for (unsigned n = 0; n < EEPROM_SIZE; n++) {
if (! (n % 16))
printf ("\n %03x: ", n);
else if (! (n % 4))
printf (" ");
byte val = EEPROM.read (n);
printf (" %02x", val);
}
printf ("\n");
}
// -------------------------------------
static void
_eeVars (void)
{
printf (" %s: %6s %d\n", __func__, "twr", twr);
printf (" %s: %6s %d\n", __func__, "board", board);
printf (" %s: %6s %s\n", __func__, "ap0", ap0);
printf (" %s: %6s %s\n", __func__, "ap1", ap1);
}
// -------------------------------------
static void
_eeWrite (
byte type,
const char *text )
{
printf ("%s: %d %s\n", __func__, type, text);
// find end of file
int addr = _eeAddr (ID_END);
if (EEPROM_SIZE <= addr) {
printf (" %s: not found - eeprom full\n", __func__);
return;
}
printf (" %s: addr 0x%02x\n", __func__, addr);
EEPROM.write (addr++, type);
byte len = strlen (text) + 1;
EEPROM.write (addr++, len);
for (unsigned n = 0; n < len; n++)
EEPROM.write (addr++, text [n]);
EEPROM.write (addr++, ID_END);
EEPROM.commit ();
}
// -------------------------------------
static void
_eeWrByte (
int buf [],
int nByte )
{
printf ("%s: id %d, nByte %d\n", __func__, buf [0], nByte);
// find end of file
int addr = _eeAddr (ID_END);
if (EEPROM_SIZE <= addr) {
printf (" %s: addr 0x%03x - eeprom full\n", __func__, addr);
return;
}
printf (" %s: addr 0x%02x\n", __func__, addr);
EEPROM.write (addr++, buf [0]);
EEPROM.write (addr++, --nByte);
for (int n = 1; n <= nByte; n++)
EEPROM.write (addr++, buf [n]);
EEPROM.write (addr++, ID_END);
EEPROM.commit ();
}
// -----------------------------------------------------------------------------
int
eeCmd (
char c,
int val [],
int nVal,
char *str )
{
if (dbgEE)
printf ("%s: %s, %d %d %d \n", __func__, str, nVal, val [0], val [1]);
switch (c) {
case 'C':
_eeClear ();
break;
case 'c':
_eeCodes ();
break;
case 'd':
if (nVal)
dbgEE = val [0];
else
dbgEE = ! dbgEE;
printf ("%s: dbgEE %d\n", __func__, dbgEE);
break;
case 'D':
_eeDelete (val [0]);
break;
case 'L':
_eeLoad ();
break;
case 'l':
_eeList ();
break;
#if 1
case 'r':
char buf [40];
_eeRead (val [0], buf, 40);
Serial.println (buf);
break;
#endif
case 's':
_eeScan ();
break;
case 'v':
_eeVars ();
break;
case 'W':
_eeWrite (val [0], str);
break;
case 'w':
_eeWrByte (val, nVal);
break;
case '.':
return CmdRestore;
break;
case '?':
printf (" C _eeClear\n");
printf (" c _eeCodes\n");
printf (" d dbgEE (#)\n");
printf (" #D _eeDelete\n");
printf (" L _eeLoad\n");
printf (" l _eeList\n");
printf (" #r _eeRead\n");
printf (" s _eeScan\n");
printf (" \"\"#W _eeWrite text\n");
printf (" #,#w eeWrite bytes\n");
printf (" ? help\n");
break;
}
return CmdNul;
}
the last function, eeCmd() is called from another that accepts commands thru the serial interface. there are several command modes that can be selected
@gcjr Yea, I know, EEPROM isn't touched when you upload code, I knew that. But I can't see what's in there without erasing the existing code, if I use the IDE, because I have to write a code to read and publish the EEPROM data. So, I lose the code that's loaded in order to get at the data that's stored. I was looking for a way past that, but @ruilviana pointed me at the answers.
Thanks
why not add Serial monitor commands that allow you to add, delete, display the existing values
here's code i'm using in my node to process serial monitor commands which as 1+ digits and an alpha (letter).
it can also capture multiple comma separated numeric values
and it can call different sub-functions (i.e. cmdFunc) to process them depending on a mode.
// pcRead, serial interface commands
#include "ee.h"
#include "pcRead.h"
#include "signals.h"
#include "wifi_.h"
unsigned int debug;
// -----------------------------------------------------------------------------
void
reportTwr (void)
{
Twr *t = & twrs [twr];
printf ("Tower %d, %s, %s, %s\n", twr, t->sym, t->name, t->ip);
}
// -----------------------------------------------------------------------------
int cmdMain (char c, int val[], int nVal, char *str);
int (*cmdFunc) (char c, int val[], int nVal, char *str) = cmdMain;
// -------------------------------------
int
cmdIo (
char c,
int val [],
int nVal,
char *str )
{
switch (c) {
case 'c':
printf (" pin %d set\n", val [0]);
digitalWrite (val [0], LOW);
break;
case 'I':
pinMode (val [0], INPUT_PULLUP);
break;
case 'O':
pinMode (val [0], OUTPUT);
break;
case 'r':
printf (" pin %d %d\n", val [0], digitalRead (val [0]));
break;
case 's':
printf (" pin %d set\n", val [0]);
digitalWrite (val [0], HIGH);
break;
case '.':
return CmdRestore;
case '?':
printf (" #c digitalWrite (#, LOW)\n");
printf (" #I pinMode (#, INPUT_PULLUP)\n");
printf (" #O pinMode (#, OUTPUT)\n");
printf (" #r digitalRead (#)\n");
printf (" #s digitalWrite (#, HIGH)\n");
printf (" ? help\n");
break;
}
return CmdNul;
}
// -------------------------------------
int
cmdMain (
char c,
int val [],
int nVal,
char *str )
{
switch (c) {
case 'D':
if (nVal)
debug = val [0];
debug = ! debug;
break;
case 'E':
cmdFunc = eeCmd;
break;
case 'I':
cmdFunc = cmdIo;
break;
case 'i':
sigInit ();
break;
case 's':
sigDisp ();
break;
case 't':
reportTwr ();
break;
case 'o':
nodeSend (LT, "ok");
break;
case 'Y':
wifiReset ();
break;
case '.':
return CmdRestore;
case '?':
printf (" a send \"antidisestablishmentarianism\"\n");
printf (" E EEPROM cmds\n");
printf (" I I/O pin cmds\n");
printf (" i sigInit\n");
printf (" n send name\n");
printf (" o send \"ok\"\n");
printf (" s sigDisp\n");
printf (" t report twr\n");
printf (" Y wifiReset\n");
printf (" ? help\n");
break;
}
return CmdNul;
}
// -------------------------------------
#define Narg 5
int arg [Narg];
int argIdx = 0;
int val = 0;
bool quote = false;
char str [40];
int idx = 0;
void pcRead ()
{
if (Serial.available ()) {
char c = Serial.read ();
if (quote) {
if ('"' == c) {
quote = false;
str [idx] = '\0';
}
else {
str [idx++] = c;
if (idx == sizeof(str)-1) {
str [idx] = '\0';
quote = false;
}
}
return;
}
switch (c) {
case '"':
quote = true;
idx = 0;
break;
case ',':
arg [argIdx++] = val;
break;
case '0' ... '9':
val = 10*val + c - '0';
return;
case 'T':
arg [argIdx++] = val;
printf (" test --");
for (int n = 0; n < argIdx; n++)
printf (" %d", arg [n]);
printf ("\n");
argIdx = 0;
break;
default:
arg [argIdx++] = val;
if (CmdRestore == cmdFunc (c, arg, argIdx, str))
cmdFunc = cmdMain;
argIdx = 0;
break;
}
val = 0;
}
}
can you post the code that deals with the eeprom?
Hi
from what I'm noticing the @camsysca doesn't have the original arduino code.
Right ?
@ruilviana
Yes and no. I have all the code versions, but failed to note ON THE DEVICE which version was loaded. Development continued for some time after those nodes were loaded, but exactly what was loaded I don't have a note for. Hence, I don't know what new features were installed - and tested. Config management egg-on-face, for sure. Given the circumstances that led to this, it's explainable, but still a lesson in poor practices.
I have 4 nodes running right now, and what they do, they do well. The EEPROM content would give me a hint as to which version is loaded, but not a clear indication (that will be fixed in the next version, for sure). So right now, I have a running system but no way to move forward without breaking at least one node to find out what might be in there - and the contents will only help nail a version series, not an exact version.
@gcjr You continue to suggest things that could be done moving forward, for sure, but don't help me figure out what's in those 4 nodes that are on the layout and working well. Moving forward, I will be adding a number of features to CMRI, including reporting of EEPROM and code versions, but none of that is germane to telling me what's in an existing node.
Thanks, everyone, I think this thread has run it's course, so I'll flag @ruilviana's original post as 'answer', since he pointed at one technique to address my question.
Any programmer has the ability to detect your code and the contents of the EEPROM if the firmware is not protected
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.