Compare commits
475
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45f2ef7fef | ||
|
|
cd67cb1689 | ||
|
|
501d72fb1e | ||
|
|
340752857d | ||
|
|
1cd36de5d5 | ||
|
|
2431113131 | ||
|
|
722cc4eb4e | ||
|
|
48baedf202 | ||
|
|
fdbae6198b | ||
|
|
1f81a6bc96 | ||
|
|
89e33cf196 | ||
|
|
7924ef474e | ||
|
|
d8c85bdc95 | ||
|
|
19c5afa329 | ||
|
|
27aea767d8 | ||
|
|
062a4e77ce | ||
|
|
68b273376d | ||
|
|
698ed02987 | ||
|
|
ecaf41265a | ||
|
|
dd93098f39 | ||
|
|
6d4567facf | ||
|
|
7f52462b68 | ||
|
|
945a04ee66 | ||
|
|
4a30a9735e | ||
|
|
27b7da8c89 | ||
|
|
78cc80064a | ||
|
|
8eb433f01e | ||
|
|
c4fa9e4138 | ||
|
|
a9f49d91b9 | ||
|
|
87818b6a22 | ||
|
|
17dcdcde23 | ||
|
|
5c0f871415 | ||
|
|
44b9dce139 | ||
|
|
a776085b76 | ||
|
|
0d30170de0 | ||
|
|
2aee847d7c | ||
|
|
9712396c6f | ||
|
|
6a36c2825f | ||
|
|
2b787cba00 | ||
|
|
3c81f749b8 | ||
|
|
3c1b1948fd | ||
|
|
3b10509ecd | ||
|
|
b734330d38 | ||
|
|
eb95db1dda | ||
|
|
3a803550ea | ||
|
|
26e0e87cd7 | ||
|
|
7f17a5307c | ||
|
|
ae87a2eaa5 | ||
|
|
738a181d61 | ||
|
|
ba6c624a59 | ||
|
|
822d4096e7 | ||
|
|
a3e932e2dc | ||
|
|
ad53dd654c | ||
|
|
522185d19b | ||
|
|
030813cd4f | ||
|
|
53467a2a5e | ||
|
|
b5fa399f79 | ||
|
|
4a72f760b6 | ||
|
|
89c73a2fd3 | ||
|
|
6b72e23638 | ||
|
|
8c6b8ef7ac | ||
|
|
a628afa815 | ||
|
|
712b900d68 | ||
|
|
79642a7653 | ||
|
|
fbe92cc1c9 | ||
|
|
9e32b3f286 | ||
|
|
b3b2c6854d | ||
|
|
29b446e648 | ||
|
|
0142ab61a3 | ||
|
|
09a832a38e | ||
|
|
9f99cb3e12 | ||
|
|
acc37e005a | ||
|
|
e5ac137b04 | ||
|
|
522a64b0f3 | ||
|
|
d465db0055 | ||
|
|
2eb48907a0 | ||
|
|
479c77d251 | ||
|
|
e32cac5666 | ||
|
|
93a967ec15 | ||
|
|
db6b5b027c | ||
|
|
76b7d4dafc | ||
|
|
f7df9ab90e | ||
|
|
5781518f44 | ||
|
|
ce05f0d78b | ||
|
|
7415ecf85f | ||
|
|
6b84a85639 | ||
|
|
38e13be623 | ||
|
|
2433417cf6 | ||
|
|
5b1ac7c74a | ||
|
|
24a5811b07 | ||
|
|
00a177fd2f | ||
|
|
acd52f78dd | ||
|
|
0b1014763e | ||
|
|
37c8fb94d4 | ||
|
|
ec1690b49d | ||
|
|
7478e4e010 | ||
|
|
bcb633e774 | ||
|
|
ac6c996602 | ||
|
|
fb0ab4b96b | ||
|
|
7cde3b39fa | ||
|
|
270bf256b7 | ||
|
|
70f0ca1b50 | ||
|
|
6091e5f9dc | ||
|
|
b968dccd28 | ||
|
|
fe644ea6bd | ||
|
|
d18a420449 | ||
|
|
9983149d0e | ||
|
|
e3fde9b71e | ||
|
|
59da0dd7e3 | ||
|
|
1f236737f2 | ||
|
|
4fd4654909 | ||
|
|
4ba6dec508 | ||
|
|
3bea423613 | ||
|
|
1ee07edcc5 | ||
|
|
b09acd5bf0 | ||
|
|
410e197723 | ||
|
|
4ed5ed50c4 | ||
|
|
38120c0ea6 | ||
|
|
e738261512 | ||
|
|
d8d747a168 | ||
|
|
cd084f183b | ||
|
|
30e704a6b1 | ||
|
|
49844dce11 | ||
|
|
2e4286233d | ||
|
|
4ba4dce451 | ||
|
|
c8f2010c8e | ||
|
|
a379c32e7a | ||
|
|
46becd1cee | ||
|
|
94968c3d4c | ||
|
|
20ee6b7446 | ||
|
|
1c4f20d7a7 | ||
|
|
9fe8d0871d | ||
|
|
1f5823bce0 | ||
|
|
582c031755 | ||
|
|
9ce8a615a2 | ||
|
|
6bc921c955 | ||
|
|
dc32dd2062 | ||
|
|
220646cfe5 | ||
|
|
f7f4c00beb | ||
|
|
7a91d3d0a1 | ||
|
|
9e2ed33bfb | ||
|
|
9ef8c5c1ee | ||
|
|
187521a905 | ||
|
|
871a2b9a80 | ||
|
|
7687a95025 | ||
|
|
df49e8a3cc | ||
|
|
c5b4f872d3 | ||
|
|
5ac70779d7 | ||
|
|
0b2685ac3f | ||
|
|
42efd815df | ||
|
|
8aadf53dc0 | ||
|
|
09e200f16c | ||
|
|
7e0ce63e26 | ||
|
|
5bb1f1715c | ||
|
|
b96c6006ed | ||
|
|
e32596d723 | ||
|
|
ac04fd8e0a | ||
|
|
2a86e3b2f9 | ||
|
|
989ccc5459 | ||
|
|
5fcb7f128d | ||
|
|
90fb8969d4 | ||
|
|
4b94e68d6c | ||
|
|
7271a26e23 | ||
|
|
6b5e16f59d | ||
|
|
e0337125d3 | ||
|
|
638c7d29c3 | ||
|
|
edde4e2cad | ||
|
|
ae8904b7cf | ||
|
|
b484d7e5da | ||
|
|
96a5e689ff | ||
|
|
7926706ea1 | ||
|
|
fce4d8e33c | ||
|
|
5fcea3ad88 | ||
|
|
e22a8911b8 | ||
|
|
29c6ebe7a2 | ||
|
|
fc21669d81 | ||
|
|
03b1654112 | ||
|
|
40bb0b2929 | ||
|
|
e13faeabc7 | ||
|
|
f66af0d424 | ||
|
|
477fcd9c6c | ||
|
|
f853cd9f79 | ||
|
|
924354afa1 | ||
|
|
409a6b7707 | ||
|
|
d59e46cc64 | ||
|
|
ba4c96a3f0 | ||
|
|
f5a2b38ee2 | ||
|
|
59d2ff935f | ||
|
|
34b258ab1b | ||
|
|
9261123491 | ||
|
|
73c59f5b29 | ||
|
|
aaf0e4b6bc | ||
|
|
6d5b34f4b9 | ||
|
|
e5cee3c228 | ||
|
|
9edd275984 | ||
|
|
af060ff574 | ||
|
|
ab956af9cb | ||
|
|
f81588a2a5 | ||
|
|
808be1aec0 | ||
|
|
3733261647 | ||
|
|
1a1ede9f4b | ||
|
|
a3d0cbcb43 | ||
|
|
c0680515ae | ||
|
|
cd5be23235 | ||
|
|
af37299d25 | ||
|
|
8a2ce50187 | ||
|
|
dbe6908386 | ||
|
|
51cc376eb9 | ||
|
|
415526c4d3 | ||
|
|
52f5c824f8 | ||
|
|
ce200b8d21 | ||
|
|
e110ac50d2 | ||
|
|
d589147552 | ||
|
|
d9cdd18bf9 | ||
|
|
4db0f6dff1 | ||
|
|
a290a346cb | ||
|
|
4fd2658a98 | ||
|
|
79f0ceff28 | ||
|
|
fdd5f0b7ba | ||
|
|
0d29fd7005 | ||
|
|
b68af9c8b9 | ||
|
|
12570f5caf | ||
|
|
4a0c33d5a5 | ||
|
|
831a9d00e1 | ||
|
|
2b2ae31907 | ||
|
|
593a415cd9 | ||
|
|
a4acbf5cc7 | ||
|
|
e92ba3dd25 | ||
|
|
e78fa76e1b | ||
|
|
22da3b405c | ||
|
|
8c1298e7f3 | ||
|
|
988468d746 | ||
|
|
3b1f2e46f3 | ||
|
|
97d935c037 | ||
|
|
2660394ccf | ||
|
|
18f6dc290a | ||
|
|
db081513db | ||
|
|
8157aa1258 | ||
|
|
4d1d43821c | ||
|
|
ab3152e9e4 | ||
|
|
ac17b24cf6 | ||
|
|
97d69bec9c | ||
|
|
e4b32f1843 | ||
|
|
a6353d3469 | ||
|
|
5ef7b2f683 | ||
|
|
025f88f559 | ||
|
|
0fb2571848 | ||
|
|
d185314a31 | ||
|
|
5bc02c012d | ||
|
|
6ed403e4d5 | ||
|
|
9a23eef998 | ||
|
|
4e0deaf695 | ||
|
|
fd0b28e66f | ||
|
|
34cefb5aa7 | ||
|
|
d846305114 | ||
|
|
a35354be5b | ||
|
|
5cc8a1fb1a | ||
|
|
7711c1939b | ||
|
|
5d055fd1ad | ||
|
|
29fbc7c798 | ||
|
|
f10540069c | ||
|
|
86a21d9c89 | ||
|
|
84dc8a1753 | ||
|
|
ce53ff5da1 | ||
|
|
fa3bb6e80f | ||
|
|
2462dfd0c9 | ||
|
|
fefecd76df | ||
|
|
e9c7264b4e | ||
|
|
c7ee4bad32 | ||
|
|
1c279e071f | ||
|
|
a6146f5644 | ||
|
|
23db7cb4dc | ||
|
|
2676e31094 | ||
|
|
d161eb19c4 | ||
|
|
4aa7fe6391 | ||
|
|
1e6d630be5 | ||
|
|
9a05458a84 | ||
|
|
0c8c508dd8 | ||
|
|
7397491eb8 | ||
|
|
637c5b8c6c | ||
|
|
164b0a1533 | ||
|
|
f5eae498ef | ||
|
|
07a51011c6 | ||
|
|
08e2e971e7 | ||
|
|
2cd57396ec | ||
|
|
ac8fd3ce56 | ||
|
|
598e8f9b3a | ||
|
|
e3ba5c08ee | ||
|
|
1c253975e5 | ||
|
|
34ae67b788 | ||
|
|
160c98f46b | ||
|
|
5f526af1e3 | ||
|
|
90960dddda | ||
|
|
7573583644 | ||
|
|
41c5ceefc7 | ||
|
|
079dc9716c | ||
|
|
3476959f98 | ||
|
|
bf4f1f0353 | ||
|
|
4fcd4e3be1 | ||
|
|
ca18f2ff10 | ||
|
|
81b85c89f2 | ||
|
|
73f3b7c6a9 | ||
|
|
27d4472440 | ||
|
|
82fa5a7087 | ||
|
|
70da7cf0a3 | ||
|
|
64229b5318 | ||
|
|
a6683fd78f | ||
|
|
ea9ec5b566 | ||
|
|
c4e57b8db8 | ||
|
|
b5211a8ea4 | ||
|
|
aa6963529e | ||
|
|
71d1a4cd29 | ||
|
|
f57c51eca8 | ||
|
|
91335c0802 | ||
|
|
3ea20310aa | ||
|
|
0bec1e0050 | ||
|
|
f6def08b01 | ||
|
|
cb2985071f | ||
|
|
7a46e9daf8 | ||
|
|
68ab4f6d45 | ||
|
|
ac2f8525e4 | ||
|
|
c9b257dc80 | ||
|
|
06da8c2e2e | ||
|
|
27b793f27e | ||
|
|
82a8e79d2b | ||
|
|
e2d715ee49 | ||
|
|
b4801dabf2 | ||
|
|
1453c522bf | ||
|
|
d7d3c47d0e | ||
|
|
89b2593a56 | ||
|
|
2361599340 | ||
|
|
538ce54b44 | ||
|
|
232d39b19d | ||
|
|
08e12546ed | ||
|
|
9377188cf7 | ||
|
|
d100d570be | ||
|
|
5de6b1cb7f | ||
|
|
c86a7509ad | ||
|
|
56700c8018 | ||
|
|
edaa7e8b81 | ||
|
|
5e61e32795 | ||
|
|
794f8fb0b0 | ||
|
|
56e0d71893 | ||
|
|
f962bfa8ef | ||
|
|
c257222509 | ||
|
|
723d2e6463 | ||
|
|
63fe5007b7 | ||
|
|
ce2049a552 | ||
|
|
4913e6b8ae | ||
|
|
152431cb8b | ||
|
|
b8df16dd67 | ||
|
|
ca17d84c0c | ||
|
|
820bdaa15a | ||
|
|
644cc95ac2 | ||
|
|
e25fa700d1 | ||
|
|
a1c26b4228 | ||
|
|
c7b3607e33 | ||
|
|
235d4257d4 | ||
|
|
7c8273bcde | ||
|
|
15d18bbe8f | ||
|
|
7c0e17342e | ||
|
|
8f59254c67 | ||
|
|
d97e919d7c | ||
|
|
49b0d764c9 | ||
|
|
7714c2cb8e | ||
|
|
cb39d17b22 | ||
|
|
fedbf473bb | ||
|
|
d62e4c7d1b | ||
|
|
89e49353e4 | ||
|
|
007a786c14 | ||
|
|
e194642d6e | ||
|
|
9eb1cd75b0 | ||
|
|
ae9293ba08 | ||
|
|
5b24faa7f1 | ||
|
|
cf61b8a4e5 | ||
|
|
df199b5f5c | ||
|
|
ca4f1654fa | ||
|
|
622e46a42f | ||
|
|
d5c0c485cf | ||
|
|
c983cb2b4e | ||
|
|
8d04c3aa04 | ||
|
|
270a075292 | ||
|
|
c36658e0e1 | ||
|
|
e4634721a1 | ||
|
|
2129299f5f | ||
|
|
3fc3ce3ea0 | ||
|
|
254c891528 | ||
|
|
0c74666409 | ||
|
|
82c45f1bca | ||
|
|
bb5c2069c9 | ||
|
|
ac749cebfa | ||
|
|
e125b33a85 | ||
|
|
6e041152a2 | ||
|
|
6945b026e9 | ||
|
|
b7c5e1a9fa | ||
|
|
178c16b0d7 | ||
|
|
5dcc69b2a2 | ||
|
|
f56bf19d17 | ||
|
|
dea445cafa | ||
|
|
db83fc9aca | ||
|
|
234cf064a3 | ||
|
|
b4742a2a8a | ||
|
|
befe49fd5c | ||
|
|
67e04257ba | ||
|
|
813308c20f | ||
|
|
bfcb578bb1 | ||
|
|
793d5b6f16 | ||
|
|
411c8c5340 | ||
|
|
f8237798a7 | ||
|
|
dfbd2542b4 | ||
|
|
8acd05f4c7 | ||
|
|
0df46cff76 | ||
|
|
1e1dbb9f2e | ||
|
|
16b81fcc98 | ||
|
|
92f48d3bb0 | ||
|
|
9a576ed19d | ||
|
|
424c691b2a | ||
|
|
676fb8047f | ||
|
|
1d49673804 | ||
|
|
c846e7517b | ||
|
|
4c3318bfe8 | ||
|
|
fdf06503db | ||
|
|
0e4bac6885 | ||
|
|
5744276a94 | ||
|
|
f393327a87 | ||
|
|
2f8b40be9f | ||
|
|
05da884e6b | ||
|
|
bd6176fbcb | ||
|
|
7ca5371bf2 | ||
|
|
f62a67c778 | ||
|
|
364d4be558 | ||
|
|
d7498e86cb | ||
|
|
e3a98b6284 | ||
|
|
1b2256abce | ||
|
|
30f34a5d8f | ||
|
|
d6ef631d06 | ||
|
|
d4f32881ab | ||
|
|
3863acde71 | ||
|
|
fb1925be2d | ||
|
|
83ffb32a2d | ||
|
|
6963c377dc | ||
|
|
4cb5ecf4f4 | ||
|
|
0ca123d027 | ||
|
|
3acf5eb716 | ||
|
|
44d954ca2d | ||
|
|
a2ede0d210 | ||
|
|
eb28208eb1 | ||
|
|
b6bd2659d0 | ||
|
|
c79e0a557f | ||
|
|
cd1e56f70e | ||
|
|
215a25df06 | ||
|
|
f81972fbb1 | ||
|
|
22a72d4b0c | ||
|
|
c673bb128d | ||
|
|
8705f76f61 | ||
|
|
3e71340dd8 | ||
|
|
d2378eed35 | ||
|
|
ed0900024e | ||
|
|
2b2ca79875 | ||
|
|
d76df2f3d3 | ||
|
|
79bbfcc70e | ||
|
|
96f59417ab | ||
|
|
0d904a30da | ||
|
|
3a3e1f9932 | ||
|
|
1c8417552a | ||
|
|
87f83999f6 | ||
|
|
62790e4a65 | ||
|
|
5baadb43e2 | ||
|
|
87d7552779 | ||
|
|
0ae55221a5 | ||
|
|
fa91c97ae8 | ||
|
|
9c195c87ac | ||
|
|
1a79c6b7b4 | ||
|
|
6335e6dbe1 | ||
|
|
1d8a23c491 |
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# https://gitlab.archlinux.org/archlinux/devtools/-/merge_requests/197
|
||||
|
||||
args=("$@")
|
||||
has_keep_unit=0
|
||||
|
||||
for arg in "${args[@]}"; do
|
||||
if [[ "$arg" == "--keep-unit" ]]; then
|
||||
has_keep_unit=1
|
||||
fi
|
||||
done
|
||||
|
||||
if ! (( has_keep_unit )); then
|
||||
args=("--keep-unit" "${args[@]}")
|
||||
fi
|
||||
|
||||
exec /usr/bin/systemd-nspawn "${args[@]}"
|
||||
@@ -0,0 +1,501 @@
|
||||
name: Make Packages
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ "*" ]
|
||||
workflow_dispatch: {}
|
||||
# Called by release-please.yml once it has cut a release. A tag pushed by
|
||||
# release-please carries the default GITHUB_TOKEN, and GitHub deliberately
|
||||
# refuses to start a workflow from an event that token created -- so the
|
||||
# `push: tags` trigger above never fires for a release-please tag. Being
|
||||
# callable means the release build runs inside release-please's own run
|
||||
# instead, with no personal access token to store on a public repo.
|
||||
workflow_call:
|
||||
inputs:
|
||||
release_tag:
|
||||
description: >-
|
||||
Tag release-please just created. Supplies the package version and
|
||||
names the release to upload to, both of which would otherwise come
|
||||
from github.ref_name.
|
||||
type: string
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build-monocoque-debs:
|
||||
# Previously this matrix used `runs-on: ${{ matrix.os }}` with
|
||||
# debian-latest/debian-stable as runner *labels*, which only resolve to
|
||||
# self-hosted runners. With none online those two legs sat queued
|
||||
# indefinitely (observed: four runs stuck 17-60min, only the
|
||||
# ubuntu-latest leg ever completing) until cancelled by hand. Building
|
||||
# each distro in a container on ordinary GitHub-hosted runners removes
|
||||
# the self-hosted dependency entirely.
|
||||
#
|
||||
# matrix.os stays the distro *name* because it's load-bearing: it picks
|
||||
# the per-distro control file under tools/distro/debian/dpkg/<os>/ and
|
||||
# names the released .deb. matrix.image is what it actually builds in.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
image: ubuntu:latest
|
||||
- os: debian-latest
|
||||
image: debian:testing
|
||||
- os: debian-stable
|
||||
image: debian:stable-slim
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ matrix.image }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
# Must come before actions/checkout: these images are minimal and ship
|
||||
# no git, so checkout would fall back to a tarball download and silently
|
||||
# skip submodules -- simapi and nappgui_src are both required to build.
|
||||
# No sudo either; container jobs already run as root.
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates curl git \
|
||||
build-essential cmake pkg-config \
|
||||
libgtk-3-dev libuv1-dev libargtable2-dev libserialport-dev \
|
||||
libconfig-dev libhidapi-dev liblua5.4-dev libxdg-basedir-dev \
|
||||
libxml2-dev libpulse-dev libproc2-dev libcurl4-openssl-dev \
|
||||
libglu1-mesa-dev
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# $GITHUB_WORKSPACE, not ${{ github.workspace }}: in a container job the
|
||||
# expression still renders the *host* path (/home/runner/work/...) while
|
||||
# the repo is actually mounted at /__w/... inside the container. Using
|
||||
# the expression made cmake fail with `The source directory
|
||||
# "/home/runner/work/monocoque/monocoque" does not exist`. The env var is
|
||||
# set to the container-side path, so it's correct either way.
|
||||
- name: Set build dir
|
||||
id: strings
|
||||
shell: bash
|
||||
run: |
|
||||
echo "build-output-dir=$GITHUB_WORKSPACE/build" >> "$GITHUB_OUTPUT"
|
||||
github_sha_hash=${{ github.sha }}
|
||||
echo "github-sha-short=${github_sha_hash:0:7}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Configure CMake
|
||||
run: >
|
||||
cmake -B ${{ steps.strings.outputs.build-output-dir }}
|
||||
-DBUILD_GUI=on
|
||||
-DCMAKE_CXX_COMPILER=g++
|
||||
-DCMAKE_C_COMPILER=gcc
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-S "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release
|
||||
|
||||
- name: Create directories in package
|
||||
run: |
|
||||
mkdir PKG_SOURCE
|
||||
mkdir -p PKG_SOURCE/DEBIAN
|
||||
mkdir -p PKG_SOURCE/usr/bin/
|
||||
mkdir -p PKG_SOURCE/usr/share/applications
|
||||
mkdir -p PKG_SOURCE/usr/share/pixmaps
|
||||
mkdir -p PKG_SOURCE/usr/share/icons/hicolor/scalable/apps
|
||||
mkdir -p PKG_SOURCE/usr/share/monocoque
|
||||
- name: Download xpm icon
|
||||
run: |
|
||||
curl -sSL https://repo.spacefreak18.xyz/monocoque/icons/monocoque.xpm -o PKG_SOURCE/usr/share/pixmaps/monocoque.xpm
|
||||
- name: Download svg icon
|
||||
run: |
|
||||
curl -sSL https://repo.spacefreak18.xyz/monocoque/icons/monocoque.svg -o PKG_SOURCE/usr/share/icons/hicolor/scalable/apps/monocoque.svg
|
||||
- name: Determine package version
|
||||
id: pkgver
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_TAG: ${{ inputs.release_tag }}
|
||||
run: |
|
||||
# The control files carry a hardcoded `Version: 1` that nobody
|
||||
# maintains, so every .deb ever released claims version 1 regardless
|
||||
# of its tag. Take the version from the tag instead.
|
||||
#
|
||||
# Guarded rather than used blindly: a Debian version must start with a
|
||||
# digit, so a branch build (workflow_dispatch) or a non-release tag
|
||||
# like `pkgtest-5` would make dpkg-deb fail outright. Anything that
|
||||
# doesn't look like a version falls back to 0.0.0.
|
||||
v=""
|
||||
# RELEASE_TAG is set only when release-please calls this workflow;
|
||||
# its tag exists but the run itself is on a branch, so GITHUB_REF_*
|
||||
# would report master and fall through to 0.0.0.
|
||||
v="${RELEASE_TAG#v}"
|
||||
[ -z "$v" ] && [ "$GITHUB_REF_TYPE" = tag ] && v="${GITHUB_REF_NAME#v}"
|
||||
case "$v" in
|
||||
[0-9]*) ;;
|
||||
*) v="0.0.0" ;;
|
||||
esac
|
||||
echo "version=$v" >> "$GITHUB_OUTPUT"
|
||||
echo "packaging as version $v"
|
||||
|
||||
- name: Copy script files around to stop .github from being added to the package then build the package
|
||||
run: |
|
||||
cp ./tools/distro/debian/dpkg/${{ matrix.os }}/control ./PKG_SOURCE/DEBIAN/control
|
||||
sed -i "s/^Version:.*/Version: ${{ steps.pkgver.outputs.version }}/" ./PKG_SOURCE/DEBIAN/control
|
||||
cp ./tools/distro/debian/dpkg/copyright ./PKG_SOURCE/DEBIAN/copyright
|
||||
cp ./build/monocoque ./PKG_SOURCE/usr/bin/monocoque
|
||||
cp ./build/gmonocoque ./PKG_SOURCE/usr/bin/gmonocoque
|
||||
cp ./tools/monocoque.desktop ./PKG_SOURCE/usr/share/applications/monocoque.desktop
|
||||
dpkg-deb --build PKG_SOURCE monocoque-${{ matrix.os }}.deb
|
||||
echo "--- control as packaged ---"
|
||||
dpkg-deb --field monocoque-${{ matrix.os }}.deb Package Version
|
||||
|
||||
- name: Release the Package
|
||||
# Only a tag can publish a release; without this a workflow_dispatch run
|
||||
# builds everything correctly and then reports failure on a step that
|
||||
# cannot work, which makes a green build look broken.
|
||||
if: github.ref_type == 'tag' || inputs.release_tag != ''
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ inputs.release_tag || github.ref_name }}
|
||||
files: monocoque-${{ matrix.os }}.deb
|
||||
# release-please writes the release body from the changelog it just
|
||||
# generated. Letting this action generate its own notes as well would
|
||||
# overwrite them, so it only does so for a hand-pushed tag.
|
||||
generate_release_notes: ${{ inputs.release_tag == '' }}
|
||||
|
||||
|
||||
build-monocoque-rpms:
|
||||
# Same self-hosted-label problem as the deb jobs above (fedora-43 was a
|
||||
# runner label with nothing online to claim it); now a container on an
|
||||
# ordinary GitHub-hosted runner, with fedora-44 added alongside 43.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: fedora-43
|
||||
image: fedora:43
|
||||
- os: fedora-44
|
||||
image: fedora:44
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ matrix.image }}
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
# The -devel list is the one fedora.spec's own header comment documents,
|
||||
# plus gtk3/libcurl/mesa-libGLU to match its `Requires:` line -- the spec
|
||||
# configures with -DBUILD_GUI=on, so the GUI's deps are build deps too.
|
||||
#
|
||||
# Retried because this pulls openh264 from Fedora's third-party Cisco
|
||||
# repo (ciscobinary.openh264.org), which is a single-mirror CDN outside
|
||||
# Fedora's own infrastructure and has proven flaky: the fedora-43 leg of
|
||||
# the first run died on `Curl error (7): Could not connect to server`
|
||||
# for it while fedora-44 downloaded the very same package fine in the
|
||||
# same run -- i.e. transient, not a dependency or package-name problem.
|
||||
# Not disabling that repo outright: openh264 is still pulled in even
|
||||
# with weak deps off, so something here requires it outright (which
|
||||
# package wasn't traced) and dropping the repo would likely break
|
||||
# dependency resolution rather than fix anything.
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
for attempt in 1 2 3; do
|
||||
dnf install -y --setopt=install_weak_deps=False \
|
||||
rpm-build cmake gcc gcc-c++ make git curl \
|
||||
pulseaudio-libs-devel argtable-devel libconfig-devel hidapi-devel \
|
||||
libserialport-devel lua-devel libuv-devel libxdg-basedir-devel \
|
||||
libxml2-devel procps-ng-devel gtk3-devel libcurl-devel \
|
||||
mesa-libGLU-devel && exit 0
|
||||
echo "dnf install failed (attempt $attempt), retrying in 15s..."
|
||||
sleep 15
|
||||
done
|
||||
echo "dnf install failed after 3 attempts" >&2
|
||||
exit 1
|
||||
|
||||
# submodules: recursive is new here. It didn't matter while %prep cloned
|
||||
# upstream and ran `git submodule update` itself; now that the staged
|
||||
# checkout is what gets built, an empty nappgui_src fails configure with
|
||||
# "does not contain a CMakeLists.txt file". Missed by the local fedora:44
|
||||
# rehearsal, which mounted a working tree that already had its submodules.
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: create rpmbuild dirs
|
||||
run: mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||
|
||||
# The spec's %prep used to `git clone` upstream master, so the rpm's
|
||||
# contents tracked master rather than the tag being built -- an rpm built
|
||||
# from a tag would quietly ship master's code. Staged here and copied by
|
||||
# %prep instead, so a package's contents are the commit it was built from.
|
||||
- name: Stage the checked-out tree as the rpm source
|
||||
run: |
|
||||
rm -rf ~/rpmbuild/SOURCES/monocoque
|
||||
cp -r "$GITHUB_WORKSPACE" ~/rpmbuild/SOURCES/monocoque
|
||||
rm -rf ~/rpmbuild/SOURCES/monocoque/.github
|
||||
|
||||
# Uses the spec from the checked-out tree instead of curl'ing it from
|
||||
# upstream's master branch, so a tag builds the spec that tag contains.
|
||||
#
|
||||
# The spec's `Version: 0.0.5` is stale and hand-maintained -- real tags are
|
||||
# well past it, so released rpms have been installing as monocoque-0.0.5.
|
||||
# Stamped from the tag here rather than edited in the spec, so the file
|
||||
# stays a working default for local rpmbuild while CI releases carry the
|
||||
# real version. Same digit guard as the deb job: rpm versions can't start
|
||||
# with a non-digit, and scratch tags / branch builds would break it.
|
||||
#
|
||||
# NOTE (pre-existing, deliberately not fixed here): the spec's own %prep
|
||||
# still does a fresh `git clone` of upstream master for the *sources*,
|
||||
# so the rpm's contents track master rather than the tag being built.
|
||||
# Worth fixing separately -- it needs the spec parameterised, which is a
|
||||
# bigger change than getting these jobs running again.
|
||||
- name: run spec file
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_TAG: ${{ inputs.release_tag }}
|
||||
run: |
|
||||
v=""
|
||||
# RELEASE_TAG is set only when release-please calls this workflow;
|
||||
# its tag exists but the run itself is on a branch, so GITHUB_REF_*
|
||||
# would report master and fall through to 0.0.0.
|
||||
v="${RELEASE_TAG#v}"
|
||||
[ -z "$v" ] && [ "$GITHUB_REF_TYPE" = tag ] && v="${GITHUB_REF_NAME#v}"
|
||||
case "$v" in
|
||||
[0-9]*) ;;
|
||||
*) v="0.0.0" ;;
|
||||
esac
|
||||
echo "packaging as version $v"
|
||||
sed -i "s/^Version:.*/Version: $v/" tools/distro/fedora/rpm/fedora.spec
|
||||
rpmbuild -ba tools/distro/fedora/rpm/fedora.spec
|
||||
|
||||
# Glob rather than a hardcoded monocoque-0.0.5-1.x86_64.rpm: the filename
|
||||
# tracks the spec's Version/Release, which is now stamped per-tag above.
|
||||
- name: rename file
|
||||
run: |
|
||||
cp ~/rpmbuild/RPMS/x86_64/monocoque-*.rpm "$GITHUB_WORKSPACE/monocoque-${{ matrix.os }}.rpm"
|
||||
echo "--- rpm identity as built ---"
|
||||
rpm -qp --qf '%{NAME} %{VERSION}-%{RELEASE}\n' "$GITHUB_WORKSPACE/monocoque-${{ matrix.os }}.rpm"
|
||||
|
||||
- name: Release the Package
|
||||
# Only a tag can publish a release; without this a workflow_dispatch run
|
||||
# builds everything correctly and then reports failure on a step that
|
||||
# cannot work, which makes a green build look broken.
|
||||
if: github.ref_type == 'tag' || inputs.release_tag != ''
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ inputs.release_tag || github.ref_name }}
|
||||
files: monocoque-${{ matrix.os }}.rpm
|
||||
# release-please writes the release body from the changelog it just
|
||||
# generated. Letting this action generate its own notes as well would
|
||||
# overwrite them, so it only does so for a hand-pushed tag.
|
||||
generate_release_notes: ${{ inputs.release_tag == '' }}
|
||||
|
||||
|
||||
build-monocoque-appimage:
|
||||
# An AppImage only runs where glibc is at least as new as the one it
|
||||
# linked against, so it wants the OLDEST base that can build the code --
|
||||
# building on current Ubuntu silently excludes older distros and defeats
|
||||
# the point of shipping one.
|
||||
#
|
||||
# That floor is set by the compiler, not by choice: simapi's F1 2018
|
||||
# headers use C23 enum-with-fixed-underlying-type (`enum TrackID :
|
||||
# uint8_t`), which GCC only implements from 13 onwards. ubuntu-22.04
|
||||
# ships GCC 11 and fails outright with `expected identifier or '(' before
|
||||
# ':' token` -- confirmed by a real run, and no -std= value rescues it
|
||||
# (verified separately: the construct is gated on compiler version, not
|
||||
# on the standard flag). ubuntu-24.04 / GCC 13 is therefore the oldest
|
||||
# usable base, giving a glibc 2.39 floor.
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# librsvg2-common and libgtk-3-bin are for linuxdeploy's gtk plugin, not
|
||||
# the build itself -- it needs an svg pixbuf loader and
|
||||
# gtk-update-icon-cache to assemble a self-contained GTK runtime.
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential cmake pkg-config \
|
||||
libgtk-3-dev libuv1-dev libargtable2-dev libserialport-dev \
|
||||
libconfig-dev libhidapi-dev liblua5.4-dev libxdg-basedir-dev \
|
||||
libxml2-dev libpulse-dev libproc2-dev libcurl4-openssl-dev \
|
||||
libglu1-mesa-dev \
|
||||
desktop-file-utils file patchelf wget \
|
||||
librsvg2-common libgtk-3-bin
|
||||
|
||||
# -std=gnu2x for the C23 enum syntax above: GCC 13 implements it but
|
||||
# still defaults to gnu17, unlike GCC 15+ (which the container-based
|
||||
# deb/rpm legs get, and which accept it without the flag). gnu2x rather
|
||||
# than c2x so GNU extensions stay on -- simapi also relies on asprintf.
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -B build -DBUILD_GUI=on -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_C_FLAGS=-std=gnu2x -S .
|
||||
cmake --build build --config Release
|
||||
|
||||
# Uses the install() rules rather than hand-copying binaries the way the
|
||||
# deb job does -- that's what they were added for.
|
||||
- name: Populate AppDir
|
||||
run: |
|
||||
DESTDIR="$PWD/AppDir" cmake --install build --prefix /usr
|
||||
# Icon basename must match the desktop file's `Icon=monocoque`.
|
||||
# tools/monocoque.svg is the square copy shared with the Flatpak
|
||||
# build; the published 100x50 artwork is rejected by icon tooling.
|
||||
install -Dm644 tools/monocoque.svg \
|
||||
AppDir/usr/share/icons/hicolor/scalable/apps/monocoque.svg
|
||||
|
||||
# APPIMAGE_EXTRACT_AND_RUN=1 because these tools are themselves
|
||||
# AppImages and there is no FUSE on the runner to mount them with.
|
||||
- name: Build AppImage
|
||||
env:
|
||||
APPIMAGE_EXTRACT_AND_RUN: 1
|
||||
RELEASE_TAG: ${{ inputs.release_tag }}
|
||||
run: |
|
||||
# An AppImage carries no package metadata, so its filename is the only
|
||||
# place a version can live -- hence versioning the output name here
|
||||
# rather than stamping a field as the deb/rpm jobs do. Same digit
|
||||
# guard, so scratch tags and branch builds still produce a valid name.
|
||||
v=""
|
||||
# RELEASE_TAG is set only when release-please calls this workflow;
|
||||
# its tag exists but the run itself is on a branch, so GITHUB_REF_*
|
||||
# would report master and fall through to 0.0.0.
|
||||
v="${RELEASE_TAG#v}"
|
||||
[ -z "$v" ] && [ "$GITHUB_REF_TYPE" = tag ] && v="${GITHUB_REF_NAME#v}"
|
||||
case "$v" in
|
||||
[0-9]*) ;;
|
||||
*) v="0.0.0" ;;
|
||||
esac
|
||||
export OUTPUT="monocoque-${v}-x86_64.AppImage"
|
||||
echo "packaging as $OUTPUT"
|
||||
|
||||
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-gtk/raw/master/linuxdeploy-plugin-gtk.sh
|
||||
chmod +x linuxdeploy-x86_64.AppImage linuxdeploy-plugin-gtk.sh
|
||||
|
||||
# Deploy dependencies first, without packaging, so there is a populated
|
||||
# AppDir to take licences from -- an AppImage distributes every library
|
||||
# it bundles, so their copyright files have to travel with it. A .deb
|
||||
# needs none of this because its dependencies stay separate packages.
|
||||
PATH="$PWD:$PATH" ./linuxdeploy-x86_64.AppImage \
|
||||
--appdir AppDir \
|
||||
--plugin gtk \
|
||||
--desktop-file AppDir/usr/share/applications/monocoque.desktop \
|
||||
--icon-file tools/monocoque.svg
|
||||
|
||||
tools/appimage-collect-licenses.sh AppDir
|
||||
|
||||
# monocoque itself and its vendored components are built here rather
|
||||
# than installed from packages, so dpkg cannot account for them.
|
||||
install -Dm644 LICENSE.rst AppDir/usr/share/doc/monocoque/LICENSE.rst
|
||||
install -Dm644 tools/distro/debian/dpkg/copyright AppDir/usr/share/doc/monocoque/copyright
|
||||
install -Dm644 src/monocoque/mgui/nappgui_src/LICENSE AppDir/usr/share/doc/nappgui/LICENSE
|
||||
|
||||
# Now package the AppDir, licences included.
|
||||
PATH="$PWD:$PATH" ./linuxdeploy-x86_64.AppImage \
|
||||
--appdir AppDir \
|
||||
--output appimage
|
||||
ls -la monocoque*.AppImage
|
||||
echo "--- licence files in the image ---"
|
||||
find AppDir/usr/share/doc -name copyright | wc -l
|
||||
|
||||
- name: Upload the AppImage as a build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: monocoque-x86_64.AppImage
|
||||
path: monocoque*.AppImage
|
||||
|
||||
- name: Release the Package
|
||||
# Only a tag can publish a release; without this a workflow_dispatch run
|
||||
# builds everything correctly and then reports failure on a step that
|
||||
# cannot work, which makes a green build look broken.
|
||||
if: github.ref_type == 'tag' || inputs.release_tag != ''
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ inputs.release_tag || github.ref_name }}
|
||||
files: monocoque*.AppImage
|
||||
# release-please writes the release body from the changelog it just
|
||||
# generated. Letting this action generate its own notes as well would
|
||||
# overwrite them, so it only does so for a hand-pushed tag.
|
||||
generate_release_notes: ${{ inputs.release_tag == '' }}
|
||||
|
||||
|
||||
build-monocoque-flatpak:
|
||||
# Folded in from the standalone flatpak.yml so every artifact lands on
|
||||
# one release. Kept as continue-on-error because it is the newest and
|
||||
# least battle-tested leg: a Flatpak failure must never turn a release
|
||||
# red or block the deb/rpm/AppImage artifacts, which are independent
|
||||
# jobs and upload regardless.
|
||||
runs-on: ubuntu-22.04
|
||||
continue-on-error: true
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
# No submodules: recursive here -- the manifest fetches simapi and
|
||||
# nappgui_src itself as explicit `type: git` sources (pinned commits,
|
||||
# reproducible regardless of what's checked out on disk). Confirmed
|
||||
# via a failed CI run: recursive submodule checkout populates those
|
||||
# paths with real .git files first, which then collides with
|
||||
# flatpak-builder's own git source trying to clone into the same spot
|
||||
# ("cannot overwrite non-directory .git with directory").
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# ppa:flatpak/stable, not Ubuntu's own package: 22.04's flatpak-builder
|
||||
# predates freedesktop Sdk 25.08 dropping the legacy appstream-compose
|
||||
# binary in favour of `appstreamcli compose`, and fails at the finish
|
||||
# stage without that fallback.
|
||||
- name: Install flatpak-builder + freedesktop runtime/SDK
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:flatpak/stable
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y flatpak flatpak-builder
|
||||
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
sudo flatpak install -y --noninteractive flathub org.freedesktop.Platform//25.08 org.freedesktop.Sdk//25.08
|
||||
|
||||
- name: Build .flatpak
|
||||
working-directory: flatpak
|
||||
run: |
|
||||
flatpak-builder --force-clean --repo=repo build-dir io.github.spacefreak18.monocoque.yml
|
||||
flatpak build-bundle repo monocoque.flatpak io.github.spacefreak18.monocoque
|
||||
|
||||
- name: Upload .flatpak as a build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: monocoque.flatpak
|
||||
path: flatpak/monocoque.flatpak
|
||||
|
||||
- name: Release the Package
|
||||
if: github.ref_type == 'tag' || inputs.release_tag != ''
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ inputs.release_tag || github.ref_name }}
|
||||
files: flatpak/monocoque.flatpak
|
||||
# release-please writes the release body from the changelog it just
|
||||
# generated. Letting this action generate its own notes as well would
|
||||
# overwrite them, so it only does so for a hand-pushed tag.
|
||||
generate_release_notes: ${{ inputs.release_tag == '' }}
|
||||
|
||||
#arch-build:
|
||||
# runs-on: ubuntu-latest
|
||||
# container:
|
||||
# image: archlinux
|
||||
# options: --privileged
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - name: Set proper repository url in PKGBUILD
|
||||
# run: sed -i "s|%URL%|${{ github.server_url }}/${{ github.repository }}|" $GITHUB_WORKSPACE/tools/distro/arch/git/PKGBUILD
|
||||
# - run: echo "$GITHUB_WORKSPACE/.github/bin" >> "$GITHUB_PATH"
|
||||
# - name: Build Arch Linux package
|
||||
# uses: FFY00/build-arch-package@v1
|
||||
# with:
|
||||
# BUILD_SCRIPT: extra-x86_64-build
|
||||
# PKGBUILD: $GITHUB_WORKSPACE/tools/distro/arch/git/PKGBUILD
|
||||
# OUTDIR: $HOME/arch-packages
|
||||
# - name: Release the Package
|
||||
# uses: softprops/action-gh-release@v1
|
||||
# with:
|
||||
# files: $HOME/arch-packages/monocoque*.zst
|
||||
@@ -0,0 +1,27 @@
|
||||
name: PR Build & Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Checkout submodules
|
||||
run: git submodule update --init --recursive
|
||||
- name: Update apt
|
||||
run: sudo apt update
|
||||
- name: Install dependencies
|
||||
# Same list as ci.yaml's ubuntu-latest leg, minus the GUI-only deps
|
||||
# (libgtk-3-dev, libglu1-mesa-dev) and libcurl4-openssl-dev — this
|
||||
# workflow builds the CLI + test suite only, not the GUI.
|
||||
run: sudo apt install -y libuv1-dev libargtable2-dev libserialport-dev libconfig-dev libhidapi-dev liblua5.4-dev libxdg-basedir-dev libxml2-dev libpulse-dev libproc2-dev
|
||||
- name: Configure CMake
|
||||
run: cmake -B build -DENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||
- name: Build
|
||||
run: cmake --build build --config Debug
|
||||
- name: Run tests
|
||||
run: ctest --test-dir build --output-on-failure
|
||||
@@ -0,0 +1,38 @@
|
||||
name: release-please
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
tag_name: ${{ steps.release.outputs.tag_name }}
|
||||
steps:
|
||||
- uses: googleapis/release-please-action@v4
|
||||
id: release
|
||||
with:
|
||||
config-file: release-please-config.json
|
||||
manifest-file: .release-please-manifest.json
|
||||
|
||||
# Builds and uploads the packages for the release that was just cut.
|
||||
#
|
||||
# This calls ci.yaml rather than relying on its `push: tags` trigger,
|
||||
# because a tag pushed with the default GITHUB_TOKEN does not start a new
|
||||
# workflow -- GitHub blocks that deliberately, to stop workflows triggering
|
||||
# each other in a loop. Calling it keeps the whole release inside one run
|
||||
# and avoids storing a personal access token on a public repository.
|
||||
build-release:
|
||||
needs: release-please
|
||||
if: needs.release-please.outputs.release_created == 'true'
|
||||
uses: ./.github/workflows/ci.yaml
|
||||
with:
|
||||
release_tag: ${{ needs.release-please.outputs.tag_name }}
|
||||
permissions:
|
||||
contents: write
|
||||
+11
@@ -1,3 +1,14 @@
|
||||
/build
|
||||
/src/monocoque/tags
|
||||
/src/monocoque/simulatorapi/simapi
|
||||
|
||||
.vscode
|
||||
|
||||
# flatpak-builder outputs from local builds -- CI regenerates these.
|
||||
# flatpak/repo in particular is an OSTree store: ~1000 binary objects.
|
||||
/flatpak/repo/
|
||||
/flatpak/build-dir/
|
||||
/flatpak/.flatpak-builder/
|
||||
# The bundle itself. Missing from this list once already, and a `git add -A`
|
||||
# duly committed a 6MB build artifact.
|
||||
*.flatpak
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
[submodule "src/monocoque/simulatorapi/simapi"]
|
||||
path = src/monocoque/simulatorapi/simapi
|
||||
url = https://github.com/spacefreak18/simapi
|
||||
# url = git@github.com:spacefreak18/simapi # ssh is better but Microsoft requires an account
|
||||
[submodule "src/monocoque/mgui/nappgui_src"]
|
||||
path = src/monocoque/mgui/nappgui_src
|
||||
url = https://github.com/frang75/nappgui_src
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{ ".": "0.3.6" }
|
||||
+23
@@ -1,3 +1,4 @@
|
||||
# small known issue with argtable2
|
||||
{
|
||||
argtable arg_parse
|
||||
Memcheck:Leak
|
||||
@@ -107,3 +108,25 @@
|
||||
...
|
||||
...
|
||||
}
|
||||
{
|
||||
ignore_unversioned_libs
|
||||
Memcheck:Leak
|
||||
...
|
||||
obj:*/lib*/lib*.so
|
||||
}
|
||||
{
|
||||
ignore_versioned_libs
|
||||
Memcheck:Leak
|
||||
...
|
||||
obj:*/lib*/lib*.so.*
|
||||
}
|
||||
{
|
||||
nappgui_gbind_uninitialized
|
||||
Memcheck:Cond
|
||||
fun:_gbind_update_control
|
||||
}
|
||||
{
|
||||
gtk_pango_uninitialized
|
||||
Memcheck:Cond
|
||||
fun:_pango_is_Vowel_Dependent
|
||||
}
|
||||
|
||||
+92
-66
@@ -1,7 +1,4 @@
|
||||
|
||||
# minimum CMake version required for C++20 support, among other things
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
# detect if Monocoque is being used as a sub-project of another CMake project
|
||||
if(NOT DEFINED PROJECT_NAME)
|
||||
set(MONOCOQUE_SUBPROJECT OFF)
|
||||
@@ -13,6 +10,10 @@ endif()
|
||||
SET_SOURCE_FILES_PROPERTIES( src/monocoque.c PROPERTIES LANGUAGE C)
|
||||
#set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
option(BUILD_GUI "Build GUI support" OFF)
|
||||
option(BUILD_SHARED "Build monocoque shared library" OFF)
|
||||
option(ENABLE_TESTS "Build test suite" OFF)
|
||||
|
||||
|
||||
project(monocoque)
|
||||
|
||||
@@ -20,78 +21,97 @@ set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed -ldl")
|
||||
set(LIBUSB_INCLUDE_DIR /usr/include/libusb-1.0)
|
||||
set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
|
||||
|
||||
FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
|
||||
HINTS $ENV{LIBUSB_ROOT}
|
||||
PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES include)
|
||||
FIND_PACKAGE(Lua)
|
||||
set(INCLUDE_DIRS ${LUA_INCLUDE_DIR})
|
||||
include_directories(${LUA_INCLUDE_DIR})
|
||||
if(Lua_FOUND AND NOT TARGET Lua::Lua)
|
||||
add_library(Lua::Lua INTERFACE IMPORTED)
|
||||
set_target_properties(
|
||||
Lua::Lua
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
|
||||
)
|
||||
|
||||
FIND_LIBRARY(LIBUSB_LIBRARY NAMES usb-1.0
|
||||
HINTS $ENV{LIBUSB_ROOT}
|
||||
PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}
|
||||
PATH_SUFFIXES lib)
|
||||
endif()
|
||||
message("Lua ${Lua_VERSION} found")
|
||||
if (Lua_VERSION VERSION_GREATER_EQUAL "5.5.0")
|
||||
add_compile_definitions(USE_LUA_55)
|
||||
else()
|
||||
remove_definitions(USE_LUA_55)
|
||||
endif()
|
||||
|
||||
set(HIDAPI_WITH_LIBUSB TRUE) # surely will be used only on Linux
|
||||
#FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
|
||||
# HINTS $ENV{LIBUSB_ROOT}
|
||||
# PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
|
||||
# PATH_SUFFIXES include)
|
||||
#
|
||||
#FIND_LIBRARY(LIBUSB_LIBRARY NAMES usb-1.0
|
||||
# HINTS $ENV{LIBUSB_ROOT}
|
||||
# PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}
|
||||
# PATH_SUFFIXES lib)
|
||||
#
|
||||
set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux
|
||||
set(BUILD_SHARED_LIBS TRUE) # HIDAPI as static library on all platforms
|
||||
|
||||
if(BUILD_SHARED)
|
||||
add_library(monocoque SHARED src/monocoque/monocoque.c)
|
||||
else()
|
||||
add_library(monocoque STATIC src/monocoque/monocoque.c)
|
||||
endif()
|
||||
add_executable(monocoque-cli src/monocoque/monocoque-cli.c)
|
||||
set_target_properties(monocoque-cli PROPERTIES OUTPUT_NAME monocoque)
|
||||
if(BUILD_GUI)
|
||||
add_executable(monocoque-gui src/monocoque/monocoque-gui.c)
|
||||
set_target_properties(monocoque-gui PROPERTIES OUTPUT_NAME gmonocoque)
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBPROCPS libprocps)
|
||||
pkg_check_modules(LIBPROC2 libproc2)
|
||||
|
||||
if (LIBPROCPS_FOUND)
|
||||
#add_compile_definitions(USE_OLD_PID_VAL=0)
|
||||
elseif (LIBPROC2_FOUND)
|
||||
# Some distro packagings (e.g. the freedesktop Flatpak SDK) ship a
|
||||
# libproc2.pc with `Version: UNKNOWN` rather than a real version
|
||||
# string. VERSION_GREATER_EQUAL against that always evaluates false,
|
||||
# silently falling through to the old (pre-4.0.5) PIDS_VAL API and
|
||||
# failing to compile against an actually-current libproc2 whose real
|
||||
# header has already moved to the new 3-argument macro. Only take the
|
||||
# old-API branch when the version string is genuinely parseable and
|
||||
# actually old.
|
||||
if (LIBPROC2_VERSION MATCHES "^[0-9]" AND LIBPROC2_VERSION VERSION_LESS "4.0.5")
|
||||
add_compile_definitions(USE_OLD_PID_VAL=1)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Either libprocps or libproc2 is required")
|
||||
endif()
|
||||
|
||||
#if(USE_PULSEAUDIO)
|
||||
#else()
|
||||
#endif()
|
||||
|
||||
message("Using pulseaudio backend...")
|
||||
add_compile_definitions(USE_PULSEAUDIO=true)
|
||||
|
||||
add_subdirectory(src/monocoque/gameloop)
|
||||
add_subdirectory(src/monocoque/simulatorapi)
|
||||
add_subdirectory(src/monocoque/helper)
|
||||
add_subdirectory(src/monocoque/devices)
|
||||
add_subdirectory(src/monocoque/slog)
|
||||
|
||||
add_executable(monocoque src/monocoque/monocoque.c)
|
||||
target_include_directories(monocoque PUBLIC config ${LIBUSB_INCLUDE_DIR} ${LIBXML_INCLUDE_DIR})
|
||||
target_link_libraries(monocoque m ${LIBUSB_LIBRARY} hidapi-libusb portaudio serialport xml2 argtable2 config gameloop helper devices slog simulatorapi)
|
||||
target_link_libraries(monocoque m hidapi-hidraw pulse serialport xml2 argtable2 config gameloop helper devices slog simulatorapi uv xdg-basedir Lua::Lua proc2)
|
||||
target_include_directories(monocoque PUBLIC config ${LIBXML_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
|
||||
target_link_libraries(monocoque-cli m hidapi-hidraw pulse serialport xml2 argtable2 config gameloop helper devices slog simulatorapi uv xdg-basedir Lua::Lua proc2)
|
||||
target_include_directories(monocoque-cli PUBLIC config ${LIBXML_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
|
||||
if(BUILD_GUI)
|
||||
add_subdirectory(src/monocoque/mgui)
|
||||
target_link_libraries(monocoque-gui m hidapi-hidraw pulse serialport xml2 argtable2 config mgui gameloop helper devices slog simulatorapi uv xdg-basedir Lua::Lua proc2)
|
||||
target_include_directories(monocoque-gui PUBLIC config ${LIBXML_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
add_executable(listusb tests/testlibusb.c)
|
||||
target_include_directories(listusb PUBLIC ${LIBUSB_INCLUDE_DIR})
|
||||
target_link_libraries(listusb ${LIBUSB_LIBRARY} portaudio)
|
||||
add_test(listusb list-usb-devices listusb)
|
||||
|
||||
add_executable(testrevburner tests/testrevburner.c)
|
||||
target_include_directories(testrevburner PUBLIC ${LIBUSB_INCLUDE_DIR})
|
||||
target_link_libraries(testrevburner ${LIBUSB_LIBRARY})
|
||||
add_test(testrevburner testrevburner)
|
||||
|
||||
add_executable(listsound tests/pa_devs.c)
|
||||
target_include_directories(listsound PUBLIC)
|
||||
target_link_libraries(listsound m portaudio)
|
||||
add_test(list-sound-devices listsound)
|
||||
|
||||
add_executable(longsine tests/patest_longsine.c)
|
||||
target_include_directories(longsine PUBLIC ${LIBUSB_INCLUDE_DIR} ${LIBXML_INCLUDE_DIR})
|
||||
target_link_libraries(longsine ${LIBUSB_LIBRARY} m portaudio)
|
||||
add_test(longsine longsine)
|
||||
|
||||
add_executable(parserevburnerxml tests/revburnerparsetest.c)
|
||||
target_include_directories(parserevburnerxml PUBLIC ${LIBXML_INCLUDE_DIR})
|
||||
target_link_libraries(parserevburnerxml ${LIBUSB_LIBRARY} portaudio xml2)
|
||||
add_test(parserevburnerxml parserevburnerxml)
|
||||
|
||||
add_executable(setmem tests/setmem.c)
|
||||
target_include_directories(setmem PUBLIC)
|
||||
target_link_libraries(setmem)
|
||||
add_test(setmem setmem)
|
||||
|
||||
add_executable(getmem tests/getmem.c)
|
||||
target_include_directories(getmem PUBLIC)
|
||||
target_link_libraries(getmem)
|
||||
add_test(getmem getmem)
|
||||
|
||||
add_executable(setsimdata tests/setsimdata.c)
|
||||
target_include_directories(setsimdata PUBLIC)
|
||||
target_link_libraries(setsimdata)
|
||||
add_test(setsimdata setsimdata)
|
||||
|
||||
add_executable(hidtest tests/hidtest.c)
|
||||
target_include_directories(hidtest PUBLIC ${LIBUSB_INCLUDE_DIR})
|
||||
target_link_libraries(hidtest ${LIBUSB_LIBRARY} hidapi-libusb)
|
||||
add_test(hidtest hidtest)
|
||||
|
||||
add_executable(simlighttest tests/simlighttest.c)
|
||||
target_include_directories(simlighttest PUBLIC)
|
||||
target_link_libraries(simlighttest serialport)
|
||||
add_test(simlighttest simlighttest)
|
||||
|
||||
|
||||
# used for enabling additional compiler options if supported
|
||||
@@ -125,7 +145,7 @@ else() # GCC/Clang warning option
|
||||
|
||||
if(analyze)
|
||||
message("-- Analyzer is on")
|
||||
target_compile_options(monocoque PRIVATE -fanalyzer -Wno-analyzer-possible-null-argument -Wno-analyzer-possible-null-dereference)
|
||||
target_compile_options(monocoque PRIVATE -fanalyzer -Wno-analyzer-possible-null-argument -Wno-analyzer-possible-null-dereference -fsanitize=address -g -fno-omit-frame-pointer)
|
||||
endif()
|
||||
|
||||
enable_cxx_compiler_flag_if_supported("-Wall")
|
||||
@@ -157,6 +177,12 @@ endif()
|
||||
# unit tests --only enable if requested AND we're not building as a sub-project
|
||||
if(ENABLE_TESTS AND NOT MONOCOQUE_SUBPROJECT)
|
||||
message(STATUS "[monocoque] Unit Tests Enabled")
|
||||
add_subdirectory(tests)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
install(TARGETS monocoque-cli RUNTIME DESTINATION bin)
|
||||
if(BUILD_GUI)
|
||||
install(TARGETS monocoque-gui RUNTIME DESTINATION bin)
|
||||
endif()
|
||||
install(FILES tools/monocoque.desktop DESTINATION share/applications)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Monocoque User Setup Guide
|
||||
|
||||
## Required Packages / Software
|
||||
* build [monocoque](https://github.com/Spacefreak18/monocoque) e.g. in `~/monocoque` - follow instructions from repo
|
||||
* build [simd](https://github.com/Spacefreak18/simapi/tree/master/simd) e.g. in `~/simapi/simd` - follow instructions from repo
|
||||
* build [simshmbridge](https://github.com/spacefreak18/simshmbridge) e.g. in `~/simshmbridge` - follow instructions from repo
|
||||
|
||||
## Configure SIMD & Monocoque
|
||||
* Create a `simd` Config in `~/.config/simd/simd.config` - [use example](https://github.com/Spacefreak18/simapi/blob/master/simd/conf/simd.config)
|
||||
* no changes required to the example file
|
||||
* create Monocoque config in `~/.config/monocque/monocoque.config` - [example](https://github.com/Spacefreak18/monocoque/blob/master/conf/monocoque.config)
|
||||
* Adapt the config to your needs, remove unused entries
|
||||
* [Documentation for Bass Shaker Config](https://spacefreak18.github.io/simapi/shakers)
|
||||
* Test your config as described in the [README](README.md#testing)
|
||||
## Steam & Game Config
|
||||
### Steam
|
||||
* Adapt Steam Launch Commands from [simshbridge](https://github.com/spacefreak18/simshmbridge?tab=readme-ov-file#basic-mapping-examples)
|
||||
* to use with `simd` you need to `SIMD_BRIDGE_EXE` to the launch command like `SIMD_BRIDGE_EXE=~/git/simshmbridge/assets/pcars2bridge.exe %command% & sleep 5 && ~/.steam/steam/steamapps/common/Proton\ 6.3/proton run ~/git/simshmbridge/assets/pcars2bridge.exe`
|
||||
|
||||
### Game specific settings
|
||||
#### Automobilista 2 (AMS2)
|
||||
* In Games like `Automobilista 2` activate the `Shared Memory` Setting and make sure to set the Protocal to `Project CARS 2`.  <img src="https://docs.simucube.com/Tuner/games/assets/automobilista2_telemetry_2.png" alt="Shared Memory Settings in AMS2" width="65%">
|
||||
* Restart the Game after changing these Settings!
|
||||
#### Assetto Corsa & Assetto Corsa Competizione (ACC)
|
||||
* Assetto Corsa & ACC do not need any additional settings and should work out of the box
|
||||
|
||||
## Run everything, but in the right order
|
||||
* first start `simd` like `~/simapi/simd/build/simd --no-daemon -vv`
|
||||
* this will likely fail, so you'll need to add `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib` or `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib64` in front of the command like `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib64 ~/simapi/simd/build/simd --no-daemon -vv`
|
||||
* now start `monocoque` like `~/monocoque/build/monocoque play`
|
||||
* finally start the Game you want to play from Steam
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* make sure `simd` is running and after you start a game, the CLI output confirms the game is detected
|
||||
* make sure the bridge application starts e.g. with `ps aux|grep pcars2bridge.exe` before the game starts
|
||||
* see if you have the path `/dev/shm/acpmf_physics` (for ACC) and/or `/dev/shm/$pcars2$` for AMS2
|
||||
* when `simd` recognizes the game, see if `monocoque` shows `RPM, Gear..` and other Car Metrics once you start a game
|
||||
* if `simd` recognozies the game, but `monocoque` does not show any data, doublecheck the [Game Settings](#steam--game-config) to see if `Shared Memory` is activated
|
||||
@@ -8,41 +8,115 @@ _ / / / / /_/ / / / / /_/ / /__ / /_/ / /_/ // /_/ // __/
|
||||
```
|
||||
Cross Platform device manager for driving and flight simulators, for use with common simulator software titles.
|
||||
|
||||
📚 **Usage Documentation:** [spacefreak18.github.io/simapi/](https://spacefreak18.github.io/simapi/)
|
||||
|
||||
## Features
|
||||
- Updates at 120 frames per seconds.
|
||||
- Updates at 60 frames per seconds.
|
||||
- Modular design for support with various titles and devices.
|
||||
- Supports bass shakers, tachometers, simlights, simwind etc, through usb and arduino serial.
|
||||
- Supports bass shakers, tachometers, several wheels and pedals, simlights, simwind etc, through usb and arduino serial.
|
||||
- Tachometer support is currently limited to the Revburner model. Supports existing revburner xml configuration files.
|
||||
- Includes utility to configure revburner tachometer
|
||||
- Can send data to any serial device. So far only tested with arduino. Includes sample arduino sketch for sim lights.
|
||||
- The support for haptic bass shakers is limited and needs the most work. So far the engine rev is a simple sine wave, which I find convincing. The gear shift event works but not convincing enough for me.
|
||||
- Can send data to any serial device. So far only tested with arduino. and ESP32. Includes sample arduino sketch for sim lights, simwind, and simhaptic effects for motors.
|
||||
- Support for custom arduino (or any serial) device, with a [custom lua format](https://spacefreak18.github.io/simapi/serial_custom) for sending data
|
||||
- Convincing shaker effects for noise tranducers for wheel slip, wheel lock, and abs, as well as engine rpm and gear shifts.
|
||||
- Support for many [wheels and pedals](https://spacefreak18.github.io/simapi/thirdpartydevices) including Clubsport Elite V3, [Logitech G29](https://spacefreak18.github.io/simapi/logitechg29), and Moza R5.
|
||||
|
||||
## Dependencies
|
||||
## Adding More Devices
|
||||
- If a device isn't already supported, feel free to assist in the reverse engineering process by submitting a pcap or even better working code in a pull request!
|
||||
https://santeri.pikarinen.com/pages/usb_hid_reverse_engineering/
|
||||
|
||||
## Quick Install
|
||||
|
||||
**One-Line Installation:**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Spacefreak18/monocoque/master/install.sh | bash
|
||||
```
|
||||
***AUR Package following git master:***
|
||||
```https://aur.archlinux.org/packages/monocoque-git```
|
||||
|
||||
**Or download and review first:**
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/Spacefreak18/monocoque/master/install.sh
|
||||
chmod +x install.sh
|
||||
./install.sh
|
||||
```
|
||||
|
||||
### Installation on Immutable Distros (Bazzite, Silverblue, etc.)
|
||||
|
||||
On immutable distributions where native package managers are unavailable,
|
||||
monocoque can be installed inside a distrobox container:
|
||||
|
||||
```bash
|
||||
bash tools/distro/distrobox/install-distrobox.sh
|
||||
```
|
||||
|
||||
This creates an Arch Linux container, installs all packages via AUR, and sets up
|
||||
wrapper scripts (`start-simd`, `start-monocoque`, `test-monocoque`) in `~/.local/bin/`.
|
||||
|
||||
To uninstall: `bash tools/distro/distrobox/uninstall-distrobox.sh`
|
||||
|
||||
**TUI Manager:**
|
||||
After installation, use the interactive manager:
|
||||
```bash
|
||||
monocoque-manager
|
||||
```
|
||||
|
||||
**Supported Games**
|
||||
[Supported Sims](https://spacefreak18.github.io/simapi/supportedsims)
|
||||
***please note on Linux some titles will require a compatibility exe from simshmbridge to be setup. Please follow the linked Documentation
|
||||
for installation instructions***
|
||||
|
||||
|
||||
## Building
|
||||
|
||||
### Dependencies (static dependencies are linked as to show their respective copyright and licenses)
|
||||
- libserialport - arduino serial devices
|
||||
- hidapi - usb hid devices
|
||||
- libusb - used by hidapi
|
||||
- hidapi - usb hid devices (hidraw)
|
||||
- portaudio - sound devices (haptic bass shakers)
|
||||
- libenet - UDP support (not yet implemented)
|
||||
- libpulse - sound devices (haptic bass shakers)
|
||||
- libuv base event loop
|
||||
- libxml2
|
||||
- argtable2
|
||||
- libconfig
|
||||
- [slog](https://github.com/kala13x/slog) (static)
|
||||
- [wine-linux-shm-adapter](https://github.com/spacefreak18/wine-linux-shm-adapter)
|
||||
- xdg-basedir
|
||||
- lua
|
||||
- libproc2
|
||||
- libcurl4
|
||||
- libgtk3
|
||||
- libglu1-mesa
|
||||
- [simapi](https://github.com/spacefreak18/simapi)
|
||||
- [slog](https://github.com/kala13x/slog) (static)
|
||||
- [nappgui](https://github.com/frang75/nappgui_src) (static) (for GUI)
|
||||
(sorta optional)
|
||||
- [simshmbridge](https://github.com/spacefreak18/simshmbridge) - for sims that need shared memory mapping like AC and Project Cars related.
|
||||
|
||||
```
|
||||
pacman -Syu git cmake pulse-native-provider libxdg-basedir libserialport libconfig libuv argtable hidapi lua libproc2
|
||||
```
|
||||
|
||||
## Building
|
||||
This code depends on the shared memory data headers in the simapi [repo](https://github.com/spacefreak18/simapi). When pulling lastest if the submodule does not download run:
|
||||
```
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
Then to compile simply:
|
||||
```
|
||||
mkdir build; cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
## User Setup Guide
|
||||
See the dedicated [How To](HOW-TO-USE.md) for detailed instructions to set up and run 'monocoque`
|
||||
|
||||
## Testing
|
||||
```
|
||||
./monocoque test -vv # Make sure that ~/.config/monocque/monocoque.config only contains the devices you have connected.
|
||||
```
|
||||
|
||||
### Logs file location
|
||||
`~/.cache/monocoque/*.log`
|
||||
|
||||
### Static Analysis
|
||||
```
|
||||
@@ -51,16 +125,20 @@ make
|
||||
cmake -Danalyze=on ..
|
||||
make
|
||||
```
|
||||
|
||||
### Valgrind
|
||||
```
|
||||
cd build
|
||||
valgrind -v --leak-check=full --show-leak-kinds=all --suppressions=../.valgrindrc ./monocoque play
|
||||
```
|
||||
|
||||
## Join the Discussion
|
||||
[Sim Racing Matrix Space](https://matrix.to/#/#simracing:matrix.org)
|
||||
|
||||
## ToDo
|
||||
- add frequency cap (low pass filter) to sound haptic effects
|
||||
- add road and kerb sound haptic effects
|
||||
- windows port
|
||||
- more memory testing
|
||||
- handling null mallocs
|
||||
- move config code around
|
||||
- cleanup tests which are basically just copies of the example from their respective projects
|
||||
- much, much more
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
-- start led, end led, color
|
||||
-- set_led_range_to_color(1, 5, YELLOW);
|
||||
|
||||
-- led, color
|
||||
-- set_led_to_color(3, ORANGE);
|
||||
|
||||
-- current colors
|
||||
-- RED
|
||||
-- GREEN
|
||||
-- BLUE
|
||||
-- YELLOW
|
||||
-- ORANGE
|
||||
|
||||
if simdata.rpm >= simdata.maxrpm-500 then
|
||||
set_led_range_to_color(1, 6, RED)
|
||||
elseif simdata.rpm >= 4000 then
|
||||
set_led_range_to_color(1, 4, YELLOW)
|
||||
elseif simdata.rpm >= 3000 then
|
||||
set_led_range_to_color(1, 3, GREEN)
|
||||
elseif simdata.rpm >= 2000 then
|
||||
set_led_range_to_rgb_color(1, 2, 0, 255, 0)
|
||||
-- set_led_to_rgb_color also available
|
||||
elseif simdata.rpm >= 1000 then
|
||||
set_led_to_color(1, GREEN)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
-- configuration
|
||||
-- set these accordingly
|
||||
right_leds_start = 7
|
||||
right_num_leds = 42
|
||||
left_leds_start = 49
|
||||
left_num_leds = 42
|
||||
--
|
||||
|
||||
max_radius = 10 -- radius is hardcoded in simapi
|
||||
proxcars = simdata.proxcars -- also hard coded in simapi
|
||||
|
||||
rightsideset = false
|
||||
leftsideset = false
|
||||
|
||||
-- right side
|
||||
litleds = 0
|
||||
i = 1
|
||||
while(i <= proxcars and rightsideset == false) do
|
||||
if simdata.pd[i].theta > 40 and simdata.pd[i].theta < 130 then
|
||||
dist = math.abs(90 - simdata.pd[i].theta)
|
||||
perct = (90 - dist)/90
|
||||
litleds = math.ceil(perct * right_num_leds)
|
||||
|
||||
color_perct = simdata.pd[i].radius/10
|
||||
yellow = math.floor(color_perct * 255)
|
||||
local rgb = (255 << 16) | (yellow << 8) | (0 << 0)
|
||||
if simdata.pd[1].theta >= 90 then
|
||||
-- car is behind
|
||||
set_led_range_to_rgb_color(right_leds_start, right_leds_start + litleds, rgb)
|
||||
else
|
||||
set_led_range_to_rgb_color(right_leds_start + (right_num_leds - litleds), right_leds_start + right_num_leds, rgb)
|
||||
end
|
||||
rightsideset = true
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- left side
|
||||
litleds = 0
|
||||
i = 1
|
||||
while(i <= proxcars and leftsideset == false) do
|
||||
if simdata.pd[i].theta > 230 and simdata.pd[i].theta < 320 then
|
||||
dist = math.abs(270 - simdata.pd[1].theta)
|
||||
perct = (90 - dist)/90
|
||||
litleds = math.ceil(perct * left_num_leds)
|
||||
|
||||
color_perct = simdata.pd[i].radius/10
|
||||
yellow = math.floor(color_perct * 255)
|
||||
local rgb = (255 << 16) | (yellow << 8) | (0 << 0)
|
||||
if simdata.pd[1].theta <= 270 then
|
||||
-- car is behind
|
||||
set_led_range_to_rgb_color(left_leds_start, left_leds_start + litleds, rgb)
|
||||
else
|
||||
set_led_range_to_rgb_color(left_leds_start + (left_num_leds - litleds), left_leds_start + left_num_leds, rgb)
|
||||
end
|
||||
leftsideset = true
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Message = simdata.velocity .. ";" .. simdata.gear .. ";" .. simdata.rpm .. ";"
|
||||
+245
-20
@@ -1,21 +1,246 @@
|
||||
configs = (
|
||||
{
|
||||
sim = "default";
|
||||
// one can also specify sim by using the "short-name" for example "ac". also, "acc", "ace", "ams2", "et", "at" and "rf2"
|
||||
//sim = "ac";
|
||||
car = "default";
|
||||
devices = (
|
||||
{
|
||||
device = "USB";
|
||||
type = "Tachometer";
|
||||
devid = "98FD:83AC";
|
||||
subtype = "Revburner";
|
||||
granularity = 2;
|
||||
config = "~/.config/monocoque/revburner15000.xml";
|
||||
},
|
||||
|
||||
devices = ( { device = "USB";
|
||||
type = "Tachometer";
|
||||
devid = "98FD:83AC";
|
||||
subtype = "Revburner";
|
||||
granularity = 4;
|
||||
config = "/home/paul/.config/monocoque/revburner1.xml"; },
|
||||
{ device = "Sound";
|
||||
type = "Engine"
|
||||
devid = "98FD:83AC"; },
|
||||
{ device = "Sound";
|
||||
type = "Gear"
|
||||
devid = "98FD:83AC"; },
|
||||
{ device = "Serial";
|
||||
type = "ShiftLights";
|
||||
config = "None";
|
||||
devpath = "/dev/ttyACM0"; } ),
|
||||
{ device = "Serial";
|
||||
type = "SimWind";
|
||||
config = "None";
|
||||
devpath = "/dev/ttyACM1"; } );
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "Engine";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 3;
|
||||
fps = 60;
|
||||
threshold = 0.2;
|
||||
channels = 8;
|
||||
volume = 70;
|
||||
modulation = "frequency";
|
||||
frequency = 17;
|
||||
frequencyMax = 37;
|
||||
noise = 10; // additive noise in Hz
|
||||
},
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "Gear"
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 3;
|
||||
fps = 60;
|
||||
threshold = 0.2;
|
||||
channels = 8;
|
||||
duration = .6;
|
||||
volume = 100;
|
||||
frequency = 15;
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "TyreSlip";
|
||||
tyre = "RearLeft";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 4;
|
||||
channels = 8;
|
||||
threshold = 0.2;
|
||||
fps = 60;
|
||||
volume = 100;
|
||||
modulation = "frequency";
|
||||
frequency = 30;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "TyreSlip";
|
||||
tyre = "RearRight";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 5;
|
||||
fps = 60;
|
||||
channels = 8;
|
||||
threshold = 0.2;
|
||||
volume = 100;
|
||||
modulation = "frequency";
|
||||
frequency = 30;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "TyreSlip";
|
||||
tyre = "FrontLeft";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 0;
|
||||
channels = 8;
|
||||
threshold = 0.2;
|
||||
volume = 100;
|
||||
modulation = "frequency";
|
||||
frequency = 30;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "TyreSlip";
|
||||
tyre = "FrontRight";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 1;
|
||||
channels = 8;
|
||||
threshold = 0.2;
|
||||
volume = 100;
|
||||
modulation = "frequency";
|
||||
frequency = 30;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "TyreLock";
|
||||
tyre = "RearLeft";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 4;
|
||||
channels = 8;
|
||||
threshold = 0.2;
|
||||
volume = 100;
|
||||
modulation = "frequency";
|
||||
frequency = 30;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "TyreLock";
|
||||
tyre = "RearRight";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 5;
|
||||
channels = 8;
|
||||
threshold = 0.2;
|
||||
modulation = "frequency";
|
||||
volume = 100;
|
||||
frequency = 30;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "TyreLock";
|
||||
tyre = "FrontLeft";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 0;
|
||||
channels = 8;
|
||||
threshold = 0.2;
|
||||
volume = 100;
|
||||
modulation = "frequency";
|
||||
frequency = 30;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "TyreLock";
|
||||
tyre = "FrontRight";
|
||||
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
|
||||
pan = 1;
|
||||
channels = 8;
|
||||
threshold = 0.2;
|
||||
volume = 100;
|
||||
modulation = "frequency";
|
||||
frequency = 30;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
|
||||
{
|
||||
device = "USB";
|
||||
type = "Haptic";
|
||||
subtype = "CSLELITEV3PEDALS";
|
||||
//devpath = "/sys/module/hid_fanatec/drivers/hid:fanatec/0003:0EB7:183B.0002/rumble";
|
||||
devpath = "/sys/module/hid_fanatec/drivers/hid:ftec_csl_elite/0003:0EB7:183B.0002/rumble";
|
||||
effect = "ABS";
|
||||
tyre = "ALL";
|
||||
threshold = 0.2;
|
||||
value0 = 0;
|
||||
value1 = 16776960;
|
||||
},
|
||||
{
|
||||
device = "USB";
|
||||
type = "Haptic";
|
||||
subtype = "CSLELITEV3PEDALS";
|
||||
//devpath = "/sys/module/hid_fanatec/drivers/hid:fanatec/0003:0EB7:183B.0002/rumble";
|
||||
devpath = "/sys/module/hid_fanatec/drivers/hid:ftec_csl_elite/0003:0EB7:183B.0002/rumble";
|
||||
effect = "Slip";
|
||||
tyre = "ALL";
|
||||
threshold = 0.2;
|
||||
value0 = 0;
|
||||
value1 = 16711680;
|
||||
},
|
||||
{
|
||||
device = "USB";
|
||||
type = "Haptic";
|
||||
subtype = "CSLELITEV3PEDALS";
|
||||
//devpath = "/sys/module/hid_fanatec/drivers/hid:fanatec/0003:0EB7:183B.0002/rumble";
|
||||
devpath = "/sys/module/hid_fanatec/drivers/hid:ftec_csl_elite/0003:0EB7:183B.0002/rumble";
|
||||
effect = "Lock";
|
||||
tyre = "ALL";
|
||||
threshold = 0.2;
|
||||
value0 = 0;
|
||||
value1 = 65280;
|
||||
},
|
||||
|
||||
{
|
||||
device = "Serial";
|
||||
type = "Haptic";
|
||||
effect = "TyreSlip";
|
||||
tyre = "ALL"; // motor m1 connected to throttle
|
||||
motors = 0;
|
||||
devpath = "/dev/ttyACM0";
|
||||
},
|
||||
{
|
||||
device = "Serial";
|
||||
type = "Haptic";
|
||||
effect = "TyreLock";
|
||||
tyre = "ALL"; // motor m3 connected to brake;
|
||||
motors = 2;
|
||||
devpath = "/dev/ttyACM0";
|
||||
},
|
||||
{
|
||||
device = "Serial";
|
||||
type = "Haptic"
|
||||
effect = "ABS";
|
||||
tyre = "ALL"; // this will have to be documented, this tells the code to spin both motors
|
||||
motors = 8;
|
||||
devpath = "/dev/ttyACM0";
|
||||
},
|
||||
{
|
||||
device = "Serial";
|
||||
type = "SimWind";
|
||||
config = "None";
|
||||
baud = 115200;
|
||||
devpath = "/dev/simdev1";
|
||||
fanpower = 0.6;
|
||||
},
|
||||
|
||||
{
|
||||
device = "Serial";
|
||||
type = "SimWind";
|
||||
config = "None";
|
||||
baud = 115200;
|
||||
devpath = "/dev/simdev1";
|
||||
fanpower = 0.6;
|
||||
},
|
||||
|
||||
{
|
||||
device = "Serial";
|
||||
type = "Simleds";
|
||||
numleds = 6;
|
||||
startled = 2;
|
||||
endled = 5;
|
||||
config = "~/git/monocoque/conf/rpms_and_radar.lua"
|
||||
baud = 115200;
|
||||
devpath = "/dev/simdev0";
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
-- from simapi.h
|
||||
-- SIMAPI_FLAG_GREEN = 0,
|
||||
-- SIMAPI_FLAG_YELLOW = 1,
|
||||
-- SIMAPI_FLAG_RED = 2,
|
||||
-- SIMAPI_FLAG_CHEQUERED = 3,
|
||||
-- SIMAPI_FLAG_BLUE = 4,
|
||||
-- SIMAPI_FLAG_WHITE = 5,
|
||||
-- SIMAPI_FLAG_BLACK = 6,
|
||||
-- SIMAPI_FLAG_BLACK_WHITE = 7,
|
||||
-- SIMAPI_FLAG_BLACK_ORANGE = 8,
|
||||
-- SIMAPI_FLAG_ORANGE = 9
|
||||
|
||||
led_clear_all()
|
||||
|
||||
if simdata.rpm > 0 and simdata.maxrpm > 0 then
|
||||
rpmmargin = .05*simdata.maxrpm;
|
||||
rpminterval = (simdata.maxrpm-rpmmargin) / 14;
|
||||
|
||||
litleds = 0
|
||||
for i = 1,14 do
|
||||
if simdata.rpm >= (rpminterval * i) then
|
||||
litleds = i;
|
||||
end
|
||||
end
|
||||
|
||||
color = GREEN
|
||||
if litleds > 5 and litleds < 12 then
|
||||
color = YELLOW
|
||||
end
|
||||
if litleds >= 12 then
|
||||
color = RED
|
||||
end
|
||||
set_led_range_to_color(59, 59+litleds, color)
|
||||
end
|
||||
|
||||
if simdata.playerflag == 0 then
|
||||
set_led_range_to_color(4, 7, GREEN)
|
||||
end
|
||||
|
||||
|
||||
if simdata.playerflag == 1 then
|
||||
set_led_range_to_color(4, 7, YELLOW)
|
||||
end
|
||||
|
||||
|
||||
if simdata.playerflag == 4 then
|
||||
set_led_range_to_color(4, 7, BLUE)
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
-- from simapi.h
|
||||
-- SIMAPI_FLAG_GREEN = 0,
|
||||
-- SIMAPI_FLAG_YELLOW = 1,
|
||||
-- SIMAPI_FLAG_RED = 2,
|
||||
-- SIMAPI_FLAG_CHEQUERED = 3,
|
||||
-- SIMAPI_FLAG_BLUE = 4,
|
||||
-- SIMAPI_FLAG_WHITE = 5,
|
||||
-- SIMAPI_FLAG_BLACK = 6,
|
||||
-- SIMAPI_FLAG_BLACK_WHITE = 7,
|
||||
-- SIMAPI_FLAG_BLACK_ORANGE = 8,
|
||||
-- SIMAPI_FLAG_ORANGE = 9
|
||||
|
||||
if simdata.rpm > 0 and simdata.maxrpm > 0 then
|
||||
rpmmargin = .05*simdata.maxrpm;
|
||||
rpminterval = (simdata.maxrpm-rpmmargin) / 6;
|
||||
|
||||
litleds = 0
|
||||
for i = 1,6 do
|
||||
if simdata.rpm >= (rpminterval * i) then
|
||||
litleds = i;
|
||||
end
|
||||
end
|
||||
|
||||
color = GREEN
|
||||
if litleds > 3 and litleds < 6 then
|
||||
color = YELLOW
|
||||
end
|
||||
if litleds >= 6 then
|
||||
color = RED
|
||||
end
|
||||
if litleds >= 6 then
|
||||
|
||||
if simdata.mtick % 2 == 0 then
|
||||
set_led_range_to_color(1, litleds, color)
|
||||
else
|
||||
led_clear_all()
|
||||
end
|
||||
else
|
||||
set_led_range_to_color(1, litleds, color)
|
||||
end
|
||||
end
|
||||
|
||||
if simdata.playerflag == 0 then
|
||||
set_led_range_to_color(7, 48, GREEN)
|
||||
end
|
||||
|
||||
|
||||
if simdata.playerflag == 1 then
|
||||
set_led_range_to_color(7, 48, YELLOW)
|
||||
end
|
||||
|
||||
|
||||
if simdata.playerflag == 4 then
|
||||
set_led_range_to_color(7, 48, BLUE)
|
||||
end
|
||||
@@ -0,0 +1,91 @@
|
||||
-- configuration
|
||||
-- set these accordingly
|
||||
right_leds_start = 7
|
||||
right_num_leds = 42
|
||||
left_leds_start = 49
|
||||
left_num_leds = 42
|
||||
--
|
||||
|
||||
max_radius = 10 -- radius is hardcoded in simapi
|
||||
proxcars = simdata.proxcars -- also hard coded in simapi
|
||||
|
||||
rightsideset = false
|
||||
leftsideset = false
|
||||
|
||||
-- right side
|
||||
litleds = 0
|
||||
i = 1
|
||||
while(i <= proxcars and rightsideset == false) do
|
||||
if simdata.pd[i].theta > 40 and simdata.pd[i].theta < 130 then
|
||||
dist = math.abs(90 - simdata.pd[i].theta)
|
||||
perct = (90 - dist)/90
|
||||
litleds = math.ceil(perct * right_num_leds)
|
||||
|
||||
color_perct = simdata.pd[i].radius/10
|
||||
yellow = math.floor(color_perct * 255)
|
||||
local rgb = (255 << 16) | (yellow << 8) | (0 << 0)
|
||||
if simdata.pd[1].theta >= 90 then
|
||||
-- car is behind
|
||||
set_led_range_to_rgb_color(right_leds_start, right_leds_start + litleds, rgb)
|
||||
else
|
||||
set_led_range_to_rgb_color(right_leds_start + (right_num_leds - litleds), right_leds_start + right_num_leds, rgb)
|
||||
end
|
||||
rightsideset = true
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- left side
|
||||
litleds = 0
|
||||
i = 1
|
||||
while(i <= proxcars and leftsideset == false) do
|
||||
if simdata.pd[i].theta > 230 and simdata.pd[i].theta < 320 then
|
||||
dist = math.abs(270 - simdata.pd[1].theta)
|
||||
perct = (90 - dist)/90
|
||||
litleds = math.ceil(perct * left_num_leds)
|
||||
|
||||
color_perct = simdata.pd[i].radius/10
|
||||
yellow = math.floor(color_perct * 255)
|
||||
local rgb = (255 << 16) | (yellow << 8) | (0 << 0)
|
||||
if simdata.pd[1].theta <= 270 then
|
||||
-- car is behind
|
||||
set_led_range_to_rgb_color(left_leds_start, left_leds_start + litleds, rgb)
|
||||
else
|
||||
set_led_range_to_rgb_color(left_leds_start + (left_num_leds - litleds), left_leds_start + left_num_leds, rgb)
|
||||
end
|
||||
leftsideset = true
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
rpm_leds = 6
|
||||
-- rpm stuff
|
||||
if simdata.rpm > 0 and simdata.maxrpm > 0 then
|
||||
rpmmargin = .05*simdata.maxrpm;
|
||||
rpminterval = (simdata.maxrpm-rpmmargin) / rpm_leds;
|
||||
|
||||
litleds = 0
|
||||
for i = 1,6 do
|
||||
if simdata.rpm >= (rpminterval * i) then
|
||||
litleds = i;
|
||||
end
|
||||
end
|
||||
|
||||
color = GREEN
|
||||
if litleds > rpm_leds/2 and litleds < rpm_leds then
|
||||
color = YELLOW
|
||||
end
|
||||
if litleds >= rpm_leds then
|
||||
color = RED
|
||||
end
|
||||
if litleds >= rpm_leds then
|
||||
|
||||
if simdata.mtick % 2 == 0 then
|
||||
set_led_range_to_color(1, litleds, color)
|
||||
else
|
||||
led_clear_range(1, rpm_leds)
|
||||
end
|
||||
else
|
||||
set_led_range_to_color(1, litleds, color)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Monocoque
|
||||
Comment=Device manager for racing sims
|
||||
Exec=gmonocoque
|
||||
Icon=io.github.spacefreak18.monocoque
|
||||
Terminal=false
|
||||
Categories=Game;Development;
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>io.github.spacefreak18.monocoque</id>
|
||||
<name>Monocoque</name>
|
||||
<summary>Device manager for racing sims</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0-only</project_license>
|
||||
<description>
|
||||
<p>
|
||||
Monocoque bridges telemetry from supported racing simulators to
|
||||
peripheral devices such as haptic shakers, LED strips, shift lights,
|
||||
and sim wind fans, and exposes a local API for third-party dashboard
|
||||
and configuration tools.
|
||||
</p>
|
||||
</description>
|
||||
<launchable type="desktop-id">io.github.spacefreak18.monocoque.desktop</launchable>
|
||||
<provides>
|
||||
<binary>monocoque</binary>
|
||||
<binary>gmonocoque</binary>
|
||||
</provides>
|
||||
<url type="homepage">https://spacefreak18.github.io/simapi/monocoque.html</url>
|
||||
<url type="vcs-browser">https://github.com/Spacefreak18/monocoque</url>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
<release version="0.3.6" date="2026-08-21" />
|
||||
</releases>
|
||||
</component>
|
||||
@@ -0,0 +1,320 @@
|
||||
# Flatpak manifest for monocoque -- NOT submitted anywhere (Flathub or
|
||||
# otherwise) yet. Built 2026-08-30 on the patterns proven working for the
|
||||
# sibling typiql-tauri/typiql project's own Flatpak pipeline (see this
|
||||
# repo's .github/workflows/flatpak.yml header for the full list of bugs
|
||||
# found and fixed here, monocoque-specific beyond what that sibling project
|
||||
# already covered). VERIFIED LOCALLY end-to-end: built, installed, and ran
|
||||
# both `monocoque --help` and `gmonocoque` from the resulting bundle. Not
|
||||
# yet verified against real CI or real hardware (HID/serial/USB device
|
||||
# access under the sandbox).
|
||||
#
|
||||
# org.freedesktop.Platform, not org.gnome.Platform: monocoque's GUI
|
||||
# (gmonocoque) only needs GTK3, which freedesktop.Platform//25.08 already
|
||||
# ships -- no need for the heavier GNOME runtime typiql needs for WebKitGTK.
|
||||
# simd is NOT bundled. It is a separate daemon with its own packaging, and
|
||||
# monocoque does not need it inside the sandbox to use it: simapi_get_sim()
|
||||
# checks /dev/shm/SIMAPI.DAT before it ever scans /proc, so a simd running on
|
||||
# the host -- however it was installed -- publishes telemetry this sandbox
|
||||
# reads directly. That is what --device=shm below is for, and it is the only
|
||||
# thing this manifest needs in order to work with simd.
|
||||
#
|
||||
# simapi itself is still present, but only as monocoque's own statically
|
||||
# linked submodule, exactly as upstream's CMakeLists builds it. There is no
|
||||
# separate simapi module here.
|
||||
app-id: io.github.spacefreak18.monocoque
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '25.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
command: gmonocoque
|
||||
finish-args:
|
||||
# --socket=x11, not fallback-x11: nappgui (the GUI toolkit gmonocoque uses)
|
||||
# goes to X11 even when a Wayland display is available. fallback-x11 is
|
||||
# deliberately inactive whenever Wayland works, so under a Wayland session
|
||||
# DISPLAY stayed empty and gmonocoque died with `cannot open display` --
|
||||
# confirmed on a real launch, with WAYLAND_DISPLAY=wayland-0 present and the
|
||||
# socket visible inside the sandbox. Granting real X11 routes it through
|
||||
# XWayland instead. --socket=wayland is kept so the toolkit can use it
|
||||
# directly if that ever changes.
|
||||
- --socket=x11
|
||||
- --socket=wayland
|
||||
# Standard companion to X11 access: without it MIT-SHM is unavailable and
|
||||
# some toolkits fall over.
|
||||
- --share=ipc
|
||||
- --socket=pulseaudio
|
||||
# HID/serial/USB access for wheels, pedals, shifters, and shaker/LED
|
||||
# devices -- known risk area, Flatpak's sandboxing of raw device nodes is
|
||||
# not always well-behaved; needs real hardware testing, not just a
|
||||
# successful build.
|
||||
- --device=all
|
||||
# Retained, but not for the reason first written here: the GraphQL API on
|
||||
# port 9000 is served by typiql's own Rust backend, not by monocoque --
|
||||
# monocoque has no network code of its own, and the only libcurl in the
|
||||
# binary is vendored nappgui's HTTP module, which nothing here calls. The
|
||||
# two programs talk through ~/.config/monocoque and the process table, not
|
||||
# a socket. This flag can probably be dropped.
|
||||
- --share=network
|
||||
# monocoque builds its paths from $HOME directly (e.g.
|
||||
# "%s/.config/monocoque/diameters.config" in monocoque-cli.c), not from
|
||||
# XDG_CONFIG_HOME, so it reads and writes the host's real directories.
|
||||
# Without these grants a real launch failed with
|
||||
# Failed to read config file '/home/david/.config/monocoque/monocoque.config'
|
||||
# slog_open_file: Failed to open file: /home/david/.cache/monocoque/...log
|
||||
# -- the manifest previously granted no filesystem access at all. Sharing
|
||||
# the real config directory is also what makes a Flatpak monocoque and a
|
||||
# native one agree, and what lets typiql configure it.
|
||||
- --filesystem=xdg-config/monocoque:create
|
||||
- --filesystem=xdg-cache/monocoque:create
|
||||
# Host /dev/shm, where a host simd publishes telemetry as POSIX shared memory
|
||||
# (/dev/shm/SIMAPI.DAT, mapped in simapi's simmapper.c). This is the flag
|
||||
# that makes a Flatpak monocoque work with a simd installed on the host, so
|
||||
# it matters more now that simd is not bundled, not less.
|
||||
#
|
||||
# shm is its own device option and is NOT implied by --device=all above --
|
||||
# measured, with simd running: `ls /dev/shm` inside this sandbox returned 0
|
||||
# entries against 18 on the host. The failure it caused is silent rather
|
||||
# than loud: `monocoque play` started, read its config, connected
|
||||
# pulseaudio, and then sat at "setting initial app state" forever, never
|
||||
# logging "Opening universal shared memory api", because simd looked absent.
|
||||
# The identical run with this flag reached that line one second in.
|
||||
- --device=shm
|
||||
|
||||
modules:
|
||||
- name: libconfig
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DBUILD_TESTS=OFF
|
||||
# libconfig 1.7.3's CMakeLists.txt predates CMake 3.5's minimum
|
||||
# version policy; confirmed via a failed local build.
|
||||
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
post-install:
|
||||
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/libconfig/$(basename "$f"); else echo "WARNING: no licence file found for libconfig"; fi'
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/hyperrealm/libconfig/archive/refs/tags/v1.7.3.tar.gz
|
||||
sha256: 68757e37c567fd026330c8a8449aa5f9cac08a642f213f2687186b903bd7e94e
|
||||
|
||||
- name: argtable2
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
# Same CMake-minimum-version issue as libconfig -- this one's
|
||||
# CMakeLists.txt is even older (2011).
|
||||
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
# 2011-era code missing <ctype.h>/<strings.h> includes for
|
||||
# isspace/toupper/bzero; GCC 14+ made implicit-function-declaration a
|
||||
# hard error by default where it used to just warn. Confirmed via a
|
||||
# failed local build.
|
||||
- -DCMAKE_C_FLAGS=-Wno-error=implicit-function-declaration
|
||||
# add_library(argtable2 ...) has no explicit STATIC/SHARED, so it
|
||||
# defaults to CMake's global BUILD_SHARED_LIBS (off by default -> a
|
||||
# static .a). monocoque's own target_link_libraries() lists argtable2
|
||||
# before helper (which needs its symbols, e.g. arg_end/arg_parse in
|
||||
# parameters.c) -- fine for a shared library (all symbols always
|
||||
# available regardless of link-line position, which is what real
|
||||
# distro packages of this ship), but a static archive only pulls
|
||||
# symbols already needed by things linked *before* it, so helper's
|
||||
# later reference to argtable2's symbols never resolves. Confirmed
|
||||
# via a failed local build ("undefined reference to arg_end" etc.
|
||||
# from helper's parameters.c, even though libargtable2.a genuinely
|
||||
# contained those symbols). Building shared instead matches how this
|
||||
# dependency normally ships and avoids the link-order sensitivity
|
||||
# entirely, rather than reordering every target_link_libraries() call
|
||||
# in CMakeLists.txt.
|
||||
- -DBUILD_SHARED_LIBS=ON
|
||||
# This CMake port has no install() rules at all (confirmed: no
|
||||
# CMakeLists.txt in the tree calls install()), so the default
|
||||
# cmake-ninja `ninja install` step silently does nothing -- monocoque's
|
||||
# own build then fails on a missing argtable2.h. build-commands here
|
||||
# replace the default install step: `ninja` still does the real
|
||||
# compile, then copy the header and whatever library form actually got
|
||||
# built (glob rather than a fixed versioned filename, since the exact
|
||||
# .so version suffix isn't predictable without a build).
|
||||
build-commands:
|
||||
- ninja
|
||||
- install -Dm644 src/argtable2.h /app/include/argtable2.h
|
||||
- |
|
||||
for f in src/libargtable2.so* src/libargtable2.a; do
|
||||
if [ -e "$f" ]; then cp -P "$f" /app/lib/; fi
|
||||
done
|
||||
true
|
||||
post-install:
|
||||
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/argtable2/$(basename "$f"); else echo "WARNING: no licence file found for argtable2"; fi'
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://sourceforge.net/projects/argtable/files/argtable/argtable-2.13/argtable2-13.tar.gz/download
|
||||
sha256: 8f77e8a7ced5301af6e22f47302fdbc3b1ff41f2b83c43c77ae5ca041771ddbf
|
||||
dest-filename: argtable2-13.tar.gz
|
||||
|
||||
- name: lua5.4
|
||||
# Lua's own Makefile, not CMake/autotools. `posix` (not `linux`) avoids
|
||||
# a readline dependency only needed for the interactive REPL binary,
|
||||
# which monocoque doesn't use -- only the library.
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- make posix MYCFLAGS=-fPIC
|
||||
- make install INSTALL_TOP=/app
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://www.lua.org/ftp/lua-5.4.7.tar.gz
|
||||
sha256: 9fbf5e28ef86c69858f6d3d34eccc32e911c1a28b4120ff3e84aaa70cfbf1e30
|
||||
|
||||
- name: libserialport
|
||||
buildsystem: autotools
|
||||
# No pre-generated configure script in the git-archive tarball, only
|
||||
# autogen.sh.
|
||||
build-commands:
|
||||
- ./autogen.sh
|
||||
post-install:
|
||||
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/libserialport/$(basename "$f"); else echo "WARNING: no licence file found for libserialport"; fi'
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/sigrokproject/libserialport/archive/refs/tags/libserialport-0.1.2.tar.gz
|
||||
sha256: cbb1192a09ff31d34e7efdb17a2f50d9d1974461c0b81c29bb449515d78d8950
|
||||
|
||||
- name: libxdg-basedir
|
||||
buildsystem: autotools
|
||||
build-commands:
|
||||
- ./autogen.sh
|
||||
post-install:
|
||||
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/libxdg-basedir/$(basename "$f"); else echo "WARNING: no licence file found for libxdg-basedir"; fi'
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/devnev/libxdg-basedir/archive/refs/tags/libxdg-basedir-1.2.3.tar.gz
|
||||
sha256: ff30c60161f7043df4dcc6e7cdea8e064e382aa06c73dcc3d1885c7d2c77451d
|
||||
|
||||
- name: hidapi
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
# Same CMake-minimum-version issue as libconfig/argtable2, despite
|
||||
# this otherwise being a current, actively maintained release.
|
||||
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
# monocoque links hidapi-hidraw specifically -- no need for the
|
||||
# libusb-backed variant, which would pull in an extra dependency.
|
||||
- -DHIDAPI_WITH_LIBUSB=OFF
|
||||
- -DHIDAPI_WITH_HIDRAW=ON
|
||||
post-install:
|
||||
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/hidapi/$(basename "$f"); else echo "WARNING: no licence file found for hidapi"; fi'
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/libusb/hidapi/archive/refs/tags/hidapi-0.14.0.tar.gz
|
||||
sha256: a5714234abe6e1f53647dd8cba7d69f65f71c558b7896ed218864ffcf405bcbd
|
||||
|
||||
- name: glu
|
||||
buildsystem: meson
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://gitlab.freedesktop.org/mesa/glu/-/archive/glu-9.0.3/glu-glu-9.0.3.tar.gz
|
||||
sha256: 7e919cbc1b2677b01d65fc28fd36a19d1f3e23d76663020e0f3b82b991475e8b
|
||||
|
||||
- name: libuv
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DBUILD_TESTING=OFF
|
||||
post-install:
|
||||
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/libuv/$(basename "$f"); else echo "WARNING: no licence file found for libuv"; fi'
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/libuv/libuv/archive/refs/tags/v1.49.2.tar.gz
|
||||
sha256: 388ffcf3370d4cf7c4b3a3205504eea06c4be5f9e80d2ab32d19f8235accc1cf
|
||||
|
||||
- name: monocoque
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DBUILD_GUI=on
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
# CMakeLists.txt links most of the dependencies built as separate
|
||||
# flatpak modules above (hidapi-hidraw, serialport, argtable2, config,
|
||||
# uv, xdg-basedir) by bare library name with no explicit -L, relying on
|
||||
# the linker's default search path -- fine on a real system where these
|
||||
# install to /usr/lib, but /app/lib isn't in that default path.
|
||||
# -DCMAKE_EXE_LINKER_FLAGS=-L/app/lib didn't work: CMake appends
|
||||
# EXE_LINKER_FLAGS at the *end* of the link command, after the -l
|
||||
# flags that need it, and ld resolves -l against -L directories seen
|
||||
# so far, left to right -- confirmed via a failed local build showing
|
||||
# the exact same missing-library set with that flag present but
|
||||
# useless. LIBRARY_PATH is a GCC driver environment variable, applied
|
||||
# to its search regardless of where in the command line things land.
|
||||
build-options:
|
||||
env:
|
||||
LIBRARY_PATH: /app/lib
|
||||
sources:
|
||||
# One level up from flatpak/manifest.yml is the repo root -- unlike
|
||||
# typiql-tauri/typiql's dual-checkout ($GITHUB_WORKSPACE containing
|
||||
# two separate repos side by side, needing an extra level), monocoque
|
||||
# is checked out on its own with the standard single-repo layout, so
|
||||
# this doesn't need the same `../..`/`../../..` juggling that session
|
||||
# got wrong twice before finding the right depth for that project.
|
||||
- type: dir
|
||||
path: ..
|
||||
# Skip the submodule paths. If the tree being built has them checked
|
||||
# out, the copy brings along a real `.git` *file* at each path, and the
|
||||
# git sources below then fail to clone into the same spot: "cannot
|
||||
# overwrite non-directory .git with directory". Without this the
|
||||
# manifest only builds from a checkout that deliberately omits
|
||||
# submodules -- which is why CI's flatpak job checks out without them,
|
||||
# and why a local build right after `git submodule update --init`
|
||||
# breaks. Skipping makes it work either way.
|
||||
skip:
|
||||
- src/monocoque/simulatorapi/simapi
|
||||
- src/monocoque/mgui/nappgui_src
|
||||
# Two git submodules, each pinned to the exact commit .gitmodules/the
|
||||
# tree tracks -- confirmed via a failed local build that the `dir`
|
||||
# source above doesn't pull submodule content in (same class of issue
|
||||
# as typiql-tauri/typiql's own submodules, src/lib/per-form etc.).
|
||||
- type: git
|
||||
url: https://github.com/spacefreak18/simapi
|
||||
commit: 50fd181681862256780b2a0f74ef4bc459ed631a
|
||||
dest: src/monocoque/simulatorapi/simapi
|
||||
# nappgui_src is add_subdirectory()'d directly into monocoque's own
|
||||
# CMake tree (src/monocoque/mgui/CMakeLists.txt), not a separately
|
||||
# installed library -- so it's a source here, not its own flatpak
|
||||
# module.
|
||||
- type: git
|
||||
url: https://github.com/frang75/nappgui_src
|
||||
commit: 29c7bd3e0243eb38aa9a7e2eafe6cf74830c4ee8
|
||||
dest: src/monocoque/mgui/nappgui_src
|
||||
- type: file
|
||||
path: io.github.spacefreak18.monocoque.desktop
|
||||
- type: file
|
||||
path: io.github.spacefreak18.monocoque.metainfo.xml
|
||||
# Icon comes from tools/monocoque.svg via the `dir` source above (no
|
||||
# separate file source needed). That file is a square 100x100 copy of
|
||||
# the upstream artwork -- the published monocoque.svg the deb job
|
||||
# downloads is 100x50, which Flatpak's export rejects outright, while
|
||||
# appstreamcli separately hard-requires any desktop-application to
|
||||
# have an icon at all (both confirmed via failed real CI runs). It is
|
||||
# padded via a <g transform="translate(0,25)">, not stretched. Shared
|
||||
# with the AppImage job, which has the same square-icon need.
|
||||
post-install:
|
||||
# This package's own licence, the vendored nappgui's (a source rather
|
||||
# than a module, so the per-module collection above misses it), and the
|
||||
# notices file naming every bundled dependency's origin.
|
||||
- install -Dm644 LICENSE.rst /app/share/licenses/monocoque/LICENSE.rst
|
||||
# The Debian copyright file is upstream's own account of what is in this
|
||||
# source tree and under what terms -- slog (MIT), nappgui (MIT), simapi
|
||||
# (LGPL) -- which a bundle has to carry because it distributes all of
|
||||
# them, unlike a .deb whose dependencies are separate packages. Shipped
|
||||
# verbatim rather than restated.
|
||||
- install -Dm644 tools/distro/debian/dpkg/copyright /app/share/licenses/monocoque/copyright
|
||||
# lua and glu ship no licence file in their tarballs -- Lua's MIT text
|
||||
# lives in doc/readme.html, glu's SGI Free B only in source headers -- so
|
||||
# checked-in copies are installed from here, where the repo is the build
|
||||
# directory. (Doing it in their own modules fails: those build
|
||||
# out-of-tree, so their source directory is not the working directory.)
|
||||
- install -Dm644 packaging/licenses/lua-5.4-LICENSE.txt /app/share/licenses/lua5.4/LICENSE
|
||||
- install -Dm644 packaging/licenses/glu-LICENSE.txt /app/share/licenses/glu/LICENSE
|
||||
- install -Dm644 src/monocoque/mgui/nappgui_src/LICENSE /app/share/licenses/nappgui/LICENSE
|
||||
- sh -c 'python3 tools/generate-third-party-notices.py flatpak/io.github.spacefreak18.monocoque.yml > /tmp/notices.md && install -Dm644 /tmp/notices.md /app/share/licenses/THIRD-PARTY-NOTICES.md'
|
||||
# Confirmed via a failed local build that flatpak-builder's cmake-ninja
|
||||
# buildsystem runs the automatic build+install step regardless of
|
||||
# whether build-commands are also given -- these run in *addition*,
|
||||
# not instead. So the fix for simlighttest (a stray, always-on scratch
|
||||
# test executable that failed to link -lserialport, a pre-existing
|
||||
# CMakeLists.txt bug unrelated to Flatpak) had to be commenting it out
|
||||
# at the source, not overriding the build here. With that gone, the
|
||||
# automatic build+install (using this repo's own new install() rules)
|
||||
# already installs monocoque/gmonocoque correctly; these build-commands
|
||||
# only need to add the extra files CMake doesn't know about.
|
||||
build-commands:
|
||||
- install -Dm644 io.github.spacefreak18.monocoque.desktop /app/share/applications/io.github.spacefreak18.monocoque.desktop
|
||||
- install -Dm644 io.github.spacefreak18.monocoque.metainfo.xml /app/share/metainfo/io.github.spacefreak18.monocoque.metainfo.xml
|
||||
- install -Dm644 tools/monocoque.svg /app/share/icons/hicolor/scalable/apps/io.github.spacefreak18.monocoque.svg
|
||||
Executable
+554
@@ -0,0 +1,554 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Monocoque Universal Installer
|
||||
# Works on: Arch, Debian/Ubuntu, Fedora, and other major distros
|
||||
# Version: 1.0.0
|
||||
|
||||
SCRIPT_VERSION="1.0.0"
|
||||
INSTALL_DIR="${MONOCOQUE_INSTALL_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/monocoque}"
|
||||
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
BIN_DIR="${HOME}/.local/bin"
|
||||
|
||||
# Color output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
print_header() {
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Monocoque Universal Installer v${SCRIPT_VERSION} ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Detect distribution
|
||||
detect_distro() {
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
DISTRO=$ID
|
||||
DISTRO_VERSION=$VERSION_ID
|
||||
DISTRO_LIKE=$ID_LIKE
|
||||
elif [ -f /etc/arch-release ]; then
|
||||
DISTRO="arch"
|
||||
else
|
||||
DISTRO="unknown"
|
||||
fi
|
||||
|
||||
# Normalize Arch-based distributions
|
||||
if [[ "$DISTRO_LIKE" == *"arch"* ]] || [[ "$DISTRO" == "manjaro" ]] || [[ "$DISTRO" == "garuda" ]] || [[ "$DISTRO" == "endeavouros" ]]; then
|
||||
log_info "Detected distribution: $DISTRO (Arch-based)"
|
||||
DISTRO="arch"
|
||||
else
|
||||
log_info "Detected distribution: $DISTRO"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
check_root() {
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
log_error "Please do not run this script as root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check for required commands
|
||||
check_requirements() {
|
||||
local missing_commands=()
|
||||
|
||||
for cmd in git cmake make gcc; do
|
||||
if ! command -v $cmd &> /dev/null; then
|
||||
missing_commands+=($cmd)
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_commands[@]} -ne 0 ]; then
|
||||
log_error "Missing required commands: ${missing_commands[*]}"
|
||||
log_info "Please install them first"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Install dependencies based on distro
|
||||
install_dependencies() {
|
||||
log_info "Installing dependencies for $DISTRO..."
|
||||
|
||||
case $DISTRO in
|
||||
arch|manjaro)
|
||||
local deps="yder libuv argtable libserialport libconfig hidapi lua54 libpulse pkgconfig libxdg-basedir mingw-w64-gcc"
|
||||
|
||||
# Check if yay is available for AUR
|
||||
if command -v yay &> /dev/null; then
|
||||
log_info "Using yay to check for AUR packages..."
|
||||
echo ""
|
||||
echo "You can install monocoque from AUR instead:"
|
||||
echo " yay -S simapi-git simd-git monocoque-git"
|
||||
echo ""
|
||||
read -p "Install from AUR? (recommended) [Y/n]: " use_aur
|
||||
|
||||
if [[ ! $use_aur =~ ^[Nn]$ ]]; then
|
||||
log_info "Installing from AUR..."
|
||||
yay -S --needed simapi-git simd-git monocoque-git
|
||||
|
||||
# Still need simshmbridge
|
||||
log_info "Building simshmbridge (not in AUR)..."
|
||||
build_simshmbridge_only
|
||||
|
||||
# Setup configs and services
|
||||
setup_configs
|
||||
setup_systemd_services
|
||||
# create_launcher_scripts uses SCRIPT_DIR which is set in main()
|
||||
create_launcher_scripts
|
||||
|
||||
log_success "Installation complete (AUR method)!"
|
||||
print_next_steps
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Installing build dependencies with pacman..."
|
||||
sudo pacman -S --needed --noconfirm $deps
|
||||
;;
|
||||
|
||||
ubuntu|debian|linuxmint|pop)
|
||||
local deps="build-essential git cmake libyder-dev libuv1-dev libargtable2-dev libserialport-dev libconfig-dev libhidapi-dev lua5.3 liblua5.3-dev libxdg-basedir-dev mingw-w64"
|
||||
|
||||
log_info "Updating package lists..."
|
||||
sudo apt-get update
|
||||
|
||||
log_info "Installing build dependencies..."
|
||||
sudo apt-get install -y $deps
|
||||
;;
|
||||
|
||||
fedora|rhel|centos)
|
||||
local deps="git cmake gcc gcc-c++ yder-devel libuv-devel argtable-devel libserialport-devel libconfig-devel hidapi-devel lua-devel libxdg-basedir-devel mingw64-gcc"
|
||||
|
||||
log_info "Installing build dependencies..."
|
||||
sudo dnf install -y $deps
|
||||
;;
|
||||
|
||||
opensuse*)
|
||||
local deps="git cmake gcc gcc-c++ libyder-devel libuv-devel argtable-devel libserialport-devel libconfig-devel hidapi-devel lua-devel libxdg-basedir-devel mingw64-gcc"
|
||||
|
||||
log_info "Installing build dependencies..."
|
||||
sudo zypper install -y $deps
|
||||
;;
|
||||
|
||||
*)
|
||||
log_error "Unsupported distribution: $DISTRO"
|
||||
log_info "Please install dependencies manually and try again"
|
||||
log_info "Required: git, cmake, gcc, yder, libuv, argtable, libserialport, libconfig, hidapi, lua, libxdg-basedir, mingw-gcc"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log_success "Dependencies installed"
|
||||
}
|
||||
|
||||
# Clone repositories
|
||||
clone_repositories() {
|
||||
log_info "Creating installation directory: $INSTALL_DIR"
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
# Clone monocoque
|
||||
if [ ! -d "monocoque" ]; then
|
||||
log_info "Cloning monocoque..."
|
||||
git clone https://github.com/Spacefreak18/monocoque.git
|
||||
cd monocoque
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
else
|
||||
log_info "monocoque already cloned, updating..."
|
||||
cd monocoque
|
||||
git pull
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
# Clone simapi
|
||||
if [ ! -d "simapi" ]; then
|
||||
log_info "Cloning simapi..."
|
||||
git clone https://github.com/Spacefreak18/simapi.git
|
||||
else
|
||||
log_info "simapi already cloned, updating..."
|
||||
cd simapi
|
||||
git pull
|
||||
fi
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
# Clone simshmbridge
|
||||
if [ ! -d "simshmbridge" ]; then
|
||||
log_info "Cloning simshmbridge..."
|
||||
git clone https://github.com/spacefreak18/simshmbridge.git
|
||||
cd simshmbridge
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
else
|
||||
log_info "simshmbridge already cloned, updating..."
|
||||
cd simshmbridge
|
||||
git pull
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
log_success "Repositories cloned/updated"
|
||||
}
|
||||
|
||||
# Build simapi
|
||||
build_simapi() {
|
||||
log_info "Building simapi..."
|
||||
cd "$INSTALL_DIR/simapi"
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake -DINSTALL_SYSTEMD_SERVICE=off ..
|
||||
make -j$(nproc)
|
||||
|
||||
log_info "Installing simapi library..."
|
||||
sudo make install
|
||||
|
||||
# Update library cache
|
||||
sudo ldconfig 2>/dev/null || true
|
||||
|
||||
log_success "simapi built and installed"
|
||||
}
|
||||
|
||||
# Build simd
|
||||
build_simd() {
|
||||
log_info "Building simd..."
|
||||
cd "$INSTALL_DIR/simapi/simd"
|
||||
|
||||
rm -rf build
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j$(nproc)
|
||||
|
||||
log_success "simd built"
|
||||
}
|
||||
|
||||
# Build simshmbridge
|
||||
build_simshmbridge() {
|
||||
log_info "Building simshmbridge..."
|
||||
cd "$INSTALL_DIR/simshmbridge"
|
||||
|
||||
make clean || true
|
||||
make -j$(nproc)
|
||||
|
||||
log_success "simshmbridge built"
|
||||
}
|
||||
|
||||
build_simshmbridge_only() {
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
if [ ! -d "simshmbridge" ]; then
|
||||
log_info "Cloning simshmbridge..."
|
||||
git clone https://github.com/spacefreak18/simshmbridge.git
|
||||
cd simshmbridge
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
build_simshmbridge
|
||||
}
|
||||
|
||||
# Build monocoque
|
||||
build_monocoque() {
|
||||
log_info "Building monocoque..."
|
||||
cd "$INSTALL_DIR/monocoque"
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j$(nproc)
|
||||
|
||||
log_success "monocoque built"
|
||||
}
|
||||
|
||||
# Setup configuration files
|
||||
setup_configs() {
|
||||
log_info "Setting up configuration files..."
|
||||
|
||||
# simd config
|
||||
mkdir -p "$CONFIG_DIR/simd"
|
||||
if [ ! -f "$CONFIG_DIR/simd/simd.config" ]; then
|
||||
if [ -f "$INSTALL_DIR/simapi/simd/conf/simd.config" ]; then
|
||||
cp "$INSTALL_DIR/simapi/simd/conf/simd.config" "$CONFIG_DIR/simd/simd.config"
|
||||
log_success "Created simd config"
|
||||
fi
|
||||
else
|
||||
log_info "simd config already exists, skipping"
|
||||
fi
|
||||
|
||||
# monocoque config
|
||||
mkdir -p "$CONFIG_DIR/monocoque"
|
||||
if [ ! -f "$CONFIG_DIR/monocoque/monocoque.config" ]; then
|
||||
cat > "$CONFIG_DIR/monocoque/monocoque.config" << 'EOF'
|
||||
configs = (
|
||||
{
|
||||
sim = "default";
|
||||
car = "default";
|
||||
devices = (
|
||||
// Add your devices here
|
||||
// Example: Serial device (ESP32/Arduino)
|
||||
/*
|
||||
{
|
||||
device = "Serial";
|
||||
type = "Custom";
|
||||
config = "None";
|
||||
baud = 115200;
|
||||
devpath = "/dev/ttyUSB0";
|
||||
},
|
||||
*/
|
||||
|
||||
// Example: Bass shaker
|
||||
/*
|
||||
{
|
||||
device = "Sound";
|
||||
effect = "Engine";
|
||||
devid = "alsa_output.your_device_here";
|
||||
pan = 0;
|
||||
fps = 60;
|
||||
threshold = 0.2;
|
||||
channels = 2;
|
||||
volume = 70;
|
||||
modulation = "frequency";
|
||||
frequency = 17;
|
||||
frequencyMax = 37;
|
||||
},
|
||||
*/
|
||||
);
|
||||
}
|
||||
);
|
||||
EOF
|
||||
log_success "Created monocoque config"
|
||||
else
|
||||
log_info "monocoque config already exists, skipping"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create launcher scripts
|
||||
create_launcher_scripts() {
|
||||
log_info "Creating launcher scripts in $BIN_DIR..."
|
||||
mkdir -p "$BIN_DIR"
|
||||
|
||||
# Find simd binary location
|
||||
local SIMD_BIN=""
|
||||
if [ -f "$INSTALL_DIR/simapi/simd/build/simd" ]; then
|
||||
SIMD_BIN="$INSTALL_DIR/simapi/simd/build/simd"
|
||||
elif command -v simd &> /dev/null; then
|
||||
SIMD_BIN=$(which simd)
|
||||
fi
|
||||
|
||||
# Find monocoque binary location
|
||||
local MONOCOQUE_BIN=""
|
||||
if [ -f "$INSTALL_DIR/monocoque/build/monocoque" ]; then
|
||||
MONOCOQUE_BIN="$INSTALL_DIR/monocoque/build/monocoque"
|
||||
elif command -v monocoque &> /dev/null; then
|
||||
MONOCOQUE_BIN=$(which monocoque)
|
||||
fi
|
||||
|
||||
# start-simd script
|
||||
cat > "$BIN_DIR/start-simd" << EOF
|
||||
#!/bin/bash
|
||||
export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib64
|
||||
exec $SIMD_BIN -vv "\$@"
|
||||
EOF
|
||||
chmod +x "$BIN_DIR/start-simd"
|
||||
|
||||
# start-monocoque script
|
||||
cat > "$BIN_DIR/start-monocoque" << EOF
|
||||
#!/bin/bash
|
||||
exec $MONOCOQUE_BIN play "\$@"
|
||||
EOF
|
||||
chmod +x "$BIN_DIR/start-monocoque"
|
||||
|
||||
# test-monocoque script
|
||||
cat > "$BIN_DIR/test-monocoque" << EOF
|
||||
#!/bin/bash
|
||||
exec $MONOCOQUE_BIN test -vv "\$@"
|
||||
EOF
|
||||
chmod +x "$BIN_DIR/test-monocoque"
|
||||
|
||||
# monocoque-manager TUI
|
||||
log_info "Looking for monocoque-manager to install..."
|
||||
local MANAGER_INSTALLED=false
|
||||
|
||||
# Use the SCRIPT_DIR exported from main()
|
||||
# Try multiple locations
|
||||
local SEARCH_PATHS=(
|
||||
"$SCRIPT_DIR/monocoque-manager"
|
||||
"$SCRIPT_DIR/tools/monocoque-manager"
|
||||
)
|
||||
|
||||
for MANAGER_PATH in "${SEARCH_PATHS[@]}"; do
|
||||
log_info "Checking: $MANAGER_PATH"
|
||||
if [ -f "$MANAGER_PATH" ]; then
|
||||
log_info "Found monocoque-manager at: $MANAGER_PATH"
|
||||
cp "$MANAGER_PATH" "$BIN_DIR/monocoque-manager"
|
||||
chmod +x "$BIN_DIR/monocoque-manager"
|
||||
log_success "✓ Installed monocoque-manager TUI to $BIN_DIR/monocoque-manager"
|
||||
MANAGER_INSTALLED=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$MANAGER_INSTALLED" = false ]; then
|
||||
log_error "monocoque-manager not found!"
|
||||
log_warn "Searched in: ${SEARCH_PATHS[*]}"
|
||||
log_warn "SCRIPT_DIR was: $SCRIPT_DIR"
|
||||
log_info "Please copy it manually: cp $SCRIPT_DIR/monocoque-manager ~/.local/bin/"
|
||||
fi
|
||||
|
||||
# Check if BIN_DIR is in PATH
|
||||
if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
|
||||
log_warn "$BIN_DIR is not in your PATH"
|
||||
log_info "Add this to your shell config:"
|
||||
echo " # For bash/zsh:"
|
||||
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
|
||||
echo ""
|
||||
echo " # For fish:"
|
||||
echo " fish_add_path ~/.local/bin"
|
||||
fi
|
||||
|
||||
log_success "Launcher scripts created"
|
||||
}
|
||||
|
||||
# Setup systemd user services
|
||||
setup_systemd_services() {
|
||||
local SYSTEMD_DIR="$HOME/.config/systemd/user"
|
||||
mkdir -p "$SYSTEMD_DIR"
|
||||
|
||||
log_info "Creating systemd service files..."
|
||||
|
||||
# Find binary locations
|
||||
local SIMD_BIN=""
|
||||
if [ -f "$INSTALL_DIR/simapi/simd/build/simd" ]; then
|
||||
SIMD_BIN="$INSTALL_DIR/simapi/simd/build/simd"
|
||||
elif command -v simd &> /dev/null; then
|
||||
SIMD_BIN=$(which simd)
|
||||
fi
|
||||
|
||||
# simd service
|
||||
cat > "$SYSTEMD_DIR/simd.service" << EOF
|
||||
[Unit]
|
||||
Description=Sim Telemetry Daemon
|
||||
Documentation=https://spacefreak18.github.io/simapi/
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment="LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64"
|
||||
ExecStart=$SIMD_BIN
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF
|
||||
|
||||
log_success "systemd services created"
|
||||
log_info "To enable auto-start on boot:"
|
||||
echo " systemctl --user enable simd.service"
|
||||
echo " systemctl --user start simd.service"
|
||||
}
|
||||
|
||||
# Print next steps
|
||||
print_next_steps() {
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Installation Complete! ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
log_success "Monocoque and all components are installed!"
|
||||
echo ""
|
||||
echo "📁 Installation location: $INSTALL_DIR"
|
||||
echo "⚙️ Configuration: $CONFIG_DIR/simd and $CONFIG_DIR/monocoque"
|
||||
echo "🚀 Launcher scripts: $BIN_DIR"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo ""
|
||||
echo "1️⃣ Configure your games (REQUIRED!)"
|
||||
echo " See: https://github.com/Spacefreak18/monocoque/blob/master/HOW-TO-USE.md"
|
||||
echo ""
|
||||
echo "2️⃣ Start the services:"
|
||||
echo " Terminal 1: start-simd"
|
||||
echo " Terminal 2: start-monocoque"
|
||||
echo ""
|
||||
echo " OR enable auto-start:"
|
||||
echo " systemctl --user enable --now simd.service"
|
||||
echo ""
|
||||
echo "3️⃣ Configure your devices:"
|
||||
echo " Edit: $CONFIG_DIR/monocoque/monocoque.config"
|
||||
echo ""
|
||||
echo "4️⃣ Test your setup:"
|
||||
echo " test-monocoque"
|
||||
echo ""
|
||||
echo "🎮 Supported games:"
|
||||
echo " • Assetto Corsa / ACC"
|
||||
echo " • Automobilista 2"
|
||||
echo " • Project Cars 2"
|
||||
echo " • RFactor 2"
|
||||
echo " • Euro/American Truck Simulator"
|
||||
echo ""
|
||||
echo "📚 Documentation: https://spacefreak18.github.io/simapi/"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main installation flow
|
||||
main() {
|
||||
# Save the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
export SCRIPT_DIR
|
||||
|
||||
print_header
|
||||
|
||||
check_root
|
||||
detect_distro
|
||||
check_requirements
|
||||
|
||||
log_info "Starting installation..."
|
||||
echo ""
|
||||
|
||||
install_dependencies
|
||||
clone_repositories
|
||||
|
||||
log_info "Building components (this may take a few minutes)..."
|
||||
build_simapi
|
||||
build_simd
|
||||
build_monocoque
|
||||
build_simshmbridge
|
||||
|
||||
setup_configs
|
||||
create_launcher_scripts
|
||||
setup_systemd_services
|
||||
|
||||
print_next_steps
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
|
||||
Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice including the dates of first publication and
|
||||
either this permission notice or a reference to
|
||||
http://oss.sgi.com/projects/FreeB/
|
||||
shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Silicon Graphics, Inc.
|
||||
shall not be used in advertising or otherwise to promote the sale, use or
|
||||
other dealings in this Software without prior written authorization from
|
||||
Silicon Graphics, Inc.
|
||||
/
|
||||
Pixel storage modes */
|
||||
typedef struct {
|
||||
GLint pack_alignment;
|
||||
GLint pack_row_length;
|
||||
GLint pack_skip_rows;
|
||||
GLint pack_skip_pixels;
|
||||
GLint pack_lsb_first;
|
||||
GLint pack_swap_bytes;
|
||||
GLint pack_skip_images;
|
||||
GLint pack_image_height;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright © 1994–2026 Lua.org, PUC-Rio.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
||||
"packages": {
|
||||
".": {
|
||||
"release-type": "simple",
|
||||
"package-name": "monocoque",
|
||||
|
||||
"//": "The 79 existing tags are bare semver (0.3.6), not v0.3.6, and there is only one package here. Both flags keep release-please tagging in that same series instead of starting a parallel one.",
|
||||
"include-component-in-tag": false,
|
||||
"include-v-in-tag": false,
|
||||
|
||||
"//bump": "While the major version is 0, a breaking change bumps the minor (0.3.6 -> 0.4.0) rather than declaring 1.0.0. Reaching 1.0.0 stays a deliberate act.",
|
||||
"bump-minor-pre-major": true,
|
||||
|
||||
"draft": false,
|
||||
"changelog-sections": [
|
||||
{ "type": "feat", "section": "Features" },
|
||||
{ "type": "fix", "section": "Bug Fixes" },
|
||||
{ "type": "perf", "section": "Performance Improvements", "hidden": true },
|
||||
{ "type": "revert", "section": "Reverts", "hidden": true },
|
||||
{ "type": "docs", "section": "Documentation", "hidden": true },
|
||||
{ "type": "style", "section": "Styles", "hidden": true },
|
||||
{ "type": "chore", "section": "Miscellaneous Chores", "hidden": true },
|
||||
{ "type": "refactor", "section": "Code Refactoring", "hidden": true },
|
||||
{ "type": "test", "section": "Tests", "hidden": true },
|
||||
{ "type": "build", "section": "Build System", "hidden": true },
|
||||
{ "type": "ci", "section": "Continuous Integration", "hidden": true }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
# Makefile for Arduino based scketches
|
||||
#
|
||||
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
|
||||
# MIT License - see License.txt file
|
||||
#
|
||||
# This Makefile uses the arduino-cli, the Arduino command line interface
|
||||
# and has been designed and tested to run on Linux, not on Windows.
|
||||
# Probably it will run on a Mac, but it has not been tested.
|
||||
#
|
||||
# Please note that:
|
||||
#
|
||||
# 1. each sketch must reside in his own folder with this Makefile
|
||||
#
|
||||
# 2. the main make targets are:
|
||||
# - all compiles and upload
|
||||
# - compile compiles only
|
||||
# - upload upload via serial port, compile if the binary file is
|
||||
# not available
|
||||
# - ota upload Over The Air, automatically find the device
|
||||
# IP address using the IOT_NAME (device hostname)
|
||||
# - clean clean the build directory
|
||||
# - find find OTA updatable devices on the local subnet
|
||||
# - requirements it the file "requirements.txt" exists it will
|
||||
# install the libraries listed in this file
|
||||
#
|
||||
# default is "all"
|
||||
#
|
||||
# 3. it gets the name of the sketch using the wildcard make command;
|
||||
# the name is *.ino; this means that you must have ONLY a file
|
||||
# with .ino extension, otherwise this makefile will break. This
|
||||
# also means that you can use this Makefile, almost unmodified,
|
||||
# for any sketch as long as you keep a single .ino file in each
|
||||
# folder
|
||||
#
|
||||
# 4. you can split your project in multiple files, if you wish,
|
||||
# using a single .ino file and multiple .h files, that you can
|
||||
# include in the .ino file with an '#include "myfile.h"'
|
||||
# directive
|
||||
#
|
||||
# Optionally some environment variables can be set:
|
||||
#
|
||||
# FQBN Fully Qualified Board Name; if not set in the environment
|
||||
# it will be assigned a value in this makefile
|
||||
#
|
||||
# SERIAL_DEV Serial device to upload the sketch; if not set in the
|
||||
# environment it will be assigned:
|
||||
# /dev/ttyUSB0 if it exists, or
|
||||
# /dev/ttyACM0 if it exists, or
|
||||
# unknown
|
||||
#
|
||||
# IOT_NAME Name of the IOT device; if not set in the environment
|
||||
# it will be assigned a value in this makefile. This is
|
||||
# very useful for OTA update, the device will be searched
|
||||
# on the local subnet using this name
|
||||
#
|
||||
# OTA_PORT Port used by OTA update; if not set in the environment
|
||||
# it will be assigned the default value of 8266 in this
|
||||
# makefile
|
||||
#
|
||||
# OTA_PASS Password used for OTA update; if not set in the environment
|
||||
# it will be assigned the default value of an empty string
|
||||
#
|
||||
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
|
||||
# in the environment it will be assigned a default value
|
||||
# in this makefile
|
||||
|
||||
|
||||
MAKE_DIR := $(PWD)
|
||||
#
|
||||
# ----- setup wor Wemos D1 mini -----
|
||||
#FQBN ?= esp8266:esp8266:d1_mini
|
||||
#IOT_NAME ?= esp8266-meteo
|
||||
#OTA_PORT ?= 8266
|
||||
#OTA_PASS ?=
|
||||
# ----- setup for Arduino Uno
|
||||
FQBN ?= arduino:avr:uno
|
||||
# ----- ---------------------
|
||||
V ?= 0
|
||||
VFLAG =
|
||||
|
||||
ifeq "$(V)" "1"
|
||||
VFLAG =-v
|
||||
endif
|
||||
|
||||
ifndef SERIAL_DEV
|
||||
ifneq (,$(wildcard /dev/ttyUSB0))
|
||||
SERIAL_DEV = /dev/ttyUSB0
|
||||
else ifneq (,$(wildcard /dev/ttyACM0))
|
||||
SERIAL_DEV = /dev/ttyACM0
|
||||
else
|
||||
SERIAL_DEV = unknown
|
||||
endif
|
||||
endif
|
||||
|
||||
BUILD_DIR := $(subst :,.,build/$(FQBN))
|
||||
|
||||
SRC := $(wildcard *.ino)
|
||||
HDRS := $(wildcard *.h)
|
||||
BIN := $(BUILD_DIR)/$(SRC).bin
|
||||
ELF := $(BUILD_DIR)/$(SRC).elf
|
||||
|
||||
$(info FQBN is [${FQBN}])
|
||||
$(info IOT_NAME is [${IOT_NAME}])
|
||||
$(info OTA_PORT is [${OTA_PORT}])
|
||||
$(info OTA_PASS is [${OTA_PASS}])
|
||||
$(info V is [${V}])
|
||||
$(info VFLAG is [${VFLAG}])
|
||||
$(info MAKE_DIR is [${MAKE_DIR}])
|
||||
$(info BUILD_DIR is [${BUILD_DIR}])
|
||||
$(info SRC is [${SRC}])
|
||||
$(info HDRS is [${HDRS}])
|
||||
$(info BIN is [${BIN}])
|
||||
$(info SERIAL_DEV is [${SERIAL_DEV}])
|
||||
|
||||
all: $(ELF) upload
|
||||
.PHONY: all
|
||||
|
||||
compile: $(ELF)
|
||||
.PHONY: compile
|
||||
|
||||
$(ELF): $(SRC) $(HDRS)
|
||||
arduino-cli compile -b $(FQBN) $(VFLAG)
|
||||
@if which arduino-manifest.pl; \
|
||||
then echo "---> Generating manifest.txt"; \
|
||||
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
|
||||
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
|
||||
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
|
||||
fi
|
||||
|
||||
upload:
|
||||
@if [ ! -c $(SERIAL_DEV) ] ; \
|
||||
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
|
||||
else echo "---> Uploading sketch\n"; \
|
||||
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
|
||||
fi
|
||||
|
||||
ota:
|
||||
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
|
||||
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
|
||||
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
|
||||
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
|
||||
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
|
||||
echo "PY_PATH: is [$$PY_PATH]" ; \
|
||||
echo "IOT_IP: is [$$IOT_IP]" ; \
|
||||
echo "BINFILE: is [$$BINFILE]" ; \
|
||||
if [ "$$IOT_IP" = "" ] ; \
|
||||
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
|
||||
else echo "---> Uploading Over The Air"; \
|
||||
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
|
||||
fi
|
||||
|
||||
clean:
|
||||
@echo "---> Cleaning the build directory"
|
||||
rm -rf build
|
||||
|
||||
find:
|
||||
avahi-browse _arduino._tcp --resolve --parsable --terminate
|
||||
|
||||
requirements:
|
||||
@if [ -e requirements.txt ]; \
|
||||
then while read -r i ; do echo ; \
|
||||
echo "---> Installing " '"'$$i'"' ; \
|
||||
arduino-cli lib install "$$i" ; \
|
||||
done < requirements.txt ; \
|
||||
else echo "---> MISSING requirements.txt file"; \
|
||||
fi
|
||||
@@ -0,0 +1,30 @@
|
||||
// Called when starting the arduino (setup method in main sketch)
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
// Called when new data is coming from computer
|
||||
void read()
|
||||
{
|
||||
if(Serial.available())
|
||||
{
|
||||
int speed = Serial.readStringUntil(';').toInt();
|
||||
int gear = Serial.readStringUntil(';').toInt();
|
||||
int rpms = Serial.readStringUntil(';').toInt();
|
||||
//Serial.println("speed: " + String(speed) + " " + "gear: " + String(gear) + " " + "rpms: " + String(rpms));
|
||||
}
|
||||
}
|
||||
|
||||
// Called once per arduino loop, timing can't be predicted,
|
||||
// but it's called between each command sent to the arduino
|
||||
void loop() {
|
||||
read();
|
||||
}
|
||||
|
||||
// Called once between each byte read on arduino,
|
||||
// THIS IS A CRITICAL PATH :
|
||||
// AVOID ANY TIME CONSUMING ROUTINES !!!
|
||||
// PREFER READ OR LOOP METHOS AS MUCH AS POSSIBLE
|
||||
// AVOID ANY INTERRUPTS DISABLE (serial data would be lost!!!)
|
||||
void idle() {
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
# Makefile for Arduino based scketches
|
||||
#
|
||||
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
|
||||
# MIT License - see License.txt file
|
||||
#
|
||||
# This Makefile uses the arduino-cli, the Arduino command line interface
|
||||
# and has been designed and tested to run on Linux, not on Windows.
|
||||
# Probably it will run on a Mac, but it has not been tested.
|
||||
#
|
||||
# Please note that:
|
||||
#
|
||||
# 1. each sketch must reside in his own folder with this Makefile
|
||||
#
|
||||
# 2. the main make targets are:
|
||||
# - all compiles and upload
|
||||
# - compile compiles only
|
||||
# - upload upload via serial port, compile if the binary file is
|
||||
# not available
|
||||
# - ota upload Over The Air, automatically find the device
|
||||
# IP address using the IOT_NAME (device hostname)
|
||||
# - clean clean the build directory
|
||||
# - find find OTA updatable devices on the local subnet
|
||||
# - requirements it the file "requirements.txt" exists it will
|
||||
# install the libraries listed in this file
|
||||
#
|
||||
# default is "all"
|
||||
#
|
||||
# 3. it gets the name of the sketch using the wildcard make command;
|
||||
# the name is *.ino; this means that you must have ONLY a file
|
||||
# with .ino extension, otherwise this makefile will break. This
|
||||
# also means that you can use this Makefile, almost unmodified,
|
||||
# for any sketch as long as you keep a single .ino file in each
|
||||
# folder
|
||||
#
|
||||
# 4. you can split your project in multiple files, if you wish,
|
||||
# using a single .ino file and multiple .h files, that you can
|
||||
# include in the .ino file with an '#include "myfile.h"'
|
||||
# directive
|
||||
#
|
||||
# Optionally some environment variables can be set:
|
||||
#
|
||||
# FQBN Fully Qualified Board Name; if not set in the environment
|
||||
# it will be assigned a value in this makefile
|
||||
#
|
||||
# SERIAL_DEV Serial device to upload the sketch; if not set in the
|
||||
# environment it will be assigned:
|
||||
# /dev/ttyUSB0 if it exists, or
|
||||
# /dev/ttyACM0 if it exists, or
|
||||
# unknown
|
||||
#
|
||||
# IOT_NAME Name of the IOT device; if not set in the environment
|
||||
# it will be assigned a value in this makefile. This is
|
||||
# very useful for OTA update, the device will be searched
|
||||
# on the local subnet using this name
|
||||
#
|
||||
# OTA_PORT Port used by OTA update; if not set in the environment
|
||||
# it will be assigned the default value of 8266 in this
|
||||
# makefile
|
||||
#
|
||||
# OTA_PASS Password used for OTA update; if not set in the environment
|
||||
# it will be assigned the default value of an empty string
|
||||
#
|
||||
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
|
||||
# in the environment it will be assigned a default value
|
||||
# in this makefile
|
||||
|
||||
|
||||
MAKE_DIR := $(PWD)
|
||||
#
|
||||
# ----- setup wor Wemos D1 mini -----
|
||||
#FQBN ?= esp8266:esp8266:d1_mini
|
||||
#IOT_NAME ?= esp8266-meteo
|
||||
#OTA_PORT ?= 8266
|
||||
#OTA_PASS ?=
|
||||
# ----- setup for Arduino Uno
|
||||
FQBN ?= arduino:avr:uno
|
||||
# ----- ---------------------
|
||||
V ?= 0
|
||||
VFLAG =
|
||||
|
||||
ifeq "$(V)" "1"
|
||||
VFLAG =-v
|
||||
endif
|
||||
|
||||
ifndef SERIAL_DEV
|
||||
ifneq (,$(wildcard /dev/ttyUSB0))
|
||||
SERIAL_DEV = /dev/ttyUSB0
|
||||
else ifneq (,$(wildcard /dev/ttyACM0))
|
||||
SERIAL_DEV = /dev/ttyACM0
|
||||
else
|
||||
SERIAL_DEV = unknown
|
||||
endif
|
||||
endif
|
||||
|
||||
BUILD_DIR := $(subst :,.,build/$(FQBN))
|
||||
|
||||
SRC := $(wildcard *.ino)
|
||||
HDRS := $(wildcard *.h)
|
||||
BIN := $(BUILD_DIR)/$(SRC).bin
|
||||
ELF := $(BUILD_DIR)/$(SRC).elf
|
||||
|
||||
$(info FQBN is [${FQBN}])
|
||||
$(info IOT_NAME is [${IOT_NAME}])
|
||||
$(info OTA_PORT is [${OTA_PORT}])
|
||||
$(info OTA_PASS is [${OTA_PASS}])
|
||||
$(info V is [${V}])
|
||||
$(info VFLAG is [${VFLAG}])
|
||||
$(info MAKE_DIR is [${MAKE_DIR}])
|
||||
$(info BUILD_DIR is [${BUILD_DIR}])
|
||||
$(info SRC is [${SRC}])
|
||||
$(info HDRS is [${HDRS}])
|
||||
$(info BIN is [${BIN}])
|
||||
$(info SERIAL_DEV is [${SERIAL_DEV}])
|
||||
|
||||
all: $(ELF) upload
|
||||
.PHONY: all
|
||||
|
||||
compile: $(ELF)
|
||||
.PHONY: compile
|
||||
|
||||
$(ELF): $(SRC) $(HDRS)
|
||||
arduino-cli compile -b $(FQBN) $(VFLAG)
|
||||
@if which arduino-manifest.pl; \
|
||||
then echo "---> Generating manifest.txt"; \
|
||||
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
|
||||
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
|
||||
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
|
||||
fi
|
||||
|
||||
upload:
|
||||
@if [ ! -c $(SERIAL_DEV) ] ; \
|
||||
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
|
||||
else echo "---> Uploading sketch\n"; \
|
||||
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
|
||||
fi
|
||||
|
||||
ota:
|
||||
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
|
||||
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
|
||||
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
|
||||
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
|
||||
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
|
||||
echo "PY_PATH: is [$$PY_PATH]" ; \
|
||||
echo "IOT_IP: is [$$IOT_IP]" ; \
|
||||
echo "BINFILE: is [$$BINFILE]" ; \
|
||||
if [ "$$IOT_IP" = "" ] ; \
|
||||
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
|
||||
else echo "---> Uploading Over The Air"; \
|
||||
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
|
||||
fi
|
||||
|
||||
clean:
|
||||
@echo "---> Cleaning the build directory"
|
||||
rm -rf build
|
||||
|
||||
find:
|
||||
avahi-browse _arduino._tcp --resolve --parsable --terminate
|
||||
|
||||
requirements:
|
||||
@if [ -e requirements.txt ]; \
|
||||
then while read -r i ; do echo ; \
|
||||
echo "---> Installing " '"'$$i'"' ; \
|
||||
arduino-cli lib install "$$i" ; \
|
||||
done < requirements.txt ; \
|
||||
else echo "---> MISSING requirements.txt file"; \
|
||||
fi
|
||||
@@ -1,95 +0,0 @@
|
||||
#include <FastLED.h>
|
||||
#include "../../monocoque/simulatorapi/simdata.h"
|
||||
|
||||
#define BYTE_SIZE sizeof(SimData)
|
||||
|
||||
#define LED_PIN 7
|
||||
#define NUM_LEDS 6
|
||||
#define BRIGHTNESS 40
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
SimData sd;
|
||||
int maxrpm = 0;
|
||||
int rpm = 0;
|
||||
int numlights = NUM_LEDS;
|
||||
int pin = LED_PIN;
|
||||
int lights[6];
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
for (int i = 0; i < numlights; i++)
|
||||
{
|
||||
leds[i] = CRGB ( 0, 0, 0);
|
||||
lights[i] = 0;
|
||||
}
|
||||
FastLED.clear();
|
||||
|
||||
sd.rpms = 0;
|
||||
sd.maxrpm = 6500;
|
||||
sd.altitude = 10;
|
||||
sd.pulses = 40000;
|
||||
sd.velocity = 10;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
int l = 0;
|
||||
char buff[BYTE_SIZE];
|
||||
|
||||
if (Serial.available() >= BYTE_SIZE)
|
||||
{
|
||||
Serial.readBytes(buff, BYTE_SIZE);
|
||||
memcpy(&sd, &buff, BYTE_SIZE);
|
||||
rpm = sd.rpms;
|
||||
maxrpm = sd.maxrpm;
|
||||
|
||||
}
|
||||
|
||||
|
||||
while (l < numlights)
|
||||
{
|
||||
lights[l] = 0;
|
||||
l++;
|
||||
}
|
||||
l = -1;
|
||||
int rpmlights = 0;
|
||||
while (rpm > rpmlights)
|
||||
{
|
||||
if (l>=0)
|
||||
{
|
||||
lights[l] = 1;
|
||||
}
|
||||
l++;
|
||||
rpmlights = rpmlights + (((maxrpm-250)/numlights));
|
||||
}
|
||||
|
||||
l = 0;
|
||||
FastLED.clear();
|
||||
while (l < numlights)
|
||||
{
|
||||
|
||||
if (l >= numlights / 2)
|
||||
{
|
||||
leds[l] = CRGB ( 0, 0, 255);
|
||||
}
|
||||
if (l < numlights / 2)
|
||||
{
|
||||
leds[l] = CRGB ( 0, 255, 0);
|
||||
}
|
||||
if (l == numlights - 1)
|
||||
{
|
||||
leds[l] = CRGB ( 255, 0, 0);
|
||||
}
|
||||
if (lights[l] <= 0)
|
||||
{
|
||||
leds[l] = CRGB ( 0, 0, 0);
|
||||
}
|
||||
FastLED.show();
|
||||
l++;
|
||||
}
|
||||
}
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#ifndef _SHIFTLIGHTSDATA_H
|
||||
#define _SHIFTLIGHTSDATA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t litleds;
|
||||
//unsigned char color_1_red;
|
||||
//unsigned char color_1_green;
|
||||
//unsigned char color_1_blue;
|
||||
//unsigned char color_2_red;
|
||||
//unsigned char color_2_green;
|
||||
//unsigned char color_2_blue;
|
||||
//unsigned char color_3_red;
|
||||
//unsigned char color_3_green;
|
||||
//unsigned char color_3_blue;
|
||||
//unsigned char space_1;
|
||||
//unsigned char space_2;
|
||||
//unsigned char space_3;
|
||||
//uint32_t maxrpm;
|
||||
//uint32_t rpm;
|
||||
}
|
||||
ShiftLightsData;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
#include <FastLED.h>
|
||||
#include "shiftlights.h"
|
||||
|
||||
#define SIMDATA_SIZE sizeof(ShiftLightsData)
|
||||
|
||||
#define LED_PIN 7
|
||||
#define NUM_LEDS 6
|
||||
#define BRIGHTNESS 40
|
||||
#define STARTLED 1
|
||||
|
||||
#define COLOR1R 0
|
||||
#define COLOR1G 255
|
||||
#define COLOR1B 0
|
||||
#define COLOR2R 255
|
||||
#define COLOR2G 255
|
||||
#define COLOR2B 0
|
||||
#define COLOR3R 255
|
||||
#define COLOR3G 0
|
||||
#define COLOR3B 0
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
ShiftLightsData sd;
|
||||
int maxrpm = 0;
|
||||
int rpm = 0;
|
||||
int numlights = NUM_LEDS;
|
||||
int pin = LED_PIN;
|
||||
int lights[NUM_LEDS];
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
for (int i = 0; i < numlights; i++)
|
||||
{
|
||||
leds[i] = CRGB ( 0, 0, 0);
|
||||
lights[i] = 0;
|
||||
}
|
||||
FastLED.clear();
|
||||
|
||||
sd.litleds = 0;
|
||||
//sd.rpm = 0;
|
||||
//sd.maxrpm = 6500;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
int l = 0;
|
||||
int lit = sd.litleds;
|
||||
char buff[SIMDATA_SIZE];
|
||||
|
||||
if (Serial.available() >= SIMDATA_SIZE)
|
||||
{
|
||||
Serial.readBytes(buff, SIMDATA_SIZE);
|
||||
memcpy(&sd, &buff, SIMDATA_SIZE);
|
||||
lit = sd.litleds;
|
||||
}
|
||||
|
||||
l = 0;
|
||||
FastLED.clear();
|
||||
while (l < lit)
|
||||
{
|
||||
if (l >= numlights / 2)
|
||||
{
|
||||
leds[l+STARTLED-1] = CRGB ( COLOR2R, COLOR2G, COLOR2B);
|
||||
}
|
||||
if (l < numlights / 2)
|
||||
{
|
||||
leds[l+STARTLED-1] = CRGB ( COLOR1R, COLOR1G, COLOR1B);
|
||||
}
|
||||
if (l == numlights - 1)
|
||||
{
|
||||
leds[l+STARTLED-1] = CRGB ( COLOR3R, COLOR3G, COLOR3B);
|
||||
}
|
||||
//if (lights[l] <= 0)
|
||||
//{
|
||||
// leds[l] = CRGB ( 0, 0, 0);
|
||||
//}
|
||||
l++;
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../monocoque/simulatorapi/simapi/simapi/simdata.h
|
||||
@@ -0,0 +1,166 @@
|
||||
# Makefile for Arduino based scketches
|
||||
#
|
||||
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
|
||||
# MIT License - see License.txt file
|
||||
#
|
||||
# This Makefile uses the arduino-cli, the Arduino command line interface
|
||||
# and has been designed and tested to run on Linux, not on Windows.
|
||||
# Probably it will run on a Mac, but it has not been tested.
|
||||
#
|
||||
# Please note that:
|
||||
#
|
||||
# 1. each sketch must reside in his own folder with this Makefile
|
||||
#
|
||||
# 2. the main make targets are:
|
||||
# - all compiles and upload
|
||||
# - compile compiles only
|
||||
# - upload upload via serial port, compile if the binary file is
|
||||
# not available
|
||||
# - ota upload Over The Air, automatically find the device
|
||||
# IP address using the IOT_NAME (device hostname)
|
||||
# - clean clean the build directory
|
||||
# - find find OTA updatable devices on the local subnet
|
||||
# - requirements it the file "requirements.txt" exists it will
|
||||
# install the libraries listed in this file
|
||||
#
|
||||
# default is "all"
|
||||
#
|
||||
# 3. it gets the name of the sketch using the wildcard make command;
|
||||
# the name is *.ino; this means that you must have ONLY a file
|
||||
# with .ino extension, otherwise this makefile will break. This
|
||||
# also means that you can use this Makefile, almost unmodified,
|
||||
# for any sketch as long as you keep a single .ino file in each
|
||||
# folder
|
||||
#
|
||||
# 4. you can split your project in multiple files, if you wish,
|
||||
# using a single .ino file and multiple .h files, that you can
|
||||
# include in the .ino file with an '#include "myfile.h"'
|
||||
# directive
|
||||
#
|
||||
# Optionally some environment variables can be set:
|
||||
#
|
||||
# FQBN Fully Qualified Board Name; if not set in the environment
|
||||
# it will be assigned a value in this makefile
|
||||
#
|
||||
# SERIAL_DEV Serial device to upload the sketch; if not set in the
|
||||
# environment it will be assigned:
|
||||
# /dev/ttyUSB0 if it exists, or
|
||||
# /dev/ttyACM0 if it exists, or
|
||||
# unknown
|
||||
#
|
||||
# IOT_NAME Name of the IOT device; if not set in the environment
|
||||
# it will be assigned a value in this makefile. This is
|
||||
# very useful for OTA update, the device will be searched
|
||||
# on the local subnet using this name
|
||||
#
|
||||
# OTA_PORT Port used by OTA update; if not set in the environment
|
||||
# it will be assigned the default value of 8266 in this
|
||||
# makefile
|
||||
#
|
||||
# OTA_PASS Password used for OTA update; if not set in the environment
|
||||
# it will be assigned the default value of an empty string
|
||||
#
|
||||
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
|
||||
# in the environment it will be assigned a default value
|
||||
# in this makefile
|
||||
|
||||
|
||||
MAKE_DIR := $(PWD)
|
||||
#
|
||||
# ----- setup wor Wemos D1 mini -----
|
||||
#FQBN ?= esp8266:esp8266:d1_mini
|
||||
#IOT_NAME ?= esp8266-meteo
|
||||
#OTA_PORT ?= 8266
|
||||
#OTA_PASS ?=
|
||||
# ----- setup for Arduino Uno
|
||||
FQBN ?= arduino:avr:uno
|
||||
# ----- ---------------------
|
||||
V ?= 0
|
||||
VFLAG =
|
||||
|
||||
ifeq "$(V)" "1"
|
||||
VFLAG =-v
|
||||
endif
|
||||
|
||||
ifndef SERIAL_DEV
|
||||
ifneq (,$(wildcard /dev/ttyUSB0))
|
||||
SERIAL_DEV = /dev/ttyUSB0
|
||||
else ifneq (,$(wildcard /dev/ttyACM0))
|
||||
SERIAL_DEV = /dev/ttyACM0
|
||||
else
|
||||
SERIAL_DEV = unknown
|
||||
endif
|
||||
endif
|
||||
|
||||
BUILD_DIR := $(subst :,.,build/$(FQBN))
|
||||
|
||||
SRC := $(wildcard *.ino)
|
||||
HDRS := $(wildcard *.h)
|
||||
BIN := $(BUILD_DIR)/$(SRC).bin
|
||||
ELF := $(BUILD_DIR)/$(SRC).elf
|
||||
|
||||
$(info FQBN is [${FQBN}])
|
||||
$(info IOT_NAME is [${IOT_NAME}])
|
||||
$(info OTA_PORT is [${OTA_PORT}])
|
||||
$(info OTA_PASS is [${OTA_PASS}])
|
||||
$(info V is [${V}])
|
||||
$(info VFLAG is [${VFLAG}])
|
||||
$(info MAKE_DIR is [${MAKE_DIR}])
|
||||
$(info BUILD_DIR is [${BUILD_DIR}])
|
||||
$(info SRC is [${SRC}])
|
||||
$(info HDRS is [${HDRS}])
|
||||
$(info BIN is [${BIN}])
|
||||
$(info SERIAL_DEV is [${SERIAL_DEV}])
|
||||
|
||||
all: $(ELF) upload
|
||||
.PHONY: all
|
||||
|
||||
compile: $(ELF)
|
||||
.PHONY: compile
|
||||
|
||||
$(ELF): $(SRC) $(HDRS)
|
||||
arduino-cli compile -b $(FQBN) $(VFLAG)
|
||||
@if which arduino-manifest.pl; \
|
||||
then echo "---> Generating manifest.txt"; \
|
||||
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
|
||||
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
|
||||
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
|
||||
fi
|
||||
|
||||
upload:
|
||||
@if [ ! -c $(SERIAL_DEV) ] ; \
|
||||
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
|
||||
else echo "---> Uploading sketch\n"; \
|
||||
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
|
||||
fi
|
||||
|
||||
ota:
|
||||
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
|
||||
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
|
||||
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
|
||||
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
|
||||
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
|
||||
echo "PY_PATH: is [$$PY_PATH]" ; \
|
||||
echo "IOT_IP: is [$$IOT_IP]" ; \
|
||||
echo "BINFILE: is [$$BINFILE]" ; \
|
||||
if [ "$$IOT_IP" = "" ] ; \
|
||||
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
|
||||
else echo "---> Uploading Over The Air"; \
|
||||
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
|
||||
fi
|
||||
|
||||
clean:
|
||||
@echo "---> Cleaning the build directory"
|
||||
rm -rf build
|
||||
|
||||
find:
|
||||
avahi-browse _arduino._tcp --resolve --parsable --terminate
|
||||
|
||||
requirements:
|
||||
@if [ -e requirements.txt ]; \
|
||||
then while read -r i ; do echo ; \
|
||||
echo "---> Installing " '"'$$i'"' ; \
|
||||
arduino-cli lib install "$$i" ; \
|
||||
done < requirements.txt ; \
|
||||
else echo "---> MISSING requirements.txt file"; \
|
||||
fi
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#ifndef _SIMHAPTICDATA_H
|
||||
#define _SIMHAPTICDATA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t motor1;
|
||||
uint8_t effect1;
|
||||
uint8_t motor2;
|
||||
uint8_t effect2;
|
||||
uint8_t motor3;
|
||||
uint8_t effect3;
|
||||
uint8_t motor4;
|
||||
uint8_t effect4;
|
||||
}
|
||||
SimHapticData;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <Adafruit_MotorShield.h>
|
||||
#include "simhaptic.h"
|
||||
|
||||
#define BYTE_SIZE sizeof(SimHapticData)
|
||||
#define POWER .6
|
||||
|
||||
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
|
||||
|
||||
Adafruit_DCMotor *myMotor1 = AFMS.getMotor(1);
|
||||
//Adafruit_DCMotor *myMotor2 = AFMS.getMotor(2);
|
||||
Adafruit_DCMotor *myMotor3 = AFMS.getMotor(3);
|
||||
//Adafruit_DCMotor *myMotor4 = AFMS.getMotor(4);
|
||||
|
||||
SimHapticData sd;
|
||||
int motor1 = 0;
|
||||
int motor2 = 0;
|
||||
int motor3 = 0;
|
||||
int motor4 = 0;
|
||||
int effect1 = 0;
|
||||
int effect2 = 0;
|
||||
int effect3 = 0;
|
||||
int effect4 = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
if (!AFMS.begin()) {
|
||||
Serial.println("Could not find Motor Shield. Check wiring.");
|
||||
while (1);
|
||||
}
|
||||
sd.motor1 = 0;
|
||||
sd.motor2 = 0;
|
||||
sd.motor3 = 0;
|
||||
sd.motor4 = 0;
|
||||
|
||||
myMotor1->setSpeed(0);
|
||||
myMotor1->run(FORWARD);
|
||||
|
||||
//myMotor2->setSpeed(0);
|
||||
//myMotor2->run(FORWARD);
|
||||
|
||||
myMotor3->setSpeed(0);
|
||||
myMotor3->run(FORWARD);
|
||||
|
||||
//myMotor4->setSpeed(0);
|
||||
//myMotor4->run(FORWARD);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
char buff[BYTE_SIZE];
|
||||
|
||||
if (Serial.available() >= BYTE_SIZE)
|
||||
{
|
||||
Serial.readBytes(buff, BYTE_SIZE);
|
||||
memcpy(&sd, &buff, BYTE_SIZE);
|
||||
motor1 = sd.motor1;
|
||||
motor2 = sd.motor2;
|
||||
motor3 = sd.motor3;
|
||||
motor4 = sd.motor4;
|
||||
effect1 = sd.effect1;
|
||||
effect2 = sd.effect2;
|
||||
effect3 = sd.effect3;
|
||||
effect4 = sd.effect4;
|
||||
}
|
||||
|
||||
if (motor1 >= 1)
|
||||
{
|
||||
myMotor1->setSpeed(effect1);
|
||||
}
|
||||
//if (motor2 >= 1)
|
||||
//{
|
||||
// myMotor2->setSpeed(effect2);
|
||||
//}
|
||||
if (motor3 >= 1)
|
||||
{
|
||||
myMotor3->setSpeed(effect3);
|
||||
}
|
||||
//if (motor4 >= 1)
|
||||
//{
|
||||
// myMotor4->setSpeed(effect4);
|
||||
//}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
# Makefile for Arduino based scketches
|
||||
#
|
||||
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
|
||||
# MIT License - see License.txt file
|
||||
#
|
||||
# This Makefile uses the arduino-cli, the Arduino command line interface
|
||||
# and has been designed and tested to run on Linux, not on Windows.
|
||||
# Probably it will run on a Mac, but it has not been tested.
|
||||
#
|
||||
# Please note that:
|
||||
#
|
||||
# 1. each sketch must reside in his own folder with this Makefile
|
||||
#
|
||||
# 2. the main make targets are:
|
||||
# - all compiles and upload
|
||||
# - compile compiles only
|
||||
# - upload upload via serial port, compile if the binary file is
|
||||
# not available
|
||||
# - ota upload Over The Air, automatically find the device
|
||||
# IP address using the IOT_NAME (device hostname)
|
||||
# - clean clean the build directory
|
||||
# - find find OTA updatable devices on the local subnet
|
||||
# - requirements it the file "requirements.txt" exists it will
|
||||
# install the libraries listed in this file
|
||||
#
|
||||
# default is "all"
|
||||
#
|
||||
# 3. it gets the name of the sketch using the wildcard make command;
|
||||
# the name is *.ino; this means that you must have ONLY a file
|
||||
# with .ino extension, otherwise this makefile will break. This
|
||||
# also means that you can use this Makefile, almost unmodified,
|
||||
# for any sketch as long as you keep a single .ino file in each
|
||||
# folder
|
||||
#
|
||||
# 4. you can split your project in multiple files, if you wish,
|
||||
# using a single .ino file and multiple .h files, that you can
|
||||
# include in the .ino file with an '#include "myfile.h"'
|
||||
# directive
|
||||
#
|
||||
# Optionally some environment variables can be set:
|
||||
#
|
||||
# FQBN Fully Qualified Board Name; if not set in the environment
|
||||
# it will be assigned a value in this makefile
|
||||
#
|
||||
# SERIAL_DEV Serial device to upload the sketch; if not set in the
|
||||
# environment it will be assigned:
|
||||
# /dev/ttyUSB0 if it exists, or
|
||||
# /dev/ttyACM0 if it exists, or
|
||||
# unknown
|
||||
#
|
||||
# IOT_NAME Name of the IOT device; if not set in the environment
|
||||
# it will be assigned a value in this makefile. This is
|
||||
# very useful for OTA update, the device will be searched
|
||||
# on the local subnet using this name
|
||||
#
|
||||
# OTA_PORT Port used by OTA update; if not set in the environment
|
||||
# it will be assigned the default value of 8266 in this
|
||||
# makefile
|
||||
#
|
||||
# OTA_PASS Password used for OTA update; if not set in the environment
|
||||
# it will be assigned the default value of an empty string
|
||||
#
|
||||
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
|
||||
# in the environment it will be assigned a default value
|
||||
# in this makefile
|
||||
|
||||
|
||||
MAKE_DIR := $(PWD)
|
||||
#
|
||||
# ----- setup wor Wemos D1 mini -----
|
||||
#FQBN ?= esp8266:esp8266:d1_mini
|
||||
#IOT_NAME ?= esp8266-meteo
|
||||
#OTA_PORT ?= 8266
|
||||
#OTA_PASS ?=
|
||||
# ----- setup for Arduino Uno
|
||||
FQBN ?= arduino:avr:uno
|
||||
# ----- ---------------------
|
||||
V ?= 0
|
||||
VFLAG =
|
||||
|
||||
ifeq "$(V)" "1"
|
||||
VFLAG =-v
|
||||
endif
|
||||
|
||||
ifndef SERIAL_DEV
|
||||
ifneq (,$(wildcard /dev/ttyUSB0))
|
||||
SERIAL_DEV = /dev/ttyUSB0
|
||||
else ifneq (,$(wildcard /dev/ttyACM0))
|
||||
SERIAL_DEV = /dev/ttyACM0
|
||||
else
|
||||
SERIAL_DEV = unknown
|
||||
endif
|
||||
endif
|
||||
|
||||
BUILD_DIR := $(subst :,.,build/$(FQBN))
|
||||
|
||||
SRC := $(wildcard *.ino)
|
||||
HDRS := $(wildcard *.h)
|
||||
BIN := $(BUILD_DIR)/$(SRC).bin
|
||||
ELF := $(BUILD_DIR)/$(SRC).elf
|
||||
|
||||
$(info FQBN is [${FQBN}])
|
||||
$(info IOT_NAME is [${IOT_NAME}])
|
||||
$(info OTA_PORT is [${OTA_PORT}])
|
||||
$(info OTA_PASS is [${OTA_PASS}])
|
||||
$(info V is [${V}])
|
||||
$(info VFLAG is [${VFLAG}])
|
||||
$(info MAKE_DIR is [${MAKE_DIR}])
|
||||
$(info BUILD_DIR is [${BUILD_DIR}])
|
||||
$(info SRC is [${SRC}])
|
||||
$(info HDRS is [${HDRS}])
|
||||
$(info BIN is [${BIN}])
|
||||
$(info SERIAL_DEV is [${SERIAL_DEV}])
|
||||
|
||||
all: $(ELF) upload
|
||||
.PHONY: all
|
||||
|
||||
compile: $(ELF)
|
||||
.PHONY: compile
|
||||
|
||||
$(ELF): $(SRC) $(HDRS)
|
||||
arduino-cli compile -b $(FQBN) $(VFLAG)
|
||||
@if which arduino-manifest.pl; \
|
||||
then echo "---> Generating manifest.txt"; \
|
||||
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
|
||||
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
|
||||
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
|
||||
fi
|
||||
|
||||
upload:
|
||||
@if [ ! -c $(SERIAL_DEV) ] ; \
|
||||
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
|
||||
else echo "---> Uploading sketch\n"; \
|
||||
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
|
||||
fi
|
||||
|
||||
ota:
|
||||
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
|
||||
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
|
||||
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
|
||||
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
|
||||
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
|
||||
echo "PY_PATH: is [$$PY_PATH]" ; \
|
||||
echo "IOT_IP: is [$$IOT_IP]" ; \
|
||||
echo "BINFILE: is [$$BINFILE]" ; \
|
||||
if [ "$$IOT_IP" = "" ] ; \
|
||||
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
|
||||
else echo "---> Uploading Over The Air"; \
|
||||
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
|
||||
fi
|
||||
|
||||
clean:
|
||||
@echo "---> Cleaning the build directory"
|
||||
rm -rf build
|
||||
|
||||
find:
|
||||
avahi-browse _arduino._tcp --resolve --parsable --terminate
|
||||
|
||||
requirements:
|
||||
@if [ -e requirements.txt ]; \
|
||||
then while read -r i ; do echo ; \
|
||||
echo "---> Installing " '"'$$i'"' ; \
|
||||
arduino-cli lib install "$$i" ; \
|
||||
done < requirements.txt ; \
|
||||
else echo "---> MISSING requirements.txt file"; \
|
||||
fi
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../monocoque/simulatorapi/simapi/simapi/simdata.h
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#ifndef _SIMWINDDATA_H
|
||||
#define _SIMWINDDATA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t velocity;
|
||||
uint8_t fanpower;
|
||||
}
|
||||
SimWindData;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,28 +1,24 @@
|
||||
#include <Adafruit_MotorShield.h>
|
||||
#include "../../monocoque/simulatorapi/simdata.h"
|
||||
#include "simwind.h"
|
||||
|
||||
#define BYTE_SIZE sizeof(SimData)
|
||||
#define BYTE_SIZE sizeof(SimWindData)
|
||||
#define KPHTOMPH .621317
|
||||
|
||||
#define FANPOWER .6
|
||||
|
||||
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
|
||||
|
||||
Adafruit_DCMotor *myMotor1 = AFMS.getMotor(1);
|
||||
Adafruit_DCMotor *myMotor2 = AFMS.getMotor(3);
|
||||
|
||||
SimData sd;
|
||||
SimWindData sd;
|
||||
int velocity = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.begin(115200);
|
||||
if (!AFMS.begin()) {
|
||||
Serial.println("Could not find Motor Shield. Check wiring.");
|
||||
while (1);
|
||||
}
|
||||
sd.rpms = 0;
|
||||
sd.maxrpm = 6500;
|
||||
sd.altitude = 10;
|
||||
sd.pulses = 40000;
|
||||
sd.velocity = 10;
|
||||
|
||||
myMotor1->setSpeed(0);
|
||||
@@ -41,11 +37,7 @@ void loop() {
|
||||
memcpy(&sd, &buff, BYTE_SIZE);
|
||||
velocity = sd.velocity;
|
||||
}
|
||||
int v = ceil(velocity * KPHTOMPH);
|
||||
if (v >= 255)
|
||||
{
|
||||
v = 255;
|
||||
}
|
||||
myMotor1->setSpeed(v);
|
||||
myMotor2->setSpeed(v);
|
||||
|
||||
myMotor1->setSpeed(velocity*FANPOWER);
|
||||
myMotor2->setSpeed(velocity*FANPOWER);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
# Makefile for Arduino based scketches
|
||||
#
|
||||
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
|
||||
# MIT License - see License.txt file
|
||||
#
|
||||
# This Makefile uses the arduino-cli, the Arduino command line interface
|
||||
# and has been designed and tested to run on Linux, not on Windows.
|
||||
# Probably it will run on a Mac, but it has not been tested.
|
||||
#
|
||||
# Please note that:
|
||||
#
|
||||
# 1. each sketch must reside in his own folder with this Makefile
|
||||
#
|
||||
# 2. the main make targets are:
|
||||
# - all compiles and upload
|
||||
# - compile compiles only
|
||||
# - upload upload via serial port, compile if the binary file is
|
||||
# not available
|
||||
# - ota upload Over The Air, automatically find the device
|
||||
# IP address using the IOT_NAME (device hostname)
|
||||
# - clean clean the build directory
|
||||
# - find find OTA updatable devices on the local subnet
|
||||
# - requirements it the file "requirements.txt" exists it will
|
||||
# install the libraries listed in this file
|
||||
#
|
||||
# default is "all"
|
||||
#
|
||||
# 3. it gets the name of the sketch using the wildcard make command;
|
||||
# the name is *.ino; this means that you must have ONLY a file
|
||||
# with .ino extension, otherwise this makefile will break. This
|
||||
# also means that you can use this Makefile, almost unmodified,
|
||||
# for any sketch as long as you keep a single .ino file in each
|
||||
# folder
|
||||
#
|
||||
# 4. you can split your project in multiple files, if you wish,
|
||||
# using a single .ino file and multiple .h files, that you can
|
||||
# include in the .ino file with an '#include "myfile.h"'
|
||||
# directive
|
||||
#
|
||||
# Optionally some environment variables can be set:
|
||||
#
|
||||
# FQBN Fully Qualified Board Name; if not set in the environment
|
||||
# it will be assigned a value in this makefile
|
||||
#
|
||||
# SERIAL_DEV Serial device to upload the sketch; if not set in the
|
||||
# environment it will be assigned:
|
||||
# /dev/ttyUSB0 if it exists, or
|
||||
# /dev/ttyACM0 if it exists, or
|
||||
# unknown
|
||||
#
|
||||
# IOT_NAME Name of the IOT device; if not set in the environment
|
||||
# it will be assigned a value in this makefile. This is
|
||||
# very useful for OTA update, the device will be searched
|
||||
# on the local subnet using this name
|
||||
#
|
||||
# OTA_PORT Port used by OTA update; if not set in the environment
|
||||
# it will be assigned the default value of 8266 in this
|
||||
# makefile
|
||||
#
|
||||
# OTA_PASS Password used for OTA update; if not set in the environment
|
||||
# it will be assigned the default value of an empty string
|
||||
#
|
||||
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
|
||||
# in the environment it will be assigned a default value
|
||||
# in this makefile
|
||||
|
||||
|
||||
MAKE_DIR := $(PWD)
|
||||
#
|
||||
# ----- setup wor Wemos D1 mini -----
|
||||
#FQBN ?= esp8266:esp8266:d1_mini
|
||||
#IOT_NAME ?= esp8266-meteo
|
||||
#OTA_PORT ?= 8266
|
||||
#OTA_PASS ?=
|
||||
# ----- setup for Arduino Uno
|
||||
FQBN ?= arduino:avr:uno
|
||||
# ----- ---------------------
|
||||
V ?= 0
|
||||
VFLAG =
|
||||
|
||||
ifeq "$(V)" "1"
|
||||
VFLAG =-v
|
||||
endif
|
||||
|
||||
ifndef SERIAL_DEV
|
||||
ifneq (,$(wildcard /dev/ttyUSB0))
|
||||
SERIAL_DEV = /dev/ttyUSB0
|
||||
else ifneq (,$(wildcard /dev/ttyACM0))
|
||||
SERIAL_DEV = /dev/ttyACM0
|
||||
else
|
||||
SERIAL_DEV = unknown
|
||||
endif
|
||||
endif
|
||||
|
||||
BUILD_DIR := $(subst :,.,build/$(FQBN))
|
||||
|
||||
SRC := $(wildcard *.ino)
|
||||
HDRS := $(wildcard *.h)
|
||||
BIN := $(BUILD_DIR)/$(SRC).bin
|
||||
ELF := $(BUILD_DIR)/$(SRC).elf
|
||||
|
||||
$(info FQBN is [${FQBN}])
|
||||
$(info IOT_NAME is [${IOT_NAME}])
|
||||
$(info OTA_PORT is [${OTA_PORT}])
|
||||
$(info OTA_PASS is [${OTA_PASS}])
|
||||
$(info V is [${V}])
|
||||
$(info VFLAG is [${VFLAG}])
|
||||
$(info MAKE_DIR is [${MAKE_DIR}])
|
||||
$(info BUILD_DIR is [${BUILD_DIR}])
|
||||
$(info SRC is [${SRC}])
|
||||
$(info HDRS is [${HDRS}])
|
||||
$(info BIN is [${BIN}])
|
||||
$(info SERIAL_DEV is [${SERIAL_DEV}])
|
||||
|
||||
all: $(ELF) upload
|
||||
.PHONY: all
|
||||
|
||||
compile: $(ELF)
|
||||
.PHONY: compile
|
||||
|
||||
$(ELF): $(SRC) $(HDRS)
|
||||
arduino-cli compile -b $(FQBN) $(VFLAG)
|
||||
@if which arduino-manifest.pl; \
|
||||
then echo "---> Generating manifest.txt"; \
|
||||
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
|
||||
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
|
||||
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
|
||||
fi
|
||||
|
||||
upload:
|
||||
@if [ ! -c $(SERIAL_DEV) ] ; \
|
||||
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
|
||||
else echo "---> Uploading sketch\n"; \
|
||||
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
|
||||
fi
|
||||
|
||||
ota:
|
||||
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
|
||||
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
|
||||
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
|
||||
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
|
||||
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
|
||||
echo "PY_PATH: is [$$PY_PATH]" ; \
|
||||
echo "IOT_IP: is [$$IOT_IP]" ; \
|
||||
echo "BINFILE: is [$$BINFILE]" ; \
|
||||
if [ "$$IOT_IP" = "" ] ; \
|
||||
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
|
||||
else echo "---> Uploading Over The Air"; \
|
||||
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
|
||||
fi
|
||||
|
||||
clean:
|
||||
@echo "---> Cleaning the build directory"
|
||||
rm -rf build
|
||||
|
||||
find:
|
||||
avahi-browse _arduino._tcp --resolve --parsable --terminate
|
||||
|
||||
requirements:
|
||||
@if [ -e requirements.txt ]; \
|
||||
then while read -r i ; do echo ; \
|
||||
echo "---> Installing " '"'$$i'"' ; \
|
||||
arduino-cli lib install "$$i" ; \
|
||||
done < requirements.txt ; \
|
||||
else echo "---> MISSING requirements.txt file"; \
|
||||
fi
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#ifndef _SIMWINDDATA_H
|
||||
#define _SIMWINDDATA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t velocity;
|
||||
uint8_t fanpower;
|
||||
}
|
||||
SimWindData;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "simwind.h"
|
||||
|
||||
// ============================================================
|
||||
// Configuration
|
||||
// ============================================================
|
||||
#define BAUD_RATE 115200
|
||||
#define PWM_PIN_1 9 // Fan PWM output pins
|
||||
#define PWM_PIN_2 10 //
|
||||
#define PWM_MAX 320 // Max PWM value for 25kHz (16MHz / 25kHz / 2)
|
||||
#define SPEED_MIN 0 // Min speed (mph) — fans off below this
|
||||
#define SPEED_MAX 100 // Max speed (mph) — full fan speed at this
|
||||
#define SPEED_THRESHOLD 5 // Below this speed fans are off
|
||||
// ============================================================
|
||||
|
||||
#define BYTE_SIZE sizeof(SimWindData)
|
||||
|
||||
int velocity = 0;
|
||||
int fanpower = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(BAUD_RATE);
|
||||
|
||||
// Configure Timer1 for Phase Correct PWM at 25kHz duty cycle
|
||||
// Mode 10: Phase Correct PWM, TOP = ICR1
|
||||
// WGM13:WGM12:WGM11:WGM10 = 1:0:1:0
|
||||
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);
|
||||
TCCR1B = _BV(WGM13) | _BV(CS10); // <-- WGM13 restored, no prescaler
|
||||
ICR1 = PWM_MAX; // TOP = 320 → 16MHz / (2 * 320) = 25kHz
|
||||
pinMode(PWM_PIN_1, OUTPUT);
|
||||
pinMode(PWM_PIN_2, OUTPUT);
|
||||
OCR1A = 0;
|
||||
OCR1B = 0;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
char buff[BYTE_SIZE];
|
||||
if (Serial.available() >= BYTE_SIZE) {
|
||||
if (Serial.readBytes(buff, BYTE_SIZE) != BYTE_SIZE) return;
|
||||
union { SimWindData data; char bytes[BYTE_SIZE]; } u;
|
||||
memcpy(u.bytes, buff, BYTE_SIZE);
|
||||
velocity = u.data.velocity;
|
||||
fanpower = u.data.fanpower;
|
||||
}
|
||||
|
||||
int pwm = 0;
|
||||
if (velocity > SPEED_THRESHOLD) {
|
||||
// Square root scaling for slightly stronger sensation at lower speeds
|
||||
// fanpower (0-255) scales the overall intensity
|
||||
float powerScale = (float)fanpower / 255.0;
|
||||
pwm = (int)(sqrt((float)velocity / SPEED_MAX) * PWM_MAX * powerScale);
|
||||
pwm = constrain(pwm, 0, PWM_MAX);
|
||||
}
|
||||
|
||||
OCR1A = pwm;
|
||||
OCR1B = pwm;
|
||||
}
|
||||
@@ -7,16 +7,47 @@ set(devices_source_files
|
||||
sounddevice.c
|
||||
serialdevice.h
|
||||
serialdevice.c
|
||||
serialadapter.h
|
||||
serialadapter.c
|
||||
tachdevice.h
|
||||
tachdevice.c
|
||||
wheeldevice.h
|
||||
wheeldevice.c
|
||||
hapticeffect.h
|
||||
hapticeffect.c
|
||||
sound.h
|
||||
sound.c
|
||||
usb/revburner.h
|
||||
usb/revburner.c
|
||||
sound/usb_generic_shaker.h
|
||||
sound/usb_generic_shaker.c
|
||||
usb/cslelitev3.h
|
||||
usb/cslelitev3.c
|
||||
usb/simagicp1000.h
|
||||
usb/simagicp1000.c
|
||||
usb/simnetpedals.h
|
||||
usb/simnetpedals.c
|
||||
usb/wheels/logitechg29.h
|
||||
usb/wheels/logitechg29.c
|
||||
usb/wheels/cammusc5.h
|
||||
usb/wheels/cammusc5.c
|
||||
usb/wheels/cammusc12.h
|
||||
usb/wheels/cammusc12.c
|
||||
usb/wheels/gtneo.h
|
||||
usb/wheels/gtneo.c
|
||||
sound/usb_generic_shaker_pulse.c
|
||||
serial/arduino.h
|
||||
serial/arduino.c
|
||||
serial/arduinoledlua.h
|
||||
serial/arduinoledlua.c
|
||||
serial/moza.h
|
||||
serial/moza.c
|
||||
serial/moza_new.h
|
||||
serial/moza_new.c
|
||||
serial/moza_ks_pro_wheel.h
|
||||
serial/moza_ks_pro_wheel.c
|
||||
)
|
||||
|
||||
include_directories("." "usb" "sound" "serial")
|
||||
|
||||
add_library(devices STATIC ${devices_source_files})
|
||||
|
||||
set_property(TARGET devices PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "usbhapticdevice.h"
|
||||
#include "simdevice.h"
|
||||
#include "../../helper/confighelper.h"
|
||||
#include "../../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../../simulatorapi/simapi/simapi/simmapper.h"
|
||||
#include "../../slog/slog.h"
|
||||
|
||||
#define kmhtoms 0.277778
|
||||
#define minspeedinms 0.5
|
||||
#define minvelocity 50
|
||||
#define maxbrake 0
|
||||
#define maxthrottle 0
|
||||
#define maxXvelocity 0.001
|
||||
|
||||
|
||||
bool hasTyreDiameter(SimData* simdata)
|
||||
{
|
||||
if (simdata->tyrediameter[0] <= 0 || simdata->tyrediameter[1] <= 0 || simdata->tyrediameter[2] <= 0 || simdata->tyrediameter[3] <= 0)
|
||||
{
|
||||
slogt("failed to find tyre diameter data");
|
||||
return false;
|
||||
}
|
||||
slogt("tyre diameter data found");
|
||||
return true;
|
||||
}
|
||||
|
||||
int loadtyreconfig(SimData* simdata, char* configfile, bool setDiameters)
|
||||
{
|
||||
config_t cfg;
|
||||
config_init(&cfg);
|
||||
const config_setting_t* config_cars = NULL;
|
||||
|
||||
if (!config_read_file(&cfg, configfile))
|
||||
{
|
||||
slogw("Could not open diameters save file.");
|
||||
config_destroy(&cfg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
config_cars = config_lookup(&cfg, "cars");
|
||||
|
||||
if(config_cars == NULL)
|
||||
{
|
||||
slogd("diameters config file corrupted");
|
||||
config_destroy(&cfg);
|
||||
return 1;
|
||||
}
|
||||
slogt("parsing diameters config file");
|
||||
|
||||
const int cars = config_setting_length(config_cars);
|
||||
|
||||
bool foundCar = false;
|
||||
int i = 0;
|
||||
while (i<cars)
|
||||
{
|
||||
DeviceSettings settings;
|
||||
|
||||
config_setting_t* config_car = config_setting_get_elem(config_cars, i);
|
||||
if(config_car != NULL)
|
||||
{
|
||||
const char* car;
|
||||
const char* simstr;
|
||||
int sim = 0;
|
||||
double tyre0;
|
||||
double tyre1;
|
||||
double tyre2;
|
||||
double tyre3;
|
||||
config_setting_lookup_string(config_car, "car", &car);
|
||||
config_setting_lookup_float(config_car, "tyre0", &tyre0);
|
||||
config_setting_lookup_float(config_car, "tyre1", &tyre1);
|
||||
config_setting_lookup_float(config_car, "tyre2", &tyre2);
|
||||
config_setting_lookup_float(config_car, "tyre3", &tyre3);
|
||||
int found = config_setting_lookup_int(config_car, "sim", &sim);
|
||||
//if(found == CONFIG_FALSE)
|
||||
//{
|
||||
// int found = config_setting_lookup_int(config_car, "sim", &sim);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// sim = simapi_strtogame(simstr);
|
||||
//}
|
||||
|
||||
|
||||
if(simdata->car != NULL && car != NULL)
|
||||
{
|
||||
if(simdata->car[0] != '\0' && car[0] != '\0')
|
||||
{
|
||||
slogt("%s %s %i %i", simdata->car, car, simdata->simexe, sim);
|
||||
if (strcicmp(car, simdata->car) == 0 && sim == simdata->simexe)
|
||||
{
|
||||
slogi("found saved car %s with tyre diameters %f %f %f %f", car, tyre0, tyre1, tyre2, tyre3);
|
||||
foundCar = true;
|
||||
if(setDiameters == true)
|
||||
{
|
||||
simdata->tyrediameter[0] = tyre0;
|
||||
simdata->tyrediameter[1] = tyre1;
|
||||
simdata->tyrediameter[2] = tyre2;
|
||||
simdata->tyrediameter[3] = tyre3;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
slogw("Possible corruption in config file on entry %i attempting to continue", i+1);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
config_destroy(&cfg);
|
||||
|
||||
if(foundCar == true)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int savetyreconfig(SimData* simdata, char* configfile)
|
||||
{
|
||||
config_t cfg;
|
||||
config_setting_t* root;
|
||||
config_setting_t* array;
|
||||
config_setting_t* carobject;
|
||||
config_setting_t* setting;
|
||||
|
||||
|
||||
config_init(&cfg);
|
||||
if (!config_read_file(&cfg, configfile))
|
||||
{
|
||||
slogw("Could not open diameters save file, creating new.");
|
||||
root = config_root_setting(&cfg);
|
||||
array = config_setting_add(root, "cars", CONFIG_TYPE_LIST);
|
||||
}
|
||||
else
|
||||
{
|
||||
array = config_lookup(&cfg, "cars");
|
||||
}
|
||||
|
||||
// TODO add check to not add same car-sim combination twice
|
||||
carobject = config_setting_add(array, "cars", CONFIG_TYPE_GROUP);
|
||||
|
||||
setting = config_setting_add(carobject, "car", CONFIG_TYPE_STRING);
|
||||
config_setting_set_string(setting, simdata->car);
|
||||
setting = config_setting_add(carobject, "sim", CONFIG_TYPE_INT64);
|
||||
config_setting_set_int64(setting, simdata->simexe);
|
||||
setting = config_setting_add(carobject, "tyre0", CONFIG_TYPE_FLOAT);
|
||||
config_setting_set_float(setting, simdata->tyrediameter[0]);
|
||||
setting = config_setting_add(carobject, "tyre1", CONFIG_TYPE_FLOAT);
|
||||
config_setting_set_float(setting, simdata->tyrediameter[1]);
|
||||
setting = config_setting_add(carobject, "tyre2", CONFIG_TYPE_FLOAT);
|
||||
config_setting_set_float(setting, simdata->tyrediameter[2]);
|
||||
setting = config_setting_add(carobject, "tyre3", CONFIG_TYPE_FLOAT);
|
||||
config_setting_set_float(setting, simdata->tyrediameter[3]);
|
||||
|
||||
/* Write out the new configuration. */
|
||||
if(! config_write_file(&cfg, configfile))
|
||||
{
|
||||
slogi("Error while writing file.");
|
||||
config_destroy(&cfg);
|
||||
}
|
||||
|
||||
slogi("New configuration successfully written to: %s for sim %i, car %s\n", configfile, simdata->simexe, simdata->car);
|
||||
|
||||
config_destroy(&cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void getTyreDiameter(SimData* simdata)
|
||||
{
|
||||
if(simdata->velocity > minvelocity && simdata->brake <= maxbrake && simdata->gas <= maxthrottle)
|
||||
{
|
||||
double Speedms = kmhtoms * simdata->velocity;
|
||||
if (simdata->Xvelocity/Speedms < maxXvelocity)
|
||||
{
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
simdata->tyrediameter[i] = Speedms / simdata->tyreRPS[i] * 2;
|
||||
}
|
||||
slogi("Successfully set tyre diameters for wheel slip effects.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int initializeHapticEffect(HapticEffect* h, HapticEffectSettings* hs, MonocoqueSettings* ms)
|
||||
{
|
||||
|
||||
switch (hs->effect_type)
|
||||
{
|
||||
case (EFFECT_ENGINERPM):
|
||||
h->effecttype = EFFECT_ENGINERPM;
|
||||
slogi("Initializing haptic effect for engine vibrations.");
|
||||
break;
|
||||
case (EFFECT_GEARSHIFT):
|
||||
h->effecttype = EFFECT_GEARSHIFT;
|
||||
slogi("Initializing haptic effect for gear shift vibrations.");
|
||||
break;
|
||||
case (EFFECT_TYRESLIP):
|
||||
h->effecttype = EFFECT_TYRESLIP;
|
||||
slogi("Initializing haptic effect for tyre slip vibrations.");
|
||||
break;
|
||||
case (EFFECT_TYRELOCK):
|
||||
h->effecttype = EFFECT_TYRELOCK;
|
||||
slogi("Initializing haptic effect for tyre lock vibrations.");
|
||||
break;
|
||||
case (EFFECT_ABSBRAKES):
|
||||
h->effecttype = EFFECT_ABSBRAKES;
|
||||
slogi("Initializing haptic effect for abs vibrations.");
|
||||
break;
|
||||
|
||||
case (EFFECT_SUSPENSION):
|
||||
h->effecttype = EFFECT_SUSPENSION;
|
||||
slogi("Initializing haptic effect for suspension vibrations.");
|
||||
break;
|
||||
}
|
||||
|
||||
h->tyre = hs->tyre;
|
||||
slogi("Haptic effect: %i %i, tyre %i %i", h->effecttype, hs->effect_type, h->tyre, hs->tyre);
|
||||
|
||||
h->threshold = hs->threshold;
|
||||
h->modulationType = hs->modulation;
|
||||
h->basefrequency = hs->frequency;
|
||||
h->frequencyMax = hs->frequencyMax;
|
||||
h->baseamplitude = hs->amplitude;
|
||||
h->amplitudeMax = hs->amplitudeMax;
|
||||
h->motorposition = hs->motorposition;
|
||||
h->duration = hs->duration;
|
||||
|
||||
slogt("haptic duration: %f", h->duration);
|
||||
slogt("haptic base frequency: %i", h->basefrequency);
|
||||
slogt("haptic base amplitude: %i", h->baseamplitude);
|
||||
slogt("haptic motorposition: %i", h->motorposition);
|
||||
|
||||
h->useconfig = ms->useconfig;
|
||||
h->configcheck = &ms->configcheck;
|
||||
h->tyrediameterconfig = ms->tyre_diameter_config;
|
||||
}
|
||||
|
||||
|
||||
double slipeffect(SimData* simdata, HapticEffect* h, int useconfig, int* configcheck, char* configfile)
|
||||
{
|
||||
int effecttype = h->effecttype;
|
||||
int tyre = h->tyre;
|
||||
double threshold = h->threshold;
|
||||
double play = 0;
|
||||
double wheelslip[4];
|
||||
wheelslip[0] = 0;
|
||||
wheelslip[1] = 0;
|
||||
wheelslip[2] = 0;
|
||||
wheelslip[3] = 0;
|
||||
|
||||
int sim_slip_ratio =0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
sim_slip_ratio = abs(simdata->tyreslipratio[i]);
|
||||
}
|
||||
|
||||
if(sim_slip_ratio == 0)
|
||||
{
|
||||
//slogt("wheel vibration calculation with wheel config set to %i configchecked %i configfile %s car %s sim %i", useconfig, *configcheck, configfile, simdata->car, simdata->simexe);
|
||||
|
||||
switch (effecttype)
|
||||
{
|
||||
case (EFFECT_TYRESLIP):
|
||||
case (EFFECT_TYRELOCK):
|
||||
case (EFFECT_ABSBRAKES):
|
||||
|
||||
|
||||
if(hasTyreDiameter(simdata)==true)
|
||||
{
|
||||
double Speedms = kmhtoms * simdata->velocity;
|
||||
slogt("attempting wheel slip calculation");
|
||||
if (Speedms > minspeedinms)
|
||||
{
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
wheelslip[i] = (Speedms - simdata->tyrediameter[i] * simdata->tyreRPS[i] / 2) / Speedms;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
wheelslip[i] = 0;
|
||||
}
|
||||
}
|
||||
slogt("wheelslip values are %f %f %f %f", wheelslip[0], wheelslip[1], wheelslip[2], wheelslip[3]);
|
||||
slogt("velocities (x,y,z) are %f %f %f", simdata->Xvelocity, simdata->Yvelocity, simdata->Zvelocity);
|
||||
}
|
||||
break;
|
||||
case EFFECT_SUSPENSION:
|
||||
break;
|
||||
default:
|
||||
slogw("Unknown effect type %i", effecttype);
|
||||
}
|
||||
if(simdata->Yvelocity <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if(simdata->Zvelocity > 1 || simdata->Zvelocity < -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
wheelslip[i] = simdata->tyreslipratio[i];
|
||||
}
|
||||
slogt("wheelslip values from sim are %f %f %f %f", wheelslip[0], wheelslip[1], wheelslip[2], wheelslip[3]);
|
||||
}
|
||||
|
||||
|
||||
switch (effecttype)
|
||||
{
|
||||
case (EFFECT_TYRESLIP):
|
||||
|
||||
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[0] < -threshold)
|
||||
{
|
||||
play += fabs(wheelslip[0]) - fabs(threshold);
|
||||
slogt("slip is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[1] < -threshold)
|
||||
{
|
||||
play += fabs(wheelslip[1]) - fabs(threshold);
|
||||
slogt("slip is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[2] < -threshold)
|
||||
{
|
||||
play += fabs(wheelslip[2]) - fabs(threshold);
|
||||
slogt("slip is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[3] < -threshold)
|
||||
{
|
||||
play += fabs(wheelslip[3]) - fabs(threshold);
|
||||
slogt("slip is %f", play);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case (EFFECT_TYRELOCK):
|
||||
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[0] > threshold)
|
||||
{
|
||||
play += wheelslip[0] - threshold;
|
||||
slogt("lock is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[1] > threshold)
|
||||
{
|
||||
play += wheelslip[1] - threshold;
|
||||
slogt("lock is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[2] > threshold)
|
||||
{
|
||||
play += wheelslip[2] - threshold;
|
||||
slogt("lock is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[3] > threshold)
|
||||
{
|
||||
play += wheelslip[3] - threshold;
|
||||
slogt("lock is %f", play);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case (EFFECT_ABSBRAKES):
|
||||
threshold = simdata->abs + threshold;
|
||||
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[0] > threshold)
|
||||
{
|
||||
play += wheelslip[0] - threshold;
|
||||
slogt("abs is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[1] > threshold)
|
||||
{
|
||||
play += wheelslip[1] - threshold;
|
||||
slogt("abs is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[2] > threshold)
|
||||
{
|
||||
play += wheelslip[2] - threshold;
|
||||
slogt("abs is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
|
||||
{
|
||||
if(wheelslip[3] > threshold)
|
||||
{
|
||||
play += wheelslip[3] - threshold;
|
||||
slogt("abs is %f", play);
|
||||
}
|
||||
}
|
||||
if(simdata->abs <= 0)
|
||||
{
|
||||
play = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case (EFFECT_SUSPENSION):
|
||||
|
||||
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
|
||||
{
|
||||
if(simdata->suspension[0] > threshold)
|
||||
{
|
||||
play += simdata->suspension[0] - threshold;
|
||||
slogt("suspension is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
|
||||
{
|
||||
if(simdata->suspension[1] > threshold)
|
||||
{
|
||||
play += simdata->suspension[1] - threshold;
|
||||
slogt("suspension is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
|
||||
{
|
||||
if(simdata->suspension[2] > threshold)
|
||||
{
|
||||
play += simdata->suspension[2] - threshold;
|
||||
slogt("suspension is %f", play);
|
||||
}
|
||||
}
|
||||
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
|
||||
{
|
||||
if(simdata->suspension[3] > threshold)
|
||||
{
|
||||
play += simdata->suspension[3] - threshold;
|
||||
slogt("suspension is %f", play);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return play;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef _HAPTICEFFECT_H
|
||||
#define _HAPTICEFFECT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HAPTIC_EFFECT_MODULATION_NONE = 0,
|
||||
HAPTIC_EFFECT_MODULATION_FREQUENCY = 1,
|
||||
HAPTIC_EFFECT_MODULATION_AMPLIFY = 2,
|
||||
}
|
||||
HapticEffectModulationType;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t curr_frequency;
|
||||
uint32_t curr_amplitude;
|
||||
double curr_duration;
|
||||
uint32_t last_gear;
|
||||
}
|
||||
CurrentEffectData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
VibrationEffectType effecttype;
|
||||
MonocoqueTyreIdentifier tyre;
|
||||
HapticEffectModulationType modulationType;
|
||||
|
||||
uint32_t volume;
|
||||
double duration;
|
||||
double threshold;
|
||||
uint32_t motorposition;
|
||||
uint32_t basefrequency;
|
||||
uint32_t frequencyMax;
|
||||
uint32_t baseamplitude;
|
||||
uint32_t amplitudeMax;
|
||||
|
||||
CurrentEffectData live_effect;
|
||||
|
||||
int useconfig;
|
||||
int* configcheck;
|
||||
char* tyrediameterconfig;
|
||||
}
|
||||
HapticEffect;
|
||||
|
||||
int initializeHapticEffect(HapticEffect* h, HapticEffectSettings* hs, MonocoqueSettings* ms);
|
||||
double slipeffect(SimData* simdata, HapticEffect* h, int useconfig, int* configcheck, char* configfile);
|
||||
bool hasTyreDiameter(SimData* simdata);
|
||||
int loadtyreconfig(SimData* simdata, char* configfile, bool setDiameters);
|
||||
int savetyreconfig(SimData* simdata, char* configfile);
|
||||
void getTyreDiameter(SimData* simdata);
|
||||
|
||||
#endif
|
||||
@@ -2,81 +2,428 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "arduino.h"
|
||||
#include "arduinoledlua.h"
|
||||
#include "../serialadapter.h"
|
||||
#include "../../slog/slog.h"
|
||||
|
||||
#define arduino_timeout 2000
|
||||
#define arduino_timeout 9000
|
||||
|
||||
int arduino_update(SerialDevice* serialdevice, SimData* simdata)
|
||||
int arduino_check(enum sp_return result)
|
||||
{
|
||||
int result = 1;
|
||||
if (serialdevice->port)
|
||||
{
|
||||
result = check(sp_blocking_write(serialdevice->port, simdata, sizeof(SimData), arduino_timeout));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int arduino_init(SerialDevice* serialdevice, const char* portdev)
|
||||
{
|
||||
slogi("initializing arduino serial device...");
|
||||
int error = 0;
|
||||
char* port_name = strdup(portdev);
|
||||
|
||||
slogd("Looking for port %s.\n", port_name);
|
||||
error = check(sp_get_port_by_name(port_name, &serialdevice->port));
|
||||
if (error != 0)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
slogd("Opening port.\n");
|
||||
check(sp_open(serialdevice->port, SP_MODE_READ_WRITE));
|
||||
|
||||
slogd("Setting port to 9600 8N1, no flow control.\n");
|
||||
check(sp_set_baudrate(serialdevice->port, 9600));
|
||||
check(sp_set_bits(serialdevice->port, 8));
|
||||
check(sp_set_parity(serialdevice->port, SP_PARITY_NONE));
|
||||
check(sp_set_stopbits(serialdevice->port, 1));
|
||||
check(sp_set_flowcontrol(serialdevice->port, SP_FLOWCONTROL_NONE));
|
||||
|
||||
free(port_name);
|
||||
slogd("Successfully setup arduino serial device...");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arduino_free(SerialDevice* serialdevice)
|
||||
{
|
||||
check(sp_close(serialdevice->port));
|
||||
sp_free_port(serialdevice->port);
|
||||
}
|
||||
|
||||
int check(enum sp_return result)
|
||||
{
|
||||
/* For this example we'll just exit on any error by calling abort(). */
|
||||
char* error_message;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case SP_ERR_ARG:
|
||||
//printf("Error: Invalid argument.\n");
|
||||
return 1;
|
||||
//abort();
|
||||
case SP_ERR_FAIL:
|
||||
error_message = sp_last_error_message();
|
||||
printf("Error: Failed: %s\n", error_message);
|
||||
sloge("error: serial write failed: %s", error_message);
|
||||
sp_free_error_message(error_message);
|
||||
abort();
|
||||
case SP_ERR_SUPP:
|
||||
printf("Error: Not supported.\n");
|
||||
abort();
|
||||
case SP_ERR_MEM:
|
||||
printf("Error: Couldn't allocate memory.\n");
|
||||
abort();
|
||||
case SP_OK:
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int arduino_update(SerialDevice* serialdevice, void* data, size_t size)
|
||||
{
|
||||
int result = 1;
|
||||
slogt("copying %i bytes to arduino device", size);
|
||||
result = monocoque_serial_write(serialdevice->id, data, size, arduino_timeout);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// the input event waiting and flushing is the right way to do this
|
||||
// most of the time i'm just "bit blasting" and the only receiving
|
||||
// happens initially to get the number of lights
|
||||
int GetNumberOfLeds(SerialDevice* serialdevice, int* numlights)
|
||||
|
||||
{
|
||||
int count = 0;
|
||||
int bytesWaiting;
|
||||
char buf[256];
|
||||
int retval = 0;
|
||||
int i;
|
||||
|
||||
//sp_flush(port, SP_BUF_INPUT);
|
||||
|
||||
while (count < 4)
|
||||
{
|
||||
slogi("Attempting to retrieve num lights from port...");
|
||||
|
||||
size_t bufsize1 = 11;
|
||||
size_t recv_bufsize1 = 5;
|
||||
char recv_buf1[recv_bufsize1];
|
||||
char bytes1[bufsize1];
|
||||
|
||||
for(int j = 0; j < bufsize1; j++)
|
||||
{
|
||||
bytes1[j] = 0x00;
|
||||
}
|
||||
bytes1[0] = 0xff;
|
||||
bytes1[1] = 0xff;
|
||||
bytes1[2] = 0xff;
|
||||
bytes1[3] = 0xff;
|
||||
bytes1[4] = 0xff;
|
||||
bytes1[5] = 0xff;
|
||||
bytes1[6] = 0x6c;
|
||||
bytes1[7] = 0x65;
|
||||
bytes1[8] = 0x64;
|
||||
bytes1[9] = 0x73;
|
||||
bytes1[10] = 0x63;
|
||||
|
||||
int result = 0;
|
||||
unsigned int timeout = 6000;
|
||||
slogd("Sending message to get num lights");
|
||||
monocoque_wait_for_event(serialdevice->id, 6);
|
||||
|
||||
result = monocoque_serial_write(serialdevice->id, &bytes1, bufsize1, arduino_timeout);
|
||||
|
||||
|
||||
monocoque_wait_for_event(serialdevice->id, 5);
|
||||
bytesWaiting = monocoque_input_wait(serialdevice->id);
|
||||
if (bytesWaiting > 0)
|
||||
{
|
||||
memset(buf, 0, sizeof(buf));
|
||||
retval = monocoque_serial_read_block(serialdevice->id, buf, sizeof(buf)-1, 10);
|
||||
if (retval < 0)
|
||||
{
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i=0; i<retval; i++)
|
||||
{
|
||||
if (buf[i] == 13)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
int ret = atoi(buf);
|
||||
*numlights = ret;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (bytesWaiting < 0)
|
||||
{
|
||||
sloge("Error getting bytes available from serial port: %d", bytesWaiting);
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int arduino_init(SerialDevice* serialdevice, const char* portdev)
|
||||
{
|
||||
int id = monocoque_serial_open(serialdevice, portdev);
|
||||
if (id < 0) return id;
|
||||
serialdevice->id = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool uselights)
|
||||
{
|
||||
int error = arduino_init(serialdevice, portdev);
|
||||
|
||||
int numlights = 0;
|
||||
|
||||
if(uselights == true)
|
||||
{
|
||||
error = GetNumberOfLeds(serialdevice, &numlights);
|
||||
slogi("numlights is %i\n", numlights);
|
||||
// close, error and free if numlights is 0
|
||||
serialdevice->numleds = numlights;
|
||||
}
|
||||
|
||||
if(luafile == NULL)
|
||||
{
|
||||
return serialdevice->id;
|
||||
}
|
||||
|
||||
|
||||
slogi("LUA config specified for this device... initializing...");
|
||||
|
||||
lua_State* L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
|
||||
int top=lua_gettop(L);
|
||||
int status = luaL_loadfile(L, luafile);
|
||||
|
||||
if (status) {
|
||||
/* If something went wrong, error message is at the top of */
|
||||
/* the stack */
|
||||
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
|
||||
lua_close(serialdevice->m.L);
|
||||
return -1;
|
||||
exit(1);
|
||||
}
|
||||
lua_setglobal(L,"myFunc");
|
||||
|
||||
serialdevice->m.L = L;
|
||||
|
||||
slogi("LUA config setup successful.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int arduino_simled_update(SerialDevice* serialdevice, SimData* simdata)
|
||||
{
|
||||
int result = 1;
|
||||
|
||||
int total_leds = serialdevice->numleds;
|
||||
size_t bufsize = (total_leds * 3) + 14;
|
||||
char bytes[bufsize];
|
||||
int endled = serialdevice->endled;
|
||||
int startled = serialdevice->startled;
|
||||
if(endled == 0)
|
||||
{
|
||||
endled = total_leds;
|
||||
}
|
||||
int num_avail_leds = endled - startled + 1;
|
||||
int rpm = simdata->rpms;
|
||||
int maxrpm = simdata->maxrpm;
|
||||
int litleds = 0;
|
||||
if(rpm > 0 && maxrpm > 0)
|
||||
{
|
||||
int rpmmargin = ceil(.05*maxrpm);
|
||||
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds);
|
||||
|
||||
|
||||
for (int l = 1; l <= (num_avail_leds); l++)
|
||||
{
|
||||
if(rpm >= (rpminterval * l))
|
||||
{
|
||||
litleds = l;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for(int j = 0; j < bufsize; j++)
|
||||
{
|
||||
bytes[j] = 0x00;
|
||||
}
|
||||
bytes[0] = 0xff;
|
||||
bytes[1] = 0xff;
|
||||
bytes[2] = 0xff;
|
||||
bytes[3] = 0xff;
|
||||
bytes[4] = 0xff;
|
||||
bytes[5] = 0xff;
|
||||
bytes[6] = 0x73;
|
||||
bytes[7] = 0x6c;
|
||||
bytes[8] = 0x65;
|
||||
bytes[9] = 0x64;
|
||||
bytes[10] = 0x73;
|
||||
bytes[bufsize-1] = 0xfd;
|
||||
bytes[bufsize-2] = 0xfe;
|
||||
bytes[bufsize-3] = 0xff;
|
||||
|
||||
for(int i = 0; i < litleds; i++)
|
||||
{
|
||||
if(i < ((num_avail_leds) / 2))
|
||||
{
|
||||
//green
|
||||
bytes[11 + ((i + startled - 1) * 3) + 1] = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(i < num_avail_leds - 1)
|
||||
{
|
||||
//yellow
|
||||
bytes[11 + ((i + startled - 1) * 3) + 0] = 0xff;
|
||||
bytes[11 + ((i + startled - 1) * 3) + 1] = 0xff;
|
||||
}
|
||||
}
|
||||
if(i == num_avail_leds - 1)
|
||||
{
|
||||
//red
|
||||
bytes[11 + ((i + startled - 1) * 3) + 0] = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slogt("Updating arduino device lights to %i", litleds);
|
||||
// we can add configs to set all the colors
|
||||
size_t size = sizeof(bytes);
|
||||
|
||||
result = monocoque_serial_write(serialdevice->id, &bytes, size, arduino_timeout);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int arduino_customled_update(SerialDevice* serialdevice, SimData* simdata)
|
||||
{
|
||||
int result = 1;
|
||||
|
||||
int total_leds = serialdevice->numleds;
|
||||
size_t bufsize = (total_leds * 3) + 14;
|
||||
char ledbytes[total_leds * 3];
|
||||
char bytes[bufsize];
|
||||
|
||||
for(int j = 0; j < bufsize; j++)
|
||||
{
|
||||
bytes[j] = 0x00;
|
||||
}
|
||||
for(int j = 0; j < total_leds * 3; j++)
|
||||
{
|
||||
ledbytes[j] = 0x00;
|
||||
}
|
||||
bytes[0] = 0xff;
|
||||
bytes[1] = 0xff;
|
||||
bytes[2] = 0xff;
|
||||
bytes[3] = 0xff;
|
||||
bytes[4] = 0xff;
|
||||
bytes[5] = 0xff;
|
||||
bytes[6] = 0x73;
|
||||
bytes[7] = 0x6c;
|
||||
bytes[8] = 0x65;
|
||||
bytes[9] = 0x64;
|
||||
bytes[10] = 0x73;
|
||||
bytes[bufsize-1] = 0xfd;
|
||||
bytes[bufsize-2] = 0xfe;
|
||||
bytes[bufsize-3] = 0xff;
|
||||
|
||||
|
||||
lua_State* L = serialdevice->m.L;
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_pushlightuserdata(L, &ledbytes);
|
||||
lua_settable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
simdata_to_lua(L, simdata);
|
||||
lua_setglobal(L, "simdata");
|
||||
|
||||
lua_pushinteger(L, total_leds);
|
||||
lua_setglobal(L, "TotalLeds");
|
||||
|
||||
lua_register(L, "set_led_to_color", set_led_to_color);
|
||||
lua_register(L, "set_led_range_to_color", set_led_range_to_color);
|
||||
lua_register(L, "set_led_to_rgb_color", set_led_to_rgb_color);
|
||||
lua_register(L, "set_led_range_to_rgb_color", set_led_range_to_rgb_color);
|
||||
lua_register(L, "led_clear_all", led_clear_all);
|
||||
lua_register(L, "led_clear_range", led_clear_all);
|
||||
|
||||
lua_pushinteger(L, LUALEDCOLOR_RED);
|
||||
lua_setglobal(L, "RED");
|
||||
lua_pushinteger(L, LUALEDCOLOR_GREEN);
|
||||
lua_setglobal(L, "GREEN");
|
||||
lua_pushinteger(L, LUALEDCOLOR_BLUE);
|
||||
lua_setglobal(L, "BLUE");
|
||||
lua_pushinteger(L, LUALEDCOLOR_YELLOW);
|
||||
lua_setglobal(L, "YELLOW");
|
||||
lua_pushinteger(L, LUALEDCOLOR_ORANGE);
|
||||
lua_setglobal(L, "ORANGE");
|
||||
|
||||
lua_getglobal(L,"myFunc");
|
||||
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
|
||||
{
|
||||
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
|
||||
}
|
||||
|
||||
for(int i = 0; i < total_leds; i++)
|
||||
{
|
||||
bytes[11 + (i * 3) + 0] = ledbytes[(i * 3) + 0];
|
||||
bytes[11 + (i * 3) + 1] = ledbytes[(i * 3) + 1];
|
||||
bytes[11 + (i * 3) + 2] = ledbytes[(i * 3) + 2];
|
||||
}
|
||||
|
||||
size_t size = sizeof(bytes);
|
||||
result = arduino_update(serialdevice, &bytes, size);
|
||||
|
||||
slogt("custom led wrote %i bytes", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int arduino_customled_free(SerialDevice* serialdevice, bool lua)
|
||||
{
|
||||
size_t bufsize = (serialdevice->numleds * 3) + 14;
|
||||
char bytes[bufsize];
|
||||
int endled = serialdevice->endled;
|
||||
int startled = serialdevice->startled;
|
||||
if(endled == 0)
|
||||
|
||||
for(int j = 0; j < bufsize; j++)
|
||||
{
|
||||
bytes[j] = 0x00;
|
||||
}
|
||||
bytes[0] = 0xff;
|
||||
bytes[1] = 0xff;
|
||||
bytes[2] = 0xff;
|
||||
bytes[3] = 0xff;
|
||||
bytes[4] = 0xff;
|
||||
bytes[5] = 0xff;
|
||||
bytes[6] = 0x73;
|
||||
bytes[7] = 0x6c;
|
||||
bytes[8] = 0x65;
|
||||
bytes[9] = 0x64;
|
||||
bytes[10] = 0x73;
|
||||
bytes[bufsize-1] = 0xfd;
|
||||
bytes[bufsize-2] = 0xfe;
|
||||
bytes[bufsize-3] = 0xff;
|
||||
|
||||
for(int i = 0; i < serialdevice->numleds; i++)
|
||||
{
|
||||
bytes[11 + (i * 3) + 0] = 0x00;
|
||||
bytes[11 + (i * 3) + 1] = 0x00;
|
||||
bytes[11 + (i * 3) + 2] = 0x00;
|
||||
}
|
||||
size_t size = sizeof(bytes);
|
||||
|
||||
int result = monocoque_serial_write_block(serialdevice->id, &bytes, size, arduino_timeout);
|
||||
|
||||
if(lua == false)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
lua_close(serialdevice->m.L);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int arduino_custom_update(SerialDevice* serialdevice, SimData* simdata)
|
||||
{
|
||||
size_t bufsize = 14;
|
||||
char bytes[bufsize];
|
||||
lua_State* L = serialdevice->m.L;
|
||||
|
||||
simdata_to_lua(L, simdata);
|
||||
lua_setglobal(L, "simdata");
|
||||
|
||||
lua_getglobal(L,"myFunc");
|
||||
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
|
||||
{
|
||||
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
|
||||
}
|
||||
|
||||
lua_getglobal(L, "Message");
|
||||
int result = 0;
|
||||
if (lua_isstring(L, -1)) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
size_t size = strlen(msg);
|
||||
result = arduino_update(serialdevice, (void*) msg, size);
|
||||
slogt("custom arduino wrote message %s of %i bytes", msg, result);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
lua_pop(L, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
#include "../simdevice.h"
|
||||
#include "../serialdevice.h"
|
||||
|
||||
int arduino_update(SerialDevice* serialdevice, SimData* simdata);
|
||||
int arduino_init(SerialDevice* serialdevice, const char* portdev);
|
||||
|
||||
int arduino_simled_update(SerialDevice* serialdevice, SimData* simdata);
|
||||
int arduino_update(SerialDevice* serialdevice, void* data, size_t size);
|
||||
int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool useleds);
|
||||
int arduino_customled_update(SerialDevice* serialdevice, SimData* simdata);
|
||||
int arduino_custom_update(SerialDevice* serialdevice, SimData* simdata);
|
||||
int arduino_init(SerialDevice* serialdevice, const char* portdev);
|
||||
int arduino_free(SerialDevice* serialdevice);
|
||||
int check(enum sp_return result);
|
||||
int arduino_customled_free(SerialDevice* serialdevice, bool lua);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "arduinoledlua.h"
|
||||
#include "arduino.h"
|
||||
|
||||
#include "../../slog/slog.h"
|
||||
|
||||
long long ledTimeInMilliseconds(void) {
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv,NULL);
|
||||
return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
|
||||
}
|
||||
|
||||
|
||||
int simdata_to_lua(lua_State *L, SimData* simdata) {
|
||||
|
||||
// make the time up now if we are running in test mode
|
||||
if(simdata->mtick == 0)
|
||||
{
|
||||
simdata->mtick = ledTimeInMilliseconds();
|
||||
}
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, simdata->gearc);
|
||||
lua_setfield(L, -2, "gearc");
|
||||
|
||||
lua_pushinteger(L, simdata->playerflag);
|
||||
lua_setfield(L, -2, "playerflag");
|
||||
|
||||
lua_pushinteger(L, simdata->rpms);
|
||||
lua_setfield(L, -2, "rpm");
|
||||
|
||||
lua_pushinteger(L, simdata->gear);
|
||||
lua_setfield(L, -2, "gear");
|
||||
|
||||
lua_pushinteger(L, simdata->velocity);
|
||||
lua_setfield(L, -2, "velocity");
|
||||
|
||||
lua_pushinteger(L, simdata->mtick);
|
||||
lua_setfield(L, -2, "mtick");
|
||||
|
||||
lua_pushinteger(L, simdata->maxrpm);
|
||||
lua_setfield(L, -2, "maxrpm");
|
||||
|
||||
lua_pushinteger(L, PROXCARS);
|
||||
lua_setfield(L, -2, "proxcars");
|
||||
|
||||
lua_newtable(L);
|
||||
for(int i = 0; i < PROXCARS; i++)
|
||||
{
|
||||
lua_newtable(L);
|
||||
lua_pushinteger(L, simdata->pd[i].radius);
|
||||
lua_setfield(L, -2, "radius");
|
||||
lua_pushinteger(L, simdata->pd[i].theta);
|
||||
lua_setfield(L, -2, "theta");
|
||||
lua_rawseti(L, -2, i+1);
|
||||
}
|
||||
lua_setfield(L, -2, "pd");
|
||||
|
||||
lua_pushnumber(L, simdata->gas);
|
||||
lua_setfield(L, -2, "gas");
|
||||
|
||||
lua_pushnumber(L, simdata->fuel);
|
||||
lua_setfield(L, -2, "fuel");
|
||||
|
||||
lua_pushnumber(L, simdata->turboboost);
|
||||
lua_setfield(L, -2, "turboboost");
|
||||
|
||||
lua_newtable(L);
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
lua_pushnumber(L, simdata->tyreRPS[i]);
|
||||
lua_rawseti(L, -2, i+1);
|
||||
}
|
||||
lua_setfield(L, -2, "tyreRPS");
|
||||
|
||||
lua_newtable(L);
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
lua_pushnumber(L, simdata->tyrediameter[i]);
|
||||
lua_rawseti(L, -2, i+1);
|
||||
}
|
||||
lua_setfield(L, -2, "tyrediameter");
|
||||
|
||||
lua_newtable(L);
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
lua_pushnumber(L, simdata->tyretemp[i]);
|
||||
lua_rawseti(L, -2, i+1);
|
||||
}
|
||||
lua_setfield(L, -2, "tyretemp");
|
||||
|
||||
return 0; // Return the table to Lua
|
||||
}
|
||||
|
||||
uint8_t get_color_rgb_value(int color, int rgb)
|
||||
{
|
||||
switch (color)
|
||||
{
|
||||
case LUALEDCOLOR_RED:
|
||||
switch (rgb)
|
||||
{
|
||||
case 0:
|
||||
return 255;
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 0;
|
||||
}
|
||||
case LUALEDCOLOR_GREEN:
|
||||
switch (rgb)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return 255;
|
||||
case 2:
|
||||
return 0;
|
||||
}
|
||||
case LUALEDCOLOR_BLUE:
|
||||
switch (rgb)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 255;
|
||||
}
|
||||
case LUALEDCOLOR_YELLOW:
|
||||
switch (rgb)
|
||||
{
|
||||
case 0:
|
||||
return 255;
|
||||
case 1:
|
||||
return 255;
|
||||
case 2:
|
||||
return 0;
|
||||
}
|
||||
case LUALEDCOLOR_ORANGE:
|
||||
switch (rgb)
|
||||
{
|
||||
case 0:
|
||||
return 255;
|
||||
case 1:
|
||||
return 165;
|
||||
case 2:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int set_led_range_to_color(lua_State *L)
|
||||
{
|
||||
|
||||
slogt("lua called c function set_led_range_to_color");
|
||||
|
||||
int range_start = lua_tonumber(L, 1);
|
||||
int range_end = lua_tonumber(L, 2);
|
||||
int color = lua_tonumber(L, 3);
|
||||
|
||||
slogd("lua range start is %i", range_start);
|
||||
slogd("lua range end is %i", range_end);
|
||||
slogd("lua color is %i", color);
|
||||
|
||||
range_start = range_start - 1;
|
||||
|
||||
lua_getglobal(L, "TotalLeds");
|
||||
int numlights = 0;
|
||||
if (lua_isnumber(L, -1))
|
||||
{
|
||||
numlights = lua_tonumber(L, -1);
|
||||
}
|
||||
slogd("num leds is %i", numlights);
|
||||
|
||||
if(range_end > numlights)
|
||||
{
|
||||
range_end = numlights;
|
||||
}
|
||||
|
||||
if(range_end == 0)
|
||||
{
|
||||
slogt("Invalid range, doing nothing");
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
char* bytes = lua_touserdata(L, -1);
|
||||
|
||||
slogt("first byte of buff is x%02x", bytes[0]);
|
||||
|
||||
uint8_t color0 = get_color_rgb_value(color, 0);
|
||||
uint8_t color1 = get_color_rgb_value(color, 1);
|
||||
uint8_t color2 = get_color_rgb_value(color, 2);
|
||||
|
||||
for( int i = range_start; i < range_end; i++)
|
||||
{
|
||||
bytes[(i * 3) + 0] = color0;
|
||||
bytes[(i * 3) + 1] = color1;
|
||||
bytes[(i * 3) + 2] = color2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int set_led_range_to_rgb_color(lua_State *L)
|
||||
{
|
||||
|
||||
slogt("lua called c function set_led_range_to_rgb_color");
|
||||
|
||||
int range_start = lua_tonumber(L, 1);
|
||||
int range_end = lua_tonumber(L, 2);
|
||||
int color = lua_tonumber(L, 3);
|
||||
|
||||
uint8_t color0 = (color >> 16) & 0xff;
|
||||
uint8_t color1 = (color >> 8) & 0xff;
|
||||
uint8_t color2 = (color >> 0) & 0xff;
|
||||
//int color0 = lua_tonumber(L, 3);
|
||||
//int color1 = lua_tonumber(L, 4);
|
||||
//int color2 = lua_tonumber(L, 5);
|
||||
|
||||
slogd("lua range start is %i", range_start);
|
||||
slogd("lua range end is %i", range_end);
|
||||
slogd("lua color0 is %i", color0);
|
||||
slogd("lua color1 is %i", color1);
|
||||
slogd("lua color2 is %i", color2);
|
||||
|
||||
range_start = range_start - 1;
|
||||
|
||||
lua_getglobal(L, "TotalLeds");
|
||||
int numlights = 0;
|
||||
if (lua_isnumber(L, -1))
|
||||
{
|
||||
numlights = lua_tonumber(L, -1);
|
||||
}
|
||||
slogd("num leds is %i", numlights);
|
||||
|
||||
if(range_end > numlights)
|
||||
{
|
||||
range_end = numlights;
|
||||
}
|
||||
|
||||
if(range_end == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
char* bytes = lua_touserdata(L, -1);
|
||||
|
||||
slogt("first byte of buff is x%02x", bytes[0]);
|
||||
|
||||
for( int i = range_start; i < range_end; i++)
|
||||
{
|
||||
bytes[(i * 3) + 0] = color0;
|
||||
bytes[(i * 3) + 1] = color1;
|
||||
bytes[(i * 3) + 2] = color2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int set_led_to_color(lua_State *L)
|
||||
{
|
||||
slogt("lua called c function set_led_to_rgb_color");
|
||||
|
||||
int led = lua_tonumber(L, 1);
|
||||
int color = lua_tonumber(L, 2);
|
||||
|
||||
slogd("lua led is %i", led);
|
||||
slogd("lua color is %i", color);
|
||||
|
||||
led = led - 1;
|
||||
|
||||
lua_getglobal(L, "TotalLeds");
|
||||
int numlights = 0;
|
||||
if (lua_isnumber(L, -1))
|
||||
{
|
||||
numlights = lua_tonumber(L, -1);
|
||||
}
|
||||
slogd("num leds is %i", numlights);
|
||||
|
||||
if(led > numlights)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
char* bytes = lua_touserdata(L, -1);
|
||||
|
||||
slogt("first byte of buff is x%02x", bytes[0]);
|
||||
|
||||
uint8_t color0 = get_color_rgb_value(color, 0);
|
||||
uint8_t color1 = get_color_rgb_value(color, 1);
|
||||
uint8_t color2 = get_color_rgb_value(color, 2);
|
||||
|
||||
bytes[(led * 3) + 0] = color0;
|
||||
bytes[(led * 3) + 1] = color1;
|
||||
bytes[(led * 3) + 2] = color2;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int set_led_to_rgb_color(lua_State *L)
|
||||
{
|
||||
slogt("lua called c function set_led_to_rgb_color");
|
||||
|
||||
int led = lua_tonumber(L, 1);
|
||||
int color = lua_tonumber(L, 2);
|
||||
|
||||
uint8_t color0 = (color >> 16) & 0xff;
|
||||
uint8_t color1 = (color >> 8) & 0xff;
|
||||
uint8_t color2 = (color >> 0) & 0xff;
|
||||
|
||||
slogd("lua led is %i", led);
|
||||
slogd("lua color0 is %i", color0);
|
||||
slogd("lua color1 is %i", color1);
|
||||
slogd("lua color2 is %i", color2);
|
||||
|
||||
led = led - 1;
|
||||
|
||||
lua_getglobal(L, "TotalLeds");
|
||||
int numlights = 0;
|
||||
if (lua_isnumber(L, -1))
|
||||
{
|
||||
numlights = lua_tonumber(L, -1);
|
||||
}
|
||||
slogd("num leds is %i", numlights);
|
||||
|
||||
if(led > numlights)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
char* bytes = lua_touserdata(L, -1);
|
||||
|
||||
slogt("first byte of buff is x%02x", bytes[0]);
|
||||
|
||||
bytes[(led * 3) + 0] = color0;
|
||||
bytes[(led * 3) + 1] = color1;
|
||||
bytes[(led * 3) + 2] = color2;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int led_clear_all(lua_State *L)
|
||||
{
|
||||
|
||||
slogt("lua called c function led_clear_all");
|
||||
|
||||
lua_getglobal(L, "TotalLeds");
|
||||
int numlights = 0;
|
||||
if (lua_isnumber(L, -1))
|
||||
{
|
||||
numlights = lua_tonumber(L, -1);
|
||||
}
|
||||
slogd("num leds is %i", numlights);
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
char* bytes = lua_touserdata(L, -1);
|
||||
|
||||
slogt("first byte of buff is x%02x", bytes[0]);
|
||||
|
||||
for( int i = 0; i < numlights; i++)
|
||||
{
|
||||
bytes[(i * 3) + 0] = 0x00;
|
||||
bytes[(i * 3) + 1] = 0x00;
|
||||
bytes[(i * 3) + 2] = 0x00;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int led_clear_range(lua_State *L)
|
||||
{
|
||||
|
||||
slogt("lua called c function led_clear_range");
|
||||
|
||||
int range_start = lua_tonumber(L, 1);
|
||||
int range_end = lua_tonumber(L, 2);
|
||||
|
||||
slogd("lua range start is %i", range_start);
|
||||
slogd("lua range end is %i", range_end);
|
||||
|
||||
range_start = range_start - 1;
|
||||
|
||||
lua_getglobal(L, "TotalLeds");
|
||||
int numlights = 0;
|
||||
if (lua_isnumber(L, -1))
|
||||
{
|
||||
numlights = lua_tonumber(L, -1);
|
||||
}
|
||||
slogd("num leds is %i", numlights);
|
||||
|
||||
if(range_end > numlights)
|
||||
{
|
||||
range_end = numlights;
|
||||
}
|
||||
|
||||
if(range_end == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
char* bytes = lua_touserdata(L, -1);
|
||||
|
||||
slogt("first byte of buff is x%02x", bytes[0]);
|
||||
|
||||
for( int i = 0; i < numlights; i++)
|
||||
{
|
||||
bytes[(i * 3) + 0] = 0x00;
|
||||
bytes[(i * 3) + 1] = 0x00;
|
||||
bytes[(i * 3) + 2] = 0x00;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef _ARDUINOLEDLUA_H
|
||||
#define _ARDUINOLEDLUA_H
|
||||
|
||||
//#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
#ifdef USE_LUA_55
|
||||
/* open all libraries */
|
||||
#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
|
||||
#endif
|
||||
|
||||
#include "../../simulatorapi/simapi/simapi/simdata.h"
|
||||
|
||||
typedef enum {
|
||||
LUALEDCOLOR_RED = 1,
|
||||
LUALEDCOLOR_GREEN,
|
||||
LUALEDCOLOR_BLUE,
|
||||
LUALEDCOLOR_YELLOW,
|
||||
LUALEDCOLOR_ORANGE,
|
||||
} LUALEDColor;
|
||||
|
||||
int simdata_to_lua(lua_State *L, SimData* simdata);
|
||||
int set_led_range_to_color(lua_State *L);
|
||||
int set_led_to_color(lua_State *L);
|
||||
int set_led_range_to_rgb_color(lua_State *L);
|
||||
int set_led_to_rgb_color(lua_State *L);
|
||||
int led_clear_all(lua_State *L);
|
||||
int led_clear_range(lua_State *L);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "moza.h"
|
||||
#include "../serialadapter.h"
|
||||
#include "../../slog/slog.h"
|
||||
|
||||
#define MOZA_TIMEOUT 1000
|
||||
#define MOZA_SERIAL_TEMPLATE {0x7e, 0x06, 0x41, 0x13, 0xfd, 0xde, 0x0, 0x0, 0x0, 0x0, 0x0}
|
||||
#define MOZA_NUM_AVAILABLE_LEDS 10
|
||||
#define MOZA_BLINKING_BIT 7
|
||||
#define MOZA_PAYLOAD_SIZE 11
|
||||
#define MOZA_MAGIC_VALUE 0x0d
|
||||
#define BIT(nr) (1UL << (nr))
|
||||
|
||||
unsigned char moza_checksum(unsigned char *data, unsigned short size)
|
||||
{
|
||||
unsigned int ret = MOZA_MAGIC_VALUE;
|
||||
for (unsigned short i = 0; i < size; i++)
|
||||
{
|
||||
ret += data[i];
|
||||
}
|
||||
|
||||
return ret % 0x100;
|
||||
}
|
||||
|
||||
int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short maxrpm)
|
||||
{
|
||||
unsigned char bytes[] = MOZA_SERIAL_TEMPLATE;
|
||||
unsigned short size = MOZA_PAYLOAD_SIZE;
|
||||
float perctflt = ((float)rpm/(float)maxrpm)*100;
|
||||
int perct = round(perctflt);
|
||||
|
||||
if (perct >= 10)
|
||||
bytes[9] |= BIT(0);
|
||||
|
||||
if (perct >= 20)
|
||||
bytes[9] |= BIT(1);
|
||||
|
||||
if (perct >= 30)
|
||||
bytes[9] |= BIT(2);
|
||||
|
||||
if (perct >= 40)
|
||||
bytes[9] |= BIT(3);
|
||||
|
||||
if (perct >= 50)
|
||||
bytes[9] |= BIT(4);
|
||||
|
||||
if (perct >= 60)
|
||||
bytes[9] |= BIT(5);
|
||||
|
||||
if (perct >= 70)
|
||||
bytes[9] |= BIT(6);
|
||||
|
||||
if (perct >= 80)
|
||||
bytes[9] |= BIT(7);
|
||||
|
||||
if (perct >= 90)
|
||||
bytes[8] |= BIT(0);
|
||||
|
||||
if (perct >= 92)
|
||||
bytes[8] |= BIT(1);
|
||||
|
||||
// blinking
|
||||
if (perct >= 94)
|
||||
bytes[8] |= BIT(MOZA_BLINKING_BIT);
|
||||
|
||||
bytes[10] = moza_checksum(bytes, size);
|
||||
|
||||
int result = 1;
|
||||
if (serialdevice->port)
|
||||
{
|
||||
slogd("copying %i bytes to moza device", MOZA_PAYLOAD_SIZE);
|
||||
slogt("writing bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x from rpm %i maxrpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], rpm, maxrpm);
|
||||
result = monocoque_serial_write(serialdevice->id, bytes, size, MOZA_TIMEOUT);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int moza_init(SerialDevice* serialdevice, const char* portdev)
|
||||
{
|
||||
int id = monocoque_serial_open(serialdevice, portdev);
|
||||
if (id < 0) return id;
|
||||
serialdevice->id = id;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _MOZA_H
|
||||
#define _MOZA_H
|
||||
|
||||
#include "../serialdevice.h"
|
||||
#include "../simdevice.h"
|
||||
|
||||
int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short maxrpm);
|
||||
int moza_init(SerialDevice* serialdevice, const char* portdev);
|
||||
unsigned char moza_checksum(unsigned char *data, unsigned short size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,142 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "moza.h"
|
||||
#include "moza_ks_pro_wheel.h"
|
||||
#include "../serialadapter.h"
|
||||
#include "../../slog/slog.h"
|
||||
|
||||
#define MOZA_TIMEOUT 1000
|
||||
#define MOZA_MAGIC_VALUE 0x0d
|
||||
#define MOZA_RPM_MASK_TEMPLATE {0x7e, 0x06, 0x3f, 0x17, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
|
||||
#define MOZA_RPM_MASK_PAYLOAD_SIZE 11
|
||||
#define MOZA_RPM_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0xff, 0, 0, 4, 0xff, 0, 0, 0}
|
||||
#define MOZA_RPM_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 5, 0xff, 0, 0, 6, 0xff, 0, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
|
||||
#define MOZA_RPM_COLOR_PAYLOAD_3 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 10, 0xff, 0x7f, 0, 11, 0xff, 0x7f, 0, 12, 0xff, 0x7f, 0, 13, 0, 0, 0xff, 14, 0, 0, 0xff, 0}
|
||||
#define MOZA_BTN_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 0, 0xff, 0, 0, 1, 0xff, 0, 0, 2, 0xff, 0, 0, 3, 0xff, 0, 0, 4, 0xff, 0, 0, 0}
|
||||
#define MOZA_BTN_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 5, 0xff, 0, 0, 6, 0xff, 0, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
|
||||
#define MOZA_COLOR_PAYLOAD_SIZE 27
|
||||
#define MOZA_FLAG_COLOR_YELLOW_LEFT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0xff, 0xaa, 0, 1, 0xff, 0xaa, 0, 2, 0xff, 0xaa, 0, 0, 0xff, 0xaa, 0, 1, 0xff, 0xaa, 0, 0}
|
||||
#define MOZA_FLAG_COLOR_YELLOW_RIGHT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 15, 0xff, 0xaa, 0, 16, 0xff, 0xaa, 0, 17, 0xff, 0xaa, 0, 15, 0xff, 0xaa, 0, 16, 0xff, 0xaa, 0, 0}
|
||||
#define MOZA_FLAG_COLOR_OFF_LEFT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}
|
||||
#define MOZA_FLAG_COLOR_OFF_RIGHT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 15, 0, 0, 0, 16, 0, 0, 0, 17, 0, 0, 0, 15, 0, 0, 0, 16, 0, 0, 0, 0}
|
||||
#define BIT(nr) (1UL << (nr))
|
||||
|
||||
int moza_ks_pro_wheel_update(SerialDevice* serialdevice, SimData* simData)
|
||||
{
|
||||
static uint8_t last_flag = 0xff;
|
||||
unsigned char bytes[] = MOZA_RPM_MASK_TEMPLATE;
|
||||
int size = MOZA_RPM_MASK_PAYLOAD_SIZE;
|
||||
float perctflt = ((float)simData->rpms/(float)simData->maxrpm)*100;
|
||||
int perct = round(perctflt);
|
||||
if (perct >= 98 && (simData->mtick >> 7) & 1 == 1) perct = 0;
|
||||
|
||||
|
||||
if (perct >= 75)
|
||||
bytes[6] |= BIT(3);
|
||||
|
||||
if (perct >= 77)
|
||||
bytes[6] |= BIT(4);
|
||||
|
||||
if (perct >= 79)
|
||||
bytes[6] |= BIT(5);
|
||||
|
||||
if (perct >= 81)
|
||||
bytes[6] |= BIT(6);
|
||||
|
||||
if (perct >= 83)
|
||||
bytes[6] |= BIT(7);
|
||||
|
||||
if (perct >= 85)
|
||||
bytes[7] |= BIT(0);
|
||||
|
||||
if (perct >= 87)
|
||||
bytes[7] |= BIT(1);
|
||||
|
||||
if (perct >= 89)
|
||||
bytes[7] |= BIT(2);
|
||||
|
||||
if (perct >= 91)
|
||||
bytes[7] |= BIT(3);
|
||||
|
||||
if (perct >= 93)
|
||||
bytes[7] |= BIT(4);
|
||||
|
||||
if (perct >= 95)
|
||||
bytes[7] |= BIT(5);
|
||||
|
||||
if (perct >= 97)
|
||||
bytes[7] |= BIT(6);
|
||||
|
||||
|
||||
if (simData->playerflag == SIMAPI_FLAG_YELLOW) {
|
||||
bytes[6] |= BIT(0) | BIT(1) | BIT(2); // left 3 flag LEDs (indices 0, 1, 2)
|
||||
bytes[7] |= BIT(7); // right flag LED (index 15)
|
||||
bytes[8] |= BIT(0) | BIT(1); // right flag LEDs (indices 16, 17)
|
||||
}
|
||||
|
||||
bytes[10] = moza_checksum(bytes, size);
|
||||
|
||||
int result = 1;
|
||||
if (serialdevice->port)
|
||||
{
|
||||
if (simData->playerflag != last_flag) {
|
||||
last_flag = simData->playerflag;
|
||||
if (simData->playerflag == SIMAPI_FLAG_YELLOW) {
|
||||
unsigned char fl[] = MOZA_FLAG_COLOR_YELLOW_LEFT;
|
||||
unsigned char fr[] = MOZA_FLAG_COLOR_YELLOW_RIGHT;
|
||||
fl[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fl, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
fr[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fr, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
monocoque_serial_write(serialdevice->id, fl, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
monocoque_serial_write(serialdevice->id, fr, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
} else {
|
||||
unsigned char fl[] = MOZA_FLAG_COLOR_OFF_LEFT;
|
||||
unsigned char fr[] = MOZA_FLAG_COLOR_OFF_RIGHT;
|
||||
fl[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fl, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
fr[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fr, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
monocoque_serial_write(serialdevice->id, fl, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
monocoque_serial_write(serialdevice->id, fr, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
slogd("copying %i bytes to moza device", MOZA_RPM_MASK_PAYLOAD_SIZE);
|
||||
slogt("writing bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x from rpm %i maxrpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], simData->rpms, simData->maxrpm);
|
||||
result = monocoque_serial_write(serialdevice->id, bytes, size, MOZA_TIMEOUT);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int moza_ks_pro_wheel_init(SerialDevice* serialdevice, const char* portdev)
|
||||
{
|
||||
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
|
||||
if (serialdevice->id == -1) return serialdevice->id;
|
||||
|
||||
unsigned char p1[] = MOZA_RPM_COLOR_PAYLOAD_1;
|
||||
unsigned char p2[] = MOZA_RPM_COLOR_PAYLOAD_2;
|
||||
unsigned char p3[] = MOZA_RPM_COLOR_PAYLOAD_3;
|
||||
unsigned char p4[] = MOZA_BTN_COLOR_PAYLOAD_1;
|
||||
unsigned char p5[] = MOZA_BTN_COLOR_PAYLOAD_2;
|
||||
|
||||
p1[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p1, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
p2[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p2, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
p3[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p3, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
p4[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p4, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
p5[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p5, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
|
||||
monocoque_serial_write(serialdevice->id, p1, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
monocoque_serial_write(serialdevice->id, p2, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
monocoque_serial_write(serialdevice->id, p3, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
monocoque_serial_write(serialdevice->id, p4, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
monocoque_serial_write(serialdevice->id, p5, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
|
||||
unsigned char p6[] = MOZA_FLAG_COLOR_OFF_RIGHT;
|
||||
p6[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p6, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
monocoque_serial_write(serialdevice->id, p6, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
|
||||
return serialdevice->id;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef _MOZA_KS_PRO_WHEEL_H
|
||||
#define _MOZA_KS_PRO_WHEEL_H
|
||||
|
||||
#include "../serialdevice.h"
|
||||
#include "../simdevice.h"
|
||||
|
||||
int moza_ks_pro_wheel_update(SerialDevice* serialdevice, SimData* simData);
|
||||
int moza_ks_pro_wheel_init(SerialDevice* serialdevice, const char* portdev);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "moza.h"
|
||||
#include "moza_new.h"
|
||||
#include "../serialadapter.h"
|
||||
#include "../../slog/slog.h"
|
||||
|
||||
#define MOZA_TIMEOUT 1000
|
||||
#define MOZA_MAGIC_VALUE 0x0d
|
||||
#define MOZA_RPM_MASK_TEMPLATE {0x7e, 0x06, 0x3f, 0x17, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
|
||||
#define MOZA_RPM_MASK_PAYLOAD_SIZE 11
|
||||
#define MOZA_RPM_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0, 0xff, 0, 1, 0, 0xff, 0, 2, 0, 0xff, 0, 3, 0xff, 0x7f, 0, 4, 0xff, 0x7f, 0, 0}
|
||||
#define MOZA_RPM_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 5, 0xff, 0x7f, 0, 6, 0xff, 0x7f, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
|
||||
#define MOZA_BTN_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 0, 0xff, 0, 0, 1, 0xff, 0, 0, 2, 0xff, 0, 0, 3, 0xff, 0, 0, 4, 0xff, 0, 0, 0}
|
||||
#define MOZA_BTN_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 5, 0xff, 0, 0, 6, 0xff, 0, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
|
||||
#define MOZA_COLOR_PAYLOAD_SIZE 27
|
||||
#define BIT(nr) (1UL << (nr))
|
||||
|
||||
int moza_new_update(SerialDevice* serialdevice, SimData* simData)
|
||||
{
|
||||
unsigned char bytes[] = MOZA_RPM_MASK_TEMPLATE;
|
||||
int size = MOZA_RPM_MASK_PAYLOAD_SIZE;
|
||||
float perctflt = ((float)simData->rpms/(float)simData->maxrpm)*100;
|
||||
int perct = round(perctflt);
|
||||
if (perct >= 98 && (simData->mtick >> 7) & 1 == 1) perct = 0;
|
||||
|
||||
if (perct >= 75)
|
||||
bytes[6] |= BIT(0);
|
||||
|
||||
if (perct >= 79)
|
||||
bytes[6] |= BIT(1);
|
||||
|
||||
if (perct >= 82)
|
||||
bytes[6] |= BIT(2);
|
||||
|
||||
if (perct >= 85)
|
||||
bytes[6] |= BIT(3);
|
||||
|
||||
if (perct >= 87)
|
||||
bytes[6] |= BIT(4);
|
||||
|
||||
if (perct >= 88)
|
||||
bytes[6] |= BIT(5);
|
||||
|
||||
if (perct >= 89)
|
||||
bytes[6] |= BIT(6);
|
||||
|
||||
if (perct >= 90)
|
||||
bytes[6] |= BIT(7);
|
||||
|
||||
if (perct >= 92)
|
||||
bytes[7] |= BIT(0);
|
||||
|
||||
if (perct >= 94)
|
||||
bytes[7] |= BIT(1);
|
||||
|
||||
bytes[10] = moza_checksum(bytes, size);
|
||||
|
||||
int result = 1;
|
||||
if (serialdevice->port)
|
||||
{
|
||||
slogd("copying %i bytes to moza device", MOZA_RPM_MASK_PAYLOAD_SIZE);
|
||||
slogt("writing bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x from rpm %i maxrpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], simData->rpms, simData->maxrpm);
|
||||
result = monocoque_serial_write(serialdevice->id, bytes, size, MOZA_TIMEOUT);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int moza_new_init(SerialDevice* serialdevice, const char* portdev)
|
||||
{
|
||||
int id = monocoque_serial_open(serialdevice, portdev);
|
||||
if (id < 0) return id;
|
||||
serialdevice->id = id;
|
||||
|
||||
unsigned char p1[] = MOZA_RPM_COLOR_PAYLOAD_1;
|
||||
unsigned char p2[] = MOZA_RPM_COLOR_PAYLOAD_2;
|
||||
unsigned char p3[] = MOZA_BTN_COLOR_PAYLOAD_1;
|
||||
unsigned char p4[] = MOZA_BTN_COLOR_PAYLOAD_2;
|
||||
|
||||
p1[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p1, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
p2[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p2, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
p3[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p3, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
p4[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p4, MOZA_COLOR_PAYLOAD_SIZE);
|
||||
|
||||
unsigned char* payloads[] = {p1, p2, p3, p4};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int result = monocoque_serial_write(serialdevice->id, payloads[i], MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
|
||||
if (result < 0) return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef _MOZA_NEW_H
|
||||
#define _MOZA_NEW_H
|
||||
|
||||
#include "../serialdevice.h"
|
||||
#include "../simdevice.h"
|
||||
|
||||
int moza_new_update(SerialDevice* serialdevice, SimData* simData);
|
||||
int moza_new_init(SerialDevice* serialdevice, const char* portdev);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,280 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "serialadapter.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
int msastrcicmp(char const *a, char const *b)
|
||||
{
|
||||
for (;; a++, b++) {
|
||||
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
if (d != 0 || !*a)
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
int check(enum sp_return result)
|
||||
{
|
||||
char* error_message;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case SP_ERR_ARG:
|
||||
return 1;
|
||||
case SP_ERR_FAIL:
|
||||
error_message = sp_last_error_message();
|
||||
sloge("error: serial write failed: %s", error_message);
|
||||
sp_free_error_message(error_message);
|
||||
return result;
|
||||
case SP_ERR_SUPP:
|
||||
printf("Error: Not supported.\n");
|
||||
return result;
|
||||
case SP_ERR_MEM:
|
||||
printf("Error: Couldn't allocate memory.\n");
|
||||
return result;
|
||||
case SP_OK:
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int monocoque_serial_free(SerialDevice* serialdevice)
|
||||
{
|
||||
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevice->id];
|
||||
|
||||
if(monocoque_serial_dev.open == true)
|
||||
{
|
||||
while(monocoque_serial_dev.busy == true)
|
||||
{
|
||||
slogt("hopefully this doesn't happen long");
|
||||
continue;
|
||||
}
|
||||
|
||||
monocoque_serial_devices[serialdevice->id].busy = true;
|
||||
monocoque_serial_devices[serialdevice->id].refs--;
|
||||
|
||||
if(monocoque_serial_devices[serialdevice->id].refs == 0)
|
||||
{
|
||||
slogd("freeing physical device %s", monocoque_serial_devices[serialdevice->id].portname);
|
||||
sp_close(monocoque_serial_devices[serialdevice->id].port);
|
||||
sp_free_port(monocoque_serial_devices[serialdevice->id].port);
|
||||
free(monocoque_serial_devices[serialdevice->id].portname);
|
||||
|
||||
monocoque_serial_devices[serialdevice->id].port = NULL;
|
||||
monocoque_serial_devices[serialdevice->id].portname = NULL;
|
||||
monocoque_serial_devices[serialdevice->id].busy = false;
|
||||
monocoque_serial_devices[serialdevice->id].open = false;
|
||||
monocoque_serial_devices[serialdevice->id].openfail = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
monocoque_serial_devices[serialdevice->id].busy = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int monocoque_wait_for_event(uint8_t serialdevicenum, int event)
|
||||
{
|
||||
|
||||
slogt("serial device id %i", serialdevicenum);
|
||||
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
|
||||
|
||||
int retval;
|
||||
struct sp_event_set* eventSet = NULL;
|
||||
|
||||
retval = sp_new_event_set(&eventSet);
|
||||
if (retval == SP_OK)
|
||||
{
|
||||
retval = sp_add_port_events(eventSet, monocoque_serial_dev.port, event);
|
||||
if (retval == SP_OK)
|
||||
{
|
||||
slogd("set event on port");
|
||||
retval = sp_wait(eventSet, 5000);
|
||||
}
|
||||
else
|
||||
{
|
||||
sloge("Unable to add events to port.");
|
||||
retval = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sloge("Unable to create new event set.");
|
||||
retval = -1;
|
||||
}
|
||||
sp_free_event_set(eventSet);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int monocoque_input_wait(uint8_t serialdevicenum)
|
||||
{
|
||||
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
|
||||
return sp_input_waiting(monocoque_serial_dev.port);
|
||||
}
|
||||
|
||||
// Helper function to get and validate device
|
||||
static monocoque_serial_device* monocoque_get_serial_device(uint8_t serialdevicenum)
|
||||
{
|
||||
monocoque_serial_device* dev = &monocoque_serial_devices[serialdevicenum];
|
||||
slogt("serial device id %i", serialdevicenum);
|
||||
slogt("port name: %s, busy %i, open %i, openfail %i", dev->portname, dev->busy, dev->open, dev->openfail);
|
||||
|
||||
if(dev->port == NULL)
|
||||
{
|
||||
sloge("port is null");
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout)
|
||||
{
|
||||
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
|
||||
|
||||
int result = -1;
|
||||
if(dev->busy == false && dev->open == true)
|
||||
{
|
||||
dev->busy = true;
|
||||
result = sp_blocking_write(dev->port, data, size, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
slogw("serial device data update ignored due to busy or lost device");
|
||||
result = -1;
|
||||
}
|
||||
slogt("write result is %i", result);
|
||||
dev->busy = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
|
||||
{
|
||||
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
|
||||
|
||||
int result = -1;
|
||||
if(dev->open == true)
|
||||
{
|
||||
while(dev->busy == true)
|
||||
{
|
||||
slogt("hopefully this doesn't happen long");
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->busy = true;
|
||||
result = sp_blocking_write(dev->port, data, size, timeout);
|
||||
slogi("actually performed write");
|
||||
}
|
||||
|
||||
dev->busy = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
|
||||
{
|
||||
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
|
||||
|
||||
int result = -1;
|
||||
if(dev->open == true)
|
||||
{
|
||||
while(dev->busy == true)
|
||||
{
|
||||
slogt("hopefully this doesn't happen long");
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->busy = true;
|
||||
result = sp_blocking_read(dev->port, data, size, timeout);
|
||||
slogi("actually performed read");
|
||||
}
|
||||
|
||||
dev->busy = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
int monocoque_serial_open(SerialDevice* serialdevice, const char* portdev)
|
||||
{
|
||||
int serial_device_num = -1;
|
||||
bool notfound = true;
|
||||
slogi("looking to open physical serialdevice %s", portdev);
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
if(monocoque_serial_devices[i].open == true && monocoque_serial_devices[i].openfail == false)
|
||||
{
|
||||
if(msastrcicmp(monocoque_serial_devices[i].portname, portdev) == 0)
|
||||
{
|
||||
notfound = false;
|
||||
serial_device_num = i;
|
||||
monocoque_serial_devices[i].refs++;
|
||||
slogd("found exisiting handle to serial device %s", portdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(notfound == true)
|
||||
{
|
||||
slogd("no existing connections found, looking to create new");
|
||||
int i = -1;
|
||||
bool avail = false;
|
||||
while(avail == false)
|
||||
{
|
||||
i++;
|
||||
if(monocoque_serial_devices[i].open == false && monocoque_serial_devices[i].openfail == false)
|
||||
{
|
||||
avail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
slogi("opening physical serial device...");
|
||||
int error = 0;
|
||||
char* port_name = strdup(portdev);
|
||||
monocoque_serial_devices[i].portname = strdup(port_name);
|
||||
|
||||
struct sp_port* sp;
|
||||
slogd("Looking for port %s", port_name);
|
||||
error = check(sp_get_port_by_name(port_name, &sp));
|
||||
if (error != 0)
|
||||
{
|
||||
sloge("Error opening serial port");
|
||||
free(port_name);
|
||||
free(monocoque_serial_devices[i].portname);
|
||||
monocoque_serial_devices[i].portname = NULL;
|
||||
monocoque_serial_devices[i].open = false;
|
||||
monocoque_serial_devices[i].openfail = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
slogd("Opening port");
|
||||
check(sp_open(sp, SP_MODE_READ | SP_MODE_WRITE));
|
||||
|
||||
slogd("Setting port to %i 8N1, no flow control", serialdevice->baudrate);
|
||||
check(sp_set_baudrate(sp, serialdevice->baudrate));
|
||||
check(sp_set_bits(sp, 8));
|
||||
check(sp_set_parity(sp, SP_PARITY_NONE));
|
||||
check(sp_set_stopbits(sp, 1));
|
||||
check(sp_set_flowcontrol(sp, SP_FLOWCONTROL_NONE));
|
||||
|
||||
check(sp_set_rts(sp, 1));
|
||||
check(sp_set_dtr(sp, 1));
|
||||
|
||||
monocoque_serial_devices[i].port = sp;
|
||||
|
||||
monocoque_serial_devices[i].open = true;
|
||||
monocoque_serial_devices[i].openfail = false;
|
||||
monocoque_serial_devices[i].busy = false;
|
||||
monocoque_serial_devices[i].refs++;
|
||||
|
||||
serial_device_num = i;
|
||||
|
||||
free(port_name);
|
||||
slogd("Successfully setup monocoque serial device...");
|
||||
}
|
||||
|
||||
return serial_device_num;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef _SERIALADAPTER_H
|
||||
#define _SERIALADAPTER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <libserialport.h>
|
||||
|
||||
#include "simdevice.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* portname;
|
||||
struct sp_port* port;
|
||||
uint8_t refs;
|
||||
bool open;
|
||||
bool openfail;
|
||||
bool busy;
|
||||
}
|
||||
monocoque_serial_device;
|
||||
|
||||
static monocoque_serial_device monocoque_serial_devices[20];
|
||||
|
||||
int monocoque_input_wait(uint8_t serialdevicenum);
|
||||
int monocoque_wait_for_event(uint8_t serialdevicenum, int event);
|
||||
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout);
|
||||
int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout);
|
||||
int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout);
|
||||
int monocoque_serial_open(SerialDevice* serialdevice, const char* port);
|
||||
int monocoque_serial_free(SerialDevice* serialdevice);
|
||||
|
||||
#endif
|
||||
@@ -6,45 +6,291 @@
|
||||
|
||||
#include "simdevice.h"
|
||||
#include "serialdevice.h"
|
||||
#include "serialadapter.h"
|
||||
#include "hapticeffect.h"
|
||||
#include "serial/arduino.h"
|
||||
#include "serial/moza.h"
|
||||
#include "serial/moza_new.h"
|
||||
#include "serial/moza_ks_pro_wheel.h"
|
||||
#include "../helper/parameters.h"
|
||||
#include "../simulatorapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
#define KPHTOMPH .621317
|
||||
|
||||
int serial_wheel_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
|
||||
switch (serialdevice->devicetype) {
|
||||
case SIMDEVSUBTYPE_MOZA_NEW:
|
||||
moza_new_update(serialdevice, simdata);
|
||||
break;
|
||||
|
||||
case SERIALDEV__MOZA_KS_PRO_WHEEL:
|
||||
moza_ks_pro_wheel_update(serialdevice, simdata);
|
||||
break;
|
||||
|
||||
case SIMDEVSUBTYPE_MOZAR5:
|
||||
default:
|
||||
moza_update(serialdevice, simdata->rpms, simdata->maxrpm);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serialdev_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
|
||||
arduino_update(serialdevice, simdata);
|
||||
arduino_update(serialdevice, simdata, sizeof(SimData));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arduino_custom_updater(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
|
||||
arduino_custom_update(serialdevice, simdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arduino_customled_updater(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
arduino_customled_update(serialdevice, simdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arduino_simled_updater(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
|
||||
arduino_simled_update(serialdevice, simdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arduino_shiftlights_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
int result = 1;
|
||||
|
||||
|
||||
int num_avail_leds = serialdevice->numlights;
|
||||
int rpm = simdata->rpms;
|
||||
int maxrpm = simdata->maxrpm;
|
||||
int litleds = 0;
|
||||
if(rpm > 0 && maxrpm > 0)
|
||||
{
|
||||
int rpmmargin = ceil(.05*maxrpm);
|
||||
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds);
|
||||
|
||||
|
||||
for (int l = 1; l <= (num_avail_leds); l++)
|
||||
{
|
||||
if(rpm >= (rpminterval * l))
|
||||
{
|
||||
litleds = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serialdevice->u.shiftlightsdata.litleds = litleds;
|
||||
//serialdevice->u.shiftlightsdata.rpm = simdata->rpms;
|
||||
slogt("Updating arduino device lights to %i", serialdevice->u.shiftlightsdata.litleds);
|
||||
// we can add configs to set all the colors
|
||||
// i can move the size to the initialization since it should not change
|
||||
size_t size = sizeof(ShiftLightsData);
|
||||
|
||||
arduino_update(serialdevice, &serialdevice->u.shiftlightsdata, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int arduino_simwind_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
int result = 1;
|
||||
|
||||
// sending over as mph, when you consider 255mph to be a reasonable upper limit
|
||||
serialdevice->u.simwinddata.velocity = ceil(simdata->velocity*KPHTOMPH);
|
||||
|
||||
slogt("Updating arduino device speed to %i", serialdevice->u.simwinddata.velocity);
|
||||
|
||||
serialdevice->u.simwinddata.fanpower = (int)(serialdevice->fanpower * 255);
|
||||
slogt("Sending fanpower: %i (from config: %f)", serialdevice->u.simwinddata.fanpower, serialdevice->fanpower);
|
||||
size_t size = sizeof(SimWindData);
|
||||
|
||||
arduino_update(serialdevice, &serialdevice->u.simwinddata, size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int arduino_simhaptic_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
|
||||
int result = 1;
|
||||
|
||||
slogt("arduino haptic device updating");
|
||||
serialdevice->u.simhapticdata.motor1 = 0;
|
||||
serialdevice->u.simhapticdata.motor2 = 0;
|
||||
serialdevice->u.simhapticdata.motor3 = 0;
|
||||
serialdevice->u.simhapticdata.motor4 = 0;
|
||||
|
||||
double play = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
|
||||
|
||||
double rplay = play;
|
||||
play = play * serialdevice->ampfactor;
|
||||
if(play > 1.0)
|
||||
{
|
||||
play = 1.0;
|
||||
}
|
||||
uint8_t effectspeed = ceil(255 * play);
|
||||
|
||||
uint8_t motor = this->hapticeffect.motorposition;
|
||||
|
||||
if (play != serialdevice->hapticstate)
|
||||
{
|
||||
// motor 1
|
||||
if (motor == 0 || motor == 4 || motor == 7 || motor == 8 || motor == 10 || motor == 11 || motor == 13 || motor == 14)
|
||||
{
|
||||
serialdevice->u.simhapticdata.effect1 = effectspeed;
|
||||
serialdevice->u.simhapticdata.motor1 = 1;
|
||||
slogt("Updating arduino haptic device with effect type %i speed motor speed %i on motor %i from original effect %f with ampfactor %f", this->hapticeffect.effecttype, serialdevice->u.simhapticdata.effect1, 1, rplay, serialdevice->ampfactor);
|
||||
}
|
||||
// motor 3
|
||||
if (motor == 2 || motor == 6 || motor == 8 || motor == 9 || motor == 10 || motor == 11 || motor == 12 || motor == 14)
|
||||
{
|
||||
serialdevice->u.simhapticdata.effect3 = effectspeed;
|
||||
serialdevice->u.simhapticdata.motor3 = 1;
|
||||
slogt("Updating arduino haptic device with effect type %i speed motor speed %i on motor %i from original effect %f with ampfactor %f", this->hapticeffect.effecttype, serialdevice->u.simhapticdata.effect3, 3, rplay, serialdevice->ampfactor);
|
||||
}
|
||||
serialdevice->hapticstate = play;
|
||||
}
|
||||
|
||||
size_t size = sizeof(SimHapticData);
|
||||
|
||||
arduino_update(serialdevice, &serialdevice->u.simhapticdata, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
int serialdev_free(SimDevice* this)
|
||||
{
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
|
||||
arduino_free(serialdevice);
|
||||
switch (serialdevice->devicetype) {
|
||||
case (ARDUINODEV__HAPTIC):
|
||||
|
||||
serialdevice->u.simhapticdata.effect1 = 0;
|
||||
serialdevice->u.simhapticdata.motor1 = 1;
|
||||
serialdevice->u.simhapticdata.effect2 = 0;
|
||||
serialdevice->u.simhapticdata.motor2 = 1;
|
||||
serialdevice->u.simhapticdata.effect3 = 0;
|
||||
serialdevice->u.simhapticdata.motor3 = 1;
|
||||
serialdevice->u.simhapticdata.effect4 = 0;
|
||||
serialdevice->u.simhapticdata.motor4 = 1;
|
||||
serialdevice->hapticstate = 0;
|
||||
|
||||
size_t size = sizeof(SimHapticData);
|
||||
monocoque_serial_write_block(serialdevice->id, &serialdevice->u.simhapticdata, size, 9000);
|
||||
slogi("set zero to arduino device");
|
||||
break;
|
||||
case ARDUINODEV__SIMLED__CUSTOM:
|
||||
//arduino_customled_free(serialdevice, true);
|
||||
free(serialdevice->m.device_specific_config_file);
|
||||
break;
|
||||
case ARDUINODEV__SIMLED:
|
||||
arduino_customled_free(serialdevice, false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
monocoque_serial_free(serialdevice);
|
||||
|
||||
free(serialdevice);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serialdev_init(SerialDevice* serialdevice, const char* portdev)
|
||||
int serial_wheel_free(SimDevice* this)
|
||||
{
|
||||
slogi("initializing serial device...");
|
||||
SerialDevice* serialdevice = (void *) this->derived;
|
||||
|
||||
monocoque_serial_free(serialdevice);
|
||||
|
||||
free(serialdevice);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds, SimInfo* siminfo)
|
||||
{
|
||||
slogi("initializing serial device on port %s to %i...", ds->dev, ds->serialdevsettings.baud);
|
||||
int error = 0;
|
||||
|
||||
serialdevice->type = SERIALDEV_UNKNOWN;
|
||||
serialdevice->type = SERIALDEV_ARDUINO;
|
||||
|
||||
error = arduino_init(serialdevice, portdev);
|
||||
serialdevice->motorsposition = ds->serialdevsettings.motorsposition;
|
||||
serialdevice->baudrate = ds->serialdevsettings.baud;
|
||||
|
||||
switch (serialdevice->devicetype)
|
||||
{
|
||||
case SERIALDEV__MOZAR5:
|
||||
// the wheel stuff assumed it was a usb
|
||||
//error = wheeldev_init(&serialdevice->u.wheeldevice, ds);
|
||||
// maybe this call a more generic serial wheel init first
|
||||
error = moza_init(serialdevice, ds->dev);
|
||||
break;
|
||||
case SERIALDEV__MOZA_NEW:
|
||||
error = moza_new_init(serialdevice, ds->dev);
|
||||
break;
|
||||
case SERIALDEV__MOZA_KS_PRO_WHEEL:
|
||||
error = moza_ks_pro_wheel_init(serialdevice, ds->dev);
|
||||
break;
|
||||
case ARDUINODEV__SIMLED__CUSTOM:
|
||||
serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
|
||||
error = arduino_custom_init(serialdevice, ds->dev, serialdevice->m.device_specific_config_file, true);
|
||||
if(error < 0)
|
||||
{
|
||||
free(serialdevice->m.device_specific_config_file);
|
||||
}
|
||||
break;
|
||||
case ARDUINODEV__CUSTOM:
|
||||
serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
|
||||
error = arduino_custom_init(serialdevice, ds->dev, serialdevice->m.device_specific_config_file, false);
|
||||
if(error < 0)
|
||||
{
|
||||
free(serialdevice->m.device_specific_config_file);
|
||||
}
|
||||
break;
|
||||
case ARDUINODEV__SIMLED:
|
||||
error = arduino_custom_init(serialdevice, ds->dev, NULL, false);
|
||||
break;
|
||||
default:
|
||||
error = arduino_init(serialdevice, ds->dev);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static const vtable serial_simdevice_vtable = { &serialdev_update, &serialdev_free };
|
||||
static const vtable arduino_shiftlights_vtable = { &arduino_shiftlights_update, &serialdev_free };
|
||||
static const vtable arduino_simled_vtable = { &arduino_simled_updater, &serialdev_free };
|
||||
static const vtable arduino_custom_vtable = { &arduino_custom_updater, &serialdev_free };
|
||||
static const vtable arduino_simled_custom_vtable = { &arduino_customled_updater, &serialdev_free };
|
||||
static const vtable arduino_simwind_vtable = { &arduino_simwind_update, &serialdev_free };
|
||||
static const vtable arduino_simhaptic_vtable = { &arduino_simhaptic_update, &serialdev_free };
|
||||
static const vtable serialwheel_vtable = { &serial_wheel_update, &serial_wheel_free };
|
||||
|
||||
SerialDevice* new_serial_device(DeviceSettings* ds) {
|
||||
SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
|
||||
|
||||
SerialDevice* this = (SerialDevice*) malloc(sizeof(SerialDevice));
|
||||
|
||||
@@ -52,11 +298,113 @@ SerialDevice* new_serial_device(DeviceSettings* ds) {
|
||||
this->m.free = &simdevfree;
|
||||
this->m.derived = this;
|
||||
this->m.vtable = &serial_simdevice_vtable;
|
||||
this->type = SERIALDEV_ARDUINO;
|
||||
int error = 0;
|
||||
|
||||
int error = serialdev_init(this, ds->serialdevsettings.portdev);
|
||||
slogt("Attempting to configure arduino device with subtype: %i", ds->dev_subtype);
|
||||
switch (ds->dev_subtype) {
|
||||
case (SIMDEVTYPE_SHIFTLIGHTS):
|
||||
this->devicetype = ARDUINODEV__SHIFTLIGHTS;
|
||||
this->numlights = ds->serialdevsettings.numlights;
|
||||
this->m.vtable = &arduino_shiftlights_vtable;
|
||||
slogi("Initializing arduino device for shiftlights.");
|
||||
break;
|
||||
case (SIMDEVTYPE_SIMLED):
|
||||
|
||||
if(ds->has_config == true)
|
||||
{
|
||||
this->devicetype = ARDUINODEV__SIMLED__CUSTOM;
|
||||
this->startled = ds->serialdevsettings.startled;
|
||||
this->endled = ds->serialdevsettings.endled;
|
||||
this->m.vtable = &arduino_simled_custom_vtable;
|
||||
slogi("Initializing arduino device for custom simled.");
|
||||
}
|
||||
else
|
||||
{
|
||||
this->devicetype = ARDUINODEV__SIMLED;
|
||||
this->numleds = ds->serialdevsettings.numleds;
|
||||
this->startled = ds->serialdevsettings.startled;
|
||||
this->endled = ds->serialdevsettings.endled;
|
||||
this->m.vtable = &arduino_simled_vtable;
|
||||
slogi("Initializing arduino device for simled.");
|
||||
}
|
||||
break;
|
||||
case (SIMDEVTYPE_SIMWIND):
|
||||
this->devicetype = ARDUINODEV__SIMWIND;
|
||||
this->m.vtable = &arduino_simwind_vtable;
|
||||
this->fanpower = ds->serialdevsettings.fanpower;
|
||||
slogi("Initializing arduino devices for sim wind with fanpower: %f", this->fanpower);
|
||||
break;
|
||||
case (SIMDEVTYPE_ARDUINOCUSTOM):
|
||||
this->devicetype = ARDUINODEV__CUSTOM;
|
||||
this->m.vtable = &arduino_custom_vtable;
|
||||
slogi("Initializing custom arduino device.");
|
||||
break;
|
||||
case (SIMDEVTYPE_SERIALHAPTIC):
|
||||
if(siminfo->SimSupportsHapticEffects == false)
|
||||
{
|
||||
error = MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
|
||||
}
|
||||
this->devicetype = ARDUINODEV__HAPTIC;
|
||||
this->m.vtable = &arduino_simhaptic_vtable;
|
||||
this->u.simhapticdata.motor1 = 0;
|
||||
this->u.simhapticdata.motor2 = 0;
|
||||
this->u.simhapticdata.motor3 = 0;
|
||||
this->u.simhapticdata.motor4 = 0;
|
||||
this->u.simhapticdata.effect1 = 0;
|
||||
this->u.simhapticdata.effect2 = 0;
|
||||
this->u.simhapticdata.effect3 = 0;
|
||||
this->u.simhapticdata.effect4 = 0;
|
||||
this->hapticstate = 0;
|
||||
this->ampfactor = ds->serialdevsettings.ampfactor;
|
||||
this->fanpower = ds->serialdevsettings.fanpower;
|
||||
slogi("Initializing arduino device for haptic effects.");
|
||||
break;
|
||||
case (SIMDEVTYPE_SERIALWHEEL):
|
||||
this->type = SERIALDEV_WHEEL;
|
||||
|
||||
switch (ds->dev_subsubtype) {
|
||||
|
||||
case SIMDEVSUBTYPE_MOZA_NEW:
|
||||
slogi("Initializing new firmware Moza serial wheel device.");
|
||||
this->devicetype = SERIALDEV__MOZA_NEW;
|
||||
this->m.vtable = &serialwheel_vtable;
|
||||
break;
|
||||
case SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL:
|
||||
slogi("Initializing new firmware Moza serial wheel device.");
|
||||
this->devicetype = SERIALDEV__MOZA_KS_PRO_WHEEL;
|
||||
this->m.vtable = &serialwheel_vtable;
|
||||
break;
|
||||
case SIMDEVSUBTYPE_MOZAR5:
|
||||
default:
|
||||
//move this stuff to wheeldevice or it's own serial wheel device module
|
||||
slogi("Initializing Moza serial wheel device.");
|
||||
this->devicetype = SERIALDEV__MOZAR5;
|
||||
this->m.vtable = &serialwheel_vtable;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(this->devicetype == ARDUINODEV__HAPTIC && error == 0)
|
||||
{
|
||||
this->m.hapticeffect.threshold = ds->hapticsettings.threshold;
|
||||
this->m.hapticeffect.effecttype = ds->hapticsettings.effect_type;
|
||||
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->hapticsettings.effect_type);
|
||||
this->m.hapticeffect.tyre = ds->hapticsettings.tyre;
|
||||
this->m.hapticeffect.useconfig = ms->useconfig;
|
||||
this->m.hapticeffect.configcheck = &ms->configcheck;
|
||||
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
|
||||
}
|
||||
|
||||
if(error == 0)
|
||||
{
|
||||
slogi("Starting serial device initialization");
|
||||
error = serialdev_init(this, ds, siminfo);
|
||||
}
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
slogw("Did not initialize serial device due to error code %i", error);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,20 @@
|
||||
#define _SERIALDEVICE_H
|
||||
|
||||
#include <libserialport.h>
|
||||
#include "wheeldevice.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ARDUINODEV__SHIFTLIGHTS = 0,
|
||||
ARDUINODEV__SIMWIND = 1,
|
||||
ARDUINODEV__HAPTIC = 2,
|
||||
SERIALDEV__MOZAR5 = 3,
|
||||
ARDUINODEV__SIMLED = 4,
|
||||
ARDUINODEV__SIMLED__CUSTOM = 5,
|
||||
ARDUINODEV__CUSTOM = 6,
|
||||
SERIALDEV__MOZA_NEW = 7,
|
||||
SERIALDEV__MOZA_KS_PRO_WHEEL = 8
|
||||
}
|
||||
SerialDeviceType;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "simdevice.h"
|
||||
#include "../helper/parameters.h"
|
||||
#include "../helper/confighelper.h"
|
||||
#include "../simulatorapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
|
||||
@@ -24,29 +24,9 @@ int devupdate(SimDevice* this, SimData* simdata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devfree(SimDevice* simdevices, int numdevices)
|
||||
int devinit(SimDevice* simdevices, SimInfo* siminfo, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms)
|
||||
{
|
||||
|
||||
slogi("freeing %i simdevices...", numdevices);
|
||||
int devices = 0;
|
||||
|
||||
for (int j = 0; j < numdevices; j++)
|
||||
{
|
||||
SimDevice simdev = simdevices[j];
|
||||
if (simdev.initialized == true)
|
||||
{
|
||||
simdev.free(&simdev);
|
||||
}
|
||||
}
|
||||
|
||||
free(simdevices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds)
|
||||
{
|
||||
slogi("initializing simdevices...");
|
||||
slogi("initializing simdevices for simapi %i...", siminfo->simulatorapi);
|
||||
int devices = 0;
|
||||
|
||||
for (int j = 0; j < numdevices; j++)
|
||||
@@ -54,12 +34,13 @@ int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds)
|
||||
simdevices[j].initialized = false;
|
||||
|
||||
if (ds[j].dev_type == SIMDEV_USB) {
|
||||
USBDevice* sim = new_usb_device(&ds[j]);
|
||||
USBDevice* sim = new_usb_device(&ds[j], ms, siminfo);
|
||||
if (sim != NULL)
|
||||
{
|
||||
simdevices[j] = sim->m;
|
||||
simdevices[j].initialized = true;
|
||||
simdevices[j].type = SIMDEV_USB;
|
||||
simdevices[j].fps = ds[j].fps;
|
||||
devices++;
|
||||
}
|
||||
else
|
||||
@@ -69,30 +50,38 @@ int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds)
|
||||
}
|
||||
|
||||
if (ds[j].dev_type == SIMDEV_SOUND) {
|
||||
|
||||
SoundDevice* sim = new_sound_device(&ds[j]);
|
||||
if (sim != NULL)
|
||||
if(ms->disable_audio == true)
|
||||
{
|
||||
|
||||
simdevices[j] = sim->m;
|
||||
simdevices[j].initialized = true;
|
||||
simdevices[j].type = SIMDEV_SOUND;
|
||||
devices++;
|
||||
slogi("skipping configured sound device due to disable_audio being specified...");
|
||||
}
|
||||
else
|
||||
{
|
||||
slogw("Could not initialize Sound Device");
|
||||
SoundDevice* sim = new_sound_device(&ds[j], ms, siminfo);
|
||||
if (sim != NULL)
|
||||
{
|
||||
|
||||
simdevices[j] = sim->m;
|
||||
simdevices[j].initialized = true;
|
||||
simdevices[j].type = SIMDEV_SOUND;
|
||||
simdevices[j].fps = ds[j].fps;
|
||||
devices++;
|
||||
}
|
||||
else
|
||||
{
|
||||
slogw("Could not initialize Sound Device");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ds[j].dev_type == SIMDEV_SERIAL) {
|
||||
|
||||
SerialDevice* sim = new_serial_device(&ds[j]);
|
||||
SerialDevice* sim = new_serial_device(&ds[j], ms, siminfo);
|
||||
if (sim != NULL)
|
||||
{
|
||||
simdevices[j] = sim->m;
|
||||
simdevices[j].initialized = true;
|
||||
simdevices[j].type = SIMDEV_SERIAL;
|
||||
simdevices[j].fps = ds[j].fps;
|
||||
devices++;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,12 +2,22 @@
|
||||
#define _SIMDEVICE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include <lua.h>
|
||||
|
||||
#include "usbdevice.h"
|
||||
#include "sounddevice.h"
|
||||
#include "serialdevice.h"
|
||||
|
||||
#include "hapticeffect.h"
|
||||
|
||||
#include "../helper/confighelper.h"
|
||||
#include "../simulatorapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
|
||||
#include "../../arduino/simwind/simwind.h"
|
||||
#include "../../arduino/simhaptic/simhaptic.h"
|
||||
#include "../../arduino/shiftlights/shiftlights.h"
|
||||
|
||||
typedef struct SimDevice SimDevice;
|
||||
|
||||
@@ -18,9 +28,12 @@ struct SimDevice
|
||||
int (*free)(SimDevice*);
|
||||
void* derived;
|
||||
int id;
|
||||
int fps;
|
||||
bool initialized;
|
||||
lua_State* L;
|
||||
char* device_specific_config_file;
|
||||
DeviceType type;
|
||||
|
||||
HapticEffect hapticeffect;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -32,7 +45,8 @@ typedef struct {
|
||||
typedef enum
|
||||
{
|
||||
SERIALDEV_UNKNOWN = 0,
|
||||
SERIALDEV_ARDUINO = 1
|
||||
SERIALDEV_ARDUINO = 1,
|
||||
SERIALDEV_WHEEL = 2,
|
||||
}
|
||||
SerialType;
|
||||
|
||||
@@ -42,19 +56,42 @@ typedef struct
|
||||
int id;
|
||||
SerialType type;
|
||||
struct sp_port* port;
|
||||
|
||||
SerialDeviceType devicetype;
|
||||
// move these two they only apply to the haptic device
|
||||
int motorsposition;
|
||||
int numlights;
|
||||
int numleds;
|
||||
int startled;
|
||||
int endled;
|
||||
int baudrate;
|
||||
double ampfactor;
|
||||
double fanpower;
|
||||
double hapticstate;
|
||||
union
|
||||
{
|
||||
SimWindData simwinddata;
|
||||
SimHapticData simhapticdata;
|
||||
ShiftLightsData shiftlightsdata;
|
||||
WheelDevice wheeldevice;
|
||||
} u;
|
||||
}
|
||||
SerialDevice;
|
||||
|
||||
int serialdev_update(SimDevice* this, SimData* simdata);
|
||||
int arduino_shiftlights_update(SimDevice* this, SimData* simdata);
|
||||
int arduino_simwind_update(SimDevice* this, SimData* simdata);
|
||||
int arduino_simhaptic_update(SimDevice* this, SimData* simdata);
|
||||
int serialdev_free(SimDevice* this);
|
||||
|
||||
SerialDevice* new_serial_device(DeviceSettings* ds);
|
||||
SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
|
||||
|
||||
/********* USB HID Devices *****/
|
||||
typedef enum
|
||||
{
|
||||
USBDEV_UNKNOWN = 0,
|
||||
USBDEV_TACHOMETER = 1
|
||||
USBDEV_UNKNOWN = 0,
|
||||
USBDEV_TACHOMETER = 1,
|
||||
USBDEV_GENERICHAPTIC = 2,
|
||||
USBDEV_WHEEL_OR_PEDALS = 3
|
||||
}
|
||||
USBType;
|
||||
|
||||
@@ -62,10 +99,15 @@ typedef struct
|
||||
{
|
||||
SimDevice m;
|
||||
int id;
|
||||
hid_device* handle;
|
||||
FILE* filehandle;
|
||||
char* dev;
|
||||
double hapticstate;
|
||||
USBType type;
|
||||
union
|
||||
{
|
||||
TachDevice tachdevice;
|
||||
WheelDevice wheeldevice;
|
||||
} u;
|
||||
}
|
||||
USBDevice;
|
||||
@@ -73,7 +115,7 @@ USBDevice;
|
||||
int usbdev_update(SimDevice* this, SimData* simdata);
|
||||
int usbdev_free(SimDevice* this);
|
||||
|
||||
USBDevice* new_usb_device(DeviceSettings* ds);
|
||||
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
|
||||
|
||||
|
||||
/********* Sound Devices *****/
|
||||
@@ -81,19 +123,23 @@ typedef struct
|
||||
{
|
||||
SimDevice m;
|
||||
int id;
|
||||
SoundType type;
|
||||
VibrationEffectType effecttype;
|
||||
PATestData sounddata;
|
||||
PaStreamParameters outputParameters;
|
||||
PaStream* stream;
|
||||
int configcheck;
|
||||
SoundData sounddata;
|
||||
//#ifdef USE_PULSEAUDIO
|
||||
pa_stream *stream;
|
||||
//#else
|
||||
//PaStreamParameters outputParameters;
|
||||
//PaStream* stream;
|
||||
//#endif
|
||||
}
|
||||
SoundDevice;
|
||||
|
||||
int sounddev_engine_update(SimDevice* this, SimData* simdata);
|
||||
int sounddev_gearshift_update(SimDevice* this, SimData* simdata);
|
||||
int sounddev_tyreslip_update(SimDevice* this, SimData* simdata);
|
||||
int sounddev_free(SimDevice* this);
|
||||
|
||||
SoundDevice* new_sound_device(DeviceSettings* ds);
|
||||
SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
|
||||
|
||||
/***** Generic Methods *********/
|
||||
|
||||
@@ -101,10 +147,23 @@ int update(SimDevice* simdevice, SimData* simdata);
|
||||
|
||||
int devupdate(SimDevice* simdevice, SimData* simdata);
|
||||
|
||||
int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds);
|
||||
int devinit(SimDevice* simdevices, SimInfo* siminfo, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms);
|
||||
|
||||
int devfree(SimDevice* simdevices, int numdevices);
|
||||
|
||||
int simdevfree(SimDevice* this);
|
||||
|
||||
|
||||
/****** USB SubTypes ****************/
|
||||
int wheelhapticdev_update(SimDevice* this, SimData* simdata);
|
||||
int wheeldev_update(SimDevice* this, SimData* simdata);
|
||||
int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds);
|
||||
int wheeldev_free(USBDevice* usbdevice);
|
||||
|
||||
int tachdev_update(USBDevice* tachdevice, SimData* simdata);
|
||||
int tachdev_init(USBDevice* tachdevice, DeviceSettings* ds);
|
||||
int tachdev_free(USBDevice* tachdevice);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "sound.h"
|
||||
|
||||
#include "../slog/slog.h"
|
||||
|
||||
pa_threaded_mainloop* mainloop;
|
||||
pa_context* context;
|
||||
|
||||
void context_state_cb(pa_context* context, void* mainloop) {
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
}
|
||||
|
||||
int setupsound()
|
||||
{
|
||||
|
||||
|
||||
|
||||
pa_mainloop_api *mainloop_api;
|
||||
|
||||
slogi("connecting pulseaudio...");
|
||||
// Get a mainloop and its context
|
||||
mainloop = pa_threaded_mainloop_new();
|
||||
mainloop_api = pa_threaded_mainloop_get_api(mainloop);
|
||||
context = pa_context_new(mainloop_api, "Monocoque");
|
||||
|
||||
pa_context_set_state_callback(context, &context_state_cb, mainloop);
|
||||
|
||||
// Lock the mainloop so that it does not run and crash before the context is ready
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
|
||||
// Start the mainloop
|
||||
pa_threaded_mainloop_start(mainloop);
|
||||
pa_context_connect(context, NULL, 0, NULL);
|
||||
|
||||
// Wait for the context to be ready
|
||||
for(;;) {
|
||||
pa_context_state_t context_state = pa_context_get_state(context);
|
||||
if (context_state == PA_CONTEXT_READY) break;
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
slogi("successfully connected pulseaudio...");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int freesound()
|
||||
{
|
||||
if (mainloop) {
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
}
|
||||
|
||||
if (context)
|
||||
{
|
||||
pa_context_unref(context);
|
||||
slogt("freed pulseaudio context");
|
||||
}
|
||||
|
||||
if (mainloop) {
|
||||
pa_signal_done();
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
pa_threaded_mainloop_free(mainloop);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//int setupsound()
|
||||
//{
|
||||
// slogi("connecting portaudio...");
|
||||
//}
|
||||
//
|
||||
//
|
||||
//int freesound()
|
||||
//{
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _SOUND_H
|
||||
#define _SOUND_H
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
extern pa_threaded_mainloop* mainloop;
|
||||
extern pa_context* context;
|
||||
|
||||
|
||||
int setupsound();
|
||||
int freesound();
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifndef USE_PULSEAUDIO
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
@@ -10,6 +12,9 @@
|
||||
|
||||
#define SAMPLE_RATE (48000)
|
||||
|
||||
#define DURATION 4.0
|
||||
#define AMPLITUDE .5
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
@@ -22,39 +27,27 @@ int patestCallbackEngineRPM(const void* inputBuffer,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void* userData)
|
||||
{
|
||||
PATestData* data = (PATestData*)userData;
|
||||
SoundData* data = (SoundData*)userData;
|
||||
float* out = (float*)outputBuffer;
|
||||
memset(out, 0, framesPerBuffer * 2 * sizeof(float));
|
||||
unsigned int i;
|
||||
unsigned int n;
|
||||
n = data->n;
|
||||
//n = data->n;
|
||||
(void) inputBuffer; /* Prevent unused argument warning. */
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++,n++ )
|
||||
{
|
||||
float v = 0;
|
||||
v = data->amp * sin (2 * M_PI * ((float) n) / (float) SAMPLE_RATE);
|
||||
static double t = 0.0;
|
||||
double sample = AMPLITUDE * 32767.0 * sin(2.0 * M_PI * data->curr_frequency * t);
|
||||
|
||||
if (n>=data->table_size)
|
||||
{
|
||||
n=0;
|
||||
}
|
||||
|
||||
|
||||
if (data->gear_sound_data > 0)
|
||||
{
|
||||
*out++ = 0;
|
||||
*out++ = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out++ = v;
|
||||
*out++ = v;
|
||||
t += 1.0 / SAMPLE_RATE;
|
||||
if (t >= DURATION) {
|
||||
t = 0.0;
|
||||
}
|
||||
*out++ = sample;
|
||||
*out++ = sample;
|
||||
}
|
||||
|
||||
data->gear_sound_data = 0;
|
||||
data->n=n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -66,39 +59,33 @@ int patestCallbackGearShift(const void* inputBuffer,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void* userData)
|
||||
{
|
||||
PATestData* data = (PATestData*)userData;
|
||||
SoundData* data = (SoundData*)userData;
|
||||
float* out = (float*)outputBuffer;
|
||||
memset(out, 0, framesPerBuffer * 2 * sizeof(float));
|
||||
unsigned int i;
|
||||
unsigned int n;
|
||||
n = data->n;
|
||||
//n = data->n;
|
||||
(void) inputBuffer; /* Prevent unused argument warning. */
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++,n++ )
|
||||
{
|
||||
float v = 0;
|
||||
v = data->amp * sin (2 * M_PI * ((float) n) / (float) SAMPLE_RATE);
|
||||
|
||||
if (n>=data->table_size)
|
||||
static double t = 0.0;
|
||||
double sample = 0;
|
||||
if (data->frequency>0)
|
||||
{
|
||||
n=0;
|
||||
sample = AMPLITUDE * 32767.0 * sin(2.0 * M_PI * data->curr_frequency * data->curr_duration);
|
||||
}
|
||||
|
||||
if(data->gear_sound_data > 0)
|
||||
{
|
||||
*out++ = v;
|
||||
*out++ = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out++ = 0;
|
||||
*out++ = 0;
|
||||
|
||||
data->curr_duration += 1.0 / SAMPLE_RATE;
|
||||
if (data->curr_duration >= data->duration) {
|
||||
data->curr_duration = 0.0;
|
||||
data->curr_frequency = 0.0;
|
||||
}
|
||||
|
||||
*out++ = sample;
|
||||
*out++ = sample;
|
||||
}
|
||||
data->gear_sound_data = 0;
|
||||
|
||||
data->n=n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_generic_shaker_free(SoundDevice* sounddevice)
|
||||
@@ -129,7 +116,7 @@ int usb_generic_shaker_init(SoundDevice* sounddevice)
|
||||
sounddevice->outputParameters.suggestedLatency = Pa_GetDeviceInfo( sounddevice->outputParameters.device )->defaultLowOutputLatency;
|
||||
sounddevice->outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
if (sounddevice->effecttype == SOUNDEFFECT_GEARSHIFT)
|
||||
if (sounddevice->m.hapticeffect.effecttype == EFFECT_GEARSHIFT)
|
||||
{
|
||||
err = Pa_OpenStream( &sounddevice->stream,
|
||||
NULL, /* No input. */
|
||||
@@ -169,3 +156,5 @@ error:
|
||||
Pa_Terminate();
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#ifndef _USB_GENERIC_SHAKER_H
|
||||
#define _USB_GENERIC_SHAKER_H
|
||||
|
||||
//#include "../sounddevice.h"
|
||||
#include "../simdevice.h"
|
||||
|
||||
int usb_generic_shaker_init(SoundDevice* sounddevice);
|
||||
int usb_generic_shaker_free(SoundDevice* sounddevice);
|
||||
//#ifdef USE_PULSEAUDIO
|
||||
int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop, pa_context* context, const char* devname, int volume, int pan, int channels, const char* streamname);
|
||||
int usb_generic_shaker_free(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop);
|
||||
//#else
|
||||
//int usb_generic_shaker_init(SoundDevice* sounddevice);
|
||||
//int usb_generic_shaker_free(SoundDevice* sounddevice);
|
||||
//#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include "usb_generic_shaker.h"
|
||||
#include "../sounddevice.h"
|
||||
|
||||
#define FORMAT PA_SAMPLE_S16LE
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define AMPLITUDE 1
|
||||
#define DURATION 1.0
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
|
||||
static double apply_noise(double base_freq, double noise_amount) {
|
||||
if (noise_amount > 0.0) {
|
||||
double r = (double) rand() / RAND_MAX * 2.0 - 1.0;
|
||||
return base_freq + r * noise_amount;
|
||||
} else {
|
||||
return base_freq;
|
||||
}
|
||||
}
|
||||
|
||||
void gear_sound_stream(pa_stream *s, size_t length, void *userdata) {
|
||||
|
||||
SoundData* data = (SoundData*)userdata;
|
||||
size_t num_samples = length / sizeof(int16_t);
|
||||
int16_t buffer[num_samples];
|
||||
|
||||
|
||||
for (size_t i = 0; i < num_samples; i++) {
|
||||
|
||||
double sample = 0;
|
||||
|
||||
if (data->curr_frequency>0.0)
|
||||
{
|
||||
double t = data->phase;
|
||||
double a = (double)data->curr_amplitude/100;
|
||||
|
||||
// fade-out 5ms to prevent discontinuity
|
||||
static double fade_out_duration = 0.005;
|
||||
|
||||
double remaining = data->duration - data->curr_duration;
|
||||
if (remaining < fade_out_duration) {
|
||||
a *= remaining / fade_out_duration;
|
||||
}
|
||||
|
||||
sample = a * 32767.0 * sin(2.0 * M_PI * t);
|
||||
|
||||
double f = apply_noise(data->curr_frequency, data->noise);
|
||||
|
||||
t += f / SAMPLE_RATE;
|
||||
if (t >= 1.0) {
|
||||
t -= floor(t);
|
||||
}
|
||||
|
||||
data->phase = t;
|
||||
|
||||
data->curr_duration += 1.0 / SAMPLE_RATE;
|
||||
if (data->curr_duration >= data->duration) {
|
||||
data->curr_duration = 0.0;
|
||||
data->curr_frequency = 0.0;
|
||||
data->phase = 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buffer[i] = (int16_t)sample;
|
||||
|
||||
}
|
||||
pa_stream_write(s, buffer, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
}
|
||||
|
||||
void engine_sound_stream(pa_stream *s, size_t length, void *userdata) {
|
||||
|
||||
SoundData* data = (SoundData*)userdata;
|
||||
|
||||
size_t num_samples = length / sizeof(int16_t);
|
||||
|
||||
//size_t num_samples = (size_t) (DURATION * SAMPLE_RATE);
|
||||
int16_t buffer[num_samples];
|
||||
|
||||
//double t = 0;
|
||||
for (size_t i = 0; i < num_samples; i++) {
|
||||
|
||||
double t = data->phase;
|
||||
|
||||
double sample = ((double)data->curr_amplitude/100) * 32767.0 * sin( 2.0 * M_PI * t );
|
||||
|
||||
buffer[i] = (int16_t)sample;
|
||||
|
||||
double f = apply_noise(data->curr_frequency, data->noise);
|
||||
|
||||
t += f / SAMPLE_RATE;
|
||||
if (t >= 1.0) {
|
||||
t -= floor(t);
|
||||
}
|
||||
|
||||
data->phase = t;
|
||||
}
|
||||
|
||||
pa_stream_write(s, buffer, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void stream_success_cb(pa_stream *stream, int success, void *userdata) {
|
||||
return;
|
||||
}
|
||||
|
||||
void stream_state_cb(pa_stream *s, void *mainloop) {
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
}
|
||||
|
||||
|
||||
int usb_generic_shaker_free(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop)
|
||||
{
|
||||
if(!mainloop)
|
||||
{
|
||||
// if this happens we are in trouble
|
||||
return 1;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
|
||||
int err = 0;
|
||||
if (sounddevice->stream)
|
||||
{
|
||||
pa_stream_disconnect(sounddevice->stream);
|
||||
pa_stream_unref(sounddevice->stream);
|
||||
// why is this wrong
|
||||
|
||||
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop, pa_context* context, const char* devname, int volume, int pan, int channels, const char* streamname)
|
||||
{
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
pa_stream *stream;
|
||||
|
||||
// Create a playback stream
|
||||
pa_sample_spec sample_specifications;
|
||||
sample_specifications.format = FORMAT;
|
||||
sample_specifications.rate = SAMPLE_RATE;
|
||||
sample_specifications.channels = channels;
|
||||
|
||||
|
||||
pa_channel_map channel_map;
|
||||
pa_channel_map_init_auto(&channel_map, channels, PA_CHANNEL_MAP_DEFAULT);
|
||||
//pa_channel_map_init_stereo(&channel_map);
|
||||
|
||||
// not sure about what to do here
|
||||
if(channels == 2)
|
||||
{
|
||||
pa_channel_map_parse(&channel_map, "front-left,front-right");
|
||||
}
|
||||
if(channels == 4)
|
||||
{
|
||||
pa_channel_map_parse(&channel_map, "front-left,front-right,rear-left,rear-right");
|
||||
}
|
||||
if(channels == 6)
|
||||
{
|
||||
pa_channel_map_parse(&channel_map, "front-left,front-right,front-center,lfe,rear-left,rear-right");
|
||||
}
|
||||
if(channels == 8)
|
||||
{
|
||||
pa_channel_map_parse(&channel_map, "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right");
|
||||
}
|
||||
|
||||
stream = pa_stream_new(context, streamname, &sample_specifications, &channel_map);
|
||||
pa_stream_set_state_callback(stream, stream_state_cb, mainloop);
|
||||
|
||||
if (sounddevice->m.hapticeffect.effecttype == EFFECT_GEARSHIFT)
|
||||
{
|
||||
pa_stream_set_write_callback(stream, gear_sound_stream, &sounddevice->sounddata);
|
||||
}
|
||||
else
|
||||
{
|
||||
pa_stream_set_write_callback(stream, engine_sound_stream, &sounddevice->sounddata);
|
||||
}
|
||||
|
||||
// recommended settings, i.e. server uses sensible values
|
||||
pa_buffer_attr buffer_attr;
|
||||
buffer_attr.maxlength = (uint32_t) 32767;
|
||||
buffer_attr.tlength = (uint32_t) 2048;
|
||||
buffer_attr.prebuf = (uint32_t) -1;
|
||||
buffer_attr.minreq = (uint32_t) -1;
|
||||
|
||||
pa_cvolume cv;
|
||||
pa_cvolume_mute(&cv, channels);
|
||||
|
||||
pa_volume_t channel_volume = PA_CLAMP_VOLUME((pa_volume_t)((volume/100.d)*PA_VOLUME_NORM));
|
||||
|
||||
pa_stream_flags_t stream_flags;
|
||||
stream_flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_NOT_MONOTONIC | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY | PA_STREAM_START_CORKED;
|
||||
|
||||
|
||||
// for now i'm only supporting playing on one specified channel which is the concept you should build your setups around
|
||||
cv.values[pan] = channel_volume;
|
||||
|
||||
pa_stream_connect_playback(stream, devname, &buffer_attr, stream_flags, &cv, NULL);
|
||||
//pa_stream_connect_playback(stream, devname, &buffer_attr, stream_flags, &cv, NULL);
|
||||
|
||||
// Wait for the stream to be ready
|
||||
for(;;)
|
||||
{
|
||||
pa_stream_state_t stream_state = pa_stream_get_state(stream);
|
||||
PA_STREAM_IS_GOOD(stream_state);
|
||||
//PA_STREAM_IS_GOOD(stream_state);
|
||||
if (stream_state == PA_STREAM_READY) break;
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Uncork the stream so it will start playing
|
||||
pa_stream_cork(stream, 0, stream_success_cb, mainloop);
|
||||
|
||||
sounddevice->stream = stream;
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
return 0;
|
||||
}
|
||||
@@ -4,21 +4,61 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "sound.h"
|
||||
#include "simdevice.h"
|
||||
#include "sounddevice.h"
|
||||
#include "hapticeffect.h"
|
||||
#include "sound/usb_generic_shaker.h"
|
||||
#include "../simulatorapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../helper/parameters.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
int gear_sound_set(SoundDevice* sounddevice, SimData* simdata)
|
||||
int gear_sound_set(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
if (sounddevice->sounddata.last_gear != simdata->gear && simdata->gear != 0)
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
|
||||
if (sounddevice->sounddata.last_gear != simdata->gear && simdata->gear > 1)
|
||||
{
|
||||
sounddevice->sounddata.gear_sound_data = 3.14;
|
||||
//sounddevice->sounddata.gear_sound_data = 3.14;
|
||||
sounddevice->sounddata.curr_frequency = this->hapticeffect.basefrequency;
|
||||
sounddevice->sounddata.curr_amplitude = this->hapticeffect.baseamplitude;
|
||||
sounddevice->sounddata.curr_duration = 0;
|
||||
}
|
||||
sounddevice->sounddata.last_gear = simdata->gear;
|
||||
|
||||
slogt("set gear frequency to %f", sounddevice->sounddata.curr_frequency);
|
||||
}
|
||||
|
||||
|
||||
double modulate(SimDevice* this, double raw_effect, HapticEffectModulationType modulation)
|
||||
{
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
|
||||
double modulated_effect = raw_effect;
|
||||
switch (modulation)
|
||||
{
|
||||
case HAPTIC_EFFECT_MODULATION_FREQUENCY:
|
||||
modulated_effect = ((this->hapticeffect.frequencyMax - this->hapticeffect.basefrequency) * raw_effect) + this->hapticeffect.basefrequency;
|
||||
sounddevice->sounddata.curr_frequency = modulated_effect;
|
||||
sounddevice->sounddata.curr_amplitude = this->hapticeffect.baseamplitude;
|
||||
slogt("set curr frequency to %f from raw effect %f and base frequency %i", sounddevice->sounddata.curr_frequency, raw_effect, this->hapticeffect.basefrequency);
|
||||
break;
|
||||
case HAPTIC_EFFECT_MODULATION_AMPLIFY:
|
||||
modulated_effect = ((this->hapticeffect.amplitudeMax - this->hapticeffect.baseamplitude) * raw_effect) + this->hapticeffect.baseamplitude;
|
||||
sounddevice->sounddata.curr_amplitude = modulated_effect;
|
||||
sounddevice->sounddata.curr_frequency = this->hapticeffect.basefrequency;
|
||||
slogt("set curr amplitude to %i from raw effect %f and base amplitude %i", sounddevice->sounddata.curr_amplitude, raw_effect, this->hapticeffect.baseamplitude);
|
||||
break;
|
||||
case HAPTIC_EFFECT_MODULATION_NONE:
|
||||
default:
|
||||
sounddevice->sounddata.curr_frequency = this->hapticeffect.basefrequency;
|
||||
sounddevice->sounddata.curr_amplitude = this->hapticeffect.baseamplitude;
|
||||
break;
|
||||
}
|
||||
|
||||
return modulated_effect;
|
||||
}
|
||||
|
||||
// we could make a vtable for these different effects too
|
||||
@@ -26,79 +66,235 @@ int sounddev_engine_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
|
||||
gear_sound_set(sounddevice, simdata);
|
||||
double effect = ((double)simdata->rpms/60)/((double)simdata->maxrpm/60);
|
||||
slogt("Set base effect of %f from rpms of %i", effect, simdata->rpms);
|
||||
modulate(this, effect, this->hapticeffect.modulationType);
|
||||
}
|
||||
|
||||
int sounddev_tyreslip_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
|
||||
double effect = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
|
||||
slogt("Updating sound device frequency from original %i tyre slip effect %f", this->hapticeffect.effecttype, effect);
|
||||
|
||||
if (effect > 0)
|
||||
{
|
||||
modulate(this, effect, this->hapticeffect.modulationType);
|
||||
}
|
||||
else
|
||||
{
|
||||
sounddevice->sounddata.curr_frequency = 0.0;
|
||||
sounddevice->sounddata.curr_amplitude = 0;
|
||||
sounddevice->sounddata.curr_duration = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int sounddev_tyrelock_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
|
||||
double play = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
|
||||
slogt("Updating sound device frequency from original tyre lock effect %f", play);
|
||||
|
||||
if (play > 0)
|
||||
{
|
||||
modulate(this, play, this->hapticeffect.modulationType);
|
||||
}
|
||||
else
|
||||
{
|
||||
sounddevice->sounddata.curr_frequency = 0.0;
|
||||
sounddevice->sounddata.curr_amplitude = 0;
|
||||
sounddevice->sounddata.curr_duration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sounddev_absbrakes_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
double play = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
|
||||
slogt("Updating sound device frequency from original abs effect %f", play);
|
||||
|
||||
if (play > 0)
|
||||
{
|
||||
modulate(this, play, this->hapticeffect.modulationType);
|
||||
}
|
||||
else
|
||||
{
|
||||
sounddevice->sounddata.curr_frequency = 0.0;
|
||||
sounddevice->sounddata.curr_amplitude = 0;
|
||||
sounddevice->sounddata.curr_duration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sounddev_suspension_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
|
||||
double effect = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
|
||||
effect = effect * 10;
|
||||
slogt("Updating sound device output from original suspension travel effect %f", effect);
|
||||
|
||||
if (effect > 0)
|
||||
{
|
||||
effect = modulate(this, effect, this->hapticeffect.modulationType);
|
||||
}
|
||||
else
|
||||
{
|
||||
sounddevice->sounddata.curr_frequency = 0.0;
|
||||
sounddevice->sounddata.curr_amplitude = 0;
|
||||
sounddevice->sounddata.curr_duration = 0;
|
||||
}
|
||||
|
||||
sounddevice->sounddata.table_size = 44100/(simdata->rpms/60);
|
||||
}
|
||||
|
||||
int sounddev_gearshift_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
|
||||
gear_sound_set(sounddevice, simdata);
|
||||
gear_sound_set(this, simdata);
|
||||
}
|
||||
|
||||
|
||||
int sounddev_free(SimDevice* this)
|
||||
{
|
||||
SoundDevice* sounddevice = (void *) this->derived;
|
||||
|
||||
usb_generic_shaker_free(sounddevice);
|
||||
|
||||
usb_generic_shaker_free(sounddevice, mainloop);
|
||||
free(sounddevice);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sounddev_init(SoundDevice* sounddevice)
|
||||
int sounddev_init(SoundDevice* sounddevice, const char* devname, SoundDeviceSettings sds)
|
||||
{
|
||||
slogi("initializing standalone sound device...");
|
||||
|
||||
sounddevice->sounddata.pitch = 1;
|
||||
sounddevice->sounddata.pitch = 261.626;
|
||||
sounddevice->sounddata.amp = 32;
|
||||
sounddevice->sounddata.left_phase = sounddevice->sounddata.right_phase = 0;
|
||||
sounddevice->sounddata.table_size = 44100/(100/60);
|
||||
sounddevice->sounddata.last_gear = 0;
|
||||
|
||||
switch (sounddevice->effecttype) {
|
||||
case (SOUNDEFFECT_GEARSHIFT):
|
||||
slogi("volume is: %i", sds.volume);
|
||||
slogi("pan is: %i", sds.pan);
|
||||
slogi("channels is: %i", sds.channels);
|
||||
slogi("noise is: %i", sds.noise);
|
||||
|
||||
sounddevice->sounddata.pitch = 500;
|
||||
sounddevice->sounddata.amp = 128;
|
||||
sounddevice->sounddata.left_phase = sounddevice->sounddata.right_phase = 0;
|
||||
sounddevice->sounddata.table_size = 44100/(1);
|
||||
|
||||
sounddevice->sounddata.noise = (double)sds.noise;
|
||||
sounddevice->sounddata.curr_duration = 0;
|
||||
|
||||
sounddevice->sounddata.phase = 0;
|
||||
|
||||
sounddevice->sounddata.curr_amplitude = 0;
|
||||
sounddevice->sounddata.curr_frequency = 0.0;
|
||||
|
||||
|
||||
const char* streamname= "Engine";
|
||||
switch (sounddevice->m.hapticeffect.effecttype) {
|
||||
case (EFFECT_GEARSHIFT):
|
||||
sounddevice->sounddata.last_gear = 0;
|
||||
sounddevice->sounddata.duration = 0.125;
|
||||
streamname = "Gear";
|
||||
break;
|
||||
case (EFFECT_TYRESLIP):
|
||||
streamname = "TyreSlip";
|
||||
break;
|
||||
case (EFFECT_TYRELOCK):
|
||||
streamname = "TyreLock";
|
||||
break;
|
||||
case (EFFECT_ABSBRAKES):
|
||||
streamname = "ABS";
|
||||
break;
|
||||
case (EFFECT_SUSPENSION):
|
||||
streamname = "Suspension";
|
||||
break;
|
||||
case (EFFECT_ENGINERPM):
|
||||
default:
|
||||
streamname = "Engine";
|
||||
break;
|
||||
}
|
||||
|
||||
usb_generic_shaker_init(sounddevice);
|
||||
|
||||
// Returned, not discarded: this function is declared int and used to fall
|
||||
// off its end, so new_sound_device() read whatever happened to be in the
|
||||
// return register and treated most devices as failures -- "Could not
|
||||
// initialize Sound Device" for 22 of 24 configured shakers, while their
|
||||
// PulseAudio streams had in fact connected. The devices were freed and
|
||||
// never fed telemetry, so the graph looked correct in qpwgraph and nothing
|
||||
// shook. Being undefined behaviour it varied by build, which is why the
|
||||
// same config worked against a locally compiled monocoque and not the
|
||||
// packaged one.
|
||||
return usb_generic_shaker_init(sounddevice, mainloop, context, devname, sds.volume, sds.pan, sds.channels, streamname);
|
||||
}
|
||||
|
||||
static const vtable engine_sound_simdevice_vtable = { &sounddev_engine_update, &sounddev_free };
|
||||
static const vtable gear_sound_simdevice_vtable = { &sounddev_gearshift_update, &sounddev_free };
|
||||
static const vtable tyreslip_sound_simdevice_vtable = { &sounddev_tyreslip_update, &sounddev_free };
|
||||
static const vtable tyrelock_sound_simdevice_vtable = { &sounddev_tyrelock_update, &sounddev_free };
|
||||
static const vtable absbrakes_sound_simdevice_vtable = { &sounddev_absbrakes_update, &sounddev_free };
|
||||
static const vtable suspension_sound_simdevice_vtable = { &sounddev_suspension_update, &sounddev_free };
|
||||
|
||||
SoundDevice* new_sound_device(DeviceSettings* ds) {
|
||||
SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
|
||||
|
||||
SoundDevice* this = (SoundDevice*) malloc(sizeof(SoundDevice));
|
||||
|
||||
this->m.update = &update;
|
||||
this->m.free = &simdevfree;
|
||||
this->m.derived = this;
|
||||
int error = 0;
|
||||
|
||||
slogt("Attempting to configure sound device with subtype: %i", ds->dev_subtype);
|
||||
switch (ds->dev_subtype) {
|
||||
case (SIMDEVTYPE_ENGINESOUND):
|
||||
this->effecttype = SOUNDEFFECT_ENGINERPM;
|
||||
this->m.vtable = &engine_sound_simdevice_vtable;
|
||||
slogi("Initializing sound device for engine vibrations.");
|
||||
break;
|
||||
case (SIMDEVTYPE_GEARSOUND):
|
||||
this->effecttype = SOUNDEFFECT_GEARSHIFT;
|
||||
this->m.vtable = &gear_sound_simdevice_vtable;
|
||||
slogi("Initializing sound device for gear shift vibrations.");
|
||||
break;
|
||||
switch (ds->hapticsettings.effect_type)
|
||||
{
|
||||
case (EFFECT_TYRESLIP):
|
||||
case (EFFECT_TYRELOCK):
|
||||
case (EFFECT_ABSBRAKES):
|
||||
case (EFFECT_SUSPENSION):
|
||||
if(siminfo->SimSupportsHapticEffects == false)
|
||||
{
|
||||
slogw("Skipping sound effect setup because sim does not support haptic effects");
|
||||
error = MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
|
||||
}
|
||||
defaut:
|
||||
error = 0;
|
||||
}
|
||||
|
||||
|
||||
if(error == 0)
|
||||
{
|
||||
initializeHapticEffect(&this->m.hapticeffect, &ds->hapticsettings, ms);
|
||||
slogt("Attempting to configure sound device with subtype: %i", ds->hapticsettings.effect_type);
|
||||
switch (ds->hapticsettings.effect_type)
|
||||
{
|
||||
case (EFFECT_ENGINERPM):
|
||||
this->m.vtable = &engine_sound_simdevice_vtable;
|
||||
slogi("Initializing sound device for engine vibrations.");
|
||||
break;
|
||||
case (EFFECT_GEARSHIFT):
|
||||
this->m.vtable = &gear_sound_simdevice_vtable;
|
||||
slogi("Initializing sound device for gear shift vibrations.");
|
||||
break;
|
||||
case (EFFECT_TYRESLIP):
|
||||
this->m.vtable = &tyreslip_sound_simdevice_vtable;
|
||||
slogi("Initializing sound device for tyre slip vibrations.");
|
||||
break;
|
||||
case (EFFECT_TYRELOCK):
|
||||
this->m.vtable = &tyrelock_sound_simdevice_vtable;
|
||||
slogi("Initializing sound device for tyre lock vibrations.");
|
||||
break;
|
||||
case (EFFECT_ABSBRAKES):
|
||||
this->m.vtable = &absbrakes_sound_simdevice_vtable;
|
||||
slogi("Initializing sound device for abs vibrations.");
|
||||
break;
|
||||
|
||||
case (EFFECT_SUSPENSION):
|
||||
this->m.vtable = &suspension_sound_simdevice_vtable;
|
||||
slogi("Initializing sound device for suspension vibrations.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(error == 0)
|
||||
{
|
||||
slogt("Attempting to use sound device %s", ds->dev);
|
||||
error = sounddev_init(this, ds->dev, ds->sounddevsettings);
|
||||
}
|
||||
|
||||
int error = sounddev_init(this);
|
||||
if (error != 0)
|
||||
{
|
||||
free(this);
|
||||
@@ -107,4 +303,3 @@ SoundDevice* new_sound_device(DeviceSettings* ds) {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,25 @@
|
||||
#ifndef _SOUNDDEVICE_H
|
||||
#define _SOUNDDEVICE_H
|
||||
|
||||
#include "portaudio.h"
|
||||
//#ifdef USE_PULSEAUDIO
|
||||
#include <pulse/pulseaudio.h>
|
||||
//#else
|
||||
//#include "portaudio.h"
|
||||
//#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SOUNDDEV_UNKNOWN = 0,
|
||||
SOUNDDEV_SHAKER = 1
|
||||
}
|
||||
SoundType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SOUNDEFFECT_ENGINERPM = 0,
|
||||
SOUNDEFFECT_GEARSHIFT = 1
|
||||
}
|
||||
VibrationEffectType;
|
||||
|
||||
#define MAX_TABLE_SIZE (6000)
|
||||
typedef struct
|
||||
{
|
||||
float sine[MAX_TABLE_SIZE];
|
||||
float pitch;
|
||||
int last_gear;
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
int n;
|
||||
int table_size;
|
||||
int amp;
|
||||
int gear_sound_data;
|
||||
uint8_t last_gear;
|
||||
int volume;
|
||||
double duration;
|
||||
double curr_frequency;
|
||||
uint32_t curr_amplitude;
|
||||
double curr_duration;
|
||||
double phase;
|
||||
double noise;
|
||||
}
|
||||
PATestData;
|
||||
SoundData;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tachdevice.h"
|
||||
#include "revburner.h"
|
||||
#include "../../helper/confighelper.h"
|
||||
#include "../../simulatorapi/simdata.h"
|
||||
#include "../../slog/slog.h"
|
||||
//#include "tachdevice.h"
|
||||
#include "simdevice.h"
|
||||
#include "usb/revburner.h"
|
||||
#include "../helper/confighelper.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
int tachdev_update(TachDevice* tachdevice, SimData* simdata)
|
||||
int tachdev_update(USBDevice* usbdevice, SimData* simdata)
|
||||
{
|
||||
TachDevice* tachdevice = &usbdevice->u.tachdevice;
|
||||
// current plan is to just use the revburner xml format for other possible tachometer devices
|
||||
// with that assumption this same logic is assumed the same for other tachometer devices
|
||||
// the only difference then being in communication to the physical device
|
||||
@@ -43,30 +45,33 @@ int tachdev_update(TachDevice* tachdevice, SimData* simdata)
|
||||
}
|
||||
}
|
||||
slogt("Settings tachometer pulses to %i", pulses);
|
||||
revburner_update(tachdevice, pulses);
|
||||
revburner_update(usbdevice, pulses);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tachdev_free(TachDevice* tachdevice)
|
||||
int tachdev_free(USBDevice* usbdevice)
|
||||
{
|
||||
TachDevice* tachdevice = &usbdevice->u.tachdevice;
|
||||
switch ( tachdevice->type )
|
||||
{
|
||||
case TACHDEV_UNKNOWN :
|
||||
case TACHDEV_REVBURNER :
|
||||
revburner_update(tachdevice, 0);
|
||||
revburner_free(tachdevice);
|
||||
revburner_update(usbdevice, 0);
|
||||
revburner_free(usbdevice);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds)
|
||||
int tachdev_init(USBDevice* usbdevice, DeviceSettings* ds)
|
||||
{
|
||||
slogi("initializing tachometer device...");
|
||||
|
||||
TachDevice* tachdevice = &usbdevice->u.tachdevice;
|
||||
int error = 0;
|
||||
// detection of tach device model
|
||||
tachdevice->type = TACHDEV_UNKNOWN;
|
||||
@@ -74,7 +79,7 @@ int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds)
|
||||
|
||||
tachdevice->tachsettings = ds->tachsettings;
|
||||
|
||||
error = revburner_init(tachdevice);
|
||||
error = revburner_init(usbdevice);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef _TACHDEVICE_H
|
||||
#define _TACHDEVICE_H
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "../helper/confighelper.h"
|
||||
#include "../simulatorapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
|
||||
//typedef int (*tachdev_update)(int revs);
|
||||
|
||||
@@ -19,13 +19,10 @@ typedef struct
|
||||
int id;
|
||||
TachType type;
|
||||
bool use_pulses;
|
||||
hid_device* handle;
|
||||
TachometerSettings tachsettings;
|
||||
}
|
||||
TachDevice;
|
||||
|
||||
int tachdev_update(TachDevice* tachdevice, SimData* simdata);
|
||||
int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds);
|
||||
int tachdev_free(TachDevice* tachdevice);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glob.h>
|
||||
|
||||
#include "cslelitev3.h"
|
||||
|
||||
#include "../../helper/confighelper.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
const char* SYSFSRUMBLEPATH = "/sys/module/hid_fanatec/drivers/hid:f*/0003:0EB7:183B.*/rumble";
|
||||
|
||||
|
||||
int cslelitev3_update(USBDevice* usbdevice, int effecttype, int play)
|
||||
{
|
||||
|
||||
int res = 0;
|
||||
int value = 0;
|
||||
|
||||
if (play > 0)
|
||||
{
|
||||
switch (effecttype)
|
||||
{
|
||||
case (EFFECT_TYRESLIP):
|
||||
value = 0xff0000;
|
||||
break;
|
||||
case (EFFECT_TYRELOCK):
|
||||
value = 0xff00;
|
||||
break;
|
||||
case (EFFECT_ABSBRAKES):
|
||||
value = 0xffff00;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(usbdevice->filehandle, "%i\n", value);
|
||||
fflush(usbdevice->filehandle);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int cslelitev3_free(USBDevice* usbdevice)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
free(usbdevice->dev);
|
||||
|
||||
fflush(usbdevice->filehandle);
|
||||
fclose(usbdevice->filehandle);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int cslelitev3_init(USBDevice* usbdevice)
|
||||
{
|
||||
slogi("initializing CSL Elite V3 Pedals...");
|
||||
|
||||
int res = 0;
|
||||
|
||||
|
||||
glob_t globlist;
|
||||
int i = 0;
|
||||
if (glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_NOSPACE || glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_NOMATCH)
|
||||
{
|
||||
res = 1;
|
||||
}
|
||||
if (glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_ABORTED)
|
||||
{
|
||||
res = 2;
|
||||
}
|
||||
if (res == 0)
|
||||
{
|
||||
while (globlist.gl_pathv[i])
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
usbdevice->dev = strdup(globlist.gl_pathv[i]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
globfree(&globlist);
|
||||
|
||||
if (res == 1) {
|
||||
sloge("Could not find attach Club Sport Elite V3 Pedals");
|
||||
return res;
|
||||
}
|
||||
if (res == 2) {
|
||||
sloge("Permissions issue finding Club Sport Elite V3 Pedals");
|
||||
return res;
|
||||
}
|
||||
|
||||
usbdevice->filehandle = fopen(usbdevice->dev, "w");
|
||||
|
||||
if (!usbdevice->filehandle)
|
||||
{
|
||||
sloge("Could not open pedal device...");
|
||||
return res;
|
||||
}
|
||||
|
||||
slogd("CSL Elite V3 Pedals Successfully initialized...");
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _CSLELITEV3_H
|
||||
#define _CSLELITEV3_H
|
||||
|
||||
#include "../wheeldevice.h"
|
||||
#include "../simdevice.h"
|
||||
|
||||
int cslelitev3_update(USBDevice* usbdevice, int effecttype, int play);
|
||||
int cslelitev3_init(USBDevice* usbdevice);
|
||||
int cslelitev3_free(USBDevice* usbdevice);
|
||||
|
||||
#endif
|
||||
@@ -2,19 +2,19 @@
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "tachdevice.h"
|
||||
#include "../slog/slog.h"
|
||||
#include "../simdevice.h"
|
||||
#include "../../slog/slog.h"
|
||||
|
||||
const int buf_size = 65;
|
||||
const size_t REVBURNER_BUFSIZE = 8;
|
||||
|
||||
|
||||
int revburner_update(TachDevice* tachdevice, int pulses)
|
||||
int revburner_update(USBDevice* tachdevice, int pulses)
|
||||
{
|
||||
|
||||
int res = 0;
|
||||
|
||||
unsigned char bytes[buf_size];
|
||||
for (int x = 0; x < buf_size; x++)
|
||||
unsigned char bytes[REVBURNER_BUFSIZE];
|
||||
for (int x = 0; x < REVBURNER_BUFSIZE; x++)
|
||||
{
|
||||
bytes[x] = 0x00;
|
||||
}
|
||||
@@ -27,7 +27,7 @@ int revburner_update(TachDevice* tachdevice, int pulses)
|
||||
|
||||
if (tachdevice->handle)
|
||||
{
|
||||
res = hid_write(tachdevice->handle, bytes, buf_size);
|
||||
res = hid_write(tachdevice->handle, bytes, REVBURNER_BUFSIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -37,7 +37,7 @@ int revburner_update(TachDevice* tachdevice, int pulses)
|
||||
return res;
|
||||
}
|
||||
|
||||
int revburner_free(TachDevice* tachdevice)
|
||||
int revburner_free(USBDevice* tachdevice)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
@@ -47,7 +47,7 @@ int revburner_free(TachDevice* tachdevice)
|
||||
return res;
|
||||
}
|
||||
|
||||
int revburner_init(TachDevice* tachdevice)
|
||||
int revburner_init(USBDevice* tachdevice)
|
||||
{
|
||||
slogi("initializing revburner tachometer...");
|
||||
//tachdevice->update_tachometer = revburner_device_update;
|
||||
@@ -64,6 +64,6 @@ int revburner_init(TachDevice* tachdevice)
|
||||
res = hid_exit();
|
||||
return 1;
|
||||
}
|
||||
slogd("Found RevBurner Tachometer...");
|
||||
slogd("Found RevBurner Tachometer handle %i...", tachdevice->handle);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#ifndef _REVBURNER_H
|
||||
#define _REVBURNER_H
|
||||
|
||||
int revburner_update(TachDevice* tachdevice, int pulses);
|
||||
int revburner_init(TachDevice* tachdevice);
|
||||
int revburner_free(TachDevice* tachdevice);
|
||||
#include "../simdevice.h"
|
||||
|
||||
int revburner_update(USBDevice* tachdevice, int pulses);
|
||||
int revburner_init(USBDevice* tachdevice);
|
||||
int revburner_free(USBDevice* tachdevice);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "simagicp1000.h"
|
||||
|
||||
#include "../../helper/confighelper.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
#define SIMAGIC_VENDOR_ID 0x0483
|
||||
#define SIMAGIC_PRODUCT_ID_P1000 0x0525
|
||||
|
||||
const size_t SIMAGIC_P1000_BUFSIZE = 49;
|
||||
const char* SIMAGICP1000DEVSTRING = "SIMAGIC P1000 Pedals";
|
||||
|
||||
int senddevreport(USBDevice* usbdevice, unsigned char* buf, size_t bufsize)
|
||||
{
|
||||
int res = 0;
|
||||
if (usbdevice->handle)
|
||||
{
|
||||
res = hid_send_feature_report(usbdevice->handle, buf, SIMAGIC_P1000_BUFSIZE);
|
||||
slogd("sent %i bytes to %s", bufsize, SIMAGICP1000DEVSTRING);
|
||||
slogt("sent bytes %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
|
||||
}
|
||||
else
|
||||
{
|
||||
slogd("no handle");
|
||||
}
|
||||
return res != bufsize;
|
||||
}
|
||||
|
||||
int simagicp1000_update(USBDevice* usbdevice, int effecttype, int play)
|
||||
{
|
||||
|
||||
int res = 0;
|
||||
int value = 0;
|
||||
|
||||
unsigned char bytes[SIMAGIC_P1000_BUFSIZE];
|
||||
for (int x = 0; x < SIMAGIC_P1000_BUFSIZE; x++)
|
||||
{
|
||||
bytes[x] = 0x00;
|
||||
}
|
||||
|
||||
if (play > 0)
|
||||
{
|
||||
bytes[0] = 241;
|
||||
bytes[1] = 0xEC;
|
||||
// we can add config settings for these values
|
||||
bytes[3] = 0x01;
|
||||
bytes[4] = 0x0A;
|
||||
bytes[5] = 0xFF;
|
||||
switch (effecttype)
|
||||
{
|
||||
case (EFFECT_TYRESLIP):
|
||||
bytes[2] = 0x02;
|
||||
senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
|
||||
break;
|
||||
case (EFFECT_TYRELOCK):
|
||||
bytes[2] = 0x01;
|
||||
senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
|
||||
break;
|
||||
case (EFFECT_ABSBRAKES):
|
||||
bytes[2] = 0x02;
|
||||
senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
|
||||
bytes[2] = 0x01;
|
||||
senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes[0] = 241;
|
||||
bytes[2] = 0x01;
|
||||
senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
|
||||
bytes[2] = 0x02;
|
||||
senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int simagicp1000_free(USBDevice* usbdevice)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
hid_close(usbdevice->handle);
|
||||
res = hid_exit();
|
||||
|
||||
return res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int simagicp1000_init(USBDevice* usbdevice)
|
||||
{
|
||||
slogi("initializing %s...", SIMAGICP1000DEVSTRING);
|
||||
|
||||
int res = 0;
|
||||
|
||||
res = hid_init();
|
||||
|
||||
usbdevice->handle = hid_open(SIMAGIC_VENDOR_ID, SIMAGIC_PRODUCT_ID_P1000, NULL);
|
||||
|
||||
if (!usbdevice->handle)
|
||||
{
|
||||
sloge("Could not find attached %s", SIMAGICP1000DEVSTRING);
|
||||
res = hid_exit();
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char bytes[SIMAGIC_P1000_BUFSIZE];
|
||||
for (int x = 0; x < SIMAGIC_P1000_BUFSIZE; x++)
|
||||
{
|
||||
bytes[x] = 0x00;
|
||||
}
|
||||
bytes[0] = 241;
|
||||
bytes[1] = 0xF1;
|
||||
bytes[2] = 0x17;
|
||||
bytes[3] = 0x00;
|
||||
bytes[4] = 0x00;
|
||||
bytes[5] = 0x00;
|
||||
bytes[6] = 0x01;
|
||||
bytes[7] = 0x02;
|
||||
res = senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
|
||||
slogd("Initialization returned %i", res);
|
||||
if(res != 0)
|
||||
{
|
||||
slogw("Problem with initialization of %s", SIMAGICP1000DEVSTRING);
|
||||
}
|
||||
}
|
||||
|
||||
slogd("Found %s...", SIMAGICP1000DEVSTRING);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _SIMAGICP1000_H
|
||||
#define _SIMAGICP1000_H
|
||||
|
||||
#include "../wheeldevice.h"
|
||||
#include "../simdevice.h"
|
||||
|
||||
int simagicp1000_update(USBDevice* usbdevice, int effecttype, int play);
|
||||
int simagicp1000_init(USBDevice* usbdevice);
|
||||
int simagicp1000_free(USBDevice* usbdevice);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glob.h>
|
||||
|
||||
#include "simnetpedals.h"
|
||||
|
||||
#include "../../helper/confighelper.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
#define SIMNETPEDALS_BUFSIZE 64
|
||||
|
||||
//Byte[0] : 0x01 (fixed) = Report ID
|
||||
//Byte[1] : 0x00 (fixed)
|
||||
//Byte[2] : motor number (0x01, 0x2 or 0x4)
|
||||
//Byte[3]: motor 1 frequency in hz
|
||||
//Byte[4] : motor 1 amplitude (0 to 100%)
|
||||
//Byte[5] : motor 2 frequency in hz
|
||||
//Byte[6]: motor 2 amplitude (0 to 100%)
|
||||
//Byte[7]: motor 3 frequency in hz
|
||||
//Byte[8]: motor 3 amplitude (0 to 100%)
|
||||
|
||||
|
||||
int simnetpedals_update(USBDevice* usbdevice, int effecttype, int play, int motorposition, int frequency, int amplitude)
|
||||
{
|
||||
|
||||
int res = 0;
|
||||
|
||||
unsigned char bytes[SIMNETPEDALS_BUFSIZE];
|
||||
for (int x = 0; x < SIMNETPEDALS_BUFSIZE; x++)
|
||||
{
|
||||
bytes[x] = 0x00;
|
||||
}
|
||||
bytes[0] = 0x01;
|
||||
bytes[1] = 0x00;
|
||||
bytes[2] = motorposition;
|
||||
uint8_t motormultiple = motorposition - 1;
|
||||
if(motorposition == 0x04)
|
||||
{
|
||||
motormultiple = 2;
|
||||
}
|
||||
if(play > 0)
|
||||
{
|
||||
bytes[3 + (motormultiple * 2)] = frequency;
|
||||
bytes[4 + (motormultiple * 2)] = amplitude;
|
||||
}
|
||||
|
||||
|
||||
if (usbdevice->handle)
|
||||
{
|
||||
slogt("writing bytes x%02xx%02xx%02xx%02xx%02xx%02xx%02xx%02xx%02x", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8]);
|
||||
res = hid_write(usbdevice->handle, bytes, SIMNETPEDALS_BUFSIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
slogd("no handle");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int simnetpedals_free(USBDevice* usbdevice)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
hid_close(usbdevice->handle);
|
||||
res = hid_exit();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int simnetpedals_init(USBDevice* usbdevice)
|
||||
{
|
||||
slogi("initializing simnet pedals...");
|
||||
|
||||
int res = 0;
|
||||
|
||||
res = hid_init();
|
||||
|
||||
usbdevice->handle = hid_open(0xcafe, 0xa301, NULL);
|
||||
|
||||
if (!usbdevice->handle)
|
||||
{
|
||||
sloge("Could not find attached Simnet Pedals");
|
||||
res = hid_exit();
|
||||
return 1;
|
||||
}
|
||||
slogd("Found Simnet Pedals handle %i...", usbdevice->handle);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _SIMNETPEDALS_H
|
||||
#define _SIMNETPEDALS_H
|
||||
|
||||
#include "../wheeldevice.h"
|
||||
#include "../simdevice.h"
|
||||
|
||||
int simnetpedals_update(USBDevice* usbdevice, int effecttype, int play, int motorposition, int frequency, int amplitude);
|
||||
int simnetpedals_init(USBDevice* usbdevice);
|
||||
int simnetpedals_free(USBDevice* usbdevice);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,231 @@
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "cammusc12.h"
|
||||
#include "../../serial/arduinoledlua.h"
|
||||
#include "../../../slog/slog.h"
|
||||
|
||||
const int cammusc12_hidupdate_buf_size = 16;
|
||||
const int cammusc12_hidledupdate_buf_size = 7;
|
||||
|
||||
const int cammusc12_total_leds = 23;
|
||||
|
||||
int cammusc12_update(USBDevice* usbdevice, int maxrpm, int rpm, int gear, int velocity)
|
||||
{
|
||||
|
||||
int res = 0;
|
||||
|
||||
unsigned char bytes[cammusc12_hidupdate_buf_size];
|
||||
|
||||
for (int x = 0; x < cammusc12_hidupdate_buf_size; x++)
|
||||
{
|
||||
bytes[x] = 0x00;
|
||||
}
|
||||
// byte 1 must be fc it seems
|
||||
bytes[0] = 0xFA;
|
||||
bytes[1] = 0xFB;
|
||||
bytes[2] = 0xD4;
|
||||
|
||||
int perct = 0;
|
||||
if(rpm > 0 && maxrpm > 0)
|
||||
{
|
||||
double rpmperct = (double) rpm / (double) maxrpm;
|
||||
perct = trunc(nearbyint( rpmperct * 100 ));
|
||||
}
|
||||
bytes[3] = perct;
|
||||
|
||||
// bytes 2 and 3 are a 16 bit velocity
|
||||
if ( velocity > 0 )
|
||||
{
|
||||
bytes[5] = (velocity >> 8) & 0xFF;
|
||||
bytes[4] = velocity & 0xFF;
|
||||
}
|
||||
|
||||
// byte 4 is gear
|
||||
bytes[6] = gear-1;
|
||||
|
||||
|
||||
if (usbdevice->handle)
|
||||
{
|
||||
slogt("writing bytes x%02xx%02xx%02xx%02xx%02xx%02xx%02x from rpm %i velocity %i gear %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], rpm, velocity, gear);
|
||||
res = hid_write(usbdevice->handle, bytes, cammusc12_hidupdate_buf_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
slogd("no handle");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int cammusc12_free(USBDevice* wheeldevice)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
hid_close(wheeldevice->handle);
|
||||
res = hid_exit();
|
||||
|
||||
if(wheeldevice->u.wheeldevice.useLua == true)
|
||||
{
|
||||
slogt("closing lua");
|
||||
lua_close(wheeldevice->m.L);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int cammusc12_init_(USBDevice* wheeldevice)
|
||||
{
|
||||
slogi("initializing cammus c12 wheel...");
|
||||
|
||||
int res = 0;
|
||||
|
||||
res = hid_init();
|
||||
|
||||
wheeldevice->handle = hid_open(0x3416, 0x1023, NULL);
|
||||
|
||||
if (!wheeldevice->handle)
|
||||
{
|
||||
sloge("Could not find attached Cammus C12 Wheel");
|
||||
res = hid_exit();
|
||||
return 1;
|
||||
}
|
||||
slogd("Found Cammus C12 Wheel...");
|
||||
return res;
|
||||
}
|
||||
|
||||
int cammusc12_init(USBDevice* wheeldevice, const char* luafile)
|
||||
{
|
||||
|
||||
wheeldevice->u.wheeldevice.useLua = false;
|
||||
int res = cammusc12_init_(wheeldevice);
|
||||
|
||||
if(!wheeldevice->handle)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(luafile == NULL)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
slogt("Using lua file for cammus c12 device");
|
||||
|
||||
lua_State* L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
|
||||
//int top=lua_gettop(L);
|
||||
int status = luaL_loadfile(L, luafile);
|
||||
|
||||
if (status) {
|
||||
/* If something went wrong, error message is at the top of */
|
||||
/* the stack */
|
||||
sloge("There is an issue with your lua script");
|
||||
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
|
||||
lua_close(L);
|
||||
return -1;
|
||||
//exit(1);
|
||||
}
|
||||
|
||||
wheeldevice->u.wheeldevice.useLua = true;
|
||||
lua_setglobal(L,"myFunc");
|
||||
|
||||
wheeldevice->m.L = L;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int cammusc12_customled_update(USBDevice* usbdevice, SimData* simdata)
|
||||
{
|
||||
int result = 1;
|
||||
|
||||
|
||||
size_t bufsize = (cammusc12_total_leds * 3);
|
||||
char ledbytes[bufsize];
|
||||
for(int x = 0; x < bufsize; x++)
|
||||
{
|
||||
ledbytes[x] = 0x00;
|
||||
}
|
||||
|
||||
lua_State* L = usbdevice->m.L;
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_pushlightuserdata(L, &ledbytes);
|
||||
lua_settable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
simdata_to_lua(L, simdata);
|
||||
lua_setglobal(L, "simdata");
|
||||
|
||||
lua_pushinteger(L, cammusc12_total_leds);
|
||||
lua_setglobal(L, "TotalLeds");
|
||||
|
||||
lua_register(L, "set_led_to_color", set_led_to_color);
|
||||
lua_register(L, "set_led_range_to_color", set_led_range_to_color);
|
||||
lua_register(L, "set_led_to_rgb_color", set_led_to_rgb_color);
|
||||
lua_register(L, "set_led_range_to_rgb_color", set_led_range_to_rgb_color);
|
||||
lua_register(L, "led_clear_all", led_clear_all);
|
||||
|
||||
lua_pushinteger(L, LUALEDCOLOR_RED);
|
||||
lua_setglobal(L, "RED");
|
||||
lua_pushinteger(L, LUALEDCOLOR_GREEN);
|
||||
lua_setglobal(L, "GREEN");
|
||||
lua_pushinteger(L, LUALEDCOLOR_BLUE);
|
||||
lua_setglobal(L, "BLUE");
|
||||
lua_pushinteger(L, LUALEDCOLOR_YELLOW);
|
||||
lua_setglobal(L, "YELLOW");
|
||||
lua_pushinteger(L, LUALEDCOLOR_ORANGE);
|
||||
lua_setglobal(L, "ORANGE");
|
||||
|
||||
lua_getglobal(L,"myFunc");
|
||||
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
|
||||
{
|
||||
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
|
||||
}
|
||||
|
||||
int res = 0;
|
||||
|
||||
unsigned char bytes[cammusc12_hidledupdate_buf_size];
|
||||
for(int x = 0; x < cammusc12_hidledupdate_buf_size; x++)
|
||||
{
|
||||
bytes[x] = 0x00;
|
||||
}
|
||||
bytes[0] = 0xFA;
|
||||
bytes[1] = 0xFB;
|
||||
bytes[2] = 0x02;
|
||||
|
||||
for(int i = 0; i < cammusc12_total_leds; i++)
|
||||
{
|
||||
if(i >= 15) // make this a setting
|
||||
{
|
||||
for (int x = 3; x < cammusc12_hidledupdate_buf_size; x++)
|
||||
{
|
||||
bytes[x] = 0x00;
|
||||
}
|
||||
uint8_t led = i+1;
|
||||
uint8_t red = ledbytes[(i * 3) + 0];
|
||||
uint8_t green = ledbytes[(i * 3) + 1];
|
||||
uint8_t blue = ledbytes[(i * 3) + 2];
|
||||
|
||||
bytes[3] = led;
|
||||
bytes[4] = red;
|
||||
bytes[5] = green;
|
||||
bytes[6] = blue;
|
||||
|
||||
|
||||
if (usbdevice->handle)
|
||||
{
|
||||
res = hid_write(usbdevice->handle, bytes, cammusc12_hidupdate_buf_size);
|
||||
slogt("writing bytes x%02xx%02xx%02xx%02xx%02xx%02xx%02x from red %i green %i blue %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], red, green, blue);
|
||||
}
|
||||
else
|
||||
{
|
||||
slogd("no handle");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef _CAMMUSC12_H
|
||||
#define _CAMMUSC12_H
|
||||
|
||||
#include "../../wheeldevice.h"
|
||||
#include "../../simdevice.h"
|
||||
|
||||
int cammusc12_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
|
||||
int cammusc12_customled_update(USBDevice* usbdevice, SimData* simdata);
|
||||
int cammusc12_init(USBDevice* wheeldevice, const char* luafile);
|
||||
int cammusc12_free(USBDevice* wheeldevice);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "cammusc5.h"
|
||||
#include "../../../slog/slog.h"
|
||||
|
||||
const int cammusc5_hidupdate_buf_size = 14;
|
||||
const int num_avail_leds = 9;
|
||||
|
||||
int cammusc5_update(USBDevice* usbdevice, int maxrpm, int rpm, int gear, int velocity)
|
||||
{
|
||||
|
||||
int res = 0;
|
||||
|
||||
unsigned char bytes[cammusc5_hidupdate_buf_size];
|
||||
|
||||
for (int x = 0; x < cammusc5_hidupdate_buf_size; x++)
|
||||
{
|
||||
bytes[x] = 0x00;
|
||||
}
|
||||
// byte 1 must be fc it seems
|
||||
bytes[0] = 0xFC;
|
||||
|
||||
|
||||
// byte 2 is number of lit leds, assuming 9 available leds,
|
||||
// if we send 10, all leds will blink singling a gear change
|
||||
// attempting to build in a margin before the maxrpm is achieved
|
||||
int litleds = 0;
|
||||
|
||||
if(rpm > 0 && maxrpm > 0)
|
||||
{
|
||||
int rpmmargin = ceil(.05*maxrpm);
|
||||
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds+1);
|
||||
|
||||
|
||||
for (int l = 1; l <= (num_avail_leds+1); l++)
|
||||
{
|
||||
if(rpm >= (rpminterval * l))
|
||||
{
|
||||
litleds = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
bytes[1] = litleds;
|
||||
|
||||
// bytes 2 and 3 are a 16 bit velocity
|
||||
if ( velocity > 0 )
|
||||
{
|
||||
bytes[2] = (velocity >> 8) & 0xFF;
|
||||
bytes[3] = velocity & 0xFF;
|
||||
}
|
||||
|
||||
// byte 4 is gear
|
||||
bytes[4] = gear-1;
|
||||
|
||||
slogt("writing bytes x%02xx%02xx%02xx%02xx%02x from rpm %i velocity %i gear %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], rpm, velocity, gear);
|
||||
if (usbdevice->handle)
|
||||
{
|
||||
res = hid_write(usbdevice->handle, bytes, cammusc5_hidupdate_buf_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
slogd("no handle");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int cammusc5_free(USBDevice* usbdevice)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
hid_close(usbdevice->handle);
|
||||
res = hid_exit();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int cammusc5_init(USBDevice* usbdevice)
|
||||
{
|
||||
slogi("initializing cammus c5 wheel...");
|
||||
|
||||
int res = 0;
|
||||
|
||||
res = hid_init();
|
||||
|
||||
usbdevice->handle = hid_open(0x3416, 0x1021, NULL);
|
||||
|
||||
if (!usbdevice->handle)
|
||||
{
|
||||
sloge("Could not find attached Cammus C5 Wheel");
|
||||
res = hid_exit();
|
||||
return 1;
|
||||
}
|
||||
slogd("Found Cammus C5 Wheel...");
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef _CAMMUSC5_H
|
||||
#define _CAMMUSC5_H
|
||||
|
||||
#include "../../simdevice.h"
|
||||
|
||||
int cammusc5_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
|
||||
int cammusc5_init(USBDevice* wheeldevice);
|
||||
int cammusc5_free(USBDevice* wheeldevice);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,225 @@
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "gtneo.h"
|
||||
#include <string.h>
|
||||
#include "../../../slog/slog.h"
|
||||
#include "../../serial/arduinoledlua.h"
|
||||
|
||||
// Simagic allows setting any led, therefore LED index should be defined.
|
||||
struct LED {
|
||||
uint8_t index;
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
};
|
||||
|
||||
// Simagic is using SEND_FEATURE_REPORT
|
||||
// ReportID is 240 (0xf0)
|
||||
// Data length is 64 bytes
|
||||
// Prefix is 00 00 00 00 00
|
||||
// Then we have some descriptor. I had 0xEC and 0x03.
|
||||
// Then: length of LED array
|
||||
// Then: number of LED in hex
|
||||
// RGB value for that LED
|
||||
|
||||
|
||||
void prepare_device(hid_device *device) {
|
||||
uint8_t prepare_report[SIMAGIC_MAX_PAYLOAD_SIZE] = {0};
|
||||
prepare_report[0] = 0x00;
|
||||
prepare_report[1] = 0x00;
|
||||
prepare_report[2] = 0x00;
|
||||
prepare_report[3] = 0x00;
|
||||
prepare_report[4] = 0x00;
|
||||
prepare_report[5] = SIMAGIC_LED_DESCRIPTOR_LED_CONTROL;
|
||||
prepare_report[6] = SIMAGIC_LED_DESCRIPTOR_PREPARE_LEDS;
|
||||
prepare_report[7] = 0x01;
|
||||
|
||||
uint8_t report_id_with_data[SIMAGIC_MAX_PAYLOAD_SIZE] = {SIMAGIC_LED_REPORT_ID}; // Report ID at the start
|
||||
memcpy(&report_id_with_data[1], prepare_report, SIMAGIC_MAX_PAYLOAD_SIZE-1);
|
||||
|
||||
// Send the HID feature report
|
||||
int res = hid_send_feature_report(device, report_id_with_data, sizeof(report_id_with_data));
|
||||
if (res < 0) {
|
||||
fprintf(stderr, "Failed to send HID feature report\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Arbitrary function for sending out leds to simagic.
|
||||
Inputs:
|
||||
device: hid_device handle
|
||||
leds: array of the struct LED
|
||||
led_count: count of leds in leds array
|
||||
*/
|
||||
int send_leds(hid_device *device, struct LED* leds, size_t led_count) {
|
||||
uint8_t descriptor[2] = {SIMAGIC_LED_DESCRIPTOR_LED_CONTROL, SIMAGIC_LED_DESCRIPTOR_SET_LEDS};
|
||||
|
||||
prepare_device(device);
|
||||
int result = 0;
|
||||
size_t led_i = 0;
|
||||
while (led_i < led_count) {
|
||||
size_t chunk_length = (SIMAGIC_MAX_PAYLOAD_SIZE -1-9) / 4; // 1 for reportid, 9 bytes for header, rest for LED data (each LED is 4 bytes)
|
||||
size_t chunk_size = chunk_length * 4;
|
||||
|
||||
uint8_t report[SIMAGIC_MAX_PAYLOAD_SIZE] = {0}; // SIMAGIC_MAX_PAYLOAD_SIZE bytes per report
|
||||
memcpy(report, (uint8_t[]){0x00, 0x00, 0x00, 0x00, 0x00}, 5); // Set the prefix (5 bytes)
|
||||
|
||||
memcpy(&report[5], descriptor, 2); // Set the descriptor (2 bytes)
|
||||
|
||||
// Fill in the LED data
|
||||
for (size_t i = 0; i < chunk_length && led_i + i < led_count; ++i) {
|
||||
memcpy(&report[8+i*4], &leds[led_i+i], 4);
|
||||
report[7]++; // Set the LED array length for this chunk
|
||||
}
|
||||
|
||||
uint8_t report_id_with_data[SIMAGIC_MAX_PAYLOAD_SIZE] = {SIMAGIC_LED_REPORT_ID};
|
||||
memcpy(&report_id_with_data[1], report, SIMAGIC_MAX_PAYLOAD_SIZE-1);
|
||||
|
||||
// Send the HID feature report
|
||||
int res = hid_send_feature_report(device, report_id_with_data, sizeof(report_id_with_data));
|
||||
if (res < 0) {
|
||||
fprintf(stderr, "Failed to send HID feature report chunk\n");
|
||||
return -1;
|
||||
}
|
||||
result += res;
|
||||
|
||||
// Move to the next chunk of LEDs
|
||||
led_i += chunk_length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// default simpro profile:
|
||||
// 97: b b b b b b b b b b b b b b b //flashing
|
||||
// 93: b b b b b b b b b b b b b b b //flashing
|
||||
// 90: g g g g g g g g g y y y w w w
|
||||
// 88: g g g g g g g g g y y y
|
||||
// 85: g g g g g g g g g
|
||||
// 83: g g g g g g g
|
||||
// 78: g g g g g
|
||||
// 75: g g g
|
||||
// 70: g
|
||||
// less: empty (000000)
|
||||
// Telemetry LEDs are 58-72
|
||||
|
||||
|
||||
int simagic_gtneo_customled_update(USBDevice* usbdevice, SimData* simdata)
|
||||
{
|
||||
int res = 1;
|
||||
|
||||
size_t led_count = GT_NEO_LEDS_TOTAL-GT_NEO_LEDS_TELEMETRY_START; // Expose whole LED array
|
||||
size_t bufsize = (led_count * 3);
|
||||
char ledbytes[bufsize];
|
||||
for(int x = 0; x < bufsize; x++)
|
||||
ledbytes[x] = 0x00;
|
||||
|
||||
lua_State* L = usbdevice->m.L;
|
||||
|
||||
lua_pushstring(L, "buff");
|
||||
lua_pushlightuserdata(L, &ledbytes);
|
||||
lua_settable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
simdata_to_lua(L, simdata);
|
||||
lua_setglobal(L, "simdata");
|
||||
|
||||
lua_pushinteger(L, led_count);
|
||||
lua_setglobal(L, "TotalLeds");
|
||||
|
||||
lua_register(L, "set_led_to_color", set_led_to_color);
|
||||
lua_register(L, "set_led_range_to_color", set_led_range_to_color);
|
||||
lua_register(L, "set_led_to_rgb_color", set_led_to_rgb_color);
|
||||
lua_register(L, "set_led_range_to_rgb_color", set_led_range_to_rgb_color);
|
||||
lua_register(L, "led_clear_all", led_clear_all);
|
||||
|
||||
lua_pushinteger(L, LUALEDCOLOR_RED);
|
||||
lua_setglobal(L, "RED");
|
||||
lua_pushinteger(L, LUALEDCOLOR_GREEN);
|
||||
lua_setglobal(L, "GREEN");
|
||||
lua_pushinteger(L, LUALEDCOLOR_BLUE);
|
||||
lua_setglobal(L, "BLUE");
|
||||
lua_pushinteger(L, LUALEDCOLOR_YELLOW);
|
||||
lua_setglobal(L, "YELLOW");
|
||||
lua_pushinteger(L, LUALEDCOLOR_ORANGE);
|
||||
lua_setglobal(L, "ORANGE");
|
||||
|
||||
lua_getglobal(L,"myFunc");
|
||||
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
|
||||
{
|
||||
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
|
||||
}
|
||||
|
||||
// ledbytes is an array of leds. We need to copy colours to our struct LED and call 'send_leds'
|
||||
struct LED teleleds[led_count];
|
||||
|
||||
for (int i = 0; i < led_count; i++) {
|
||||
teleleds[i] = (struct LED){GT_NEO_LEDS_TELEMETRY_START+i, 0x00, 0x00, 0x00};
|
||||
teleleds[i].red = ledbytes[(i * 3) + 0];
|
||||
teleleds[i].green = ledbytes[(i * 3) + 1];
|
||||
teleleds[i].blue = ledbytes[(i * 3) + 2];
|
||||
}
|
||||
|
||||
res = send_leds(usbdevice->handle, teleleds, led_count);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int simagic_gtneo_init(USBDevice* wheeldevice, const char* luafile)
|
||||
{
|
||||
wheeldevice->u.wheeldevice.useLua = false;
|
||||
|
||||
slogi("initializing Simagic GT Neo wheel...");
|
||||
|
||||
int res = 0;
|
||||
|
||||
res = hid_init();
|
||||
|
||||
wheeldevice->handle = hid_open(SIMAGIC_GTNEO_VENDORID, SIMAGIC_GTNEO_PRODUCTID, NULL);
|
||||
|
||||
if (!wheeldevice->handle)
|
||||
{
|
||||
sloge("Could not find attached GT Neo Wheel");
|
||||
res = hid_exit();
|
||||
return 1;
|
||||
}
|
||||
slogd("Found Simagic GT Neo Wheel...");
|
||||
|
||||
slogt("Using lua file");
|
||||
|
||||
lua_State* L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
|
||||
//int top=lua_gettop(L);
|
||||
int status = luaL_loadfile(L, luafile);
|
||||
|
||||
if (status) {
|
||||
/* If something went wrong, error message is at the top of */
|
||||
/* the stack */
|
||||
sloge("There is an issue with your lua script");
|
||||
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
|
||||
lua_close(L);
|
||||
return -1;
|
||||
//exit(1);
|
||||
}
|
||||
|
||||
wheeldevice->u.wheeldevice.useLua = true;
|
||||
lua_setglobal(L,"myFunc");
|
||||
|
||||
wheeldevice->m.L = L;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int simagic_gtneo_free(USBDevice* wheeldevice)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
hid_close(wheeldevice->handle);
|
||||
res = hid_exit();
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef _GTNEO_H
|
||||
#define _GTNEO_H
|
||||
|
||||
#include "../../wheeldevice.h"
|
||||
#include "../../simdevice.h"
|
||||
|
||||
#define SIMAGIC_GTNEO_VENDORID 0x3670
|
||||
#define SIMAGIC_GTNEO_PRODUCTID 0x0805
|
||||
|
||||
#define SIMAGIC_MAX_PAYLOAD_SIZE 64
|
||||
#define SIMAGIC_LED_REPORT_ID 0xf0 // LED control report id?
|
||||
|
||||
#define SIMAGIC_LED_DESCRIPTOR_LED_CONTROL 0xEC // LED control?
|
||||
#define SIMAGIC_LED_DESCRIPTOR_PREPARE_LEDS 0x02 //probably something like 'prepare led canvas'
|
||||
#define SIMAGIC_LED_DESCRIPTOR_SET_LEDS 0x03 //probably 'set temporare leds'
|
||||
|
||||
// expose whole LED array to the lua.
|
||||
// Telemetry leds are 58-72
|
||||
#define GT_NEO_LEDS_TELEMETRY_START 0
|
||||
#define GT_NEO_LEDS_TOTAL 73
|
||||
|
||||
int simagic_gtneo_init(USBDevice* wheeldevice, const char* luafile);
|
||||
int simagic_gtneo_free(USBDevice* wheeldevice);
|
||||
int simagic_gtneo_customled_update(USBDevice* usbdevice, SimData* simdata);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
#include "logitechg29.h"
|
||||
#include "../../../slog/slog.h"
|
||||
|
||||
const int logitechg29_hidupdate_buf_size = 7;
|
||||
|
||||
int logitechg29_update(USBDevice* usbdevice, int maxrpm, int rpm, int gear, int velocity)
|
||||
{
|
||||
|
||||
int res = 0;
|
||||
|
||||
uint8_t leds = 0;
|
||||
|
||||
float percent = 0;
|
||||
if(maxrpm > 0)
|
||||
{
|
||||
percent = (float) rpm / (float) maxrpm;
|
||||
|
||||
if (percent > .84)
|
||||
{
|
||||
leds = 0b11111;
|
||||
}
|
||||
else if (percent > .69)
|
||||
{
|
||||
leds = 0b1111;
|
||||
}
|
||||
else if (percent > .39)
|
||||
{
|
||||
leds = 0b111;
|
||||
}
|
||||
else if (percent > .19)
|
||||
{
|
||||
leds = 0b11;
|
||||
}
|
||||
else if (percent > .4)
|
||||
{
|
||||
leds = 0b1;
|
||||
}
|
||||
else
|
||||
{
|
||||
leds = 0b0;
|
||||
}
|
||||
}
|
||||
uint8_t bytes[7] = {
|
||||
0xF8,
|
||||
0x12,
|
||||
leds,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01
|
||||
};
|
||||
|
||||
slogt("writing bytes x%02xx%02xx%02xx%02xx%02x from rpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[6], rpm);
|
||||
if (usbdevice->handle)
|
||||
{
|
||||
res = hid_write(usbdevice->handle, bytes, logitechg29_hidupdate_buf_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
slogd("no handle");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int logitechg29_free(USBDevice* usbdevice)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
hid_close(usbdevice->handle);
|
||||
res = hid_exit();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int logitechg29_init(USBDevice* usbdevice)
|
||||
{
|
||||
slogi("initializing Logitech G29 wheel...");
|
||||
|
||||
int res = 0;
|
||||
|
||||
res = hid_init();
|
||||
usbdevice->handle = hid_open(0x046D, 0xC24F, NULL);
|
||||
|
||||
if (!usbdevice->handle)
|
||||
{
|
||||
sloge("Could not find attached Logitech G29 Wheel");
|
||||
res = hid_exit();
|
||||
return 1;
|
||||
}
|
||||
slogd("Found Logitech G29 Wheel...");
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef _LOGITECHG29_H
|
||||
#define _LOGITECHG29_H
|
||||
|
||||
#include "../../simdevice.h"
|
||||
|
||||
int logitechg29_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
|
||||
int logitechg29_init(USBDevice* wheeldevice);
|
||||
int logitechg29_free(USBDevice* wheeldevice);
|
||||
|
||||
#endif
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "usbdevice.h"
|
||||
#include "simdevice.h"
|
||||
#include "../helper/parameters.h"
|
||||
#include "../simulatorapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
int usbdev_update(SimDevice* this, SimData* simdata)
|
||||
@@ -17,7 +17,11 @@ int usbdev_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
case USBDEV_UNKNOWN :
|
||||
case USBDEV_TACHOMETER :
|
||||
tachdev_update(&usbdevice->u.tachdevice, simdata);
|
||||
tachdev_update(usbdevice, simdata);
|
||||
break;
|
||||
case USBDEV_WHEEL_OR_PEDALS :
|
||||
// this should be directed via vtable to wheeldevice.c
|
||||
//wheeldev_update(usbdevice, simdata);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -28,11 +32,15 @@ int usbdev_free(SimDevice* this)
|
||||
{
|
||||
USBDevice* usbdevice = (void *) this->derived;
|
||||
|
||||
slogt("Usb device free");
|
||||
switch ( usbdevice->type )
|
||||
{
|
||||
case USBDEV_UNKNOWN :
|
||||
case USBDEV_TACHOMETER :
|
||||
tachdev_free(&usbdevice->u.tachdevice);
|
||||
tachdev_free(usbdevice);
|
||||
break;
|
||||
case USBDEV_WHEEL_OR_PEDALS :
|
||||
wheeldev_free(usbdevice);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -41,40 +49,76 @@ int usbdev_free(SimDevice* this)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds)
|
||||
int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds, SimInfo* siminfo)
|
||||
{
|
||||
slogi("initializing usb device...");
|
||||
int error = 0;
|
||||
|
||||
usbdevice->type = USBDEV_TACHOMETER;
|
||||
//usbdevice->type = USBDEV_TACHOMETER;
|
||||
switch ( usbdevice->type )
|
||||
{
|
||||
case USBDEV_UNKNOWN :
|
||||
case USBDEV_TACHOMETER :
|
||||
error = tachdev_init(&usbdevice->u.tachdevice, ds);
|
||||
error = tachdev_init(usbdevice, ds);
|
||||
break;
|
||||
case USBDEV_WHEEL_OR_PEDALS :
|
||||
error = wheeldev_init(usbdevice, ds);
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static const vtable usb_simdevice_vtable = { &usbdev_update, &usbdev_free };
|
||||
|
||||
USBDevice* new_usb_device(DeviceSettings* ds) {
|
||||
static const vtable usb_simdevice_vtable = { &usbdev_update, &usbdev_free };
|
||||
static const vtable usb_wheeldevice_vtable = { &wheeldev_update, &usbdev_free };
|
||||
static const vtable usb_wheelhaptic_vtable = { &wheelhapticdev_update, &usbdev_free };
|
||||
|
||||
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
|
||||
|
||||
USBDevice* this = (USBDevice*) malloc(sizeof(USBDevice));
|
||||
int error = 0;
|
||||
|
||||
this->m.update = &update;
|
||||
this->m.free = &simdevfree;
|
||||
this->m.derived = this;
|
||||
this->m.vtable = &usb_simdevice_vtable;
|
||||
|
||||
int error = usbdev_init(this, ds);
|
||||
// TODO: turn this into a switch when we get more devices
|
||||
this->type = USBDEV_TACHOMETER;
|
||||
if(ds->dev_subtype == SIMDEVTYPE_USBWHEEL)
|
||||
{
|
||||
this->type = USBDEV_WHEEL_OR_PEDALS;
|
||||
this->m.vtable = &usb_wheeldevice_vtable;
|
||||
}
|
||||
|
||||
if(ds->has_haptic_effects == true)
|
||||
{
|
||||
if(siminfo->SimSupportsHapticEffects == false)
|
||||
{
|
||||
// if the user added haptic effects to the config and the sim does not support it, but still wishes to use features
|
||||
// just remove the haptic effect from the config?
|
||||
slogi("This sim does not support haptic effects");
|
||||
error = MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
int error = 0;
|
||||
this->type = USBDEV_WHEEL_OR_PEDALS;
|
||||
initializeHapticEffect(&this->m.hapticeffect, &ds->hapticsettings, ms);
|
||||
this->m.vtable = &usb_wheelhaptic_vtable;
|
||||
}
|
||||
}
|
||||
|
||||
error = usbdev_init(this, ds, siminfo);
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
slogw("Did not initialize usb device due to error code %i", error);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this->m.initialized = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -2,5 +2,7 @@
|
||||
#define _USBDEVICE_H
|
||||
|
||||
#include "tachdevice.h"
|
||||
#include "wheeldevice.h"
|
||||
#include "usbhapticdevice.h"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "usbhapticdevice.h"
|
||||
#include "hapticeffect.h"
|
||||
#include "usb/cslelitev3.h"
|
||||
#include "usb/simagicp1000.h"
|
||||
#include "usb/simnetpedals.h"
|
||||
|
||||
#include "../../helper/confighelper.h"
|
||||
#include "../../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../../slog/slog.h"
|
||||
|
||||
|
||||
int usbhapticdev_update(USBGenericHapticDevice* usbhapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile)
|
||||
{
|
||||
|
||||
double play = slipeffect(simdata, this->hapticeffect.effecttype, tyre, this->hapticeffect.threshold, useconfig, configcheck, configfile);
|
||||
|
||||
if (play != usbhapticdevice->state)
|
||||
{
|
||||
int rplay = 0;
|
||||
if(play > 0)
|
||||
{
|
||||
rplay = 1;
|
||||
switch ( usbhapticdevice->type )
|
||||
{
|
||||
case USBHAPTIC_CSLELITEV3PEDALS:
|
||||
cslelitev3_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
case USBHAPTIC_SIMAGICP1000PEDALS:
|
||||
simagicp1000_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
case USBHAPTIC_SIMNETPEDALS:
|
||||
simnetpedals_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ( usbhapticdevice->type )
|
||||
{
|
||||
case USBHAPTIC_CSLELITEV3PEDALS:
|
||||
cslelitev3_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
case USBHAPTIC_SIMAGICP1000PEDALS:
|
||||
simagicp1000_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
case USBHAPTIC_SIMNETPEDALS:
|
||||
simnetpedals_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
}
|
||||
}
|
||||
usbhapticdevice->state = play;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbhapticdev_free(USBGenericHapticDevice* usbhapticdevice)
|
||||
{
|
||||
slogt("closing usb haptic device");
|
||||
switch ( usbhapticdevice->type )
|
||||
{
|
||||
case USBHAPTIC_CSLELITEV3PEDALS:
|
||||
cslelitev3_free(usbhapticdevice);
|
||||
break;
|
||||
case USBHAPTIC_SIMAGICP1000PEDALS:
|
||||
simagicp1000_free(usbhapticdevice);
|
||||
break;
|
||||
case USBHAPTIC_SIMNETPEDALS:
|
||||
simnetpedals_free(usbhapticdevice);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbhapticdev_init(USBGenericHapticDevice* usbhapticdevice, DeviceSettings* ds, SimInfo* siminfo)
|
||||
{
|
||||
if(siminfo->SimSupportsHapticEffects == false)
|
||||
{
|
||||
slogi("This sim does not support haptic effects");
|
||||
return MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
|
||||
}
|
||||
|
||||
int error = 0;
|
||||
initializeHapticEffect(&this->m.hapticeffect, &ds->hapticsettings, ms);
|
||||
|
||||
slogi("initializing standalone usb haptic device...");
|
||||
// detection of usb device model
|
||||
switch (ds->dev_subsubtype) {
|
||||
case (SIMDEVSUBTYPE_SIMAGICP1000PEDALS):
|
||||
usbhapticdevice->type = USBHAPTIC_SIMAGICP1000PEDALS;
|
||||
error = simagicp1000_init(usbhapticdevice);
|
||||
break;
|
||||
case (SIMDEVSUBTYPE_CSLELITEV3PEDALS):
|
||||
usbhapticdevice->type = USBHAPTIC_CSLELITEV3PEDALS;
|
||||
error = cslelitev3_init(usbhapticdevice);
|
||||
break;
|
||||
case (SIMDEVSUBTYPE_SIMNETPEDALS):
|
||||
usbhapticdevice->type = USBHAPTIC_SIMNETPEDALS;
|
||||
error = simnetpedals_init(usbhapticdevice);
|
||||
break;
|
||||
default:
|
||||
slogw("Possibly unknown device");
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef _USBHAPTICDEVICE_H
|
||||
#define _USBHAPTICDEVICE_H
|
||||
|
||||
#include <hidapi/hidapi.h>
|
||||
#include "../helper/confighelper.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
USBHAPTIC_UNKNOWN = 0,
|
||||
USBHAPTIC_CSLELITEV3PEDALS = 1,
|
||||
USBHAPTIC_SIMAGICP1000PEDALS = 2,
|
||||
USBHAPTIC_SIMNETPEDALS = 3
|
||||
}
|
||||
HapticType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
FILE* filehandle;
|
||||
hid_device* handle;
|
||||
char* dev;
|
||||
}
|
||||
USBGenericHapticDevice;
|
||||
|
||||
|
||||
int usbhapticdev_update(USBGenericHapticDevice* hapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile);
|
||||
int usbhapticdev_init(USBGenericHapticDevice* hapticdevice, DeviceSettings* ds, SimInfo* siminfo);
|
||||
int usbhapticdev_free(USBGenericHapticDevice* hapticdevice);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,224 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "usb/wheels/logitechg29.h"
|
||||
#include "usb/wheels/cammusc5.h"
|
||||
#include "usb/wheels/cammusc12.h"
|
||||
#include "usb/wheels/gtneo.h"
|
||||
#include "serial/moza.h"
|
||||
|
||||
|
||||
#include "usb/cslelitev3.h"
|
||||
#include "usb/simagicp1000.h"
|
||||
#include "usb/simnetpedals.h"
|
||||
|
||||
#include "simdevice.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
int wheelhapticdev_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
USBDevice* usbdevice = (void *) this->derived;
|
||||
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
|
||||
|
||||
double play = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
|
||||
|
||||
if (play != usbdevice->hapticstate)
|
||||
{
|
||||
int rplay = 0;
|
||||
if(play > 0)
|
||||
{
|
||||
rplay = 1;
|
||||
switch ( wheeldevice->type )
|
||||
{
|
||||
case WHEELDEV_CSLELITEV3PEDALS:
|
||||
cslelitev3_update(usbdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
case WHEELDEV_SIMAGICP1000PEDALS:
|
||||
simagicp1000_update(usbdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
case WHEELDEV_SIMNETPEDALS:
|
||||
simnetpedals_update(usbdevice, this->hapticeffect.effecttype, rplay, this->hapticeffect.motorposition, this->hapticeffect.basefrequency, this->hapticeffect.baseamplitude);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ( wheeldevice->type )
|
||||
{
|
||||
case WHEELDEV_CSLELITEV3PEDALS:
|
||||
cslelitev3_update(usbdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
case WHEELDEV_SIMAGICP1000PEDALS:
|
||||
simagicp1000_update(usbdevice, this->hapticeffect.effecttype, rplay);
|
||||
break;
|
||||
case WHEELDEV_SIMNETPEDALS:
|
||||
simnetpedals_update(usbdevice, this->hapticeffect.effecttype, rplay, this->hapticeffect.motorposition, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
usbdevice->hapticstate = play;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wheeldev_update(SimDevice* this, SimData* simdata)
|
||||
{
|
||||
USBDevice* usbdevice = (void *) this->derived;
|
||||
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
|
||||
|
||||
switch ( wheeldevice->type )
|
||||
{
|
||||
case WHEELDEV_UNKNOWN :
|
||||
case WHEELDEV_CAMMUSC5 :
|
||||
cammusc5_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
|
||||
break;
|
||||
case WHEELDEV_LOGITECHG29 :
|
||||
logitechg29_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
|
||||
break;
|
||||
case WHEELDEV_CAMMUSC12 :
|
||||
if(usbdevice->u.wheeldevice.useLua == true)
|
||||
{
|
||||
cammusc12_customled_update(usbdevice, simdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
cammusc12_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
|
||||
}
|
||||
break;
|
||||
case WHEELDEV_SIMAGICGTNEO :
|
||||
if(usbdevice->u.wheeldevice.useLua == true)
|
||||
{
|
||||
simagic_gtneo_customled_update(usbdevice, simdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
sloge("GT Neo requires config file");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wheeldev_free(USBDevice* usbdevice)
|
||||
{
|
||||
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
|
||||
slogt("wheel device free");
|
||||
switch ( wheeldevice->type )
|
||||
{
|
||||
case WHEELDEV_UNKNOWN :
|
||||
case WHEELDEV_CAMMUSC5 :
|
||||
cammusc5_update(usbdevice, 0, 0, 0, 0);
|
||||
cammusc5_free(usbdevice);
|
||||
break;
|
||||
case WHEELDEV_LOGITECHG29 :
|
||||
logitechg29_update(usbdevice, 0, 0, 0, 0);
|
||||
logitechg29_free(usbdevice);
|
||||
break;
|
||||
case WHEELDEV_CAMMUSC12 :
|
||||
cammusc12_free(usbdevice);
|
||||
if(wheeldevice->useLua == true)
|
||||
{
|
||||
free(usbdevice->m.device_specific_config_file);
|
||||
}
|
||||
break;
|
||||
case WHEELDEV_CSLELITEV3PEDALS:
|
||||
cslelitev3_free(usbdevice);
|
||||
break;
|
||||
case WHEELDEV_SIMAGICP1000PEDALS:
|
||||
simagicp1000_free(usbdevice);
|
||||
break;
|
||||
case WHEELDEV_SIMNETPEDALS:
|
||||
simnetpedals_free(usbdevice);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds)
|
||||
{
|
||||
slogi("initializing wheel or pedals device...");
|
||||
int error = 0;
|
||||
usbdevice->hapticstate = 0;
|
||||
// detection of wheel model
|
||||
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
|
||||
switch (ds->dev_subsubtype) {
|
||||
|
||||
case SIMDEVSUBTYPE_CAMMUSC5:
|
||||
wheeldevice->type = WHEELDEV_CAMMUSC5;
|
||||
slogi("Attempting to initialize cammus C5");
|
||||
error = cammusc5_init(usbdevice);
|
||||
break;
|
||||
case SIMDEVSUBTYPE_LOGITECH_G29:
|
||||
wheeldevice->type = WHEELDEV_LOGITECHG29;
|
||||
slogi("Attempting to initialize Logitech G29");
|
||||
error = logitechg29_init(usbdevice);
|
||||
break;
|
||||
case SIMDEVSUBTYPE_CAMMUSC12:
|
||||
wheeldevice->type = WHEELDEV_CAMMUSC12;
|
||||
slogi("Attempting to initialize cammus C12");
|
||||
|
||||
if(ds->has_config == true)
|
||||
{
|
||||
usbdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
|
||||
error = cammusc12_init(usbdevice, usbdevice->m.device_specific_config_file);
|
||||
if(error != 0)
|
||||
{
|
||||
if(usbdevice->m.device_specific_config_file != NULL)
|
||||
{
|
||||
free(usbdevice->m.device_specific_config_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = cammusc12_init(usbdevice, NULL);
|
||||
}
|
||||
break;
|
||||
case SIMDEVSUBTYPE_SIMAGICGTNEO:
|
||||
wheeldevice->type = WHEELDEV_SIMAGICGTNEO;
|
||||
slogi("Attempting to initialize Simagic GT Neo");
|
||||
|
||||
if(ds->has_config == true)
|
||||
{
|
||||
usbdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
|
||||
error = simagic_gtneo_init(usbdevice, usbdevice->m.device_specific_config_file);
|
||||
if(error != 0)
|
||||
{
|
||||
if(usbdevice->m.device_specific_config_file != NULL)
|
||||
{
|
||||
free(usbdevice->m.device_specific_config_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sloge("Simagic GT Neo requires lua config file to function");
|
||||
error = -1;
|
||||
}
|
||||
break;
|
||||
case (SIMDEVSUBTYPE_SIMAGICP1000PEDALS):
|
||||
wheeldevice->type = WHEELDEV_SIMAGICP1000PEDALS;
|
||||
error = simagicp1000_init(usbdevice);
|
||||
break;
|
||||
case (SIMDEVSUBTYPE_CSLELITEV3PEDALS):
|
||||
slogi("Attempting to initialize CSL Elite V3 Pedals");
|
||||
wheeldevice->type = WHEELDEV_CSLELITEV3PEDALS;
|
||||
error = cslelitev3_init(usbdevice);
|
||||
break;
|
||||
case (SIMDEVSUBTYPE_SIMNETPEDALS):
|
||||
wheeldevice->type = WHEELDEV_SIMNETPEDALS;
|
||||
error = simnetpedals_init(usbdevice);
|
||||
break;
|
||||
default:
|
||||
slogw("Possibly unknown wheel device");
|
||||
}
|
||||
|
||||
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef _WHEELDEVICE_H
|
||||
#define _WHEELDEVICE_H
|
||||
|
||||
#include "../helper/confighelper.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
|
||||
//typedef int (*tachdev_update)(int revs);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
WHEELDEV_UNKNOWN = 0,
|
||||
WHEELDEV_CAMMUSC5 = 1,
|
||||
WHEELDEV_CAMMUSC12 = 2,
|
||||
WHEELDEV_MOZAR5 = 3,
|
||||
WHEELDEV_SIMAGICGTNEO = 4,
|
||||
WHEELDEV_LOGITECHG29 = 5,
|
||||
WHEELDEV_CSLELITEV3PEDALS = 6,
|
||||
WHEELDEV_SIMAGICP1000PEDALS = 7,
|
||||
WHEELDEV_SIMNETPEDALS = 8
|
||||
}
|
||||
WheelType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
WheelType type;
|
||||
bool useLua;
|
||||
char* port;
|
||||
}
|
||||
WheelDevice;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -3,9 +3,12 @@ set(gameloop_source_files
|
||||
gameloop.h
|
||||
tachconfig.c
|
||||
tachconfig.h
|
||||
loopdata.h
|
||||
)
|
||||
|
||||
set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
|
||||
include_directories("." ${LIBXML_INCLUDE_DIR})
|
||||
|
||||
add_library(gameloop STATIC ${gameloop_source_files})
|
||||
|
||||
set_property(TARGET gameloop PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
+991
-118
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,24 @@
|
||||
#include "../devices/simdevice.h"
|
||||
#include "../helper/parameters.h"
|
||||
#include "loopdata.h"
|
||||
|
||||
extern int appstate;
|
||||
|
||||
int tester(SimDevice* devices, int numdevices);
|
||||
int looper (SimDevice* devices, int numdevices, Simulator simulator);
|
||||
int looper(SimDevice* devices, int numdevices, Parameters* p);
|
||||
|
||||
int monocoque_mainloop(MonocoqueSettings* ms);
|
||||
|
||||
int start_loop(MonocoqueSettings* ms);
|
||||
int start_test(test_loop_args* test_loop_data);
|
||||
int monocoque_mainloop_stop(MonocoqueSettings* ms);
|
||||
int monocoque_testloop_stop();
|
||||
|
||||
const char* get_simd_onoff(void);
|
||||
const char* get_simexe_name(void);
|
||||
SimData* get_test_simdata();
|
||||
|
||||
|
||||
void set_basic_simdata(SimData* simdata);
|
||||
void set_wheel_spin_simdata(SimData* simdata);
|
||||
void set_wheel_lock_simdata(SimData* simdata);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef _LOOPDATA_H
|
||||
#define _LOOPDATA_H
|
||||
|
||||
#include <uv.h>
|
||||
#include "../helper/parameters.h"
|
||||
#include "../helper/confighelper.h"
|
||||
#include "../devices/simdevice.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simmapper.h"
|
||||
|
||||
typedef struct device_loop_data
|
||||
{
|
||||
// these are all pointers to pointers and freed independently, usually in releaseloop
|
||||
uv_work_t req;
|
||||
SimData* simdata;
|
||||
SimDevice* simdevice;
|
||||
MonocoqueSettings* ms;
|
||||
SimInfo siminfo;
|
||||
} device_loop_data;
|
||||
|
||||
typedef struct loop_data
|
||||
{
|
||||
uv_work_t req;
|
||||
bool use_udp;
|
||||
bool uion;
|
||||
bool releasing;
|
||||
bool started_tyre_calc_thread;
|
||||
int numdevices;
|
||||
SimInfo siminfo;
|
||||
// monocoque settings is a pointer from monocoque.c and freed there
|
||||
MonocoqueSettings* ms;
|
||||
// these are freed at the end of the main gameloop
|
||||
SimData* simdata;
|
||||
SimMap* simmap;
|
||||
// these all get malloced in looprun and freed in releaseloop
|
||||
SimDevice* simdevices;
|
||||
uv_timer_t* device_timers;
|
||||
device_loop_data* device_batons;
|
||||
device_loop_data* tyrebaton;
|
||||
} loop_data;
|
||||
|
||||
|
||||
typedef struct test_loop_args
|
||||
{
|
||||
MonocoqueSettings* ms;
|
||||
DeviceSettings* ds;
|
||||
SimData* simdata;
|
||||
int devicenum;
|
||||
int confignum;
|
||||
} test_loop_args;
|
||||
|
||||
#endif
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "../devices/simdevice.h"
|
||||
#include "../simulatorapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
#include "../slog/slog.h"
|
||||
|
||||
#define DEFAULT_UPDATE_RATE 30.0
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define _TACHCONFIG_H
|
||||
|
||||
#include "../devices/simdevice.h"
|
||||
#include "../simulatorapi/simdata.h"
|
||||
#include "../simulatorapi/simapi/simapi/simdata.h"
|
||||
|
||||
int config_tachometer(int max_revs, int granularity, const char* save_file, SimDevice* simdevice, SimData* simdata);
|
||||
|
||||
|
||||
@@ -11,3 +11,5 @@ set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
|
||||
include_directories("." ${LIBXML_INCLUDE_DIR})
|
||||
|
||||
add_library(helper STATIC ${helper_source_files})
|
||||
|
||||
set_property(TARGET helper PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
+1296
-44
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user