• Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    1. Home
    2. ntd
    • Profile
    • Following 0
    • Followers 0
    • Topics 2
    • Posts 18
    • Best 4
    • Controversial 0
    • Groups 0

    ntd

    @ntd

    4
    Reputation
    432
    Profile views
    18
    Posts
    0
    Followers
    0
    Following
    Joined Last Online
    Website www.entidi.com

    ntd Unfollow Follow

    Best posts made by ntd

    • RE: Internal ModBUS problem

      @martin-kudláček Ok, I just finished to prepare the SD card based on latest UniPian. I will update this thread when I will have some news. Thank you for the support so far.

      posted in UniPi Neuron Series
      ntd
      ntd
    • RE: Need Some sort of Jumpstart

      @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))
      
      posted in Official EVOK API
      ntd
      ntd
    • RE: How to Read M103 Modbus Doc?

      @knebb TL/DR: just use uint8_t. This is the right thing to do.

      There is no differences (because of integer promotion) only when you use that variable in an expression. Incidentally, and for different reasons [1], reading a single bit will work with whatever 0-initialized integer you throw at modbus_read_bits, although conceptually wrong. But I can assure you that, when reading multiple channels, using anything but uint8_t will give you wrong results.

      [1] Raspberry Pi is little-endian. Setting the first byte of any 0-initialized uint* variable to a specific value (e.g., TRUE), makes that very same variable initialized to that value, regardless of its type.

      Here is an example that hopefully will shed some light:

      #include <assert.h>
      #include <stdint.h>
      #include <string.h>
      
      #define TRUE 12
      
      int main()
      {
          // Using an union to be able to set the first byte of everything
          // with only one instruction (m.byte = ...)
          union {
              uint8_t byte;
              uint16_t word;
              uint32_t dword;
              uint64_t qword;
          } m;
      
          // Let's set the first byte of 0 initialized memory
          memset(&m, 0, sizeof m);
          m.byte = TRUE;
      
          assert(m.byte == TRUE);  // Ok
          assert(m.word == TRUE);  // Ok on little-endian machines
          assert(m.dword == TRUE); // Ok on little-endian machines
          assert(m.qword == TRUE); // Ok on little-endian machines
      
          // Now let's try with random initialized memory
          memset(&m, 3, sizeof m);
          m.byte = TRUE;
      
          assert(m.byte == TRUE);  // Ok
          assert(m.word == TRUE);  // Error!
          assert(m.dword == TRUE); // Error!
          assert(m.qword == TRUE); // Error!
      
          return 0;
      }
      
      posted in UniPi Neuron Series
      ntd
      ntd
    • RE: How to Read M103 Modbus Doc?

      @knebb said:

      Would both work?

      None of them will. You should really read a ModBUS introduction... I think the wikipedia page should suffice.

      The counter is an input register, not a bit, so:

      union {
          uint16_t word[2];
          uint32_t dword;
      } counter;
      
      if (modbus_read_registers(bus, 103, 2, counter.word) != 2) {
          printf("Error\n");
      } else {
          printf("DI2.1 counter is %u\n", counter.dword);
      }
      

      Not sure which is the less and the most significant word, so if the above gives wrong results just swap counter.word[0] and counter.word[1].

      posted in UniPi Neuron Series
      ntd
      ntd

    Latest posts made by ntd

    • RE: Sound card support

      I managed to get what I need by compiling a new (hopefully) compatible set of modules and installing by hands only the needed ones. Here are the steps I followed, executed directly on the AXON as root:

      v=$(uname -r)
      
      cd /usr/src
      wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-$v.tar.xz
      tar xf linux-$v.tar.xz
      cd linux-$v
      gzip -dc /proc/config.gz > .config
      apt install libncurses5-dev bc
      
      # Customize the kernel to your needs. In my case I enabled the
      # snd-usb-audio module that, in cascade, enabled other sound stuff.
      make menuconfig
      
      # Build and install *only* the new modules.
      make modules
      mkdir -p /lib/modules/$v/kernel/sound/usb
      cp sound/usb/*.ko /lib/modules/$v/kernel/sound/usb/
      cp sound/core/snd-*.ko /lib/modules/$v/kernel/sound/core/
      depmod
      
      # Not strictly needed, but forbidding future axon-kernel upgrades
      # could avoid potential hard-to-debug problems.
      apt-mark hold axon-kernel
      
      posted in Axon series
      ntd
      ntd
    • RE: Need Some sort of Jumpstart

      @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).

      posted in Official EVOK API
      ntd
      ntd
    • RE: How to Read M103 Modbus Doc?

      @knebb said:

      Would both work?

      None of them will. You should really read a ModBUS introduction... I think the wikipedia page should suffice.

      The counter is an input register, not a bit, so:

      union {
          uint16_t word[2];
          uint32_t dword;
      } counter;
      
      if (modbus_read_registers(bus, 103, 2, counter.word) != 2) {
          printf("Error\n");
      } else {
          printf("DI2.1 counter is %u\n", counter.dword);
      }
      

      Not sure which is the less and the most significant word, so if the above gives wrong results just swap counter.word[0] and counter.word[1].

      posted in UniPi Neuron Series
      ntd
      ntd
    • RE: How to Read M103 Modbus Doc?

      @knebb TL/DR: just use uint8_t. This is the right thing to do.

      There is no differences (because of integer promotion) only when you use that variable in an expression. Incidentally, and for different reasons [1], reading a single bit will work with whatever 0-initialized integer you throw at modbus_read_bits, although conceptually wrong. But I can assure you that, when reading multiple channels, using anything but uint8_t will give you wrong results.

      [1] Raspberry Pi is little-endian. Setting the first byte of any 0-initialized uint* variable to a specific value (e.g., TRUE), makes that very same variable initialized to that value, regardless of its type.

      Here is an example that hopefully will shed some light:

      #include <assert.h>
      #include <stdint.h>
      #include <string.h>
      
      #define TRUE 12
      
      int main()
      {
          // Using an union to be able to set the first byte of everything
          // with only one instruction (m.byte = ...)
          union {
              uint8_t byte;
              uint16_t word;
              uint32_t dword;
              uint64_t qword;
          } m;
      
          // Let's set the first byte of 0 initialized memory
          memset(&m, 0, sizeof m);
          m.byte = TRUE;
      
          assert(m.byte == TRUE);  // Ok
          assert(m.word == TRUE);  // Ok on little-endian machines
          assert(m.dword == TRUE); // Ok on little-endian machines
          assert(m.qword == TRUE); // Ok on little-endian machines
      
          // Now let's try with random initialized memory
          memset(&m, 3, sizeof m);
          m.byte = TRUE;
      
          assert(m.byte == TRUE);  // Ok
          assert(m.word == TRUE);  // Error!
          assert(m.dword == TRUE); // Error!
          assert(m.qword == TRUE); // Error!
      
          return 0;
      }
      
      posted in UniPi Neuron Series
      ntd
      ntd
    • RE: How to Read M103 Modbus Doc?

      @knebb said in How to Read M103 Modbus Doc?:

      *dest is supposed to be uint16_t.

      No, it is an uint8_t array. Every element of the array contains the value of a single digital channel, set to TRUE (that is, IIRC, 1) or FALSE (that is surely 0).

      Here is my untested snippet:

      // Reading one channel
      uint8_t my_digital;
      modbus_read_bits(bus, 108, 1, &my_digital);
      if (my_digital) {
          ...
      }
      // Reading multiple channels
      uint8_t my_digitals[2];
      modbus_read_bits(bus, 108, 2, my_digitals);
      if (my_digitals[0]) {
          ...
      }
      
      posted in UniPi Neuron Series
      ntd
      ntd
    • RE: Need Some sort of Jumpstart

      @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))
      
      posted in Official EVOK API
      ntd
      ntd
    • RE: Need Some sort of Jumpstart

      @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;
      
      posted in Official EVOK API
      ntd
      ntd
    • RE: Sound card support

      I would be really interested in the configuration used to build the axon-kernel, so I could try to rebuild the kernel and include the modules I need. Is there a place where I can get it?

      posted in Axon series
      ntd
      ntd
    • RE: Need Some sort of Jumpstart

      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.

      posted in Official EVOK API
      ntd
      ntd
    • RE: Sound card support

      @martin-kudláček said in Sound card support:

      https://askubuntu.com/questions/28176/how-do-i-run-pulseaudio-in-a-headless-server-installation

      Yes, this is exactly what I followed before writing the previous post. The only sink listed is a dummy output that I suppose you see too:

      $ pacmd list-sinks
      1 sink(s) available.
        * index: 0
      	name: <auto_null>
      	driver: <module-null-sink.c>
              ...
      	properties:
      		device.description = "Dummy Output"
      		device.class = "abstract"
      		device.icon_name = "audio-card"
      

      As stated above I'm pretty sure this is because of the sound kernel modules not loaded.

      An alternative approach would be to install the 4.9.0 image without enabling it and try to force-load the needed sound modules with the current kernel. Do you think it is worth a try?

      Don't be mistaken: I really appreciate your help so far and I know this could be considered a kind of corner case.

      posted in Axon series
      ntd
      ntd