commit b24ebba1f6f257a321bb5b506335bee9e839e850 Author: 00asdf Date: Fri Apr 11 18:44:49 2025 +0200 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59477e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +### IntelliJ IDEA ### +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Kotlin ### +.kotlin + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store +/.idea/misc.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..919ce1f --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..df543e3 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..cba7a76 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/KotlinJavaRuntime.xml b/.idea/libraries/KotlinJavaRuntime.xml new file mode 100644 index 0000000..3e6dc71 --- /dev/null +++ b/.idea/libraries/KotlinJavaRuntime.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..365aa6d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/VFHeatControl.iml b/VFHeatControl.iml new file mode 100644 index 0000000..4eba30b --- /dev/null +++ b/VFHeatControl.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/kotlin/dev/asdf00/visionfive/hc/Gpio.kt b/src/main/kotlin/dev/asdf00/visionfive/hc/Gpio.kt new file mode 100644 index 0000000..b9d3bff --- /dev/null +++ b/src/main/kotlin/dev/asdf00/visionfive/hc/Gpio.kt @@ -0,0 +1,90 @@ +package dev.asdf00.visionfive.hc + +import java.nio.file.Files +import java.nio.file.Path + +class GpioPin(val num: Int, val isInput: Boolean) : AutoCloseable { + var state: Boolean = false + get() { + if (isInput) + field = Files.readString(valuePath) == "1\n" + return field + } + set(newVal) { + if (isInput) + throw IllegalStateException("can not write state of input") + if (field == newVal) + return + if (!isInput) + Files.writeString(valuePath, if (newVal) "1" else "0") + field = newVal + } + + private val basePath get() = Path.of("/sys/class/gpio") + private val pinPath get() = basePath.resolve("gpio$num") + private val valuePath get() = pinPath.resolve("value") + + init { + if (num !in ALLOWED_PINS) + throw IllegalArgumentException("GPIO$num does not exist!") + println("initializing with 'echo $num > ${basePath.resolve("export")}'") + Files.writeString(basePath.resolve("export"), "$num") + Files.writeString(basePath.resolve("gpio$num").resolve("direction"), if (isInput) "in" else "out") + } + + override fun close() { + if (!isInput) + state = false + Files.writeString(basePath.resolve("unexport"), "$num") + } + + companion object { + private val ALLOWED_PINS = setOf(58, 57, 55, 5, 6, 42, 38, 43, 47, 54, 51, 52, 53, 50, 48, 49, 56, 45, 40, 37, 39, 46, 59, 63, 36, 60, 61, 44) + } +} + +class PinGroup(vararg val pins: GpioPin) { + var state: BooleanArray + get() = BooleanArray(pins.size) { pins[it].state } + set(nuStates) { + if (nuStates.size != pins.size) + throw IllegalArgumentException("state count mismatch, expected ${pins.size}, got ${nuStates.size}") + for (i in nuStates.indices) + pins[i].state = nuStates[i] + } + + var intState: Long + get() { + if (pins.size > 64) + throw UnsupportedOperationException("can not use intState for PinGroup with more than 64 pins") + var l = 0L + for (i in pins.indices.reversed()) { + l = l shl 1 + l = l or if (pins[i].state) 1 else 0 + } + return l + } + set(value) { + var l = value + if (pins.size > 64) + throw UnsupportedOperationException("can not use intState for PinGroup with more than 64 pins") + for (i in pins.indices) { + pins[i].state = l and 1 == 1L + l = l ushr 1 + } + } +} + +fun usingPins(vararg pins: Pair, body: (Array) -> Unit) { + fun recUsingPins(i: Int, pins: Array, pinIds: Array>, body: (Array) -> Unit) { + if (i < pins.size) { + GpioPin(pinIds[i].first, pinIds[i].second).use { + pins[i] = it + recUsingPins(i + 1, pins, pinIds, body) + } + } else { + body(Array(pins.size) { pins[it]!! }) + } + } + recUsingPins(0, arrayOfNulls(pins.size), pins, body) +} diff --git a/src/main/kotlin/dev/asdf00/visionfive/hc/MotorControl.kt b/src/main/kotlin/dev/asdf00/visionfive/hc/MotorControl.kt new file mode 100644 index 0000000..ece77e0 --- /dev/null +++ b/src/main/kotlin/dev/asdf00/visionfive/hc/MotorControl.kt @@ -0,0 +1,115 @@ +package dev.asdf00.visionfive.hc + +class Controller(private val senseBot: GpioPin, private val senseTop: GpioPin, private val motor: PinGroup) { + constructor(senseBot: GpioPin, senseTop: GpioPin, m0: GpioPin, m1: GpioPin, m2: GpioPin, m3: GpioPin) : this( + senseBot, + senseTop, + PinGroup(m0, m1, m2, m3) + ) + + init { + motor.intState = initialStep + } + + private var internalState = -1 + private var maxStep = -1 + + var state: Double + get() = if (internalState < 0) Double.NaN else internalState.toDouble() / maxStep.toDouble() + set(target) { + if (internalState < 0) + throw IllegalStateException("call Controller#runInitSequence before using it") + TODO() + } + + fun runInitSequence() { + val prevState = runToBot() + val range = runToTop() + stepClock(range - prevState) + internalState = prevState + maxStep = range + } + + private fun stepClock(cnt: Int = 1): Int { + var actual = cnt + for (i in 0..