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

    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 API - Evok
      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 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 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 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 API - Evok
      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 API - Evok
      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 API - Evok
      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
    • RE: Sound card support

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

      Downgrading of the kernel could work, but it is never the correct solution, since you will not receive updates.

      But this is not a downgrade: linux-image-4.9.0-9-arm64 is the default kernel of Debian Stretch. The 4.14.52 kernel is provided by your package (axon-kernel), that is by default UniPian does not use the stock Stretch kernel image. I suppose this was due for some reason and I'd like to know if I can just ignore those reasons and use the default kernel.

      Regardling pulseaudio I cannot see how it can work without the proper sound kernel module loaded. I just tried anyway (it is quite tricky because the info provided expects a running desktop environment) and the result is no card found:

      $ pactl list cards 
      $
      
      posted in Axon series
      ntd
      ntd
    • RE: Sound card support

      Hi @Martin-Kudláček,

      AFAIK the hardware is detected properly:

      # dmesg 
      [ 4789.159932] usb 5-1: new high-speed USB device number 2 using ehci-platform
      [ 4790.663103] input: Generic USB Audio as /devices/platform/soc/1c1d000.usb/usb5/5-1/5-1:1.3/0003:0BDA:4809.0001/input/input2
      [ 4790.720706] hid-generic 0003:0BDA:4809.0001: input,hiddev96,hidraw0: USB HID v1.11 Device [Generic USB Audio] on usb-1c1d000.usb-1/input3
      # lsusb 
      Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
      Bus 005 Device 003: ID 0bda:4809 Realtek Semiconductor Corp. 
      Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
      Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
      Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
      Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
      Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
      Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
      Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
      Bus 009 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
      

      My problem is the ALSA installation:

      # apt update
      ...
      # apt install alsa-base
      Reading package lists... Done
      Building dependency tree       
      Reading state information... Done
      Package alsa-base is not available, but is referred to by another package.
      This may mean that the package is missing, has been obsoleted, or
      is only available from another source
      
      E: Package 'alsa-base' has no installation candidate
      

      The system is a stock UniPian with very few customizations (created a user, added some packages, configured network and SSH and a few more)... nothing sound-related though.

      Further investigation shows the sound driver is provided by linux-image-4.9.0-9-arm64 but the active kernel is 4.14.42:

      # apt-file search snd-usb-audio
      linux-image-4.9.0-9-arm64: /lib/modules/4.9.0-9-arm64/kernel/sound/usb/snd-usb-audio.ko
      linux-image-4.9.0-9-arm64-dbg: /usr/lib/debug/lib/modules/4.9.0-9-arm64/kernel/sound/usb/snd-usb-audio.ko
      # uname -a
      Linux sicur19a 4.14.52 #37 SMP Mon Nov 5 12:29:14 CET 2018 aarch64 GNU/Linux
      

      Not sure what to do now... I would try to install and enable the stock kernel (4.9.0) but I was fearing unintended consequences. Let me know it this is not a problem.

      posted in Axon series
      ntd
      ntd
    • Sound card support

      Hi all,

      the past week I installed an USB audio card into a Neuron M103 and, after a bit of tinkering (e.g. disabling the internal audio card), it all worked fine:

      $ cat /proc/asound/cards 
       1 [Audio          ]: USB-Audio - USB Audio
                            Generic USB Audio at usb-3f980000.usb-1.3, high speed
      

      Now I need to do the same change on an Axon M205 but I discovered the sound drivers and modules seem to not be installed as they are part of the standard kernel. The /proc/asound directory does not even exist.

      posted in Axon series
      ntd
      ntd
    • RE: Internal ModBUS problem

      @ntd said in Internal ModBUS problem:

      I will update this thread when I will have some news.

      Well, I installed the new SD card a couple of months ago and received no complaints ever since, so I suppose this update solved the problem.

      @Martin-Kudláček: thanks again for your support .

      posted in Neuron Series
      ntd
      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 Neuron Series
      ntd
      ntd
    • RE: Internal ModBUS problem

      @martin-kudláček Many thanks for your reply.

      # dpkg -l | egrep "neuron|unipi|raspberrypi-kernel"
      ii  raspberrypi-kernel                 1.20180417-1                 armhf        Raspberry Pi bootloader
      

      Unfortunately I do not have the fwspi binary under /opt/unipi-bin and the one I can have (manually built from neuron-tcp-modbus-overlay, commit 8db93c8f1350) does not have the -i/--index option. This is probably due to the fact I installed the system using the (now legacy) script.

      If you think a new SD card updated to latest would likely solve my problem, I will prepare that card. I just need some time.

      Just to be on the safe side: I would follow this instructions on a bare Raspbian Stretch Lite image.

      posted in Neuron Series
      ntd
      ntd
    • Internal ModBUS problem

      Hi all,

      I am using a UniPi Neuron L203 on a quite small industrial automation machinery. The software communicates with the hardware via the internal ModBUS/TCP server listening on the 502 TCP port. I'm using a periodic cycle spawned every 20 ms (50 Hz).

      All is working fine but sometime (spanning from once per day to once per month!) the machinery got stuck. Today for the first time I have been able to perform some test while in that condition.

      1. My software suddently stops working because every call I make to libmodbus (modbus_read_registers or modbus_write_register) returns always Illegal data address.
      2. systemctl restart neurontcp.service did not solve the issue: after that I got a bunch of errors in the logs (Broken pipe).
      3. After a reboot the prolem did not vanish either: I got another bunch of errors in the logs (this time was Target device failed to respond).
      4. After a power off and power on cycle, all is working as expected.

      In particular, point 3 made me think that the problem is hardware or firmware related. Anyway I have no problems in sharing my code or whatever else needed to be able to get rid of this (really nasty!) problem. Also, any suggestion in hardening the hardware side would be appreciated.

      posted in Neuron Series
      ntd
      ntd