diff --git a/MIGRATION.md b/MIGRATION.md
index 903d76126..88edea9fb 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -9,6 +9,23 @@ are listed here and a migration strategy is provided.
 
 ## Switch from version 2.4.0 (applications using the gpiod options affected)
 
+### NanoPi NEO adaptor was moved to friendlyelec folder
+
+With introduce of FriendlyELEC NanoPC-T6 a second adaptor from FriendlyELEC (formerly friendlarm) now exists. Please
+search and replace to change the import path as follows.
+
+```go
+// old
+...
+  "gobot.io/x/gobot/v2/platforms/nanopi"
+...
+
+// new
+...
+  "gobot.io/x/gobot/v2/platforms/friendlyelec/nanopi"
+...
+```
+
 ### The term gpiod was renamed to cdev
 
 Using the term "cdev" (short for character device Kernel ABI for GPIO access) is more suitable than using "gpiod" (the
diff --git a/README.md b/README.md
index 40846a9cf..ba94d5cb0 100644
--- a/README.md
+++ b/README.md
@@ -252,6 +252,8 @@ platforms are currently supported:
 - [DJI Tello](https://www.ryzerobotics.com/tello) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/dji/tello)
 - [DragonBoard](https://developer.qualcomm.com/hardware/dragonboard-410c) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/dragonboard)
 - [ESP8266](http://esp8266.net/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/firmata)
+- [FriendlyELEC NanoPi NEO](https://wiki.friendlyelec.com/wiki/index.php/NanoPi_NEO) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/friendlyelec/nanopi)
+- [FriendlyELEC NanoPC-T6](https://wiki.friendlyelec.com/wiki/index.php/NanoPC-T6) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/friendlyelec/nanopct6)
 - [GoPiGo 3](https://www.dexterindustries.com/gopigo3/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/dexter/gopigo3)
 - [Intel Curie](https://www.intel.com/content/www/us/en/products/boards-kits/curie.html) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/intel-iot/curie)
 - [Intel Edison](http://www.intel.com/content/www/us/en/do-it-yourself/edison.html) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/intel-iot/edison)
@@ -264,7 +266,6 @@ platforms are currently supported:
 - [MegaPi](http://www.makeblock.com/megapi) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/megapi)
 - [Microbit](http://microbit.org/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/microbit)
 - [MQTT](http://mqtt.org/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/mqtt)
-- [NanoPi NEO](https://wiki.friendlyelec.com/wiki/index.php/NanoPi_NEO) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/nanopi)
 - [NATS](http://nats.io/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/nats)
 - [Neurosky](http://neurosky.com/products-markets/eeg-biosensors/hardware/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/neurosky)
 - [OpenCV](http://opencv.org/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/opencv)
diff --git a/examples/edison_grove_blink.go b/examples/edison_grove_blink.go
deleted file mode 100644
index 54617b10a..000000000
--- a/examples/edison_grove_blink.go
+++ /dev/null
@@ -1,39 +0,0 @@
-//go:build example
-// +build example
-
-//
-// Do not build by default.
-
-package main
-
-import (
-	"fmt"
-	"time"
-
-	"gobot.io/x/gobot/v2"
-	"gobot.io/x/gobot/v2/drivers/gpio"
-	"gobot.io/x/gobot/v2/platforms/intel-iot/edison"
-)
-
-func main() {
-	e := edison.NewAdaptor()
-	led := gpio.NewLedDriver(e, "13")
-
-	work := func() {
-		gobot.Every(1*time.Second, func() {
-			if err := led.Toggle(); err != nil {
-				fmt.Println(err)
-			}
-		})
-	}
-
-	robot := gobot.NewRobot("blinkBot",
-		[]gobot.Connection{e},
-		[]gobot.Device{led},
-		work,
-	)
-
-	if err := robot.Start(); err != nil {
-		panic(err)
-	}
-}
diff --git a/examples/edison_grove_button.go b/examples/edison_grove_button.go
deleted file mode 100644
index 7a86dc963..000000000
--- a/examples/edison_grove_button.go
+++ /dev/null
@@ -1,40 +0,0 @@
-//go:build example
-// +build example
-
-//
-// Do not build by default.
-
-package main
-
-import (
-	"fmt"
-
-	"gobot.io/x/gobot/v2"
-	"gobot.io/x/gobot/v2/drivers/gpio"
-	"gobot.io/x/gobot/v2/platforms/intel-iot/edison"
-)
-
-func main() {
-	e := edison.NewAdaptor()
-	button := gpio.NewButtonDriver(e, "2")
-
-	work := func() {
-		_ = button.On(gpio.ButtonPush, func(data interface{}) {
-			fmt.Println("On!")
-		})
-
-		_ = button.On(gpio.ButtonRelease, func(data interface{}) {
-			fmt.Println("Off!")
-		})
-	}
-
-	robot := gobot.NewRobot("bot",
-		[]gobot.Connection{e},
-		[]gobot.Device{button},
-		work,
-	)
-
-	if err := robot.Start(); err != nil {
-		panic(err)
-	}
-}
diff --git a/examples/edison_grove_led.go b/examples/edison_grove_led.go
deleted file mode 100644
index 2c11fadbf..000000000
--- a/examples/edison_grove_led.go
+++ /dev/null
@@ -1,39 +0,0 @@
-//go:build example
-// +build example
-
-//
-// Do not build by default.
-
-package main
-
-import (
-	"fmt"
-	"time"
-
-	"gobot.io/x/gobot/v2"
-	"gobot.io/x/gobot/v2/drivers/gpio"
-	"gobot.io/x/gobot/v2/platforms/intel-iot/edison"
-)
-
-func main() {
-	e := edison.NewAdaptor()
-	led := gpio.NewLedDriver(e, "4")
-
-	work := func() {
-		gobot.Every(1*time.Second, func() {
-			if err := led.Toggle(); err != nil {
-				fmt.Println(err)
-			}
-		})
-	}
-
-	robot := gobot.NewRobot("blinkBot",
-		[]gobot.Connection{e},
-		[]gobot.Device{led},
-		work,
-	)
-
-	if err := robot.Start(); err != nil {
-		panic(err)
-	}
-}
diff --git a/examples/edison_grove_touch.go b/examples/edison_grove_touch.go
deleted file mode 100644
index c8b7f7632..000000000
--- a/examples/edison_grove_touch.go
+++ /dev/null
@@ -1,40 +0,0 @@
-//go:build example
-// +build example
-
-//
-// Do not build by default.
-
-package main
-
-import (
-	"fmt"
-
-	"gobot.io/x/gobot/v2"
-	"gobot.io/x/gobot/v2/drivers/gpio"
-	"gobot.io/x/gobot/v2/platforms/intel-iot/edison"
-)
-
-func main() {
-	e := edison.NewAdaptor()
-	touch := gpio.NewButtonDriver(e, "2")
-
-	work := func() {
-		_ = touch.On(gpio.ButtonPush, func(data interface{}) {
-			fmt.Println("On!")
-		})
-
-		_ = touch.On(gpio.ButtonRelease, func(data interface{}) {
-			fmt.Println("Off!")
-		})
-	}
-
-	robot := gobot.NewRobot("blinkBot",
-		[]gobot.Connection{e},
-		[]gobot.Device{touch},
-		work,
-	)
-
-	if err := robot.Start(); err != nil {
-		panic(err)
-	}
-}
diff --git a/examples/nanopct6_direct_pin.go b/examples/nanopct6_direct_pin.go
new file mode 100644
index 000000000..abf398572
--- /dev/null
+++ b/examples/nanopct6_direct_pin.go
@@ -0,0 +1,84 @@
+//go:build example
+// +build example
+
+//
+// Do not build by default.
+
+package main
+
+import (
+	"fmt"
+	"time"
+
+	"gobot.io/x/gobot/v2"
+	"gobot.io/x/gobot/v2/drivers/gpio"
+	"gobot.io/x/gobot/v2/platforms/adaptors"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopct6"
+)
+
+// Wiring
+// PWR   : 1, 17 (+3.3V, VCC), 2, 4 (+5V), 6, 9, 14, 20, 25, 30, 34, 39 (GND)
+// GPIO  : header pin 36 is input, pin 37 used as normal output, pin 38 used as inverted output
+// Button: the input pin is wired with a button to GND, the internal pull up resistor is used
+// LED's: the output pins are wired to the cathode of the LED, the anode is wired with a resistor (70-130Ohm for 20mA)
+// to VCC
+// Expected behavior: always one LED is on, the other in opposite state, if button is pressed for >2 seconds the state
+// changes
+func main() {
+	const (
+		inPinNum          = "36"
+		outPinNum         = "37"
+		outPinInvertedNum = "38"
+		debounceTime      = 2 * time.Second
+	)
+	// note: WithGpiosOpenDrain() is optional, if using WithGpiosOpenSource() the LED's will not light up
+	board := nanopct6.NewAdaptor(adaptors.WithGpiosActiveLow(outPinInvertedNum),
+		adaptors.WithGpiosOpenDrain(outPinNum, outPinInvertedNum),
+		adaptors.WithGpiosPullUp(inPinNum),
+		adaptors.WithGpioDebounce(inPinNum, debounceTime))
+
+	inPin := gpio.NewDirectPinDriver(board, inPinNum)
+	outPin := gpio.NewDirectPinDriver(board, outPinNum)
+	outPinInverted := gpio.NewDirectPinDriver(board, outPinInvertedNum)
+
+	work := func() {
+		level := byte(1)
+
+		gobot.Every(500*time.Millisecond, func() {
+			read, err := inPin.DigitalRead()
+			fmt.Printf("pin %s state is %d\n", inPinNum, read)
+			if err != nil {
+				fmt.Println(err)
+				if level == 1 {
+					level = 0
+				} else {
+					level = 1
+				}
+			} else {
+				level = byte(read)
+			}
+
+			err = outPin.DigitalWrite(level)
+			fmt.Printf("pin %s is now %d\n", outPinNum, level)
+			if err != nil {
+				fmt.Println(err)
+			}
+
+			err = outPinInverted.DigitalWrite(level)
+			fmt.Printf("pin %s is now not %d\n", outPinInvertedNum, level)
+			if err != nil {
+				fmt.Println(err)
+			}
+		})
+	}
+
+	robot := gobot.NewRobot("pinBot",
+		[]gobot.Connection{board},
+		[]gobot.Device{inPin, outPin, outPinInverted},
+		work,
+	)
+
+	if err := robot.Start(); err != nil {
+		panic(err)
+	}
+}
diff --git a/examples/nanopct6_ds18b20.go b/examples/nanopct6_ds18b20.go
new file mode 100644
index 000000000..0c76d51a4
--- /dev/null
+++ b/examples/nanopct6_ds18b20.go
@@ -0,0 +1,80 @@
+//go:build example
+// +build example
+
+//
+// Do not build by default.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"time"
+
+	"gobot.io/x/gobot/v2"
+	"gobot.io/x/gobot/v2/drivers/onewire"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopct6"
+)
+
+// Preparation: see /gobot/system/ONEWIRE.md
+//
+// Wiring:
+// PWR    : 1, 17 (+3.3V, VCC), 6, 9, 14, 20, 25, 30, 34, 39 (GND)
+// 1-wire : 16 (DQ) - resistor to VCC, ~1.5kOhm ... 5kOhm
+// DS18B20: 1 (GND), 2 (DQ), 3 (VDD, +3 ... 5.5V) for local power mode
+func main() {
+	adaptor := nanopct6.NewAdaptor()
+	// resolution change not supported by all devices
+	temp0 := onewire.NewDS18B20Driver(adaptor, 0xde5e710a6461, onewire.WithResolution(12))
+	temp1 := onewire.NewDS18B20Driver(adaptor, 0x1e40710a6461, onewire.WithFahrenheit(), onewire.WithConversionTime(500))
+
+	work := func() {
+		time0, err := temp0.ConversionTime()
+		if err != nil {
+			log.Printf("Err CT0: %v\n", err)
+		}
+		res0, err := temp0.Resolution()
+		if err != nil {
+			log.Printf("Err R0: %v\n", err)
+		}
+		log.Printf("Conversion time @%d bit for Temp 0: %d ms\n", res0, time0)
+
+		time1, err := temp1.ConversionTime()
+		if err != nil {
+			log.Printf("Err CT1: %v\n", err)
+		}
+		res1, err := temp1.Resolution()
+		if err != nil {
+			log.Printf("Err R1: %v\n", err)
+		}
+		log.Printf("Conversion time @%d bit for Temp 0: %d ms\n", res1, time1)
+
+		gobot.Every(10*(time.Duration(time0))*time.Millisecond, func() {
+			t0, err := temp0.Temperature()
+			if err != nil {
+				log.Printf("Err Temp 0: %v\n", err)
+			}
+
+			fmt.Printf("Temp 0: %2.1f °C\n", t0)
+		})
+
+		gobot.Every(10*(time.Duration(time1))*time.Millisecond, func() {
+			t1, err := temp1.Temperature()
+			if err != nil {
+				log.Printf("Err Temp 1:  %v\n", err)
+			}
+
+			fmt.Printf("Temp 1: %2.3f °F\n", t1)
+		})
+	}
+
+	robot := gobot.NewRobot("onewireBot",
+		[]gobot.Connection{adaptor},
+		[]gobot.Device{temp0, temp1},
+		work,
+	)
+
+	if err := robot.Start(); err != nil {
+		panic(err)
+	}
+}
diff --git a/examples/nanopct6_servo.go b/examples/nanopct6_servo.go
new file mode 100644
index 000000000..37b5e499e
--- /dev/null
+++ b/examples/nanopct6_servo.go
@@ -0,0 +1,93 @@
+//go:build example
+// +build example
+
+// Do not build by default.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"time"
+
+	"gobot.io/x/gobot/v2"
+	"gobot.io/x/gobot/v2/drivers/gpio"
+	"gobot.io/x/gobot/v2/platforms/adaptors"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopct6"
+)
+
+// Wiring
+// PWR: 1, 17 (+3.3V, VCC), 2, 4 (+5V), 6, 9, 14, 20, 25, 30, 34, 39 (GND)
+// PWM: header pin 11 (pwm14-m0), 13 (pwm15-m0), 29 (pwm12-m0), 31 (pwm13-m0), 35 (pwm10-m0)
+// Servo SG90: red (+5V), brown (GND), orange (PWM)
+func main() {
+	const (
+		pwmPin = "35"
+		wait   = 3 * time.Second
+
+		fiftyHzNanos = 20 * 1000 * 1000 // 50Hz = 0.02 sec = 20 ms
+	)
+	// usually a frequency of 50Hz is used for servos, most servos have 0.5 ms..2.5 ms for 0-180°,
+	// however the mapping can be changed with options:
+	adaptor := nanopct6.NewAdaptor(
+		adaptors.WithPWMDefaultPeriodForPin(pwmPin, fiftyHzNanos),
+		adaptors.WithPWMServoDutyCycleRangeForPin(pwmPin, 500*time.Microsecond, 2500*time.Microsecond),
+		adaptors.WithPWMServoAngleRangeForPin(pwmPin, 0, 180),
+	)
+	servo := gpio.NewServoDriver(adaptor, pwmPin)
+
+	work := func() {
+		fmt.Printf("first move to minimal position for %s...\n", wait)
+		if err := servo.ToMin(); err != nil {
+			log.Println(err)
+		}
+
+		time.Sleep(wait)
+
+		fmt.Printf("second move to center position for %s...\n", wait)
+		if err := servo.ToCenter(); err != nil {
+			log.Println(err)
+		}
+
+		time.Sleep(wait)
+
+		fmt.Printf("third move to maximal position for %s...\n", wait)
+		if err := servo.ToMax(); err != nil {
+			log.Println(err)
+		}
+
+		time.Sleep(wait)
+
+		fmt.Println("finally move 0-180° (or what your servo do for the new mapping) and back forever...")
+		angle := 0
+		fadeAmount := 45
+
+		gobot.Every(time.Second, func() {
+			if err := servo.Move(byte(angle)); err != nil {
+				log.Println(err)
+			}
+			angle = angle + fadeAmount
+			if angle < 0 || angle > 180 {
+				if angle < 0 {
+					angle = 0
+				}
+				if angle > 180 {
+					angle = 180
+				}
+				// change direction and recalculate
+				fadeAmount = -fadeAmount
+				angle = angle + fadeAmount
+			}
+		})
+	}
+
+	robot := gobot.NewRobot("motorBot",
+		[]gobot.Connection{adaptor},
+		[]gobot.Device{servo},
+		work,
+	)
+
+	if err := robot.Start(); err != nil {
+		panic(err)
+	}
+}
diff --git a/examples/nanopct6_thermalzone.go b/examples/nanopct6_thermalzone.go
new file mode 100644
index 000000000..337f980c6
--- /dev/null
+++ b/examples/nanopct6_thermalzone.go
@@ -0,0 +1,50 @@
+//go:build example
+// +build example
+
+//
+// Do not build by default.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"time"
+
+	"gobot.io/x/gobot/v2"
+	"gobot.io/x/gobot/v2/drivers/aio"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopct6"
+)
+
+// Wiring: no wiring needed
+func main() {
+	adaptor := nanopct6.NewAdaptor()
+	therm0 := aio.NewThermalZoneDriver(adaptor, "soc_thermal")
+	therm1 := aio.NewThermalZoneDriver(adaptor, "npu_thermal", aio.WithFahrenheit())
+
+	work := func() {
+		gobot.Every(500*time.Millisecond, func() {
+			t0, err := therm0.Read()
+			if err != nil {
+				log.Println(err)
+			}
+
+			t1, err := therm1.Read()
+			if err != nil {
+				log.Println(err)
+			}
+
+			fmt.Printf("SOC: %2.3f °C, NPU: %2.3f °F\n", t0, t1)
+		})
+	}
+
+	robot := gobot.NewRobot("thermalBot",
+		[]gobot.Connection{adaptor},
+		[]gobot.Device{therm0, therm1},
+		work,
+	)
+
+	if err := robot.Start(); err != nil {
+		panic(err)
+	}
+}
diff --git a/examples/nanopct6_yl40.go b/examples/nanopct6_yl40.go
new file mode 100644
index 000000000..2695eb145
--- /dev/null
+++ b/examples/nanopct6_yl40.go
@@ -0,0 +1,80 @@
+//go:build example
+// +build example
+
+//
+// Do not build by default.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"time"
+
+	"gobot.io/x/gobot/v2"
+	"gobot.io/x/gobot/v2/drivers/i2c"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopct6"
+)
+
+func main() {
+	// Wiring
+	// PWR : 1, 17 (+3.3V, VCC), 6, 9, 14, 20, 25, 30, 34, 39 (GND)
+	// I2C8: 3 (SDA), 5 (SCL)
+	// YL-40 module: wire AOUT --> AIN2 for this example, set all jumpers for temp, LDR and variable resistor
+	//
+	// Note: temperature measurement is often buggy, because sensor is not properly grounded
+	//       fix it by soldering a small bridge to the adjacent ground pin of brightness sensor
+	board := nanopct6.NewAdaptor()
+	yl := i2c.NewYL40Driver(board, i2c.WithBus(8))
+
+	work := func() {
+		// the LED light is visible above ~1.7V
+		writeVal, _ := yl.AOUT()
+
+		gobot.Every(1000*time.Millisecond, func() {
+			if err := yl.Write(writeVal); err != nil {
+				fmt.Println(err)
+			} else {
+				log.Printf(" %.1f V written", writeVal)
+				writeVal = writeVal + 0.1
+				if writeVal > 3.3 {
+					writeVal = 0
+				}
+			}
+
+			if brightness, err := yl.ReadBrightness(); err != nil {
+				fmt.Println(err)
+			} else {
+				log.Printf("Brightness: %.0f [0..1000]", brightness)
+			}
+
+			if temperature, err := yl.ReadTemperature(); err != nil {
+				fmt.Println(err)
+			} else {
+				log.Printf("Temperature: %.1f °C", temperature)
+			}
+
+			if ain2, err := yl.ReadAIN2(); err != nil {
+				fmt.Println(err)
+			} else {
+				log.Printf("Read back AOUT: %.1f [0..3.3]", ain2)
+			}
+
+			if potiState, err := yl.ReadPotentiometer(); err != nil {
+				fmt.Println(err)
+			} else {
+				log.Printf("Resistor: %.0f %% [-100..+100]", potiState)
+			}
+		})
+	}
+
+	robot := gobot.NewRobot("yl40Bot",
+		[]gobot.Connection{board},
+		[]gobot.Device{yl},
+		work,
+	)
+
+	if err := robot.Start(); err != nil {
+		panic(err)
+	}
+}
diff --git a/examples/nanopi_button.go b/examples/nanopi_button.go
index 7ea2d6da5..11302e62d 100644
--- a/examples/nanopi_button.go
+++ b/examples/nanopi_button.go
@@ -12,7 +12,7 @@ import (
 	"gobot.io/x/gobot/v2"
 	"gobot.io/x/gobot/v2/drivers/gpio"
 	"gobot.io/x/gobot/v2/platforms/adaptors"
-	"gobot.io/x/gobot/v2/platforms/nanopi"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopi"
 )
 
 // PWR  NanoPi: 1, 17 (+3.3V, VCC); 2, 4 (+5V, VDD); 6, 9, 14, 20 (GND)
diff --git a/examples/nanopi_direct_pin.go b/examples/nanopi_direct_pin.go
index ce7da612c..565735854 100644
--- a/examples/nanopi_direct_pin.go
+++ b/examples/nanopi_direct_pin.go
@@ -13,7 +13,7 @@ import (
 	"gobot.io/x/gobot/v2"
 	"gobot.io/x/gobot/v2/drivers/gpio"
 	"gobot.io/x/gobot/v2/platforms/adaptors"
-	"gobot.io/x/gobot/v2/platforms/nanopi"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopi"
 )
 
 // Wiring
diff --git a/examples/nanopi_direct_pin_event.go b/examples/nanopi_direct_pin_event.go
index 8a898a4b2..c2b61fdbd 100644
--- a/examples/nanopi_direct_pin_event.go
+++ b/examples/nanopi_direct_pin_event.go
@@ -11,7 +11,7 @@ import (
 	"time"
 
 	"gobot.io/x/gobot/v2"
-	"gobot.io/x/gobot/v2/platforms/nanopi"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopi"
 	"gobot.io/x/gobot/v2/system"
 )
 
diff --git a/examples/nanopi_led_brightness.go b/examples/nanopi_led_brightness.go
index d9ad9ac77..e64042ba7 100644
--- a/examples/nanopi_led_brightness.go
+++ b/examples/nanopi_led_brightness.go
@@ -12,7 +12,7 @@ import (
 
 	"gobot.io/x/gobot/v2"
 	"gobot.io/x/gobot/v2/drivers/gpio"
-	"gobot.io/x/gobot/v2/platforms/nanopi"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopi"
 )
 
 // Wiring
diff --git a/examples/nanopi_pca9533.go b/examples/nanopi_pca9533.go
index 844f75cea..46edcb638 100644
--- a/examples/nanopi_pca9533.go
+++ b/examples/nanopi_pca9533.go
@@ -12,7 +12,7 @@ import (
 
 	"gobot.io/x/gobot/v2"
 	"gobot.io/x/gobot/v2/drivers/i2c"
-	"gobot.io/x/gobot/v2/platforms/nanopi"
+	"gobot.io/x/gobot/v2/platforms/friendlyelec/nanopi"
 )
 
 // Wiring
diff --git a/platforms/friendlyelec/nanopct6/LICENSE b/platforms/friendlyelec/nanopct6/LICENSE
new file mode 100644
index 000000000..f4448d985
--- /dev/null
+++ b/platforms/friendlyelec/nanopct6/LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2025 The Hybrid Group
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/platforms/friendlyelec/nanopct6/README.md b/platforms/friendlyelec/nanopct6/README.md
new file mode 100644
index 000000000..3db8f6f2f
--- /dev/null
+++ b/platforms/friendlyelec/nanopct6/README.md
@@ -0,0 +1,67 @@
+# FriendlyELEC NanoPC-T6
+
+The FriendlyELEC NanoPC-T6 is a single board SoC computer based on the Rockchip RK3588 arm64 processor. It has built-in
+GPIO, I2C, PWM, SPI, 1-Wire, MIPI CSI and MIPI DSI interfaces.
+
+For more info about the FriendlyELEC NanoPC-T6, go to [https://wiki.friendlyelec.com/wiki/index.php/NanoPC-T6](https://wiki.friendlyelec.com/wiki/index.php/NanoPC-T6).
+
+## How to Install
+
+Please refer to the main [README.md](https://github.com/hybridgroup/gobot/blob/release/README.md)
+
+Tested OS:
+
+* [armbian](https://www.armbian.com/nanopct6/) with "Armbian 24.11.1 Bookworm Minimal / IOT"
+
+## Configuration steps for the OS
+
+### System access and configuration basics
+
+Please follow the instructions of the OS provider. A ssh access is used in this guide.
+
+```sh
+ssh <user>@192.168.1.xxx
+```
+
+### Enabling hardware drivers
+
+Not all drivers are enabled by default. You can have a look at the configuration file, to find out what is enabled at
+your system:
+
+```sh
+cat /boot/armbianEnv.txt
+```
+
+```sh
+sudo apt install armbian-config
+sudo armbian-config
+```
+
+## How to Use
+
+The pin numbering used by your Gobot program should match the way your board is labeled right on the board itself.
+
+```go
+r := nanopct6.NewAdaptor()
+led := gpio.NewLedDriver(r, "7")
+```
+
+## How to Connect
+
+### Compiling
+
+Compile your Gobot program on your workstation like this:
+
+```sh
+GOARCH=arm64 GOOS=linux go build -o output/ examples/nanopct6_blink.go
+```
+
+Once you have compiled your code, you can upload your program and execute it on the board from your workstation
+using the `scp` and `ssh` commands like this:
+
+```sh
+scp nanopct6_blink <user>@192.168.1.xxx:~
+ssh -t <user>@192.168.1.xxx "./nanopct6_blink"
+```
+
+## Troubleshooting
diff --git a/platforms/friendlyelec/nanopct6/adaptor.go b/platforms/friendlyelec/nanopct6/adaptor.go
new file mode 100644
index 000000000..6248e3472
--- /dev/null
+++ b/platforms/friendlyelec/nanopct6/adaptor.go
@@ -0,0 +1,161 @@
+package nanopct6
+
+import (
+	"fmt"
+	"sync"
+
+	multierror "github.com/hashicorp/go-multierror"
+
+	"gobot.io/x/gobot/v2"
+	"gobot.io/x/gobot/v2/platforms/adaptors"
+	"gobot.io/x/gobot/v2/system"
+)
+
+const (
+	defaultI2cBusNumber = 8 // on 40 pin header
+
+	defaultSpiBusNumber  = 0
+	defaultSpiChipNumber = 0
+	defaultSpiMode       = 0
+	defaultSpiBitsNumber = 8
+	defaultSpiMaxSpeed   = 500000
+)
+
+// Adaptor represents a Gobot Adaptor for the FriendlyELEC NanoPC-T6
+type Adaptor struct {
+	name  string
+	sys   *system.Accesser // used for unit tests only
+	mutex *sync.Mutex
+	*adaptors.AnalogPinsAdaptor
+	*adaptors.DigitalPinsAdaptor
+	*adaptors.PWMPinsAdaptor
+	*adaptors.I2cBusAdaptor
+	*adaptors.SpiBusAdaptor
+	*adaptors.OneWireBusAdaptor
+}
+
+// NewAdaptor creates a NanoPC-T6 Adaptor
+//
+// Optional parameters:
+//
+//	adaptors.WithGpioSysfsAccess():	use legacy sysfs driver instead of default character device driver
+//	adaptors.WithSpiGpioAccess(sclk, ncs, sdo, sdi):	use GPIO's instead of /dev/spidev#.#
+//	adaptors.WithGpiosActiveLow(pin's): invert the pin behavior
+//	adaptors.WithGpiosPullUp/Down(pin's): sets the internal pull resistor
+//	adaptors.WithGpiosOpenDrain/Source(pin's): sets the output behavior
+//	adaptors.WithGpioDebounce(pin, period): sets the input debouncer
+//	adaptors.WithGpioEventOnFallingEdge/RaisingEdge/BothEdges(pin, handler): activate edge detection
+//
+//	Optional parameters for PWM, see [adaptors.NewPWMPinsAdaptor]
+func NewAdaptor(opts ...interface{}) *Adaptor {
+	sys := system.NewAccesser()
+	a := &Adaptor{
+		name:  gobot.DefaultName("NanoPC-T6"),
+		sys:   sys,
+		mutex: &sync.Mutex{},
+	}
+
+	var digitalPinsOpts []adaptors.DigitalPinsOptionApplier
+	var pwmPinsOpts []adaptors.PwmPinsOptionApplier
+	var spiBusOpts []adaptors.SpiBusOptionApplier
+	for _, opt := range opts {
+		switch o := opt.(type) {
+		case adaptors.DigitalPinsOptionApplier:
+			digitalPinsOpts = append(digitalPinsOpts, o)
+		case adaptors.PwmPinsOptionApplier:
+			pwmPinsOpts = append(pwmPinsOpts, o)
+		case adaptors.SpiBusOptionApplier:
+			spiBusOpts = append(spiBusOpts, o)
+		default:
+			panic(fmt.Sprintf("'%s' can not be applied on adaptor '%s'", opt, a.name))
+		}
+	}
+
+	analogPinTranslator := adaptors.NewAnalogPinTranslator(sys, analogPinDefinitions)
+	digitalPinTranslator := adaptors.NewDigitalPinTranslator(sys, gpioPinDefinitions)
+	pwmPinTranslator := adaptors.NewPWMPinTranslator(sys, pwmPinDefinitions)
+	// Valid bus numbers are [3,4,5,7,8] which corresponds to /dev/i2c-3, /dev/i2c-4 ...
+	// needs to be enabled by DT-overlay: i2c3-m0, i2c4-m3, i2c5-m0, i2c8-m2
+	// i2c7-m0 maybe shared with sound, so 0x?? is in use
+	// We don't support /dev/i2c-0 (voltage regulator), /dev/i2c-1 (?), /dev/i2c-2 (voltage regulator),
+	// /dev/i2c-6 (RTC, USB-C, EEPROM 24c02), /dev/i2c-9 (ddc), /dev/i2c-10 (ddc), /dev/i2c-11 (fde50000.dp).
+	i2cBusNumberValidator := adaptors.NewBusNumberValidator([]int{3, 4, 5, 7, 8})
+	// Valid bus numbers are [0,4] which corresponds to /dev/spidev0.x, /dev/spidev4.x
+	// x is the chip number <255
+	spiBusNumberValidator := adaptors.NewBusNumberValidator([]int{0, 4})
+
+	a.AnalogPinsAdaptor = adaptors.NewAnalogPinsAdaptor(sys, analogPinTranslator.Translate)
+	a.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, digitalPinTranslator.Translate, digitalPinsOpts...)
+	a.PWMPinsAdaptor = adaptors.NewPWMPinsAdaptor(sys, pwmPinTranslator.Translate, pwmPinsOpts...)
+	a.I2cBusAdaptor = adaptors.NewI2cBusAdaptor(sys, i2cBusNumberValidator.Validate, defaultI2cBusNumber)
+	a.SpiBusAdaptor = adaptors.NewSpiBusAdaptor(sys, spiBusNumberValidator.Validate, defaultSpiBusNumber,
+		defaultSpiChipNumber, defaultSpiMode, defaultSpiBitsNumber, defaultSpiMaxSpeed, a.DigitalPinsAdaptor, spiBusOpts...)
+	// pin 16 needs to be activated by DT-overlay w1-gpio3-b3
+	a.OneWireBusAdaptor = adaptors.NewOneWireBusAdaptor(sys)
+
+	return a
+}
+
+// Name returns the name of the Adaptor
+func (a *Adaptor) Name() string { return a.name }
+
+// SetName sets the name of the Adaptor
+func (a *Adaptor) SetName(n string) { a.name = n }
+
+// Connect create new connection to board and pins.
+func (a *Adaptor) Connect() error {
+	a.mutex.Lock()
+	defer a.mutex.Unlock()
+
+	if err := a.OneWireBusAdaptor.Connect(); err != nil {
+		return err
+	}
+
+	if err := a.SpiBusAdaptor.Connect(); err != nil {
+		return err
+	}
+
+	if err := a.I2cBusAdaptor.Connect(); err != nil {
+		return err
+	}
+
+	if err := a.AnalogPinsAdaptor.Connect(); err != nil {
+		return err
+	}
+
+	if err := a.PWMPinsAdaptor.Connect(); err != nil {
+		return err
+	}
+
+	return a.DigitalPinsAdaptor.Connect()
+}
+
+// Finalize closes connection to board, pins and bus
+func (a *Adaptor) Finalize() error {
+	a.mutex.Lock()
+	defer a.mutex.Unlock()
+
+	err := a.DigitalPinsAdaptor.Finalize()
+
+	if e := a.PWMPinsAdaptor.Finalize(); e != nil {
+		err = multierror.Append(err, e)
+	}
+
+	if e := a.AnalogPinsAdaptor.Finalize(); e != nil {
+		err = multierror.Append(err, e)
+	}
+
+	if e := a.I2cBusAdaptor.Finalize(); e != nil {
+		err = multierror.Append(err, e)
+	}
+
+	if e := a.SpiBusAdaptor.Finalize(); e != nil {
+		err = multierror.Append(err, e)
+	}
+
+	if e := a.OneWireBusAdaptor.Finalize(); e != nil {
+		err = multierror.Append(err, e)
+	}
+
+	return err
+}
diff --git a/platforms/friendlyelec/nanopct6/adaptor_test.go b/platforms/friendlyelec/nanopct6/adaptor_test.go
new file mode 100644
index 000000000..def6b3b12
--- /dev/null
+++ b/platforms/friendlyelec/nanopct6/adaptor_test.go
@@ -0,0 +1,224 @@
+package nanopct6
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"gobot.io/x/gobot/v2"
+	"gobot.io/x/gobot/v2/drivers/aio"
+	"gobot.io/x/gobot/v2/drivers/gpio"
+	"gobot.io/x/gobot/v2/drivers/i2c"
+	"gobot.io/x/gobot/v2/platforms/adaptors"
+	"gobot.io/x/gobot/v2/system"
+)
+
+const (
+	pwmDir           = "/sys/devices/platform/febf0030.pwm/pwm/pwmchip7/" //nolint:gosec // false positive
+	pwmExportPath    = pwmDir + "export"
+	pwmUnexportPath  = pwmDir + "unexport"
+	pwmPwmDir        = pwmDir + "pwm0/"
+	pwmEnablePath    = pwmPwmDir + "enable"
+	pwmPeriodPath    = pwmPwmDir + "period"
+	pwmDutyCyclePath = pwmPwmDir + "duty_cycle"
+	pwmPolarityPath  = pwmPwmDir + "polarity"
+
+	pwmInvertedIdentifier = "inversed"
+)
+
+var pwmMockPaths = []string{
+	pwmExportPath,
+	pwmUnexportPath,
+	pwmEnablePath,
+	pwmPeriodPath,
+	pwmDutyCyclePath,
+	pwmPolarityPath,
+}
+
+// make sure that this Adaptor fulfills all the required interfaces
+var (
+	_ gobot.Adaptor               = (*Adaptor)(nil)
+	_ gobot.DigitalPinnerProvider = (*Adaptor)(nil)
+	_ gobot.PWMPinnerProvider     = (*Adaptor)(nil)
+	_ gpio.DigitalReader          = (*Adaptor)(nil)
+	_ gpio.DigitalWriter          = (*Adaptor)(nil)
+	_ aio.AnalogReader            = (*Adaptor)(nil)
+	_ i2c.Connector               = (*Adaptor)(nil)
+)
+
+func preparePwmFs(fs *system.MockFilesystem) {
+	fs.Files[pwmEnablePath].Contents = "0"
+	fs.Files[pwmPeriodPath].Contents = "0"
+	fs.Files[pwmDutyCyclePath].Contents = "0"
+	fs.Files[pwmPolarityPath].Contents = pwmInvertedIdentifier
+}
+
+func initConnectedTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system.MockFilesystem) {
+	a := initConnectedTestAdaptor()
+	fs := a.sys.UseMockFilesystem(mockPaths)
+	return a, fs
+}
+
+func initConnectedTestAdaptor() *Adaptor {
+	a := NewAdaptor()
+	if err := a.Connect(); err != nil {
+		panic(err)
+	}
+	return a
+}
+
+func TestNewAdaptor(t *testing.T) {
+	// arrange & act
+	a := NewAdaptor()
+	// assert
+	assert.IsType(t, &Adaptor{}, a)
+	assert.True(t, strings.HasPrefix(a.Name(), "NanoPC-T6"))
+	assert.NotNil(t, a.sys)
+	assert.NotNil(t, a.mutex)
+	assert.NotNil(t, a.AnalogPinsAdaptor)
+	assert.NotNil(t, a.DigitalPinsAdaptor)
+	assert.NotNil(t, a.PWMPinsAdaptor)
+	assert.NotNil(t, a.I2cBusAdaptor)
+	assert.NotNil(t, a.SpiBusAdaptor)
+	assert.True(t, a.sys.HasDigitalPinCdevAccess())
+	// act & assert
+	a.SetName("NewName")
+	assert.Equal(t, "NewName", a.Name())
+}
+
+func TestNewAdaptorWithOption(t *testing.T) {
+	// arrange & act
+	a := NewAdaptor(adaptors.WithGpiosActiveLow("1"), adaptors.WithGpioSysfsAccess())
+	// assert
+	require.NoError(t, a.Connect())
+	assert.True(t, a.sys.HasDigitalPinSysfsAccess())
+}
+
+func TestDigitalIO(t *testing.T) {
+	// some basic tests, further tests are done in "digitalpinsadaptor.go"
+	// arrange
+	a := initConnectedTestAdaptor()
+	dpa := a.sys.UseMockDigitalPinAccess()
+	require.True(t, a.sys.HasDigitalPinCdevAccess())
+	// act & assert write
+	err := a.DigitalWrite("7", 1)
+	require.NoError(t, err)
+	assert.Equal(t, []int{1}, dpa.Written("gpiochip3", "10"))
+	// arrange, act & assert read
+	dpa.UseValues("gpiochip0", "20", []int{3})
+	i, err := a.DigitalRead("10")
+	require.NoError(t, err)
+	assert.Equal(t, 3, i)
+	// act and assert unknown pin
+	require.ErrorContains(t, a.DigitalWrite("99", 1), "'99' is not a valid id for a digital pin")
+	// act and assert finalize
+	require.NoError(t, a.Finalize())
+	assert.Equal(t, 0, dpa.Exported("gpiochip3", "10"))
+	assert.Equal(t, 0, dpa.Exported("gpiochip0", "20"))
+}
+
+func TestDigitalIOSysfs(t *testing.T) {
+	// some basic tests, further tests are done in "digitalpinsadaptor.go"
+	// arrange
+	a := NewAdaptor(adaptors.WithGpioSysfsAccess())
+	require.NoError(t, a.Connect())
+	dpa := a.sys.UseMockDigitalPinAccess()
+	require.True(t, a.sys.HasDigitalPinSysfsAccess())
+	// act & assert write
+	err := a.DigitalWrite("7", 1)
+	require.NoError(t, err)
+	assert.Equal(t, []int{1}, dpa.Written("", "106"))
+	// arrange, act & assert read
+	dpa.UseValues("", "20", []int{4})
+	i, err := a.DigitalRead("10")
+	require.NoError(t, err)
+	assert.Equal(t, 4, i)
+	// act and assert unknown pin
+	require.ErrorContains(t, a.DigitalWrite("99", 1), "'99' is not a valid id for a digital pin")
+	// act and assert finalize
+	require.NoError(t, a.Finalize())
+	assert.Equal(t, 0, dpa.Exported("", "106"))
+	assert.Equal(t, 0, dpa.Exported("", "20"))
+}
+
+func TestAnalogRead(t *testing.T) {
+	mockPaths := []string{
+		"/sys/class/thermal/thermal_zone0/temp",
+	}
+
+	a, fs := initConnectedTestAdaptorWithMockedFilesystem(mockPaths)
+
+	fs.Files["/sys/class/thermal/thermal_zone0/temp"].Contents = "567\n"
+	got, err := a.AnalogRead("soc_thermal")
+	require.NoError(t, err)
+	assert.Equal(t, 567, got)
+
+	_, err = a.AnalogRead("thermal_zone10")
+	require.ErrorContains(t, err, "'thermal_zone10' is not a valid id for an analog pin")
+
+	fs.WithReadError = true
+	_, err = a.AnalogRead("soc_thermal")
+	require.ErrorContains(t, err, "read error")
+	fs.WithReadError = false
+
+	require.NoError(t, a.Finalize())
+}
+
+func TestFinalizeErrorAfterGPIO(t *testing.T) {
+	// arrange
+	a := initConnectedTestAdaptor()
+	dpa := a.sys.UseMockDigitalPinAccess()
+	require.True(t, a.sys.HasDigitalPinCdevAccess())
+	require.NoError(t, a.DigitalWrite("7", 1))
+	dpa.UseUnexportError("gpiochip3", "10")
+	// act
+	err := a.Finalize()
+	// assert
+	require.ErrorContains(t, err, "unexport error")
+}
+
+func TestFinalizeErrorAfterPWM(t *testing.T) {
+	// indirect test for PWM.Finalize() is called for the adaptor
+	// arrange
+	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
+	preparePwmFs(fs)
+	require.NoError(t, a.PwmWrite("13", 1))
+	fs.WithWriteError = true
+	// act
+	err := a.Finalize()
+	// assert
+	require.ErrorContains(t, err, "write error")
+}
+
+func TestSpiDefaultValues(t *testing.T) {
+	a := NewAdaptor()
+
+	assert.Equal(t, 0, a.SpiDefaultBusNumber())
+	assert.Equal(t, 0, a.SpiDefaultChipNumber())
+	assert.Equal(t, 0, a.SpiDefaultMode())
+	assert.Equal(t, 8, a.SpiDefaultBitCount())
+	assert.Equal(t, int64(500000), a.SpiDefaultMaxSpeed())
+}
+
+func TestI2cDefaultBus(t *testing.T) {
+	a := NewAdaptor()
+	assert.Equal(t, 8, a.DefaultI2cBus())
+}
+
+func TestI2cFinalizeWithErrors(t *testing.T) {
+	// arrange
+	a := initConnectedTestAdaptor()
+	a.sys.UseMockSyscall()
+	fs := a.sys.UseMockFilesystem([]string{"/dev/i2c-4"})
+	con, err := a.GetI2cConnection(0xff, 4)
+	require.NoError(t, err)
+	_, err = con.Write([]byte{0xbf})
+	require.NoError(t, err)
+	fs.WithCloseError = true
+	// act
+	err = a.Finalize()
+	// assert
+	require.ErrorContains(t, err, "close error")
+}
diff --git a/platforms/friendlyelec/nanopct6/doc.go b/platforms/friendlyelec/nanopct6/doc.go
new file mode 100644
index 000000000..9a76ecb33
--- /dev/null
+++ b/platforms/friendlyelec/nanopct6/doc.go
@@ -0,0 +1,7 @@
+/*
+Package nanopct6 contains the Gobot adaptor for the FriendlyELEC NanoPC-T6.
+
+For further information refer to the boards README:
+https://github.com/hybridgroup/gobot/blob/release/platforms/friendlyelec/nanopct6/README.md
+*/
+package nanopct6 // import "gobot.io/x/gobot/v2/platforms/friendlyelec/nanopct6"
diff --git a/platforms/friendlyelec/nanopct6/pin_map.go b/platforms/friendlyelec/nanopct6/pin_map.go
new file mode 100644
index 000000000..34222d36a
--- /dev/null
+++ b/platforms/friendlyelec/nanopct6/pin_map.go
@@ -0,0 +1,85 @@
+package nanopct6
+
+import "gobot.io/x/gobot/v2/platforms/adaptors"
+
+// notes for character device
+// sysfs: Chip*32 + (A=0, B=8, C=16) + Nr
+// tested with cdev on a NanoPC-T6 2301 board: armbian Linux, OK: works, ?: unknown, NOK: not working
+// IN: works only as input, PU: if used as input, external pullup resistor needed
+var gpioPinDefinitions = adaptors.DigitalPinDefinitions{
+	"10":      {Sysfs: 20, Cdev: adaptors.CdevPin{Chip: 0, Line: 20}},  // GPIO0_C4_UART0_RX_M0 - OK
+	"8":       {Sysfs: 21, Cdev: adaptors.CdevPin{Chip: 0, Line: 21}},  // GPIO0_C5_UART0_TX_M0_PWM4_M0 - OK
+	"32":      {Sysfs: 22, Cdev: adaptors.CdevPin{Chip: 0, Line: 22}},  // GPIO0_C6_PWM5_M1 - OK
+	"27":      {Sysfs: 32, Cdev: adaptors.CdevPin{Chip: 1, Line: 0}},   // GPIO1_A0_UART6_RX_M1 - OK
+	"28":      {Sysfs: 33, Cdev: adaptors.CdevPin{Chip: 1, Line: 1}},   // GPIO1_A1_UART6_TX_M1 - OK (UP)
+	"15":      {Sysfs: 39, Cdev: adaptors.CdevPin{Chip: 1, Line: 7}},   // GPIO1_A7 - OK
+	"26":      {Sysfs: 40, Cdev: adaptors.CdevPin{Chip: 1, Line: 8}},   // GPIO1_B0 - OK
+	"21":      {Sysfs: 41, Cdev: adaptors.CdevPin{Chip: 1, Line: 9}},   // GPIO1_B1_SPI0_MISO_M2 - OK (UP)
+	"19":      {Sysfs: 42, Cdev: adaptors.CdevPin{Chip: 1, Line: 10}},  // GPIO1_B2_SPI0_MOSI_M2_UART4_RX_M2 - OK
+	"23":      {Sysfs: 43, Cdev: adaptors.CdevPin{Chip: 1, Line: 11}},  // GPIO1_B3_SPI0_CLK_M2_UART4_TX_M2 - OK
+	"24":      {Sysfs: 44, Cdev: adaptors.CdevPin{Chip: 1, Line: 12}},  // GPIO1_B4_SPI0_CS0_M2_UART7_RX_M2 - OK
+	"22":      {Sysfs: 45, Cdev: adaptors.CdevPin{Chip: 1, Line: 13}},  // GPIO1_B5_SPI0_CS1_M0_UART7_TX_M2 - OK
+	"5":       {Sysfs: 62, Cdev: adaptors.CdevPin{Chip: 1, Line: 30}},  // GPIO1_D6_I2C8_SCL_M2 - OK
+	"3":       {Sysfs: 63, Cdev: adaptors.CdevPin{Chip: 1, Line: 31}},  // GPIO1_D7_I2C8_SDA_M2 - OK
+	"CSI1_11": {Sysfs: 81, Cdev: adaptors.CdevPin{Chip: 2, Line: 17}},  // GPIO2_C1 - ?
+	"CSI1_12": {Sysfs: 82, Cdev: adaptors.CdevPin{Chip: 2, Line: 18}},  // GPIO2_C2 - ?
+	"35":      {Sysfs: 96, Cdev: adaptors.CdevPin{Chip: 3, Line: 0}},   // GPIO3_A0_SPI4_MISO_M1_I2S3_MCLK_PWM10_M0 - OK
+	"38":      {Sysfs: 97, Cdev: adaptors.CdevPin{Chip: 3, Line: 1}},   // GPIO3_A1_SPI4_MOSI_M1_I2S3_SCLK - OK
+	"40":      {Sysfs: 98, Cdev: adaptors.CdevPin{Chip: 3, Line: 2}},   // GPIO3_A2_SPI4_CLK_M1_UART8_TX_M1_I2S3_LRCK - OK
+	"36":      {Sysfs: 99, Cdev: adaptors.CdevPin{Chip: 3, Line: 3}},   // GPIO3_A3_SPI4_CS0_M1_UART8_RX_M1_I2S3_SDO - OK
+	"37":      {Sysfs: 100, Cdev: adaptors.CdevPin{Chip: 3, Line: 4}},  // GPIO3_A4_SPI4_CS1_M1_I2S3_SDI - OK
+	"DSI0_12": {Sysfs: 102, Cdev: adaptors.CdevPin{Chip: 3, Line: 6}},  // GPIO3_A6 - ?
+	"33":      {Sysfs: 104, Cdev: adaptors.CdevPin{Chip: 3, Line: 8}},  // GPIO3_B0_PWM9_M0 - OK
+	"DSI0_10": {Sysfs: 105, Cdev: adaptors.CdevPin{Chip: 3, Line: 9}},  // GPIO3_B1_PWM2_M1 - ?
+	"7":       {Sysfs: 106, Cdev: adaptors.CdevPin{Chip: 3, Line: 10}}, // GPIO3_B2_I2S2_SDI_M1 - OK
+	"16":      {Sysfs: 107, Cdev: adaptors.CdevPin{Chip: 3, Line: 11}}, // GPIO3_B3_I2S2_SDO_M1 - OK
+	"18":      {Sysfs: 108, Cdev: adaptors.CdevPin{Chip: 3, Line: 12}}, // GPIO3_B4_I2S2_MCLK_M1 - OK
+	"29":      {Sysfs: 109, Cdev: adaptors.CdevPin{Chip: 3, Line: 13}}, // GPIO3_B5_UART3_TX_M1_I2S2_SCLK_M1_PWM12_M0 - OK
+	"31":      {Sysfs: 110, Cdev: adaptors.CdevPin{Chip: 3, Line: 14}}, // GPIO3_B6_UART3_RX_M1_I2S2_LRCK_M1_PWM13_M0 - OK
+	"12":      {Sysfs: 111, Cdev: adaptors.CdevPin{Chip: 3, Line: 15}}, // GPIO3_B7 - OK (UP)
+	"DSI0_8":  {Sysfs: 112, Cdev: adaptors.CdevPin{Chip: 3, Line: 16}}, // GPIO3_C0 - ?
+	"DSI0_14": {Sysfs: 113, Cdev: adaptors.CdevPin{Chip: 3, Line: 17}}, // GPIO3_C1 - ?
+	"11":      {Sysfs: 114, Cdev: adaptors.CdevPin{Chip: 3, Line: 18}}, // GPIO3_C2_PWM14_M0 - OK
+	"13":      {Sysfs: 115, Cdev: adaptors.CdevPin{Chip: 3, Line: 19}}, // GPIO3_C3_PWM15_IR_M0 - OK
+	"DSI1_10": {Sysfs: 125, Cdev: adaptors.CdevPin{Chip: 3, Line: 29}}, // GPIO3_D5_PWM11_M3 - ?
+	"DSI1_8":  {Sysfs: 128, Cdev: adaptors.CdevPin{Chip: 4, Line: 0}},  // GPIO4_A0 - ?
+	"DSI1_14": {Sysfs: 129, Cdev: adaptors.CdevPin{Chip: 4, Line: 1}},  // GPIO4_A1 - ?
+	"DSI1_12": {Sysfs: 131, Cdev: adaptors.CdevPin{Chip: 4, Line: 3}},  // GPIO4_A3 - ?
+	"CSI0_11": {Sysfs: 148, Cdev: adaptors.CdevPin{Chip: 4, Line: 20}}, // GPIO4_C4 - ?
+	"CSI0_12": {Sysfs: 149, Cdev: adaptors.CdevPin{Chip: 4, Line: 21}}, // GPIO4_C5 - ?
+}
+
+var pwmPinDefinitions = adaptors.PWMPinDefinitions{
+	// needs to be enabled by DT-overlay pwm2-m1 (pwm2 = "/pwm@fd8b0020";)
+	"DSI0_10": {Dir: "/sys/devices/platform/fd8b0020.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm4-m0 (pwm4 = "/pwm@febd0000";)
+	"8": {Dir: "/sys/devices/platform/febd0000.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm5-m1 (pwm5 = "/pwm@febd0010";)
+	"32": {Dir: "/sys/devices/platform/febd0010.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm9-m0 (pwm9 = "/pwm@febe0010";)
+	"33": {Dir: "/sys/devices/platform/febe0010.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm10-m0 (pwm10 = "/pwm@febe0020";)
+	"35": {Dir: "/sys/devices/platform/febe0020.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm11-m3 (pwm11 = "/pwm@febe0030";)
+	"DSI1_10": {Dir: "/sys/devices/platform/febe0030.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm12-m0 (pwm12 = "/pwm@febf0000";)
+	"29": {Dir: "/sys/devices/platform/febf0000.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm13-m0  (pwm13 = "/pwm@febf0010";)
+	"31": {Dir: "/sys/devices/platform/febf0010.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm14-m0 (pwm14 = "/pwm@febf0020";)
+	"11": {Dir: "/sys/devices/platform/febf0020.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+	// needs to be enabled by DT-overlay pwm15-m0 (pwm15 = "/pwm@febf0030";)
+	"13": {Dir: "/sys/devices/platform/febf0030.pwm/pwm/", DirRegexp: "pwmchip[0|1|2|3|4|5|6|7]$", Channel: 0},
+}
+
+var analogPinDefinitions = adaptors.AnalogPinDefinitions{
+	// +/-273.200 °C need >=7 characters to read: +/-273200 millidegree Celsius
+	// names equals /sys/class/thermal/thermal_zone*/hwmon*/name
+	"soc_thermal":        {Path: "/sys/class/thermal/thermal_zone0/temp", W: false, ReadBufLen: 7},
+	"bigcore0_thermal":   {Path: "/sys/class/thermal/thermal_zone1/temp", W: false, ReadBufLen: 7},
+	"bigcore1_thermal":   {Path: "/sys/class/thermal/thermal_zone2/temp", W: false, ReadBufLen: 7},
+	"littlecore_thermal": {Path: "/sys/class/thermal/thermal_zone3/temp", W: false, ReadBufLen: 7},
+	"center_thermal":     {Path: "/sys/class/thermal/thermal_zone4/temp", W: false, ReadBufLen: 7},
+	"gpu_thermal":        {Path: "/sys/class/thermal/thermal_zone5/temp", W: false, ReadBufLen: 7},
+	"npu_thermal":        {Path: "/sys/class/thermal/thermal_zone6/temp", W: false, ReadBufLen: 7},
+}
diff --git a/platforms/nanopi/LICENSE b/platforms/friendlyelec/nanopi/LICENSE
similarity index 100%
rename from platforms/nanopi/LICENSE
rename to platforms/friendlyelec/nanopi/LICENSE
diff --git a/platforms/nanopi/README.md b/platforms/friendlyelec/nanopi/README.md
similarity index 92%
rename from platforms/nanopi/README.md
rename to platforms/friendlyelec/nanopi/README.md
index 2e7c61798..4be4899fe 100644
--- a/platforms/nanopi/README.md
+++ b/platforms/friendlyelec/nanopi/README.md
@@ -1,9 +1,9 @@
 # NanoPi Boards
 
-The FriendlyARM NanoPi Boards are single board SoC computers with different hardware design. It has built-in GPIO, PWM,
+The FriendlyELEC NanoPi Boards are single board SoC computers with different hardware design. It has built-in GPIO, PWM,
 SPI, and I2C interfaces.
 
-For more info about the NanoPi Boards, go to [https://wiki.friendlyelec.com/wiki/index.php/Main_Page](https://wiki.friendlyelec.com/wiki/index.php/Main_Page).
+For more info about the NanoPi Boards, go to [https://wiki.friendlyelec.com/wiki/index.php/NanoPi_NEO](https://wiki.friendlyelec.com/wiki/index.php/NanoPi_NEO).
 
 ## How to Install
 
diff --git a/platforms/friendlyelec/nanopi/doc.go b/platforms/friendlyelec/nanopi/doc.go
new file mode 100644
index 000000000..368440fd0
--- /dev/null
+++ b/platforms/friendlyelec/nanopi/doc.go
@@ -0,0 +1,7 @@
+/*
+Package nanopi contains the Gobot adaptor for the FriendlyELEC NanoPi Boards.
+
+For further information refer to nanopi README:
+https://github.com/hybridgroup/gobot/blob/release/platforms/friendlyelec/nanopi/README.md
+*/
+package nanopi // import "gobot.io/x/gobot/v2/platforms/friendlyelec/nanopi"
diff --git a/platforms/nanopi/nanopi_adaptor.go b/platforms/friendlyelec/nanopi/nanopi_adaptor.go
similarity index 98%
rename from platforms/nanopi/nanopi_adaptor.go
rename to platforms/friendlyelec/nanopi/nanopi_adaptor.go
index 5bc06ce0d..f487d2257 100644
--- a/platforms/nanopi/nanopi_adaptor.go
+++ b/platforms/friendlyelec/nanopi/nanopi_adaptor.go
@@ -21,7 +21,7 @@ const (
 	defaultSpiMaxSpeed   = 500000
 )
 
-// Adaptor represents a Gobot Adaptor for the FriendlyARM NanoPi Boards
+// Adaptor represents a Gobot Adaptor for the FriendlyELEC NanoPi Boards
 type Adaptor struct {
 	name  string
 	sys   *system.Accesser // used for unit tests only
diff --git a/platforms/nanopi/nanopi_adaptor_test.go b/platforms/friendlyelec/nanopi/nanopi_adaptor_test.go
similarity index 66%
rename from platforms/nanopi/nanopi_adaptor_test.go
rename to platforms/friendlyelec/nanopi/nanopi_adaptor_test.go
index 03e9a1c93..4d1ac29b7 100644
--- a/platforms/nanopi/nanopi_adaptor_test.go
+++ b/platforms/friendlyelec/nanopi/nanopi_adaptor_test.go
@@ -1,8 +1,6 @@
 package nanopi
 
 import (
-	"fmt"
-	"strconv"
 	"strings"
 	"testing"
 
@@ -169,107 +167,6 @@ func TestAnalog(t *testing.T) {
 	require.NoError(t, a.Finalize())
 }
 
-func TestInvalidPWMPin(t *testing.T) {
-	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
-	preparePwmFs(fs)
-
-	err := a.PwmWrite("666", 42)
-	require.ErrorContains(t, err, "'666' is not a valid id for a PWM pin")
-
-	err = a.ServoWrite("666", 120)
-	require.ErrorContains(t, err, "'666' is not a valid id for a PWM pin")
-
-	err = a.PwmWrite("3", 42)
-	require.ErrorContains(t, err, "'3' is not a valid id for a PWM pin")
-
-	err = a.ServoWrite("3", 120)
-	require.ErrorContains(t, err, "'3' is not a valid id for a PWM pin")
-}
-
-func TestPwmWrite(t *testing.T) {
-	// arrange
-	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
-	preparePwmFs(fs)
-	// act
-	err := a.PwmWrite("PWM", 100)
-	// assert
-	require.NoError(t, err)
-	assert.Equal(t, "0", fs.Files[pwmExportPath].Contents)
-	assert.Equal(t, "1", fs.Files[pwmEnablePath].Contents)
-	assert.Equal(t, strconv.Itoa(10000000), fs.Files[pwmPeriodPath].Contents)
-	assert.Equal(t, "3921568", fs.Files[pwmDutyCyclePath].Contents)
-	assert.Equal(t, "normal", fs.Files[pwmPolarityPath].Contents)
-
-	require.NoError(t, a.Finalize())
-}
-
-func TestServoWrite(t *testing.T) {
-	// arrange: prepare 50Hz for servos
-	const (
-		pin         = "PWM"
-		fiftyHzNano = 20000000
-	)
-	a := NewNeoAdaptor(adaptors.WithPWMDefaultPeriodForPin(pin, fiftyHzNano))
-	fs := a.sys.UseMockFilesystem(pwmMockPaths)
-	preparePwmFs(fs)
-	require.NoError(t, a.Connect())
-	// act & assert for 0° (min default value)
-	err := a.ServoWrite(pin, 0)
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(fiftyHzNano), fs.Files[pwmPeriodPath].Contents)
-	assert.Equal(t, "500000", fs.Files[pwmDutyCyclePath].Contents)
-	// act & assert for 180° (max default value)
-	err = a.ServoWrite(pin, 180)
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(fiftyHzNano), fs.Files[pwmPeriodPath].Contents)
-	assert.Equal(t, "2500000", fs.Files[pwmDutyCyclePath].Contents)
-	// act & assert invalid pins
-	err = a.ServoWrite("3", 120)
-	require.ErrorContains(t, err, "'3' is not a valid id for a PWM pin")
-
-	require.NoError(t, a.Finalize())
-}
-
-func TestSetPeriod(t *testing.T) {
-	// arrange
-	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
-	preparePwmFs(fs)
-
-	newPeriod := uint32(2550000)
-	// act
-	err := a.SetPeriod("PWM", newPeriod)
-	// assert
-	require.NoError(t, err)
-	assert.Equal(t, "0", fs.Files[pwmExportPath].Contents)
-	assert.Equal(t, "1", fs.Files[pwmEnablePath].Contents)
-	assert.Equal(t, fmt.Sprintf("%d", newPeriod), fs.Files[pwmPeriodPath].Contents) //nolint:perfsprint // ok here
-	assert.Equal(t, "0", fs.Files[pwmDutyCyclePath].Contents)
-	assert.Equal(t, "normal", fs.Files[pwmPolarityPath].Contents)
-
-	// arrange test for automatic adjustment of duty cycle to lower value
-	err = a.PwmWrite("PWM", 127) // 127 is a little bit smaller than 50% of period
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(1270000), fs.Files[pwmDutyCyclePath].Contents)
-	newPeriod = newPeriod / 10
-
-	// act
-	err = a.SetPeriod("PWM", newPeriod)
-
-	// assert
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(127000), fs.Files[pwmDutyCyclePath].Contents)
-
-	// arrange test for automatic adjustment of duty cycle to higher value
-	newPeriod = newPeriod * 20
-
-	// act
-	err = a.SetPeriod("PWM", newPeriod)
-
-	// assert
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(2540000), fs.Files[pwmDutyCyclePath].Contents)
-}
-
 func TestFinalizeErrorAfterGPIO(t *testing.T) {
 	// arrange
 	a := initConnectedTestAdaptor()
@@ -284,14 +181,15 @@ func TestFinalizeErrorAfterGPIO(t *testing.T) {
 }
 
 func TestFinalizeErrorAfterPWM(t *testing.T) {
+	// indirect test for PWM.Finalize() is called for the adaptor
+	// arrange
 	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
 	preparePwmFs(fs)
-
 	require.NoError(t, a.PwmWrite("PWM", 1))
-
 	fs.WithWriteError = true
-
+	// act
 	err := a.Finalize()
+	// assert
 	require.ErrorContains(t, err, "write error")
 }
 
diff --git a/platforms/nanopi/nanopineo_pin_map.go b/platforms/friendlyelec/nanopi/nanopineo_pin_map.go
similarity index 100%
rename from platforms/nanopi/nanopineo_pin_map.go
rename to platforms/friendlyelec/nanopi/nanopineo_pin_map.go
diff --git a/platforms/nanopi/doc.go b/platforms/nanopi/doc.go
deleted file mode 100644
index 072c4871e..000000000
--- a/platforms/nanopi/doc.go
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
-Package nanopi contains the Gobot adaptor for the FriendlyARM NanoPi Boards.
-
-For further information refer to nanopi README:
-https://github.com/hybridgroup/gobot/blob/release/platforms/nanopi/README.md
-*/
-package nanopi // import "gobot.io/x/gobot/v2/platforms/nanopi"
diff --git a/platforms/tinkerboard/adaptor_test.go b/platforms/tinkerboard/adaptor_test.go
index ba6b6c809..ff2988d60 100644
--- a/platforms/tinkerboard/adaptor_test.go
+++ b/platforms/tinkerboard/adaptor_test.go
@@ -1,8 +1,6 @@
 package tinkerboard
 
 import (
-	"fmt"
-	"strconv"
 	"strings"
 	"testing"
 
@@ -171,93 +169,6 @@ func TestAnalogRead(t *testing.T) {
 	require.NoError(t, a.Finalize())
 }
 
-func TestPwmWrite(t *testing.T) {
-	// arrange
-	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
-	preparePwmFs(fs)
-	// act
-	err := a.PwmWrite("33", 100)
-	// assert
-	require.NoError(t, err)
-	assert.Equal(t, "0", fs.Files[pwmExportPath].Contents)
-	assert.Equal(t, "1", fs.Files[pwmEnablePath].Contents)
-	assert.Equal(t, "10000000", fs.Files[pwmPeriodPath].Contents)
-	assert.Equal(t, "3921568", fs.Files[pwmDutyCyclePath].Contents)
-	assert.Equal(t, "normal", fs.Files[pwmPolarityPath].Contents)
-	// act & assert invalid pin
-	err = a.PwmWrite("666", 42)
-	require.ErrorContains(t, err, "'666' is not a valid id for a PWM pin")
-
-	require.NoError(t, a.Finalize())
-}
-
-func TestServoWrite(t *testing.T) {
-	// arrange: prepare 50Hz for servos
-	const (
-		pin         = "33"
-		fiftyHzNano = 20000000
-	)
-	a := NewAdaptor(adaptors.WithPWMDefaultPeriodForPin(pin, fiftyHzNano))
-	fs := a.sys.UseMockFilesystem(pwmMockPaths)
-	preparePwmFs(fs)
-	require.NoError(t, a.Connect())
-	// act & assert for 0° (min default value)
-	err := a.ServoWrite(pin, 0)
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(fiftyHzNano), fs.Files[pwmPeriodPath].Contents)
-	assert.Equal(t, "500000", fs.Files[pwmDutyCyclePath].Contents)
-	// act & assert for 180° (max default value)
-	err = a.ServoWrite(pin, 180)
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(fiftyHzNano), fs.Files[pwmPeriodPath].Contents)
-	assert.Equal(t, "2500000", fs.Files[pwmDutyCyclePath].Contents)
-	// act & assert invalid pins
-	err = a.ServoWrite("3", 120)
-	require.ErrorContains(t, err, "'3' is not a valid id for a PWM pin")
-
-	require.NoError(t, a.Finalize())
-}
-
-func TestSetPeriod(t *testing.T) {
-	// arrange
-	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
-	preparePwmFs(fs)
-
-	newPeriod := uint32(2550000)
-	// act
-	err := a.SetPeriod("33", newPeriod)
-	// assert
-	require.NoError(t, err)
-	assert.Equal(t, "0", fs.Files[pwmExportPath].Contents)
-	assert.Equal(t, "1", fs.Files[pwmEnablePath].Contents)
-	assert.Equal(t, fmt.Sprintf("%d", newPeriod), fs.Files[pwmPeriodPath].Contents) //nolint:perfsprint // ok here
-	assert.Equal(t, "0", fs.Files[pwmDutyCyclePath].Contents)
-	assert.Equal(t, "normal", fs.Files[pwmPolarityPath].Contents)
-
-	// arrange test for automatic adjustment of duty cycle to lower value
-	err = a.PwmWrite("33", 127) // 127 is a little bit smaller than 50% of period
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(1270000), fs.Files[pwmDutyCyclePath].Contents)
-	newPeriod = newPeriod / 10
-
-	// act
-	err = a.SetPeriod("33", newPeriod)
-
-	// assert
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(127000), fs.Files[pwmDutyCyclePath].Contents)
-
-	// arrange test for automatic adjustment of duty cycle to higher value
-	newPeriod = newPeriod * 20
-
-	// act
-	err = a.SetPeriod("33", newPeriod)
-
-	// assert
-	require.NoError(t, err)
-	assert.Equal(t, strconv.Itoa(2540000), fs.Files[pwmDutyCyclePath].Contents)
-}
-
 func TestFinalizeErrorAfterGPIO(t *testing.T) {
 	// arrange
 	a := initConnectedTestAdaptor()
@@ -272,14 +183,15 @@ func TestFinalizeErrorAfterGPIO(t *testing.T) {
 }
 
 func TestFinalizeErrorAfterPWM(t *testing.T) {
+	// indirect test for PWM.Finalize() is called for the adaptor
+	// arrange
 	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
 	preparePwmFs(fs)
-
 	require.NoError(t, a.PwmWrite("33", 1))
-
 	fs.WithWriteError = true
-
+	// act
 	err := a.Finalize()
+	// assert
 	require.ErrorContains(t, err, "write error")
 }
 
diff --git a/platforms/upboard/up2/adaptor_test.go b/platforms/upboard/up2/adaptor_test.go
index c1a233fd3..32eaf0dc5 100644
--- a/platforms/upboard/up2/adaptor_test.go
+++ b/platforms/upboard/up2/adaptor_test.go
@@ -161,15 +161,16 @@ func TestFinalizeErrorAfterGPIO(t *testing.T) {
 }
 
 func TestFinalizeErrorAfterPWM(t *testing.T) {
+	// indirect test for PWM.Finalize() is called for the adaptor
+	// arrange
 	a, fs := initConnectedTestAdaptorWithMockedFilesystem(pwmMockPaths)
 	fs.Files[pwmDutyCyclePath].Contents = "0"
 	fs.Files[pwmPeriodPath].Contents = "0"
-
 	require.NoError(t, a.PwmWrite("32", 1))
-
 	fs.WithWriteError = true
-
+	// act
 	err := a.Finalize()
+	// assert
 	require.ErrorContains(t, err, "write error")
 }