Teensy 3.x and Array of Objects

Status
Not open for further replies.

jakorten

Well-known member
You can find the final version here: WireWrapper

I am trying to refactor my code. I'm currently working on the part that uses i2c modules that should be as dynamic as possible so that I do the loose coupling principle of good software design at least a bit justice.

I'm obiviously overlooking something. I don't want to use vectors at this point (no need for a dynamic structure), seems my memory is being overwritten or something.

Code:
#pragma once

#include "Arduino.h"

#ifndef MyModule_h
#define MyModule_h

class MyModule {
	
	private:
		
		
  public:
		void print();
		void printName();
		MyModule();
    MyModule(byte _id, char* _name);
    
    /* 
       Public:
       niet heel erg mooi, maar in dit geval 
       zijn getters / setters wat overdreven
    */
		byte id;
	  char *name;
	  boolean available;   
 
};

#endif

[B]CPP of the Object:[/B]

#pragma once
#ifndef MyModule_cpp
#define MyModule_cpp

#include "MyModule.h"
#include "Arduino.h"


MyModule::MyModule() {
	// default constructor
	id = 0;
	name = "";
	available = false;
}

MyModule::MyModule(byte _id, char *_name) {
	id = _id;
	name = _name;
	available = false;
}
 
void MyModule::print() {
	Serial.print("ID: ");
	Serial.print(id, HEX);
	Serial.print(" name: ");
	Serial.print(name);
	Serial.print(" available: ");
	Serial.print(available);
}

void MyModule::printName() {
	Serial.println(name);
}


#endif

An other classes uses it:

Header file:
Code:
const numModules = 10;
class myWire
{
    private:
       myWire(); // constructor
       MyModule myArrayOfModules[numModules];
    public:
       void doSomething();
}


CPP file:
Code:
myWire::myWire()
{
    MyModule mainModule1  = MyModule(0x40, "Module 1"); 
    myArrayOfModules[0] = mainModule1;
    MyModule mainModule2  = MyModule(0x41, "Module 2"); 
    myArrayOfModules[1] = mainModule2;
    MyModule mainModule3  = MyModule(0x44, "Module 3"); 
    myArrayOfModules[2] = mainModule3;
}

void myWire::doSomething {
  Serial.println(myArrayOfModules[2].name); // oops, looks corrupt now...
}

Unfortunately I seem to be overwriting my memory since the array becomes corrupt after that. What do I overlook? I tried using new etc. to no avail. At this point the array doesn't need to be dynamic.
 
Last edited:
you create items 1, 2, 3 - then assign those to an array of those things?

Do the items 1,2,3 work individually?

Not sure if I'm missing something - but the array should be where you create them - assuming that is a valid way to create them - or the array should be pointers to those things?
 
Since you are using a char pointer in the constructor, the compiler probably copies the strings temporarily to memory. If the strings are always fixed, make it a const char pointer, so that it can point to the flash which does not dissappear.
Also I am missing a default constructor. If this is the actual header file, I would be amazed it compiles at all.
To be save, I would also overload the assignment and/or the move operator.
 
Last edited:
I just included the entire code of the object in my first post, including the default constructor and the debug functions I use. I made the variables public, getters/setters would be better, but in this case I thought it was a bit overdone.

@defragster: indeed everything works initially. Although I do get strange characters on some objects for the name. But after making the objects from the myWire I call this:

Code:
void myWire::initModules() {
	
	for(int i = 0; i <= 2; ++i) {
		
		if ((myModules[i].name != "Module 1")) { // skip myself
			byte i2c_result = initTWIModule(myModules[i].id);
			boolean module_found = false;
			MyModule module = myModules[i];
			Serial.print("Address: ");
			Serial.println(myModules[2].id);
			if (i2c_result == 0) {
		    module_found = true;
		    if (debug_serial) {
		      //Serial.print("Module: ");
		      //module.print();
		      //Serial.println(" detected...");
		      Serial.println(module.id); 
		      if (module.id == 0x44) {
		        Serial.println("Certain Module identified..."); // this does get called indeed...
		      }
		    }
		  }
		  simModules[i].available = module_found;
		}
	}	
	
}

I use the i2c_t3 library on the modules:

Code:
int myWire::initTWIModule(uint8_t module_address) {
  Wire.beginTransmission(module_address);
  Wire.endTransmission(I2C_NOSTOP, 500); // ok?
  return print_i2c_status();
}

That works very well.

But when I call

Code:
void mySim::startSomeAction() {
	if ((myModules[2].id == 0x44) || (! myModules[2].available)) {
		Serial.println("Whoops! Can't find My module!!!");
		return;
	}
	[B]byte CPR_Controller_Address = myModules[2].id;[/B] // now this is corrupted, when I put = 0x44 instead it works fine

	Serial.print("Name: ");
	Serial.println(myModules[2].name);
	Serial.print("ID: ");
	Serial.println(myModules[2].id, HEX); // outputting the id gives 2c or something...
	
// do some wire operation 

}

I get in trouble here...
 
Last edited:
That edit looks more complete - any answer to my second question in m #2 - "Do the items MyModule mainModule1,2,3 work individually?"

They maybe should . . . but my following query still seems valid as this "MyModule myArrayOfModules[numModules];" is looking like it should be an array '*' pointer to '&' of them based on the assignment "myArrayOfModules[0] = mainModule1;" in myWire. I don't think your array is holding what you want it to after compiling.
 
I did answer your question, but should have be been more explicit indeed, so it does work in first, that is... in initModules. That is after initialisation... but... at startSomeAction() it goes wrong...

I just set the declaration in the main class to:
MyModule* myModules = new MyModule[numModules];

That doesn't seem to work, it initialises everything but then my heartbeat LED stops.

After some testing this seem to be caused by calling the array in startSomeAction:

byte CPR_Controller_Address = myModules[2].id; causes the problem if I replace it by byte CPR_Controller_Address = 0x44; it works fine.
 
Last edited:
Somehow I missed some of that? Cross posted edit?
But I was wondering about using the initial non array bound instance directly.

On my phone now, but I'm still wondering if you're getting 10 modules created from your array, using the default constructor? And then assigning to those array elements is not what you want? Create a count of them in the class and increment it on each construct and print that as you create any others. That array should be pointers to the class, not instances?
 
Yes that might have be case (cross editing). I think I would prefer the array to indeed hold just the reference to the objects. That is the problem with the new approach indeed. C is a nice language but sometimes a little confusing.
 
That count was just for debugging. If one when you add the first then different problem, if eleven then your array is instantiated objects to start with.

<edit> On big screen now - I misinterpreted your m#9 array note - if you make the array : MyModule* myArrayOfModules[numModules]; then that would take a "myArrayOfModules[2] = new MyModule(0x44, "Module 3");"
as in >> char* ptr = new char[sizeof(T)];

If I'd had the true files in one place I have tried compiling it myself.
 
Last edited:
You started in post 1 that the string was corrupted. What I understand from post #5 is that also the id gets corrupted.
Can you strip down your example to a bare minimum that still compiles and still has the error?
I get in trouble here...
Without actually running and understanding your code, a message like this is not very helpful. Please state the error and what you would have expected.

Furthermore, to rule out some sloppiness with the types, also add the following compiler flags. (No access to the teensy code, so maybe one of these is already present).
Code:
 -Wall -Wextra -pedantic
 
Last edited:
Yups, it is the same code, but I made a test program around it... Also includes a heart beat and simple serial commands to help testing.
 
Github and I don't work together - did a zip copy and some renaming - errors on compile:

C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\MyModule.cpp:20:9: warning: #pragma once in main file [enabled by default]
#pragma once
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\MyModule.cpp: In constructor 'MyModule::MyModule()':
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\MyModule.cpp:30:7: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
name = "";
^
In file included from C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:15:0:
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.h:250:1: warning: "/*" within comment [-Wcomment]
/*
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.h:272:1: warning: "/*" within comment [-Wcomment]
/*
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp: In constructor 'WireClass::WireClass(i2c_rate)':
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:52:51: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
MyModule mainModule = MyModule(0x40, "Module 1");
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:55:49: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
MyModule module2 = MyModule(0x42, "Module 2");
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:58:49: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
MyModule module3 = MyModule(0x44, "Module 3");
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:61:47: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
MyModule module4 = MyModule(0x46, "Module 4");
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:64:48: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
MyModule module5 = MyModule(0x48, "Module 5");
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:67:48: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
MyModule module6 = MyModule(0x50, "Module 6");
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:70:47: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
MyModule module7 = MyModule(0x52, "Module 7");
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp: In member function 'void WireClass::initModules()':
C:\Users\MOBIL_~1\AppData\Local\Temp\build4307643690430233507.tmp\WireClass.cpp:83:31: warning: comparison with string literal results in unspecified behaviour [-Waddress]
if ((myModules.name != "SimMainboard")) { // skip myself ;)
^
WireWrapper.ino:10:19: fatal error: SdFat.h: No such file or directory
compilation terminated.
Error compiling.


These includes are NOT on my system - it compiles with these commented:
//#include <SdFat.h>
#include <Time.h>
//#include <TeensyRTC.h>
//#include <ArduinoJson.h>
//#include <Vector.h>

and generates this output:
Address: 68
Address: 68
Address: 68
Address: 68
Address: 68
Address: 68
Address: 68
0
0
0
0
0
0

What I saw as the problem is there - I'll look to try my expectations and see what changes as long as I'm close - given the lack of includes and whatever other system hardware I can hopefully do with only a BARE T_3.1?
 
... and simple serial commands to help testing
It is not the commands that will help testing, it is you stating what commands to send and what you expect (without the modules attached).

When I first ran it I got
Code:
Whoops! Can't find my module!!!
On a PC you might be sloppy and ignore the const char warning. On a microcontroller this can lead to very strange effects.
So I added the const char in the appropriate places and replaced the test for the string in initmodules with
Code:
if (strcmp(myModules[i].name, "SimMainboard"))
Then I got the same answer as defragster.
 
Hmm that is a good idea indeed to fix the string compare issue.

"Whoops! Can't find my module!!!" is caused by the fact that the main Teensy can't find the module since there is no second teensy connected that has address 0x44 of course!
 
So can you strip it down further, by disabling the i2c communication and still show the error?
Also I was curious and tried looking up robotpatient.com and all I got was thai(?) characters on the screen.
 
Those warnings point to your problem string areas - don't ignore warnings. { I see you moved/cleaned Github and I have missed posts } just leaving this here.

I made my changes I was suggesting since msg #2 and my decently fast machine is hung on compiling . . . no errors - just off in a busy loop - not sure if it is me - or just exposed another issue . . . here is what I did - I'm not sure if I'm wholly wrong here or if this just exposes some other issue. It should compile to an error - now my code is out of date.

When I make these edits as noted the compile freezes - having to do with creating a MyModule ptr Array -> MyModule* myModules[numModules];

It may be because the definition won't fly, or maybe because the code will need '.' moved to '->' access before it can compile.

Note sure why this is coming up : there is a whole commented sketch at the end of WireClass.h
In file included from C:\Users\MOBIL_~1\AppData\Local\Temp\build832260417101092632.tmp\WireClass.cpp:18:0:
C:\Users\MOBIL_~1\AppData\Local\Temp\build832260417101092632.tmp\WireClass.h:250:1: warning: "/*" within comment [-Wcomment]
/*
^
C:\Users\MOBIL_~1\AppData\Local\Temp\build832260417101092632.tmp\WireClass.h:272:1: warning: "/*" within comment [-Wcomment]

Code:
MyModule::MyModule() {
	// default constructor
	id = 0;
	name = NULL;

Code:
class WireClass
{
	public: 
	  WireClass(i2c_rate speed);
	  //Vector<SimModule *> simModules; // zie de constructor voor modules zelf!
	   MyModule* MyModules[numModules];

Code:
WireClass::WireClass(i2c_rate speed)
{
  i2c_master_speed = speed;

  MyModules[0] = new MyModule(0x40, "Module 1");
  MyModules[1] = new MyModule(0x40, "Module 2");
  MyModules[2] = new MyModule(0x40, "Module 3");
  MyModules[3] = new MyModule(0x40, "Module 4");
  MyModules[4] = new MyModule(0x40, "Module 5");
  MyModules[5] = new MyModule(0x40, "Module 6");
  MyModules[6] = new MyModule(0x40, "Module 7");
 
@defragster, this error is because nested comments are not allowed. The usual trick to comment big sections is to use the preprocessor
Code:
#if 0
   lots of stuff here, including comments.
#endif
 
@kpc - wasn't a mystery to me - once I saw the pile of commented code drug in with the HEADER file - shouldn't generally be there - or at least made harmless.
>> Indeed that was more a rhetorical WTF than a question . . .

I saw the warnings go by - and it wasn't until I got to the end of the .h that I saw it for what it was.

Problem is that doesn't come up all the time and only after other things are changed or resolved - I was just saying . . . the compile seems confused and that isn't helping.
 
Well, now it seems to work. I added also a timeout to Wire.endTransmission(I2C_NOSTOP, 500);

This is my output now when a module is connected:

TestApp version 1.0
System starting...
0

// Pressing "i" gives:
Scanning list of modules...
Checking Module Module 1
Checking Module Module 2
Checking Module Module 3
Found Module 3 on Address: 0x44
Checking Module Module 4
Checking Module Module 5
Checking Module Module 6
Checking Module Module 7

// Pressing "p" gives:
Print all modules...
ID: 40 name: Module 1 available: 0
ID: 42 name: Module 2 available: 0
ID: 44 name: Module 3 available: 1
ID: 46 name: Module 4 available: 0
ID: 48 name: Module 5 available: 0
ID: 50 name: Module 6 available: 0
ID: 52 name: Module 7 available: 0

Now works very well. But there also was a stupid mistake in " if (!(myModules[2].id == 0x44) && (myModules[2].isAvailable())) {"

Thanks for helping me out kpc and defragster!!!
 
Status
Not open for further replies.
Back
Top