Initial Commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
6
.gitlab-ci.yml
Normal file
6
.gitlab-ci.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
image: vato.ddns.net:8083/swift:5.2.4
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- swift build
|
||||||
27
Package.swift
Normal file
27
Package.swift
Normal file
@@ -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"]),
|
||||||
|
]
|
||||||
|
)
|
||||||
113
Sources/Swift2dCar/Servo.swift
Normal file
113
Sources/Swift2dCar/Servo.swift
Normal file
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
67
Sources/Swift2dCar/Vehicle.swift
Normal file
67
Sources/Swift2dCar/Vehicle.swift
Normal file
@@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
Tests/LinuxMain.swift
Normal file
7
Tests/LinuxMain.swift
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import XCTest
|
||||||
|
|
||||||
|
import Swift2dCarTests
|
||||||
|
|
||||||
|
var tests = [XCTestCaseEntry]()
|
||||||
|
tests += Swift2dCarTests.allTests()
|
||||||
|
XCTMain(tests)
|
||||||
15
Tests/Swift2dCarTests/Swift2dCarTests.swift
Normal file
15
Tests/Swift2dCarTests/Swift2dCarTests.swift
Normal file
@@ -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),
|
||||||
|
]
|
||||||
|
}
|
||||||
9
Tests/Swift2dCarTests/XCTestManifests.swift
Normal file
9
Tests/Swift2dCarTests/XCTestManifests.swift
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import XCTest
|
||||||
|
|
||||||
|
#if !canImport(ObjectiveC)
|
||||||
|
public func allTests() -> [XCTestCaseEntry] {
|
||||||
|
return [
|
||||||
|
testCase(Swift2dCarTests.allTests),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user