La séptima vida

...o el gato así lo espera/teme

Week 41: Add three with a Modbus server

You want to see something cool? Like the multiplication table for number three? Perhaps this is the least practical way of calculating such a thing, but it is an easy way to verify that the information goes indeed to the client and then comes back to the server.

The idea is quite simple. The client sends a request to write number zero to address zero in the server and the server saves this number. Then, the client requests the next number from address two. The server takes the first number, adds three, and returns it. The client adds three to the received number and sends it again.

The client is an Arduino Uno using the ModbusMaster library. It simply sends a "Write single register" request while sending the number, and a "Read holding registers" request to retrieve the next number from the server.

The Arduino program is here:

/* Arduino side for Device::Modbus::RTU::Server demonstration 
    Based on the Basic example of the ModbusMaster library
    by Doc Walker 
*/

#include 

// instantiate ModbusMaster object as slave ID 2
// defaults to serial port 0 since no port was specified
ModbusMaster node(2);

uint16_t  plus_three = 0;

void setup() {
  // initialize Modbus communication baud rate
  node.begin(19200);
}


void loop() {
  uint8_t result;
  
  // Send the TX buffer to a 16-bit register at register 0
  result = node.writeSingleRegister(0, plus_three);
  
  // Read a 16-bit register starting at address 2 to RX buffer
  result = node.readHoldingRegisters(2, 1);
  
  // Get the received number and add 3
  if (result == node.ku8MBSuccess) {
    plus_three = node.getResponseBuffer(0);
    plus_three += 3;
  }
}

The server program that goes along is this:

#! /usr/bin/env perl

use Device::Modbus::RTU::Server;
use strict;
use warnings;
use v5.10;

{
    package My::Unit;
    our @ISA = ('Device::Modbus::Unit');

    my $number = 0;

    sub init_unit {
        my $unit = shift;

        #                Zone            addr qty   method
        #           -------------------  ---- ---  ---------
        $unit->get('holding_registers',    2,  1,  'get_plus_three');
        $unit->put('holding_registers',    0,  1,  'save_plus_three');
    }

    sub get_plus_three {
        my ($unit, $server, $req, $addr, $qty) = @_;
        $number += 3;
        $server->log(3,"> $number");
        return $number;
    }

    sub save_plus_three {
        my ($unit, $server, $req, $addr, $qty, $values) = @_;
        $server->log(3,"< " . $values->[0]);
        $number = $values->[0];
    }
}

my $server = Device::Modbus::RTU::Server->new(
    port      =>  '/dev/ttyACM0',
    baudrate  => 19200,
    parity    => 'none',
    log_level => 3,
);

my $unit = My::Unit->new(id => 2);
$server->add_server_unit($unit);

$server->start;

After running these little demonstrations, the log file contains:

NOTICE : Mon Oct  5 23:48:16 2015 -- server_rtu.pl -- Device::Modbus::RTU::Serve
r -- Starting server
INFO : Mon Oct  5 23:48:18 2015 -- server_rtu.pl -- My::Unit -- < 0
INFO : Mon Oct  5 23:48:18 2015 -- server_rtu.pl -- My::Unit -- > 3
INFO : Mon Oct  5 23:48:18 2015 -- server_rtu.pl -- My::Unit -- < 6
INFO : Mon Oct  5 23:48:18 2015 -- server_rtu.pl -- My::Unit -- > 9
INFO : Mon Oct  5 23:48:18 2015 -- server_rtu.pl -- My::Unit -- < 12
INFO : Mon Oct  5 23:48:18 2015 -- server_rtu.pl -- My::Unit -- > 15
INFO : Mon Oct  5 23:48:18 2015 -- server_rtu.pl -- My::Unit -- < 18
INFO : Mon Oct  5 23:48:19 2015 -- server_rtu.pl -- My::Unit -- > 21
INFO : Mon Oct  5 23:48:19 2015 -- server_rtu.pl -- My::Unit -- < 24
INFO : Mon Oct  5 23:48:19 2015 -- server_rtu.pl -- My::Unit -- > 27
INFO : Mon Oct  5 23:48:19 2015 -- server_rtu.pl -- My::Unit -- < 30
INFO : Mon Oct  5 23:48:19 2015 -- server_rtu.pl -- My::Unit -- > 33
INFO : Mon Oct  5 23:48:19 2015 -- server_rtu.pl -- My::Unit -- < 36
INFO : Mon Oct  5 23:48:19 2015 -- server_rtu.pl -- My::Unit -- > 39

Et voilà our multiplication table. It is such a pleasure to see it working. My primary teacher would be proud.

The latest Device::Modbus is yet to hit GitHub. I still need to move the latest upgrades to the RTU client module and to the TCP client and server. But it is definitely working now.