Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - garysdickinson

Pages: [1] 2 3 ... 33
Technical support / Re: Nano10 RS-485 Modbus termination
« on: February 26, 2022, 03:04:22 PM »
Very good work at sorting the RS-485 communications.  This is a very old communication scheme and you have done some old-school trouble shooting.

The issue with the need to bias the RS-485 lines makes perfect sense. I have been lucky in that the external devices that I have worked with all used modern RS-485 Transceivers that didn’t require the biasing circuitry.

Gary d

Technical support / Re: Nano10 RS-485 Modbus termination
« on: February 22, 2022, 03:09:19 PM »
I have connected many Modbus devices to Nano-10s. 

If you are working with 6' of cabling at 9600 BAUD there is no need for termination resistors. Trust me on this.

I wouldn’t mess with the cable pull up/down resistors either.  The RS485 transceivers on the nano-10 are designed to bias the RS-485 bus so that the signal wires will not drift, when no device is transmitting, to a voltage that would be mistaken for the start of a character.  This is probably not your problem, either.

OK. What should you try?  I’ll give you a hint.  RS-485 is NOT a 2 wire interface.  All of the RS-485 transceivers must be referenced to a common signal ground to operate reliably.

I have worked with several devices that use an electrically isolated RS-485 interface. Many of these devices are 3 phase motor controllers sometimes called "inverters".  The RS-485 port on these devices are electrically isolated from both the high voltages as well as the 24V DC logic supply.

If your device(s) have isolated RS-485 ports, then you MUST connect the signal ground of your isolated port(s) to the “-“ power supply input of the Nano-10.  This is the third wire for RS-485.

If you are using a USB/RS-485 dongle there is a good chance that the RS-485 port will behave as if it is isolated.  I always connect the signal ground on the dongle to the Nano-10 power supply "-“. If your dongle does not have a signal ground connection then you should buy one that has this connection.


Gary Dickinson

Technical support / Re: Monitoring of Com 1 and Com 3 for Ascii
« on: November 18, 2021, 12:42:35 PM »

I don't have an answer to why long messages are not accepted by the target device.

I assume that the target device is not a PLC.  So you don't have much visibility into what the device is "seeing" on it's end of the serial cable.

Since all of your print statements are are all on the same line of TBASIC and the total number of characters is less than the PLC transmit buffer size of 256 then my usual idea about gaps in the data stream probably do not apply.

OK so now I am down to other reasons that things might go south...

BAUD rate might be a problem.  If the target devices firmware is dumb, has a very short buffer or is just badly implemented firmware, then you may be sending characters faster that the target device can handle.  Drop the BAUD rate to something like 300 (about 30 characters per second).  If this fixes the problem then you have learned something about the target device. 

I run 9600 for my Modbus communications because it is fast enough for my applications. It is slow enough to not need termination resistors even with long cables.

If I  suspect the PLC programming is messing up the transmitted string, then i monitor the serial line to see exactly what is being transmitted.

COMM3 is RS485, you will need  a RS485 to USB dongle for your PC.  I use a shareware program named "Tera Term" to monitor the serial port that the USB dongle has been assigned by your PC.  This will allow you to both observe and log the data that is being transmitted from the PLC's COMM 3 port.

If you have one of the "smart" sampling oscilloscope most can decode ASCII serial data.

Good luck,

Gary D

Technical support / Re: Monitoring of Com 1 and Com 3 for Ascii
« on: November 13, 2021, 09:19:53 AM »

On the receiving end, I would suggest that you not bother with the INPUT$(ch) function. The documentation leaves out a bit of its behavior:

To return a string obtained from communication port # ch.
ch must be a numeric constant between 1 and 8. The actual target hardware determines the valid port #. This function returns an empty string if there is no valid string waiting at serial port in order not to hold up the CPU.

These are some of the things not documented:

The maximum number of characters returned is limited by the string variable.  Most of TRI's PLC’s strings are limited to 70 characters.  EEP$ strings are usually 40 characters max.  If the message is 80 characters in length, you will have to make multiple calls to read the message.

The actual function does not return the end of line character.  So if your protocol uses an end of line character you will not see it.  If your protocol uses a pair of characters to mark end of line, CR and LF, it will discard one of the pair of characters and the other will be returned in the string.  I don’t remember what the function considers end of line.

If you receive a 0 length message, then the receive buffer is empty.  This does not always indicate that you have reached the end of message. If you wait a few milliseconds you may find that more characters have arrived.

Because of the string variable length issue, the loss of the end of line character and the detection that the receive buffer is empty, I find that INPUT$() is not worth the effort.

I use the function INCOMM(ch)  as it has fewer limitations. And, since you see all characters it is easy to detect the end of message.  It is an easier function to determine if the receive buffer is empty as it returns the value -1 on this condition.

Gary d

Technical support / Re: Monitoring of Com 1 and Com 3 for Ascii
« on: November 12, 2021, 11:42:13 AM »

If your application is just to act as a simple store and forward device and does not interpret or validate the received messages, then you just need to check the state of the receive buffer on each scan of the ladder logic and copy any and all received data bytes to the transmit port.

I am assuming that your issue is non-trivial.

The simple answer about communication protocols that require > 256 characters to be transmitted or received is "maybe".

    Let's talk about sending long messages:
    • There is a 256 byte output buffer for each communication port. Characters are placed in the buffer as the result of OUTCOMM and PRINT statements. Characters are removed from this buffer by low-level PLC firmware.  This firmware moves the data into a second buffer of 16 to 32 characters that are managed by the the UART. The UART is responsible for converting the data into a series of bits and shipping them out as a serial data stream.
    • If you your outbound message is greater than 256 characters you will have to divide the message into chunks less than 256 to fit into the output buffer.
    • There is no mechanism in TBASIC to determine the state of the output buffer.  The only way to prevent corruption of the buffer is to pace the OUPUTCOM or PRINT statements.  If you communicating at 9600 BAUD each character takes 1.042 ms to transmit. If each of your PRINT statements sends 256 characters you will have to wait at least 267 ms between PRINT statements to prevent corruption of the output buffer.  You are hoping that the output buffer is empty before the next PRINT statement.

      Let's talk about receiving long messages:
      • There is a 256 byte receive buffer for each communication port. Received characters are placed into the 256 byte buffer by means of low-level PLC firmware.
      • The INCOMM(ch) command returns a value of -1 if the receive buffer is empty. I would suggest reading received data with this command and buffer it in DM[].
      • It is the responsibility of your TBASIC code to pull characters out of the receive buffer faster than they are received.  Failure to do so will result in corruption of the input buffer. If you are at 9600 BAUD then you must be able to receive 256 characters in 267 ms.
      • I would suggest checking for received characters on each scan of the ladder logic and reading all characters from the receive buffer if any are detected. Remember that at 9600 BAUD that characters will arrive every 0.001 seconds. You will have to buffer the received characters using DM[] or EPP (if you have installed the RTC 256K module).

        Let's talk about your communication protocol:
        • How tolerant is it with gaps in the messages that you will be transmitting?
        • If you are sending messages in chunks, then there will be gaps in time when no characters are transmitted. If you protocol has some sort of timeout mechanism, you will have to ensure that the transmit gaps are short enough so that the receiving end does not timeout and long enough that to prevent corruption of the PLC output buffer.
        • Modbus RTU expects a message to be sent without gaps between characters.  A gap is interpreted as an end of message. In order to not have a gap in output Modbus RTU frames, I buffer everything, compute the CRC and send the entire frame in one tight loop.  I limit the message size to less than 256 characters to avoid any gaps in the transmission.

        Gary D[/list][/list][/list]

        Technical support / Re: Monitoring of Com 1 and Com 3 for Ascii
        « on: October 28, 2021, 02:14:39 PM »

        In regards to the meaning of the C programming construct of "\n" or newline, this is an operating system dependent behavior.  In UNIX \n translated to a linefeed.  In other operating systems it translated to a carriage return.  In CPM it was 2 characters a carriage return and a linefeed. So I have no clue what your device expects as a line termination sequence.

        I think your example would output "PASS<CR><SUB>".  The 26 value used by the outcomm statement got you the  < SUB > character.  I haven’t seen a communication protocol use this character.

        The Print #n statement prints 0 or more arguments separated by a semicolons, ";". if there is an ";" at the end of the statement, then print #n will not append an end of line character to the output characters.

        If there is no  ";" at the end of the statement, then the print #n statement will append an ASCII carriage return, CR or 0x0d, to the output.


        print #1 "hello";                      ---> sends "hello" only
        print #1 "hello"                       ---> sends "hello" and an ASCII CR
        Print #1                                 ---> sends ASCII CR only
        Print #1 "hello";"\0a";            ---> sends "hello" and an ASCII LF (linefeed) with a hex value of 0x0a
        print #1 "hello";"\0d\0a";       ---> sends "hello and an ASCII CR followed by an ASCII LF

        So you  are not to limited in controlling the end of line character(s).

        If you have to send an ASCII NUL or 0x00 then you need to use OUTCOMM.

        Gary D

        Technical support / Re: Monitoring of Com 1 and Com 3 for Ascii
        « on: October 25, 2021, 04:09:59 PM »
        The short answer is yes. It is possible to use the FMD PLC to handle serial communications on both serial ports.

        The TBASIC has a full set of statements and functions to support user written communication protocol over the serial ports.

        To provide a better answer, you would need to provide a bit more detail about the communication protocol that you intend to use.

        Gary Dickinson

        Technical support / Re: I do not get interruptions with FMD88HMI
        « on: May 31, 2021, 04:08:41 PM »
        I posted PLC application that provides support to test a physical stepper motor. The code uses a single limit switch that is used to find the home position for the stepper.  This code used an external stepper driver so that used only 2 signals from the PLC, step and direction.

        The code was written to allow me to test acceleration profiles.

        The post includes documentation to explain how the code operates. There is, also, user interface code that runs on Wientek touch screen HMI. 

        You can operate the code without the HMI using either the simulator or the on-line monitor with real hardware.

        I did not use the interrupt system to stop stepper movement.  I used ladder logic to handle the switch input and invoke a custom function to stop the stepper motion.

        i found that the interrupt approach was not necessary for my application.

        I suggest that you use this code as a starting point.  Get it to work with out interrupts, first.  Then change the code to use interrupts and see if this improves how your system operates.

        The following is a link to this forum where you can find the PLC code and it’s documentation:

        Best regards,

        Gary Dickinson

        Technical support / Re: Handling more than 1000 #Define entries
        « on: March 26, 2021, 04:14:42 PM »
        I use lots of #define entries.  My #1 usage is to create named variables from DM[], DM32[] and FP[].

        I export the #defines to Excel so that I can build the register address to allow the Wientek HMI to access PLC variables and RELAYS.  I make less mistakes in getting the devices to talk to each other by this approach.

        I miss the concept of data structures that modern programming languages support, but TBASIC does not.  I use the #define mechanism to allow me to build data structures and arrays of structures.

        So I don’t know the right answer to how many #defines.

        But I have a suggestion/request on how to reduce the number of #defines that I use.

        Provide access to EEPROM data using array notation.

        something like this:

        EEP32[n] = 6  :  A = EEP32[n]

        That would allow me to define a variable stored in EEP once.   #define Diameter  EEP32[23]

        Now I can write code like this:

        Diameter = 12

        if Diameter > 2
            ' do something useful
        end if

        Currently, if I limit my self to only using a single define to access EEP I can do something like this:

        #define Diameter  23

        The usage looks like this:

        SAVE_EEP32 12, Diameter
        If LOAD_EEP32(Diameter) > 2
        end if

        To assign a value to EEP I have to use a statement.  To access a value in EEP I have to use a parameterized function call.

        The single define approach requires, that I have to be very careful in writing my code.  EEP access for integers can be 8,16 and 32 bit unsigned integers. EEP access can support floating point and string data, also. This creates a lot of ways to write buggy code.  TBASIC cannot detect that you are accessing an EEP variable in one spot as an 8 bit unsigned int and in another place as a 32-bit float.  But you can easily write code like this...

        If EEPROM could be accessed as EEP8[], EEP16[], EEP32[], EEP#[] and EEP$[] then I can use a single #define for all access to a single EEP resident variable.  It is harder for me to make simple mistakes that TBASIC can’t detect. Saves me from trying to convince my clients that my code is not buggy but rather feature laden.

        Gary d

        Technical support / Re: Handling more than 1000 #Define entries
        « on: March 08, 2021, 11:30:24 AM »
        Hi Nathanael,

        I am glad I got you thinking.

        Another resource is the file system that is supported by all of the modern PLCs (for the FMD and Nano-10 you have to add the FRAM-RTC-256 module). The file system allows up to 256K bytes of text data.  The 256K can be organized in a series of files. 

        These files can be uploaded, downloaded and deleted by use of FTP.

        The PLC can create, read, write, append, delete these files. The PLC can check for the existence of a file and determine file size.

        I use the file system with my clients to allow remote configuration of their systems.  Additionally I have the PLCs generate a set of files that describe the current system configuration on each power up/reboot event.

        The current files can be used as documentation.  One can edit the files with a text editor and upload the edited file back to the PLC to modify its configuration.

        To store 4000 32-bit data words in hexadecimal would require about 36K bytes.  DM32 holds 2000 32-bit integers and FP holds 2000 floats that adds up to 4000 32-bit values. You could store 7 copies of DM32[] and FP[] in 256k of text space! 

        TBASIC is actually pretty good at working with strings.

        More things to think about.

        Gary d

        Technical support / Re: Handling more than 1000 #Define entries
        « on: March 06, 2021, 03:42:22 PM »

        You have figured out about the same solution to handling arrays of structures in TBASIC as I did.  So I may not be able to reduce the number of define table entries that you desire.

        My approach is slightly different. This is an exert from the #Define table for a single structure. My application has an array of 8 of these structures

        Code: [Select]
        #   #Define Name        Value
        45  FillerArrayBase     1
        46  FillerArraySize     10
        47  FillerStatus        DM32[y+0]
        48  FillerCapacity      DM32[y+1]
        49  FillerVolume        DM32[y+2]
        50  FillerUllage        DM32[y+3]
        51  FillerDvDt          DM32[y+4]
        52  FillerTtlDvDt       DM32[y+5]
        53  FillerBOL           DM32[y+6]
        54  FillerDeliveryVol   DM32[y+7]
        55  Delivered           DM32[y+8]
        56  FillerReserved09    DM32[y+9]

        FillerArrayBase is the index into DM32[] for the first entry of the first data structure.
        FillerArraySize is the size of the data structure in 32-bit words.
        There are 8 of these structures in the array. The maximum number of structures is defined as MaxFillerN. This allows me to change the size
        of the array without having to change any of the CFs that act on the array of structures (there are no hard-coded values in my code for this sort of thing).
        What I do differently that you is the computation of the index into DM32[] to access a individual member of a structure. I compute the index to the the first member
        of the structure that I am interested in and assign this to the variable "y".  This keeps me from making simple mistakes in my coding that will be hard to find during debug.

        The variable "y" is set to the starting address of of the "current" data structure once on each loop through the structure.

        Code: [Select]
        ' UpdateStatus - Now that all of the tank measurements have been made, 
        '   this function will update the filler status information for each filler.
        for FillerIndex = 0 to MaxFillerN       ' for each fill point
            ' The variable, "y", is the index into the array of data structures that
            '   represent the state of an individual filler.
            y = FillerArrayBase + FillerIndex * FillerArraySize
            ' a mountain of code was deleted out of this function. The missing code
            '   assigned values to the XXXXTemp variables based on the state of 16
            '   fuel tanks that may be connected to the system. the XXXXTemp variables
            '   are defined as locations in DM32[] with the #Define mechanism.
            FillerStatus    = StatusTemp
            FillerVolume    = VolumeTemp
            FillerUllage    = UllageTemp
            FillerDvDt      = DvDtTemp
            FillerTtlDvDt   = TotalizerDvDtTemp
            FillerCapacity  = CapacityTemp

        next    ' FillerIndex (next filler)

        OK. What might I suggest that you do to cut down on the number of #Define entries?  It sort of depends on how you have structured your program.

        If you have 5 different recipes, do you save all five in the EEP?  As you appear to be using a Fx or Tile-based PLC you may have as much as 11K 16-bit words of EEP that you can store the recipes. If this is true, then you only need to access the current active recipe. You could copy the active recipe into a fixed location in DM32[] or you could access the EEP for the active recipe directly.  Load_EEP32[(y+n)] ... where y is that stating index into EEP32 for the active structure and n is the offset within the array.

        Another thing that you could do to simplify your life is to store all of the data in the same place. I notice that you have some 32-bit float values and some 32-bit integer values. If you have only a few float values you could:

        1. Convert them to scaled integers and save them in DM32[]. Lets say that the float value is 32.467 and your system only needs the number to be accurate to +/-0.1 then save the float      DM32[n] = round(32.467,1) * 10    This is a scaled integer. When you need to use it covert it to what ever units your system actually uses.
        2. Save the float value DM32 as a bit pattern.  DM32[n] = Float2Bits(32.467).  When you need to use the value as a float convert it back   BitsFloat(DM32[n]).

        By not having to split your recipes between DM32[] and FP[] might make your life a bit simpler(???).

        Oh, and you can delete all uses of "THEN" from your if..else statements. "THEN" is purely optional and is purely useless.  Won't save you any execution time or reduce the number of #defines but it might make your .PC7 files a few bytes smaller.

        Best regards,

        Gary Dickinson

        Technical support / Re: Handling more than 1000 #Define entries
        « on: March 06, 2021, 10:46:00 AM »

        I am not an expert on JSON, but I do have a method for making DMxx[] look like an array of data structures and not just a simple array of numeric values.

        The approach that I have taken does use the #Define mechanism but does not need as many #Define entries as you are using.  I only use a #Define for each structure member, and the total size of the data structure.

        If you are interested, I can post bits of my PLC code and #Define tables at some explanation for how I use the approach.  I have been using this approach for 10+ years, but have not posted it on the forum.

        Best regards,

        Gary Dickinson

        Technical support / Re: Interfacing Thermistors to TRI PLCs
        « on: October 14, 2020, 02:54:37 PM »
        This message has the .zip version of my spreadsheet.

        Gary Dickinson

        Technical support / Interfacing Thermistors to TRI PLCs
        « on: October 14, 2020, 02:50:25 PM »
        I am working on a PLC project that needed to use Thermistors for temperature measurements.  This is not a new topic, but for my current application I decided to see if I could do this without the traditional lookup table and linear interpolation algorithms.

        My application did not only required accuracy over a range or 30 or 40 degrees C. 

        I was able to get the math for converting ADC counts to temperature to a single equation for my application:
              y# = x * 2.1095E-2 - 3.3485E+1         

        No lookup tables.

        So I am sharing how I got to this sort of solution. Perhaps, it would be useful to others.

        I have attached the documentation to this message. I will post the Excel spreadsheet on the next message in this thread.

        Gary Dickinson

        Technical support / Re: Unwanted Stepper motor movement
        « on: September 14, 2020, 11:48:27 AM »

        Glad that you found a simple solution.

        I find that most of the documentation (if any) for devices is wrong. I reverse engineer the I/O circuitry of most devices. With Modbus devices, I have to run test software to figure out how stuff works.

        I  have run into a few rare exceptions.  I have worked with Modbus devices from both a Finnish and Swiss company that were actually document correctly.  And they had technical support that could help with application issues!

        Best regards,

        Gary d

        Pages: [1] 2 3 ... 33