Skip to content

Commit

Permalink
Moved some stuff again.
Browse files Browse the repository at this point in the history
More shuffling, plus added a new sensor for next party trick.
  • Loading branch information
EAGrahamJr committed Apr 22, 2024
1 parent 91ef4ba commit ae0d75d
Show file tree
Hide file tree
Showing 8 changed files with 456 additions and 125 deletions.
39 changes: 31 additions & 8 deletions servomatic/src/main/kotlin/crackers/kobots/app/HAJunk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,16 @@ import crackers.kobots.app.SuzerainOfServos.boomLink
import crackers.kobots.app.SuzerainOfServos.bucketLink
import crackers.kobots.app.SuzerainOfServos.gripper
import crackers.kobots.app.SuzerainOfServos.swing
import crackers.kobots.mqtt.homeassistant.DeviceIdentifier
import crackers.kobots.mqtt.homeassistant.KobotAnalogSensor
import crackers.kobots.mqtt.homeassistant.KobotNumberEntity
import crackers.kobots.mqtt.homeassistant.KobotSelectEntity
import crackers.kobots.app.newarm.Predestination
import crackers.kobots.mqtt.homeassistant.*
import crackers.kobots.parts.enumValue
import crackers.kobots.parts.movement.*
import kotlin.math.roundToInt

/**
* HA entities, etc.
*/
object HAJunk : AutoCloseable {
object HAJunk : Startable {
private val haIdentifier = DeviceIdentifier("Kobots", "Servomatic")

private val selectHandler = object : KobotSelectEntity.Companion.SelectHandler {
Expand All @@ -50,12 +48,16 @@ object HAJunk : AutoCloseable {
Mode.IDLE -> {
}

Mode.STOP -> AppCommon.applicationRunning = false
Mode.STOP -> {
SuzerainOfServos.stop()
AppCommon.applicationRunning = false
}
Mode.CLUCK -> {
}

Mode.HOME -> SuzerainOfServos.handleRequest(SequenceRequest(Predestination.homeSequence))
Mode.SAY_HI -> SuzerainOfServos.handleRequest(SequenceRequest(Predestination.sayHi))
Mode.CRA_CRAY -> SuzerainOfServos.handleRequest(SequenceRequest(Predestination.craCraSequence()))

else -> logger.warn("No clue what to do with $select")
}
Expand All @@ -68,6 +70,25 @@ object HAJunk : AutoCloseable {
"servomatic_tof", "Bobbi Detector", haIdentifier,
KobotAnalogSensor.Companion.AnalogDevice.DISTANCE, unitOfMeasurement = "mm"
)
val proxSensor = object : KobotBinarySensor(
"proximity_alert",
"Proximity",
haIdentifier,
deviceClass = KobotBinarySensor.Companion.BinaryDevice.OCCUPANCY,
) {
override val icon = "mdi:alert"
}

val ambientSensor = object : KobotAnalogSensor(
"ambient_light",
"Luminosity",
haIdentifier,
deviceClass = KobotAnalogSensor.Companion.AnalogDevice.ILLUMINANCE,
unitOfMeasurement = "lumens",
) {
override val icon = "mdi:lightbulb-alert"
}


private class ArmRotateHandler(val rotator: Rotator, val thing: String) :
KobotNumberEntity.Companion.NumberHandler {
Expand Down Expand Up @@ -171,14 +192,16 @@ object HAJunk : AutoCloseable {
/**
* LET'S LIGHT THIS THING UP!!!
*/
fun start() {
override fun start() {
swingEntity.start()
boomEntity.start()
armEntity.start()
gripperEntity.start()
bucketEntity.start()
commandSelectEntity.start()
tofSensor.start()
proxSensor.start()
ambientSensor.start()
}

fun sendUpdatedStates() {
Expand All @@ -189,6 +212,6 @@ object HAJunk : AutoCloseable {
bucketEntity.sendCurrentState()
}

override fun close() {
override fun stop() {
}
}
41 changes: 41 additions & 0 deletions servomatic/src/main/kotlin/crackers/kobots/app/I2CFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2022-2024 by E. A. Graham, Jr.
*
* 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.
*/

package crackers.kobots.app

import com.diozero.api.I2CDevice
import com.diozero.devices.PCA9685
import com.diozero.devices.oled.SH1106
import crackers.kobots.devices.sensors.VCNL4040
import crackers.kobots.devices.sensors.VL6180X

/**
* Masks whatever the bleep I'm doing with I2C
*/
object I2CFactory {
val toffleDevice by lazy {
I2CDevice(1, VL6180X.DEFAULT_I2C_ADDR)
}
val proxyDevice by lazy {
I2CDevice(1, VCNL4040.DEFAULT_I2C_ADDRESS)
}
val suziDevice by lazy {
I2CDevice(1, PCA9685.DEFAULT_ADDRESS)
}
val armMonitorDevice by lazy {
I2CDevice(1, SH1106.DEFAULT_I2C_ADDRESS)
}
}
88 changes: 0 additions & 88 deletions servomatic/src/main/kotlin/crackers/kobots/app/Predestination.kt

This file was deleted.

70 changes: 64 additions & 6 deletions servomatic/src/main/kotlin/crackers/kobots/app/Sensei.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@

package crackers.kobots.app

import crackers.kobots.app.HAJunk.tofSensor
import crackers.kobots.devices.sensors.VCNL4040
import crackers.kobots.devices.sensors.VL6180X
import crackers.kobots.parts.elapsed
import crackers.kobots.parts.scheduleWithDelay
import org.slf4j.LoggerFactory
import java.time.Instant
import java.util.concurrent.Future
import java.util.concurrent.ScheduledFuture
import kotlin.time.Duration.Companion.seconds

object Sensei {
object Sensei : Startable {
private val logger = LoggerFactory.getLogger("Sensei")
internal val toffle by lazy { VL6180X() }
private val toffle by lazy { VL6180X(I2CFactory.toffleDevice) }

private fun VL6180X.distance(): Float = try {
range.toFloat()
Expand All @@ -31,9 +37,61 @@ object Sensei {
0f
}

fun publishEvent() {
toffle.distance().run {
if (this < 200) tofSensor.currentState = toString()
private const val PROXIMITY_THRESHOLD = 4
private val TRIP_DURATION = java.time.Duration.ofSeconds(2)
private val polly by lazy {
VCNL4040(I2CFactory.proxyDevice).apply {
ambientLightEnabled = true
proximityEnabled = true
}
}
private var proxFiredAt = Instant.EPOCH

private lateinit var future: Future<*>
private lateinit var slowFuture: ScheduledFuture<*>

override fun start() {
// initialize the sensor
toffle.range

var lastProxTriggered = false
future = AppCommon.executor.scheduleWithDelay(1.seconds) {
AppCommon.whileRunning {
// time-of-flight
toffle.distance().run {
if (this < 200) HAJunk.tofSensor.currentState = toString()
}

// proximity
polly.proximity.toInt().let {
(it > PROXIMITY_THRESHOLD).let { tripped ->
if (tripped) {
// fresh trip, start the countdown
if (!lastProxTriggered) {
proxFiredAt = Instant.now()
} else {
if (proxFiredAt.elapsed() > TRIP_DURATION)
HAJunk.proxSensor.currentState = true
}
} else {
HAJunk.proxSensor.currentState = false
}
lastProxTriggered = tripped
}
}
}
}

slowFuture = AppCommon.executor.scheduleWithDelay(30.seconds)
{
AppCommon.whileRunning {
HAJunk.ambientSensor.currentState = polly.luminosity.toString()
}
}
}

override fun stop() {
::slowFuture.isInitialized && slowFuture.cancel(true)
::future.isInitialized && future.cancel(true)
}
}
35 changes: 14 additions & 21 deletions servomatic/src/main/kotlin/crackers/kobots/app/ServoThing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,15 @@

package crackers.kobots.app

import com.diozero.devices.Button
import crackers.kobots.app.AppCommon.REMOTE_PI
import crackers.kobots.app.AppCommon.mqttClient
import crackers.kobots.app.HAJunk.commandSelectEntity
import crackers.kobots.app.SuzerainOfServos.INTERNAL_TOPIC
import crackers.kobots.app.newarm.ArmMonitor
import crackers.kobots.parts.app.KobotSleep
import crackers.kobots.parts.app.publishToTopic
import crackers.kobots.parts.movement.ActionSequence
import crackers.kobots.parts.movement.SequenceRequest
import crackers.kobots.parts.scheduleWithFixedDelay
import org.slf4j.LoggerFactory
import java.util.concurrent.atomic.AtomicReference
import kotlin.concurrent.thread
import kotlin.system.exitProcess
import kotlin.time.Duration.Companion.milliseconds

/**
* Handles a bunch of different servos for various things. Everything should have an HA interface.
Expand All @@ -39,7 +33,7 @@ import kotlin.time.Duration.Companion.milliseconds
val logger = LoggerFactory.getLogger("Servomatic")

enum class Mode {
IDLE, STOP, CLUCK, TEXT, HOME, SAY_HI
IDLE, STOP, CLUCK, TEXT, HOME, SAY_HI, CRA_CRAY
}

internal interface Startable {
Expand All @@ -49,7 +43,7 @@ internal interface Startable {

// because we might be doing something else?
enum class SystemState {
IDLE, MOVING
IDLE, MOVING, SHUTDOWN
}

private val state = AtomicReference(SystemState.IDLE)
Expand All @@ -72,15 +66,9 @@ fun main(args: Array<String>?) {
// add the shutdown hook
Runtime.getRuntime().addShutdownHook(thread(start = false, block = ::stopEverything))

val button = Button(17)
var lastPush = false
AppCommon.executor.scheduleWithFixedDelay(20.milliseconds, 20.milliseconds) {
lastPush = button.value.also { pushed ->
if (!pushed && lastPush) AppCommon.applicationRunning = false
}
if (AppCommon.applicationRunning) Sensei.publishEvent()
}
Sensei.start()
SuzerainOfServos.start()
ArmMonitor.start()
HAJunk.start()
mqttClient.apply {
startAliveCheck()
Expand All @@ -89,16 +77,21 @@ fun main(args: Array<String>?) {

AppCommon.awaitTermination()
KobotSleep.seconds(1)
stopEverything()
exitProcess(0)
}

fun stopEverything() {
if (systemState == SystemState.SHUTDOWN) {
logger.warn("Already stopped")
return
}
ArmMonitor.stop()
SuzerainOfServos.stop()
Sensei.stop()
HAJunk.stop()

AppCommon.executor.shutdownNow()
HAJunk.close()

logger.warn("Servomatic exit")
systemState = SystemState.SHUTDOWN
}

internal fun servoRequest(sequence: ActionSequence) = publishToTopic(INTERNAL_TOPIC, SequenceRequest(sequence))
Loading

0 comments on commit ae0d75d

Please sign in to comment.