Merge branch 'esp32' into 'master'
Esp32 See merge request vato007/picar!5
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -32,4 +32,6 @@ SwiftyCar/Sources/SwiftyCar/car
|
|||||||
xcuserdata/
|
xcuserdata/
|
||||||
.settings
|
.settings
|
||||||
.project
|
.project
|
||||||
.classpath
|
.classpath
|
||||||
|
|
||||||
|
.vscode/settings.json
|
||||||
@@ -15,6 +15,12 @@ build_pycar_docker:
|
|||||||
- echo ${DOCKER_PASSWORD} | docker login vato.ddns.net:8083 --username ${DOCKER_USERNAME} --password-stdin
|
- echo ${DOCKER_PASSWORD} | docker login vato.ddns.net:8083 --username ${DOCKER_USERNAME} --password-stdin
|
||||||
- docker build -f pycar/Dockerfile --build-arg PYPI_USERNAME=${PYPI_USERNAME} --build-arg PYPI_PASSWORD=${PYPI_PASSWORD} -t vato.ddns.net:8082/pycar:latest pycar
|
- docker build -f pycar/Dockerfile --build-arg PYPI_USERNAME=${PYPI_USERNAME} --build-arg PYPI_PASSWORD=${PYPI_PASSWORD} -t vato.ddns.net:8082/pycar:latest pycar
|
||||||
|
|
||||||
|
build_esp32:
|
||||||
|
image: vato.ddns.net:8083/shaguarger/platformio
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- platformio ci --project-conf esp32/platformio.ini esp32
|
||||||
|
|
||||||
deploy_pycar:
|
deploy_pycar:
|
||||||
image: vato.ddns.net:8083/python-infra:buster
|
image: vato.ddns.net:8083/python-infra:buster
|
||||||
stage: deploy
|
stage: deploy
|
||||||
|
|||||||
18
.idea/codeStyles/Project.xml
generated
18
.idea/codeStyles/Project.xml
generated
@@ -1,5 +1,23 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
|
<value>
|
||||||
|
<package name="java.util" alias="false" withSubpackages="false" />
|
||||||
|
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||||
|
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||||
|
<value>
|
||||||
|
<package name="" alias="false" withSubpackages="true" />
|
||||||
|
<package name="java" alias="false" withSubpackages="true" />
|
||||||
|
<package name="javax" alias="false" withSubpackages="true" />
|
||||||
|
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||||
|
<package name="" alias="true" withSubpackages="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</JetCodeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
|||||||
3
.idea/gradle.xml
generated
3
.idea/gradle.xml
generated
@@ -11,9 +11,10 @@
|
|||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
<option value="$PROJECT_DIR$/CarControlleriOS" />
|
<option value="$PROJECT_DIR$/CarControlleriOS" />
|
||||||
|
<option value="$PROJECT_DIR$/SwiftyCar" />
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
<option value="$PROJECT_DIR$/car" />
|
|
||||||
<option value="$PROJECT_DIR$/protobuf" />
|
<option value="$PROJECT_DIR$/protobuf" />
|
||||||
|
<option value="$PROJECT_DIR$/pycar" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
|||||||
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@@ -10,7 +10,8 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"module": "car",
|
"module": "car",
|
||||||
"env": {
|
"env": {
|
||||||
"CAR_VEHICLE": "CAR_MOCK",
|
"CAR_VEHICLE": "VEHICLE_MOCK",
|
||||||
|
// "CAR_VEHICLE": "VEHICLE_SERIAL",
|
||||||
// "CAR_LIDAR": "/dev/tty.usbserial-0001",
|
// "CAR_LIDAR": "/dev/tty.usbserial-0001",
|
||||||
"CAR_LIDAR": "LIDAR_MOCK"
|
"CAR_LIDAR": "LIDAR_MOCK"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
"repositoryURL": "https://github.com/grpc/grpc-swift.git",
|
"repositoryURL": "https://github.com/grpc/grpc-swift.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "b83ee1ee2caa0660eb02444977b9b6e353c2adbf",
|
"revision": "640b0ef1d0be63bda0ada86786cfda678ab2aae9",
|
||||||
"version": "1.0.0-alpha.12"
|
"version": "1.0.0-alpha.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
"repositoryURL": "https://github.com/apple/swift-log.git",
|
"repositoryURL": "https://github.com/apple/swift-log.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "74d7b91ceebc85daf387ebb206003f78813f71aa",
|
"revision": "173f567a2dfec11d74588eea82cecea555bdc0bc",
|
||||||
"version": "1.2.0"
|
"version": "1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "40bdad80882d307abe2c0bb36cf3bd4d3e03fe04",
|
"revision": "5fc24345f92ec4c274121776c215ab0aa1ed4d50",
|
||||||
"version": "2.16.1"
|
"version": "2.22.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -33,8 +33,8 @@
|
|||||||
"repositoryURL": "https://github.com/apple/swift-nio-http2.git",
|
"repositoryURL": "https://github.com/apple/swift-nio-http2.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "82eb3fa0974b838358ee46bc6c5381e5ae5de6b9",
|
"revision": "1e68e51752be0b43c5a0ef35818c1dd24d13e77c",
|
||||||
"version": "1.11.0"
|
"version": "1.14.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -42,8 +42,8 @@
|
|||||||
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
|
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "ae213938e151964aa691f0e902462fbe06baeeb6",
|
"revision": "ea1dfd64193bf5af4490635a4a44c4fb43b1e1ae",
|
||||||
"version": "2.7.1"
|
"version": "2.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -51,8 +51,8 @@
|
|||||||
"repositoryURL": "https://github.com/apple/swift-nio-transport-services.git",
|
"repositoryURL": "https://github.com/apple/swift-nio-transport-services.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "85a67aea7caf5396ed599543dd23cffeb6dbbf96",
|
"revision": "bb56586c4cab9a79dce6ec4738baddb5802c5de7",
|
||||||
"version": "1.5.1"
|
"version": "1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -60,8 +60,35 @@
|
|||||||
"repositoryURL": "https://github.com/apple/swift-protobuf.git",
|
"repositoryURL": "https://github.com/apple/swift-protobuf.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "7790acf0a81d08429cb20375bf42a8c7d279c5fe",
|
"revision": "0279688c9fc5a40028e1b5bb0cb56534a45a6020",
|
||||||
"version": "1.8.0"
|
"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
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -69,8 +96,8 @@
|
|||||||
"repositoryURL": "https://github.com/uraimo/SwiftyGPIO.git",
|
"repositoryURL": "https://github.com/uraimo/SwiftyGPIO.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "4127ff9dd5c6aa8acb6be34b7ce2af2f6e0b942d",
|
"revision": "2038228e020cf12a62012b1ebe36bb9b8e6fdb6a",
|
||||||
"version": "1.1.14"
|
"version": "1.2.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.0.0-alpha.19"),
|
.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://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/swiftrplidar.git", .branch("master")),
|
||||||
.package(url: "https://vato.ddns.net/gitlab/vato007/SwiftSerial.git", .branch("dtr_support")),
|
.package(url: "https://vato.ddns.net/gitlab/vato007/SwiftSerial.git", .branch("master")),
|
||||||
.package(url: "https://vato.ddns.net/gitlab/vato007/swift2dcar.git", .branch("master"))
|
.package(url: "https://vato.ddns.net/gitlab/vato007/swift2dcar.git", .branch("master"))
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
@@ -22,7 +22,12 @@ let package = Package(
|
|||||||
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
|
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
|
||||||
.target(
|
.target(
|
||||||
name: "SwiftyCar",
|
name: "SwiftyCar",
|
||||||
dependencies: ["SwiftyGPIO", .product(name: "GRPC", package: "grpc-swift"), "SwiftRPLidar", "SwiftSerial", "Swift2dCar"]),
|
dependencies: [
|
||||||
|
"SwiftyGPIO",
|
||||||
|
.product(name: "GRPC", package: "grpc-swift"),
|
||||||
|
"SwiftRPLidar",
|
||||||
|
"SwiftSerial",
|
||||||
|
"Swift2dCar"]),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "SwiftyCarTests",
|
name: "SwiftyCarTests",
|
||||||
dependencies: ["SwiftyCar"]),
|
dependencies: ["SwiftyCar"]),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// VehicleFactory.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Michael Pivato on 20/5/20.
|
// Created by Michael Pivato on 20/5/20.
|
||||||
@@ -8,16 +8,35 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftyGPIO
|
import SwiftyGPIO
|
||||||
import Swift2dCar
|
import Swift2dCar
|
||||||
|
import SwiftSerial
|
||||||
|
|
||||||
func getVehicle2D() throws -> Vehicle2D {
|
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"] {
|
if let value = ProcessInfo.processInfo.environment["CAR_VEHICLE"] {
|
||||||
switch value{
|
switch value{
|
||||||
case "CAR_2D":
|
case "VEHICLE_2D":
|
||||||
// Get car for rpi.
|
// Get car for rpi.
|
||||||
let pwms = SwiftyGPIO.hardwarePWMs(for:.RaspberryPi3)!
|
let pwms = SwiftyGPIO.hardwarePWMs(for:.RaspberryPi3)!
|
||||||
|
|
||||||
// Read the feature database.
|
|
||||||
return try RPiVehicle2D(withThrottlePin: PWMHardwareServo(forPin: (pwms[0]?[.P18])!)!, withSteeringPin:PWMHardwareServo(forPin: (pwms[1]?[.P19])!)!)
|
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 initPort.openPort()
|
||||||
|
initPort.setSettings(receiveRate: .baud115200, transmitRate: .baud115200, minimumBytesToRead: 1)x
|
||||||
|
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:
|
default:
|
||||||
return MockVehicle()
|
return MockVehicle()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func doServer() throws {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Create a provider using the features we read.
|
// Create a provider using the features we read.
|
||||||
let provider = try MotorProvider(vehicle: getVehicle2D())
|
let provider = try MotorProvider(vehicle: getVehicle2D()!)
|
||||||
let trackingProvider = LidarProvider(lidar: lidar)
|
let trackingProvider = LidarProvider(lidar: lidar)
|
||||||
|
|
||||||
// Start the server and print its address once it has started.
|
// Start the server and print its address once it has started.
|
||||||
|
|||||||
5
esp32/.gitignore
vendored
Normal file
5
esp32/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
7
esp32/.vscode/extensions.json
vendored
Normal file
7
esp32/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
]
|
||||||
|
}
|
||||||
28
esp32/README.md
Normal file
28
esp32/README.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
This module includes an arduino sketch designed to run on the ESP32.
|
||||||
|
|
||||||
|
The sketch takes simple input from serial (ESP32 microusb port), and converts the input to a duty cycle that can be used on servos.
|
||||||
|
|
||||||
|
The protocol specification is as follows, assuming an array of bytes:
|
||||||
|
|
||||||
|
|
||||||
|
| Byte Number | Value Description |
|
||||||
|
| :---------- | ----------: |
|
||||||
|
| 0 | 0 if Calibrating a servo. Higher values indicates channel number to set duty cycle on. |
|
||||||
|
| 1 | If byte 0 = 0: number of servos to calibrate. Else, the new duty cycle value for the channel specified in byte 0. |
|
||||||
|
|
||||||
|
When setting the duty cycle, the current min angle is 0, and the max angle is 255, which allows the entire byte to be used for setting the duty cycle.
|
||||||
|
|
||||||
|
The table below describes the byte format for calibrating a servo. This array of bytes can be repeated for the given number of servos in byte 1:
|
||||||
|
|
||||||
|
| Byte Number | Value Description |
|
||||||
|
| :---------- | ----------: |
|
||||||
|
| 0 | Servo channel to set (this will be byte 0 when setting duty cycle, so don't use 0). |
|
||||||
|
| 1 | The pin number to setup |
|
||||||
|
|
||||||
|
The min/max pulse widths are hardcoded to 1000us/2000us respectively.
|
||||||
|
|
||||||
|
At the end of each loop, the entire array will be read, to flush any data that may cause issues later.
|
||||||
|
|
||||||
|
Upcoming (TODO):
|
||||||
|
- Use bit shift to allow 12 bits to be used for the duty cycle range, as there can only be a max of 16 channels anyway (4 bits).
|
||||||
|
- Consider protobuf or msgpack for serialisation format, for more advanced use cases, and more maintainable communication formats (currently changing the message format will require changes to the entire protocol)
|
||||||
39
esp32/include/README
Normal file
39
esp32/include/README
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||||
46
esp32/lib/README
Normal file
46
esp32/lib/README
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||||
15
esp32/platformio.ini
Normal file
15
esp32/platformio.ini
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:esp32doit-devkit-v1]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp32doit-devkit-v1
|
||||||
|
framework = arduino
|
||||||
|
lib_deps = roboticsbrno/ServoESP32@^1.0.3
|
||||||
59
esp32/src/main.cpp
Normal file
59
esp32/src/main.cpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include "Servo.h"
|
||||||
|
|
||||||
|
// Min/max widths, used to calculate min/max duty cycles.
|
||||||
|
#define MIN_PULSE_WIDTH 1000
|
||||||
|
#define MAX_PULSE_WIDTH 2000
|
||||||
|
|
||||||
|
#define MIN_ANGLE 0
|
||||||
|
#define MAX_ANGLE 255
|
||||||
|
|
||||||
|
std::map<uint8_t, Servo *> servos;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupServos(uint8_t size, uint8_t *calibrationValues)
|
||||||
|
{
|
||||||
|
// We assume there are 3 bytes per servo. Ignore if there aren't.
|
||||||
|
if (size % 3 == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size; i += 2)
|
||||||
|
{
|
||||||
|
Servo *newServo = new Servo();
|
||||||
|
newServo->attach(calibrationValues[i + 1], calibrationValues[i], MIN_ANGLE, MAX_ANGLE, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
|
||||||
|
servos.insert(std::make_pair(calibrationValues[i], newServo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void modifyServo(uint8_t channel, uint8_t newAngle)
|
||||||
|
{
|
||||||
|
if(servos.count(channel) > 0){
|
||||||
|
servos[channel]->write(newAngle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
uint8_t *header = new uint8_t[2];
|
||||||
|
Serial.readBytes(header, 2);
|
||||||
|
|
||||||
|
if (header[0] == 0)
|
||||||
|
{
|
||||||
|
uint8_t *calibration = new uint8_t[2 * header[1]];
|
||||||
|
Serial.readBytes(calibration, header[1]);
|
||||||
|
setupServos(header[1], calibration);
|
||||||
|
delete [] calibration;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
modifyServo(header[0], header[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete [] header;
|
||||||
|
}
|
||||||
11
esp32/test/README
Normal file
11
esp32/test/README
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Unit Testing and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||||
26
pycar/src/car/control/gpio/abstract_vehicle.py
Normal file
26
pycar/src/car/control/gpio/abstract_vehicle.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from abc import ABC, abstractmethod, abstractproperty
|
||||||
|
|
||||||
|
class AbstractVehicle(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
@property
|
||||||
|
def throttle(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
@throttle.setter
|
||||||
|
def throttle(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
@property
|
||||||
|
def steering(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
@steering.setter
|
||||||
|
def throttle(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
@@ -1,18 +1,25 @@
|
|||||||
|
from car.control.gpio.abstract_vehicle import AbstractVehicle
|
||||||
|
from car.control.gpio.serial_vehicle import SerialVehicle
|
||||||
from .mockvehicle import MockVehicle
|
from .mockvehicle import MockVehicle
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
def get_vehicle(motor_pin=19, steering_pin=18):
|
# TODO: Remove need for motor/steering pin, instead retrieve from env variable.
|
||||||
|
# TODO: Dependency injectino in python?
|
||||||
|
def get_vehicle(motor_pin=19, steering_pin=18) -> AbstractVehicle:
|
||||||
ENV_CAR = None if 'CAR_VEHICLE' not in os.environ else os.environ['CAR_VEHICLE']
|
ENV_CAR = None if 'CAR_VEHICLE' not in os.environ else os.environ['CAR_VEHICLE']
|
||||||
if ENV_CAR == "CAR_2D":
|
if ENV_CAR == "VEHICLE_2D":
|
||||||
try:
|
try:
|
||||||
from .vehicle import Vehicle
|
from .vehicle import Vehicle
|
||||||
return Vehicle(motor_pin, steering_pin)
|
return Vehicle(motor_pin, steering_pin)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print(
|
print(
|
||||||
'Could not import CAR_2D vehicle. Have you installed the GPIOZERO package?')
|
'Could not import CAR_2D vehicle. Have you installed the GPIOZERO package?')
|
||||||
elif ENV_CAR == "CAR_MOCK":
|
elif ENV_CAR == "VEHICLE_MOCK":
|
||||||
return MockVehicle(motor_pin, steering_pin)
|
return MockVehicle()
|
||||||
|
elif ENV_CAR == "VEHICLE_SERIAL":
|
||||||
|
# TODO: Pins in environment variables.
|
||||||
|
return SerialVehicle()
|
||||||
else:
|
else:
|
||||||
print('No valid vehicle found. Have you set the CAR_VEHICLE environment variable?')
|
print('No valid vehicle found. Have you set the CAR_VEHICLE environment variable?')
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
# A dummy vehicle class to use when
|
# A dummy vehicle class to use when testing/not connected to a real device.
|
||||||
class MockVehicle:
|
from car.control.gpio.abstract_vehicle import AbstractVehicle
|
||||||
def __init__(self, motor_pin=19, servo_pin=18):
|
|
||||||
self.motor_pin = motor_pin
|
|
||||||
self.steering_pin = servo_pin
|
class MockVehicle(AbstractVehicle):
|
||||||
|
def __init__(self):
|
||||||
print('Using Mock Vehicle')
|
print('Using Mock Vehicle')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -23,21 +24,5 @@ class MockVehicle:
|
|||||||
def steering(self, value):
|
def steering(self, value):
|
||||||
self._steering = value
|
self._steering = value
|
||||||
|
|
||||||
@property
|
|
||||||
def motor_pin(self):
|
|
||||||
return self._motor_pin
|
|
||||||
|
|
||||||
@motor_pin.setter
|
|
||||||
def motor_pin(self, value):
|
|
||||||
self._motor_pin = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def steering_pin(self):
|
|
||||||
return self._steering_pin
|
|
||||||
|
|
||||||
@steering_pin.setter
|
|
||||||
def steering_pin(self, value):
|
|
||||||
self._steering_pin = value
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.throttle = 0
|
self.throttle = 0
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
from .abstract_vehicle import AbstractVehicle
|
||||||
|
|
||||||
|
|
||||||
class VehicleRecordingDecorator:
|
class VehicleRecordingDecorator(AbstractVehicle):
|
||||||
def __init__(self, vehicle):
|
def __init__(self, vehicle):
|
||||||
"""
|
"""
|
||||||
A decorator for a vehicle object to record the changes in steering/throttle.
|
A decorator for a vehicle object to record the changes in steering/throttle.
|
||||||
This will be recorded to memory, and will save to the given file when save is called.
|
This will be recorded to memory, and will save to the given file when save_data is called.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@@ -66,21 +67,5 @@ class VehicleRecordingDecorator:
|
|||||||
's,' + str(value) + ',' + datetime.datetime.now().isoformat(sep=' ', timespec='seconds'))
|
's,' + str(value) + ',' + datetime.datetime.now().isoformat(sep=' ', timespec='seconds'))
|
||||||
self._vehicle.steering = value
|
self._vehicle.steering = value
|
||||||
|
|
||||||
@property
|
|
||||||
def motor_pin(self):
|
|
||||||
return self._vehicle.motor_pin
|
|
||||||
|
|
||||||
@motor_pin.setter
|
|
||||||
def motor_pin(self, value):
|
|
||||||
self._vehicle.motor_pin = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def steering_pin(self):
|
|
||||||
return self._vehicle.steering_pin
|
|
||||||
|
|
||||||
@steering_pin.setter
|
|
||||||
def steering_pin(self, value):
|
|
||||||
self._vehicle.steering_pin = value
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.throttle = 0
|
self.throttle = 0
|
||||||
|
|||||||
42
pycar/src/car/control/gpio/serial_vehicle.py
Normal file
42
pycar/src/car/control/gpio/serial_vehicle.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
from .abstract_vehicle import AbstractVehicle
|
||||||
|
from serial import Serial
|
||||||
|
|
||||||
|
STEERING_CHANNEL = 1
|
||||||
|
THROTTLE_CHANNEL = 2
|
||||||
|
|
||||||
|
|
||||||
|
class SerialVehicle(AbstractVehicle):
|
||||||
|
|
||||||
|
def __init__(self, serial_port='/dev/ttyUSB0', steering_pin=12, throttle_pin=14):
|
||||||
|
self.serial_port = Serial(port=serial_port, baudrate=115200)
|
||||||
|
|
||||||
|
# Initialise the channels and pins on esp32.
|
||||||
|
self._init_esp32_pwm(steering_pin, throttle_pin)
|
||||||
|
self.throttle = 0
|
||||||
|
self.steering = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def throttle(self) -> float:
|
||||||
|
return self.throttle
|
||||||
|
|
||||||
|
@throttle.setter
|
||||||
|
def throttle(self, new_throttle: float):
|
||||||
|
self.throttle = new_throttle
|
||||||
|
self._set_servo_value(THROTTLE_CHANNEL, new_throttle)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def steering(self) -> float:
|
||||||
|
return self.steering
|
||||||
|
|
||||||
|
@steering.setter
|
||||||
|
def steering(self, new_steering: float):
|
||||||
|
self.steering = new_steering
|
||||||
|
self._set_servo_value(STEERING_CHANNEL, new_steering)
|
||||||
|
|
||||||
|
def _set_servo_value(self, channel, value):
|
||||||
|
# Scale the value to a byte, as 0-255 is the angle range for the esp32 servo.
|
||||||
|
self.serial_port.write(bytes[channel, (value + 1) / 2 * 255])
|
||||||
|
|
||||||
|
def _init_esp32_pwm(self, steering_pin, throttle_pin):
|
||||||
|
self.serial_port.write(bytes([0, 2, STEERING_CHANNEL,
|
||||||
|
steering_pin, THROTTLE_CHANNEL, throttle_pin]))
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from .abstract_vehicle import AbstractVehicle
|
||||||
from gpiozero import Servo, Device
|
from gpiozero import Servo, Device
|
||||||
from gpiozero.pins.pigpio import PiGPIOFactory
|
from gpiozero.pins.pigpio import PiGPIOFactory
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -29,7 +30,7 @@ def _is_pin_valid(pin):
|
|||||||
# two servos for controls (e.g. drone, dog)
|
# two servos for controls (e.g. drone, dog)
|
||||||
|
|
||||||
|
|
||||||
class Vehicle:
|
class Vehicle(AbstractVehicle):
|
||||||
def __init__(self, motor_pin=19, servo_pin=18):
|
def __init__(self, motor_pin=19, servo_pin=18):
|
||||||
subprocess.call(['pigpiod'])
|
subprocess.call(['pigpiod'])
|
||||||
Device.pin_factory = PiGPIOFactory()
|
Device.pin_factory = PiGPIOFactory()
|
||||||
|
|||||||
Reference in New Issue
Block a user