Notifications
Clear all

When 30 is an odd number???

29 Posts
3 Users
2 Reactions
6,745 Views
(@pugwash)
Sorcerers' Apprentice
Joined: 5 years ago
Posts: 923
Topic starter  

   
ReplyQuote
(@pugwash)
Sorcerers' Apprentice
Joined: 5 years ago
Posts: 923
Topic starter  

@dale

This looks like the final version for my boilerplate library!


   
ReplyQuote
NewburyPi
(@dale)
Member
Joined: 5 years ago
Posts: 97
 

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)
Member
Joined: 5 years ago
Posts: 1458
 

@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)
Sorcerers' Apprentice
Joined: 5 years ago
Posts: 923
Topic starter  

@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)
Member
Joined: 5 years ago
Posts: 97
 

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)
Member
Joined: 5 years ago
Posts: 97
 

@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)
Member
Joined: 5 years ago
Posts: 1458
 

@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)
Sorcerers' Apprentice
Joined: 5 years ago
Posts: 923
Topic starter  

@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)
Member
Joined: 5 years ago
Posts: 97
 

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)
Member
Joined: 5 years ago
Posts: 1458
 

@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)
Member
Joined: 5 years ago
Posts: 97
 

@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)
Member
Joined: 5 years ago
Posts: 1458
 

@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 reacted
ReplyQuote
NewburyPi
(@dale)
Member
Joined: 5 years ago
Posts: 97
 

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