Notifications
Clear all

When 30 is an odd number???  

Page 2 / 2
  RSS

Pugwash
(@pugwash)
Prominent Member
Joined: 1 year ago
Posts: 970
2020-02-09 10:26 am  

@dale

Well, I feel like I have been holding a carrot to the wrong end of a horse and wondering why it wasn't eating!! All the data was in the 32 to 63 registers where it should be, it was just not being read into the mByte[] array.

After dispensing with all your ornamental code in the sketch above (woods and trees), I figured that you had already implemented what I had been mulling over in my scatterbrain. I had been thinking also about reducing the data chunks to 16 bytes to eliminate the 32(30) problem. This may be required if using the block write function(2) from the website,

Anyway here is how I have implemented your idea in my code:

// Include the I2C Wire Library
#include "Wire.h"

// EEPROM I2C Address
#define i2c_eeprom 0x50
#define READ_DATA 32
#define START_ADDRESS 0

byte mByte[READ_DATA];
unsigned char dataArray[64];
unsigned char tempArray[16];

void setup() {

Serial.begin(9600);
Wire.begin();
randomSeed(65);

}

void loop() {

  //create an array of random numbers length 64
  for(int i = 0; i < 64; i++){
    dataArray[i] = random(255);    
  }

  // write the data in 16 byte chunks into the EEPROM registers
  for(int j = 0; j < 64; j += 16){
    for(int i = 0; i < 16; i++){
      tempArray[i] = dataArray[j + i];
      }
    writeEEPROM(i2c_eeprom, j, tempArray);
    }
  
  // show data stored in the dataArray[]
  Serial.println("\n\ndata array");
  for(int i = 0; i < 64; i++){
    Serial.println(byte(dataArray[i]));
    }
  Serial.println("array print complete");
  
  delay(5);

// get the first 32 bytes of the mByte array*********
  
  Wire.beginTransmission (i2c_eeprom);
  // Set device to start read reg 0
  Wire.write((int)(START_ADDRESS >> 8));   // MSB
  Wire.write((int)(START_ADDRESS & 0xFF)); // LSB
  Wire.endTransmission ();  

  Wire.requestFrom(i2c_eeprom, READ_DATA);

  int idx = 0;
  
  // read the bytes into an array
  while(Wire.available()) {
    
    mByte[idx] = Wire.read(); // read each byte from the register and store in the array
    idx++;
  }

  // get the second 32 bytes of the mByte array*********
  
  Wire.beginTransmission (i2c_eeprom);
  // Set device to start read reg 0
  Wire.write((int)((START_ADDRESS+32) >> 8));   // MSB
  Wire.write((int)((START_ADDRESS+32) & 0xFF)); // LSB
  Wire.endTransmission ();  

  Wire.requestFrom(i2c_eeprom, READ_DATA);

  idx = 32;
  
  // read the bytes into an array
  while(Wire.available()) {
    
    mByte[idx] = Wire.read(); // read each byte from the register and store in the array
    idx++;
  }

  // print to monitor the contents of mByte[] i.e. first 64 registers
  
  Serial.println("\nValues returned from EEPROM");
  Serial.println("Register , Value\n");
  
  for (int i = 0; i < 64; i++){
    Serial.print(i);
    Serial.print(" , ");
    Serial.println(mByte[i]);
    
    }
  
  Serial.println();
  Serial.println("Done");
  while(1);
}

//write up to 64 bytes of data

void writeEEPROM(int deviceaddress, unsigned int eeaddress, char* data) 
{
  // Write a string of chars to eeprom
  unsigned char i = 0;
  
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)((eeaddress) >> 8));   // MSB
  Wire.write((int)((eeaddress) & 0xFF)); // LSB

  do{ 
     Wire.write((byte) data[i]);
     i++;
  } while(data[i]);  
  Wire.endTransmission();
     
  delay(6);  // needs 5ms for page write
}

It only remains to put the "read and fill array" code into a separate function outside the main loop(), to handle huge blocks of data.


ReplyQuote
Pugwash
(@pugwash)
Prominent Member
Joined: 1 year ago
Posts: 970
2020-02-09 1:59 pm  

@dale

This looks like the final version for my boilerplate library!


ReplyQuote
NewburyPi
(@dale)
Estimable Member
Joined: 1 year ago
Posts: 102
2020-02-14 10:10 pm  

Hey @pugwash. You've inspired me to give the problem a try.  Also, I wanted to try to implement instructions for building an Arduino library, that Ralph Bacon published three years ago. I'm still working on setting up the github repository according to github standards, but at least I've started. I've put my first cut up on github, if you wish to give it a try. I took a slightly different approach, as I wanted to minimize the library users efforts. I provide two methods. One to write and one to read. I have hidden both the 30 byte buffer problem and the use of 64 byte pages within the methods. The user just needs to pass the start address, the data array, and the size of the array. The start address can be any address, the size of the array is limited only by the amount of Arduino's memory.  

USE:

The class constructor takes the I2C address of the EEPROM chip.

SerialROM <object>(<I2C address>);

The .begin method, is not absolutely required. It is used to allow the passing of a baud rate to the object, to support debug messages.

<object>.begin(baud rate);

The Serial EEPROM Controller library provides two methods .write and .read. For each the user supplies the starting memory address within the chip, a pointer to the data byte array, and the size of the array to be writen or read.

<object>.write( <start address>, <data array>, sizeof(<data array> ); 
<object>.read(<start address>, <data array>, sizeof(<data array> );

I've got lots to do yet. There is no fault handling, plenty of inefficiencies to wring out of the code, and I want to make the library easily loaded into the Arduino IDE. 

--
Dale


ReplyQuote
frogandtoad
(@frogandtoad)
Reputable Member
Joined: 1 year ago
Posts: 455
2020-02-15 10:54 am  

@dale

Hey Dale, had a quick look at your code... not a bad first up attempt, but there are some points that I think you might want to consider as you develop it further:

1) Your constructor - I would consider restructuring it as follows:

I don't think that int_8 is sufficient enough, even for the standard Arduino EEPROM size, did you actually mean at least uint8_t instead of int8_t?

SerialROM::SerialROM(uint8_t I2C_Address, bool displayMsg) 
  : _msg(displayMsg), _I2C_addr(I2C_Address) {/*constructor body now empty*/}

I also think you should consider using an constructor initialisation list (as shown above), instead of direct assignment in the constructor body.  The advantage of using an initialisation list, is that the data members are initialised before the body of the constructor is executed, and with greater efficiency.

2) If your class needs to work with or set the serial baud rate, then rather than re-creating the Serial object, you would be much better off passing just one Serial object around by reference, for example:

// Serial is-a type of HardwareSerial
void SerialROM::begin(HardwareSerial& serial) {
   serial.begin(9600); // Use "serial" as normal within function
   if (_msg) {
    serial.println("By the way... this is not the CONSTRUCTOR ;-).");
   }
 } 

void setup() {
  SerialROM MySR(0, true);
  MySR.begin(Serial);
 }

3) I also see that you're using too many print statements to incorporate new lines, for example:

  Serial.println("");
  Serial.println("Data as read.");
  Serial.println("");

Rather than just one line to do the same thing:

  Serial.print("\nData as read.\n");

One last thing about print statements... when crating a library, you probably want to avoid having them in there, and only include them where absolutely necessary.  Normally, it's better to allow the user the control of where they would like to place any monitoring messages, so returning a Boolean value is a good idea to indicate the status of the action of the function, then the user can determine what to do and print.

Please take this as constructive criticism 🙂

Cheers!


ReplyQuote
Pugwash
(@pugwash)
Prominent Member
Joined: 1 year ago
Posts: 970
2020-02-15 11:13 am  

@dale

Hey @pugwash. You've inspired me to give the problem a try. 

Glad to be your muse! ? 

Perhaps you should correct the typo in the SerialROM.h file "Twowire_h" not "Towwire_h" in the second line.

I will try out your library out soon and get back to you with my findings!


ReplyQuote
NewburyPi
(@dale)
Estimable Member
Joined: 1 year ago
Posts: 102
2020-02-15 1:56 pm  

Many thanks @frogandtoad

All feed back is welcome. Just one question... Regarding passing the object by reference, does this require that there is a Serial object setup in the calling sketch? I don't imagine there is a way to check for a valid object in the constructor. My preference is to not do any printing in SerialROM.cpp. I'd just strip everything out after completion of testing. Then again, in the broader sense, there is likely to be a situation when you need to pass on an object. I'll have to mull that one over. 

--
Dale


ReplyQuote
NewburyPi
(@dale)
Estimable Member
Joined: 1 year ago
Posts: 102
2020-02-15 2:07 pm  

@pugwash

That is an odd one. I originally put those three lines in to resolve a problem. While it (at the time) seemed to resolve the problem, when I now look at it. It does nothing at all. So, out it goes 🙂

 

By the by, I've updated github based on the feed back so far.

--
Dale


ReplyQuote
frogandtoad
(@frogandtoad)
Reputable Member
Joined: 1 year ago
Posts: 455
2020-02-15 3:11 pm  

@dale

Posted by: @dale

Many thanks @frogandtoad

No problem at all, glad to help!

Posted by: @dale

Regarding passing the object by reference, does this require that there is a Serial object setup in the calling sketch?

It all depends on how you want to use your program, as far as I can see there is no real requirement, because the "Serial" object has already been instantiated for immediate use... we never actually declare it, as it has already been done for us without our knowledge behind the scenes:

HardwareSerial Serial;

... we're just passing it around, and once you've called begin(n), your main loop can access it's member functions like println() just like before, because it's a global object... it's just a good habit to pass objects around by reference, especially when it might not be in visible scope.

Cheers!


ReplyQuote
Pugwash
(@pugwash)
Prominent Member
Joined: 1 year ago
Posts: 970
2020-02-15 4:23 pm  

@dale

If the wire.h library is invoked in the sketch, does it need to be invoked again in your SerialROM.h library?

Or should the code look like this:

#ifndef wire_h
#include wire.h
#endif

Just from someone who doesn't really know what they are talking about ? ? 

And I was wondering why you have included a read() function in your library as reading the EEPROM is not really an issue, whereas writing to the I2C EEPROM is a big complex issue??


ReplyQuote
NewburyPi
(@dale)
Estimable Member
Joined: 1 year ago
Posts: 102
2020-02-15 5:11 pm  

OK. So, as blind leading the blind we have...

#ifndef Wire_h
#include <Wire.h>
#endif

Does not work. It throws...

In file included from U:\New Sketches\Serial_EEPROM\Serial_EEPROM.ino:4:0:
SerialROM.h:7:10: error: #include expects "FILENAME" or <FILENAME>
#include wire.h
^~~~
exit status 1
#include expects "FILENAME" or <FILENAME>

In my original code I had the conditional, in order to avoid multiple references to Wire. This does not seem necessary however, as simply typing

#include <Wire.h>

into my .h file works just fine. 

 

As to read(); I believe we had issues with reading from the chip as well, and that this was again due to the 32 byte buffer in Wire. 

From 2020-02-08 8:44 pm, I identified that in order to read 64 bytes, you had to perform two 32 byte reads. 

Yep! looks like the 32 byte buffer strikes again. I changed the wire.right part to output incremental data. Then split the wire.read part to first read just the first 32 bytes, then repeat the read on the second 32 bytes. (code below)

Values returned from EEPROM
Register , Value

1 *** , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 *** 8
9 *** , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 *** 16
17 *** , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 *** 24
25 *** , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 *** 32

Values returned from EEPROM
Register , Value

33 *** , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 *** 40
41 *** , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 *** 48
49 *** , 48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 *** 56
57 *** , 56 , 57 , 58 , 59 , 60 , 61 , 62 , 63 *** 64

Done

Hope this helps. 

--
Dale


ReplyQuote
frogandtoad
(@frogandtoad)
Reputable Member
Joined: 1 year ago
Posts: 455
2020-02-18 2:18 pm  

@dale

All this recent talk about scope jolted my memory about another scope I totally forgot about, and it might be helpful to solve this chicken and egg situation with initialising the serial object in the constructor... not sure how well it will work yet, but I'll give it a try and see what I can come up with.

Cheers!


ReplyQuote
NewburyPi
(@dale)
Estimable Member
Joined: 1 year ago
Posts: 102
2020-02-18 3:27 pm  

@frogandtoad

Great.  While I've got my code running, I would like to know why, and/or if there are better ways to achieve it.

--
Dale


ReplyQuote
frogandtoad
(@frogandtoad)
Reputable Member
Joined: 1 year ago
Posts: 455
2020-02-19 2:01 am  

@dale

Posted by: @frogandtoad

All this recent talk about scope jolted my memory about another scope I totally forgot about, and it might be helpful to solve this chicken and egg situation with initialising the serial object in the constructor... not sure how well it will work yet, but I'll give it a try and see what I can come up with.

Posted by: @dale

Great.  While I've got my code running, I would like to know why, and/or if there are better ways to achieve it.

OK, so when I mentioned another scope I totally forgot about, it is what is known as an anonymous scope, that’s right… ANONYMOUS!

I was trying to figure out a good way to demonstrate how to use this anonymous scope, and the effect that it can impose on a piece of code.  If you’re familiar with and or comfortable with C++ objects, then you’ll know that when we create a class, there are two default (hidden) constructors, and one default (hidden) destructor provided for us, and they are:

Default Constructor (invoked for default instantiation, if no other constructor is provided)
Copy Constructor (implicitly copies one object to another)
Destructor (responsible for clean up, destroying the object at the end of its SCOPE!)

Given the role of the destructor, I have come up with an example to demonstrate how to use the ANONYMOUS scope as follows:

struct Object {
  String Name;
  Object(String name) : Name(name) {}
  ~Object(){
    Serial.println("Destructor called for object: " + Name); 
   }
 };

void setup() {
  Serial.begin(9600);

  Serial.println("Top of scope");
  
  //{ // Braces introduce a new scope and force early object destruction
   Object A("A");
  //}

  Serial.println("Bottom of scope\n");
 }

As we can see here, when we remove the comments for the curly braces, they introduce a new scope for where the object was instantiated in, therefore when execution reaches the last brace, the object goes out of scope and in turn the destructor is invoked and forced to initiate clean up, thus object destruction, and the message printed from the destructor demonstrates it’s early destruction, right in the middle of Top and Bottom of the loop scope – Check your serial monitor with and without the curly braces in play.

Interesting you say?  Any how does any of this help us to get the Serial object to work in the constructor of our class as I mentioned yesterday?  Well, let’s see, using the following example:

class Base {
  private:
    String message = "34#-'jf/2f1)4-5gf|ko";
  public:
    // Anonymous scope introduced to force the baud rate to be set
    Base(unsigned int baudrate) { {
       Serial.begin(baudrate);
      }
     Serial.println(decode());
    }

    String decode() {
      for(unsigned int idx(0); idx < message.length(); idx++) {
        message[idx] ^= 'F';
      }

     return message;
    }
 };

void setup() {
  Base B(9600);
  
 }

By introducing an anonymous scope within the context of our constructor, we are now forcing the global Serial.begin() function to complete setting the baud before exiting its current scope, and at that point when it exists, the serial object is ready for printing to the monitor – This works because the serial object has already been globally instantiated - Pretty cool eh?

Please test it out and let me know your thoughts.

Enjoy!


ZeFerby liked
ReplyQuote
NewburyPi
(@dale)
Estimable Member
Joined: 1 year ago
Posts: 102
2020-02-19 7:48 pm  

Thanks @frogandtoad ,

I'm not certain that I, entirely, grasp what you've posted. However, I'll try a few things out and see what there is to see. 

As far as

Posted by: @frogandtoad

If you’re familiar with and or comfortable with C++ objects

goes... My previous example is the first C++ class that I've ever built. It's the first time I've ever run code with a class I've written. The fact that it does preform the task I assigned it, has struck me with a level of awe. Made me a bit cocky too, I suppose. I've spent the past three days constructing (pun intended) a Python class. Testing on it has gone satisfactorily, but has certainly highlighted the deltas between C and Python. 

In any case, I'll do some testing over the weekend and post my results (and likely a few questions :).

Thanks again. 

--
Dale


ReplyQuote
Page 2 / 2