|
| 1 | +--- |
| 2 | +title: 'Getting Started with Interrupts on Opta™' |
| 3 | +description: "Learn how to make use of the Interrupts on Opta™." |
| 4 | +difficulty: beginner |
| 5 | +tags: |
| 6 | + - Getting started |
| 7 | + - Interrupts |
| 8 | +author: 'Taddy Chung and José Bagur' |
| 9 | +software: |
| 10 | + - ide-v1 |
| 11 | + - ide-v2 |
| 12 | +hardware: |
| 13 | + - hardware/07.opta/opta-family/opta |
| 14 | +--- |
| 15 | + |
| 16 | +## Overview |
| 17 | + |
| 18 | +The Opta™ micro PLC is designed to operate in several industrial environments involving crucial processes. These processes require controllers to be responsive and precise to manage sensitive tasks and capable of handling large sets of conditions within defined parameters in real-time. Asynchronous operations or spontaneous events are the kind of process that requires immediate attention at a given moment. Therefore, interrupt management is critical to control and optimize these event classes. |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +The **Interrupt**, a basic yet vital feature, is available on Opta™ to handle time-sensitive and unexpected events based on state changes. This tutorial will help you to implement interrupts on Opta™ using the Arduino programming language and the [Arduino IDE](https://www.arduino.cc/en/software). |
| 23 | + |
| 24 | +## Goals |
| 25 | + |
| 26 | +- Learn how to configure an Opta™ to use interrupts |
| 27 | +- Learn how to implement interrupts on Opta™ |
| 28 | + |
| 29 | +### Required Hardware and Software |
| 30 | + |
| 31 | +#### Hardware Requirements |
| 32 | + |
| 33 | +- Opta™ PLC (x1) |
| 34 | +- USB-C® cable (x1) |
| 35 | +- 12-24VDC/1A power supply (x1) |
| 36 | + |
| 37 | +#### Software Requirements |
| 38 | + |
| 39 | +- [Arduino IDE 1.8.10+](https://www.arduino.cc/en/software), [Arduino IDE 2.0+](https://www.arduino.cc/en/software), or [Arduino Web Editor](https://create.arduino.cc/editor) |
| 40 | +- [Interrupt example code](assets/Interrupts_Opta.zip) |
| 41 | + |
| 42 | +## Interrupt Basics |
| 43 | + |
| 44 | +**Interrupts** are execution requests triggered usually by a timed event or signal which will pause the active process if the interrupt request is accepted under certain conditions, executing new high-priority commands immediately and returning to the main process as soon as possible. The **Interrupt Service Routine**, or **ISR**, is the handler that performs a specific instruction set whenever an interrupt is raised. |
| 45 | + |
| 46 | +The handler can be defined to run particular instructions periodically, use external signals, or send an alert in case of a system failure. It is a function launched as a priority task among the other operations, whenever a specific high-awareness state change occurs respective to an assigned trigger. |
| 47 | + |
| 48 | +### Interrupt Types |
| 49 | + |
| 50 | +Interrupts are based on **hardware** and **software** events: |
| 51 | + |
| 52 | +* The *hardware interrupt* is an interrupt raised by a hardware signal sent from an external device. This interrupt class handles its asynchronously generated interrupt signal to synchronize it with the subsequent instructions of the interrupted device. For example, the button press can send a signal that represents the change in the hardware state to execute a task requiring immediate attention, such as an emergency stop alert. |
| 53 | + |
| 54 | +* The *software interrupt* is raised when the device itself is exposed to internally defined conditions or upon a particular routine call. It watches for special conditionals that create interrupts based on the present parameters. For instance, read and write operations of the storage device interacting with the controller driver is one example that involves software interrupt. |
| 55 | + |
| 56 | +### Interrupt Triggers |
| 57 | + |
| 58 | +Interrupt signals must be set with appropriate triggers to create interrupt requests correctly, and they become critical when implemented on programmable logic controllers such as Opta™. Because it handles broad signal types, it is a good practice to understand which signal circumstances suit certain applications. Generally, they are **Level-Triggered** or **Edge-Triggered** interrupts. They are characterized as follows: |
| 59 | + |
| 60 | +* **Level-Triggered:** This is when an interrupt has been requested with signals at a particular logic level, which can be either *HIGH* or *LOW*. |
| 61 | +* **Edge-Triggered:** This is when an interrupt has been requested due to a signal at a specific transition level, which can be either *RISING* or *FALLING* edge. It can also be configured with *CHANGE* to interrupt whenever either signal transition has occurred. |
| 62 | + |
| 63 | + |
| 64 | + |
| 65 | +Now that you have a better knowledge about interrupts, let's see how to use interrupts with an Opta™ device. |
| 66 | + |
| 67 | +## Instructions |
| 68 | + |
| 69 | +### Setting up the Arduino IDE |
| 70 | + |
| 71 | +This tutorial will need the latest version of the Arduino IDE. You can download the Arduino IDE [here](https://www.arduino.cc/en/software). Please check the [getting started with the Opta™ tutorial](/tutorials/opta/getting-started) if it is your first time setting up the Opta™ with the Arduino IDE. |
| 72 | + |
| 73 | +### Example Setup |
| 74 | + |
| 75 | +The example will try to keep the setup as simple as possible while maintaining the scalability of the feature on Opta™. The setup will use the programmable user button (`BTN_USER`) and `A0-A1` inputs as interrupt pins. All available `D0-D3` relays will be configured as outputs and status LEDs will indicate the corresponding contact state. |
| 76 | + |
| 77 | +Please refer to the following diagram to have an overview of the inputs and outputs position of the example model. |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | +### Example Overview |
| 82 | + |
| 83 | +The example will showcase different interrupt routines for Opta™ using two scenarios: |
| 84 | + |
| 85 | +1. The `BTN_USER` is the user-programmable button that will be used for the interrupt to simulate asynchronous events. The corresponding interrupt will make relay and corresponding status LED state switch in a sequence based on its present state. |
| 86 | +2. The `A0` and `A1` inputs will be open to external devices that send signals periodically, and it will make an interrupt on the signaled pin. The `A0` will be in charge of the `D0` and `D1` relays, while the `A1` will control the `D2` and `D3` relays. |
| 87 | + |
| 88 | +These tasks will help you test multiple interrupt schemes combined with Opta™ PLC's onboard relays and status LEDs. The following section will highlight the details of interest of the example code to help you understand it with ease. |
| 89 | + |
| 90 | +### Example Description |
| 91 | + |
| 92 | +When working with interrupts, it is crucial to designate the interrupt variables as `volatile` so that they can be used by both the ISR function and the main program. In this case, `counter` will keep the number of `BTN_USER` presses and `relayLedState` to track the status LED of the corresponding relay. The boolean variables `relCntState`, `batchState01`, and `batchState23` will be used to control the conditional flag when its respective interrupt is called. |
| 93 | + |
| 94 | +```arduino |
| 95 | +volatile unsigned int counter, relayLedState {}; |
| 96 | +volatile bool relCntState = false; |
| 97 | +volatile bool batchState01 = false; |
| 98 | +volatile bool batchState23 = false; |
| 99 | +``` |
| 100 | + |
| 101 | +The relays and corresponding status LEDs are defined in an array including their status. Using the array provides the advantage to manage and call the data flexibly. |
| 102 | + |
| 103 | +```arduino |
| 104 | +int idx{ 0 }; |
| 105 | +int relays[]{ D0, D1, D2, D3 }; |
| 106 | +int leds[]{ LED_D0, LED_D1, LED_D2, LED_D3 }; |
| 107 | +bool statuses[]{ true, true, true, true }; |
| 108 | +``` |
| 109 | + |
| 110 | +The `setup()` will define the relay and status LED outputs, and also the inputs that will be used to attach to interrupt cases. The `attachInterrupt()` function configures the inputs as interrupts with its trigger method and connects to the defined ISR functions that can be found later in the example description. |
| 111 | + |
| 112 | +***For more information about `attachInterrupt()` function, please check [here](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/). You will also be able to read briefly more information about interrupts.*** |
| 113 | + |
| 114 | +Thus, all the defined input pins are set as interrupt pins with their dedicated ISR functions and are programmed to trigger with a `RISING` signal. This way, every time one of the pins goes from LOW to HIGH (rising signal), the ISR or callback function will be executed. |
| 115 | + |
| 116 | +```arduino |
| 117 | +void setup(){ |
| 118 | +
|
| 119 | + ... |
| 120 | +
|
| 121 | + // Output Configuration |
| 122 | + for (int i = 0; i < 4; i++) { |
| 123 | + pinMode(relays[i], OUTPUT); |
| 124 | + pinMode(leds[i], OUTPUT); |
| 125 | + } |
| 126 | +
|
| 127 | + // Input Configuration |
| 128 | + pinMode(A0, INPUT); |
| 129 | + pinMode(A1, INPUT); |
| 130 | + pinMode(BTN_USER, INPUT); |
| 131 | +
|
| 132 | + // Interrupt configuration |
| 133 | + attachInterrupt(digitalPinToInterrupt(BTN_USER), relayCounterISR, RISING); |
| 134 | + attachInterrupt(digitalPinToInterrupt(A0), batch01_ISR, RISING); |
| 135 | + attachInterrupt(digitalPinToInterrupt(A1), batch23_ISR, RISING); |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +The `loop()` will be running tasks that are to execute based on `BTN_USER` or `A0-A1` interrupts. It will also be checking and keeping the number of button presses that represent interrupts generated by the `BTN_USER` button. |
| 140 | + |
| 141 | +```arduino |
| 142 | +void loop(){ |
| 143 | +
|
| 144 | + if (millis() > printNow) { |
| 145 | + Serial.print("Presses: "); |
| 146 | + Serial.println(counter); |
| 147 | +
|
| 148 | + printNow = millis() + printInterval; |
| 149 | + } |
| 150 | +
|
| 151 | + // For USR_BTN interrupts |
| 152 | + relayLinearCounter(); |
| 153 | + |
| 154 | + // For A0 & A1 interrupts |
| 155 | + relayBatchInverter(); |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +The `relayLinearCounter()` function runs a linear sequence for turning on and off the `D0` to `D3` relays with their corresponding status LEDs based on the interrupt triggered each time `BTN_USER` is pressed. |
| 160 | + |
| 161 | +The `counter` and `relayLedState` variables are used to track the total number of `BTN_USER` triggered interrupts, which also represents the number of button presses, and currently shifted relay status. The `relCntState` is used as a gatekeeper instance based on the `BTN_USER` interrupt request since it is an active function inside the `loop()` function. |
| 162 | + |
| 163 | +```arduino |
| 164 | +/** |
| 165 | + Function to manage relay and status LED state in linear sequence based on BTN_USER interrupt. |
| 166 | +*/ |
| 167 | +void relayLinearCounter(){ |
| 168 | + if (relCntState == true){ |
| 169 | + if (counter > 4){ |
| 170 | + relayLedState = counter % 4; |
| 171 | + |
| 172 | + if (relayLedState == 0){ |
| 173 | + relayLedState = 4; |
| 174 | + } |
| 175 | + } else { |
| 176 | + relayLedState = counter; |
| 177 | + } |
| 178 | +
|
| 179 | + Serial.print(F("Triggered Relay: ")); |
| 180 | + Serial.println(relayLedState); |
| 181 | + Serial.print(F("Counter Status: ")); |
| 182 | + Serial.println(counter); |
| 183 | +
|
| 184 | + // Array indexes start at 0 |
| 185 | + idx = relayLedState - 1; |
| 186 | + relayStateHandler(idx); |
| 187 | +
|
| 188 | + relCntState = false; |
| 189 | + } |
| 190 | +} |
| 191 | +``` |
| 192 | + |
| 193 | +The `relayBatchInverter()` function checks to invert a relay batch and its corresponding status LEDs based on `A0` and `A1` interrupt pins. The `A0` controls the batch of relays `D0` and `D1`, while The `A1` controls `D2` and `D3` relays. |
| 194 | + |
| 195 | +Each input has an ISR function conditioned with `batchState01` and `batchState23` boolean variables. When either pin is triggered with an interrupt signal, it will invert the state of the corresponding relay batch based on present relay states. |
| 196 | + |
| 197 | +```arduino |
| 198 | +/** |
| 199 | + Function to handle relay and status LED states based on A0 & A1 interrupts to invert its defined states in batch. |
| 200 | +*/ |
| 201 | +void relayBatchInverter(){ |
| 202 | + if (batchState01 == true){ |
| 203 | + Serial.println(F("A0 interrupt: Relay Batch 0 & 1")); |
| 204 | + for (int i = 0; i < 2; i++){ |
| 205 | + relayStateHandler(i); |
| 206 | + } |
| 207 | + batchState01 = false; |
| 208 | + } |
| 209 | +
|
| 210 | + if (batchState23 == true){ |
| 211 | + Serial.println(F("A1 interrupt: Relay Batch 2 & 3")); |
| 212 | + for (int j = 2; j < 4; j++){ |
| 213 | + relayStateHandler(j); |
| 214 | + } |
| 215 | + batchState23 = false; |
| 216 | + } |
| 217 | +} |
| 218 | +``` |
| 219 | + |
| 220 | +The `relayStateHandler()` function manages the relay status and is implemented as a complementary function. The `digitalWrite()` function will use a special conditional that stores the applied state in a variable called `status`. |
| 221 | + |
| 222 | +It will seek the corresponding relay and compare its stored status to shift its state in a more automated way. This is a practical method to avoid writing the same lines of code in different parts of the sketch and can help to maintain a cleaner code structure using a single function with an argument to process the data. |
| 223 | + |
| 224 | +```arduino |
| 225 | +/** |
| 226 | + Function to handle relay and status LED state by receiving relay designation. |
| 227 | +*/ |
| 228 | +void relayStateHandler(int relayID){ |
| 229 | + // Get current status |
| 230 | + auto status = statuses[relayID] ? HIGH : LOW; |
| 231 | + |
| 232 | + // Apply new status to the outputs |
| 233 | + digitalWrite(relays[relayID], status); |
| 234 | + digitalWrite(leds[relayID], status); |
| 235 | +
|
| 236 | + // Invert the statuses array to be updated |
| 237 | + statuses[relayID] = !statuses[relayID]; |
| 238 | +} |
| 239 | +``` |
| 240 | + |
| 241 | +These are all the ISR functions that help to shift relay states within the corresponding process whenever an interrupt has occurred. These functions are kept as short as possible to maintain quick responses for interrupts that can happen at any time. |
| 242 | + |
| 243 | +For good practice, please do not use `delay()` inside these functions as it might cause erratic behaviors, and use it as a quick state modifier to be used with functions that may be running continuously. |
| 244 | + |
| 245 | +```arduino |
| 246 | +/** |
| 247 | + ISR functions. Below are related inputs with respective ISR function. |
| 248 | + - BTN_USER: relayCounterISR() |
| 249 | + - A0: batch01_ISR() |
| 250 | + - A1: batch23_ISR() |
| 251 | +*/ |
| 252 | +void relayCounterISR(){ |
| 253 | + counter++; |
| 254 | + relCntState = true; |
| 255 | +} |
| 256 | +
|
| 257 | +void batch01_ISR() { |
| 258 | + batchState01 = !batchState01; |
| 259 | +} |
| 260 | +
|
| 261 | +void batch23_ISR() { |
| 262 | + batchState23 = !batchState23; |
| 263 | +} |
| 264 | +``` |
| 265 | + |
| 266 | +### Full Example Code |
| 267 | + |
| 268 | +You can access the complete example code [here](assets/Interrupts_Opta.zip). After extracting the compressed file, you will be able to upload and test it out with your Opta™ device. |
| 269 | + |
| 270 | +### Testing Interrupt Example |
| 271 | + |
| 272 | +You will be able to observe the following results when testing if you were able to upload the example code correctly to the Opta™. |
| 273 | + |
| 274 | +* You will be able to observe that the `D0-D3` relays are turning on in sequence and turning off in the next succession linearly as you press the `BTN_USER` button. The sequence then will repeat, and the counter will keep a record of the number of interrupts caused by `BTN_USER` button press, and currently actuated relay via `relayLedState`. |
| 275 | +* If you send feedback on either `A0` or `A1` input with a rising edge signal, it will apply state inversion on top of the actual relay states as a result of the interrupt. Thus, you will observe the `D0` and `D1` relays invert their states if the interrupt was triggered on `A0`; while `A1` will do the same job, but on `D2` and `D3` relays. |
| 276 | + |
| 277 | +Hence, your Opta™ is processing based on asynchronous interrupt generated by `BTN_USER` button while `A0-A1` are in charge of the interrupts generated periodically by an external device. |
| 278 | + |
| 279 | +## Conclusion |
| 280 | + |
| 281 | +In this tutorial, you have learned to enable and use interrupts on Opta™. On top of it, with the supplied example, you have tested Opta™ with multiple interrupt cases by using an integrated user-programmable button and signal sources from external devices, along with the Opta™ PLC's hardware features as onboard relays and status LEDs. Using the example of this tutorial as reference, now you can implement interrupts in your own professional solutions. |
| 282 | + |
| 283 | +### Next Steps |
| 284 | + |
| 285 | +Now that you are familiar with interrupts on the Opta™, take a look at the following documentation to learn more: |
| 286 | + |
| 287 | +* Take a look at [getting started tutorial](/tutorials/opta/getting-started) to get a better overview of the features of Opta™ |
| 288 | + |
| 289 | +* If you wish to incorporate Wi-Fi®/Bluetooth® Low Energy in your Opta™ solutions, have a look at [connectivity tutorial](/tutorials/opta/getting-started-connectivity) |
0 commit comments