PLC array incrementation troubleshooting issues

Good afternoon, currently I’m trying to create a timer that increments by 1, in an array of [0…9]. This array will receive an integer. This program will store inputs from a user and output them in the order that they were stored in. My idea was that if I were to map each input to a variable that equaled an intger. Each integer represented one of the light colors. Red = 1, Green = 2, Blue = 3. After the inputs were submitted to the array. The array would be put through a case statement, the case statement would increment down the array by 1, and output the color of the requested light. The problem I’m running into is that the array isn’t running correctly. Attached below is a txt document of the code, and the test inputs I did. To me it seems like the counter is counting up and receiving inputs. I can’t manage to figure out whats causing it to skip positions or change numbers at the same location. Unfortunately txt is an unsupported format. I will post the program here /* The delay is to prevent the input from going into plc next too many times. Color Number is the integer that I’m using to fill the array for the case statement at the bottom of the program. Start_rec is there to reset the variables and then move over to start_rec2. Start_rec2 is there to act as a safeguard to protect the button inputs from accidentally triggering. Increment2 is there to act as something else for later. Please ignore. Increment is to count through the array. The data at the bottom were my test inputs and the response from the PLC. These inputs are where the numbers showed up when inputted them. The arrow key is to mark where the inputs were „turned“ into the new input. The 0 is there to act as a way of telling you that the array was skipped. If there is anything in there that is too confusing let me know! */ Delay(IN:=TRUE, PT:=T#5s); If NOT Delay.Q THEN return; END_IF Stop(IN:=TRUE, PT:=T#1s); If NOT Stop.Q THEN return; End_If //Color RGB inputs If (Digital_2 or Input_1) THEN Light_1 := TRUE; Color_Number := 1; ELSE Light_1 := FALSE; Digital_2 := FALSE; Color_Number:=0; END_IF; If (Digital_4 or Input_3) THEN Light_3 := TRUE; Color_Number3:=3; ELSE Light_3 := FALSE; Digital_4 := FALSE; Color_Number3 :=0; END_IF; If (Input_2 or Digital_3) THEN Light_2 := TRUE; Color_Number2:= 2; ELSE Light_2 := FALSE; Digital_3 := FALSE; Color_Number2 :=0; END_IF; IF (Rec_Start) THEN Color_Chosen :=TRUE; ELSE Color_Chosen := FALSE; Color_Chosen2 := FALSE; Rec_Start2 := FALSE; Incrementer := 0; Incrementer2 :=0; Placed_Num := 0; inc3 := 0; END_IF; IF(Input_1 or Input_2 or Input_3 or Digital_5 or Digital_6 or Digital_7 and Rec_Start) THEN Rec_Start2 := TRUE; ELSE Rec_Start2 := FALSE; END_IF; IF (Rec_Start2 and Color_Number = 1) THEN Incrementer := Incrementer+1; Color_Array[Incrementer] := Color_Number; Stop(IN:=FALSE); END_IF; IF(Color_Number3 = 3 and Rec_Start2 ) THEN Incrementer := Incrementer+1; inc5 := Incrementer; Color_Array[Incrementer] := Color_Number; Stop(IN:=FALSE); END_IF; IF(Color_Number2 = 2 and Rec_Start2 ) THEN Incrementer := Incrementer+1; inc4 := Incrementer; Color_Array[Incrementer] := Color_Number; Stop(IN:=FALSE); END_IF; IF(Rec_Start3) THEN Case Color_Array[Incrementer] OF 0: Incrementer := Incrementer-1; Incrementer2 := Incrementer2+1; Delay(IN:=FALSE); 1: Light_1 :=TRUE;Incrementer := Incrementer -1; Incrementer2 := Incrementer2 +1; Delay(IN:=FALSE); 2: Light_2 :=TRUE;Incrementer := Incrementer -1; Incrementer2 := Incrementer2 +1; Delay(IN:=FALSE); 3: Light_3 :=TRUE;Incrementer := Incrementer -1; Incrementer2 := Incrementer2 +1; Delay(IN:=FALSE); END_CASE END_IF; [0]0 [1]2 → 1 [2]0 [3]3 [4]3 -->1 [5]0 [6]3 [7]2 → 1 [8]0 [9]2 [10]2

Hello Keith, when one of my colleagues has the time he will check your code. in the mean time you can set a breakpoint at the start of your code and then step through it step by step to debug what is going wrong. The LogicAnalyser might help as well. kind regards, Oliver

I’ve tried to understand what the code is supposed to do, but unfortunately without much success. a) Rather than post the complete program, it would be good if you could strip down the program to demonstrate one single problem, e.g. if the problem is that is data being inserted at an unexpected location in an array, then the simplest possible program that demonstrates this problem would save time for everyone who wants to help. b) It is also important to show the data types of all the variables, e.g. in your program it’s not clear what data type „Delay“ is. c) If you could then give a step-by-step explanation of how to reproduce the problem (e.g "Turn on Input 1 for 1 second, then …), that would also be very helpful. d) It would also be useful to describe the problem that the code is trying to solve, i.e. the „Functional specification“ for the project. You didn’t ask, but someone may want to suggest an alternative solution that also fulfils the requirements, but perhaps in a simpler way.

I’ll be a lot more thorough this time. Sorry about that, usually I try to make my posts as thorough as possible and I didn’t feel like up to it that day. Also, currently I’ve completely lost access to my PLCNext after doing a firmware update so I’m not able to try troubleshooting yet. So essentially I wanted this program to be a teaching experience for my co-workers. None of them have touched structured text before. I wanted to create a module that’d teach them how to do basic things in ST. Similar to how a college course would introduce you to the fundamentals of C++. Ie it’d focus on for loops, case statements, if statements, using arrays, and functions. Then maybe structs. So far, I was able to cover all of those topics except for arrays. This module would use an RGB light that would be hooked up to the outputs of the PLC. The people who go through this program could use analogue inputs to interact with this module and get a more direct interpretation on PLCNext’s software. These inputs would control the way the lights worked. The first part of this program I made code where the user could turn on or off the lights individually. If (Input_1) THEN Light_1 := TRUE; ELSE Light_1 := FALSE; END_IF; If (Input_3) THEN Light_3 := TRUE; ELSE Light_3 := FALSE; END_IF; If (Input_2 or Digital_3) THEN Light_2 := TRUE; ELSE Light_2 := FALSE; END_IF; To clarify what these variables mean. Light_1, Light_2, and Light_3 are my output lights. They’re directly connected to lights RGB. They are booleans, true turns on the light, and false turns off the light. The variables Input_1, Input_2, and Input_3 are my discrete inputs hooked up to 3 different buttons. When I press down on the buttons the Input_1,2,&3 go from false to true, and when I release them they go from true to false. For the array I decided that I’d have to do several things to get it to function properly for this module. First, I wanted to have an if statement that would trigger when the light buttons are activated and it would have a „confirmation button“ that would be activated through PLCNexts debugger. This confirmation button would prevent this code from being triggered in other parts of the program. I called the confirmation button rec_start. Short for record start. The record start also functioned as a way to set some variables to 0 if the user decided to end this part of the module. Finally the if statement being true would turn on the second part of recording our user inputs, Rec_Start2. I’ll cover Rec_Start2 in the other part. IF(Input_1 or Input_2 or Input_3 and Rec_Start) THEN Rec_Start2 := TRUE; ELSE Incrementer:=0; Incrementer2:=0; Rec_Start2 := FALSE; END_IF; To clarify what rec_start is, it is a local boolean variable. Incrementer & Incrementer2 are local integer variables. Rec_start2 has a lot more moving parts. The second part of the recording process breaks up each light into its own if statement. The if statement would do two things, increment the array, and record whatever light was pressed. Each color had its own respective if statement. Each of these if statements also had an increment variable that would be used to push the array a position up every time the user decided to input a light. Before implementing this I was concerned that the user would press down on the button for too long. Which would cause our array to fill up fast because multiple cycles of the same input would fill up the array. So I decided to create a delay timer of 2 seconds and push it into each of my IF statements for where the input would be recorded. These 2 second delay timers would be the function Delay(). Then I realized another problem, how would I record the inputs? It couldn’t be a boolean, a True or False statement in an array wouldn’t mean anything. So instead of making a boolean I decided to give each light an integer variable called color_number, color_number2, and color_number3. So now our array would have a number that it could use to reference the lights by, and we’d be able to implement those numbers to being lights later on in the code. I’ll post what the code would look like now since we covered a lot. Delay(IN:=TRUE, PT:=T#5s); If NOT Delay.Q THEN return; END_IF If (Input_1) THEN Light_1 := TRUE; Color_Number:= 1; ELSE Light_1 := FALSE; END_IF; If (Input_3) THEN Light_3 := TRUE; Color_Number3:= 3; ELSE Light_3 := FALSE; END_IF; If (Input_2 or Digital_3) THEN Light_2 := TRUE; Color_Number2:= 2; ELSE Light_2 := FALSE; END_IF; IF (Rec_Start) THEN Color_Chosen := TRUE; ELSE Rec_Start2 := FALSE; END_IF; IF(Input_1 or Input_2 or Input_3 or Digital_5 or Digital_6 or Digital_7 and Rec_Start) THEN Rec_Start2 := TRUE; ELSE Rec_Start2 := FALSE; END_IF; IF (Rec_Start2 and Color_Number = 1) THEN Incrementer := Incrementer+1; Color_Array[Incrementer] := Color_Number; Delay(IN:=FALSE); END_IF; IF(Color_Number3 = 3 and Rec_Start2 ) THEN Incrementer := Incrementer+1; Color_Array[Incrementer] := Color_Number3; Delay(IN:=FALSE); END_IF; IF(Color_Number2 = 2 and Rec_Start2 ) THEN Incrementer := Incrementer+1; Color_Array[Incrementer] := Color_Number2; Delay(IN:=FALSE); END_IF; Finally after the array was filled we’d turn on our third record. The name of this boolean was rec_start3. Once rec_start3 was turned on it would input all of our array’s variables in a descending order. I decided to use a case statement for this problem. A case statement could sort through the integers in the array. To increment down the array we made sure to de increment the array by having the increment decrease every time a case is called. Once it would de increment and once it got through all of the variables our user could disable the rec_start3 manually. IF (Rec_Start3) THEN Case Color_Array[Incrementer] OF 0: Incrementer := Incrementer-1; Delay(IN:=FALSE); 1: Light_1 :=TRUE; Incrementer := Incrementer -1Delay(IN:=FALSE); 2: Light_2 :=TRUE; Incrementer := Incrementer -1;Delay(IN:=FALSE); 3: Light_3 :=TRUE; Incrementer := Incrementer -1;Delay(IN:=FALSE); END_CASE END_IF; If you’re wondering why 0 is a case statement thats because our incrementer had unresolved bugs that I will address now. For some reason my array seems to have a problem with recording inputs at the proper location. In some cases it would skip parts of the array, and others it would input a new integer at the same array, erasing whatever was there in the first place. I did notice a pattern when I was trying to trouble shoot it. Entering two different numbers might cause a bug, but entering the same number never causes a bug. I have a sample sequence below on what the software outputted in sample arrays. Essentially each number is a button press. A 1---->3 is when I pressed 1 then 3, but 1 was replaced by 3. A 0 in this means that an array was skipped, and the value there is 0. [0]0 [1]2 → 1 [2]0 [3]3 [4]3 -->1 [5]0 [6]3 [7]2 → 1 [8]0 [9]2 [10]2

Some comments: ===================================================== It seems that the functional specification of this project can be summarised as: [list] []The machine has three lights (Red, Blue, Green) and three pushbuttons (Red, Blue and Green). []When commanded, the machine records the sequence that the buttons are pressed. [*]When commanded, the machine replays the sequence by illuminating the corresponding lights in the same sequence that the buttons were pressed. [/list] There’s more to it than that, but it’s important to make the function of the machine clear before writing any code. ===================================================== If (Input_1) THEN Light_1 := TRUE; ELSE Light_1 := FALSE; END_IF; Can also be written as: Light_1 := Input_1; ===================================================== [quote]Before implementing this I was concerned that the user would press down on the button for too long. Which would cause our array to fill up fast because multiple cycles of the same input would fill up the array.[/quote]This type of problem is typically solved using edge detection, which sets a boolean value for a single execution cycle on the rising or falling edge of another boolean value. You can either implement that yourself, or use the edge detection function blocks R_TRIG (rising edge) and F_TRIG (falling edge). If input „bouncing“ is a problem, then that is typically solved using a timer with a preset time slightly longer than the maximum expected duration of the bounce. ===================================================== A timer FB instance should never be called in more than one place. There might be exceptions to this rule, but I can’t think of any good examples. Instead, the boolean variable that is passed as the „IN“ parameter to the time FB should be set/reset in the code to control the behaviour of the timer. In addition, timer instances should always be called on every execution cycle. Putting timer instances in conditional code blocks - e.g. after a return statement, or in case statements - makes it very difficult to figure out how the timer will behave. ===================================================== It is generally best not to use return (or goto) statements unless absolutely necessary. It is easy to forget that none of the code that is jumped over will be executed, and that can make it difficult to figure our how the code will behave. This is particularly true when timer FB instances are not called on every execution cycle. ===================================================== In summary, I think the most of the problems with this code can be explained by the way that timer FB instances are being used. To explain the behaviour in detail, it would be necessary to draw up a timing table, where you list the values of every variable (including timer ET values) at the start and end of every execution cycle. If you do that, you will be able to explain the behaviour that you are seeing. If you want someone to suggest an alternative solution to the problem you’re trying to solve, then a clear and complete functional specification would be required.