• Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    Need Some sort of Jumpstart

    Official EVOK API
    3
    11
    1418
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • K
      knebb last edited by

      Hi all,

      I am controlling my heating up to now based on a Raspi together with a relay board. Now I want to use my Neuron M103 for this purpose.

      Up to now I coded in good, old C-Code. Running perfectly, integrated into systemd, doing syslogging and so on. Regarding Python- I know it is a interpreting programing language and did some first basic steps. But I doubt I can do the same I did in C in Python (from my knowledge, not from language abilities).

      However, I would like to use my Neuron to control the relays. I have ssen the Evok API but I have no clue how to really use. Are there somewhere some nice examples how others used it?

      Is there a possibility to use C anyhow?

      Thanks!

      /KNEBB

      1 Reply Last reply Reply Quote 0
      • ntd
        ntd last edited by

        Hi @knebb
        I developed a fistful of programs in plain C on Neuron. I just linked against libmodbus and called the loopback ModBUS server at 127.0.0.1, port 502 (hence no need to enable the EVOK service).
        If you are not familiar I can send you some code to help you getting started.

        1 Reply Last reply Reply Quote 0
        • M
          martin_triska administrators last edited by

          Hello @knebb, as @ntd wrote there is no need to use the Evok (which is, basically a bridge between Modbus server and several web-service interfaces). The simplest option for direct access from a C/C++ application should be the sysfs interface. Of course, a Modbus client connected to the internally running Modbus server can be used also.

          K 1 Reply Last reply Reply Quote 1
          • K
            knebb last edited by

            @martin_triska @ntd
            Great thanks for the hints. Using libmodbus seems to be the way I will go. Seems to be easy enough to use and is available in C. I already found the Neuron Docs regarding the coils and registers.

            If you could send me your example code sniplets? How to set the relays, read DI or set DO?

            Thanks a lot in advance! first step is done.

            /KNEBB

            ntd 1 Reply Last reply Reply Quote 0
            • K
              knebb @martin_triska last edited by

              @martin_triska

              I checked for the sysfs, too.

              I did not see any references to the relays? How can I set relays (Neuron M103 has some) through sysfs?

              Thanks again!

              /KNEBB

              1 Reply Last reply Reply Quote 0
              • ntd
                ntd @knebb last edited by

                @knebb The relevant function is fieldbus_thread, that basically runs a polling loop every 20 ms. If you do not need threads, you can consider that function as main() and drop all the locking stuff (i.e. a good 50% of code in iteration).

                #include "lappaseven.h"
                #include "fieldbus.h"
                #include <errno.h>
                #include <string.h>
                
                #define MODBUS_ID       0
                
                #define AI1_ADDRESS     3
                #define DI1_ADDRESS     0
                #define DI2_ADDRESS     100
                #define DI3_ADDRESS     200
                #define DO1_ADDRESS     1
                #define DO2_ADDRESS     101
                #define DO3_ADDRESS     201
                #define AO1_ADDRESS     2
                #define VREFINT_ADDRESS 5
                #define VREF_ADDRESS    1009
                
                
                static IO image = { 0 };
                
                
                void
                fieldbus_free(modbus_t *modbus)
                {
                    if (modbus != NULL) {
                        modbus_close(modbus);
                        modbus_free(modbus);
                    }
                }
                
                modbus_t *
                fieldbus_new(void)
                {
                    modbus_t *modbus;
                    gchar *host;
                    int port;
                
                    host   = g_settings_get_string(gs.settings, "modbus-host");
                    port   = g_settings_get_uint(gs.settings, "modbus-porta");
                    modbus = modbus_new_tcp(host, port);
                
                    if (modbus == NULL ||
                        modbus_connect(modbus) == -1 ||
                        modbus_set_slave(modbus, MODBUS_ID) == -1)
                    {
                        g_warning("Unable to initialize communication on %s:%d: %s",
                                  host, port, modbus_strerror(errno));
                        fieldbus_free(modbus);
                        modbus = NULL;
                    }
                
                    g_free(host);
                    return modbus;
                }
                
                gboolean
                fieldbus_get_ai(modbus_t *modbus)
                {
                    uint16_t vrefint, vref, ai;
                
                    if (modbus_read_registers(modbus, VREFINT_ADDRESS, 1, &vrefint) == -1 ||
                        modbus_read_registers(modbus, VREF_ADDRESS, 1, &vref) == -1 ||
                        modbus_read_registers(modbus, AI1_ADDRESS, 1, &ai) == -1)
                    {
                        g_warning("fieldbus_get_ai error: %s", modbus_strerror(errno));
                        return FALSE;
                    }
                
                    /* Linearization to volts as described by Neuron manual, page 14.
                     * AI1vdev and AI1voffset are set to supposedly 0. */
                    image.AI1 = 3.3 * vref / vrefint * 3 * ai / 4096;
                    return TRUE;
                }
                
                gboolean
                fieldbus_get_di(modbus_t *modbus)
                {
                    uint16_t word1, word2, word3;
                    int n;
                
                    if (modbus_read_registers(modbus, DI1_ADDRESS, 1, &word1) == -1 ||
                        modbus_read_registers(modbus, DI2_ADDRESS, 1, &word2) == -1 ||
                        modbus_read_registers(modbus, DI3_ADDRESS, 1, &word3) == -1)
                    {
                        g_warning("fieldbus_get_di error: %s", modbus_strerror(errno));
                        return FALSE;
                    }
                
                    for (n = 0; n < G_N_ELEMENTS(image.DI1); ++n) {
                        image.DI1[n] = (word1 & (1 << n)) > 0;
                    }
                    for (n = 0; n < G_N_ELEMENTS(image.DI2); ++n) {
                        image.DI2[n] = (word2 & (1 << n)) > 0;
                    }
                    for (n = 0; n < G_N_ELEMENTS(image.DI3); ++n) {
                        image.DI3[n] = (word3 & (1 << n)) > 0;
                    }
                
                    return TRUE;
                }
                
                gboolean
                fieldbus_set_do(modbus_t *modbus)
                {
                    uint16_t word1, word2, word3;
                    int n;
                
                    word1 = 0;
                    word2 = 0;
                    word3 = 0;
                
                    for (n = 0; n < G_N_ELEMENTS(image.DO1); ++n) {
                        word1 |= image.DO1[n] << n;
                    }
                    for (n = 0; n < G_N_ELEMENTS(image.DO2); ++n) {
                        word2 |= image.DO2[n] << n;
                    }
                    for (n = 0; n < G_N_ELEMENTS(image.DO3); ++n) {
                        word3 |= image.DO3[n] << n;
                    }
                
                    if (modbus_write_register(modbus, DO1_ADDRESS, word1) == -1 ||
                        modbus_write_register(modbus, DO2_ADDRESS, word2) == -1 ||
                        modbus_write_register(modbus, DO3_ADDRESS, word3) == -1)
                    {
                        g_warning("fieldbus_set_do error: %s", modbus_strerror(errno));
                        return FALSE;
                    }
                
                    return TRUE;
                }
                
                gboolean
                fieldbus_set_ao(modbus_t *modbus)
                {
                    /* Analog output must be between 0 .. 5 V, hence the division */
                    if (modbus_write_register(modbus, AO1_ADDRESS, image.AO1 / 2) == -1) {
                        g_warning("fieldbus_set_ao error: %s", modbus_strerror(errno));
                        return FALSE;
                    }
                
                    return TRUE;
                }
                
                #if THREADING
                static gboolean
                iteration(modbus_t *modbus)
                {
                    static gboolean workaround = FALSE;
                    gboolean AI, DI, AO, DO, quit;
                
                    AI = fieldbus_get_ai(modbus);
                    DI = fieldbus_get_di(modbus);
                
                    g_mutex_lock(&gs.fieldbus_lock);
                    AO = gs.hal.AO1 != gs.share.AO1;
                    DO = memcmp(gs.hal.DO1, gs.share.DO1, sizeof(gs.hal.DO1)) != 0 ||
                         memcmp(gs.hal.DO2, gs.share.DO2, sizeof(gs.hal.DO2)) != 0 ||
                         memcmp(gs.hal.DO3, gs.share.DO3, sizeof(gs.hal.DO3)) != 0;
                    if (DI) {
                        memcpy(gs.share.DI1, image.DI1, sizeof(image.DI1));
                        memcpy(gs.share.DI2, image.DI2, sizeof(image.DI2));
                        memcpy(gs.share.DI3, image.DI3, sizeof(image.DI3));
                    }
                    if (AI) {
                        gs.share.AI1 = image.AI1;
                    }
                    if (DO) {
                        memcpy(image.DO1, gs.share.DO1, sizeof(image.DO1));
                        memcpy(image.DO2, gs.share.DO2, sizeof(image.DO2));
                        memcpy(image.DO3, gs.share.DO3, sizeof(image.DO3));
                    }
                    if (AO) {
                        image.AO1 = gs.share.AO1;
                    }
                    quit = gs.quit;
                    g_mutex_unlock(&gs.fieldbus_lock);
                
                    if (DI) {
                        memcpy(gs.hal.DI1, image.DI1, sizeof(image.DI1));
                        memcpy(gs.hal.DI2, image.DI2, sizeof(image.DI2));
                        memcpy(gs.hal.DI3, image.DI3, sizeof(image.DI3));
                    }
                    if (DO && fieldbus_set_do(modbus)) {
                        memcpy(gs.hal.DO1, image.DO1, sizeof(image.DO1));
                        memcpy(gs.hal.DO2, image.DO2, sizeof(image.DO2));
                        memcpy(gs.hal.DO3, image.DO3, sizeof(image.DO3));
                    }
                
                    /* XXX Workaround for a unknown fieldbus error that seems to reset the
                     *     analog output: if this happens, force a AO1 refresh cycle */
                    if (! DI || ! AI) {
                        workaround = TRUE;
                    }
                    AO = AO || workaround;
                
                    if (AO && fieldbus_set_ao(modbus)) {
                        g_debug("AO1 set to %d", image.AO1);
                        gs.hal.AO1 = image.AO1;
                        workaround = FALSE;
                    }
                
                    return ! quit;
                }
                
                gpointer
                fieldbus_thread(gpointer user_data)
                {
                    modbus_t *modbus;
                    gint64 period, scan, now, span, limit;
                
                    if (gs.dry_run) {
                        return NULL;
                    }
                
                    modbus = fieldbus_new();
                    if (modbus == NULL) {
                        return NULL;
                    }
                
                    period = g_settings_get_uint(gs.settings, "periodo");
                    if (period == 0) {
                        g_warning("Period set to 0: fallback to 20000");
                        period = 20000;
                    }
                
                    scan  = g_get_monotonic_time();
                    limit = period;
                
                    do {
                        g_main_context_invoke_full(NULL, G_PRIORITY_HIGH,
                                                   cycle_iteration, NULL, NULL);
                        now  = g_get_monotonic_time();
                        span = now - scan;
                        if (span > limit) {
                            limit = span;
                            g_warning("Period exceeded: %.3f ms > %.3f ms" ,
                                      limit / 1000., period / 1000.);
                        }
                        scan += period;
                        if (now < scan) {
                            g_usleep(scan - now);
                        }
                    } while (iteration(modbus));
                
                    fieldbus_free(modbus);
                    return NULL;
                }
                #endif
                

                The IO struct is just an image of the I/O status:

                typedef struct {
                    uint8_t         DI1[4];
                    uint8_t         DI2[16];
                    uint8_t         DI3[16];
                    uint8_t         DO1[4];
                    uint8_t         DO2[14];
                    uint8_t         DO3[14];
                    double          AI1;
                    uint16_t        AO1;
                } IO;
                
                K 1 Reply Last reply Reply Quote 0
                • K
                  knebb @ntd last edited by

                  @ntd Thanks a lot! I think I got it so far. The adresses of the devices you got from Neuron documentation I assume?

                  Only one question left so far. If I want to write the current state of the relay I would use (according to doc):

                  //int modbus_write_bit(modbus_t *ctx, int addr, int status);
                  errlvl=modbus_write_bit(modbus_handle, 100, status);
                  

                  I am unsure about the "status". This means I will enable or disable the bit and therefore switching the relay, right?

                  Ok, how about reading the DI 2.1?
                  Would I use the following?

                  //int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
                  errlvl=modbus_read_bits(modbus_handle, 108, nb, *dest);
                  

                  Here I have no clue for what I would need the nb... does it mean it will read nb bits? To be stored in nb*dest? I would always use the value 1, wouldn't I? If using 2 it would return the value of the second DI 2.2 in the second bit, correct?

                  Thanks a lot in advance!

                  /KNEBB

                  ntd 1 Reply Last reply Reply Quote 0
                  • K
                    knebb last edited by

                    Hi,

                    for reference purposes even though some questions are still unanswered.
                    I was able to set my relays as expected with the following code:

                    #include <modbus/modbus.h>
                    #include <errno.h>
                    #include <stdio.h>
                    #include <stdlib.h>
                    #include <string.h>
                    #include <unistd.h>
                    
                    void main()
                    {
                    
                    modbus_t *modbusglob;
                    int errlvl;
                    
                    void fieldbus_free(modbus_t *modbus)
                    {
                            if (modbus != NULL) {
                            modbus_close(modbus);
                            modbus_free(modbus);
                        }
                      return;
                    }
                    
                    modbus_t *fieldbus_new(void)
                    {
                        int err;
                        modbus_t *modbus;
                        char host[15];
                        int port;
                    
                    // Need to use IP as it does not resolve DNS Names to IPs
                        strcpy(host,"127.0.0.1");
                        port   = 502;
                        modbus = modbus_new_tcp(host, port);
                    
                        if (modbus == NULL )
                        {
                             printf("modbus is NULL\n");
                            fieldbus_free(modbus);
                            exit (55);
                        }
                    
                         err = modbus_connect(modbus);
                         if (err  == -1)
                         {
                            printf("modbus_connect, %s",modbus_strerror(errno));
                            fieldbus_free(modbus);
                            exit (55);
                         };
                     
                         err=modbus_set_slave(modbus, 0);
                         if(  err == -1)
                        {
                            printf("modbus_set_slave, %s",modbus_strerror(errno));
                            fieldbus_free(modbus);
                            modbus = NULL;
                            exit (55);
                        }
                    
                        return modbus;
                    }
                    
                    modbusglob=fieldbus_new();
                    printf("ON\n");
                    // Using fixed address of 100 here for first relay (M103), relay 2.2 ist 101 and so on.
                    // Using TRUE as synonym for HIGH or ON
                    errlvl=modbus_write_bit(modbusglob, 100, TRUE);
                    // Just for demo purposes- keep the relay on for 10 seconds
                    sleep(10);
                    printf("OFF\n");
                    errlvl=modbus_write_bit(modbusglob, 100, FALSE);
                    fieldbus_free(modbusglob);
                    }
                    
                    1 Reply Last reply Reply Quote 0
                    • ntd
                      ntd @knebb last edited by

                      @knebb said in Need Some sort of Jumpstart:

                      I am unsure about the "status".

                      According to the documentation, status is the new value of the output and can be TRUE (to enable it) or FALSE (to disable it).

                      Here I have no clue for what I would need the nb... does it mean it will read nb bits? To be stored in nb*dest? I would always use the value 1, wouldn't I? If using 2 it would return the value of the second DI 2.2 in the second bit, correct?

                      Yes to all. This mimics the behavior of the underlying protocol: ModBUS has a Read coils function but does not have Read single coil. You can easily provide your own macro for that, if you really want:

                      #define read_bit(bus,addr,dst) modbus_read_bits((bus),(addr),1,(dst))
                      
                      K 1 Reply Last reply Reply Quote 1
                      • K
                        knebb @ntd last edited by knebb

                        @ntd

                        Sorry to bother you. I think I got it finally!
                        Using modbus_read_bits will read nb bits and write (according to the state) 0 or 1 into dst[0]...dst[nb].
                        I had thought when reading 4 bits it will write the first state into the first bit of dst, second into the second and so on.

                        But no, it writes the state as 0 or 1 into each of the uint8_t .

                        I was successfull in reading RO2.8 and DI2.1 together with

                        int nb,i;
                        uint8_t dst[2];
                        nb=2;
                        modbus_read_bits(modbus,107,nb,dst);
                        for (i=0;i<nb;i++)
                        {
                          if (dst[i] != 0) 
                          {
                            printf("Bit %d is %u\n",i,dst[i]);
                          } else {
                            printf("Bit %d is %u\n",i,dst[i])
                          }
                        }
                        

                        And in case RO2.8 is on an DI2.1 is off I will get:
                        Bit 0 is 1
                        Bit 1 is 0

                        Similar this would apply to modbus_read_register();

                        Holy sh**, I think I really got it now!

                        Thanks for your patience!

                        /KNEBB

                        ntd 1 Reply Last reply Reply Quote 0
                        • ntd
                          ntd @knebb last edited by ntd

                          @knebb said in Need Some sort of Jumpstart:

                          it writes the state as 0 or 1 into each of the uint8_t .

                          Or, using semantic constants, as FALSE and TRUE.

                          Directly using bits would be really slow and error prone (and a nightmare for language bindings).

                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post