commit 2777d5c48d11ccf3b37307781c38a569e23aae5e Author: Piv <18462828+Piv200@users.noreply.github.com> Date: Mon Aug 31 22:47:00 2020 +0930 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95c4320 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..e2aed8f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,6 @@ +image: vato.ddns.net:8083/swift:5.2.4 + +build: + stage: build + script: + - swift build diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..c8a6836 --- /dev/null +++ b/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version:5.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Swift2dCar", + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "Swift2dCar", + targets: ["Swift2dCar"]), + ], + dependencies: [ + .package(url: "https://github.com/uraimo/SwiftyGPIO.git", from: "1.2.5"), + ], + 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: "Swift2dCar", + dependencies: ["SwiftyGPIO"]), + .testTarget( + name: "Swift2dCarTests", + dependencies: ["Swift2dCar"]), + ] +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7391f8 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Swift2dCar + +A description of this package. diff --git a/Sources/Swift2dCar/Servo.swift b/Sources/Swift2dCar/Servo.swift new file mode 100644 index 0000000..bbf2fe3 --- /dev/null +++ b/Sources/Swift2dCar/Servo.swift @@ -0,0 +1,113 @@ +// +// Servo.swift +// +// Simple wrappers for PwmOutput I'll create as I go along, to match gpioZero's implementation I'm currently using. +// See gpiozero code here: https://github.com/gpiozero/gpiozero/blob/master/gpiozero/output_devices.py +// +// Created by Michael Pivato on 9/5/20. +// + +import Foundation +import SwiftyGPIO + +enum ServoError: Error{ + case invalidMinPulseWidth + case invalidMaxPulseWidth +} + +protocol Servo { + var value: Float {get set} + + var minPulseWidth: Float {get} + var maxPulseWidth: Float {get} + var pulseWidth: Float {get} + + func min() + func mid() + func max() + func detach() +} + +public class PWMHardwareServo : Servo{ + private (set) var frameWidth : Float + private var minDc: Float + private var dcRange: Float + private var minValue: Float + private var valueRange: Float + private var internalValue: Float + + // TODO: Use a factory to provide the correct pwm rather than passing the pin in. + private var device: PWMOutput + private var currentPulseWIdth: Float + + var minPulseWidth: Float { + get{ + return minDc * frameWidth + } + } + + var maxPulseWidth: Float{ + get{ + return (dcRange * frameWidth) + minPulseWidth + } + } + + var pulseWidth: Float{ + get{ + return internalValue * frameWidth + } + } + + var value: Float{ + get{ + internalValue + } + set(newValue){ + internalValue = newValue + device.startPWM(period: Int(frameWidth), + // Multiply by 100 to get into percentage. I don't like that... + duty: minDc + dcRange * ((internalValue - minValue) / valueRange) * 100) + } + } + + + init?(forPin pin: PWMOutput, startingAt initialValue: Float = 0, withMinPulseWidth minPulseWidth: Float = 1000000, + withMaxPulseWidth maxPulseWidth: Float = 2000000, withFrameWidth frameWidth: Float = 20000000) throws { + if(minPulseWidth >= maxPulseWidth){ + throw ServoError.invalidMinPulseWidth + } + + if(maxPulseWidth >= frameWidth){ + throw ServoError.invalidMaxPulseWidth + } + self.frameWidth = frameWidth + self.minDc = minPulseWidth / frameWidth + self.dcRange = (maxPulseWidth - minPulseWidth) / frameWidth + self.minValue = -1 + self.valueRange = 2 + + self.currentPulseWIdth = 0 + + // Initialise pin immediately. + self.device = pin + self.device.initPWM() + self.internalValue = initialValue + self.device.startPWM(period: Int(self.frameWidth), duty: self.minDc + self.dcRange * ((internalValue - self.minValue) / self.valueRange) * 100) + } + + func min(){ + self.value = -1 + } + + func mid(){ + self.value = 0 + } + + func max() { + self.value = 1 + } + + func detach(){ + self.device.stopPWM() + } +} diff --git a/Sources/Swift2dCar/Vehicle.swift b/Sources/Swift2dCar/Vehicle.swift new file mode 100644 index 0000000..19d94d1 --- /dev/null +++ b/Sources/Swift2dCar/Vehicle.swift @@ -0,0 +1,67 @@ +// +// Vehicle.swift +// +// +// Created by Michael Pivato on 8/5/20. +// + +import Foundation +import SwiftyGPIO + + +protocol Vehicle2D{ + var throttle: Float {get set} + var steering: Float {get set} + mutating func move2D(magnitude: Float, angle: Float) +} + +class MockVehicle: Vehicle2D { + var throttle: Float = 0 + var steering: Float = 0 + + func move2D(magnitude: Float, angle: Float) { + + } +} + +class RPiVehicle2D: Vehicle2D{ + public var pwmThrottle: Servo + public var pwmSteering: Servo + + var throttle: Float{ + get{ + return pwmThrottle.value + } + set(value){ + pwmThrottle.value = value + } + } + + var steering: Float{ + get{ + return pwmSteering.value + } + set(value){ + pwmSteering.value = value + } + } + + init(withThrottlePin: Servo, withSteeringPin: Servo){ + pwmThrottle = withThrottlePin + pwmSteering = withSteeringPin + } + + func calibrate(){ + // Define a function that indicates how the throttle/steering should be set for given magnitude/angle to move. + } + + func move2D(magnitude: Float, angle: Float) { + + } + + func stop(){ + pwmThrottle.detach() + pwmSteering.detach() + } + +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift new file mode 100644 index 0000000..9f8bdc4 --- /dev/null +++ b/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import Swift2dCarTests + +var tests = [XCTestCaseEntry]() +tests += Swift2dCarTests.allTests() +XCTMain(tests) diff --git a/Tests/Swift2dCarTests/Swift2dCarTests.swift b/Tests/Swift2dCarTests/Swift2dCarTests.swift new file mode 100644 index 0000000..0608c3a --- /dev/null +++ b/Tests/Swift2dCarTests/Swift2dCarTests.swift @@ -0,0 +1,15 @@ +import XCTest +@testable import Swift2dCar + +final class Swift2dCarTests: 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(Swift2dCar().text, "Hello, World!") + } + + static var allTests = [ + ("testExample", testExample), + ] +} diff --git a/Tests/Swift2dCarTests/XCTestManifests.swift b/Tests/Swift2dCarTests/XCTestManifests.swift new file mode 100644 index 0000000..8551cb8 --- /dev/null +++ b/Tests/Swift2dCarTests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) +public func allTests() -> [XCTestCaseEntry] { + return [ + testCase(Swift2dCarTests.allTests), + ] +} +#endif