Best way to bridge two UARTs?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Best way to bridge two UARTs?

Jump to solution
2,040 Views
robertyork
Contributor II

I have an application where I have a K70 running MQX 4.1, with Codewarrior 10.6. On one UART, I have a console I connect to my PC. Through this I can send commands to a shell I've written. On another UART on the K70, I've connected an embedded linux computer, right into it's console. Eventually, I'll likely have my K70 build its own commands and send them into the linux console, and parse the outputs. But for now, I would like to be able to slave the one UART connected to the PC right over to the linux UART, so I can just use the same serial connection to the computer to jump into the linux console. I don't need to be able to get back out for now. Something like this pseudo-code below:

while(1)

{

     // Take chars in from uart 0 and put them into uart 1

     in0 = fgetc(p_uart0)

     fputc(in0, p_uart1)

     // Take chars from uart 1 and put them into uart 0

     in1 = fgetc(p_uart1)

     fputc(in1, p_uart0)

}

First off, lets not get caught up in semantics. This is just pseudo-code. I also don't ever need to exit at this point.

The first challenge is of course the blocking nature of fgetc(). Even when opening the file pointer as IO_SERIAL_NON_BLOCKING, it still blocks. No idea why. For the curious, or those who think fixing this may be the solution, here's what I use to initialize the pointer:

p_uart0 = fopen("ittye:", BSP_DEFAULT_IO_OPEN_MODE);

ioctl(p_uart0 , IO_IOCTL_SERIAL_SET_FLAGS, (void *)(IO_SERIAL_TRANSLATION | IO_SERIAL_NON_BLOCKING));

Another challenge is that input and output could happen entirely asynchronously. Input could come in at any time from either source, and output returned could take some indeterminate amount of time. So it's hard to have one task just piping data from one port into the other, without pausing for other tasks to watch the other port.

I basically just want it to check if it's gotten a character in a buffer from one UART, and if so, throw it in the output of the other, and vice versa. It seems like the pseudo-code should work, but (probably due to the blocking nature of serial port drivers in MQX), it won't.

If we can't get around the blocking fgetc(), another implementation is what I've done with my K70's shell; set up tasks to continually poll the UARTs, buffer characters, and let another task pull off captured data and forward it along. I thought this is what the interrupt driven UART drivers in MQX were for? To re-invent this seems, frankly, to be ridiculously complex for what I'm trying to do, and the purist in me seems there should be a way to make the something more like the pseudo-code above, work.

Any thoughts or suggestions would be appreciated.

0 Kudos
1 Solution
1,413 Views
isaacavila
NXP Employee
NXP Employee

Hi Robert,

Actually fopen function works properly, the point here is that when uart driver has been opened before, current opening configuration is not achieved. (Only first configuration is set, then, you need to use ioctl function).

In my previous code, two uart drivers are opened, the first one is the default_io_channel so it has been opened before (with default configuration) so when i tried to opened same driver for second time with IO_SERIAL_NON_BLOCKING configuration, it does not set this configuration to UART, io_open function assigns previously configured flags (settings):

_mqx_int _io_serial_int_open

   (

      /* [IN] the file handle for the device being opened */

      FILE_DEVICE_STRUCT_PTR fd_ptr,

      

      /* [IN] the remaining portion of the name of the device */

      char              *open_name_ptr,

      /* [IN] the flags to be used during operation:

      ** echo, translation, xon/xoff

      */

      char              *flags

   )

{ /* Body */

   IO_DEVICE_STRUCT_PTR            io_dev_ptr;

   IO_SERIAL_INT_DEVICE_STRUCT_PTR int_io_dev_ptr;

   _mqx_uint                       result = MQX_OK;

   _mqx_uint                       ioctl_val;

   io_dev_ptr     = fd_ptr->DEV_PTR;

   int_io_dev_ptr = (void *)io_dev_ptr->DRIVER_INIT_PTR;

  

   if (int_io_dev_ptr->COUNT) {

      /* Device is already opened */

      int_io_dev_ptr->COUNT++;

      fd_ptr->FLAGS = int_io_dev_ptr->FLAGS;

      return(result);

   } /* Endif */

So that is why I used the ioctl function to get this flags and then set the new one for IO_SERIAL_NON_BLOCKING.

This situation does not happen with second UART driver, because it is the first time that this UART is openned (if you debug your code, you will see that for second UART, flag for IO_SERIAL_NON_BLOCKING (0x10) is set after fopen function is called).

About documentation, there is no other documentation than MQX_IO_User_Guide.pdf located at <MQX_ROOT_PATH>\doc\mqx for IO drivers.

MQX's drivers are based on POSIX standard which uses fopen, fread, fwrite, fclose and ioctl functions and basically these functions should be enough to use these drivers with all their functionality, that is why documentation does not cover functions such as fstatus for example (This functions are internally created and user could make use of them if user digs further on code and understands how it is working)

Actually fgets calls fread and request to read 1 byte, then, this byte is returned. (Obviously, difference here lies fread could be used either to poll for received data (return 0 when no data is available) and also read this data (if available), that is why i decided to use fread.)

fstatus polls for received data (as you intuited) but I think it is easier to use fread instead of fstatus and fgetc combination.

Sorry for does not have documentation for these functions, but i can help you on understand how they work

Hope this can help you,

Best Regards,

Isaac

-----------------------------------------------------------------------------------------------------------------------

Note: If this post answers your question, please click the Correct Answer button. Thank you!

-----------------------------------------------------------------------------------------------------------------------

View solution in original post

0 Kudos
11 Replies
1,413 Views
robertyork
Contributor II

Okay, I have another thought on how this could be done. It's more complex, but not too terrible. Is there a way I can just check to see if there's a character available, before I call fgetc? I could put in a 'monitor UARTs' task, and I could get away with something like:

     while(isChar(p_uart0))

          doSomething(uart0, fgetc(p_uart0));

     while(isChar(p_uart1))

          doSomething(uart1, fgetc(p_uart1));

     ... etc..

Does MQX support some sort of 'is there a waiting character in the buffer' command? I've not found one in the past. Perhaps MQX has something that does all of this internally already, and I can just arbitrarily call some sort of fread() on a whim, and get whatever characters have been pooled up in the itty: buffer?

0 Kudos
1,413 Views
isaacavila
NXP Employee
NXP Employee

Hi Robert,

Effectively, the best way to do what you want to is using UART driver with its interrupts version and I can suggest you to have two task, one for each terminal (I decided to call them windows_task and linux_task respectively).

By using UART driver in its interrupt version, you will be able to block any task that is waiting for receiving bytes and when “number of bytes to be received” have been received, then your waiting task will be put in ready status. This way, you can wait for asynchronous bytes and meanwhile you can do another process.

I do a small example about it (hope this can help you or serves you as guidance) using TWR-K60D100M (due this board has to UART module mapped to OSJTAG and TWR-SER’s RS232 connector).

OSJTAG ‘s USB connector uses UART5 (ttyf) and TWR-SER’s connector uses UART3 (ttyd) and first of all we need to enable this modules to uses their interrupt version.

  • Go to bsp_twrk60d100m and enter to User_Config folder. Select user_config.h. Here you need to set to 1 macros BSPCFG_ENABLE_ITTYD and BSPCFG_ENABLE_ITTYF and set to 0 BSPCFG_ENABLE_TTYD as well as BSPCFG_ENABLE_TTYF. (Prefix ‘I’ in ITTFx means interrupt driver):
  • enable UART interrupt drivers.jpgBSP libraries define TTYF as its default console channel, and due we have been disable it, we need to change ITTFY to be this default console channel. Go to bsp_twrk60d100m and then to BSP_Files folder. Here you can find twrk60d100m.h file that defines BSP_DEFAULT_IO_CHANNEL: We replace this NULL definition as shows below:

enable default io channel.jpg

  • Then recompile MQX libraries.

After this, our UART3 (ittyd:) and UART5 (ittyf:) are configured to be used by interrupts, so every time we make a read request this task will be blocked until bytes are received.

After creating a new MQX 4.1 project, I open these 2 drivers:

printf("Opening UART1 port \"%s\" .......",UART1_PORT_NAME);

    g_uart1_pointer = fopen(UART1_PORT_NAME,NULL);

    if (NULL == g_uart1_pointer) {

        printf("[FAIL]\n");

        _task_block();

    } else {

        printf("[DONE]\n");

    }

      

    printf("Opening UART2 port \"%s\" .......",UART2_PORT_NAME);

    g_uart2_pointer = fopen(UART2_PORT_NAME,NULL);

    if (NULL == g_uart2_pointer) {

        printf("[FAIL]\n");

        _task_block();

    } else {

        printf("[DONE]\n");

    }

Then I created two task that are waiting to read one byte from every UART port:

void Windows_bridge(uint32_t initial_data) {

    uint32_t read_bytes = 0, written_bytes = 0;

    uint8_t received_bytes = 0;

    /* Unused variable */

    (void)initial_data;

      

    while (1) {

        read_bytes = fread((void *)&received_bytes,1,1,g_uart1_pointer);

        written_bytes = fwrite((void *)&received_bytes, 1, read_bytes,

g_uart2_pointer);

        if (written_bytes != read_bytes) {

            /* Error handling */

        }

read_bytes = 0;

    }

}

void Linux_bridge(uint32_t initial_data) {

    uint32_t read_bytes = 0, written_bytes = 0;

    uint8_t received_bytes = 0;

    /* Unused variable */

    (void)initial_data;

      

    while (1) {

        read_bytes = fread((void *)&received_bytes,1,1,g_uart2_pointer);

        written_bytes = fwrite((void *)&received_bytes, 1, read_bytes,

                                                              g_uart1_pointer);

        if (written_bytes != read_bytes) {

            /* Error handling */

        }

        read_bytes = 0;

    }

}

Once character has been received, it sends to the opposite UART port.

This solutions seems to response well to both terminals and will no wait until one port has received something to start polling the other port.

I attach this project (and added extra logic in case you will need to wait for a string (finished by ‘\r’ or ‘\n’ character)). I test it and seems to work as you want to.

both terminals.jpg

(Note: Terminal 1 makes echo due it uses default io channel (ittfy:), but both tasks are making loopback to each other as you wanted to)

I hope this can help you.

Best Regards,

Isaac

-----------------------------------------------------------------------------------------------------------------------

Note: If this post answers your question, please click the Correct Answer button. Thank you!

-------------------------------------------------------------------------------------------------------------------------------------

0 Kudos
1,413 Views
robertyork
Contributor II

Thanks for the response, Isaac. However, I was trying to avoid doing the multiple task approach to solving this.

As you can see from my example in the original post above, where I create the file pointer, I've been using the interrupt version of the serial port. So up to the point where you create two tasks, I've been doing the same thing. My questions are more that it seems the entire point of having the interrupt version of the UART driver is that the driver is responding to interrupts and buffering the characters it receives. This is why it has a buffer size in the user_config.h. Here's an example from mine, where I increased the buffer size due to some long strings I was receiving:

     #define BSPCFG_ENABLE_ITTYA                      1

     #define BSPCFG_SCI0_QUEUE_SIZE                 256

While I've yet to find any documentation on this, it strongly implies it's buffering the UART for me. This is great, but it's hard to tell how I can just do a quick check to see how much is sitting in that buffer. This would eliminate the need for extra tasks at all, since in effect, the interrupt based UART driver is doing that checking for me.

One of my concerns with the example given, is that I'm trying to be sure that when you do an fread() and there's no character waiting, it will halt until a character is available, pass execution on to another task, or just return out. I would rather it return out so that task can continue doing other things. But I need to know one way or the other. I can't seem to find this specified anywhere, and I've ran into situations where it halted that task, and any task lower priority was simply stuck. It wasn't yielding until a valid character came around. With several UARTs to pay attention to, I can't risk one of them blocking reading of the others. It was a while back, so it was possible that at the time I was not using the interrupt version of the UART driver. And this blocking concern aside, I can't afford to be making a separate task for every UART and other port, control, and input I have on the hardware, in addition to all the internal state machines going on. I'm already over 10 tasks, without doing this.

In digging through some old code where I was trying to make my own shell, I stumbled across some commented out lines that looked interesting. Specifically, an fstatus() call. It seems to behave in the way I'm looking for. Using if(fstatus(stdin)), it seems to evaluate as true when there's a character waiting, false when not. I have no idea where I found this originally. But, it means I can simply do this:

     while(1)

     {

          if(fstatus(stdin))

               fputc(fgetc(stdin), p_linux);

          if(fstatus(p_linux))

               fputc(fgetc(p_linux), stdout);

     }

This loops forever, checks to see if there's a waiting character in stdin, if so, it reads it, and puts it into the p_linux file pointer. And then, vice versa. This is simple and to the point. I like it, it seems to work, and I could run on and do other things before looping back to check again. But it's relying on code that I can't find any documentation on; and that makes me a little nervous. I need a better understanding if this is good code, or am I going to run into problems down the line doing it this way? Can I use something similar (or some other usage of ioctrl) to read off every character waiting in the UART's interrupt buffer at once? Maybe to know how many characters are waiting? fstatus() may actually return a number of chars waiting, I don't know. I'd have to experiment, but experimentation isn't a good replacement for actual documentation.

Again, I've not been able to find much of the functionality of this UART driver documented, and digging through source has led me down a rabbit hole of macroed functions and somewhat cryptic calls to ioctrl (which I can't find much meaningful documentation for, either). I've given up on trying to figure it out. In several cases, I've been looking up generic posix documentation to see how that function works in linux, but that's by no means a guarantee that it works that way in MQX. Maybe I'm looking in the wrong places, but I've dug through the documents provided under the MQX docs folder. When people rely on my code to work every time, simply using a function on the premise that 'It seems to work like I want' isn't good enough. Yes, one could argue that trusting documentation is accurate and up to date isn't safe either, but you have to start somewhere.

If someone could shed some light on whether this fstatus() is a good idea or not, and how I could tell how many characters are waiting, and the behavior of fread() if there are no characters waiting, that would be helpful. Also, is there a big chunk of documentation I'm just missing? It seems like I've got to be overlooking some key document.

0 Kudos
1,413 Views
isaacavila
NXP Employee
NXP Employee

Hi Robert,

I could not find such document but I have enabled NON_BLOCKING functionality on UART drivers and implement a while forever loop that polls for data on both UART modules.

fread and fwrite functions return number of data sent/written so when polling for incoming data, fread function returns 0 when no data is available, otherwise, return 1 (in my case, I requested to read just 1 byte) this way I can send this data to opposite UART.

In your first comment you tried to enable this functionality using:

ioctl(p_uart0 , IO_IOCTL_SERIAL_SET_FLAGS, (void *)(IO_SERIAL_TRANSLATION | IO_SERIAL_NON_BLOCKING));

but for this ioctl function, parameters should be passed throug pointers, for example:

char flags = IO_SERIAL_NON_BLOCKING;

ioctl(g_uart2_pointer, IO_IOCTL_SERIAL_GET_FLAGS, (void *)&flags)

due in _io_serial_int_ioctl function, this parameters is obtained from a pointer (serl_int.c line 571):

case IO_IOCTL_SERIAL_SET_FLAGS:

         int_io_dev_ptr->FLAGS = *((_mqx_uint_ptr)param_ptr);

         fd_ptr->FLAGS = *((_mqx_uint_ptr)param_ptr);

This is not needed when openning UART port for first time:

g_uart2_pointer = fopen(UART2_PORT_NAME,(char *)IO_SERIAL_NON_BLOCKING);

Once, NON_BLOCKING functionality is enabled, you can implement your polling system without blocking this task. Here is my code for this unique task:

void UART_Bridge_task(uint32_t initial_data) {

    /* UART Pointer*/

    MQX_FILE_PTR g_uart1_pointer = NULL;

    MQX_FILE_PTR g_uart2_pointer = NULL;

    uint32_t read_bytes = 0, written_bytes = 0;

    uint8_t received_bytes = 0;

    char flags;

    printf("Opening UART1 port \"%s\" .......",UART1_PORT_NAME);

    /* Try to open this port with NON_BLOCKING functionality */

    g_uart1_pointer = fopen(UART1_PORT_NAME, (char *)IO_SERIAL_NON_BLOCKING);

    if (NULL == g_uart1_pointer) {

        printf("[FAIL]\n");

        _task_block();

    } else {

        printf("[DONE]\n");

    }

    /* Get Driver's flags*/

    if (MQX_OK != ioctl(g_uart1_pointer, IO_IOCTL_SERIAL_GET_FLAGS, (void *)&flags)) {

        printf("Error getting UART flags\n");

    } else {

        /* Verify if NON_BLOCKING functionality is enabled */

        if (0 == (flags & IO_SERIAL_NON_BLOCKING)) {

            /* Add NON_BLOCKING functionality to current configuration */

            flags |= IO_SERIAL_NON_BLOCKING;

            if (MQX_OK != ioctl(g_uart1_pointer, IO_IOCTL_SERIAL_SET_FLAGS, (void *)&flags)) {

                printf("Error setting flags\n");

            }

        }

    }

    printf("Opening UART2 port \"%s\" .......",UART2_PORT_NAME);

    g_uart2_pointer = fopen(UART2_PORT_NAME,(char *)IO_SERIAL_NON_BLOCKING);

    if (NULL == g_uart2_pointer) {

       printf("[FAIL]\n");

       _task_block();

    } else {

       printf("[DONE]\n");

    }

    if (MQX_OK != ioctl(g_uart2_pointer, IO_IOCTL_SERIAL_GET_FLAGS, (void *)&flags)) {

        printf("Error getting UART flags\n");

    } else {

        if (0 == (flags & IO_SERIAL_NON_BLOCKING)) {

            flags |= IO_SERIAL_NON_BLOCKING;

            if (MQX_OK != ioctl(g_uart2_pointer, IO_IOCTL_SERIAL_SET_FLAGS, (void *)&flags)) {

                printf("Error setting flags\n");

            }

        }

    }

    while (1) {

        read_bytes = fread((void *)&received_bytes,1,1,g_uart1_pointer);

        if (read_bytes != 0) {

            written_bytes = fwrite((void *)&received_bytes, 1, read_bytes, g_uart2_pointer);

            if (written_bytes != read_bytes) {

                /* Error handling */

            }

        }

        read_bytes = fread((void *)&received_bytes,1,1,g_uart2_pointer);

        if (read_bytes != 0) {

            written_bytes = fwrite((void *)&received_bytes, 1, read_bytes, g_uart1_pointer);

            if (written_bytes != read_bytes) {

                /* Error handling */

            }

        }

    }

}

Could you try this program and see if this fits your needs?

I hope this can helps you,

Best Regards,

Isaac

-----------------------------------------------------------------------------------------------------------------------

Note: If this post answers your question, please click the Correct Answer button. Thank you!

-----------------------------------------------------------------------------------------------------------------------

0 Kudos
1,413 Views
robertyork
Contributor II

So, I've been struggling through trying to get this working. Here's what I've come up with.

     p_console = fopen(SHELL_SERIAL_PORT, (char *)(IO_SERIAL_TRANSLATION));

This line above, by itself, should work fine according to everything I've read. However, it doesn't. It blocks, it echos, it does all sorts of things that it shouldn't. However, if I do the following:

     char uart_flags = (IO_SERIAL_TRANSLATION);

     p_console = fopen(SHELL_SERIAL_PORT, (char *)(IO_SERIAL_TRANSLATION));

     ioctl(p_console, IO_IOCTL_SERIAL_SET_FLAGS, (void *)&uart_flags);

So all I've done is to again set the same flags. And now, with just that change, it works exactly as it should. Still blocks, but no longer echos. I can insert  '| IO_SERIAL_NON_BLOCKING' in as well, and it no longer blocks. But basically, the fopen command isn't working properly at all, as I understand it should work.

This still leaves the question open as to whether or not fstatus() will behave like it seems to. Also, is the fread((void *)&received_bytes,1,1,g_uart1_pointer); you use any different than a fgetc()? It would be real nice if I could find this stuff in a document on fio in MQX.

Thank you for the pointers on making the ioctl() work. Still not sure why fopen() doesn't seem to be working like it should. It seems strange that these sorts of basic questions about how fio functions work in MQX requires someone from Freescale to respond. This should be documented somewhere. If it is, someone please point me to it.

0 Kudos
1,414 Views
isaacavila
NXP Employee
NXP Employee

Hi Robert,

Actually fopen function works properly, the point here is that when uart driver has been opened before, current opening configuration is not achieved. (Only first configuration is set, then, you need to use ioctl function).

In my previous code, two uart drivers are opened, the first one is the default_io_channel so it has been opened before (with default configuration) so when i tried to opened same driver for second time with IO_SERIAL_NON_BLOCKING configuration, it does not set this configuration to UART, io_open function assigns previously configured flags (settings):

_mqx_int _io_serial_int_open

   (

      /* [IN] the file handle for the device being opened */

      FILE_DEVICE_STRUCT_PTR fd_ptr,

      

      /* [IN] the remaining portion of the name of the device */

      char              *open_name_ptr,

      /* [IN] the flags to be used during operation:

      ** echo, translation, xon/xoff

      */

      char              *flags

   )

{ /* Body */

   IO_DEVICE_STRUCT_PTR            io_dev_ptr;

   IO_SERIAL_INT_DEVICE_STRUCT_PTR int_io_dev_ptr;

   _mqx_uint                       result = MQX_OK;

   _mqx_uint                       ioctl_val;

   io_dev_ptr     = fd_ptr->DEV_PTR;

   int_io_dev_ptr = (void *)io_dev_ptr->DRIVER_INIT_PTR;

  

   if (int_io_dev_ptr->COUNT) {

      /* Device is already opened */

      int_io_dev_ptr->COUNT++;

      fd_ptr->FLAGS = int_io_dev_ptr->FLAGS;

      return(result);

   } /* Endif */

So that is why I used the ioctl function to get this flags and then set the new one for IO_SERIAL_NON_BLOCKING.

This situation does not happen with second UART driver, because it is the first time that this UART is openned (if you debug your code, you will see that for second UART, flag for IO_SERIAL_NON_BLOCKING (0x10) is set after fopen function is called).

About documentation, there is no other documentation than MQX_IO_User_Guide.pdf located at <MQX_ROOT_PATH>\doc\mqx for IO drivers.

MQX's drivers are based on POSIX standard which uses fopen, fread, fwrite, fclose and ioctl functions and basically these functions should be enough to use these drivers with all their functionality, that is why documentation does not cover functions such as fstatus for example (This functions are internally created and user could make use of them if user digs further on code and understands how it is working)

Actually fgets calls fread and request to read 1 byte, then, this byte is returned. (Obviously, difference here lies fread could be used either to poll for received data (return 0 when no data is available) and also read this data (if available), that is why i decided to use fread.)

fstatus polls for received data (as you intuited) but I think it is easier to use fread instead of fstatus and fgetc combination.

Sorry for does not have documentation for these functions, but i can help you on understand how they work

Hope this can help you,

Best Regards,

Isaac

-----------------------------------------------------------------------------------------------------------------------

Note: If this post answers your question, please click the Correct Answer button. Thank you!

-----------------------------------------------------------------------------------------------------------------------

0 Kudos
1,413 Views
robertyork
Contributor II

Actually, this brings up another question. Since this port has already been opened somewhere else in MQX, me calling: p_console = fopen(SHELL_SERIAL_PORT, (char *)(IO_SERIAL_TRANSLATION)); probably isn't the best way to get a pointer to it. Is there a better way to get a pointer to SHELL_SERIAL_PORT?

0 Kudos
1,413 Views
isaacavila
NXP Employee
NXP Employee

Hi Robert,

It was opened to use printf and scanf functions (IO_DEFAULT_CHANNEL for MQX) and fopen is the right way to get this pointer.

If you need to use another UART port as IO_DEFAULT_CHANNEL you can define this twrk70f120m.h file.

enable default io channel.jpg

I hope this can help you,

Best Regards,

Isaac

0 Kudos
1,413 Views
robertyork
Contributor II

Thanks, good to know. I've already had to reassign the default IO channel (before I learned how to use fopen()) to test out all the ports. So I've gotten good with changing that part.

Thanks again!

0 Kudos
1,413 Views
robertyork
Contributor II

While I understand what you mean, to my knowledge, these ports have not been opened before. It's likely that MQX has already opened it somewhere, since the port I'm having troubles with is the port used for stdio. This would explain why I'm not seeing this behavior on other UARTs.

Thank you for the help in clarifying this. I know you're not in charge of documentation, but I think situations like this would be handled a lot better if they functions were just documented. Things like the fact that my stdio port is being opened automatically, and some reference to what flags are being set on it would be nice.

In the time you've spent helping me, someone could have spent the same amount of time putting these details into the MQX_IO_User_Guide.pdf, and then it would all be in one place, for everyone. If possible, please pass this along to someone who can update documentation so that your time isn't being spent answering trivial issues like this one.

Thank you again for the help. It just pains me that the answer was so simple, yet I couldn't find it on my own.

0 Kudos
1,413 Views
isaacavila
NXP Employee
NXP Employee

Hi Robert,

You are welcomed and I am totally agree with you.

I will inform MQX's documentation team to include some points like these for future releases.

Best Regards,

Isaac

0 Kudos