help me understand structures and data management

Hello everybody!

Ive gone against better judgement and figured i need to build a library in order to learn some finer things in programming, like structure, classes, constructors and instances. :slight_smile:

As a test-library im thinking a middle-man between sketch code and i2c pin extender libraries.
Im using Adafruits GREAT library for mcp23008 and a handfull of these ic´s for a bunch of projects, some devices are used both for inputs and outputs but its somewhat cumbersome to read or write pin-by-pin when youre using more than a couple of them..
Its a LOT more effective to read or write the GPIO byte x times/second than to read each button every x ms & write each output every time it changes.

The aim is to set up a structure of inputs and outputs, link them with their respective pins and devices and keep track of when they were last updated.

The end-user would then be able to check if i2c.button1.isPressed (true/false) or i2c.button3.clickType (int return type, 1= is_pressed, 2=shortclicked, 3=longclicked).
The outputs would be used with something like "i2c.lights2.on/off/blink(500)"

The middle-man library would configure the pin extenders and keep record of their last read/write time (counted with millis()) and store the last read value between update intervals.

So if the update-interval is set to 50ms, if i poll the button state every 10ms, i would get 5 identical replies, between each i2c extender update cycle.
I could then have different timers for inputs and outputs, checking inputs every 50ms and setting outputs every 120ms for example.

The middle-man library would then act the same way, but in the other direction to handle the outputs.
I would "simply" define each output channel with its chip "id" and "pin#", allowing multiple outputs for each channel, so "i2c.light1.on" effects all the output pins (on their respective i2c device) configured as "light1".

I would read and set inputs/outputs by reading/writing (to) the whole pin registry, instead of speaking to each pin separately.
This would save a LOT of i2c bus traffic...

Now, i have very limited knowledge of the needed ingredients here, but this is how my learningprocess works, i find something i need to do and learn how to do it along the way.
So could i please ask for a constructive tone on the replies? :slight_smile:

First big question;
How do i "link" a hypothetical "button1" with a specific i2c-pin-extender and pin#?

i´ll be using a byte to store each pin extenders buffer, so i can bitRead/bitSet/bitClear the pin state for each pin# in each pin-extender-buffer, but i´ll have to figure out how to setup a constructor to do this for me and then instance it for each button.

To express the question in another format; how do i make "i2c.button1.read" into "bitRead(buffer1, 3)" (if its the pin# = 3) with a constructor and instance?

Oh and i almost forgot to even ask;
How do i setup the config bit of the equation?
I figure that the end user (me) would want to modify each pin extender i2c address, as well as setting up inputs/outputs..

Im trying to read thru the source files of other libraries, but most do use so advanced languages and dont explain the "basics", but cover only the higher level logics in their occational code commets..

So any and all hints, advices and links are welcome, if they help me learn what im lacking here.. :slight_smile:

when using a number of MCP23018 devices on a PIC24F microcontroller to control a matrix of LEDs I used two arrays to hold the LATB and LATA value, e.g.

// setting for columns LATB and LATA
unsigned int col[]={0xfffe,0xfffd, 0xfffb, 0xfff7, 0xffef, 0xffdf, 0xffbf, 0xff7f,  // U1 port A
                    0xfeff,0xfdff, 0xfbff, 0xf7ff, 0xefff, 0xdfff, 0xbfff, 0x7fff,  // U1 port B
                    0xfffe,0xfffd, 0xfffb, 0xfff7, 0xffef, 0xffdf};                 // U2

// setting for rows LATB and LATA
unsigned int row[]={0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100,0x0200,0x0400,0x0800, 0x1000};

/*
 * Write Data MCP23018_I2C_ADDRESS, Register Address, Data
 * return 0 OK -1 fail
 */
static int MCP23018write(unsigned char MCP23018_Address, unsigned char regAddress, unsigned char data)
{
    //return;
    unsigned char tx[2]={regAddress, data};
    return writeI2C2(MCP23018_Address, tx, 2, 1);
}


// set up LED at column and row then delay mSec, if print=1 print debug info
int topPCB_writeLED(int column, int rowx, int delay, int print)
{
    char *U;
    if(column>21) printf("ERROR column>21 is %d\n", column);
    if(rowx>12) printf("ERROR row > 12 is %d\n", rowx);
    char u1[]="U1", u2[]="U2";
    // set up row
    if(MCP23018write(MCP23018_U3_ADDR,OLATA,row[rowx]) != 0) 
		{ printf("topPCB_writeLED() FAIL!\n"); return -1; };
    MCP23018write(MCP23018_U3_ADDR,OLATB,row[rowx]>>8);
    // set up column using U1 or U2
    if(column<16)           // U1
        {
        MCP23018write(MCP23018_U2_ADDR,OLATA,0xff); // Port A set to logic 0
        MCP23018write(MCP23018_U2_ADDR,OLATB,0xff); // Port B set to logic 0
        MCP23018write(MCP23018_U1_ADDR,OLATA,col[column]);
        MCP23018write(MCP23018_U1_ADDR,OLATB,col[column]>>8);
        U=u1;
        }
    else                    // U2
        {
        MCP23018write(MCP23018_U1_ADDR,OLATA,0xff); // Port A set to logic 0
        MCP23018write(MCP23018_U1_ADDR,OLATB,0xff); // Port B set to logic 0
        MCP23018write(MCP23018_U2_ADDR,OLATA,col[column]);
        MCP23018write(MCP23018_U2_ADDR,OLATB,col[column]>>8);
        U=u2;
        }
    if(print) printf("in writeLED column %2d row %2d  %s colB %02x colA %02X rowB %02x rowA %02x\n",
            column, rowx, U, (col[column]>>8) & 0xff,  col[column] & 0xFF, (row[rowx] >> 8) & 0xff, row[rowx] & 0xff);
   mSecDelay(delay);
	return 0;
}

xarvox:
Im trying to read thru the source files of other libraries, but most do use so advanced languages and dont explain the "basics", but cover only the higher level logics in their occational code commets..

So any and all hints, advices and links are welcome, if they help me learn what im lacking here.. :slight_smile:

you are trying to abstract something that is not trivial, but if you don't know how to make a C++ class, or understand what you are looking at when you study examples, then this will be even more difficult.

you have to start by understanding the basics... Pick a task that you would like to create a class and try that. Understand the meaning of public and private members, etc.

In the end, if you are already working with a working I2C extender library, you'd benefit from an understanding of inheritance, but that is more like level five work, and you are still at level one.

Well, i do thank you for your feedback, and your opinion regarding the difficulty.

However, it really doesn't bring me any closer to my goal here.. :slight_smile:

Im not like "everybody else", i learn in a different way than most are used to, so i rarely have much success with structured learning, like "learn to program in 30 days".
I need a "how do i solve this problem"-type of approach in order to learn pretty much anything..

On the other hand, i thrive with a (for others) serious challenge, quickly understanding complex but completely new models and so on.

Such pesky details like "difficulty" haven´t stopped me before, i rather thrive with the challenge.

So yet again, any help forward would be greatly appreciated!

difficult != impossible

it is just so much easier to relate if you've learned a practical example as a reference

You know what they say at times like these...

post some code! :smiley:

Ive been trying to write some sort of code to structuralize the project, but this is not going as i hoped..
Im lacking a couple of intermediate pieces to help me use the higher-level stuff, but please help me fill in the blanks.. :slight_smile:

ive been following some class examples and tried to merge their methods with my project plans but im kind of hitting a brick wall with my very limited knowledge of classes.. :confused:
(most search-results are ether folks asking for help or just cover the very basics)

buttons.h

class Buttons {
  public:
    bool pressed(void);    // is the button pressed?
    byte clickType(void);  // last click registered, 0=none, 1=is pressed, 2= shortclicked, 3=longclicked
  private:
    byte device;           // mcp23008 pin extender device
    byte pin;              // the pin# @ device
    byte function;         // main function to affect
    byte alt_function;     // alternative function (for modifier-button & input button combo´s)
    uint32_t lastClicked;  // timestamp for when this buttonpress was first detected
};

!buttons.h

Now, i figure i want a structure like the one in the "buttons.h" example above, so i can define A button, then use that definition in constructing ALL of the buttons.
Much of the data would be stored in arrays or structs but i have not written any code examples for that part just yet.

My problem revolves in "automating" the button construction and usage, im just not having a clue in how to actually use the class, or how to configure stuff like number of buttons, and how to populate the data fields for each button.

A simple example that explains the behaviors of the code would certainly help me exactly how stuff works.. :slight_smile:

The goal here is to setup the buttons and initialize them (setting up pin extender addresses and link the other data to their respective data containers.

Also, can i setup a class of classes?
to have a class named "inputs" for example, to include each instance of my "buttons" while also managing my other inputs (hall triggers).

I would rather have a single instance to define instead of a block of inputs in my .ino code..

And on a separate note, ive been having some real problems with "nested code".

In my "mainProject.h", (the only included header-file in my .ino, ) ive setup a #include "i2c.h" and that i2c.h holds the includes for the adafruit_mcp23008 library, including the setup and instancing normally found before the void setup() in a working sketch.
The void setup() part of a standard sketch (mcp.begin(addr)) will be added to my class i2c.begin and executed from my sketch in void setup().
The plan is then to use i2c.command to interface with the mcp23008, so i wouldnt have to include the adafruit_mcp23008 library in my main sketch, as long as i #include mainProject.h".

Is this supposed to work?

The idea here is to write building-"blocks" of code functions, that i can "lego" together, depending on the situation, something like a sub-library if you will.
So the i2c.h would take care of any and all hardware interfacing and library managing for my i2c devices for example, providing me with a neat end-user-interface without having to re-write the same functions over and over.

However, im failing to realize how to actually use a library from inside a separate .h/.cpp file combo, i ether get "was defined twice"-compile error, or it claims that [x] "was not definied inside this scope".

xarvox:
My problem revolves in "automating" the button construction and usage, im just not having a clue in how to actually use the class, or how to configure stuff like number of buttons, and how to populate the data fields for each button.

A simple example that explains the behaviors of the code would certainly help me exactly how stuff works.. :slight_smile:

Take a look at this example of a simple library that detects multiple button presses on multiple buttons. the example uses an array of button objects, but they need not be in an array for it to work. It is a little different from what you may have seen as it uses a callback function which you identify in the object's constructor.

See if that helps you, it sort of looks like your outline above.