MODBUS on the Pi Part 2, Adding functionality for testing

Making a more useful example involves adding registers to emulate the behaviour of an existing MODBUS device.
The previous code in Part 1 is good for getting a simple MODBUS connection, but to do some more serious testing it would be nice to have some data that has definite structure to be able to validate the functionality.
I have modified the code to include some test registers that randomly change, and large groups of registers that emulate the surface card format of a Lufkin Rod String controller.

Here are simple functions for creating Lufkin Surface Cards, and Pump Cards that I have incorporated into my Pi program

void CreateSurfaceCard(int nAddress,modbus_mapping_t *mb_mapping)
{
    // this simulates a LUFKIN Rod String Controller MODBUS device
    int nStrokeLenx100_in = 14000;
    int nMinLoad_lbs = 3000;
    int nMaxLoad_lbs=10000;
    int nLoad_lbs = nMinLoad_lbs;
    int nLoadRange = nMaxLoad_lbs - nMinLoad_lbs;
    int i;
    // write header
    time_t timer;
    time(&timer);
    mb_mapping->tab_input_registers[nAddress++] = timer ;
    mb_mapping->tab_input_registers[nAddress++] = timer >> 16;
    mb_mapping->tab_input_registers[nAddress++] = 5; // high byte= shutdown eventID, low byte=num points, can hold up to 200 pts
    mb_mapping->tab_input_registers[nAddress++] = nMaxLoad_lbs; // max load lbs
    mb_mapping->tab_input_registers[nAddress++] = nMinLoad_lbs; // min load lbs
    mb_mapping->tab_input_registers[nAddress++] = nStrokeLenx100_in; // stroke length x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 1500; // stroke period x 100 (s)
    // Note: card data is in reverse chronological order (bottom stroke, then downstroke then top then upstroke)
    nLoad_lbs = nMinLoad_lbs;
    
    // five points to look like a simple card
    mb_mapping->tab_input_registers[nAddress++] = 23*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 1200; // scaled load (lbs)

    mb_mapping->tab_input_registers[nAddress++] = 120*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 1200; // scaled load (lbs)
    
    mb_mapping->tab_input_registers[nAddress++] = 140*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 2200; // scaled load (lbs)

    mb_mapping->tab_input_registers[nAddress++] = 40*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 2200; // scaled load (lbs)	
    
    mb_mapping->tab_input_registers[nAddress++] = 20*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 1200; // scaled load (lbs)
}

void CreatePumpCard(int nAddress,modbus_mapping_t *mb_mapping)
{
    // this simulates a LUFKIN Rod String Controller MODBUS device
    int nStrokeLenx100_in = 13000;
    int nMinLoad_lbs = 0001;
    int nMaxLoad_lbs=4000;
    int nLoad_lbs = nMinLoad_lbs;
    int nLoadRange = nMaxLoad_lbs - nMinLoad_lbs;
    int i;
    // write header
    time_t timer;
    time(&timer);
    mb_mapping->tab_input_registers[nAddress++] = timer >> 16;
    mb_mapping->tab_input_registers[nAddress++] = timer;
    mb_mapping->tab_input_registers[nAddress++] = nMaxLoad_lbs; // max load lbs
    mb_mapping->tab_input_registers[nAddress++] = nMinLoad_lbs; // min load lbs
    mb_mapping->tab_input_registers[nAddress++] = 5; // high byte= shutdown eventID, low byte=num points , can hold up to 100 pts
    mb_mapping->tab_input_registers[nAddress++] = 100*100; // gross stroke x 100 (in) ??
    mb_mapping->tab_input_registers[nAddress++] = 90*100; // net stroke x 100 (in) ??
    
    mb_mapping->tab_input_registers[nAddress++] = 99; // pump fillage (?)
    mb_mapping->tab_input_registers[nAddress++] = 3000; // fluid load (lbs?)
    // card data is in reverse chronological order (bottom stroke, then downstroke then top then upstroke)

    
    // five points to look like a simple pump card
    mb_mapping->tab_input_registers[nAddress++] = 3*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 10; // scaled load (lbs)
    
    mb_mapping->tab_input_registers[nAddress++] = 100*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 10; // scaled load (lbs)
            
    mb_mapping->tab_input_registers[nAddress++] = 120*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 1000; // scaled load (lbs)
    
    mb_mapping->tab_input_registers[nAddress++] = 20*100; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 1000; // scaled load (lbs)
    
    mb_mapping->tab_input_registers[nAddress++] = 0; // position x 100 (in)
    mb_mapping->tab_input_registers[nAddress++] = 10; // scaled load (lbs)
}


void updateAllSurfaceCards(modbus_mapping_t *mb_mapping)
{
    // this simulates a LUFKIN Rod String Controller MODBUS device
    // update the surface card buffer 5 cards, then the Pump Card buffer 5 cards, then the single surface and pump card, top is most recent (input registers 2669 - cnt=2035)
    int nAddress = 2669; // start of 5 surface card buffer
    int i;
    for( i= 0 ; i < 5; i++ )
    {
      CreateSurfaceCard(nAddress,mb_mapping);
      nAddress += 407;
    }

    nAddress = 4704; // start of 5 Pump Card Buffer
    for( i= 0 ; i < 5; i++ )
    {
      CreatePumpCard(nAddress,mb_mapping);
      nAddress += 209;
    }

    nAddress = 5749; // start of single surface card buffer
    CreateSurfaceCard(nAddress,mb_mapping);

    nAddress = 6156; // start of single pump card buffer
    CreatePumpCard(nAddress,mb_mapping);
}

Now, after we have initialized the mb_mapping structure, and inside the 'for' loop we can call the function to fill our card data. We can also put some other data in a few registers.

updateAllSurfaceCards(mb_mapping); 

// every time we do a communication, update a bunch of the registers so we have something interesting to plot on the graphs
mb_mapping->tab_registers[0]++; // increment the holding reg 0 for each read
mb_mapping->tab_input_registers[0] = 2; // version number
// put randomly changing values in input and holding registers 1 to 9
for( i=1;i<10;i++)
{
    // randomly increase or decrease the register, but do not allow wrapping
    if( rand() > RAND_MAX/2 )
    {		
        if ( mb_mapping->tab_input_registers[i] < 0xfffe );
	    mb_mapping->tab_input_registers[i] += 1;

	if( mb_mapping->tab_registers[i+1] < 0xfffe )
	    mb_mapping->tab_registers[i+1] += 1;
    }
    else
    {
        if( mb_mapping->tab_input_registers[i] > 0 )
            mb_mapping->tab_input_registers[i] -= 1;
        if( mb_mapping->tab_registers[i+1] > 0 )
            mb_mapping->tab_registers[i+1] -= 1;
    } 
}

Updated 2014-01-24
The latest full code for the Modbus example can be found at the link below, plus a little bit more involving preparing the IO's for access that I have not written about yet.
I have renamed the c file to be modserv.c because I wanted to type less, and it barely resembles the original program any more.
modserv Part 2
To compile this program type:
$gcc -I /usr/include/modbus modserv.c -o modserv -L/usr/lib/modbus -lmodbus

Continue on to MODBUS on the Pi Part 3, Interfacing with IO’s

Previously MODBUS on the Pi Part 1, compiling a basic example