Contact and Coil | Nearly In Control

Mapping Your Inputs

This is part of an RSLogix 5000 Tutorial.

Understanding Asynchronous I/O

It’s important that you understand the difference between synchronous and asynchronous I/O in PLCs. If you come from the SLC 500 or PLC/5 world, you’ll be used to synchronous I/O, and that made your life a bit easier. In the PLC/3 world, I/O was asynchronous, and in a move that surprised many programmers, the ControlLogix/CompactLogix platform uses asynchronous I/O as well.

In a SLC 500, the PLC “scanned” the inputs by reading the state of the hardware and storing them in memory. Then it solved the logic. Then it “scanned” the outputs by copying the output coils to the actual hardware outputs. The important thing to realize is that the state of an input could never change during the logic solve. That meant you could write things like this:

Asynchronous I/O Example

…and you could be certain that Output 1 and Output 2 would never be on at the same time. However, with asynchronous I/O, our assumptions are wrong. Both Output 1 and Output 2 could be on during the same scan, or off during the same scan if Input 1 changes state between the time that it solves the first rung and it solves the second rung. Rare, but possible, and it leads to bugs that are intermittent and very hard to find, especially in logic that deals with critical timing, like data collection or communication logic.

In the days of the PLC/3, I understand programmers used to make sure they didn’t use any input more than once. They would copy the state of the input to an internal coil if they had to use it in more than one rung. With RSLogix 5000, I suggest solving it by creating a MapInputs routine that “scans” your inputs into temporary coils at the beginning of your program. Then you can forget about synchronous/asynchronous and go about your business.

Contact and Coil

Faced with the problem above, what I see a lot of programmers do is define a UDT for each input card, and then use a copy instruction to move the hardware input state into the UDT tag. There are two problems with this: first, UDTs can’t be edited without downloading the processor, so if you change an input you have to stop the machine, and second, if you’re using the UDT tag in your program, you can’t force those bits directly. You have to find the input bit that corresponds to the UDT tag bit, and force that. How would you go about finding that input? You can easily right click and do a cross reference, but that only brings you to the copy instruction. You still have to figure out that you’re looking at the 5th bit in the UDT, and then you know it corresponds to output 4 in the I/O card. Quite painful when you’re trying to fix a machine.

The solution is to use a separate rung for each input: a contact for the “real” input and a coil for the “internal” input that you use in your program. If you right click on the internal input in your program and pull up a cross reference, you can always find the OTE instruction, and easily find the real input bit to force.

Does it seem like a lot of work to do this? It really isn’t, especially if you master the art of copy-paste-search-replace, and the first time you have to find the input you’re looking for, you’ll save yourself at least that much time.

Back to the Tutorial

When we finished the last section of the tutorial, we had created a MapInputs program, but it still looked like this:

RSLogix 5000 - MapInputs - MainRoutine

Following the naming convention from the last section of the tutorial, rename MainRoutine to Aa_Main (by right clicking, selecting properties, and editing the name, then clicking OK). It’s already configured as the Main routine of the MapInputs program, so we don’t have to do that.

Right click on the MapInputs program and choose New Routine… from the menu:

RSLogix 5000 Tutorial - MapInputs - New Routine

That will open the New Routine dialog box. We want this routine to hold the logic for mapping the local input card that we added in section 3 of the tutorial. We named the I/O card “LI01” (for Local Input card 1). Name this routine the same, for consistency:

RSLogix 5000 Tutorial - MapInputs - New Routine: LI01

Click OK to finish creating it. You’ll now see LI01 in the controller organizer:

RSLogix 5000 Tutorial - MapInputs - New Routine LI01 Added

Follow the instructions from the last tutorial section to edit the Aa_Main routine in MapInputs and add a JSR instruction to call the LI01 routine:

RSLogix 5000 Tutorial - MapInputs - JSR to LI01

Now open the LI01 routine:

RSLogix 5000 Tutorial - Empty Routine

Add a contact and a coil to this empty rung. Locate the toolbar above the ladder editor, and click the contact button, and then the coil button (highlighted):

RSLogix 5000 Tutorial - Toolbar Buttons - Contact and Coil

That should change the rung so it looks like the following:

RSLogix 5000 Tutorial - MapInputs - Rung with Contact and Coil

When you created the LI01 input card in the I/O configuration, RSLogix 5000 automatically created some tags in the Controller Tags section for access to the inputs of this card. The tags begin with the word “Local”. The first input of the card is “Local:1:I.Data.0”. That means local rack, slot 1, input data, bit 0. Select the question mark above the contact in the new rung, and enter this full tag name. You should see it above the contact now:

RSLogix 5000 Tutorial - Local:1:I.Data.0

That’s the input side. Now we need an internal coil to store the value of the input during the scan. Rather than creating individual boolean tags for each coil, I suggest creating a single DINT tag called LI01 and using LI01.0 (bit 0) for the first input, LI01.1 for the second input, etc. I would go as far as using these tag names as the input wire names in your electrical drawings.

Select the question mark above the coil, and type LI01.0 and press enter:

RSLogix 5000 Tutorial - Coil LI01.0

Notice that the rung still has errors. This is because we haven’t defined the LI01 tag yet. If you hover over the tag, it will say “Undefined Tag”. To define the new tag, right click on LI01.0 above the coil and select New “LI01” from the context menu:

RSLogix 5000 - New LI01 Tag

That will open the New Tag dialog and autopopulate the Name field with LI01. However since we defining a tag based on a Coil instruction, it defaults to a Boolean data type. Change the data type (highlighted) to DINT (a DINT is a double-integer, meaning it has 32 bits – it is the default word size in the processor):

RSLogix 5000 Tutorial - Map Inputs - New Tag LI01

Click OK, and the rung won’t show an error anymore. However, even though LI01.0 is a perfect name for a tag because it’s concise and consistent, we also need to add a description so we know what it is. Right click on LI01.0 above the coil again, and this time select Edit Main Operand Description from the context menu:

RSLogix 5000 Tutorial - Edit Main Operand Description

That will open a small window above the ladder editor where you can enter a description. Go ahead an enter “INLET Cold Water Valve On”:

RSLogix 5000 Tutorial - INLET Cold Water Valve On

Click down in the ladder area when you’re done, and the description editing window will close. That will leave you with your first complete rung in the LI01 routine:

RSLogix 5000 Tutorial - MapInputs - One Rune

Add 15 more rungs to this routine in the same way, except increment the last number in Local:1:I.Data.XX from 0 to 15, and the last number in LI01.XX from 0 to 15. You can add the descriptions for the coils that you see here:

RSLogix 5000 Tutorial - MapInputs - Rungs 0 to 7
RSLogix 5000 Tutorial - MapInputs - Rungs 8 to 15

That’s all the inputs we’re going to have for this tutorial. If you had more input cards in your application, you’d add more routines in the MapInputs routine and more DINT tags for holding the state of the inputs during the logic scan.

Next time I’ll describe how to map your outputs, which is actually a bit different.


  • Chris Van · June 9, 2010 at 11:41 am

    First of all, thanks for taking the time to write this tutorial, I’ve been looking for some sort of instruction to get me started programming in ladder logic on RSLogix 5000 and this has been a big help, especially your explaination as to why you write things the way you do.

    I’ve worked through every step of the tutorial today and in doing so I noticed that you only create the LI01 tag in the MapInputs program – for this tag to be used in the other programs (I first noticed this when writing the Fa_Critical routine in the Faults program) I copied the entry for LI01 in the tag explorer and pasted it into the Program Tags section for each of other programs. Later on I found that I had to do the same with the FC tag created in Fa_Critical in order to reference it in Bc_CycleStart of the WashingMachine Program.

    I am not quite sure if this was the right thing to do, but it seems to work okay for the time being – whether the contents of tag are passed between the different programs is something I haven’t tested so far.

    Once again, thanks for writing the tutorial, I hope you findthe time to finish it soon.

  • Author comment by Scott Whitlock · June 9, 2010 at 12:10 pm

    Hi Chris,

    When I create the LI01 tag, take care to notice the “Scope” box on that dialog. I have it set to RSLogix5000_Tutorial, which should make it a controller tag. By default, the first time you create a tag, it will create it as program scope, so you have to change this to controller scope. Once you do that, I think all subsequent tags will default to controller scope.

    You can separate your tags out into controller and program scope. LI01 should definitely be controller, since it represents I/O which affects everything. I hope that helps.

    – Scott

  • Chris Van · June 10, 2010 at 4:06 am

    At least the fix is simple! All I needed to do to correct my mistake was copy paste the LI01 and FC tags into the controller tags database, then delete the entries I previously created for them in the relevant programs tag databases, acknowledging that this will cause the tag references in each routine to now refer to the newly created controller tags. If only variable management was this simple and explicit in all programming languages!

  • Fernando Vivanco · July 12, 2010 at 5:34 pm

    Thanks for this tutorial, I am integrator and I have experience in Micrologix, SLC500 and PLC5 but, it is the first time that I have to work with platform Controllogix. This toutoria has been useful for me.

    Thanks a lot.

  • ivirban · February 11, 2011 at 2:35 pm

    Thanks for tips!
    I’ve never seen your method before (seen lots of programs so far), I admit I also used CPS instruction.
    Question related:
    If you have a UDT and want a “alias” to a IO point from a member (which you can’t now) which option will you describe as best use:
    1- directly in IO map routine XIC+OTE to UDT member, or
    2- XIC+OTE from mapped Input to UDT member? IF this one, in same routine? Right under mapped input assigment?

  • Author comment by Scott Whitlock · February 11, 2011 at 5:43 pm

    @ivirban – If you feel you need to organize your I/O into UDT, then I suggest replacing the LIxxx DINT’s with your UDTs, and just use XIC+OTE to go right to the UDT. However, I caution against using UDT’s at all for this because you can’t edit a UDT without downloading the CPU. That’s the reason I used a stand-alone DINT for each input card. It provides more flexibility when you’re online.

  • Korda Mentu · March 26, 2012 at 5:23 pm

    I’ve always used Function Block Diagrams for my I/O buffering. It’s nice because it looks like a wiring diagram and is easy to locate the physical I/O for forcing purposes.

  • steel targets for .223 · May 12, 2013 at 7:35 am

    I have read some just right stuff here. Definitely price
    bookmarking for revisiting. I surprise how so
    much attempt you set to create the sort of excellent informative web site.

  • CevinMoses · March 30, 2015 at 9:52 am

    Wanted to get your opinion on something. I have an operator pedestal for selecting and starting cycles, and also an HMI on the other side of the machine for loading recipes. The HMI will also have the ability to mimic the inputs of the pedestal so that a cycle can be started from there. What makes more sense:

    a) Mapping the HMI input as a branch rung under the corresponding local input in the MapInputs task

    b) Mapping the HMI input as a branch rung under the corresponding LI01 bit in the Bc_CycleStart routine in the Machine task

    c) Having buttons in the HMI associated with the LI01 tag used as outputs in the MapInputs task

    I hope that made sense. I’m interested to hear your thoughts. Thanks.

  • Author comment by Scott Whitlock · March 30, 2015 at 8:20 pm

    @CevinMoses – option (b) is almost certainly your best option. Someone reading your program will assume that the LI01 bits are proxies for the inputs directly, so to mess with them without a really good reason (like a debounce) is going to invoke confusion. You want someone looking at your logic to see that there are two things that can start this cycle: the hard-wired button or the HMI button. They will definitely want to see that.

    Your option (c) is overly complicated.

    Typically what you’ll see is a tag called HMI_CycleStartPB. The HMI will write to that tag, and you can use it to branch around where you use your existing cycle start PB. The tag name itself tells the reader that the signal is coming from the HMI, and that it’s a button.

  • Adrian · April 7, 2016 at 9:33 pm

    This was very helpful. I have a miss matched mess of an io tree that has been added to multiple times, and everything was aliased to a raw table with a loop to populate it. could never find anything and very slow! I have started down the path of AOI’s for all IO, and trying to get it mapped correctly was horrendous as i am not an electrical engineer or such and my first attempt at the IO rather than just logic routines. this was very helpful information on the physical io properties.
    Thank you.

  • Jake · August 1, 2016 at 6:06 pm

    Great article!

    I’m a novice programmer. Mapping digital Inputs seems like great method. However, with the most recent version of RS Logix5000, it has a “Parameters and Local Tags” which seems to me, a new way of buffering IO. Considering design, memory use, scan time, and troubleshooting, is this new feature a better way to go?

    Also, does it make sense to use your method with Analog Inputs?

    Thanks again for creating this tutorial, it’s a nice gem to reference!

  • HR · August 20, 2016 at 8:33 am

    Great tutorial, Thanks. Could you please clarify one thing for me? As much as I like the I/O mapping, I feel as if alias will achieve the same goal. Ofcourse with I/O mapping we can control the program flow better but since the option is available, is there any other reason why you can’t/shouldn’t use alias tags instead of mapping?
    Thanks in advance.

  • HR · August 20, 2016 at 8:58 am

    Silly me, I skipped your info on asynchronous I/O. I just read it and answered my question but if there is any other reason/explanation or example, then please share

  • Author comment by Scott Whitlock · August 22, 2016 at 11:22 am

    @HR – there are actually two reasons: one is the asynchronous I/O reason, but the more important reason is to create a single point where you can add more logic for each input. It’s saved me a few times where I had a problematic input and I wanted to add a debounce timer or some other conditioning logic. This gives you a single place to do it.

    @Jake – When I’m using analog inputs, I also usually put the input scaling (converting from raw counts to engineering values) in the input map routine.

  • LZ · November 14, 2016 at 3:49 pm

    just to clarify, are all of these “mapped” inputs LI01.XX from 0 to 15, physically wired to inputs? I see that some of them have both states defined – INLET Cold Water Valve On is LI01.1 and INLET Cold Water Valve Off is LI01.2 on Local:1:I.Data.0 and Local:1:I.Data.1, respectively.

    How can this be? I am using your tutorial to set up a simple push button/LED and don’t understand why I would want to have both states mapped to a location on the input card when I could read the state and make a decision.

    (coming from the world of microcontrollers, PLCs seem counter intuitive).

    Thanks for your clarification.

  • Wessam · February 16, 2017 at 10:40 am

    Note to avoid problems along way with the tutorial note that the scope of the LI01 tag is not local to the routine, rather it has a controller scope

  • Kairi Gainsborough · June 21, 2017 at 4:26 pm

    I’ve just started trying to teach myself a few different programming languages, and tutorials like this are a huge help. All the screenshots you provided are really clear and helpful! I learn so much better when I can follow along like this than I do when I have to watch videos. Actually, I’m thinking about taking short RSLogix 5000 training course. Learning how to deal with the asynchronous I/O in the ControlLogix/CompactLogix platform should give me a good headstart.

  • Warren · June 27, 2017 at 3:59 pm

    I disagree with this mapping procedure. I’ve run into programs like this with horrific results. The engineer that writes the program has left the company and took the program with him. He’s one of those guys that likes job security and doesn’t share his information. Now something happens to the machine and you have no recourse but to upload the program. You don’t get all of those comments with the upload. Just the tags. So you don’t have a clue what any of that means. Use descriptive tags and alias them to the IO. Then everyone can understand what the program is doing. Share and backup. Don’t be one those guys!

  • Author comment by Scott Whitlock · June 28, 2017 at 5:48 am

    @Warren – I have never run into a problem with missing comments in a place where I’ve worked, because any place I’ve worked understands the importance of having the original program. Years ago I ran into a place that had purchased a machine and didn’t have the PLC source code, so they paid me to go there for a day, going through the prints and the wiring, figuring out what it did, and writing comments for all the bits in the PLC (this was a SLC, not a Compact/ControlLogix).

    The idea that you might someday run across a program without comments is hardly a reason to write all your code without using comments.

Leave a Reply

Theme Design by