From b6e6bcbf2eeccd0767564de70afd2dd95ea22e92 Mon Sep 17 00:00:00 2001 From: Jakub Andrysek Date: Wed, 12 Nov 2025 13:15:06 +0100 Subject: [PATCH] feat(gpio): Enhance GPIO validation tests with interrupt handling and button functionality --- tests/validation/gpio/diagram.esp32.json | 18 +- tests/validation/gpio/diagram.esp32c3.json | 14 +- tests/validation/gpio/diagram.esp32c6.json | 14 +- tests/validation/gpio/diagram.esp32h2.json | 14 +- tests/validation/gpio/diagram.esp32p4.json | 9 +- tests/validation/gpio/diagram.esp32s2.json | 14 +- tests/validation/gpio/diagram.esp32s3.json | 18 +- tests/validation/gpio/gpio.ino | 242 +++++++++++++++++++-- tests/validation/gpio/scenario.yaml | 40 ---- tests/validation/gpio/test_gpio.py | 109 +++++++++- 10 files changed, 411 insertions(+), 81 deletions(-) delete mode 100644 tests/validation/gpio/scenario.yaml diff --git a/tests/validation/gpio/diagram.esp32.json b/tests/validation/gpio/diagram.esp32.json index 05b28156e37..23603bc60fd 100644 --- a/tests/validation/gpio/diagram.esp32.json +++ b/tests/validation/gpio/diagram.esp32.json @@ -13,16 +13,26 @@ { "type": "wokwi-pushbutton", "id": "btn1", - "top": -13, - "left": -19.2, - "attrs": { "color": "green" } + "top": 83, + "left": -38.4, + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": -39.6, + "left": -41.4, + "rotate": 90, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-19.2", "v48", "h-38.4" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v57.8", "h-240", "v-76.8" ] ], + [ "esp32:GND.2", "led1:C", "black", [ "v0" ] ], + [ "esp32:4", "led1:A", "green", [ "h0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32c3.json b/tests/validation/gpio/diagram.esp32c3.json index c237e089ea2..e4c35c60667 100644 --- a/tests/validation/gpio/diagram.esp32c3.json +++ b/tests/validation/gpio/diagram.esp32c3.json @@ -15,14 +15,24 @@ "id": "btn1", "top": -22.6, "left": -19.2, - "attrs": { "color": "green" } + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": 28, + "left": -286.6, + "rotate": 270, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-28.8", "v144", "h-144", "v-95.7" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ], + [ "esp32:GND.4", "led1:C", "black", [ "h0" ] ], + [ "esp32:4", "led1:A", "green", [ "v0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32c6.json b/tests/validation/gpio/diagram.esp32c6.json index 5020171f4e6..f019943df78 100644 --- a/tests/validation/gpio/diagram.esp32c6.json +++ b/tests/validation/gpio/diagram.esp32c6.json @@ -15,14 +15,24 @@ "id": "btn1", "top": -22.6, "left": -19.2, - "attrs": { "color": "green" } + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": 56.8, + "left": -286.6, + "rotate": 270, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-19.2", "v-96", "h-163.2", "v93.77" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ], + [ "esp32:GND.1", "led1:C", "black", [ "h0" ] ], + [ "esp32:4", "led1:A", "green", [ "h0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32h2.json b/tests/validation/gpio/diagram.esp32h2.json index 48189dcea9f..e8c760d34db 100644 --- a/tests/validation/gpio/diagram.esp32h2.json +++ b/tests/validation/gpio/diagram.esp32h2.json @@ -15,14 +15,24 @@ "id": "btn1", "top": -22.6, "left": -19.2, - "attrs": { "color": "green" } + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": -0.8, + "left": -267.4, + "rotate": 270, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-19.2", "v-96", "h-163.2", "v93.77" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ], + [ "esp32:GND.2", "led1:C", "black", [ "h0" ] ], + [ "esp32:4", "led1:A", "green", [ "h-29.14", "v-26.57" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32p4.json b/tests/validation/gpio/diagram.esp32p4.json index ffb0cde2775..21bc4177896 100644 --- a/tests/validation/gpio/diagram.esp32p4.json +++ b/tests/validation/gpio/diagram.esp32p4.json @@ -15,14 +15,17 @@ "id": "btn1", "top": -128.2, "left": -19.2, - "attrs": { "color": "green", "bounce": "1" } - } + "attrs": { "color": "green", "bounce": "0" } + }, + { "type": "wokwi-led", "id": "led1", "top": -138, "left": -92.2, "attrs": { "color": "red" } } ], "connections": [ [ "esp32:38", "$serialMonitor:TX", "", [] ], [ "esp32:37", "$serialMonitor:RX", "", [] ], [ "btn1:2.r", "esp32:GND.3", "black", [ "h19.4", "v29" ] ], - [ "esp32:0", "btn1:1.l", "blue", [ "h-48", "v-67.2" ] ] + [ "esp32:0", "btn1:1.l", "blue", [ "h-48", "v-67.2" ] ], + [ "esp32:GND.1", "led1:C", "black", [ "v0" ] ], + [ "esp32:4", "led1:A", "green", [ "v-19.2", "h-48" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32s2.json b/tests/validation/gpio/diagram.esp32s2.json index e3f850e193e..6607863962d 100644 --- a/tests/validation/gpio/diagram.esp32s2.json +++ b/tests/validation/gpio/diagram.esp32s2.json @@ -15,14 +15,24 @@ "id": "btn1", "top": -22.6, "left": -19.2, - "attrs": { "color": "green" } + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": -0.8, + "left": -277, + "rotate": 270, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-28.8", "v-57.6", "h-144", "v42.71" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ], + [ "esp32:GND.1", "led1:C", "black", [ "h-67.47", "v-167.51" ] ], + [ "esp32:4", "led1:A", "green", [ "h0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32s3.json b/tests/validation/gpio/diagram.esp32s3.json index ad9f9e0308a..11b14e86cf6 100644 --- a/tests/validation/gpio/diagram.esp32s3.json +++ b/tests/validation/gpio/diagram.esp32s3.json @@ -13,16 +13,26 @@ { "type": "wokwi-pushbutton", "id": "btn1", - "top": -22.6, - "left": -19.2, - "attrs": { "color": "green" } + "top": 83, + "left": 9.6, + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": 66.4, + "left": -257.8, + "rotate": 270, + "attrs": { "color": "red", "flip": "" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-38.4", "v105.78" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v221", "h-269.2", "v-57.42" ] ] + [ "btn1:2.r", "esp32:GND.3", "green", [ "h19.4", "v48.2", "h-144.4", "v0.18" ] ], + [ "esp32:4", "led1:A", "green", [ "h0" ] ], + [ "esp32:GND.1", "led1:C", "black", [ "h0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/gpio.ino b/tests/validation/gpio/gpio.ino index a5bec1cb5a3..b0c3f7ec0bd 100644 --- a/tests/validation/gpio/gpio.ino +++ b/tests/validation/gpio/gpio.ino @@ -1,31 +1,241 @@ +/** + * GPIO Validation Test + * There are multiple synchronization points in this test. + * They are required for proper timing between the running code and wokwi-pytest. + * Without them, the test sometimes fails due to timing issues. + */ + #include #include #define BTN 0 +#define LED 4 + +volatile int interruptCounter = 0; +volatile bool interruptFlag = false; +volatile unsigned long lastInterruptTime = 0; + +// Variables for interrupt with argument test +volatile int argInterruptCounter = 0; +volatile bool argInterruptFlag = false; +volatile int receivedArg = 0; -void test_button() { - Serial.println("Button test"); - static int count = 0; - static int lastState = HIGH; - while (count < 3) { - int state = digitalRead(BTN); - if (state != lastState) { - if (state == LOW) { - count++; - Serial.print("Button pressed "); - Serial.print(count); - Serial.println(" times"); - } - lastState = state; +void waitForSyncAck(const String &token = "OK") { + while (true) { + String response = Serial.readStringUntil('\n'); + response.trim(); + if (response.equalsIgnoreCase(token)) { + break; } delay(10); } } +void setUp(void) { + interruptCounter = 0; + interruptFlag = false; + lastInterruptTime = 0; + argInterruptCounter = 0; + argInterruptFlag = false; + receivedArg = 0; +} + +void tearDown(void) {} + +void IRAM_ATTR buttonISR() { + unsigned long currentTime = millis(); + if (currentTime - lastInterruptTime > 50) { + interruptCounter = interruptCounter + 1; + interruptFlag = true; + lastInterruptTime = currentTime; + } +} + +void IRAM_ATTR buttonISRWithArg(void *arg) { + unsigned long currentTime = millis(); + if (currentTime - lastInterruptTime > 50) { + argInterruptCounter = argInterruptCounter + 1; + argInterruptFlag = true; + receivedArg = *(int *)arg; + lastInterruptTime = currentTime; + } +} + +void test_read_basic(void) { + pinMode(BTN, INPUT_PULLUP); + TEST_ASSERT_EQUAL(HIGH, digitalRead(BTN)); + Serial.println("BTN read as HIGH after pinMode INPUT_PULLUP"); + + waitForSyncAck(); // sync ack R1 + TEST_ASSERT_EQUAL(LOW, digitalRead(BTN)); + Serial.println("BTN read as LOW"); + + waitForSyncAck(); // sync ack R2 + TEST_ASSERT_EQUAL(HIGH, digitalRead(BTN)); + Serial.println("BTN read as HIGH"); +} + +void test_write_basic(void) { + pinMode(LED, OUTPUT); + Serial.println("GPIO LED set to OUTPUT"); + waitForSyncAck(); // sync ack W1 + + digitalWrite(LED, HIGH); + Serial.println("LED set to HIGH"); + waitForSyncAck(); // sync ack W2 + + digitalWrite(LED, LOW); + Serial.println("LED set to LOW"); +} + +void test_interrupt_attach_detach(void) { + pinMode(BTN, INPUT_PULLUP); + pinMode(LED, OUTPUT); + digitalWrite(LED, LOW); + + interruptCounter = 0; + attachInterrupt(digitalPinToInterrupt(BTN), buttonISR, FALLING); + Serial.println("Interrupt attached - FALLING edge"); + + for (int i = 1; i <= 3; i++) { + interruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(interruptFlag); + TEST_ASSERT_EQUAL(i, interruptCounter); + Serial.println(String(i) + " interrupt triggered successfully"); + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Interrupt detached"); + + interruptCounter = 0; + interruptFlag = false; + + TEST_ASSERT_FALSE(interruptFlag); + TEST_ASSERT_EQUAL(0, interruptCounter); + Serial.println("No interrupt triggered after detach"); + waitForSyncAck(); +} + +void test_interrupt_falling(void) { + pinMode(BTN, INPUT_PULLUP); + + interruptCounter = 0; + interruptFlag = false; + + attachInterrupt(digitalPinToInterrupt(BTN), buttonISR, FALLING); + Serial.println("Testing FALLING edge interrupt"); + + for (int i = 1; i <= 3; i++) { + interruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(interruptFlag); + TEST_ASSERT_EQUAL(i, interruptCounter); + Serial.println(String(i) + " FALLING edge interrupt worked"); + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Testing FALLING edge END"); + waitForSyncAck(); +} + +void test_interrupt_rising(void) { + pinMode(BTN, INPUT_PULLUP); + + interruptCounter = 0; + interruptFlag = false; + + attachInterrupt(digitalPinToInterrupt(BTN), buttonISR, RISING); + Serial.println("Testing RISING edge interrupt"); + + interruptCounter = 0; + interruptFlag = false; + + for (int i = 1; i <= 3; i++) { + interruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(interruptFlag); + TEST_ASSERT_EQUAL(i, interruptCounter); + Serial.println(String(i) + " RISING edge interrupt worked"); + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Testing RISING edge END"); + waitForSyncAck(); +} + +void test_interrupt_change(void) { + pinMode(BTN, INPUT_PULLUP); + + interruptCounter = 0; + interruptFlag = false; + + attachInterrupt(digitalPinToInterrupt(BTN), buttonISR, CHANGE); + Serial.println("Testing CHANGE edge interrupt"); + + for (int i = 1; i <= 6; i++) { + interruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(interruptFlag); + TEST_ASSERT_EQUAL(i, interruptCounter); + Serial.println(String(i) + " CHANGE edge interrupt worked"); + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Testing CHANGE edge END"); + waitForSyncAck(); +} + +void test_interrupt_with_arg(void) { + pinMode(BTN, INPUT_PULLUP); + + int argValue = 42; // Example argument to pass + interruptCounter = 0; + argInterruptFlag = false; + + attachInterruptArg(digitalPinToInterrupt(BTN), buttonISRWithArg, &argValue, FALLING); + Serial.println("Testing interrupt with argument"); + + for (int i = 1; i <= 3; i++) { + argInterruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(argInterruptFlag); + TEST_ASSERT_EQUAL(i, argInterruptCounter); + TEST_ASSERT_EQUAL(argValue, receivedArg); + Serial.println(String(i) + " interrupt with argument worked, received arg: " + String(receivedArg)); + ++argValue; + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Testing interrupt with argument END"); + waitForSyncAck(); +} + void setup() { Serial.begin(115200); - pinMode(BTN, INPUT_PULLUP); - test_button(); + while (!Serial) {} + + UNITY_BEGIN(); + + Serial.println("GPIO test START"); + RUN_TEST(test_read_basic); + RUN_TEST(test_write_basic); + + Serial.println("GPIO interrupt START"); + RUN_TEST(test_interrupt_attach_detach); + RUN_TEST(test_interrupt_falling); + RUN_TEST(test_interrupt_rising); + + RUN_TEST(test_interrupt_change); + RUN_TEST(test_interrupt_with_arg); + + UNITY_END(); + Serial.println("GPIO test END"); } void loop() {} diff --git a/tests/validation/gpio/scenario.yaml b/tests/validation/gpio/scenario.yaml deleted file mode 100644 index 957f58b2176..00000000000 --- a/tests/validation/gpio/scenario.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: Pushbutton counter test -version: 1 -author: Jan Prochazka (jan.prochazka@espressif.com) - -steps: - - wait-serial: "Button test" - - # Need for 1s delay for scenario to run properly - - delay: 5000ms - - # Press once - - set-control: - part-id: btn1 - control: pressed - value: 1 - - delay: 2000ms - - set-control: - part-id: btn1 - control: pressed - value: 0 - - delay: 3000ms - - # Press 2nd time - - set-control: - part-id: btn1 - control: pressed - value: 1 - - delay: 2000ms - - set-control: - part-id: btn1 - control: pressed - value: 0 - - delay: 3000ms - - # Press for the 3rd time - - set-control: - part-id: btn1 - control: pressed - value: 1 - - wait-serial: "Button pressed 3 times" diff --git a/tests/validation/gpio/test_gpio.py b/tests/validation/gpio/test_gpio.py index 42010ab520b..30f32562c88 100644 --- a/tests/validation/gpio/test_gpio.py +++ b/tests/validation/gpio/test_gpio.py @@ -1,17 +1,114 @@ import logging from pytest_embedded_wokwi import Wokwi from pytest_embedded import Dut +from time import sleep def test_gpio(dut: Dut, wokwi: Wokwi): LOGGER = logging.getLogger(__name__) - LOGGER.info("Waiting for Button test begin...") - dut.expect_exact("Button test") - - for i in range(3): - LOGGER.info(f"Setting button pressed for {i + 1} seconds") + def test_read_basic(): + dut.expect_exact("BTN read as HIGH after pinMode INPUT_PULLUP") wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write("OK\n") # Sync ack R1 + dut.expect_exact("BTN read as LOW") - dut.expect_exact(f"Button pressed {i + 1} times") wokwi.client.set_control("btn1", "pressed", 0) + wokwi.client.serial_write("OK\n") # Sync ack R2 + dut.expect_exact("BTN read as HIGH") + LOGGER.info("GPIO read basic test passed.") + + def test_write_basic(): + dut.expect_exact("GPIO LED set to OUTPUT") + assert wokwi.client.read_pin("led1", "A")["value"] == 0 # Anode pin + wokwi.client.serial_write("OK\n") # Sync ack W1 + + dut.expect_exact("LED set to HIGH") + assert wokwi.client.read_pin("led1", "A")["value"] == 1 + wokwi.client.serial_write("OK\n") # Sync ack W2 + + dut.expect_exact("LED set to LOW") + assert wokwi.client.read_pin("led1", "A")["value"] == 0 + LOGGER.info("GPIO write basic test passed.") + + def test_interrupt_attach_detach(): + dut.expect_exact("Interrupt attached - FALLING edge") + + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write(f"OK:{i}\n") + dut.expect_exact(f"{i} interrupt triggered successfully") + wokwi.client.set_control("btn1", "pressed", 0) + + dut.expect_exact("Interrupt detached") + wokwi.client.set_control("btn1", "pressed", 1) + sleep(0.1) + dut.expect_exact("No interrupt triggered after detach") + wokwi.client.set_control("btn1", "pressed", 0) + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt attach/detach test passed.") + + def test_interrupt_falling(): + dut.expect_exact("Testing FALLING edge interrupt") + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write(f"OK:{i}\n") + dut.expect_exact(f"{i} FALLING edge interrupt worked") + wokwi.client.set_control("btn1", "pressed", 0) + + dut.expect_exact("Testing FALLING edge END") + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt falling test passed.") + + def test_interrupt_rising(): + dut.expect_exact("Testing RISING edge interrupt") + + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.set_control("btn1", "pressed", 0) + wokwi.client.serial_write(f"OK:{i}\n") + dut.expect_exact(f"{i} RISING edge interrupt worked") + + dut.expect_exact("Testing RISING edge END") + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt rising test passed.") + + def test_interrupt_change(): + dut.expect_exact("Testing CHANGE edge interrupt") + + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write(f"OK:{i * 2 - 1}\n") + dut.expect_exact(f"{i * 2 - 1} CHANGE edge interrupt worked") + + wokwi.client.set_control("btn1", "pressed", 0) + wokwi.client.serial_write(f"OK:{i * 2}\n") + dut.expect_exact(f"{i * 2} CHANGE edge interrupt worked") + + dut.expect_exact("Testing CHANGE edge END") + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt change test passed.") + + def test_interrupt_with_arg(): + dut.expect_exact("Testing interrupt with argument") + + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write(f"OK:{i}\n") + dut.expect_exact(f"{i} interrupt with argument worked, received arg: {42 + i - 1}") + wokwi.client.set_control("btn1", "pressed", 0) + dut.expect_exact("Testing interrupt with argument END") + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt with argument test passed.") + + LOGGER.info("Waiting for GPIO test begin...") + dut.expect_exact("GPIO test START") + test_read_basic() + test_write_basic() + dut.expect_exact("GPIO interrupt START") + test_interrupt_attach_detach() + test_interrupt_falling() + test_interrupt_rising() + test_interrupt_change() + test_interrupt_with_arg() + LOGGER.info("GPIO test END")