Question about progmem

Hello,

I am succesfully (as in works so far) coding an open source model railway dcc decoder. The decoder can control solenoids and LEDs.

With LEDs the decoders should allow you to create many nations' signaling system.

In short a signal can have 2-5 leds and atm it may show up to 10 different aspects (= certain combination of lights).

I coded a structure

typedef struct Aspects 
{
    const uint8_t nAspect ;
    const uint8_t nLeds ;
    uint8_t aspects[maxAspect][maxLeds] ;
} Aspect ;

And I made an array which currently contains 3 different types of signals (X means flashing)

const Aspect aspects[15] =
{
    {   3,                                  // nAspect
        1,                                  // nLeds   <-- for single output devices like decouplers
        {   { OFF },                        // OFF
            {  ON },                        // ON
            {   X }, }, },                  // toggle

    {   2,                                  // nAspect
        2,                                  // nLeds
        {   {  ON, OFF },                   // Green
            { OFF,  ON }, }, },             // Red

    {   5,                                  // nAspect 
        3,                                  // nLeds
        {   {  ON, OFF, OFF },              // Green
            {   X, OFF, OFF },              // Green flashing
            { OFF,  ON, OFF },              // Yellow
            { OFF,   X, OFF },              // Yellow flashing
            { OFF, OFF,  ON }, } } ,        // red

    {   7,                                  // nAspect 
        4,                                  // nLeds
        {   {  ON, OFF, OFF, OFF },         // red
            { OFF,  ON, OFF,  ON },         // double yellow
            { OFF,   X, OFF,   X },         // double yellow flashing
            { OFF,  ON, OFF, OFF },         // single yellow
            { OFF,   X, OFF, OFF },         // single yellow flashing
            { OFF, OFF,   X, OFF },         // red flashing
            { OFF, OFF,  ON, OFF }, },},    // RED
} ;

Than I have a signal class and 16 signal objects. Each object has a private variable named 'type' and this one is used to get the information out of the above structure array.

One such example

void Signal::setType( uint8_t _type )
{
    type = _type ;
    ledCount = aspects[type].nLeds ; // 'aspects' is the array of this topic.
    nAspects = aspects[type].nAspect ;
    nAddresses = (nAspects-1) / 2 + 1  ;
}

TL:DR.
My stucture array consumes 52 bytes of RAM per signal type. With the rest of the code I can propably have 30 different types. That sounds enough and it could be. But I am restrained to 5 leds and 10 aspects. I am likeley going to meet a signal which needs more.

In order to 'get it to fit' I can move the structure array to PROGMEM. But how would I get the data out of it again? I had a similar situation before: Pointer question about PROGMEM - #8 by bask185 only it is slightly different.

My project is not yet finished for a week or two and I can partially debug one thing at time. But I'd like to inquire in advance how I can get the array data out of progmem.

What I think I should do, is something like this:

void Signal::setType( uint8_t _type )
{
    Aspect aspect ; // create local object

    memcpy_P(aspect , &aspects[type],  52 /* or sizeof( aspect )*/ ); // copy one of the array ellements to local object

    type = _type ;
    ledCount = aspect [type].nLeds ;  // do the same as before but with local object instead
    nAspects = aspect [type].nAspect ;
    nAddresses = (nAspects-1) / 2 + 1  ;
}

I am not much familiar with progmem and memcpy_P.
Am I in the right direction here?

Kind regards :tumbler_glass:

Bas

Yes you could store the full array of structs in flash memory and extract the right one on demand - costing you only 52 bytes on RAM.

I would recommend you to make these 52 bytes static so that they are permanently allocated and there for you / reported at compile time rather than depend on having enough SRAM available on the stack at run time and of course use sizeof in the code rather than the magic number 52.

You could spare those 52 bytes and only consume much less bytes (all your data is uint8_t) by actually pulling only the byte(s) of interest at a given moment by accessing directly the relevant bytes in flash based on the start address of the array / structure

As a side note - if memory is super tight -

  • as the array is fixed size, you could spare the nAspect byte by padding the unused array’s entries with a EMPTY value. Your loop would then just have to check if you reach an empty position to know you are done. Or if maxAspect and maxLeds are both less or equal to 15 you could pack them (using bit fields) into the upper and lower nibble of one byte

  • You don’t need a full byte to code X, ON or OFF, 3 values fit in two bits and you have an extra value saying EMPTY. Two bytes would then be enough to store up to 8 combinations

  • although not likely to happen with the current definition - I would advise to add the packed attribute to your structure definition to ensure the compiler does not add padding and that you really have only 52 bytes, one after the other

  • use enum for readability

This seems quite wasteful. Every element consumes as much RAM as the largest element. How many bytes do you think would be the average that is actually used? Some of your examples only use 4 or 5 bytes and waste the other 47~48 bytes.

A less wasteful approach could be to build the patterns in a single, one-dimensional byte array. Then each element would only take up the space it actually needs. A downside would be that, in order to access a required element at random, a short loop to step through the array, from index zero, to find the start index of the required element, would be necessary.

I generally use an external I2C 32K x 8 FRAM memory module. They are inexpensive and fast. They work just like EEPROM with no delay and give you over a billion memory cycles.

here it's write once read many type of use, so the EEPROM or the flash will do just fine. No need for an additional / external chip

i had a similar problem storing all the tables for switch machine routes thru an interlock (see west interlock)

westIntLck.h

#ifndef ROUTES_H
#define ROUTES_H

#include "Arduino.h"

extern const char *versionTbl;

#define N_CHIP   3

typedef unsigned char byte;

// i2c bit definiton
typedef struct {
    byte        chip;
    byte        bit;
    const char* desc;
} I2cBitDef_t;

// buttons
typedef enum  {
    B_0 = 0,
    Ba1 = 10, Ba2, Ba3, Ba4, Ba5, Ba6, Ba7, Ba8, Ba9,
    Bb1 = 20, Bb2, Bb3, Bb4, Bb5, Bb6, Bb7, Bb8, Bb9,
    Bc1 = 30, Bc2, Bc3, Bc4, Bc5, Bc6, Bc7, Bc8, Bc9,
    Bx1 = 40, Bx2, Bx3, Bx4, Bx5, Bx6, Bx7, Bx8, Bx9,
    B_N = 100
} But_t;

typedef struct {
    But_t       but;
    I2cBitDef_t io;
} ButIo_t;

extern const PROGMEM ButIo_t butIos[];

// switch machine chip/bit address and polarity
typedef enum  { N, R } SwPos_t;

typedef struct {
    byte        id;
    SwPos_t     pos;
    byte        bitVal;
    I2cBitDef_t io;
} SwMach_t;

extern const SwMach_t*  smListNorm [];
extern const SwMach_t*  smListRev  [];

#define MAX_TO  9

typedef struct {
    But_t     but0;
    But_t     but1;
    const SwMach_t* list [MAX_TO];
} Route_t;

extern const PROGMEM Route_t routes [];

extern int routesSize;
#endif

westIntLck.cpp -- tables stored in PROGMEM

// New Haven West turnout and route descriptions
//

#include "westIntlck.h"

const char *versionTbl = "New Haven West 220226x table";

// button i/o position
const PROGMEM ButIo_t butIos [] = {
    { Bx1, { 0, 7, "X1" }},
    { Ba1, { 0, 6, "A1" }},
    { Ba2, { 0, 5, "A2" }},
    { Ba3, { 0, 4, "A3" }},
    { Ba4, { 0, 3, "A4" }},
    { Bx2, { 0, 2, "X2" }},
    { Bb1, { 0, 1, "B1" }},

    { Bb2, { 1, 7, "B2" }},
    { Bb3, { 1, 6, "B3" }},
    { Bb4, { 1, 5, "B4" }},
    { Bb5, { 1, 4, "B5" }},
    { Bb6, { 1, 3, "B6" }},
    { Bb7, { 1, 2, "B7" }},
    { Bx3, { 1, 1, "X3" }},

    { Bc1, { 2, 7, "C1" }},
    { Bc2, { 2, 6, "C2" }},
    { Bc3, { 2, 5, "C3" }},
    { Bc4, { 2, 4, "C4" }},
    { Bc5, { 2, 3, "C5" }},
    { Bc6, { 2, 2, "C6" }},
    { Bc7, { 2, 1, "C7" }},
    { Bc8, { 2, 0, "C8" }},

    { B_0, { 0, 0, ""   }},
};

// machine address and polarity
const PROGMEM SwMach_t s71n = { 71, N, 0, { 0, 7, "s71n" }};
const PROGMEM SwMach_t s71r = { 71, R, 1, { 0, 7, "s71r" }};
const PROGMEM SwMach_t s70n = { 70, N, 0, { 0, 6, "s70n" }};
const PROGMEM SwMach_t s70r = { 70, R, 1, { 0, 6, "s70r" }};
const PROGMEM SwMach_t s72n = { 72, N, 0, { 0, 5, "s72n" }};
const PROGMEM SwMach_t s72r = { 72, R, 1, { 0, 5, "s72r" }};
const PROGMEM SwMach_t s68n = { 68, N, 0, { 0, 4, "s68n" }};
const PROGMEM SwMach_t s68r = { 68, R, 1, { 0, 4, "s68r" }};

const PROGMEM SwMach_t s66n = { 66, N, 0, { 0, 3, "s66n" }};
const PROGMEM SwMach_t s66r = { 66, R, 1, { 0, 3, "s66r" }};
const PROGMEM SwMach_t s69n = { 69, N, 0, { 0, 2, "s69n" }};
const PROGMEM SwMach_t s69r = { 69, R, 1, { 0, 2, "s69r" }};
const PROGMEM SwMach_t s67n = { 67, N, 0, { 0, 1, "s67n" }};
const PROGMEM SwMach_t s67r = { 67, R, 1, { 0, 1, "s67r" }};
const PROGMEM SwMach_t s65n = { 65, N, 0, { 0, 0, "s65n" }};
const PROGMEM SwMach_t s65r = { 65, R, 1, { 0, 0, "s65r" }};

const PROGMEM SwMach_t s64n = { 64, N, 0, { 1, 7, "s64n" }};
const PROGMEM SwMach_t s64r = { 64, R, 1, { 1, 7, "s64r" }};
const PROGMEM SwMach_t s58n = { 58, N, 1, { 1, 6, "s58n" }};
const PROGMEM SwMach_t s58r = { 58, R, 0, { 1, 6, "s58r" }};
const PROGMEM SwMach_t s56n = { 56, N, 0, { 1, 5, "s56n" }};
const PROGMEM SwMach_t s56r = { 56, R, 1, { 1, 5, "s56r" }};
const PROGMEM SwMach_t s59n = { 59, N, 0, { 1, 4, "s59n" }};
const PROGMEM SwMach_t s59r = { 59, R, 1, { 1, 4, "s59r" }};

const PROGMEM SwMach_t s60n = { 60, N, 0, { 1, 3, "s60n" }};
const PROGMEM SwMach_t s60r = { 60, R, 1, { 1, 3, "s60r" }};
const PROGMEM SwMach_t s61n = { 61, N, 0, { 1, 2, "s61n" }};
const PROGMEM SwMach_t s61r = { 61, R, 1, { 1, 2, "s61r" }};
const PROGMEM SwMach_t s62n = { 62, N, 0, { 1, 1, "s62n" }};
const PROGMEM SwMach_t s62r = { 62, R, 1, { 1, 1, "s62r" }};
const PROGMEM SwMach_t s63n = { 63, N, 0, { 1, 0, "s63n" }};
const PROGMEM SwMach_t s63r = { 63, R, 1, { 1, 0, "s63r" }};


const PROGMEM SwMach_t s54n = { 54, N, 0, { 2, 7, "s54n" }};
const PROGMEM SwMach_t s54r = { 54, R, 1, { 2, 7, "s54r" }};
const PROGMEM SwMach_t s51n = { 51, N, 0, { 2, 6, "s51n" }};
const PROGMEM SwMach_t s51r = { 51, R, 1, { 2, 6, "s51r" }};
const PROGMEM SwMach_t s53n = { 53, N, 0, { 2, 5, "s53n" }};
const PROGMEM SwMach_t s53r = { 53, R, 1, { 2, 5, "s53r" }};
const PROGMEM SwMach_t s57n = { 57, N, 1, { 2, 4, "s57n" }};
const PROGMEM SwMach_t s57r = { 57, R, 0, { 2, 4, "s57r" }};

const PROGMEM SwMach_t s52n = { 52, N, 0, { 2, 3, "s52n" }};
const PROGMEM SwMach_t s52r = { 52, R, 1, { 2, 3, "s52r" }};
const PROGMEM SwMach_t s55n = { 55, N, 1, { 2, 2, "s55n" }};
const PROGMEM SwMach_t s55r = { 55, R, 0, { 2, 2, "s55r" }};


#if 1
const SwMach_t* smListNorm [] = {
   &s51n, &s52n, &s53n, &s54n, &s55n, &s56n, &s57n, &s58n, &s59n,
   &s61n, &s63n, &s65n, &s66n, &s67n, &s68n, &s69n,
   &s70n, &s71n, &s72n, (const SwMach_t*) 0 };

const SwMach_t* smListRev [] = {
   &s51r, &s52r, &s53r, &s54r, &s55r, &s56r, &s57r, &s58r, &s59r,
   &s61r, &s63r, &s65r, &s66r, &s67r, &s68r, &s69r,
   &s70r, &s71r, &s72r, (const SwMach_t*) 0 };
#endif

// #0_ Bx1 ___ x71 ___ ___ ___ ___ ___ ___ ___ x67 Bx2 ___
//                                     s66 x67 ___ ___ ___ ___ ___ ___ x58 ___ ___ ___ ___ ___ ___ Bc1 #9_
// #3_ Ba1 ___ ___ ___ x71 x70 ___ s66 ___ ___ ___ Bb1 x64 ___ ___ x58 ___ x56 ___ ___ ___ ___ x51 Bc2 #7_
// #1_ Ba2 ___ ___ ___ ___ ___ x70 x68 ___ ___ ___ Bb2 x65 x64 ___ ___ x56 ___ Bd1 ___ x53 x51 ___ Bc3 #5_
//                                                                                         s52 ___ Bc4 #3_
// #2_ Ba3 ___ ___ ___ ___ x72 ___ ___ ___ x68 s69 Bb3 ___ ___ x65 ___ ___ x59 x53 ___ s52 ___ ___ Bc5 #1_
//                                                 s69 ___ ___ ___ x61 x59 ___ x57 ___ ___ x54 ___ Bc6 #2_
// #4_ Ba4 ___ ___ x72 ___ ___ ___ ___ ___ ___ ___ Bb4 ___ s63 x61 ___ ___ ___ ___ x57 x55 ___ x54 Bc7 #4_
//                                                 Bx3 s63                                 x55 ___ Bc8 #6_

// routes
const PROGMEM Route_t routes [] = {
#if 0
    { Bx1, Bx2, { &x71n, &x67n }},

    { Bx1, Bb1, { &x71r, &x70n, &s66n }},
    { Bx1, Bb2, { &x71r, &x70r, &x68n }},
    { Bx1, Bb3, { &x71r, &x70r, &x68r, &s69n }},



    { Ba1, Bx2, { &x71n, &x70n, &s66r, &x67r }},
    { Ba1, Bb1, { &x71n, &x70n, &s66n }},
    { Ba1, Bb2, { &x71n, &x70r, &x68n }},
    { Ba1, Bb3, { &x71n, &x70r, &x68r, &s69n }},



    { Ba2, Bb2, { &x70n, &x68n }},
    { Ba2, Bb3, { &x70n, &x68r, &s69n }},



    { Ba3, Bb3, { &x72n, &x68n, &s69n }},



    { Ba4, Bb4, { &x72n }},



    { Bx1, B_N, { &x71r }},

    { Bx1, Bc1, { &x71r, &x70n, &s66r, &x67n, &x58n }},
    { Bx1, Bc2, { &x71r, &x70n, &s66n, &x64n, &x58n, &x56n, &x51n }},
    { Bx1, Bc3, { &x71r, &x70r, &x68n, &x65n, &x64n, &x56n, &x53n, &x51n }},
    { Bx1, Bc4, { &x71r, &x70r, &x68r, &s69n, &x65n, &x59n, &x53n, &s52r }},

    { Bx1, Bc5, { &x71r, &x70r, &x68r, &s69n, &x65n, &x59n, &x53n, &s52n }},
    { Bx1, Bc6, { &x71r, &x70r, &x68r, &s69r, &x61n, &x59n, &x57n, &x54n }},
    { Bx1, Bc7, { &x71r, &x70r, &x68r, &s69r, &x61n, &x59n, &x57r, &x54n }},
    { Bx1, Bc8, { &x71r, &x70r, &x68r, &s69r, &x61n, &x59n, &x57r, &x55r }},


    { Ba1, Bc1, { &x71n, &x70n, &s66r, &x67n, &x58r }},
    { Ba1, Bc2, { &x71n, &x70n, &s66n, &x64n, &x58n, &x56n, &x51n }},
    { Ba1, Bc3, { &x71n, &x70r, &x68n, &x65n, &x64n, &x56n, &x53n, &x51n }},
    { Ba1, Bc4, { &x71n, &x70r, &x68r, &s69n, &x65n, &x59n, &x53n, &s52r }},

    { Ba1, Bc5, { &x71n, &x70r, &x68r, &s69n, &x65n, &x59n, &x53n, &s52n }},
    { Ba1, Bc6, { &x71n, &x70r, &x68r, &s69r, &x61n, &x59n, &x57n, &x54n }},
    { Ba1, Bc7, { &x71n, &x70r, &x68r, &s69r, &x61n, &x59n, &x57r, &x54n }},
    { Ba1, Bc8, { &x71n, &x70r, &x68r, &s69r, &x61n, &x59n, &x57r, &x55r }},



    { Ba2, Bc2, { &x70n,               &x64n, &x56r, &x53n, &x51n }},
    { Ba2, Bc3, { &x70n, &x68n, &x65n, &x64n, &x56n, &x53n, &x51n }},
    { Ba2, Bc4, { &x70n, &x68r, &s69n, &x65n, &x59n, &x53n, &s52r }},
    { Ba2, Bc5, { &x70n, &x68r, &s69n, &x65n, &x59n, &x53n, &s52n }},
    { Ba2, Bc6, { &x70n, &x68r, &s69r, &x61n, &x59n, &x57n, &x54n }},
    { Ba2, Bc7, { &x70n, &x68r, &s69r, &x61n, &x59n, &x57r, &x55n, &x54n }},
    { Ba2, Bc8, { &x70n, &x68r, &s69r, &x61n, &x59n, &x57r, &x55r }},



    { Ba3, Bc2, { &x72n, &x68n, &s69n, &x65n, &x59n, &x53r, &x51r }},
    { Ba3, Bc3, { &x72n, &x68n, &s69n, &x65n, &x59n, &x53r, &x51n }},
    { Ba3, Bc4, { &x72n, &x68n, &s69n, &x65n, &x59n, &x53n, &s52r }},
    { Ba3, Bc5, { &x72n, &x68n, &s69n, &x65n, &x59n, &x53n, &s52n }},
    { Ba3, Bc6, { &x72n, &x68n, &s69r, &x61n, &x57n, &x54n }},
    { Ba3, Bc7, { &x72n, &x68n, &s69r, &x61n, &x57r, &x55n }},
    { Ba3, Bc8, { &x72n, &x68n, &s69r, &x61n, &x59n, &x57r, &x55r }},



    { Ba4, Bc2, { &x72r, &x68n, &s69n, &x65n, &x59n, &x53r, &x51r }},
    { Ba4, Bc3, { &x72r, &x68n, &s69n, &x65n, &x59n, &x53r, &x51n }},
    { Ba4, Bc4, { &x72r, &x68n, &s69n, &x65n, &x59n, &x53n, &s52r }},
    { Ba4, Bc5, { &x72r, &x68n, &s69n, &x65n, &x59n, &x53n, &s52n }},
    { Ba4, Bc6, { &x72r, &x68n, &s69r, &x61n, &x57n, &x54n }},
    { Ba4, Bc7, { &x72n, &x68n, &s69r, &x61n, &x57n, &x55n }},
    { Ba4, Bc8, { &x72n, &x68n, &s69r, &x61n, &x57n, &x55r }},



    { Bx3, Bc2, { &s63n, &x61n, &x59r, &x53r, &x51r }},
    { Bx3, Bc3, { &s63n, &x61n, &x59r, &x53r, &x51n }},
    { Bx3, Bc4, { &s63n, &x61n, &x59r, &x53n, &s52r }},
    { Bx3, Bc5, { &s63n, &x61n, &x59r, &x53n, &s52n }},
    { Bx3, Bc6, { &s63n, &x61r, &x59n, &x57n, &x54n }},
    { Bx3, Bc7, { &s63r, &x57n, &x55n }},
    { Bx3, Bc8, { &s63r, &x57n, &x55r }},
#else

    { Bx1, Bc1, { &s71r, &s70n, &s66r, &s67n, &s58n, }},
    { Bx1, Bc2, { &s71r, &s70n, &s66r, &s64n, &s58n, &s56n, &s51n, }},
    { Bx1, Bc3, { &s71r, &s70n, &s66n, &s64n, &s56n, &s53n, &s51n, }},
    { Bx1, Bc4, { &s71r, &s70r, &s68n, &s65r, &s59n, &s53n, &s52r, }},
    { Bx1, Bc5, { &s71r, &s70r, &s68n, &s65r, &s59n, &s53n, &s52n, }},
    { Bx1, Bc6, { &s71r, &s70r, &s68r, &s69r, &s60n, &s57n, &s54n, }},
    { Bx1, Bc7, { &s71r, &s70r, &s68r, &s69r, &s60n, &s57r, &s55n, &s54n, }},
    { Bx1, Bc8, { &s71r, &s70r, &s68r, &s69r, &s60n, &s57r, &s55r, }},

    { Bx3, Bc2, { &s63n, &s61n, &s59r, &s53r, }},
    { Bx3, Bc3, { &s63n, &s61n, &s59r, &s53r, &s51n, }},
    { Bx3, Bc4, { &s63n, &s61n, &s59r, &s53n, &s52r, }},
    { Bx3, Bc5, { &s63n, &s61n, &s59r, &s53n, &s52n, }},
    { Bx3, Bc6, { &s63n, &s61r, &s60r, &s57n, &s54n, }},
    { Bx3, Bc7, { &s63r, &s62r, &s57n, &s55n, &s54n, }},
    { Bx3, Bc8, { &s63r, &s62r, &s57n, &s55r, }},

    { Ba1, Bx2, { &s71n, &s70n, &s66r, &s67r, }},
    { Ba1, Bc1, { &s71n, &s70n, &s66r, &s67n, &s58n, }},
    { Ba1, Bc2, { &s71n, &s70n, &s66n, &s64n, &s58n, &s56n, &s51n, }},
    { Ba1, Bc3, { &s71n, &s70r, &s68n, &s65n, &s64n, &s56n, &s53n, &s51n, }},
    { Ba1, Bc4, { &s71n, &s70r, &s68n, &s65r, &s59n, &s53n, &s52r, }},
    { Ba1, Bc5, { &s71n, &s70r, &s68n, &s65r, &s59n, &s53n, &s52n, }},
    { Ba1, Bc6, { &s71n, &s70r, &s68r, &s69r, &s60n, &s57n, &s54n, }},
    { Ba1, Bc7, { &s71n, &s70r, &s68r, &s69r, &s60n, &s57r, &s55n, &s54n, }},
    { Ba1, Bc8, { &s71n, &s70r, &s68r, &s69r, &s60n, &s57r, &s55r, }},

    { Ba2, Bc2, { &s70n, &s68n, &s65n, &s64n, &s56r, &s51n, }},
    { Ba2, Bc3, { &s70n, &s68n, &s65n, &s64n, &s56n, &s53n, &s51n, }},
    { Ba2, Bc4, { &s70n, &s68n, &s65r, &s59n, &s53n, &s52r, }},
    { Ba2, Bc5, { &s70n, &s68n, &s65r, &s59n, &s53n, &s52n, }},
    { Ba2, Bc6, { &s70n, &s68r, &s67r, &s60n, &s57n, &s54n, }},
    { Ba2, Bc7, { &s70n, &s68r, &s69r, &s60n, &s57r, &s55n, &s54n, }},
    { Ba2, Bc8, { &s70n, &s68r, &s69n, &s60n, &s57r, &s55r, }},

    { Ba3, Bc2, { &s72n, &s68n, &s69n, &s65n, &s59n, &s53r, &s51r, }},
    { Ba3, Bc3, { &s72n, &s68n, &s69n, &s65n, &s59n, &s53r, &s51n, }},
    { Ba3, Bc4, { &s72n, &s68n, &s69n, &s65n, &s59n, &s53n, &s52r, }},
    { Ba3, Bc5, { &s72n, &s68n, &s69n, &s65n, &s59n, &s53n, &s52n, }},
    { Ba3, Bc6, { &s72n, &s68n, &s69r, &s60n, &s57n, &s55n, &s54n, }},
    { Ba3, Bc7, { &s72n, &s68n, &s69r, &s60n, &s57r, &s55n, &s54n, }},
    { Ba3, Bc8, { &s72n, &s68n, &s69r, &s60n, &s57r, &s55r, }},

    { Ba4, Bc2, { &s72r, &s68n, &s69n, &s65n, &s59r, &s53r, &s51r, }},
    { Ba4, Bc3, { &s72r, &s68n, &s69n, &s65n, &s59r, &s53r, &s51n, }},
    { Ba4, Bc4, { &s72r, &s68n, &s69n, &s65n, &s59n, &s53n, &s52r, }},
    { Ba4, Bc5, { &s72r, &s68n, &s69n, &s65n, &s59n, &s53n, &s52n, }},
    { Ba4, Bc6, { &s72r, &s68n, &s69r, &s60n, &s57n, &s54n, }},
    { Ba4, Bc7, { &s72n, &s62n, &s57n, &s55n, &s54n, }},
    { Ba4, Bc8, { &s72n, &s62n, &s57n, &s55r, }},

    { Ba1, Bx2, { &s71n, &s70n, &s66r, &s67r, }},
    { Ba1, Bc1, { &s71n, &s70n, &s66r, &s67n, &s58n, }},
    { Ba1, Bc2, { &s71n, &s70n, &s66n, &s64n, &s58n, &s56n, &s51n, }},
    { Ba1, Bc3, { &s71n, &s70r, &s68n, &s65n, &s64n, &s56n, &s53n, &s51n, }},
    { Ba1, Bc4, { &s71n, &s70r, &s68n, &s65r, &s59n, &s53n, &s52r, }},
    { Ba1, Bc5, { &s71n, &s70r, &s68n, &s65r, &s59n, &s53n, &s52n, }},
    { Ba1, Bc6, { &s71n, &s70r, &s68r, &s69r, &s60n, &s57n, &s54n, }},
    { Ba1, Bc7, { &s71n, &s70r, &s68r, &s69r, &s60n, &s57r, &s55n, &s54n, }},
    { Ba1, Bc8, { &s71n, &s70r, &s68r, &s69r, &s60n, &s57r, &s55r, }},

    { Ba2, Bc2, { &s70n, &s68n, &s65n, &s64n, &s56r, &s51n, }},
    { Ba2, Bc3, { &s70n, &s68n, &s65n, &s64n, &s56n, &s53n, &s51n, }},
    { Ba2, Bc4, { &s70n, &s68n, &s65r, &s59n, &s53n, &s52r, }},
    { Ba2, Bc5, { &s70n, &s68n, &s65r, &s59n, &s53n, &s52n, }},
    { Ba2, Bc6, { &s70n, &s68r, &s67r, &s60n, &s57n, &s54n, }},
    { Ba2, Bc7, { &s70n, &s68r, &s69r, &s60n, &s57r, &s55n, &s54n, }},
    { Ba2, Bc8, { &s70n, &s68r, &s69n, &s60n, &s57r, &s55r, }},

    { Ba3, Bc2, { &s72n, &s68n, &s69n, &s65n, &s59n, &s53r, &s51r, }},
    { Ba3, Bc3, { &s72n, &s68n, &s69n, &s65n, &s59n, &s53r, &s51n, }},
    { Ba3, Bc4, { &s72n, &s68n, &s69n, &s65n, &s59n, &s53n, &s52r, }},
    { Ba3, Bc5, { &s72n, &s68n, &s69n, &s65n, &s59n, &s53n, &s52n, }},
    { Ba3, Bc6, { &s72n, &s68n, &s69r, &s60n, &s57n, &s55n, &s54n, }},
    { Ba3, Bc7, { &s72n, &s68n, &s69r, &s60n, &s57r, &s55n, &s54n, }},
    { Ba3, Bc8, { &s72n, &s68n, &s69r, &s60n, &s57r, &s55r, }},

    { Ba4, Bc2, { &s72r, &s68n, &s69n, &s65n, &s59r, &s53r, &s51r, }},
    { Ba4, Bc3, { &s72r, &s68n, &s69n, &s65n, &s59r, &s53r, &s51n, }},
    { Ba4, Bc4, { &s72r, &s68n, &s69n, &s65n, &s59n, &s53n, &s52r, }},
    { Ba4, Bc5, { &s72r, &s68n, &s69n, &s65n, &s59n, &s53n, &s52n, }},
    { Ba4, Bc6, { &s72r, &s68n, &s69r, &s60n, &s57n, &s54n, }},
    { Ba4, Bc7, { &s72n, &s62n, &s57n, &s55n, &s54n, }},
    { Ba4, Bc8, { &s72n, &s62n, &s57n, &s55r, }},
#endif

    { },
};

int routesSize = sizeof(routes);

loadSwMach () makes a local copy in RAM from the entry in PROGMEM

SwMach_t *
loadSwMach (
    SwMach_t        *sm,
    const SwMach_t  *smPgm )
{
    sprintf (s, "%s:  sm %p, smPgm %p", __func__, sm, smPgm);
 // Serial.println (s);

    sm->id      = pgm_read_byte (& (smPgm->id));
    sm->pos     = pgm_read_byte (& (smPgm->pos));
    sm->bitVal  = pgm_read_byte (& (smPgm->bitVal));

    sm->io.chip = pgm_read_byte (& (smPgm->io.chip));
    sm->io.bit  = pgm_read_byte (& (smPgm->io.bit));


    return sm;
}

swTurnout() operate one copy in RAM

void
swTurnout (
    int  id )
{
    SwMach_t    sm;

    for (const SwMach_t **p = smListRev; 0 != *p; p++)  {
        loadSwMach (&sm, *p);

        if (sm.id == id)  {
            sprintf (s, "%s: id %2d, pos %d, val %d, chip %d, bit %d",
                __func__, sm.id, sm.pos, sm.bitVal, sm.io.chip, sm.io.bit);
            Serial.println (s);


            bitTgl (sm.io.chip, GPIOA, sm.io.bit);
            return;
        }
    }

    sprintf (s, "%s: id %2d, not found", __func__, id);
    Serial.println (s);
}

just some thought on

if that ever matters, you could have saved one byte per ButIo_t by just forcing the enum to use a byte instead of a int

enum But_t : byte { // <<== tell the compiler that the underlying type can be a byte instead of the default int
  B_0 = 0,
  Ba1 = 10, Ba2, Ba3, Ba4, Ba5, Ba6, Ba7, Ba8, Ba9,
  Bb1 = 20, Bb2, Bb3, Bb4, Bb5, Bb6, Bb7, Bb8, Bb9,
  Bc1 = 30, Bc2, Bc3, Bc4, Bc5, Bc6, Bc7, Bc8, Bc9,
  Bx1 = 40, Bx2, Bx3, Bx4, Bx5, Bx6, Bx7, Bx8, Bx9,
  B_N = 100
} ;

thanks for the suggestion and example of syntax
worth considering

Thnx for al the answers

This seems quite wasteful. Every element consumes as much RAM as the largest element

This is true, but with all information stored in Flash, it simply does not matter. Terms like 'efficieny' are subject to interpretation. Some signals may only need 6/52 bytes. But "unused space is wasted space"? Flash will be in abundance.

Using separate array could make the code more cumbersome. The structs are easy to use and they are easy for maintanance. So yeah, what is 'efficient'?

I am not in favor of using EEPROM this time. I don't really want to. I know I would just have to rewrite the data in the structs in a different form. But using progmem seems more logical to me. And I am not yet sure if 2048 bytes will be enough. It is for now, but if I want to add 3 more LEDS end 10 more aspects (which is plausible) I go to up 162 bytes per signal. With 10k of flash I can still have 60 signal types.

As is now, my sketch uses 7500 bytes of flash and I don't expect exceeding the 8k.

You don’t need a full byte to code X, ON or OFF, 3 values fit in two bits and you have an extra value saying EMPTY. Two bytes would then be enough to store up to 8 combinations

I am fully aware of this, but I don't know how to. At first I used one byte in binary notation for ON and OFF than I realized I needed blinking. At first I wanted to use bitfields but with an array I could not get a viable solution.

although not likely to happen with the current definition - I would advise to add the packed attribute to your structure definition to ensure the compiler does not add padding and that you really have only 52 bytes, one after the other

I thought that 'padding' does not exist on 8 bit uControllers. I though that padding was ment for storing like 4 bytes of a structure in one memory location of a 32 bit architecture??

use enum for readability

I'm sorry, I don't follow here. How can I use enum in this example exactly? I don't see these structures as unreadable and I don't make use of sequential enumeration?

Kind regards :coffee:

Bas

Something here doesn't add up

Two things actually.

I made a mistake. EEPROM is 1024 bytes in an atmega not 2048... :see_no_evil:
I also forgot that using progmem would ofcourse increasy my flash usage drastically :see_no_evil:

Use of progmem and bit twiddling tricks can certainly save memory in a project like this. It can be very satisfying, costing you nothing but time.

Note though that there are plenty of dirt cheap alternatives to the base Arduino models now and for less than $20, you can buy your way out of the problem :grinning:

I stopped with making arduino shields. Too much soldering work for my taste. I do full SMT assembly now. This board contains an atmega and is currently somewhere between China and my front door.

(Cheaper than 20$ )

I meant for your ON, OFF, X or whatever magic value (not sure if they were #define or constexpr or whatever)

Ah I see :+1: . I was actually considering to replace the X by a blinking time in / 10ms or a frequency. They are const int atm

0 = OFF, 
1 = ON,
 
2 = blink every 20ms
...
250 = blink every 2500ms

That would enable me to get different blinking frequencies per aspect for 1 type. And 2.5s is more than sufficient.

Regards,

Bas

Got me a compile error and I can't figure out what I'm doing wrong.

error: cannot convert 'Aspect {aka Aspects}' to 'void*' for 
argument '1' to 'void* memcpy_P(void*, const void*, size_t)'
         memcpy_P( localAspect , &aspects[type], sizeof( localAspect ) ) ;

The structure

typedef struct Aspects 
{
    uint8 nAspect ;
    uint8 nLeds ;
    uint8 aspects[maxAspect][maxLeds] ;
} Aspect ;

The array

const int nAspects = 30 ;
const Aspect aspects[nAspects] PROGMEM =
{
    {   2,                                  // nAspect
        2,                                  // nLeds   <-- for double coils only
...
...

Underneath it I have a static aspect

static Aspect localAspect ;

And with this line

memcpy_P( localAspect , &aspects[type], sizeof( localAspect ) ) ; // type is uint8_t variable

I try to copy the array element into the localAspect object.

What I am doing wrong this time :see_no_evil:

Bas

P.S. uint8 is typedefed as uint8_t

You really should not need to copy the data from PROGMEM to ram in order to use it. I took a quick look at the other discussion that you linked, the memcpy_P was needed there because the data needed to be passed to a library that required the data be in ram, but in this case you are writing the code that accesses the data.

I think you have misread something.

the memcpy_P was needed there because the data needed to be passed to a library that required the data be in ram

The memcpy_P was needed there because I put the pixle tables in PROGMEM deliberatly for the very same reason as I want to do it now. Me back than:

I do want to use PROGMEM really bad because that will save me atleast 12% SRAM.

In this case RAM will be even too small to contain the array. It has to be moved out of RAM or the program won't work at all. What'd you suggest I do?

Bas

I found the solution!!! The previous time that I used memcpy_P I was copying to an array. Therefor it was not needed to add an & before the first argument. It is now because localAspect isn't an array.

memcpy_P(&localAspect , &aspects[index], sizeof( localAspect ) ) ; 

:white_check_mark:

A coworker was so kind to help me out here.

I was so dumb to even attempt asking on stackoverflow. I spent time making a clear question to comply with all d'em rules and within no time you get a comment:

You do know that you must pass pointers to the memory you want to copy to and from? Are the arguments you pass really pointers? Is localAspect a pointer? The error message is really clear.

So he basically admits he knows the answer, refuses to give it and he downvotes my question.

Regards,

BAs

Knowing the rules I looked on SO for an answer, couldn't find it. I tried, honestly.. It was my last resort.

Next time I go look for normal human beings with social lives to ask my question like my dear coworkers or the people here...