Remove swift source, update esp32 usb servo to share serial port

This commit is contained in:
Piv
2023-01-22 14:20:36 +10:30
parent b56467dd1c
commit 40e5269c35
14 changed files with 17 additions and 492 deletions

View File

@@ -1,9 +0,0 @@
FROM vato.ddns.net:8083/swift:latest as builder
WORKDIR /root
COPY . .
RUN swift build -c release
FROM vato.ddns.net:8083/swift:slim
WORKDIR /root
COPY --from=builder /root .
CMD [".build/x86_64-unknown-linux/release/docker-test"]

View File

@@ -1,106 +0,0 @@
{
"object": {
"pins": [
{
"package": "grpc-swift",
"repositoryURL": "https://github.com/grpc/grpc-swift.git",
"state": {
"branch": null,
"revision": "640b0ef1d0be63bda0ada86786cfda678ab2aae9",
"version": "1.0.0-alpha.19"
}
},
{
"package": "swift-log",
"repositoryURL": "https://github.com/apple/swift-log.git",
"state": {
"branch": null,
"revision": "173f567a2dfec11d74588eea82cecea555bdc0bc",
"version": "1.4.0"
}
},
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "5fc24345f92ec4c274121776c215ab0aa1ed4d50",
"version": "2.22.0"
}
},
{
"package": "swift-nio-http2",
"repositoryURL": "https://github.com/apple/swift-nio-http2.git",
"state": {
"branch": null,
"revision": "1e68e51752be0b43c5a0ef35818c1dd24d13e77c",
"version": "1.14.1"
}
},
{
"package": "swift-nio-ssl",
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
"state": {
"branch": null,
"revision": "ea1dfd64193bf5af4490635a4a44c4fb43b1e1ae",
"version": "2.9.1"
}
},
{
"package": "swift-nio-transport-services",
"repositoryURL": "https://github.com/apple/swift-nio-transport-services.git",
"state": {
"branch": null,
"revision": "bb56586c4cab9a79dce6ec4738baddb5802c5de7",
"version": "1.9.0"
}
},
{
"package": "SwiftProtobuf",
"repositoryURL": "https://github.com/apple/swift-protobuf.git",
"state": {
"branch": null,
"revision": "0279688c9fc5a40028e1b5bb0cb56534a45a6020",
"version": "1.12.0"
}
},
{
"package": "Swift2dCar",
"repositoryURL": "https://vato.ddns.net/gitlab/vato007/swift2dcar.git",
"state": {
"branch": "master",
"revision": "970aac902531408614db0a37a7300e9373dafb50",
"version": null
}
},
{
"package": "SwiftRPLidar",
"repositoryURL": "https://vato.ddns.net/gitlab/vato007/swiftrplidar.git",
"state": {
"branch": "master",
"revision": "761eb0bc1a00b4627a7870ffac121a542ff0cd6b",
"version": null
}
},
{
"package": "SwiftSerial",
"repositoryURL": "https://vato.ddns.net/gitlab/vato007/SwiftSerial.git",
"state": {
"branch": "master",
"revision": "27a5d92aa00f6e91581389485994364e16bed2c5",
"version": null
}
},
{
"package": "SwiftyGPIO",
"repositoryURL": "https://github.com/uraimo/SwiftyGPIO.git",
"state": {
"branch": null,
"revision": "2038228e020cf12a62012b1ebe36bb9b8e6fdb6a",
"version": "1.2.5"
}
}
]
},
"version": 1
}

View File

@@ -1,35 +0,0 @@
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SwiftyCar",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.0.0-alpha.19"),
.package(url: "https://github.com/uraimo/SwiftyGPIO.git", from: "1.0.0"),
.package(url: "https://vato.ddns.net/gitlab/vato007/swiftrplidar.git", .branch("master")),
.package(url: "https://vato.ddns.net/gitlab/vato007/SwiftSerial.git", .branch("master")),
.package(url: "https://vato.ddns.net/gitlab/vato007/swift2dcar.git", .branch("master"))
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "SwiftyCar",
dependencies: [
"SwiftyGPIO",
.product(name: "GRPC", package: "grpc-swift"),
"SwiftRPLidar",
"SwiftSerial",
"Swift2dCar"]),
.testTarget(
name: "SwiftyCarTests",
dependencies: ["SwiftyCar"]),
]
)

View File

@@ -1,61 +0,0 @@
//
// LidarProvider.swift
//
//
// Created by Michael Pivato on 10/7/20.
//
import Foundation
import GRPC
import NIO
import SwiftProtobuf
import SwiftRPLidar
class LidarProvider: Persontracking_PersonTrackingProvider {
private let lidar: SwiftRPLidar
private var shouldScan: Bool = false
init(lidar: SwiftRPLidar) {
self.lidar = lidar
}
func set_tracking_group(request: Persontracking_Int32Value, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
}
func stop_tracking(request: Google_Protobuf_Empty, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
shouldScan = false
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
}
func start_tracking(request: Google_Protobuf_Empty, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
}
func record(request: Google_Protobuf_BoolValue, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
}
func save_lidar(request: MotorControl_SaveRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
}
func lidar_stream(request: Persontracking_StreamMessage, context: StreamingResponseCallContext<Persontracking_PointScan>) -> EventLoopFuture<GRPCStatus> {
shouldScan = true
try! lidar.iterScans{scan in
_ = context.sendResponse(.with{protoScan in
protoScan.points = scan.map{ point in
Persontracking_Point.with{ protoPoint in
protoPoint.angle = Double(point.angle)
protoPoint.distance = Double(point.distance)
// Placeholder group number.
protoPoint.groupNumber = 0
}
}
})
return shouldScan
}
return context.eventLoop.makeSucceededFuture(.ok)
}
}

View File

@@ -1,58 +0,0 @@
//
// MotorProvider.swift
//
//
// Created by Michael Pivato on 13/5/20.
//
import Foundation
import GRPC
import NIO
import SwiftProtobuf
import Swift2dCar
class MotorProvider: MotorControl_CarControlProvider{
private var vehicle: Vehicle2D
init(vehicle: Vehicle2D){
self.vehicle = vehicle
}
func set_throttle(request: MotorControl_ThrottleRequest, context: StatusOnlyCallContext) -> EventLoopFuture<MotorControl_ThrottleResponse> {
self.vehicle.throttle = request.throttle
return context.eventLoop.makeSucceededFuture(.with{ throttle in
throttle.throttleSet = true
})
}
func set_steering(request: MotorControl_SteeringRequest, context: StatusOnlyCallContext) -> EventLoopFuture<MotorControl_SteeringResponse> {
self.vehicle.steering = request.steering
return context.eventLoop.makeSucceededFuture(.with{
$0.steeringSet = true
})
}
func stream_vehicle_2d(context: UnaryResponseCallContext<Google_Protobuf_Empty>) -> EventLoopFuture<(StreamEvent<MotorControl_Vehicle2DRequest>) -> Void> {
return context.eventLoop.makeSucceededFuture({event in
switch event{
case .message(let movement):
self.vehicle.throttle = movement.throttle.throttle
self.vehicle.steering = movement.steering.steering
case .end:
context.responsePromise.succeed(Google_Protobuf_Empty())
}
})
}
func record(request: MotorControl_RecordingReqeust, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
// TODO: Recording...
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
}
func save_recorded_data(request: MotorControl_SaveRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
// TODO Recording...
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
}
}

View File

@@ -1,52 +0,0 @@
//
// Vehicle.swift
//
//
// Created by Michael Pivato on 8/5/20.
//
import Foundation
import Swift2dCar
public typealias ThrottleHandler = (_ magnitude: Float) -> Float
public typealias SteeringHandler = (_ angle: Float) -> Float
public class IntelligentPiCar : RPiVehicle2D {
private var vehicleLength: Float?
private var throttleFunc: ThrottleHandler?
private var steeringFunc: SteeringHandler?
/**:
Calibration function for how the car moves (acoording to a bicycle model) for a given throttle/steering angle. This sets the way the
- Parameters
- carLength
*/
func calibrate(vehicleLength: Float, throttleFunc: @escaping ThrottleHandler, steeringFunc: @escaping SteeringHandler){
// Define a function that indicates how the throttle/steering should be set for given magnitude/angle to move.
self.vehicleLength = vehicleLength
self.throttleFunc = throttleFunc
self.steeringFunc = steeringFunc
}
/**
Move the car by the given magnitude and angle, depending on how the is known to move with apriori throttle/steering.
*/
func move2D(magnitude: Float, angle: Float) {
if let throttleFunc = self.throttleFunc {
// self.pwmThrottle.value = throttleFunc(magnitude)
}
if let steeringFunc = steeringFunc {
// self.pwmSteering.value = steeringFunc(angle)
}
}
/**
Move to the coordinates relative to the car. You must first calibrate the car. A bicycle model is assumed.
*/
func moveRelativeTo2D(x: Float, y: Float) {
// TODO: This function, has a lot of edge cases. Also is really
}
}

View File

@@ -1,45 +0,0 @@
//
// VehicleFactory.swift
//
//
// Created by Michael Pivato on 20/5/20.
//
import Foundation
import SwiftyGPIO
import Swift2dCar
import SwiftSerial
func getVehicle2D() throws -> Vehicle2D? {
// TODO: Clean up this factory, or see if we can get dependency injection working.
if let value = ProcessInfo.processInfo.environment["CAR_VEHICLE"] {
switch value{
case "VEHICLE_2D":
// Get car for rpi.
let pwms = SwiftyGPIO.hardwarePWMs(for:.RaspberryPi3)!
return try RPiVehicle2D(withThrottlePin: PWMHardwareServo(forPin: (pwms[0]?[.P18])!)!, withSteeringPin:PWMHardwareServo(forPin: (pwms[1]?[.P19])!)!)
case "VEHICLE_SERIAL":
// TODO: Get from environment variable. tty won't work in macos anyway.
// We share the serialport object, as cu will block on macOS (required by SwiftSerial), so can't open 2 of the same port.
let serialPort = SerialPort(path: "/dev/ttyUSB0")
// The port does not open/initialise inside of the ESP32ServoOutputs, as on macOS /dev/cu.* blocks.
try serialPort.openPort()
serialPort.setSettings(receiveRate: .baud115200, transmitRate: .baud115200, minimumBytesToRead: 1)
guard let throttlePin = Esp32ServoOutput(forChannel: 1, forPin: 14, onPort: serialPort) else {
print("Failed to create throttle pin.")
return nil
}
guard let steeringPin = Esp32ServoOutput(forChannel: 2, forPin: 12, onPort: serialPort) else {
print("Failed to create steering pin.")
return nil
}
return try RPiVehicle2D(withThrottlePin: PWMHardwareServo(forPin: throttlePin)!, withSteeringPin: PWMHardwareServo(forPin: steeringPin)!)
default:
return MockVehicle()
}
}
return MockVehicle()
}

View File

@@ -1,70 +0,0 @@
//
// main.swift
//
//
// Created by Michael Pivato on 8/5/20.
//
import NIO
import GRPC
import SwiftRPLidar
import SwiftSerial
func doServer() throws {
// Copied from examples
// Create an event loop group for the server to run on.
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
defer {
try! group.syncShutdownGracefully()
}
let lidar = createLidar()
try lidar.iterMeasurements{measruement in
print(measruement.quality)
return false
}
// Create a provider using the features we read.
let provider = try MotorProvider(vehicle: getVehicle2D()!)
let trackingProvider = LidarProvider(lidar: lidar)
// Start the server and print its address once it has started.
let server = Server.insecure(group: group)
.withServiceProviders([provider, trackingProvider])
.bind(host: "localhost", port: 0)
server.map {
$0.channel.localAddress
}.whenSuccess { address in
print("server started on port \(address!.port!)")
}
// Wait on the server's `onClose` future to stop the program from exiting.
_ = try server.flatMap {
$0.onClose
}.wait()
}
func createLidar() -> SwiftRPLidar{
return try! SwiftRPLidar(onPort: SerialPort(path: "/dev/cu.usbserial0001"))
}
// Entry-Point to the Swift Car Controller
print("Starting Server")
do{
try doServer()
}
catch{
print("Server failed")
}
extension SerialPort: LidarSerial{
public func setBaudrate(baudrate: Int) {
// TODO: handle different baudrates. Only need this for now.
switch baudrate{
default:
setSettings(receiveRate: .baud115200, transmitRate: .baud115200, minimumBytesToRead: 1)
}
}
}

View File

@@ -1,7 +0,0 @@
import XCTest
import SwiftyCarTests
var tests = [XCTestCaseEntry]()
tests += SwiftyCarTests.allTests()
XCTMain(tests)

View File

@@ -1,15 +0,0 @@
import XCTest
@testable import SwiftyCar
final class SwiftyCarTests: XCTestCase {
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
// XCTAssertEqual(SwiftyCar().text, "Hello, World!")
}
static var allTests = [
("testExample", testExample),
]
}

View File

@@ -1,9 +0,0 @@
import XCTest
#if !canImport(ObjectiveC)
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(SwiftyCarTests.allTests),
]
}
#endif

View File

@@ -1,16 +0,0 @@
configurations{
swift {
canBeConsumed = false
canBeResolved = true
}
}
dependencies {
swift project(path: ':protobuf', configuration: 'swift')
}
task copySwiftCode(type: Copy, dependsOn: configurations.swift) {
// Copy python protobuf code from proto project.
from zipTree(configurations.swift.asPath)
into './Sources/SwiftyCar'
}

View File

@@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};
use serialport::SerialPort;
// TODO: Should be returning results in these traits
@@ -89,14 +91,14 @@ pub mod rppal {
}
pub struct Esp32SerialPwmServo<T: SerialPort> {
serial_port: T,
serial_port: Arc<Mutex<T>>,
value: f64,
channel: u8,
pin: u8,
}
impl<T: SerialPort> Esp32SerialPwmServo<T> {
pub fn new(serial_port: T, channel: u8, pin: u8) -> Esp32SerialPwmServo<T> {
pub fn new(serial_port: Arc<Mutex<T>>, channel: u8, pin: u8) -> Esp32SerialPwmServo<T> {
Esp32SerialPwmServo {
serial_port,
value: 0.,
@@ -108,7 +110,11 @@ impl<T: SerialPort> Esp32SerialPwmServo<T> {
impl<T: SerialPort> Esp32SerialPwmServo<T> {
fn init_pwm(&mut self) {
let bytes_written = self.serial_port.write(&[0, 1, self.channel, self.pin]);
let bytes_written = self
.serial_port
.lock()
.unwrap()
.write(&[0, 1, self.channel, self.pin]);
// TODO: Better error handling
match bytes_written {
Ok(size) => println!("{}", size),
@@ -133,6 +139,8 @@ impl<T: SerialPort> Servo for Esp32SerialPwmServo<T> {
self.value = temp_value;
let bytes_written = self
.serial_port
.lock()
.unwrap()
.write(&[self.channel, ((value + 1.) / 2. * 255.) as u8]);
// TODO: Better error handling
match bytes_written {

View File

@@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};
use car_rs::{Esp32SerialPwmServo, ServoVehicle};
use grpcserver::{
motor_control_service::car_control_server::CarControlServer, MotorControlService,
@@ -15,13 +17,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let serial_port = serialport::new("", 32400)
.open_native()
.expect("Could not open serial port");
let throttle_port = serialport::new("", 32400)
.open_native()
.expect("Could not open serial port");
let servo = Esp32SerialPwmServo::new(serial_port, 1, 12);
let throttle_servo = Esp32SerialPwmServo::new(throttle_port, 2, 18);
let serial_servo = Arc::new(Mutex::new(serial_port));
let steering_servo = Esp32SerialPwmServo::new(serial_servo.clone(), 1, 12);
let throttle_servo = Esp32SerialPwmServo::new(serial_servo.clone(), 2, 18);
let motor_control = MotorControlService::new(ServoVehicle::new(servo, throttle_servo));
let motor_control = MotorControlService::new(ServoVehicle::new(steering_servo, throttle_servo));
let svc = CarControlServer::new(motor_control);
Server::builder().add_service(svc).serve(addr).await?;