Fifty SKUs on a mixer, each with its own shake and mix time, sounds like fifty compare rungs and a hundred moves. It is not. The pattern that survives commissioning is one table, one search, one active recipe: store every SKU as a record in a PLC array, search the array for the string the reader just delivered, copy the matched record once into an active-recipe block, and fault the cell when nothing matches. If you are wiring a fixed code reader to a PLC whose sequence must not start until a valid scan completes, this is the build: the record layout, two search options with their timing arithmetic, the no-match fault, and the tests that prove the table before the agitator starts.
Why one rung per SKU fails
The count gets away from you fast. One SKU needs an equality compare plus two moves, so fifty SKUs is 150 rungs, and SKU 51 means editing the running program instead of typing a row into a table. Parameters that live in logic can only be changed by someone holding the project file and the downtime to download it.
Keep the parameters in data and the program generic. The whole lookup becomes one array, one search routine of about ten rungs, and one active block the sequence reads. Adding SKU 51 is a row, not a revision. When the recipe set later outgrows one controller, this same table is the PLC side of the wider barcode part number parameter lookup question.
Lay out the record: one UDT per SKU
Define one record type, one array of it, and a small active block the sequence reads. The names are yours; the types are what matter.
- Recipe_Type (UDT): SKU as STRING, Shake_Time as REAL in seconds, Mix_Time as REAL in seconds.
- Recipe[0..49] of Recipe_Type: the table, with Row_Count (DINT) holding the filled rows, 50 here.
- Active_SKU (STRING), Active_Shake_Time (REAL), Active_Mix_Time (REAL), Recipe_Valid (BOOL, FALSE at power-up): the active block.
- Scan_String (STRING), Search_Index (DINT, initial 0), Search_Done (BOOL), Match_Found (BOOL), Match_Index (DINT): the search.
The sequence reads only the active block, so the search can change without touching the sequence, and the interlock that lets agitation start is the single Recipe_Valid bit rather than fifty rungs.
Get one clean string per scan
The reader delivers the decoded code over Ethernet or serial and it lands in the PLC as a string. The physical options are the standard barcode scanner input methods to a PLC, and the Ethernet case, reader as server with the PLC as client, is wired step by step in the barcode reader as TCP server guide.
Normalize before you search. A UPC or EAN-13 is digits only, so case never bites; a Code 128 carrying letters does, and the reader is configured to send one fixed case. Strip the terminator, prefix or suffix the reader appends, which the prefix, suffix and capture group parsing guide shows, and compare only what you normalized.
Then fire the search once per scan from a rising-edge bit that marks a fresh string. Re-arm it every scan and the search restarts at index 0 forever. Skip the search on an empty string: an empty string matches an unfilled row, and an unfilled row is exactly what a blank label delivers.
Search the array: FSC or one index per scan
String equality is one instruction; the ladder logic that compares barcode strings and counts three reads article shows that compare in context. The design decision is how you step through the array, and two documented patterns work:
- FSC (File Search & Compare). Give the instruction the expression Recipe[Recipe_FSC.POS].SKU = Scan_String, where Recipe_FSC is its own control tag. It compares element by element, stepping .POS one row per program scan, setting .FD on the scan it finds a match and .DN when the search ends. The match row is the value in .POS at the moment .FD sets.
- Your own loop. A LBL/JMP pair or a structured-text FOR loop that advances Search_Index one index per scan and sets Match_Found on equality. Same timing as the FSC, and you control the compare expression fully.
Both spread the search across scans, so the timing arithmetic is identical:
If you want the instruction wired rung by rung, a step-by-step FSC recipe-lookup walkthrough covers the array setup, the expression and the found-bit handling.
| Approach | Logic at 50 SKUs | Adding SKU 51 | First result after scan |
|---|---|---|---|
| One rung per SKU | 150 rungs | Logic edit, download, re-test | Same scan |
| FSC on the UDT array | About 10 rungs | One table row | One row per scan |
| Own one-index-per-scan loop | About 10 rungs | One table row | One row per scan |
Worked example: 50 SKUs at a 10 ms scan time
Inputs: 50 SKUs in the table (the mixer's count); a 10 ms scan time, the assumption in this example, since a mid-size program on a current controller typically lands in the 2-20 ms band and you read your own from the scan-time diagnostics; and a search stepping one row per scan, which both patterns above do.
Rule: worst-case search time = entries × scan time, and the average is half of that.
Arithmetic: worst case 50 × 10 ms = 500 ms. Average 25 × 10 ms = 250 ms. A match on row 1 answers in 1 × 10 ms = 10 ms; a match on row 50 takes the full 500 ms.
Result: the recipe is ready 10-500 ms after the scan, wherever the row sits in the table.
Decision: the mixer is disabled until a valid scan completes, so the operator takes whole seconds to place the tote and 500 ms is invisible; one row per scan is the right build here. The budget rule flips it: if the cell must have the recipe within 100 ms, one row per scan caps the table at 100 ms ÷ 10 ms = 10 entries, so at fifty rows run the entire loop inside one scan instead. Fifty string compares is a small, bounded load, and the scan-time diagnostics show the delta before and after, so the decision is measured rather than guessed.
Copy once, fault clean, clear after the mix
Three events drive the active block, and each does its work in one scan:
- Match. On Match_Found, move SKU, Shake_Time and Mix_Time from Recipe[Match_Index] into the active block and set Recipe_Valid. Then disarm until the next scan event, or the copy repeats every scan.
- No match. On Search_Done with Match_Found still clear, leave Recipe_Valid false, raise the no-match signal through the stack light, HMI message and reject counter, and require a fresh scan. Never fall back to the last valid recipe: the unreadable label is exactly the batch you know least about.
- Mix complete. Clear Active_SKU, zero both times, drop Recipe_Valid. The next start needs a fresh valid scan.
Log the SKU with start and end timestamps on events 1 and 3. The batch record then costs nothing and settles the twelve-minutes-or-eight argument on its own.
Keep the table trustworthy
Four checks keep the search honest:
- No duplicate SKUs. The search stops at the first match, so a SKU entered twice with different times runs whichever row comes first, which is an uncontrolled recipe. Reject a duplicate at entry.
- Search only filled rows. Loop to Row_Count, not the array bound. Searching all 50 slots of a 30-row table spends 20 compares per pass on rows that can never match, and an unfilled row matches an empty string that slipped past the guard.
- One case, no padding. Compare the normalized string, not the raw frame; fixed-length messages can carry padding spaces or leading zeros the table does not store.
- Survive a power cycle. The table holds production parameters, so save it to non-volatile storage or reload it at startup from the HMI. A reboot that quietly empties the shake times stops the line in a way that looks like a logic fault.
Prove it with five tests before the first production tote: a match on row 1, a match on row 50, an unknown code that faults, an empty string that does nothing, and a duplicate rejected at entry. Each has a known expected outcome, so run them in order and record the results with the log timestamps.
The reader in front of the logic
The lookup does not care which reader feeds it; it needs a clean ASCII string over Ethernet or serial, plus a link plan for the day the reader stops talking, with timeout, retry and a remote re-trigger, which the barcode reader to PLC communication plan lays out. Choose the front end by reading distance, code size and line rate:
- Resolution
- Up to 3 MP
- Shutter
- Global
- Interface
- Fast Ethernet
- Resolution
- 5 MP
- Interface
- GigE
- Resolution
- Up to 3 MP
For a reader that drops the string straight onto the network the PLC already speaks:
- Interface
- Fast Ethernet
- Resolution
- Up to 3 MP
- Shutter
- Global
- Lens mount
- M12
If the same device must also check label presence or orientation, a smart camera runs both jobs on one mount. Leave room for the optics side too: the code reader accessories range, light boards and protective or polarizing covers, is what fixes glare on glossy shrink-wrap without a logic change.
Before you power the mixer
- Table filled, Row_Count correct, duplicate check passed at entry.
- Reader output normalized: one case, terminators stripped, fixed length.
- The five search tests run and recorded.
- Recipe_Valid interlock proven with a forced no-match scan.
- Mix-complete step clears the active block.
- Timestamp logging live on match and completion.
Start at the fixed code readers category and filter by the interface your panel speaks; the camera finder covers the same range. If you want a second pair of eyes on the sequence interlock, send it to us through the contact page. More string-handling and decode patterns sit in the code-reading topic, and the recipe-array walkthrough shows this same pattern assembled end to end.
Questions
How long does the lookup take on 50 SKUs?
Entries multiplied by scan time is the whole calculation. At a 10 ms scan, 50 rows answer in at most 500 ms and about half that on average; at a 5 ms scan the same table answers in 250 ms. The copy into the active block is one additional scan either way, so the search dominates and the answer scales linearly with row count.
What if two rows carry the same barcode?
The search stops at the first match, so row order decides which shake and mix times run, which is an uncontrolled recipe. Reject duplicates when a row is entered, and treat a duplicate found during commissioning as a data fault to fix in the table, not something the logic should arbitrate.
Can the PLC compare two STRING tags directly?
Yes. The equality instruction on two STRING tags compares their characters, so no byte-level looping is needed. What trips integrations is content rather than the compare: terminators, padding spaces and leading zeros make visually identical strings unequal, which is why normalization happens before the search, not inside it.
Does the recipe table need a special driver in the PLC?
No. The reader delivers the decoded string as ordinary ASCII over Ethernet or serial, and the PLC side is generic ladder plus a table. Anything that speaks the same string format can replace the reader later without touching the lookup, which is the practical payback for keeping parameters in data.
How do I add SKU 51 to a running line?
Type a row, not a revision: raise Row_Count to 51 and enter the record through the HMI. The search loops to Row_Count, so no logic edit is needed, unlike the per-SKU compare approach, which adds three rungs and a code change to the running program for every new SKU.
Where should the recipe values live after a power cycle?
In non-volatile storage the controller keeps through power loss, or reloaded at startup from the HMI's saved set. Whichever you choose, add a startup check that the values are non-zero and Row_Count matches the saved count, so a partial restore is caught before the first tote rather than after.



