package sbc import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.slf4j.LoggerFactory import somecodes.Codes.Companion.gpioExportPath import somecodes.Codes.Companion.gpioPath import somecodes.Codes.Companion.gpioUnexportPath import somecodes.Codes.Companion.haveGpioSupport import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.isDirectory /** * Create a digital output for a GPIO pin. * @param linuxGpio The GPIO pin number in Linux. * @param name The name of the digital output. * @param activeHigh If true, the output is active high (default is true). * @property inited Indicates whether the digital output has been initialized. * */ @Suppress("unused") class DigitalOutput(val linuxGpio: Int, val name: String, val activeHigh: Boolean = true) { var inited = false; private set private var valuepath: Path? = null private val logger = LoggerFactory.getLogger("DigitalOutput $name ($linuxGpio)") init{ if (haveGpioSupport()){ if (checkExists()){ // already exists try{ // try to set direction to output val dir = gpioPath.resolve("gpio$linuxGpio").resolve("direction") Files.writeString(dir, "out") // successfully set direction to output inited = true valuepath = gpioPath.resolve("gpio$linuxGpio").resolve("value") logger.info("GPIO $name GPIO $linuxGpio already exists as output") } catch (e : Exception){ // failed to set direction to output, log error logger.error("Failed to set existing GPIO $name GPIO $linuxGpio as output: ${e.message}") } } else { // not yet exists, export it try{ // export the GPIO pin Files.writeString(gpioExportPath, linuxGpio.toString()) if (checkExists()){ // successfully exported, now set direction to output val dir = gpioPath.resolve("gpio$linuxGpio").resolve("direction") Files.writeString(dir, "out") inited = true valuepath = gpioPath.resolve("gpio$linuxGpio").resolve("value") logger.info("Initialized GPIO $name GPIO $linuxGpio as output") } else { // failed to export, log error logger.error("GPIO $name GPIO $linuxGpio does not exist after export") } } catch (e: Exception){ // failed to export GPIO pin, log error logger.error("Failed to export GPIO $name GPIO $linuxGpio: ${e.message}") } } } else logger.error("DigitalOutput $name GPIO $linuxGpio not initialized: GPIO support not available") } fun checkExists(): Boolean{ return try { val gpioPath = gpioPath.resolve("gpio$linuxGpio") if (gpioPath.isDirectory() && gpioPath.toFile().exists()){ val dir = gpioPath.resolve("direction").toFile() val value = gpioPath.resolve("value").toFile() if (dir.exists() && value.exists()) { return dir.canWrite() && value.canWrite() } } false } catch (e: Exception) { logger.error("Error checking if GPIO $linuxGpio exists: ${e.message}") false } } /** * Release the GPIO pin by unexporting it. */ fun release(){ if (inited){ try{ Files.writeString(gpioUnexportPath, linuxGpio.toString()) inited = false valuepath = null } catch (e : Exception){ logger.error("Failed to unexport GPIO $name GPIO ${e.message}") } } } /** * Set Digital Output to ON */ fun setON() : Boolean{ if (inited){ if (valuepath!=null){ try{ Files.writeString(valuepath!!, if (activeHigh) "1" else "0") return true } catch (e: Exception){ logger.error("Failed to set GPIO $name GPIO $linuxGpio ON: ${e.message}") } } } return false } /** * Set Digital Output to OFF */ fun setOFF() : Boolean{ if (inited){ if (valuepath!=null){ try{ Files.writeString(valuepath!!, if (activeHigh) "0" else "1") return true } catch (e : Exception){ logger.error("Failed to set GPIO $name GPIO $linuxGpio OFF: ${e.message}") } } } return false } /** * Blink the Digital Output for a specified duration. * @param ms The duration in milliseconds to keep the output ON before turning it OFF (default is 50ms). */ fun Blink(ms: Long = 50){ if (inited){ CoroutineScope(Dispatchers.IO).launch { setON() delay(ms) setOFF() } } } }