Compare commits

..
220 Commits
Author SHA1 Message Date
Paul Dino Jones 3c81f749b8 various fixes for weirdness with serial devices 2026-05-28 16:25:55 -04:00
David Scott 3c1b1948fd create a rootful, privileged distrobox container (#47) 2026-05-27 13:49:30 -04:00
Paul Dino Jones 3b10509ecd add some directives that seem necessary for debian 2026-05-25 23:58:33 -04:00
Paul Dino Jones b734330d38 fix lua linking 2026-05-25 23:36:57 -04:00
Paul Dino Jones eb95db1dda rename lua in headers 2026-05-25 23:13:53 -04:00
Paul Dino Jones 3a803550ea add more lua variables back to cmake 2026-05-25 23:11:04 -04:00
Paul Dino Jones 26e0e87cd7 add macro for lua 5.4 backwards compatibility so we can compile against lua 5.4 or 5.5 2026-05-25 23:03:57 -04:00
Paul Dino Jones 7f17a5307c refer explicitly to lua 5.4 in headers 2026-05-25 20:46:47 -04:00
Paul Dino Jones ae87a2eaa5 add more dependencies to install.sh 2026-05-25 19:54:08 -04:00
Paul Dino Jones 738a181d61 force lua 5.4 in cmake 2026-05-25 19:53:47 -04:00
Paul Dino Jones ba6c624a59 Improve lua dependency detection for linking 2026-05-25 19:17:47 -04:00
Brandan Cotton 822d4096e7 make additional data available to lua scripts (#45) 2026-05-25 08:42:19 -04:00
Paul Dino Jones a3e932e2dc updated to latest simapi for process detection 2026-05-23 12:40:42 -04:00
Paul Dino Jones ad53dd654c build simapi in install script 2026-05-22 17:45:36 -04:00
Paul Dino Jones 522185d19b remove deprectated getpid.h file 2026-04-26 19:38:15 -04:00
Paul Dino Jones 030813cd4f Update to latest simapi 2026-04-26 19:16:29 -04:00
Paul Dino Jones 53467a2a5e Fix cmake compile definitions 2026-04-23 17:35:50 -04:00
Paul Dino Jones b5fa399f79 fix cmake boolean 2026-04-23 17:20:57 -04:00
Paul Dino Jones 4a72f760b6 fix proc2 detection 2026-04-23 16:02:02 -04:00
Paul Dino Jones 89c73a2fd3 fix lua version issue 2026-04-23 15:25:21 -04:00
Paul Dino Jones 6b72e23638 Add procps dependency. Update to latest simapi 2026-04-22 16:35:43 -04:00
Tony Miceli 8c6b8ef7ac Fix multiple serial device init issues (#43) 2026-04-20 10:35:36 -04:00
Francesco a628afa815 Distrobox install (#41) 2026-04-20 10:26:16 -04:00
Simon Zengerling 712b900d68 Feat add moza ks pro wheel (#42) 2026-04-11 16:07:17 -04:00
Paul Dino Jones 79642a7653 Add config entry for Moza R3 2026-04-04 10:02:16 -04:00
Tony Miceli fbe92cc1c9 add configurable fanpower for simwind and new PWM sketch (#37) 2026-04-03 18:03:08 -04:00
Paul Dino Jones 9e32b3f286 disable installing systemd service via cmake 2026-03-20 19:47:31 -04:00
Paul Dino Jones b3b2c6854d move saving of tyre diameter config to when we still know the car and sim info 2026-03-18 15:41:57 -04:00
Paul Dino Jones 29b446e648 Fix issue with monocoque not shutting down cleanly when used in standalone udp mode 2026-03-18 11:48:43 -04:00
Paul Dino Jones 0142ab61a3 Refactor how sim calculate tyre diameter data if necessary 2026-03-18 11:10:35 -04:00
Paul Dino Jones 09a832a38e Use sim provided slip ratio if available for slip based effects 2026-03-17 10:32:27 -04:00
Paul Dino Jones 9f99cb3e12 Fix regression braking udp support 2026-03-17 10:31:32 -04:00
Paul Dino Jones acc37e005a Refactor to provide siminfo to help decide if and how a device can be initialize 2026-03-16 17:40:13 -04:00
Paul Dino Jones e5ac137b04 bring in fixes to crashes caused by new simapi 2026-03-11 23:57:01 -04:00
Paul Dino Jones 522a64b0f3 Update to latest simapi 2026-03-11 22:40:01 -04:00
Paul Dino Jones d465db0055 Remove more debug code 2026-03-04 01:09:45 -05:00
Paul Dino Jones 2eb48907a0 fix compilation error 2026-03-03 23:42:18 -05:00
Paul Dino Jones 479c77d251 clean up debug code in pulseaudio mainloop start 2026-03-03 23:37:45 -05:00
Paul Dino Jones e32cac5666 Allow to automatically spawn pulseaudio daemon 2026-03-03 23:08:00 -05:00
Paul Dino Jones 93a967ec15 Update to latest simapi for RaceRoom support 2026-02-28 16:04:31 -05:00
Paul Dino Jones db6b5b027c Add link to guide for reverse engineering 2026-02-14 13:45:02 -05:00
dafteran4 76b7d4dafc Noise for haptics (#31) 2026-02-14 12:32:14 -05:00
dafteran4 f7df9ab90e Low latency (#32) 2026-02-13 11:13:33 -05:00
dafteran4 5781518f44 sound: Fix frequency handling and add RPM rev test (#29)
- Change curr_frequency from uint32_t to double for precise frequency
  control
- Remove get_closest_frequency() and change phase calculation in engine
  sound to ensure continuous sine curve
- Add RPM revving test
2026-02-12 14:29:10 -05:00
Paul Dino Jones ce05f0d78b Update to latest simapi for Forza support 2026-02-11 10:52:13 -05:00
Paul Dino Jones 7415ecf85f use separate debian control for each build 2026-02-08 11:42:37 -05:00
Paul Dino Jones 6b84a85639 Added new files to build from new version of simapi 2026-02-07 10:57:21 -05:00
Paul Dino Jones 38e13be623 updated to lastest simapi for Wreckfest 2 Support 2026-02-07 10:47:57 -05:00
Paul Dino Jones 2433417cf6 restructure some libuv stuff 2026-02-06 17:54:17 -05:00
Paul Dino Jones 5b1ac7c74a Improve handling of shutdown when in udp mode 2026-01-27 01:43:01 -05:00
Paul Dino Jones 24a5811b07 update to latest simapi 2026-01-25 19:09:51 -05:00
Paul Dino Jones 00a177fd2f Improve verbiage around invalid config file 2026-01-20 18:11:55 -05:00
Paul Dino Jones acd52f78dd fix small issues with command line parameters 2026-01-20 18:11:55 -05:00
Paul Dino Jones 0b1014763e move tyre diameter calculations and diameters config saving out of haptic effect device specific logic 2026-01-15 18:18:29 -05:00
Paul Dino Jones 37c8fb94d4 move finished rpm to github workspace 2026-01-12 15:31:52 -05:00
Paul Dino Jones ec1690b49d hardcode directory of generated rpm file 2026-01-12 15:22:22 -05:00
Paul Dino Jones 7478e4e010 run on creation of any new tag 2026-01-12 15:14:50 -05:00
Paul Dino Jones bcb633e774 fix typo on file type 2026-01-12 15:11:58 -05:00
Paul Dino Jones ac6c996602 create rpm build dirs first 2026-01-12 15:08:22 -05:00
Paul Dino Jones fb0ab4b96b fix checkout command 2026-01-12 15:06:22 -05:00
Paul Dino Jones 7cde3b39fa Add rpm workflow 2026-01-12 15:04:37 -05:00
Paul Dino Jones 270bf256b7 add libxml2 dependency 2026-01-12 12:00:37 -05:00
Paul Dino Jones 70f0ca1b50 adding first attempt at rpm spec file 2026-01-12 11:47:43 -05:00
Paul Dino Jones 6091e5f9dc fix spelling error 2026-01-10 15:34:37 -05:00
Paul Dino Jones b968dccd28 matrix based package name 2026-01-10 15:33:54 -05:00
Paul Dino Jones fe644ea6bd added system info action 2026-01-10 15:15:19 -05:00
Paul Dino Jones d18a420449 update package release names 2026-01-10 15:09:05 -05:00
Paul Dino Jones 9983149d0e update to build latest ubuntu and debian packages 2026-01-10 15:02:33 -05:00
Paul Dino Jones e3fde9b71e update to build ubuntu and debian packages 2026-01-10 15:00:24 -05:00
Paul Dino Jones 59da0dd7e3 update to build ubuntu and debian packages 2026-01-10 14:59:33 -05:00
Paul Dino Jones 1f236737f2 update to build ubuntu and debian packages 2026-01-10 14:54:58 -05:00
Paul Dino Jones 4fd4654909 update to build ubuntu and debian packages 2026-01-10 14:51:10 -05:00
Paul Dino Jones 4ba6dec508 update to use self hosted runner debian-latest 2026-01-10 12:47:46 -05:00
Paul Dino Jones 3bea423613 update to use self hosted runner 2026-01-10 12:05:03 -05:00
Paul Dino Jones 1ee07edcc5 fix typo 2026-01-09 17:33:56 -05:00
Paul Dino Jones b09acd5bf0 use -p to create dirs 2026-01-09 17:23:39 -05:00
Paul Dino Jones 410e197723 try to apt update 2026-01-09 17:20:20 -05:00
Paul Dino Jones 4ed5ed50c4 try ubuntu 24 2026-01-09 17:16:41 -05:00
Paul Dino Jones 38120c0ea6 try ubuntu latest 2026-01-09 17:14:59 -05:00
Paul Dino Jones e738261512 Fix bad label 2026-01-09 17:12:14 -05:00
Paul Dino Jones d8d747a168 Add libpulse dependency 2026-01-09 17:03:18 -05:00
Paul Dino Jones cd084f183b Add step to checkout submodultes 2026-01-09 17:01:03 -05:00
Paul Dino Jones 30e704a6b1 Add step to install dependencies 2026-01-09 16:57:42 -05:00
Paul Dino Jones 49844dce11 fix indentation problem 2026-01-09 16:52:13 -05:00
Paul Dino Jones 2e4286233d fix indentation problem 2026-01-09 16:51:04 -05:00
Paul Dino Jones 4ba4dce451 fix indentation problem 2026-01-09 16:49:35 -05:00
Paul Dino Jones c8f2010c8e update README 2026-01-09 16:41:58 -05:00
Paul Dino Jones a379c32e7a monocoque debian package 2026-01-09 16:40:12 -05:00
Paul Dino Jones 46becd1cee Adding debian package control file 2026-01-09 12:47:32 -05:00
Paul Dino Jones 94968c3d4c updated readme 2026-01-07 01:01:08 -05:00
Paul Dino Jones 20ee6b7446 Adding support for Logitech G29 Wheel 2026-01-06 21:14:10 -05:00
Paul Dino Jones 1c4f20d7a7 updated readme to point to new supported sims chart 2026-01-05 13:05:11 -05:00
Paul Dino Jones 9fe8d0871d update to latest simapi for Dirt2 support 2025-12-30 11:40:40 -05:00
Paul Dino Jones 1f5823bce0 allow for non set cars to atleast use provided tyre diameters 2025-12-24 16:23:03 -05:00
M4X1K02 582c031755 feat: Add universal installer and TUI manager (#26)
* feat: Add universal installer and TUI manager

This vibe-coded project now includes a comprehensive installation system
that simplifies monocoque setup across all major Linux distributions.

Key Features:
- Universal installer (install.sh) with automatic distribution detection
- Installs binaries to ~/.local/share/monocoque (user-local, no root needed)
- Creates launcher scripts in ~/.local/bin (start-simd, start-monocoque, test-monocoque)
- Interactive TUI manager (monocoque-manager) for service management
- Complete uninstaller (tools/uninstall.sh) with selective removal
- Dynamic systemd service generation for auto-start capability

Installation Components:
- install.sh: One-command installation across Arch, Debian/Ubuntu, Fedora, openSUSE
- tools/monocoque-manager: Python TUI for real-time status monitoring
- tools/uninstall.sh: Clean removal with config/log preservation options
- Updated README.md with quick install instructions

Architecture:
- User-local installation (no system pollution)
- Binaries: ~/.local/share/monocoque/{monocoque,simapi,simshmbridge}/
- Configs: ~/.config/{monocoque,simd}/
- Launchers: ~/.local/bin/{start-*,test-*,monocoque-manager}
- Services: ~/.config/systemd/user/ (generated dynamically)

Distribution Support:
- AUR integration for Arch users (fastest installation path)
- Native package manager integration (pacman, apt, dnf, zypper)
- Comprehensive dependency handling and build automation

This reduces installation time from 30-60 minutes to 5-15 minutes while
providing better user experience and easier maintenance.

Tested on Garuda Linux (Arch-based) - successful installation, operation, and cleanup.

* refactor: address PR feedback and improve XDG compliance

- Consolidate installers: removed universal-install.sh in favor of install.sh
- Fix XDG compliance: updated install.sh, uninstall.sh, and monocoque-manager to respect XDG_DATA_HOME, XDG_CONFIG_HOME, and XDG_CACHE_HOME with proper fallbacks
- Remove --no-daemon flag from simd launcher and systemd service as requested
- Improve TUI compatibility: fixed popen execution for Kitty (confirmed working), xfce4-terminal, Alacritty, and xterm
- Add documentation: added references to spacefreak18.github.io/simapi/ in README and installer
2025-12-23 17:11:51 -05:00
Paul Dino Jones 9ce8a615a2 move duplicate loop release code to its own function 2025-12-22 22:44:47 -05:00
Paul Dino Jones 6bc921c955 update to latest simapi 2025-12-22 22:44:19 -05:00
Paul Dino Jones dc32dd2062 Update to latest simapi for car from outgauge 2025-12-18 00:23:45 -05:00
Paul Dino Jones 220646cfe5 update to latest simapi for ACEvo and ACRally support 2025-12-14 17:46:27 -05:00
Paul Dino Jones f7f4c00beb update to latest simapi for Assetto Corsa Rally support 2025-12-13 13:04:38 -05:00
Paul Dino Jones 7a91d3d0a1 fix flipped rpm and maxrpm in moza device update signature 2025-12-13 11:51:44 -05:00
Paul Dino Jones 9e2ed33bfb update to latest simapi to fix outgauge mapping 2025-12-12 15:39:57 -05:00
Paul Dino Jones 9ef8c5c1ee update to latest simapi for BeamNG workaround 2025-12-12 15:33:07 -05:00
Paul Dino Jones 187521a905 thread showstats so it does not go crazy 2025-12-12 15:26:50 -05:00
Paul Dino Jones 871a2b9a80 update to latest simapi to fix small issue using simd with udp sims 2025-12-12 12:22:59 -05:00
Paul Dino Jones 7687a95025 pull in simapi update to fix mapping issue with outgauge 2025-12-12 10:22:57 -05:00
Paul Dino Jones df49e8a3cc add support for BeamNG 2025-12-11 20:21:36 -05:00
Paul Dino Jones c5b4f872d3 Add basic support for Live For Speed through OutGauge and Outsim protocols 2025-12-11 17:49:39 -05:00
Paul Dino Jones 5ac70779d7 pull latest simapi to fix too many files bug 2025-11-27 19:12:32 -05:00
Paul Dino Jones 0b2685ac3f update to latest simapi to fix gear int value 2025-11-23 13:50:35 -05:00
Paul Dino Jones 42efd815df add lua dependency to README 2025-11-22 11:39:41 -05:00
Paul Dino Jones 8aadf53dc0 updated to latest simapi 2025-11-22 09:28:20 -05:00
Paul Dino Jones 09e200f16c update to latest simapi 2025-11-18 12:25:28 -05:00
Paul Dino Jones 7e0ce63e26 fix type for fourth gear 2025-11-10 13:02:15 -05:00
Paul Dino Jones 5bb1f1715c use new enums for flag and gear 2025-11-10 11:27:10 -05:00
Paul Dino Jones b96c6006ed fix issue with diameters.config not using gameid. Update to latest simapi 2025-10-28 12:18:59 -04:00
Olly Smith e32596d723 Initial support for latest Moza firmware LED controls (#25) 2025-10-28 01:39:33 -04:00
Paul Dino Jones ac04fd8e0a init and destroy logging with thread safety 2025-10-28 00:26:04 -04:00
Paul Dino Jones 2a86e3b2f9 add compilation targets for new mapping structure 2025-10-23 14:01:18 -04:00
Paul Dino Jones 989ccc5459 Updated to latest simapi 2025-10-23 13:52:24 -04:00
Paul Dino Jones 5fcb7f128d Add gearc to lua and to test sequence 2025-10-22 14:36:59 -04:00
Paul Dino Jones 90fb8969d4 working custom serial 2025-10-20 19:37:28 -04:00
Paul Dino Jones 4b94e68d6c consolidate cleanup gotos 2025-09-01 19:30:43 -04:00
Paul Dino Jones 7271a26e23 clean up argument parsing a little 2025-09-01 17:24:26 -04:00
Paul Dino Jones 6b5e16f59d fix freeing of xdg handle when help is specified 2025-09-01 11:52:52 -04:00
Paul Dino Jones e0337125d3 Add beginnings of custom arduino devices. 2025-09-01 11:38:36 -04:00
Paul Dino Jones 638c7d29c3 update to lastest simapi 2025-09-01 10:59:03 -04:00
Sebastian NoeandSebastian Noe edde4e2cad Provide instructions to set up monocoque (#24)
* add consolidated instructions on how to run monocoque

* reference config test

* reference user setup guide

---------

Co-authored-by: Sebastian Noe <sebastian.schneider@boxine.de>
2025-08-31 21:57:41 -04:00
Paul Dino Jones ae8904b7cf fix formatting of monocoque.config 2025-08-31 19:20:58 -04:00
Paul Dino Jones b484d7e5da add newer and better examples to monocoque.config 2025-08-31 19:14:33 -04:00
Paul Dino Jones 96a5e689ff Update to latest simapi 2025-08-29 19:15:15 -04:00
Paul Dino Jones 7926706ea1 Update to latest simapi to fix tyre diameters 2025-08-29 19:01:46 -04:00
Paul Dino Jones fce4d8e33c updated to latest simapi for tyre diameter and turbo boost 2025-08-28 17:00:08 -04:00
Paul Dino Jones 5fcea3ad88 Add led_clear_range lua function so leds can be cleared without clearing them all for certain effects 2025-08-26 18:05:09 -04:00
Paul Dino Jones e22a8911b8 updated to latest simapi 2025-08-25 20:28:16 -04:00
Paul Dino Jones 29c6ebe7a2 Update to latest simapi 2025-08-25 17:46:48 -04:00
Paul Dino Jones fc21669d81 Add disable_audio flag 2025-08-15 04:19:39 -04:00
Paul Dino Jones 03b1654112 Fix gear and engine effects remove some unnecessary code 2025-07-30 03:32:41 -04:00
Morgan Garcia 40bb0b2929 Update moza.c
Change MOZA_MAGIC_VALUE back to 0x0d from 13.
2025-07-29 20:43:49 -04:00
Morgan Garcia e13faeabc7 Update moza.c
Changed MOZA_TIMEOUT back to 1000.
2025-07-29 20:43:49 -04:00
Morgan Garcia f66af0d424 Update moza.c
moza_update function

Updated variable name to perctflt and changed the value to multiply the quotient of rpm/maxrpm by 100.

Made int perct variable to round perctflt to nearest whole number.

Updated LED activation ranges so that all 10 LEDs get activated when given rpm percentage is reached.
2025-07-29 20:43:49 -04:00
Morgan Garcia 477fcd9c6c Update gameloop.c
Reverted back to default from Spacefreak18 source now that I've tested and gotten the LEDs working.
2025-07-29 20:43:49 -04:00
Morgan Garcia f853cd9f79 Update gameloop.c
Text output update
2025-07-29 20:43:49 -04:00
Morgan Garcia 924354afa1 Update moza.c
Fixed error in BIT number increasing.
2025-07-29 20:43:49 -04:00
Morgan Garcia 409a6b7707 Update gameloop.c
Comment out normal LED test.

Added test for quicker push to the wheel for troubleshooting.
2025-07-29 20:43:49 -04:00
Morgan Garcia d59e46cc64 Update moza.c
moza_update values updated for LED test.

MOZA_PAYLOAD_SIZE added per update from Spacefreak18
2025-07-29 20:43:49 -04:00
Morgan Garcia ba4c96a3f0 Update moza.c
Updated MOZA_SERIAL_TEMPLATE[3] from 0x17 to 0x13 which fixed communication issues with the device.
2025-07-29 20:43:49 -04:00
Paul Dino Jones f5a2b38ee2 Bump to latest simapi for unix timestamp ticks. Push unix timestamp ticks to lua variable. Update test logic to test on 8000 rpms. Update example lua scripts with examples of led rpm blink. 2025-07-15 16:39:41 -04:00
Paul Dino Jones 59d2ff935f fix serial receiving 2025-07-14 00:15:47 -04:00
Paul Dino Jones 34b258ab1b fix misleading log message 2025-06-16 13:19:13 -04:00
Paul Dino Jones 9261123491 accept usbwheel for wheel on usb devices in config 2025-06-16 13:02:34 -04:00
Oleg Makarenko 73c59f5b29 Add Simagic GT Neo to the udev rules, opening hidraw 2025-06-15 22:59:41 -04:00
Oleg Makarenko aaf0e4b6bc Add lua config for Simagic GT Neo 2025-06-15 22:59:41 -04:00
Oleg Makarenko 6d5b34f4b9 Add GT Neo LED update feature 2025-06-15 22:59:41 -04:00
Paul Dino Jones e5cee3c228 update to latest simapi for lmu support 2025-06-14 19:11:44 -04:00
Paul Dino Jones 9edd275984 Update to latest simapi for improvements to tyre diameter detection 2025-04-30 19:07:05 -04:00
Paul Dino Jones af060ff574 update to latest simapi to fix detecting diameters while simd is running 2025-04-29 00:01:39 -04:00
Paul Dino Jones ab956af9cb fix code around generating diameters config 2025-04-29 00:00:58 -04:00
Paul Dino Jones f81588a2a5 Re-enable shared memory test prog 2025-04-28 20:38:34 -04:00
Mon ouïe 808be1aec0 Add udev rule for Simagic P1000 support 2025-04-21 14:09:46 -04:00
Mon ouïe 3733261647 Fix HID send report commands for Simagic P1000 Haptic modules 2025-04-21 14:09:46 -04:00
Paul Dino Jones 1a1ede9f4b add function to expand tilde and expand tilde on device specific config files 2025-04-07 10:56:38 -04:00
Paul Dino Jones a3d0cbcb43 small cleanup to fix memory leaks 2025-04-07 10:56:38 -04:00
Paul Dino Jones c0680515ae add -c for specified config file option 2025-04-07 10:56:38 -04:00
Paul Dino Jones cd5be23235 fix issue with freeing lua file on cammus c12 2025-03-27 17:12:23 -04:00
Paul Dino Jones af37299d25 Add extra log information on revburner 2025-03-27 15:52:51 -04:00
Paul Dino Jones 8a2ce50187 improvements for cammus c12 2025-03-27 15:38:35 -04:00
Paul Dino Jones dbe6908386 fix arduino lua device free 2025-03-27 14:47:34 -04:00
Paul Dino Jones 51cc376eb9 Temporarily remove feature to ignore reattempting to open a serial device after it had failed once. Increase number of allowed serial device blocks to 20. 2025-03-27 01:51:37 -04:00
Paul Dino Jones 415526c4d3 Move release of serial adapter to before underlying device is released 2025-03-26 18:25:26 -04:00
Paul Dino Jones 52f5c824f8 Fix some pulseaudio stuff 2025-03-26 17:35:05 -04:00
Paul Dino Jones ce200b8d21 Improve logging over device specific configs 2025-03-26 16:41:18 -04:00
Paul Dino Jones e110ac50d2 remove debug build hardcode 2025-03-26 15:20:43 -04:00
Paul Dino Jones d589147552 actually pass the config file around 2025-03-26 15:16:04 -04:00
Paul Dino Jones d9cdd18bf9 Move device specific config file test to more logical spot 2025-03-26 15:01:06 -04:00
Paul Dino Jones 4db0f6dff1 Fix tachometer config 2025-03-26 13:03:50 -04:00
Paul Dino Jones a290a346cb Fix leak in arduino lua 2025-03-26 12:35:16 -04:00
Paul Dino Jones 4fd2658a98 Remove log entries 2025-03-26 11:58:39 -04:00
Paul Dino Jones 79f0ceff28 Cleanup pulseaudio code 2025-03-26 11:58:05 -04:00
Paul Dino Jones fdd5f0b7ba Fix leak in xdglib 2025-03-26 11:57:40 -04:00
Paul Dino Jones 0d29fd7005 fix leak in config 2025-03-26 11:56:31 -04:00
Paul Dino Jones b68af9c8b9 fix leak in cammus c12 2025-03-26 11:56:04 -04:00
Paul Dino Jones 12570f5caf fix leak in wheel device 2025-03-26 11:55:43 -04:00
Paul Dino Jones 4a0c33d5a5 Fix leak in serial adapter 2025-03-26 11:55:18 -04:00
Paul Dino Jones 831a9d00e1 Fix segfault freeing cammus c12 2025-03-25 14:57:52 -04:00
Paul Dino Jones 2b2ae31907 Rename to car radar.lua 2025-03-25 14:03:58 -04:00
Paul Dino Jones 593a415cd9 fix colors 2025-03-25 13:54:16 -04:00
Paul Dino Jones a4acbf5cc7 Fix off by one on rgb math 2025-03-25 13:52:22 -04:00
Paul Dino Jones e92ba3dd25 Add example for cars alongside 2025-03-25 13:25:11 -04:00
Paul Dino Jones e78fa76e1b Add flags to test loop 2025-03-25 12:57:55 -04:00
Paul Dino Jones 22da3b405c Do not write to rpm leds on cammus c12 2025-03-25 12:31:00 -04:00
Paul Dino Jones 8c1298e7f3 Always write to led 2025-03-25 10:09:39 -04:00
Paul Dino Jones 988468d746 Huge refactor to improve memory efficiency mostly around pulseaudio 2025-03-25 10:04:39 -04:00
Paul Dino Jones 3b1f2e46f3 Remove some unnecessary variables from lua variable setup 2025-03-25 10:04:14 -04:00
Paul Dino Jones 97d935c037 Properly release lua if it is used on cammus 2025-03-25 10:03:38 -04:00
Paul Dino Jones 2660394ccf Fix logging spam 2025-03-24 16:43:09 -04:00
Paul Dino Jones 18f6dc290a Fix segfaults on device specific config files 2025-03-24 15:58:37 -04:00
Paul Dino Jones db081513db Fix issue with serial devices opening when they have already failed once 2025-03-24 15:57:50 -04:00
Paul Dino Jones 8157aa1258 Fix issue with not found configs 2025-03-24 14:22:20 -04:00
Paul Dino Jones 4d1d43821c Fix off by one error. Fix log format 2025-03-24 14:07:16 -04:00
Paul Dino Jones ab3152e9e4 Increase cammus c12 leds 2025-03-24 13:22:45 -04:00
Paul Dino Jones ac17b24cf6 Make sure leds clear each time 2025-03-24 12:13:39 -04:00
Paul Dino Jones 97d69bec9c Fix to use lua on c12 if lua script loads properly 2025-03-24 12:05:49 -04:00
Paul Dino Jones e4b32f1843 Fix issue with arduino leds not clearing 2025-03-24 11:44:44 -04:00
Paul Dino Jones a6353d3469 change buffer log to first byte, probably safe to assume there will be atleast 1 byte in the buffer. 2025-03-23 18:10:03 -04:00
Paul Dino Jones 5ef7b2f683 Fix led lua code so the buffer do not include device specific magic values 2025-03-23 18:05:58 -04:00
Paul Dino Jones 025f88f559 Fix some issues with sound device code 2025-03-22 19:24:15 -04:00
Paul Dino Jones 0fb2571848 Add ability to use lua on cammus c12 2025-03-22 19:16:54 -04:00
Paul Dino Jones d185314a31 Add Suspension travel effect 2025-03-21 19:45:34 -04:00
Paul Dino Jones 5bc02c012d Fix issues with config segfaults 2025-03-21 19:44:55 -04:00
Paul Dino Jones 6ed403e4d5 Add ability to use car proximity for lights 2025-03-21 13:55:56 -04:00
Paul Dino Jones 9a23eef998 Add more logging to config loading 2025-03-21 13:55:21 -04:00
Paul Dino Jones 4e0deaf695 Add AMPLIFY modulation 2025-03-15 23:42:15 -04:00
Paul Dino Jones fd0b28e66f Add lua include dir to cmake 2025-03-11 12:23:02 -04:00
Paul Dino Jones 34cefb5aa7 Fix lua cmake dependency. Use specified frequency as base frequency for engine effects 2025-03-11 12:01:24 -04:00
Paul Dino Jones d846305114 Updating configuration to reflect new simled code 2025-02-25 22:13:49 -05:00
Paul Dino Jones a35354be5b Add support for simhub protocol and custom lua scripts to control leds 2025-02-25 18:41:10 -05:00
Paul Dino Jones 5cc8a1fb1a Updated to latest simapi for getpid improvements 2025-02-19 15:46:44 -05:00
Paul Dino Jones 7711c1939b Update sketches to use 115200 baud 2025-02-12 15:04:38 -05:00
Paul Dino Jones 5d055fd1ad fix improper detecting of usb haptic device 2025-02-01 17:01:50 -05:00
85 changed files with 5757 additions and 1168 deletions
+81
View File
@@ -0,0 +1,81 @@
name: Make Packages
on:
push:
tags: [ "*" ]
jobs:
build-monocoque-debs:
strategy:
matrix:
os: [ubuntu-latest, debian-latest, debian-stable]
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v1
- name: Checkout submodules
run: git submodule update --init --recursive
- name: Update apt
run: sudo apt update
- name: Install Dependencies
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: 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 }}
-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: Copy script files around to stop .github from being added to the package then build the package
run: |
mkdir PKG_SOURCE
mkdir -p PKG_SOURCE/DEBIAN
mkdir -p PKG_SOURCE/usr/bin/
cp ./tools/distro/debian/dpkg/${{ matrix.os }}/control ./PKG_SOURCE/DEBIAN/control
cp ./build/monocoque ./PKG_SOURCE/usr/bin/monocoque
dpkg-deb --build PKG_SOURCE monocoque-${{ matrix.os }}.deb
- name: Release the Package
uses: softprops/action-gh-release@v1
with:
files: monocoque-${{ matrix.os }}.deb
build-monocoque-rpms:
strategy:
matrix:
os: [fedora-43]
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- name: create rpmbuild dirs
run: mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
- name: get spec file
run: curl -o ~/fedora.spec https://raw.githubusercontent.com/Spacefreak18/monocoque/refs/heads/master/tools/distro/fedora/rpm/fedora.spec
- name: run spec file
run: rpmbuild -ba ~/fedora.spec
- name: rename file
run: cp ~/rpmbuild/RPMS/x86_64/monocoque-0.0.5-1.x86_64.rpm $GITHUB_WORKSPACE/monocoque-${{ matrix.os }}.rpm
- name: Release the Package
uses: softprops/action-gh-release@v1
with:
files: monocoque-${{ matrix.os }}.rpm
+1
View File
@@ -1,3 +1,4 @@
# small known issue with argtable2
{
argtable arg_parse
Memcheck:Leak
+52 -19
View File
@@ -1,7 +1,4 @@
# minimum CMake version required for C++20 support, among other things
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)
@@ -20,6 +17,26 @@ 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_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}"
)
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()
#FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
# HINTS $ENV{LIBUSB_ROOT}
# PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
@@ -34,16 +51,32 @@ set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux
set(BUILD_SHARED_LIBS TRUE) # HIDAPI as static library on all platforms
add_executable(monocoque src/monocoque/monocoque.c)
if(USE_PULSEAUDIO)
message("Using pulseaudio backend...")
add_compile_definitions(USE_PULSEAUDIO=true)
target_link_libraries(monocoque m hidapi-hidraw pulse serialport xml2 argtable2 config gameloop helper devices slog simulatorapi uv xdg-basedir lua5.4)
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)
if (LIBPROC2_VERSION VERSION_GREATER_EQUAL "4.0.5")
#add_compile_definitions(USE_OLD_PID_VAL=0)
else()
add_compile_definitions(USE_OLD_PID_VAL=1)
endif()
else()
message("Using portaudio backend...")
target_link_libraries(monocoque m hidapi-hidraw portaudio serialport xml2 argtable2 config gameloop helper devices slog simulatorapi uv xdg-basedir lua5.4)
message(FATAL_ERROR "Either libprocps or libproc2 is required")
endif()
target_include_directories(monocoque PUBLIC config ${LIBXML_INCLUDE_DIR})
#if(USE_PULSEAUDIO)
#else()
#endif()
message("Using pulseaudio backend...")
add_compile_definitions(USE_PULSEAUDIO=true)
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})
add_subdirectory(src/monocoque/gameloop)
add_subdirectory(src/monocoque/simulatorapi)
@@ -76,15 +109,15 @@ add_subdirectory(src/monocoque/slog)
#target_link_libraries(parserevburnerxml 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(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)
+39
View File
@@ -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`. ![System Settings in AMS2](https://static.wixstatic.com/media/910f3b_adabfa94a57944cca33e488972534fdd~mv2.png/v1/fill/w_964,h_374,al_c,q_90,usm_0.66_1.00_0.01,enc_avif,quality_auto/game_setup_ams2_1.png) <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
+61 -89
View File
@@ -8,37 +8,70 @@ _ / / / / /_/ / / / / /_/ / /__ / /_/ / /_/ // /_/ // __/
```
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 60 frames per seconds.
- Modular design for support with various titles and devices.
- Supports bass shakers, tachometers, several wheels, 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, simwind, and simhaptic effects for motors.
- 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.
- Choice of Portaudio or Pulseaudio (libpulse) backend.
- 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.
## Supported Games ( see [simapi](https://github.com/spacefreak18/simapi) for more details of what is supported from each sim )
- Using [SimSHMBridge](https://github.com/spacefreak18/simshmbridge)
- Asseto Corsa
- Assetto Corsa Competizione
- Project Cars 2 (untested)
- Automobilista 2
- Using [scs-sdk-plugin](https://github.com/jackz314/scs-sdk-plugin/releases)
- Euro Truck Simuator 2
- American Truck Simulator
## 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/
- Using [rfactor 2 plugin](https://github.com/schlegp/rF2SharedMemoryMapPlugin_Wine)
- RFactor 2
## Quick Install
- Using UDP
- Automobilista 2
- Project Cars 2 (untested)
**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```
## Dependencies
**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
- libserialport - arduino serial devices
- hidapi - usb hid devices
- hidapi - usb hid devices (hidraw)
- portaudio - sound devices (haptic bass shakers)
- libpulse - sound devices (haptic bass shakers)
- libuv base event loop
@@ -46,14 +79,15 @@ Cross Platform device manager for driving and flight simulators, for use with co
- argtable2
- libconfig
- xdg-basedir
- lua
- libproc2
- [slog](https://github.com/kala13x/slog) (static)
- [simshmbridge](https://github.com/spacefreak18/simshmbridge) - for sims that need shared memory mapping like AC and Project Cars related.
- [simapi](https://github.com/spacefreak18/simapi)
```
pacman -Syu git cmake pulse-native-provider libxdg-basedir libserialport libconfig libuv argtable hidapi
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
@@ -67,74 +101,17 @@ cmake ..
make
```
to use the pulseaudio backend use this cmake command
```
cmake -DUSE_PULSEAUDIO=YES ..
```
## Bass Shaker Sound Devices
When using pulseaudio it is necesarry to provide a devid in the configuration. This command should list available sound cards with the "Name" being the "devid" needed in the monocoque config.
```
pactl list sinks | grep -E -i 'index:|name:' -A 4
```
One can generate sample sine waves at specific frequency on a specific channel on a specific sound card with this command (change -d to the Name of your desired target card and change 440 to any frequency)
```
ffmpeg -f lavfi -i "sine=f=440" -af "pan=7.1|c1=c0" -f wav pipe:1 | paplay --no-remap --no-remix -d alsa_output.pci-0000_00_1b.0.analog-stereo --volume=65536 -n "Monocoque" --stream-name="Haptic Test"
```
(BE CAREFUL!!! AS THIS SETS THE MAXIMUM STREAM VOLUME!!! the -n and --stream-name are optional but it helps you identify the stream in pavucontrol)
The channel map from the pactl command is the order of channels. Change c1 in this above command to the desired output channel.
The effects generated by monocoque should match the effects generated by this ffmpeg command. The frequency will differ slightly due to having to [fit in the buffer size](https://stackoverflow.com/questions/40078417/how-to-calculate-minimum-cycles-of-sine-wave-to-have-integer-n-cycles-in-a-buffe)
(monocoque lowers the target frequency until it fits in the audio buffer).
## Using Arduino Devices
Currently Monocoque supports simwind and shiftlights through the included arduino sketches which have been tested on Uno boards. The simwind controller requires a Motor shield.
There are included Makefiles for each controller. For now, the makefiles expect the device to be attached at /dev/ttyACM0. So unplug both controllers, and then plug in just the
controller you're updating to ensure the correct controller is at /dev/ttyACM0.
To compile and upload these sketches, the Makefiles use arduino-cli. Unfortunately it seems some distributions such as debian do not include this in the repositories. If this is
the case follow the install instructions here:
```
https://arduino.github.io/arduino-cli/0.35/installation/
```
You may have to download the core libraries, it will prompt you to do so if you do not have them and you go further
```
arduino-cli core install arduino:avr
```
Then for shiftlights navigate to included shiftlight directory ( be sure only the shiftlight controller is plugged into the machine and is available at /dev/ttyACM0 ) and
```
arduino-cli lib install FastLED
make
```
Then for simwind navigate to the included simwind directory ( be sure only the simwind controller is plugged into the machine and is available at /dev/ttyACM0 ) and
```
ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/adafruit/Adafruit_Motor_Shield_V2_Library.git
ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/adafruit/Adafruit_BusIO.git
make
```
### Uploading sketch to Arduino Uno
```
cd ../src/arduino/{simwind/shiftlights/simhaptic} # Depending on the controller you have.
make # Make sure serial connection is the same as on the host pc and it have the right group permission to access the device without root
```
### SimHaptic Ardunio Motor Connection
- Acc Pedal Motor to be connected to M1
- Brake Pedal Motor to be connected to M3
- you can actually connect the motors how you wish, but you'd have to make the appropriate changes to the config and to the arduino sketch.
## Getting car tyre diameter for rumbles to work
- User needs to start monocoque and drive the new car straight at 70 kmph and then stop steering, acc or brake input
## 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
```
mkdir build; cd build
@@ -149,15 +126,10 @@ make # Make sure serial connection is the same as on the host pc and it have th
valgrind -v --leak-check=full --show-leak-kinds=all --suppressions=../.valgrindrc ./monocoque play
```
### Logs file location
`~/.cache/monocoque/*.log`
## Join the Discussion
[Sim Racing Matrix Space](https://matrix.to/#/#simracing:matrix.org)
## ToDo
- add option to specify frame rate
- add ability to set frame rate per effect
- add frequency cap (low pass filter) to sound haptic effects
- add road and kerb sound haptic effects
- windows port
+29
View File
@@ -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
+60
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
Message = simdata.velocity .. ";" .. simdata.gear .. ";" .. simdata.rpm .. ";"
+246 -68
View File
@@ -1,68 +1,246 @@
configs = ({
sim = 0;
car = "default";
devices = ( { device = "USB";
type = "Tachometer";
devid = "98FD:83AC";
subtype = "Revburner";
granularity = 2;
config = "~/.config/monocoque/revburner15000.xml"; },
{ device = "Sound";
effect = "Engine";
devid = "alsa_output.usb-Generic_USB2.0_Device_20170726905959-00.analog-stereo";
pan = 1;
volume = 90;
frequency = 27; },
{ device = "Sound";
effect = "Gear"
devid = "alsa_output.usb-Generic_USB2.0_Device_20170726905959-00.analog-stereo";
pan = 1;
duration = .25;
volume = 100;
frequency = 27; },
{ device = "USB";
type = "Haptic";
subtype = "ClubSportElitePedalsV3";
effect = "ABS";
tyre = "ALL";
threshold = 0.05; },
{ device = "USB";
type = "Haptic";
subtype = "ClubSportElitePedalsV3";
effect = "Slip";
tyre = "REARS";
threshold = 0.75; },
{ device = "USB";
type = "Haptic";
subtype = "ClubSportElitePedalsV3";
effect = "Lock";
tyre = "ALL";
threshold = 0.75; },
{ device = "Serial";
type = "SimWind";
config = "None";
devpath = "/dev/ttyACM1"; },
{ device = "Serial";
type = "ShiftLights";
config = "None";
devpath = "/dev/ttyACM0"; },
{ 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";}
);
});
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";
},
{
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";
});
}
);
+48
View File
@@ -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
+55
View File
@@ -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
+91
View File
@@ -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
Executable
+554
View File
@@ -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 "$@"
+166
View File
@@ -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
+30
View File
@@ -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() {
}
-1
View File
@@ -1 +0,0 @@
../../monocoque/simulatorapi/simapi/simapi/simdata.h
+166
View File
@@ -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
+15
View File
@@ -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
+56
View File
@@ -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;
}
+8 -2
View File
@@ -25,12 +25,14 @@ set(devices_source_files
usb/cslelitev3.c
usb/simagicp1000.h
usb/simagicp1000.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
sound/usb_generic_shaker.h
sound/usb_generic_shaker.c
usb/wheels/gtneo.h
usb/wheels/gtneo.c
sound/usb_generic_shaker_pulse.c
serial/arduino.h
serial/arduino.c
@@ -38,6 +40,10 @@ set(devices_source_files
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")
+126 -97
View File
@@ -21,7 +21,7 @@
bool hasTyreDiameter(SimData* simdata)
{
if (simdata->tyrediameter[0] == -1 || simdata->tyrediameter[1] == -1 || simdata->tyrediameter[2] == -1 || simdata->tyrediameter[3] == -1)
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;
@@ -30,11 +30,11 @@ bool hasTyreDiameter(SimData* simdata)
return true;
}
int loadtyreconfig(SimData* simdata, char* configfile)
int loadtyreconfig(SimData* simdata, char* configfile, bool setDiameters)
{
config_t cfg;
config_init(&cfg);
config_setting_t* config_cars = NULL;
const config_setting_t* config_cars = NULL;
if (!config_read_file(&cfg, configfile))
{
@@ -53,8 +53,9 @@ int loadtyreconfig(SimData* simdata, char* configfile)
}
slogt("parsing diameters config file");
int cars = config_setting_length(config_cars);
const int cars = config_setting_length(config_cars);
bool foundCar = false;
int i = 0;
while (i<cars)
{
@@ -75,15 +76,15 @@ int loadtyreconfig(SimData* simdata, char* configfile)
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_string(config_car, "sim", &simstr);
if(found == 0)
{
int found = config_setting_lookup_int(config_car, "sim", &sim);
}
else
{
sim = simapi_strtogame(simstr);
}
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)
@@ -94,10 +95,15 @@ int loadtyreconfig(SimData* simdata, char* configfile)
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);
simdata->tyrediameter[0] = tyre0;
simdata->tyrediameter[1] = tyre1;
simdata->tyrediameter[2] = tyre2;
simdata->tyrediameter[3] = tyre3;
foundCar = true;
if(setDiameters == true)
{
simdata->tyrediameter[0] = tyre0;
simdata->tyrediameter[1] = tyre1;
simdata->tyrediameter[2] = tyre2;
simdata->tyrediameter[3] = tyre3;
}
break;
}
}
@@ -113,7 +119,11 @@ int loadtyreconfig(SimData* simdata, char* configfile)
config_destroy(&cfg);
return 0;
if(foundCar == true)
{
return i;
}
return -1;
}
int savetyreconfig(SimData* simdata, char* configfile)
@@ -142,8 +152,8 @@ int savetyreconfig(SimData* simdata, char* configfile)
setting = config_setting_add(carobject, "car", CONFIG_TYPE_STRING);
config_setting_set_string(setting, simdata->car);
setting = config_setting_add(carobject, "sim", CONFIG_TYPE_INT);
config_setting_set_string(setting, simapi_gametostr(simdata->simexe));
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);
@@ -160,7 +170,7 @@ int savetyreconfig(SimData* simdata, char* configfile)
config_destroy(&cfg);
}
slogi("New configuration successfully written to: %s\n", configfile);
slogi("New configuration successfully written to: %s for sim %i, car %s\n", configfile, simdata->simexe, simdata->car);
config_destroy(&cfg);
@@ -194,89 +204,72 @@ double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold,
wheelslip[2] = 0;
wheelslip[3] = 0;
if(simdata->car == NULL)
int sim_slip_ratio =0;
for (int i = 0; i < 4; i++)
{
slogw("sim is not compatible with wheel and tyre effects, or car and session not loaded.");
return 0;
}
if(simdata->car[0] == '\0')
{
slogw("sim is not compatible with wheel and tyre effects, or car not loaded.");
return 0;
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:
slogd("Unknown effect type");
}
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]);
}
slogt("wheel vibration calculation with wheel config set to %i configchecked %i configfile %s car %s", useconfig, *configcheck, configfile, simdata->car);
switch (effecttype)
{
case (EFFECT_TYRESLIP):
case (EFFECT_TYRELOCK):
case (EFFECT_ABSBRAKES):
if(useconfig == 1 && configfile != NULL)
{
// check for saved tyre diameter in config file
// if not saved version exists get tyre diameter and save it
// use config check variable to track if the config check has been performed
// avoid many opens of the same file
int error = 0;
if(hasTyreDiameter(simdata)==false && *configcheck == 0)
{
slogi("attempting load of tyre diameter config");
error = loadtyreconfig(simdata, configfile);
*configcheck = 1;
}
if(hasTyreDiameter(simdata)==false)
{
slogt("could not find tyre diameter in config file, attempting to calculate new");
getTyreDiameter(simdata);
if(hasTyreDiameter(simdata)==true)
{
slogi("saving tyre config");
error = savetyreconfig(simdata, configfile);
}
}
}
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;
default:
slogd("Unknown effect type");
}
if(simdata->Yvelocity <= 0)
{
return 0;
}
if(simdata->Zvelocity > 1 || simdata->Zvelocity < -1)
{
return 0;
}
switch (effecttype)
{
case (EFFECT_TYRESLIP):
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
@@ -386,6 +379,42 @@ double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold,
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;
+4
View File
@@ -17,5 +17,9 @@ typedef struct
HapticEffect;
double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold, 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
+159 -60
View File
@@ -9,7 +9,7 @@
#include "../serialadapter.h"
#include "../../slog/slog.h"
#define arduino_timeout 5000
#define arduino_timeout 9000
int arduino_check(enum sp_return result)
{
@@ -42,69 +42,123 @@ int arduino_update(SerialDevice* serialdevice, void* data, size_t size)
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)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
return serialdevice->id;
}
int arduino_customled_init(SerialDevice* serialdevice, const char* portdev, const char* luafile)
int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool uselights)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
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 numlights = 0;
int result = 0;
unsigned int timeout = 2000;
result = monocoque_serial_write_block(serialdevice->id, &bytes1, bufsize1, timeout);
result = monocoque_serial_read_block(serialdevice->id, &recv_buf1, recv_bufsize1, timeout);
//slogi("wrote %i bytes", result);
//sleep(2);
//monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevice->id];
//result = arduino_check(sp_blocking_read(monocoque_serial_dev.port, &recv_buf1, recv_bufsize1, 5000));
//slogi("read %i bytes", result);
//result = sp_blocking_read(serialdevice->port, &recv_buf1, recv_bufsize1, timeout);
char numstr[recv_bufsize1];
for(int j = 0; j < recv_bufsize1; j++)
monocoque_serial_device serialdev = monocoque_serial_devices[serialdevice->id];
int error = 0;
if(uselights == true)
{
numstr[j] = '\0';
error = GetNumberOfLeds(serialdevice, &numlights);
slogi("numlights is %i\n", numlights);
// close, error and free if numlights is 0
serialdevice->numleds = numlights;
}
for(int j = 0; j < recv_bufsize1; j++)
{
if(recv_buf1[j] != 0 && recv_buf1[j] != 0x0d && recv_buf1[j] != 0x0a)
{
numstr[j] = recv_buf1[j];
}
}
int numlights = atoi(numstr);
serialdevice->numleds = numlights;
slogi("numlights is %i\n", numlights);
if(luafile == NULL)
{
return serialdevice->id;
}
slogi("LUA config specified for this device... initializing...");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
@@ -115,19 +169,22 @@ int arduino_customled_init(SerialDevice* serialdevice, const char* portdev, cons
/* 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->L = L;
serialdevice->m.L = L;
slogi("LUA config setup successful.");
return serialdevice->id;
}
int arduino_simled_update(SimDevice* this, SimData* simdata)
int arduino_simled_update(SerialDevice* serialdevice, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
int result = 1;
int total_leds = serialdevice->numleds;
@@ -211,19 +268,23 @@ int arduino_simled_update(SimDevice* this, SimData* simdata)
return result;
}
int arduino_customled_update(SimDevice* this, SimData* simdata)
int arduino_customled_update(SerialDevice* serialdevice, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
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;
@@ -240,10 +301,10 @@ int arduino_customled_update(SimDevice* this, SimData* simdata)
bytes[bufsize-3] = 0xff;
lua_State* L = serialdevice->L;
lua_State* L = serialdevice->m.L;
lua_pushstring(L, "buff");
lua_pushlightuserdata(L, &bytes);
lua_pushlightuserdata(L, &ledbytes);
lua_settable(L, LUA_REGISTRYINDEX);
simdata_to_lua(L, simdata);
@@ -257,6 +318,7 @@ int arduino_customled_update(SimDevice* this, SimData* simdata)
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");
@@ -275,13 +337,17 @@ int arduino_customled_update(SimDevice* this, SimData* simdata)
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);
arduino_update(serialdevice, &bytes, size);
// move to free
slogt("custom led wrote %i bytes", result);
return result;
}
@@ -321,11 +387,44 @@ int arduino_customled_free(SerialDevice* serialdevice, bool lua)
}
size_t size = sizeof(bytes);
int result = monocoque_serial_write(serialdevice->id, &bytes, size, arduino_timeout);
int result = monocoque_serial_write_block(serialdevice->id, &bytes, size, arduino_timeout);
if(lua == true)
if(lua == false)
{
lua_close(serialdevice->L);
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 -3
View File
@@ -5,10 +5,11 @@
#include "../serialdevice.h"
int arduino_simled_update(SimDevice* this, SimData* simdata);
int arduino_simled_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_update(SerialDevice* serialdevice, void* data, size_t size);
int arduino_customled_init(SerialDevice* serialdevice, const char* portdev, const char* luafile);
int arduino_customled_update(SimDevice* this, SimData* simdata);
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 arduino_customled_free(SerialDevice* serialdevice, bool lua);
+167 -39
View File
@@ -3,26 +3,101 @@
#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");
return 1; // Return the table to Lua
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)
@@ -106,12 +181,12 @@ int set_led_range_to_color(lua_State *L)
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");
@@ -122,17 +197,17 @@ int set_led_range_to_color(lua_State *L)
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("tenth byte of buff is x%02x", bytes[10]);
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) + 11 + 0] = color0;
bytes[(i * 3) + 11 + 1] = color1;
bytes[(i * 3) + 11 + 2] = color2;
bytes[(i * 3) + 0] = color0;
bytes[(i * 3) + 1] = color1;
bytes[(i * 3) + 2] = color2;
}
}
@@ -144,9 +219,14 @@ int set_led_range_to_rgb_color(lua_State *L)
int range_start = lua_tonumber(L, 1);
int range_end = lua_tonumber(L, 2);
int color0 = lua_tonumber(L, 3);
int color1 = lua_tonumber(L, 4);
int color2 = lua_tonumber(L, 5);
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);
@@ -163,7 +243,7 @@ int set_led_range_to_rgb_color(lua_State *L)
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(range_end > numlights)
{
range_end = numlights;
@@ -178,13 +258,13 @@ int set_led_range_to_rgb_color(lua_State *L)
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("tenth byte of buff is x%02x", bytes[10]);
slogt("first byte of buff is x%02x", bytes[0]);
for( int i = range_start; i < range_end; i++)
{
bytes[(i * 3) + 11 + 0] = color0;
bytes[(i * 3) + 11 + 1] = color1;
bytes[(i * 3) + 11 + 2] = color2;
bytes[(i * 3) + 0] = color0;
bytes[(i * 3) + 1] = color1;
bytes[(i * 3) + 2] = color2;
}
}
@@ -198,7 +278,7 @@ int set_led_to_color(lua_State *L)
slogd("lua led is %i", led);
slogd("lua color is %i", color);
led = led - 1;
lua_getglobal(L, "TotalLeds");
@@ -218,15 +298,15 @@ int set_led_to_color(lua_State *L)
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("tenth byte of buff is x%02x", bytes[10]);
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) + 11 + 0] = color0;
bytes[(led * 3) + 11 + 1] = color1;
bytes[(led * 3) + 11 + 2] = color2;
bytes[(led * 3) + 0] = color0;
bytes[(led * 3) + 1] = color1;
bytes[(led * 3) + 2] = color2;
}
@@ -236,15 +316,17 @@ 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 color0 = lua_tonumber(L, 2);
int color1 = lua_tonumber(L, 3);
int color2 = lua_tonumber(L, 4);
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");
@@ -264,11 +346,11 @@ int set_led_to_rgb_color(lua_State *L)
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("tenth byte of buff is x%02x", bytes[10]);
bytes[(led * 3) + 11 + 0] = color0;
bytes[(led * 3) + 11 + 1] = color1;
bytes[(led * 3) + 11 + 2] = color2;
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;
}
@@ -285,18 +367,64 @@ int led_clear_all(lua_State *L)
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("tenth byte of buff is x%02x", bytes[10]);
slogt("first byte of buff is x%02x", bytes[0]);
for( int i = 0; i < numlights; i++)
{
bytes[(i * 3) + 11 + 0] = 0x00;
bytes[(i * 3) + 11 + 1] = 0x00;
bytes[(i * 3) + 11 + 2] = 0x00;
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;
}
}
+9 -3
View File
@@ -1,9 +1,14 @@
#ifndef _ARDUINOLEDLUA_H
#define _ARDUINOLEDLUA_H
#include "lua5.4/lua.h"
#include "lua5.4/lauxlib.h"
#include "lua5.4/lualib.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"
@@ -21,5 +26,6 @@ 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
+32 -29
View File
@@ -10,70 +10,71 @@
#include "../../slog/slog.h"
#define MOZA_TIMEOUT 1000
#define MOZA_SERIAL_TEMPLATE {0x7e, 0x06, 0x41, 0x17, 0xfd, 0xde, 0x0, 0x0, 0x0, 0x0, 0x0}
#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 char moza_checksum(unsigned char *data, unsigned short size)
{
unsigned char ret = MOZA_MAGIC_VALUE;
for (short i = 0; i < sizeof(data)/sizeof(data[0]); i++)
unsigned int ret = MOZA_MAGIC_VALUE;
for (unsigned short i = 0; i < size; i++)
{
ret += data[i];
}
return ret;
return ret % 0x100;
}
int moza_update(SerialDevice* serialdevice, unsigned short maxrpm, unsigned short rpm)
int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short maxrpm)
{
unsigned char bytes[] = MOZA_SERIAL_TEMPLATE;
int size = sizeof(bytes)/sizeof(bytes[0]);
unsigned short size = MOZA_PAYLOAD_SIZE;
float perctflt = ((float)rpm/(float)maxrpm)*100;
int perct = round(perctflt);
float perct = (float)rpm/(float)maxrpm;
if (perct >= 0.8)
if (perct >= 10)
bytes[9] |= BIT(0);
if (perct >= 0.83)
if (perct >= 20)
bytes[9] |= BIT(1);
if (perct >= 0.86)
if (perct >= 30)
bytes[9] |= BIT(2);
if (perct >= 0.89)
if (perct >= 40)
bytes[9] |= BIT(3);
if (perct >= 0.91)
bytes[8] |= BIT(4);
if (perct >= 50)
bytes[9] |= BIT(4);
if (perct >= 0.92)
bytes[8] |= BIT(5);
if (perct >= 60)
bytes[9] |= BIT(5);
if (perct >= 0.93)
bytes[8] |= BIT(6);
if (perct >= 70)
bytes[9] |= BIT(6);
if (perct >= 0.94)
bytes[8] |= BIT(7);
if (perct >= 80)
bytes[9] |= BIT(7);
if (perct >= 0.96)
if (perct >= 90)
bytes[8] |= BIT(0);
if (perct >= 0.96)
if (perct >= 92)
bytes[8] |= BIT(1);
// blinking
if (perct >= 0.97)
bytes[9] |= BIT(MOZA_BLINKING_BIT);
if (perct >= 94)
bytes[8] |= BIT(MOZA_BLINKING_BIT);
bytes[10] = moza_checksum(bytes);
bytes[10] = moza_checksum(bytes, size);
int result = 1;
if (serialdevice->port)
{
slogd("copying %i bytes to moza device", size);
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);
}
@@ -83,6 +84,8 @@ int moza_update(SerialDevice* serialdevice, unsigned short maxrpm, unsigned shor
int moza_init(SerialDevice* serialdevice, const char* portdev)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
return serialdevice->id;
int id = monocoque_serial_open(serialdevice, portdev);
if (id < 0) return id;
serialdevice->id = id;
return 0;
}
+2 -4
View File
@@ -4,10 +4,8 @@
#include "../serialdevice.h"
#include "../simdevice.h"
int moza_update(SerialDevice* serialdevice, unsigned short maxrpm, unsigned short rpm);
int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short maxrpm);
int moza_init(SerialDevice* serialdevice, const char* portdev);
int moza_free(SerialDevice* serialdevice);
int moza_serial_check(enum sp_return result);
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
+98
View File
@@ -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;
}
+10
View File
@@ -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
+84 -40
View File
@@ -27,10 +27,13 @@ int check(enum sp_return result)
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;
@@ -57,10 +60,13 @@ int monocoque_serial_free(SerialDevice* serialdevice)
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;
free(monocoque_serial_devices[serialdevice->id].portname);
}
else
{
@@ -70,23 +76,71 @@ int monocoque_serial_free(SerialDevice* serialdevice)
}
}
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout)
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];
slogt("port name: %s, busy %i, open %i, openfail %i", monocoque_serial_dev.portname, monocoque_serial_dev.busy, monocoque_serial_dev.open, monocoque_serial_dev.busy);
int retval;
struct sp_event_set* eventSet = NULL;
if(monocoque_serial_dev.port == 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(monocoque_serial_dev.busy == false && monocoque_serial_dev.open == true)
if(dev->busy == false && dev->open == true)
{
monocoque_serial_dev.busy = true;
result = sp_blocking_write(monocoque_serial_dev.port, data, size, timeout);
dev->busy = true;
result = sp_blocking_write(dev->port, data, size, timeout);
}
else
{
@@ -94,67 +148,51 @@ int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int
result = -1;
}
slogt("write result is %i", result);
monocoque_serial_dev.busy = false;
dev->busy = false;
return result;
}
int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
slogt("serial device id %i", serialdevicenum);
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
slogt("port name: %s, busy %i, open %i, openfail %i", monocoque_serial_dev.portname, monocoque_serial_dev.busy, monocoque_serial_dev.open, monocoque_serial_dev.busy);
if(monocoque_serial_dev.port == NULL)
{
sloge("port is null");
}
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1;
if(monocoque_serial_dev.open == true)
if(dev->open == true)
{
while(monocoque_serial_dev.busy == true)
while(dev->busy == true)
{
slogt("hopefully this doesn't happen long");
continue;
}
monocoque_serial_dev.busy = true;
result = sp_blocking_write(monocoque_serial_dev.port, data, size, timeout);
dev->busy = true;
result = sp_blocking_write(dev->port, data, size, timeout);
slogi("actually performed write");
}
monocoque_serial_dev.busy = false;
dev->busy = false;
return result;
}
int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
slogt("serial device id %i", serialdevicenum);
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
slogt("port name: %s, busy %i, open %i, openfail %i", monocoque_serial_dev.portname, monocoque_serial_dev.busy, monocoque_serial_dev.open, monocoque_serial_dev.busy);
if(monocoque_serial_dev.port == NULL)
{
sloge("port is null");
}
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1;
if(monocoque_serial_dev.open == true)
if(dev->open == true)
{
while(monocoque_serial_dev.busy == true)
while(dev->busy == true)
{
slogt("hopefully this doesn't happen long");
continue;
}
monocoque_serial_dev.busy = true;
result = sp_blocking_read(monocoque_serial_dev.port, data, size, timeout);
dev->busy = true;
result = sp_blocking_read(dev->port, data, size, timeout);
slogi("actually performed read");
}
monocoque_serial_dev.busy = false;
dev->busy = false;
return result;
}
@@ -165,7 +203,7 @@ int monocoque_serial_open(SerialDevice* serialdevice, const char* portdev)
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 == true)
if(monocoque_serial_devices[i].open == true && monocoque_serial_devices[i].openfail == false)
{
if(msastrcicmp(monocoque_serial_devices[i].portname, portdev) == 0)
{
@@ -204,13 +242,16 @@ int monocoque_serial_open(SerialDevice* serialdevice, const char* portdev)
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 = true;
monocoque_serial_devices[i].openfail = false;
return -1;
}
slogd("Opening port");
check(sp_open(sp, SP_MODE_READ_WRITE));
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));
@@ -219,6 +260,9 @@ int monocoque_serial_open(SerialDevice* serialdevice, const char* portdev)
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;
@@ -229,7 +273,7 @@ int monocoque_serial_open(SerialDevice* serialdevice, const char* portdev)
serial_device_num = i;
free(port_name);
slogd("Successfully setup arduino serial device...");
slogd("Successfully setup monocoque serial device...");
}
return serial_device_num;
+3 -1
View File
@@ -18,8 +18,10 @@ typedef struct
}
monocoque_serial_device;
static monocoque_serial_device monocoque_serial_devices[10];
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);
+138 -26
View File
@@ -10,6 +10,8 @@
#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/simapi/simapi/simdata.h"
#include "../slog/slog.h"
@@ -20,7 +22,20 @@ int serial_wheel_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
moza_update(serialdevice, simdata->maxrpm, simdata->rpms);
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;
}
@@ -34,6 +49,33 @@ int serialdev_update(SimDevice* this, SimData* 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;
@@ -81,8 +123,9 @@ int arduino_simwind_update(SimDevice* this, SimData* simdata)
serialdevice->u.simwinddata.velocity = ceil(simdata->velocity*KPHTOMPH);
slogt("Updating arduino device speed to %i", serialdevice->u.simwinddata.velocity);
// this can be added to the config, all config dependent can be added to init
serialdevice->u.simwinddata.fanpower = (int)0.6 * 255;
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);
@@ -97,6 +140,10 @@ int arduino_simhaptic_update(SimDevice* this, SimData* simdata)
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.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
@@ -106,23 +153,25 @@ int arduino_simhaptic_update(SimDevice* this, SimData* simdata)
{
play = 1.0;
}
int effectspeed = ceil(255 * play);
uint8_t effectspeed = ceil(255 * play);
int motor = serialdevice->motorsposition;
uint8_t motor = serialdevice->motorsposition;
if (play != serialdevice->state)
{
// 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.effect3, serialdevice->motorsposition, rplay, serialdevice->ampfactor);
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, serialdevice->motorsposition, rplay, serialdevice->ampfactor);
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->state = play;
}
@@ -149,13 +198,14 @@ int serialdev_free(SimDevice* this)
serialdevice->u.simhapticdata.effect4 = 0;
serialdevice->u.simhapticdata.motor4 = 1;
serialdevice->state = 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);
//arduino_customled_free(serialdevice, true);
free(serialdevice->m.device_specific_config_file);
break;
case ARDUINODEV__SIMLED:
arduino_customled_free(serialdevice, false);
@@ -181,7 +231,7 @@ int serial_wheel_free(SimDevice* this)
return 0;
}
int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds)
int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds, SimInfo* siminfo)
{
slogi("initializing serial device on port %s to %i...", ds->serialdevsettings.portdev, ds->serialdevsettings.baud);
int error = 0;
@@ -198,14 +248,49 @@ int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds)
// maybe this call a more generic serial wheel init first
error = moza_init(serialdevice, ds->serialdevsettings.portdev);
break;
case SERIALDEV__MOZA_NEW:
error = moza_new_init(serialdevice, ds->serialdevsettings.portdev);
break;
case SERIALDEV__MOZA_KS_PRO_WHEEL:
error = moza_ks_pro_wheel_init(serialdevice, ds->serialdevsettings.portdev);
break;
case ARDUINODEV__SIMLED__CUSTOM:
error = arduino_customled_init(serialdevice, ds->serialdevsettings.portdev, ds->serialdevsettings.config_file);
serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, serialdevice->m.device_specific_config_file, true);
if(error < 0)
{
free(serialdevice->m.device_specific_config_file);
}
if(error > 0)
{
error = 0;
}
break;
case ARDUINODEV__CUSTOM:
serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, serialdevice->m.device_specific_config_file, false);
if(error < 0)
{
free(serialdevice->m.device_specific_config_file);
}
if(error > 0)
{
error = 0;
}
break;
case ARDUINODEV__SIMLED:
error = arduino_customled_init(serialdevice, ds->serialdevsettings.portdev, NULL);
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, NULL, false);
if(error > 0)
{
error = 0;
}
break;
default:
error = arduino_init(serialdevice, ds->serialdevsettings.portdev);
if(error > 0)
{
error = 0;
}
break;
}
@@ -215,13 +300,14 @@ int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds)
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_update, &serialdev_free };
static const vtable arduino_simled_custom_vtable = { &arduino_customled_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, MonocoqueSettings* ms) {
SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
SerialDevice* this = (SerialDevice*) malloc(sizeof(SerialDevice));
@@ -230,6 +316,7 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms) {
this->m.derived = this;
this->m.vtable = &serial_simdevice_vtable;
this->type = SERIALDEV_ARDUINO;
int error = 0;
slogt("Attempting to configure arduino device with subtype: %i", ds->dev_subtype);
switch (ds->dev_subtype) {
@@ -262,9 +349,19 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms) {
case (SIMDEVTYPE_SIMWIND):
this->devicetype = ARDUINODEV__SIMWIND;
this->m.vtable = &arduino_simwind_vtable;
slogi("Initializing arduino devices for sim wind.");
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;
@@ -277,25 +374,35 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms) {
this->u.simhapticdata.effect4 = 0;
this->state = 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;
slogi("Initializing serial wheel device.");
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
this->devicetype = SERIALDEV__MOZAR5;
this->m.vtable = &serialwheel_vtable;
break;
//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)
if(this->devicetype == ARDUINODEV__HAPTIC && error == 0)
{
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.effecttype = ds->effect_type;
@@ -306,10 +413,15 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms) {
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
}
int error = serialdev_init(this, ds);
if (error < 0)
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;
}
+9 -6
View File
@@ -6,12 +6,15 @@
typedef enum
{
ARDUINODEV__SHIFTLIGHTS = 0,
ARDUINODEV__SIMWIND = 1,
ARDUINODEV__HAPTIC = 2,
SERIALDEV__MOZAR5 = 3,
ARDUINODEV__SIMLED = 4,
ARDUINODEV__SIMLED__CUSTOM = 5
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;
+20 -14
View File
@@ -24,9 +24,9 @@ int devupdate(SimDevice* this, SimData* simdata)
return 0;
}
int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms)
int devinit(SimDevice* simdevices, SimInfo* siminfo, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms)
{
slogi("initializing simdevices...");
slogi("initializing simdevices for simapi %i...", siminfo->simulatorapi);
int devices = 0;
for (int j = 0; j < numdevices; j++)
@@ -34,7 +34,7 @@ int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds, Monocoque
simdevices[j].initialized = false;
if (ds[j].dev_type == SIMDEV_USB) {
USBDevice* sim = new_usb_device(&ds[j], ms);
USBDevice* sim = new_usb_device(&ds[j], ms, siminfo);
if (sim != NULL)
{
simdevices[j] = sim->m;
@@ -50,26 +50,32 @@ int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds, Monocoque
}
if (ds[j].dev_type == SIMDEV_SOUND) {
SoundDevice* sim = new_sound_device(&ds[j], ms);
if (sim != NULL)
if(ms->disable_audio == true)
{
simdevices[j] = sim->m;
simdevices[j].initialized = true;
simdevices[j].type = SIMDEV_SOUND;
simdevices[j].fps = ds[j].fps;
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], ms);
SerialDevice* sim = new_serial_device(&ds[j], ms, siminfo);
if (sim != NULL)
{
simdevices[j] = sim->m;
+30 -11
View File
@@ -2,13 +2,16 @@
#define _SIMDEVICE_H
#include <stdbool.h>
#include <hidapi/hidapi.h>
#include "lua5.4/lua.h"
#include <lua.h>
#include "usbdevice.h"
#include "sounddevice.h"
#include "serialdevice.h"
#include "hapticeffect.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
@@ -27,6 +30,8 @@ struct SimDevice
int id;
int fps;
bool initialized;
lua_State* L;
char* device_specific_config_file;
DeviceType type;
HapticEffect hapticeffect;
};
@@ -51,7 +56,7 @@ typedef struct
int id;
SerialType type;
struct sp_port* port;
lua_State* L;
SerialDeviceType devicetype;
// move these two they only apply to the haptic device
int motorsposition;
@@ -61,6 +66,7 @@ typedef struct
int endled;
int baudrate;
double ampfactor;
double fanpower;
double state;
union
{
@@ -77,7 +83,7 @@ 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, MonocoqueSettings* ms);
SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/********* USB HID Devices *****/
typedef enum
@@ -93,6 +99,7 @@ typedef struct
{
SimDevice m;
int id;
hid_device* handle;
USBType type;
union
{
@@ -106,7 +113,7 @@ USBDevice;
int usbdev_update(SimDevice* this, SimData* simdata);
int usbdev_free(SimDevice* this);
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms);
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/********* Sound Devices *****/
@@ -117,12 +124,12 @@ typedef struct
int configcheck;
SoundEffectModulationType modulationType;
SoundData sounddata;
#ifdef USE_PULSEAUDIO
//#ifdef USE_PULSEAUDIO
pa_stream *stream;
#else
PaStreamParameters outputParameters;
PaStream* stream;
#endif
//#else
//PaStreamParameters outputParameters;
//PaStream* stream;
//#endif
}
SoundDevice;
@@ -131,7 +138,7 @@ 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, MonocoqueSettings* ms);
SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/***** Generic Methods *********/
@@ -139,10 +146,22 @@ int update(SimDevice* simdevice, SimData* simdata);
int devupdate(SimDevice* simdevice, SimData* simdata);
int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms);
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 wheeldev_update(USBDevice* usbdevice, 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
+19 -21
View File
@@ -2,8 +2,6 @@
#include "../slog/slog.h"
#ifdef USE_PULSEAUDIO
pa_threaded_mainloop* mainloop;
pa_context* context;
@@ -21,10 +19,8 @@ int setupsound()
slogi("connecting pulseaudio...");
// Get a mainloop and its context
mainloop = pa_threaded_mainloop_new();
assert(mainloop);
mainloop_api = pa_threaded_mainloop_get_api(mainloop);
context = pa_context_new(mainloop_api, "Monocoque");
assert(context);
pa_context_set_state_callback(context, &context_state_cb, mainloop);
@@ -32,48 +28,50 @@ int setupsound()
pa_threaded_mainloop_lock(mainloop);
// Start the mainloop
assert(pa_threaded_mainloop_start(mainloop) == 0);
assert(pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0);
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);
assert(PA_CONTEXT_IS_GOOD(context_state));
if (context_state == PA_CONTEXT_READY) break;
pa_threaded_mainloop_wait(mainloop);
}
pa_threaded_mainloop_unlock(mainloop);
slogi("successfully connected pulseaudio...");
return 1;
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);
}
}
#else
int setupsound()
{
slogi("connecting portaudio...");
return 0;
}
int freesound()
{
}
#endif
//int setupsound()
//{
// slogi("connecting portaudio...");
//}
//
//
//int freesound()
//{
//
//}
@@ -80,7 +80,7 @@ int patestCallbackGearShift(const void* inputBuffer,
data->curr_duration += 1.0 / SAMPLE_RATE;
if (data->curr_duration >= data->duration) {
data->curr_duration = 0.0;
data->curr_frequency = 0;
data->curr_frequency = 0.0;
}
*out++ = sample;
@@ -3,12 +3,12 @@
#include "../simdevice.h"
#ifdef USE_PULSEAUDIO
//#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
//#else
//int usb_generic_shaker_init(SoundDevice* sounddevice);
//int usb_generic_shaker_free(SoundDevice* sounddevice);
//#endif
#endif
@@ -1,24 +1,30 @@
#ifdef USE_PULSEAUDIO
#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 .5
#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;
@@ -27,59 +33,75 @@ void gear_sound_stream(pa_stream *s, size_t length, void *userdata) {
for (size_t i = 0; i < num_samples; i++) {
static double t = 0.0;
double sample = 0;
if (data->frequency>0)
{
sample = AMPLITUDE * 32767.0 * sin(2.0 * M_PI * data->curr_frequency * data->curr_duration);
}
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;
data->curr_duration += 1.0 / SAMPLE_RATE;
if (data->curr_duration >= data->duration) {
data->curr_duration = 0.0;
data->curr_frequency = 0;
}
}
pa_stream_write(s, buffer, length, NULL, 0LL, PA_SEEK_RELATIVE);
}
double get_closest_frequency(size_t num_samples, double input_frequency) {
double samples_per_cycle = SAMPLE_RATE / input_frequency;
double adjusted_frequency = SAMPLE_RATE / (num_samples / floor(num_samples/samples_per_cycle) );
return adjusted_frequency;
}
void engine_sound_stream(pa_stream *s, size_t length, void *userdata) {
SoundData* data = (SoundData*)userdata;
double freq = data->curr_frequency;
size_t num_samples = length / sizeof(int16_t);
freq = get_closest_frequency( num_samples, freq );
//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 = AMPLITUDE * 32767.0 * sin( 2.0 * M_PI * freq * t );
double sample = ((double)data->curr_amplitude/100) * 32767.0 * sin( 2.0 * M_PI * t );
buffer[i] = (int16_t)sample;
t += 1.0 / SAMPLE_RATE;
if (t >= SAMPLE_RATE) {
t = 0;
double f = apply_noise(data->curr_frequency, data->noise);
t += f / SAMPLE_RATE;
if (t >= 1.0) {
t -= floor(t);
}
data->phase = t;
}
data->phase = round(data->phase);
pa_stream_write(s, buffer, length, NULL, 0LL, PA_SEEK_RELATIVE);
}
@@ -94,18 +116,29 @@ 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_xfree(sounddevice->stream);
}
pa_threaded_mainloop_unlock(mainloop);
return err;
}
@@ -158,7 +191,7 @@ int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* main
// recommended settings, i.e. server uses sensible values
pa_buffer_attr buffer_attr;
buffer_attr.maxlength = (uint32_t) 32767;
buffer_attr.tlength = (uint32_t) -1;
buffer_attr.tlength = (uint32_t) 2048;
buffer_attr.prebuf = (uint32_t) -1;
buffer_attr.minreq = (uint32_t) -1;
@@ -174,13 +207,16 @@ int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* main
// 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;
assert(pa_stream_connect_playback(stream, devname, &buffer_attr, stream_flags, &cv, NULL) == 0);
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(;;) {
for(;;)
{
pa_stream_state_t stream_state = pa_stream_get_state(stream);
assert(PA_STREAM_IS_GOOD(stream_state));
if (stream_state == PA_STREAM_READY) break;
PA_STREAM_IS_GOOD(stream_state);
//PA_STREAM_IS_GOOD(stream_state);
if (stream_state == PA_STREAM_READY) break;
pa_threaded_mainloop_wait(mainloop);
}
@@ -193,5 +229,3 @@ int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* main
pa_threaded_mainloop_unlock(mainloop);
return 0;
}
#endif
+169 -103
View File
@@ -4,6 +4,7 @@
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include "sound.h"
#include "simdevice.h"
@@ -20,23 +21,14 @@ int gear_sound_set(SoundDevice* sounddevice, SimData* simdata)
{
//sounddevice->sounddata.gear_sound_data = 3.14;
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude;
sounddevice->sounddata.curr_duration = 0;
}
sounddevice->sounddata.last_gear = simdata->gear;
slogt("set gear frequency to %i", sounddevice->sounddata.frequency);
slogt("set gear frequency to %f", sounddevice->sounddata.curr_frequency);
}
// we could make a vtable for these different effects too
int sounddev_engine_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
sounddevice->sounddata.curr_frequency = simdata->rpms/60;
//sounddevice->sounddata.table_size = 48000/(sounddevice->sounddata.frequency);
slogt("set engine frequency to %i", sounddevice->sounddata.frequency);
}
double modulate(SoundDevice* sounddevice, double raw_effect, SoundEffectModulationType modulation)
{
@@ -44,17 +36,37 @@ double modulate(SoundDevice* sounddevice, double raw_effect, SoundEffectModulati
switch (modulation)
{
case SOUND_EFFECT_MODULATION_FREQUENCY:
modulated_effect = (sounddevice->sounddata.frequencyMax - sounddevice->sounddata.frequency) * raw_effect;
modulated_effect = ((sounddevice->sounddata.frequencyMax - sounddevice->sounddata.frequency) * raw_effect) + sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_frequency = modulated_effect;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude;
slogt("set curr frequency to %f from raw effect %f and base frequency %i", sounddevice->sounddata.curr_frequency, raw_effect, sounddevice->sounddata.frequency);
break;
case SOUND_EFFECT_MODULATION_AMPLIFY:
modulated_effect = ((sounddevice->sounddata.amplitudeMax - sounddevice->sounddata.amplitude) * raw_effect) + sounddevice->sounddata.amplitude;
sounddevice->sounddata.curr_amplitude = modulated_effect;
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
slogt("set curr amplitude to %i from raw effect %f and base amplitude %i", sounddevice->sounddata.curr_amplitude, raw_effect, sounddevice->sounddata.amplitude);
break;
case SOUND_EFFECT_MODULATION_NONE:
default:
modulated_effect = raw_effect;
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude;
break;
}
return modulated_effect;
}
// we could make a vtable for these different effects too
int sounddev_engine_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double effect = ((double)simdata->rpms/60)/((double)simdata->maxrpm/60);
slogt("Set base effect of %f from rpms of %i", effect, simdata->rpms);
modulate(sounddevice, effect, sounddevice->modulationType);
}
int sounddev_tyreslip_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
@@ -64,13 +76,12 @@ int sounddev_tyreslip_update(SimDevice* this, SimData* simdata)
if (effect > 0)
{
effect = modulate(sounddevice, effect, sounddevice->modulationType);
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_duration = sounddevice->sounddata.duration;
modulate(sounddevice, effect, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0;
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
@@ -85,12 +96,12 @@ int sounddev_tyrelock_update(SimDevice* this, SimData* simdata)
if (play > 0)
{
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_duration = sounddevice->sounddata.duration;
modulate(sounddevice, play, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0;
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
@@ -103,16 +114,37 @@ int sounddev_absbrakes_update(SimDevice* this, SimData* simdata)
if (play > 0)
{
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_duration = sounddevice->sounddata.duration;
modulate(sounddevice, play, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0;
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.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, 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(sounddevice, effect, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
int sounddev_gearshift_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
@@ -121,51 +153,46 @@ int sounddev_gearshift_update(SimDevice* this, SimData* simdata)
}
#ifdef USE_PULSEAUDIO
int sounddev_free(SimDevice* this)
{
SoundDevice* sounddevice = (void *) this->derived;
usb_generic_shaker_free(sounddevice, mainloop);
free(sounddevice);
return 0;
}
#else
int sounddev_free(SimDevice* this)
{
SoundDevice* sounddevice = (void *) this->derived;
usb_generic_shaker_free(sounddevice);
free(sounddevice);
return 0;
}
#endif
int sounddev_init(SoundDevice* sounddevice, const char* devname, MonocoqueTyreIdentifier tyre, SoundDeviceSettings sds)
{
slogi("initializing standalone sound device...");
slogi("volume is: %i", sds.volume);
slogi("Modulation type: %i", sds.modulation);
slogi("frequency is: %i", sds.frequency);
slogi("frequency Max is: %i", sds.frequencyMax);
slogi("amplitude is: %i", sds.amplitude);
slogi("amplitude Max is: %i", sds.amplitudeMax);
slogi("pan is: %i", sds.pan);
slogi("channels is: %i", sds.channels);
slogi("duration is: %f", sds.duration);
slogi("noise is: %i", sds.noise);
sounddevice->modulationType = sds.modulation;
sounddevice->sounddata.frequency = sds.frequency;
sounddevice->sounddata.curr_frequency = 0;
sounddevice->sounddata.frequencyMax = sds.frequencyMax;
sounddevice->sounddata.amplitude = sds.amplitude;
sounddevice->sounddata.amplitudeMax = sds.amplitudeMax;
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) {
@@ -183,18 +210,18 @@ int sounddev_init(SoundDevice* sounddevice, const char* devname, MonocoqueTyreId
case (EFFECT_ABSBRAKES):
streamname = "ABS";
break;
case (EFFECT_SUSPENSION):
streamname = "Suspension";
break;
case (EFFECT_ENGINERPM):
default:
streamname = "Engine";
break;
}
#ifdef USE_PULSEAUDIO
//pa_threaded_mainloop* mainloop;
//pa_context* context;
usb_generic_shaker_init(sounddevice, mainloop, context, devname, sds.volume, sds.pan, sds.channels, streamname);
#else
usb_generic_shaker_init(sounddevice);
#endif
//usb_generic_shaker_init(sounddevice);
}
static const vtable engine_sound_simdevice_vtable = { &sounddev_engine_update, &sounddev_free };
@@ -202,74 +229,114 @@ static const vtable gear_sound_simdevice_vtable = { &sounddev_gearshift_update,
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, MonocoqueSettings* ms) {
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->effect_type);
switch (ds->effect_type) {
case (EFFECT_ENGINERPM):
this->m.hapticeffect.effecttype = EFFECT_ENGINERPM;
this->m.vtable = &engine_sound_simdevice_vtable;
slogi("Initializing sound device for engine vibrations.");
break;
case (EFFECT_GEARSHIFT):
this->m.hapticeffect.effecttype = EFFECT_GEARSHIFT;
this->m.vtable = &gear_sound_simdevice_vtable;
slogi("Initializing sound device for gear shift vibrations.");
break;
switch (ds->effect_type)
{
case (EFFECT_TYRESLIP):
this->m.hapticeffect.effecttype = EFFECT_TYRESLIP;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &tyreslip_sound_simdevice_vtable;
slogi("Initializing sound device for tyre slip vibrations.");
break;
case (EFFECT_TYRELOCK):
this->m.hapticeffect.effecttype = EFFECT_TYRELOCK;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &tyrelock_sound_simdevice_vtable;
slogi("Initializing sound device for tyre slip vibrations.");
break;
case (EFFECT_ABSBRAKES):
this->m.hapticeffect.effecttype = EFFECT_ABSBRAKES;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &absbrakes_sound_simdevice_vtable;
slogi("Initializing sound device for abs vibrations.");
break;
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;
}
slogt("Attempting to use sound device %s", ds->sounddevsettings.dev);
int error = sounddev_init(this, ds->sounddevsettings.dev, ds->tyre, ds->sounddevsettings);
if(error == 0)
{
slogt("Attempting to configure sound device with subtype: %i", ds->effect_type);
switch (ds->effect_type)
{
case (EFFECT_ENGINERPM):
this->m.hapticeffect.effecttype = EFFECT_ENGINERPM;
this->m.vtable = &engine_sound_simdevice_vtable;
slogi("Initializing sound device for engine vibrations.");
break;
case (EFFECT_GEARSHIFT):
this->m.hapticeffect.effecttype = EFFECT_GEARSHIFT;
this->m.vtable = &gear_sound_simdevice_vtable;
slogi("Initializing sound device for gear shift vibrations.");
break;
case (EFFECT_TYRESLIP):
this->m.hapticeffect.effecttype = EFFECT_TYRESLIP;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &tyreslip_sound_simdevice_vtable;
slogi("Initializing sound device for tyre slip vibrations.");
break;
case (EFFECT_TYRELOCK):
this->m.hapticeffect.effecttype = EFFECT_TYRELOCK;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &tyrelock_sound_simdevice_vtable;
slogi("Initializing sound device for tyre slip vibrations.");
break;
case (EFFECT_ABSBRAKES):
this->m.hapticeffect.effecttype = EFFECT_ABSBRAKES;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &absbrakes_sound_simdevice_vtable;
slogi("Initializing sound device for abs vibrations.");
break;
case (EFFECT_SUSPENSION):
this->m.hapticeffect.effecttype = EFFECT_SUSPENSION;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i on tyre %i", this->m.hapticeffect.effecttype, ds->effect_type, ds->tyre);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
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->sounddevsettings.dev);
error = sounddev_init(this, ds->sounddevsettings.dev, ds->tyre, ds->sounddevsettings);
}
if (error != 0)
{
free(this);
@@ -278,4 +345,3 @@ SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms) {
return this;
}
+10 -6
View File
@@ -1,11 +1,11 @@
#ifndef _SOUNDDEVICE_H
#define _SOUNDDEVICE_H
#ifdef USE_PULSEAUDIO
//#ifdef USE_PULSEAUDIO
#include <pulse/pulseaudio.h>
#else
#include "portaudio.h"
#endif
//#else
//#include "portaudio.h"
//#endif
typedef enum
@@ -19,14 +19,18 @@ SoundEffectModulationType;
#define MAX_TABLE_SIZE (6000)
typedef struct
{
int last_gear;
uint8_t last_gear;
int volume;
uint32_t frequency;
uint32_t frequencyMax;
uint32_t amplitude;
uint32_t amplitudeMax;
double duration;
int curr_frequency;
double curr_frequency;
uint32_t curr_amplitude;
double curr_duration;
double phase;
double noise;
}
SoundData;
+17 -12
View File
@@ -2,14 +2,16 @@
#include <stdlib.h>
#include <string.h>
#include "tachdevice.h"
#include "revburner.h"
#include "../../helper/confighelper.h"
#include "../../simulatorapi/simapi/simapi/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;
}
+2 -5
View File
@@ -1,7 +1,7 @@
#ifndef _TACHDEVICE_H
#define _TACHDEVICE_H
#include <hidapi/hidapi.h>
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
@@ -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
+6 -6
View File
@@ -2,13 +2,13 @@
#include <hidapi/hidapi.h>
#include "tachdevice.h"
#include "../slog/slog.h"
#include "../simdevice.h"
#include "../../slog/slog.h"
const size_t REVBURNER_BUFSIZE = 8;
int revburner_update(TachDevice* tachdevice, int pulses)
int revburner_update(USBDevice* tachdevice, int pulses)
{
int res = 0;
@@ -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;
}
+5 -3
View File
@@ -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
+20 -17
View File
@@ -19,7 +19,7 @@ int senddevreport(USBGenericHapticDevice* usbhapticdevice, unsigned char* buf, s
int res = 0;
if (usbhapticdevice->handle)
{
res = hid_write(usbhapticdevice->handle, buf, SIMAGIC_P1000_BUFSIZE);
res = hid_send_feature_report(usbhapticdevice->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]);
}
@@ -27,7 +27,7 @@ int senddevreport(USBGenericHapticDevice* usbhapticdevice, unsigned char* buf, s
{
slogd("no handle");
}
return res;
return res != bufsize;
}
int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play)
@@ -44,34 +44,36 @@ int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype,
if (play > 0)
{
bytes[0] = 0xEC;
bytes[0] = 241;
bytes[1] = 0xEC;
// we can add config settings for these values
bytes[2] = 0x01;
bytes[3] = 0x0A;
bytes[4] = 0xFF;
bytes[3] = 0x01;
bytes[4] = 0x0A;
bytes[5] = 0xFF;
switch (effecttype)
{
case (EFFECT_TYRESLIP):
bytes[1] = 0x02;
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
case (EFFECT_TYRELOCK):
bytes[1] = 0x01;
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
case (EFFECT_ABSBRAKES):
bytes[1] = 0x02;
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
bytes[1] = 0x01;
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
}
}
else
{
bytes[1] = 0x01;
bytes[0] = 241;
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
bytes[1] = 0x02;
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
}
@@ -113,13 +115,14 @@ int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice)
{
bytes[x] = 0x00;
}
bytes[0] = 0xF1;
bytes[1] = 0x17;
bytes[2] = 0x00;
bytes[0] = 241;
bytes[1] = 0xF1;
bytes[2] = 0x17;
bytes[3] = 0x00;
bytes[4] = 0x00;
bytes[5] = 0x01;
bytes[6] = 0x02;
bytes[5] = 0x00;
bytes[6] = 0x01;
bytes[7] = 0x02;
res = senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
slogd("Initialization returned %i", res);
if(res != 0)
+152 -7
View File
@@ -4,17 +4,21 @@
#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;
int cammusc12_update(WheelDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity)
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;
@@ -42,10 +46,11 @@ int cammusc12_update(WheelDevice* wheeldevice, int maxrpm, int rpm, int gear, in
// byte 4 is gear
bytes[6] = gear-1;
slogt("writing bytes x%02xx%02xx%02xx%02xx%02x%02x%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);
if (wheeldevice->handle)
if (usbdevice->handle)
{
res = hid_write(wheeldevice->handle, bytes, cammusc12_hidupdate_buf_size);
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
{
@@ -55,17 +60,23 @@ int cammusc12_update(WheelDevice* wheeldevice, int maxrpm, int rpm, int gear, in
return res;
}
int cammusc12_free(WheelDevice* wheeldevice)
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(WheelDevice* wheeldevice)
int cammusc12_init_(USBDevice* wheeldevice)
{
slogi("initializing cammus c12 wheel...");
@@ -84,3 +95,137 @@ int cammusc12_init(WheelDevice* wheeldevice)
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;
}
+6 -3
View File
@@ -3,8 +3,11 @@
#include "../../wheeldevice.h"
int cammusc12_update(WheelDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
int cammusc12_init(WheelDevice* wheeldevice);
int cammusc12_free(WheelDevice* wheeldevice);
#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
+9 -9
View File
@@ -9,13 +9,13 @@
const int cammusc5_hidupdate_buf_size = 14;
const int num_avail_leds = 9;
int cammusc5_update(WheelDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity)
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;
@@ -56,9 +56,9 @@ int cammusc5_update(WheelDevice* wheeldevice, int maxrpm, int rpm, int gear, int
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 (wheeldevice->handle)
if (usbdevice->handle)
{
res = hid_write(wheeldevice->handle, bytes, cammusc5_hidupdate_buf_size);
res = hid_write(usbdevice->handle, bytes, cammusc5_hidupdate_buf_size);
}
else
{
@@ -68,17 +68,17 @@ int cammusc5_update(WheelDevice* wheeldevice, int maxrpm, int rpm, int gear, int
return res;
}
int cammusc5_free(WheelDevice* wheeldevice)
int cammusc5_free(USBDevice* usbdevice)
{
int res = 0;
hid_close(wheeldevice->handle);
hid_close(usbdevice->handle);
res = hid_exit();
return res;
}
int cammusc5_init(WheelDevice* wheeldevice)
int cammusc5_init(USBDevice* usbdevice)
{
slogi("initializing cammus c5 wheel...");
@@ -86,9 +86,9 @@ int cammusc5_init(WheelDevice* wheeldevice)
res = hid_init();
wheeldevice->handle = hid_open(0x3416, 0x1021, NULL);
usbdevice->handle = hid_open(0x3416, 0x1021, NULL);
if (!wheeldevice->handle)
if (!usbdevice->handle)
{
sloge("Could not find attached Cammus C5 Wheel");
res = hid_exit();
+4 -4
View File
@@ -1,10 +1,10 @@
#ifndef _CAMMUSC5_H
#define _CAMMUSC5_H
#include "../../wheeldevice.h"
#include "../../simdevice.h"
int cammusc5_update(WheelDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
int cammusc5_init(WheelDevice* wheeldevice);
int cammusc5_free(WheelDevice* wheeldevice);
int cammusc5_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
int cammusc5_init(USBDevice* wheeldevice);
int cammusc5_free(USBDevice* wheeldevice);
#endif
+225
View File
@@ -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;
}
+26
View File
@@ -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
+11 -10
View File
@@ -17,10 +17,10 @@ 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 :
wheeldev_update(&usbdevice->u.wheeldevice, simdata);
wheeldev_update(usbdevice, simdata);
break;
case USBDEV_GENERICHAPTIC :
usbhapticdev_update(&usbdevice->u.hapticdevice, simdata, this->hapticeffect.tyre, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
@@ -39,10 +39,10 @@ int usbdev_free(SimDevice* this)
{
case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER :
tachdev_free(&usbdevice->u.tachdevice);
tachdev_free(usbdevice);
break;
case USBDEV_WHEEL :
wheeldev_free(&usbdevice->u.wheeldevice);
wheeldev_free(usbdevice);
break;
case USBDEV_GENERICHAPTIC :
usbhapticdev_free(&usbdevice->u.hapticdevice);
@@ -54,7 +54,7 @@ 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;
@@ -64,13 +64,13 @@ int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds)
{
case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER :
error = tachdev_init(&usbdevice->u.tachdevice, ds);
error = tachdev_init(usbdevice, ds);
break;
case USBDEV_WHEEL :
error = wheeldev_init(&usbdevice->u.wheeldevice, ds);
error = wheeldev_init(usbdevice, ds);
break;
case USBDEV_GENERICHAPTIC :
error = usbhapticdev_init(&usbdevice->u.hapticdevice, ds);
error = usbhapticdev_init(&usbdevice->u.hapticdevice, ds, siminfo);
break;
}
@@ -79,7 +79,7 @@ int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds)
static const vtable usb_simdevice_vtable = { &usbdev_update, &usbdev_free };
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms) {
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
USBDevice* this = (USBDevice*) malloc(sizeof(USBDevice));
@@ -112,10 +112,11 @@ USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms) {
this->type = USBDEV_GENERICHAPTIC;
}
int error = usbdev_init(this, ds);
int 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;
}
+7 -1
View File
@@ -67,8 +67,14 @@ int usbhapticdev_free(USBGenericHapticDevice* usbhapticdevice)
return 0;
}
int usbhapticdev_init(USBGenericHapticDevice* usbhapticdevice, DeviceSettings* ds)
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;
usbhapticdevice->state = 0;
usbhapticdevice->value0 = ds->usbdevsettings.value0;
+1 -1
View File
@@ -31,7 +31,7 @@ USBGenericHapticDevice;
int usbhapticdev_update(USBGenericHapticDevice* hapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile);
int usbhapticdev_init(USBGenericHapticDevice* hapticdevice, DeviceSettings* ds);
int usbhapticdev_init(USBGenericHapticDevice* hapticdevice, DeviceSettings* ds, SimInfo* siminfo);
int usbhapticdev_free(USBGenericHapticDevice* hapticdevice);
#endif
+92 -15
View File
@@ -2,73 +2,150 @@
#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 "wheeldevice.h"
#include "simdevice.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../slog/slog.h"
int wheeldev_update(WheelDevice* wheeldevice, SimData* simdata)
int wheeldev_update(USBDevice* usbdevice, SimData* simdata)
{
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
switch ( wheeldevice->type )
{
case WHEELDEV_UNKNOWN :
case WHEELDEV_CAMMUSC5 :
cammusc5_update(wheeldevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
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 :
cammusc12_update(wheeldevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
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(WheelDevice* wheeldevice)
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(wheeldevice, 0, 0, 0, 0);
cammusc5_free(wheeldevice);
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_update(wheeldevice, 0, 0, 0, 0);
cammusc12_free(wheeldevice);
cammusc12_free(usbdevice);
if(wheeldevice->useLua == true)
{
free(usbdevice->m.device_specific_config_file);
}
break;
}
return 0;
}
int wheeldev_init(WheelDevice* wheeldevice, DeviceSettings* ds)
int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds)
{
slogi("initializing wheel device...");
int error = 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(wheeldevice);
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");
error = cammusc12_init(wheeldevice);
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;
default:
wheeldevice->type = WHEELDEV_UNKNOWN;
slogw("Unknown cammus wheel detected, trying C5");
error = cammusc5_init(wheeldevice);
error = cammusc5_init(usbdevice);
break;
}
return error;
}
+4 -6
View File
@@ -1,7 +1,6 @@
#ifndef _WHEELDEVICE_H
#define _WHEELDEVICE_H
#include <hidapi/hidapi.h>
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
@@ -12,7 +11,9 @@ typedef enum
WHEELDEV_UNKNOWN = 0,
WHEELDEV_CAMMUSC5 = 1,
WHEELDEV_CAMMUSC12 = 2,
WHEELDEV_MOZAR5 = 3
WHEELDEV_MOZAR5 = 3,
WHEELDEV_SIMAGICGTNEO = 4,
WHEELDEV_LOGITECHG29 = 5
}
WheelType;
@@ -20,13 +21,10 @@ typedef struct
{
int id;
WheelType type;
hid_device* handle;
bool useLua;
char* port;
}
WheelDevice;
int wheeldev_update(WheelDevice* wheeldevice, SimData* simdata);
int wheeldev_init(WheelDevice* wheeldevice, DeviceSettings* ds);
int wheeldev_free(WheelDevice* wheeldevice);
#endif
+266 -142
View File
@@ -11,11 +11,9 @@
#include "gameloop.h"
#include "loopdata.h"
#include "../helper/parameters.h"
#include "../helper/confighelper.h"
#include "../devices/simdevice.h"
#include "../devices/serialadapter.h"
#include "../devices/hapticeffect.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../simulatorapi/simapi/simapi/simmapper.h"
#include "../slog/slog.h"
@@ -29,16 +27,29 @@ struct sigaction act;
uv_idle_t idler;
uv_timer_t datachecktimer;
uv_timer_t showstatstimer;
uv_timer_t datamaptimer;
uv_timer_t tyrediametertimer;
uv_udp_t recv_socket;
bool doui = false;
int appstate = 0;
void shmdatamapcallback(uv_timer_t* handle);
void showstatscallback(uv_timer_t* handle);
void datacheckcallback(uv_timer_t* handle);
void tyrediametercheckcallback(uv_timer_t* handle);
void startdatalogger(MonocoqueSettings* ms, loop_data* l);
static void close_walk_cb(uv_handle_t* handle, void* arg)
{
if (!uv_is_closing(handle))
{
uv_close(handle, NULL);
}
}
#define ASSERT(expr) expr
int showstats(SimData* simdata)
{
printf("\r");
@@ -216,61 +227,95 @@ void devicetimercallback(uv_timer_t* handle)
SimData* simdata = f->simdata;
SimDevice* device = f->simdevice;
device->update(device, simdata);
}
void tyrediametercheckcallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
device_loop_data* f = (device_loop_data*) b;
SimData* simdata = f->simdata;
if(simdata->car != NULL || simdata->car[0] != 0)
{
slogi("car is %s", simdata->car);
// check for saved tyre diameter in config file
// if not saved version exists get tyre diameter and save it
// use config check variable to track if the config check has been performed
// avoid many opens of the same file
int error = 0;
if(hasTyreDiameter(simdata)==false && f->ms->configcheck == 0)
{
slogi("attempting load of tyre diameter config");
error = loadtyreconfig(simdata, f->ms->tyre_diameter_config, true);
f->ms->configcheck = 1;
}
if(hasTyreDiameter(simdata)==false)
{
slogt("could not find tyre diameter in config file, attempting to calculate new");
getTyreDiameter(simdata);
// if this successfully calculates data the diameters will be saved to the file after the
// sim stops actively mapping data
}
}
if(hasTyreDiameter(simdata)==true)
{
int a = loadtyreconfig(simdata, f->ms->tyre_diameter_config, false);
if(a < 0)
{
slogi("saving new tyre diameter config for car %s");
savetyreconfig(simdata, f->ms->tyre_diameter_config);
}
uv_timer_stop(handle);
}
}
void looprun(MonocoqueSettings* ms, loop_data* f, SimData* simdata)
{
if (doui == true)
{
slogi("looking for ui config %s pass 1", ms->config_str);
int confignum = getconfigtouse2(ms->config_str, simdata->car, f->sim);
slogi("looking for ui config %s pass 1 simapi %i", ms->config_str, f->siminfo.simulatorapi);
int confignum = getconfigtouse2(ms->config_str, simdata->car, f->siminfo.simulatorapi);
slogi("first pass finished");
if(confignum == -1)
{
slogi("looking for ui config %s pass 2", ms->config_str);
confignum = getconfigtouse1(ms->config_str, simdata->car, f->sim);
confignum = getconfigtouse1(ms->config_str, simdata->car, f->siminfo.simulatorapi);
}
if(confignum == -1)
{
slogi("looking for ui config %s pass 3", ms->config_str);
confignum = getconfigtouse(ms->config_str, simdata->car, f->sim);
confignum = getconfigtouse(ms->config_str, simdata->car, f->siminfo.simulatorapi);
}
int configureddevices;
configcheck(ms->config_str, confignum, &configureddevices);
DeviceSettings* ds = malloc(configureddevices * sizeof(DeviceSettings));
slogd("loading confignum %i, with %i devices.", confignum, configureddevices);
f->numdevices = uiloadconfig(ms->config_str, confignum, configureddevices, ms, ds);
if(ms->useconfig == 1)
{
ms->configcheck = 0;
}
f->simdevices = malloc(f->numdevices * sizeof(SimDevice));
int initdevices = devinit(f->simdevices, configureddevices, ds, ms);
int i = 0;
for( i = 0; i < configureddevices; i++)
int initdevices = devinit(f->simdevices, &f->siminfo, configureddevices, ds, ms);
slogi("initialized %i devices", initdevices);
for( int i = 0; i < configureddevices; i++)
{
settingsfree(ds[i]);
}
free(ds);
for(int d = 0; d < 10; d++)
{
monocoque_serial_devices[d].open = false;
monocoque_serial_devices[d].openfail = false;
monocoque_serial_devices[d].busy = false;
monocoque_serial_devices[d].refs = 0;
}
int numdevices = f->numdevices;
SimDevice* devices = f->simdevices;
f->device_timers = (uv_timer_t*) (malloc(uv_handle_size(UV_TIMER) * numdevices));
f->device_batons = (device_loop_data*) (malloc(sizeof(device_loop_data) * numdevices));
f->started_tyre_calc_thread = false;
for (int x = 0; x < numdevices; x++)
{
@@ -284,53 +329,79 @@ void looprun(MonocoqueSettings* ms, loop_data* f, SimData* simdata)
uv_handle_set_data((uv_handle_t*) dt, (void*) dld);
int interval = 1000/devices[x].fps;
uv_timer_start(dt, devicetimercallback, 0, interval);
slogi("starting device at %i fps (%i ms ticks)", devices[x].fps, interval);
slogi("starting device type %i at id at %i fps: %i (%i ms ticks)", devices[x].type, x, devices[x].fps, interval);
SimInfo siminfo = f->siminfo;
if(f->started_tyre_calc_thread == false)
{
if(devices[x].hapticeffect.effecttype == EFFECT_TYRELOCK || devices[x].hapticeffect.effecttype == EFFECT_TYRESLIP || devices[x].hapticeffect.effecttype == EFFECT_ABSBRAKES)
{
if(siminfo.SimCalculatesTyreDiameter == false && siminfo.SimSupportsHapticEffects == true && siminfo.SimCalculatesSlipRatio == false && f->ms->useconfig == 1 && f->ms->tyre_diameter_config != NULL)
{
slogi("Starting thread to calculate tyre diameters and save to config file");
f->started_tyre_calc_thread = true;
f->tyrebaton = (device_loop_data*) malloc(sizeof(device_loop_data));
f->tyrebaton->simdata = simdata;
f->tyrebaton->ms = f->ms;
uv_handle_set_data((uv_handle_t*) &tyrediametertimer, (void*) f->tyrebaton);
uv_timer_start(&tyrediametertimer, tyrediametercheckcallback, 0, 1000);
}
}
}
}
}
uv_timer_start(&showstatstimer, showstatscallback, 0, 100);
doui = false;
}
else
{
showstats(simdata);
//SimDevice* devices = f->simdevices;
//int numdevices = f->numdevices;
//for (int x = 0; x < numdevices; x++)
//{
// if (devices[x].initialized == true && devices[x].type == 2)
// {
// devices[x].update(&devices[x], simdata);
// }
//}
}
}
void shmdatamapcallback(uv_timer_t* handle)
void showstatscallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
loop_data* f = (loop_data*) b;
SimData* simdata = f->simdata;
SimMap* simmap = f->simmap;
MonocoqueSettings* ms = f->ms;
//appstate = 2;
if (appstate == 2)
if(appstate == 2)
{
simdatamap(simdata, simmap, NULL, f->map, false, NULL);
looprun(ms, f, simdata);
showstats(simdata);
}
}
if (f->simstate == false || simdata->simstatus <= 1 || appstate <= 1)
{
void releaseloop(loop_data* f, SimData* simdata, SimMap* simmap)
{
slogi("release loop");
if(f->releasing == false)
{
f->releasing = true;
uv_timer_stop(handle);
uv_timer_stop(&datamaptimer);
uv_timer_stop(&showstatstimer);
if (uv_is_active((uv_handle_t*)&recv_socket))
{
uv_udp_recv_stop(&recv_socket);
}
// Close the socket handle so it can be reinitialized with a different port
//if (!uv_is_closing((uv_handle_t*)&recv_socket))
//{
// uv_close((uv_handle_t*)&recv_socket, NULL);
//}
slogi("releasing devices, please wait");
//attempt tyre diameter saving
uv_timer_stop(&tyrediametertimer);
if(f->started_tyre_calc_thread == true)
{
free(f->tyrebaton);
}
f->uion = false;
SimDevice* devices = f->simdevices;
int numdevices = f->numdevices;
// help things spin down
simdata->simstatus = 0;
simdata->rpms = 0;
simdata->velocity = 0;
@@ -355,6 +426,7 @@ void shmdatamapcallback(uv_timer_t* handle)
devices[x].update(&devices[x], simdata);
}
}
sleep(1);
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
@@ -364,15 +436,7 @@ void shmdatamapcallback(uv_timer_t* handle)
}
free(devices);
for(int d = 0; d < 10; d++)
{
if(monocoque_serial_devices[d].portname != NULL)
{
free(monocoque_serial_devices[d].portname);
}
}
int r = simfree(simdata, simmap, f->map);
int r = simapi_sim_clear(simdata, simmap, false);
slogd("simfree returned %i", r);
f->numdevices = 0;
slogi("stopped mapping data, press q again to quit");
@@ -381,7 +445,8 @@ void shmdatamapcallback(uv_timer_t* handle)
if(appstate > 0)
{
uv_timer_start(&datachecktimer, datacheckcallback, 3000, 1000);
slogi("restarting checking for data...");
uv_timer_start(&datachecktimer, datacheckcallback, 0, 1000);
}
f->releasing = false;
if(appstate > 1)
@@ -389,6 +454,25 @@ void shmdatamapcallback(uv_timer_t* handle)
appstate = 1;
}
}
}
void shmdatamapcallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
loop_data* f = (loop_data*) b;
SimData* simdata = f->simdata;
SimMap* simmap = f->simmap;
MonocoqueSettings* ms = f->ms;
//appstate = 2;
if (appstate == 2)
{
simapi_datamap(simdata, simmap, f->siminfo.mapapi, false, NULL);
looprun(ms, f, simdata);
}
if (f->siminfo.isSimOn == false || simdata->simstatus <= 1 || appstate <= 1)
{
releaseloop(f, simdata, simmap);
}
}
@@ -403,6 +487,10 @@ static void on_udp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf,
if (nread > 0) {
slogt("udp data received");
}
if (nread <= 0) {
free(rcvbuf->base);
return;
}
char* a;
a = rcvbuf->base;
@@ -415,56 +503,13 @@ static void on_udp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf,
if (appstate == 2)
{
simdatamap(simdata, simmap, NULL, f->map, true, a);
simapi_datamap(simdata, simmap, f->siminfo.mapapi, true, a);
looprun(ms, f, simdata);
}
if (f->simstate == false || simdata->simstatus <= 1 || appstate <= 1)
if (f->siminfo.isSimOn == false || simdata->simstatus <= 1 || appstate <= 1)
{
if(f->releasing == false)
{
f->releasing = true;
uv_udp_recv_stop(handle);
slogi("releasing devices, please wait");
f->uion = false;
SimDevice* devices = f->simdevices;
int numdevices = f->numdevices;
// help things spin down
simdata->rpms = 0;
simdata->velocity = 0;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].free(&devices[x]);
}
}
free(devices);
int r = simfree(simdata, simmap, f->map);
slogd("simfree returned %i", r);
f->numdevices = 0;
slogi("stopped mapping data, press q again to quit");
//stopui(ms->ui_type, f);
// free loop data
if(appstate > 0)
{
uv_timer_start(&datachecktimer, datacheckcallback, 3000, 1000);
}
f->releasing = false;
if(appstate > 1)
{
appstate = 1;
}
}
releaseloop(f, simdata, simmap);
}
slogt("udp free :%lu %p\n",rcvbuf->len,rcvbuf->base);
@@ -473,7 +518,7 @@ static void on_udp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf,
int startudp(int port)
{
uv_udp_init(uv_default_loop(), &recv_socket);
struct sockaddr_in recv_addr;
uv_ip4_addr("0.0.0.0", port, &recv_addr);
int err = uv_udp_bind(&recv_socket, (const struct sockaddr *) &recv_addr, UV_UDP_REUSEADDR);
@@ -486,7 +531,7 @@ void udpstart(MonocoqueSettings* sms, loop_data* f, SimData* simdata, SimMap* si
{
if (appstate == 2)
{
simdatamap(simdata, simmap, NULL, f->sim, true, NULL);
simapi_datamap(simdata, simmap, f->siminfo.simulatorapi, true, NULL);
if (doui == true)
{
looprun(sms, f, simdata);
@@ -503,19 +548,14 @@ void datacheckcallback(uv_timer_t* handle)
if ( appstate == 1 )
{
SimInfo si = getSim(simdata, simmap, f->ms->force_udp_mode, startudp, false);
//TODO: move all this to a siminfo struct in loop_data
f->simstate = si.isSimOn;
f->sim = si.simulatorapi;
f->map = si.mapapi;
f->use_udp = si.SimUsesUDP;
f->siminfo = simapi_get_sim(simdata, simmap, f->ms->force_udp_mode, startudp, false);
if(f->ms->force_udp_mode == true)
{
f->use_udp = true;
}
}
if (f->simstate == true && simdata->simstatus >= 2)
if (f->siminfo.isSimOn == true && simdata->simstatus >= 2)
{
if ( appstate == 1 )
{
@@ -526,7 +566,7 @@ void datacheckcallback(uv_timer_t* handle)
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
if(f->use_udp == true)
if(f->use_udp == true || f->siminfo.SimUsesUDP == true)
{
slogt("starting udp receive loop");
udpstart(f->ms, f, simdata, simmap);
@@ -539,13 +579,21 @@ void datacheckcallback(uv_timer_t* handle)
slogd("starting telemetry mapping at %i fps (%i ms ticks)", f->ms->fps, interval);
uv_timer_start(&datamaptimer, shmdatamapcallback, 2000, interval);
}
uv_timer_stop(handle);
}
if(appstate == 2)
{
f->siminfo = simapi_get_sim(simdata, simmap, f->ms->force_udp_mode, NULL, false);
if(f->siminfo.isSimOn == false)
{
appstate = 1;
releaseloop(f, simdata, simmap);
}
}
}
if (appstate == 0)
{
slogi("stopping checking for data");
slogi("stopped checking for data");
uv_timer_stop(handle);
}
}
@@ -570,6 +618,10 @@ void cb(uv_poll_t* handle, int status, int events)
if (appstate == 0)
{
slogi("Monocoque is exiting...");
uv_udp_recv_stop(&recv_socket);
uv_timer_stop(&datamaptimer);
uv_timer_stop(&showstatstimer);
// at this point these below should be the only active threads
uv_timer_stop(&datachecktimer);
uv_poll_stop(handle);
}
@@ -581,7 +633,7 @@ int monocoque_mainloop(MonocoqueSettings* ms)
{
SimData* simdata = malloc(sizeof(SimData));
SimMap* simmap = createSimMap();
SimMap* simmap = simapi_simmap_create();
struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode);
@@ -599,46 +651,57 @@ int monocoque_mainloop(MonocoqueSettings* ms)
baton->simmap = simmap;
baton->simdata = simdata;
baton->ms = ms;
baton->simstate = false;
baton->uion = false;
baton->releasing = false;
baton->sim = 0;
baton->use_udp = false;
baton->req.data = (void*) baton;
uv_handle_set_data((uv_handle_t*) &datachecktimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &datamaptimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &recv_socket, (void*) baton);
uv_handle_set_data((uv_handle_t*) poll, (void*) baton);
appstate = 1;
slogd("setting initial app state");
uv_timer_init(uv_default_loop(), &datachecktimer);
fprintf(stdout, "Searching for sim data... Press q to quit...\n");
uv_timer_start(&datachecktimer, datacheckcallback, 1000, 1000);
set_simapi_log_info(simapilib_loginfo);
set_simapi_log_debug(simapilib_logdebug);
set_simapi_log_trace(simapilib_logtrace);
simapi_set_log_info(simapilib_loginfo);
simapi_set_log_debug(simapilib_logdebug);
simapi_set_log_trace(simapilib_logtrace);
if (0 != uv_poll_init(uv_default_loop(), poll, 0))
{
return 1;
};
uv_udp_init(uv_default_loop(), &recv_socket);
uv_timer_init(uv_default_loop(), &datachecktimer);
uv_timer_init(uv_default_loop(), &showstatstimer);
uv_timer_init(uv_default_loop(), &datamaptimer);
uv_timer_init(uv_default_loop(), &tyrediametertimer);
slogd("setting initial app state");
appstate = 1;
uv_handle_set_data((uv_handle_t*) &datachecktimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &datamaptimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &showstatstimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &recv_socket, (void*) baton);
uv_handle_set_data((uv_handle_t*) poll, (void*) baton);
if (0 != uv_poll_start(poll, UV_READABLE, cb))
{
return 2;
};
uv_timer_start(&datachecktimer, datacheckcallback, 1000, 1000);
uv_timer_init(uv_default_loop(), &datamaptimer);
fprintf(stdout, "Searching for sim data... Press q to quit...\n");
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
uv_stop(uv_default_loop());
//uv_walk(uv_default_loop(), close_walk_cb, NULL);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
uv_loop_close(uv_default_loop());
uv_library_shutdown();
slogi("All threads stopped...");
fprintf(stdout, "\n");
fflush(stdout);
tcsetattr(0, TCSANOW, &canonicalmode);
free(baton);
free(simdata);
freesimmap(simmap, false);
free(simmap);
return 0;
}
@@ -663,7 +726,9 @@ int tester(SimDevice* devices, int numdevices)
simdata->car[2] = 'R';
simdata->car[3] = '\0';
simdata->gear = MONOCOQUE_GEAR_NEUTRAL;
simdata->gear = SIMAPI_GEAR_NEUTRAL;
simdata->gearc[0] = 0x4e;
simdata->gearc[1] = 0;
simdata->velocity = 16;
simdata->rpms = 100;
simdata->maxrpm = 8000;
@@ -672,11 +737,43 @@ int tester(SimDevice* devices, int numdevices)
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->tyreslipratio[0] = 0;
simdata->tyreslipratio[1] = 0;
simdata->tyreslipratio[2] = 0;
simdata->tyreslipratio[3] = 0;
simdata->Xvelocity = 0;
simdata->Yvelocity = 100;
simdata->Zvelocity = 0;
sleep(3);
sleep(1);
fprintf(stdout, "Revving rpm from 1000 to 8000 and back\n");
// TODO: look into this, my serial leds make this hang
for (int r = 0; r < 8000; r += 3)
{
simdata->rpms = 1000 + r;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
usleep(1000);
}
for (int r = 0; r < 8000; r += 3)
{
simdata->rpms = 9000 - r;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
usleep(1000);
}
fprintf(stdout, "Setting rpms to 1000\n");
simdata->rpms = 1000;
@@ -689,8 +786,12 @@ int tester(SimDevice* devices, int numdevices)
}
sleep(3);
fprintf(stdout, "Green Flag!\n");
simdata->playerflag = SIMAPI_FLAG_GREEN;
fprintf(stdout, "Shifting into first gear\n");
simdata->gear = 2;
simdata->gear = SIMAPI_GEAR_FIRST;
simdata->gearc[0] = 0x31;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
@@ -755,7 +856,8 @@ int tester(SimDevice* devices, int numdevices)
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->abs = 0;
simdata->gear = 3;
simdata->gear = SIMAPI_GEAR_SECOND;
simdata->gearc[0] = 0x32;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
@@ -804,8 +906,12 @@ int tester(SimDevice* devices, int numdevices)
}
sleep(3);
fprintf(stdout, "Yellow Flag!\n");
simdata->playerflag = SIMAPI_FLAG_YELLOW;
fprintf(stdout, "Shifting into third gear\n");
simdata->gear = 4;
simdata->gear = SIMAPI_GEAR_THIRD;
simdata->gearc[0] = 0x33;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
@@ -826,6 +932,9 @@ int tester(SimDevice* devices, int numdevices)
}
sleep(3);
fprintf(stdout, "Blue Flag!\n");
simdata->playerflag = SIMAPI_FLAG_BLUE;
fprintf(stdout, "Setting rpms to 4000\n");
simdata->rpms = 4000;
for (int x = 0; x < numdevices; x++)
@@ -838,7 +947,8 @@ int tester(SimDevice* devices, int numdevices)
sleep(3);
fprintf(stdout, "Shifting into fourth gear\n");
simdata->gear = 5;
simdata->gear = SIMAPI_GEAR_FOURTH;
simdata->gearc[0] = 0x34;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
@@ -870,6 +980,9 @@ int tester(SimDevice* devices, int numdevices)
}
sleep(3);
fprintf(stdout, "Red Flag!\n");
simdata->playerflag = SIMAPI_FLAG_RED;
fprintf(stdout, "Setting rpms to 8000\n");
simdata->rpms = 8000;
for (int x = 0; x < numdevices; x++)
@@ -879,11 +992,22 @@ int tester(SimDevice* devices, int numdevices)
devices[x].update(&devices[x], simdata);
}
}
for(int x = 0; x < 100; x++)
{
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
}
sleep(3);
simdata->velocity = 0;
simdata->rpms = 100;
simdata->gear = MONOCOQUE_GEAR_NEUTRAL;
simdata->gear = SIMAPI_GEAR_NEUTRAL;
simdata->gearc[0] = 0x4e;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
+9 -3
View File
@@ -10,27 +10,33 @@
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;
SimulatorAPI sim;
SimulatorAPI map;
bool use_udp;
bool simstate;
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;
+146 -47
View File
@@ -10,7 +10,7 @@
#include <libxml/tree.h>
#include "confighelper.h"
#include "dirhelper.h"
#include "../slog/slog.h"
#include "parameters.h"
@@ -44,6 +44,11 @@ int strtoeffecttype(const char* effect, DeviceSettings* ds)
ds->is_valid = true;
ds->effect_type = EFFECT_GEARSHIFT;
}
if (strcicmp(effect, "Suspension") == 0)
{
ds->is_valid = true;
ds->effect_type = EFFECT_SUSPENSION;
}
if (strcicmp(effect, "ABS") == 0)
{
ds->is_valid = true;
@@ -81,32 +86,57 @@ int strtodevsubsubtype(const char* device_subsubtype, DeviceSettings* ds)
if (strcicmp(device_subsubtype, "CammusC5") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_CAMMUSC5;
bool devfound = true;
devfound = true;
}
if (strcicmp(device_subsubtype, "CammusC12") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_CAMMUSC12;
bool devfound = true;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaNew") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZA_NEW;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaR5") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5;
bool devfound = true;
devfound = true;
}
if (strcicmp(device_subsubtype, "LogitechG29") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_LOGITECH_G29;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaR8") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5;
bool devfound = true;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaR3") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaKSProWheel") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL;
devfound = true;
}
if (strcicmp(device_subsubtype, "CSLELITEV3PEDALS") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_CSLELITEV3PEDALS;
bool devfound = true;
devfound = true;
}
if (strcicmp(device_subsubtype, "SIMAGICP1000PEDALS") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICP1000PEDALS;
bool devfound = true;
devfound = true;
}
if (strcicmp(device_subsubtype, "SIMAGICGTNEO") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICGTNEO;
devfound = true;
}
if(devfound == false)
@@ -129,7 +159,7 @@ int strtodevsubtype(const char* device_subtype, DeviceSettings* ds, int simdev)
ds->dev_subtype = SIMDEVTYPE_TACHOMETER;
break;
}
if (strcicmp(device_subtype, "Wheel") == 0)
if (strcicmp(device_subtype, "Wheel") == 0 || strcicmp(device_subtype, "UsbWheel") == 0)
{
ds->dev_subtype = SIMDEVTYPE_USBWHEEL;
break;
@@ -150,6 +180,11 @@ int strtodevsubtype(const char* device_subtype, DeviceSettings* ds, int simdev)
ds->dev_subtype = SIMDEVTYPE_SIMLED;
break;
}
if (strcicmp(device_subtype, "ArduinoCustom") == 0 || strcicmp(device_subtype, "Custom") == 0)
{
ds->dev_subtype = SIMDEVTYPE_ARDUINOCUSTOM;
break;
}
if (strcicmp(device_subtype, "SimWind") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SIMWIND;
@@ -238,18 +273,24 @@ int getNumberOfConfigs(const char* config_file_str)
config = config_lookup(&cfg, "configs");
int configs = config_setting_length(config);
config_destroy(&cfg);
return configs;
}
int getconfigtouse2(const char* config_file_str, char* car, int sim)
{
slogt("inside first pass");
config_t cfg;
config_init(&cfg);
if (!config_read_file(&cfg, config_file_str))
{
sloge("config read error on pass 1");
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return -1;
}
slogt("config validates");
config_setting_t* config = NULL;
config_setting_t* config_widgets = NULL;
config = config_lookup(&cfg, "configs");
@@ -260,6 +301,7 @@ int getconfigtouse2(const char* config_file_str, char* car, int sim)
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return -1;
}
int confignum = -1;
@@ -295,6 +337,8 @@ int getconfigtouse2(const char* config_file_str, char* car, int sim)
break;
}
}
config_destroy(&cfg);
return confignum;
}
@@ -305,6 +349,7 @@ int getconfigtouse1(const char* config_file_str, char* car, int sim)
if (!config_read_file(&cfg, config_file_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return -1;
}
config_setting_t* config = NULL;
@@ -317,6 +362,7 @@ int getconfigtouse1(const char* config_file_str, char* car, int sim)
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return -1;
}
int confignum = -1;
@@ -363,6 +409,8 @@ int getconfigtouse1(const char* config_file_str, char* car, int sim)
break;
}
}
config_destroy(&cfg);
return confignum;
}
@@ -373,8 +421,10 @@ int getconfigtouse(const char* config_file_str, char* car, int sim)
if (!config_read_file(&cfg, config_file_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return -1;
}
config_setting_t* config = NULL;
config_setting_t* config_widgets = NULL;
config = config_lookup(&cfg, "configs");
@@ -385,6 +435,7 @@ int getconfigtouse(const char* config_file_str, char* car, int sim)
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return 0;
}
int confignum = 0;
@@ -431,11 +482,13 @@ int getconfigtouse(const char* config_file_str, char* car, int sim)
break;
}
}
config_destroy(&cfg);
return confignum;
}
int loadtachconfig(const char* config_file, DeviceSettings* ds)
int loadtachconfig(char* config_file, DeviceSettings* ds)
{
@@ -532,9 +585,9 @@ int gettyre(config_setting_t* device_settings, DeviceSettings* ds) {
const char* temp;
int found = config_setting_lookup_string(device_settings, "tyre", &temp);
ds->tyre = ALLFOUR;
if (strcicmp(temp, "FRONTS") == 0)
{
ds->tyre = FRONTS;
@@ -562,11 +615,40 @@ int gettyre(config_setting_t* device_settings, DeviceSettings* ds) {
}
int loadconfig(const char* config_file, DeviceSettings* ds)
static int load_device_specific_config(const char* config_file, DeviceSettings* ds)
{
ds->has_config = false;
if(config_file == NULL)
{
slogt("config set to none");
}
else
{
ds->has_config = true;
if(strcicmp(config_file, "none") == 0)
{
ds->has_config = false;
ds->specific_config_file = NULL;
}
else
{
ds->specific_config_file = strdup(config_file);
ds->specific_config_file = expand_tilde(ds->specific_config_file);
slogt("will try to load config file at %s", ds->specific_config_file);
}
}
// in the case of the revburner tachometer, we can parse once and store
if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER)
{
return loadtachconfig(config_file, ds);
if(ds->has_config == false)
{
slogw("Tachometer must have a device specific config file!");
return 1;
}
loadtachconfig(ds->specific_config_file, ds);
}
return 0;
}
@@ -608,7 +690,7 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
if (ms->program_action == A_PLAY || ms->program_action == A_TEST)
{
error = loadconfig(config_file, ds);
error = load_device_specific_config(config_file, ds);
}
if (error != MONOCOQUE_ERROR_NONE)
{
@@ -638,7 +720,7 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
}
}
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_subtype == SIMDEVTYPE_USBWHEEL)
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_subtype == SIMDEVTYPE_USBWHEEL || ds->dev_subtype == SIMDEVTYPE_SERIALWHEEL)
{
// logic for different devices
int b = 0;
@@ -657,7 +739,7 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
const char* effect;
config_setting_lookup_string(device_settings, "effect", &effect);
strtoeffecttype(effect, ds);
if (ds->effect_type == EFFECT_TYRESLIP || ds->effect_type == EFFECT_TYRELOCK || ds->effect_type == EFFECT_ABSBRAKES)
if (ds->effect_type == EFFECT_TYRESLIP || ds->effect_type == EFFECT_TYRELOCK || ds->effect_type == EFFECT_ABSBRAKES || ds->effect_type == EFFECT_SUSPENSION )
{
gettyre(device_settings, ds);
ds->threshold = 0;
@@ -669,10 +751,13 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
slogi("reading configured sound device settings");
ds->sounddevsettings.frequency = 0;
ds->sounddevsettings.frequencyMax = 0;
ds->sounddevsettings.amplitude = 50;
ds->sounddevsettings.amplitudeMax = 50;
ds->sounddevsettings.volume = 0;
ds->sounddevsettings.pan = 0;
ds->sounddevsettings.channels = 1;
ds->sounddevsettings.duration = 2.0;
ds->sounddevsettings.noise = 0;
if (ds->effect_type == EFFECT_GEARSHIFT)
{
ds->sounddevsettings.duration = .125;
@@ -683,20 +768,26 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
config_setting_lookup_int(device_settings, "volume", &ds->sounddevsettings.volume);
config_setting_lookup_int(device_settings, "frequency", &ds->sounddevsettings.frequency);
config_setting_lookup_int(device_settings, "frequencyMax", &ds->sounddevsettings.frequencyMax);
config_setting_lookup_int(device_settings, "amplitude", &ds->sounddevsettings.amplitude);
config_setting_lookup_int(device_settings, "amplitudeMax", &ds->sounddevsettings.amplitudeMax);
config_setting_lookup_int(device_settings, "pan", &ds->sounddevsettings.pan);
config_setting_lookup_int(device_settings, "channels", &ds->sounddevsettings.channels);
config_setting_lookup_float(device_settings, "duration", &ds->sounddevsettings.duration);
config_setting_lookup_int(device_settings, "noise", &ds->sounddevsettings.noise);
const char* temp;
const char* temp = NULL;
int found = 0;
found = config_setting_lookup_string(device_settings, "devid", &temp);
if (found == 0)
if (found == CONFIG_FALSE)
{
ds->sounddevsettings.dev = NULL;
}
else
{
ds->sounddevsettings.dev = strdup(temp);
if(temp != NULL)
{
ds->sounddevsettings.dev = strdup(temp);
}
}
@@ -709,7 +800,7 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
}
else
{
if(strcicmp(temp, "FREQUENCY"))
if(strcicmp(temp, "FREQUENCY") == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_FREQUENCY;
if(ds->sounddevsettings.frequencyMax == 0 || ds->sounddevsettings.frequencyMax < ds->sounddevsettings.frequency)
@@ -722,7 +813,7 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
slogi("Effect modulation found, set to FREQUENCY");
}
}
else if(strcicmp(temp, "AMPLIFY"))
else if(strcicmp(temp, "AMPLIFY") == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_AMPLIFY;
slogi("Effect modulation found, set to AMPLIFY");
@@ -742,6 +833,12 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
if (device_settings != NULL)
{
const char* temp;
int found = config_setting_lookup_string(device_settings, "subtype", &temp);
if(temp != NULL && found > 0)
{
strtodevsubsubtype(temp, ds);
}
config_setting_lookup_string(device_settings, "devpath", &temp);
ds->serialdevsettings.portdev = strdup(temp);
@@ -771,29 +868,20 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
double ampfactor = 1.0;
ds->serialdevsettings.ampfactor = 1.0;
int found = config_setting_lookup_float(device_settings, "ampfactor", &ampfactor);
found = config_setting_lookup_float(device_settings, "ampfactor", &ampfactor);
ds->serialdevsettings.ampfactor = ampfactor;
slogt("set port baud rate to %i, ampfactor %f", baud, ampfactor);
double fanpower = 0.6;
config_setting_lookup_float(device_settings, "fanpower", &fanpower);
ds->serialdevsettings.fanpower = fanpower;
slogt("set port baud rate to %i, ampfactor %f, fanpower %f", baud, ampfactor, fanpower);
ds->has_config = false;
const char* temp2;
found = config_setting_lookup_string(device_settings, "config", &temp2);
slogt("config is %s found is %i", temp2, found);
if(strcicmp(temp2, "none") == 0)
{
slogt("config set to none");
}
else
{
ds->has_config = true;
ds->serialdevsettings.config_file = strdup(temp2);
slogt("will try to load config file at %s", ds->serialdevsettings.config_file);
}
}
}
return error;
}
@@ -825,16 +913,24 @@ int uiloadconfig(const char* config_file_str, int confignum, int configureddevic
DeviceSettings settings;
config_setting_t* config_device = config_setting_get_elem(config_devices, i);
const char* device_type;
const char* device_subtype;
const char* device_config_file;
const char* device_type = NULL;
const char* device_subtype = NULL;
const char* device_config_file = NULL;
int found = 0;
config_setting_lookup_string(config_device, "device", &device_type);
config_setting_lookup_string(config_device, "type", &device_subtype);
config_setting_lookup_string(config_device, "config", &device_config_file);
found = config_setting_lookup_string(config_device, "config", &device_config_file);
//slogt("device type: %s", device_type);
//slogt("device sub type: %s", device_subtype);
//slogt("device config file: %s", device_config_file);
slogt("device type: %s", device_type);
slogt("device sub type: %s", device_subtype);
if(found == CONFIG_FALSE)
{
device_config_file = NULL;
}
else
{
slogt("device config file: %s", device_config_file);
}
if (error == MONOCOQUE_ERROR_NONE)
{
error = devsetup(device_type, device_subtype, device_config_file, ms, &settings, config_device);
@@ -858,14 +954,12 @@ int uiloadconfig(const char* config_file_str, int confignum, int configureddevic
int settingsfree(DeviceSettings ds)
{
if (ds.dev_subtype == SIMDEVTYPE_SIMWIND || ds.dev_subtype == SIMDEVTYPE_SHIFTLIGHTS || ds.dev_subtype == SIMDEVTYPE_SIMLED)
if (ds.dev_type == SIMDEV_SERIAL)
{
if (ds.serialdevsettings.portdev != NULL)
{
free(ds.serialdevsettings.portdev);
}
}
if (ds.dev_type == SIMDEV_SOUND)
{
@@ -875,6 +969,11 @@ int settingsfree(DeviceSettings ds)
}
}
if(ds.has_config && ds.specific_config_file != NULL)
{
free(ds.specific_config_file);
}
return 0;
}
+23 -11
View File
@@ -30,7 +30,8 @@ typedef enum
SIMDEVTYPE_SERIALHAPTIC = 5,
SIMDEVTYPE_USBWHEEL = 6,
SIMDEVTYPE_SERIALWHEEL = 7,
SIMDEVTYPE_SIMLED = 8
SIMDEVTYPE_SIMLED = 8,
SIMDEVTYPE_ARDUINOCUSTOM = 9
}
DeviceSubType;
@@ -41,7 +42,11 @@ typedef enum
SIMDEVSUBTYPE_CAMMUSC12 = 2,
SIMDEVSUBTYPE_MOZAR5 = 3,
SIMDEVSUBTYPE_CSLELITEV3PEDALS = 4,
SIMDEVSUBTYPE_SIMAGICP1000PEDALS =5
SIMDEVSUBTYPE_SIMAGICP1000PEDALS = 5,
SIMDEVSUBTYPE_SIMAGICGTNEO = 6,
SIMDEVSUBTYPE_MOZA_NEW = 7,
SIMDEVSUBTYPE_LOGITECH_G29 = 8,
SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL = 9
}
DeviceSubSubType;
@@ -63,7 +68,8 @@ typedef enum
EFFECT_GEARSHIFT = 1,
EFFECT_ABSBRAKES = 2,
EFFECT_TYRESLIP = 3,
EFFECT_TYRELOCK = 4
EFFECT_TYRELOCK = 4,
EFFECT_SUSPENSION = 5
}
VibrationEffectType;
@@ -104,12 +110,13 @@ MonocoqueGear;
typedef enum
{
MONOCOQUE_ERROR_NONE = 0,
MONOCOQUE_ERROR_UNKNOWN = 1,
MONOCOQUE_ERROR_INVALID_SIM = 2,
MONOCOQUE_ERROR_INVALID_DEV = 3,
MONOCOQUE_ERROR_NODATA = 4,
MONOCOQUE_ERROR_UNKNOWN_DEV = 5
MONOCOQUE_ERROR_NONE = 0,
MONOCOQUE_ERROR_UNKNOWN = 1,
MONOCOQUE_ERROR_INVALID_SIM = 2,
MONOCOQUE_ERROR_INVALID_DEV = 3,
MONOCOQUE_ERROR_NODATA = 4,
MONOCOQUE_ERROR_UNKNOWN_DEV = 5,
MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE = 6,
}
MonocoqueError;
@@ -134,6 +141,7 @@ typedef struct
int verbosity_count;
int fps;
bool force_udp_mode;
bool disable_audio;
char* tyre_diameter_config;
char* config_str;
char* log_filename_str;
@@ -154,13 +162,14 @@ TachometerSettings;
typedef struct
{
char* portdev;
char* config_file;
MotorPosition motorsposition;
int numlights;
int numleds;
int startled;
int endled;
float ampfactor;
float fanpower;
int baud;
}
SerialDeviceSettings;
@@ -172,11 +181,14 @@ typedef struct
int lowbound_frequency;
int upperbound_frequency;
uint32_t frequencyMax;
uint32_t amplitudeMax;
uint32_t amplitude;
int pan;
int channels;
double duration;
char* dev;
SoundEffectModulationType modulation;
int noise;
}
SoundDeviceSettings;
@@ -201,12 +213,12 @@ typedef struct
MonocoqueTyreIdentifier tyre;
double threshold;
bool has_config;
char* specific_config_file;
// union?
TachometerSettings tachsettings;
SerialDeviceSettings serialdevsettings;
SoundDeviceSettings sounddevsettings;
USBDeviceSettings usbdevsettings;
}
DeviceSettings;
+28 -8
View File
@@ -48,19 +48,12 @@ char* gethome()
char* homedir = getenv("HOME");
return homedir;
if (homedir != NULL)
{
printf("Home dir in enviroment");
printf("%s\n", homedir);
}
uid_t uid = getuid();
struct passwd* pw = getpwuid(uid);
if (pw == NULL)
{
printf("Failed\n");
exit(EXIT_FAILURE);
return NULL;
}
return pw->pw_dir;
@@ -269,3 +262,30 @@ bool does_file_exist(const char* file)
return false;
#endif
}
char* expand_tilde(char* path) {
if (path[0] != '~') {
return path;
}
const char* home_dir = getenv("HOME");
if (!home_dir) {
return path;
}
size_t expanded_size = strlen(home_dir) + strlen(path);
char* expanded_path = (char*)malloc(expanded_size + 1);
if (!expanded_path) {
return path;
}
strcpy(expanded_path, home_dir);
strcat(expanded_path, path + 1);
if (path) {
free(path);
}
return expanded_path;
}
+1
View File
@@ -12,5 +12,6 @@ bool does_directory_exist(char* path);
void restrict_folders_to_cache(char* path, int cachesize);
void delete_dir(char* path);
bool does_file_exist(const char* file);
char* expand_tilde(char* path);
#endif
+84 -57
View File
@@ -46,6 +46,7 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
p->verbosity_count = 0;
p->fps = 60;
p->disable_audio = false;
p->udp = false;
p->user_specified_config_file = false;
@@ -61,14 +62,15 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
struct arg_rex* cmd1 = arg_rex1(NULL, NULL, "play", NULL, REG_ICASE, NULL);
struct arg_lit* arg_udp = arg_lit0("d", "udp", "force udp mode for sims which support it");
struct arg_file* arg_conf = arg_filen(NULL, "uiconf", "<config_file>", 0, 1, NULL);
struct arg_lit* arg_audio1 = arg_lit0("a", "disable_audio", "force disable of audio devices");
struct arg_file* arg_conf = arg_file0("c", "uiconf", "<config_file>", NULL);
struct arg_file* arg_log = arg_filen("l", "log", "<log_file>", 0, 1, NULL);
struct arg_str* arg_confdir = arg_strn(NULL, "configdir", "config_dir>", 0, 1, NULL);
struct arg_str* arg_confdir = arg_str1(NULL, NULL, "configdir", "<config_dir>");
struct arg_int* arg_fps = arg_int0("f", "fps", "fps", "main data refresh rate");
struct arg_lit* help = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_lit* help1 = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_lit* vers = arg_litn(NULL,"version", 0, 1, "print version information and exit");
struct arg_end* end1 = arg_end(20);
void* argtable1[] = {cmd1,arg_log,arg_conf,arg_fps,arg_udp,arg_verbosity1,help,vers,end1};
void* argtable1[] = {cmd1,arg_log,arg_conf,arg_fps,arg_udp,arg_audio1,arg_verbosity1,help1,vers,end1};
int nerrors1;
struct arg_rex* cmd2a = arg_rex1(NULL, NULL, "config", NULL, REG_ICASE, NULL);
@@ -83,10 +85,11 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
int nerrors2;
struct arg_rex* cmd3 = arg_rex1(NULL, NULL, "test", NULL, REG_ICASE, NULL);
struct arg_lit* arg_audio2 = arg_lit0("a", "disable_audio", "force disable of audio devices");
struct arg_lit* help3 = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_lit* vers3 = arg_litn(NULL,"version", 0, 1, "print version information and exit");
struct arg_end* end3 = arg_end(20);
void* argtable3[] = {cmd3,arg_verbosity3,help3,vers3,end3};
void* argtable3[] = {cmd3,arg_verbosity3,arg_audio2,help3,vers3,end3};
int nerrors3;
struct arg_lit* help0 = arg_lit0(NULL,"help", "print this help and exit");
@@ -111,8 +114,6 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
goto cleanup;
}
arg_granularity->ival[0] = 1;
nerrors0 = arg_parse(argc,argv,argtable0);
nerrors1 = arg_parse(argc,argv,argtable1);
nerrors2 = arg_parse(argc,argv,argtable2);
@@ -120,6 +121,14 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
if (nerrors1==0)
{
if (help1->count > 0)
{
arg_print_errors(stdout,end1,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
p->program_action = A_PLAY;
p->verbosity_count = arg_verbosity1->count;
@@ -132,6 +141,10 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
{
p->udp = true;
}
if (arg_audio1->count > 0)
{
p->disable_audio = true;
}
if(arg_fps->count > 0)
{
p->fps = arg_fps->ival[0];
@@ -149,60 +162,75 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
}
exitcode = E_SUCCESS_AND_DO;
}
else
if (nerrors2==0)
else if (nerrors2==0)
{
if (help2->count > 0)
{
p->program_action = A_CONFIG_TACH;
p->max_revs = arg_max_revs->ival[0];
p->granularity = 1;
if (arg_granularity->ival[0] > 0 && arg_granularity->ival[0] < 5 && arg_granularity->ival[0] != 3)
{
p->granularity=arg_granularity->ival[0];
}
p->save_file = *arg_save->filename;
p->verbosity_count = arg_verbosity2->count;
exitcode = E_SUCCESS_AND_DO;
}
else if (nerrors3==0)
{
p->program_action = A_TEST;
p->verbosity_count = arg_verbosity3->count;
exitcode = E_SUCCESS_AND_DO;
}
else
{
if (cmd1->count > 0)
{
arg_print_errors(stdout,end1,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
}
else
if (cmd2a->count > 0)
{
arg_print_errors(stdout,end2,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
}
else
{
if (help->count==0 && vers->count==0)
{
printf("%s: missing <play|config|test> command.\n",progname);
printf("Usage 1: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
printf("Usage 2: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
printf("Usage 3: %s ", progname);
arg_print_syntax(stdout,argtable3,"\n");
}
}
arg_print_errors(stdout,end2,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
p->program_action = A_CONFIG_TACH;
p->max_revs = arg_max_revs->ival[0];
p->granularity = 1;
if (arg_granularity->ival[0] > 0 && arg_granularity->ival[0] < 5 && arg_granularity->ival[0] != 3)
{
p->granularity=arg_granularity->ival[0];
}
p->save_file = *arg_save->filename;
p->verbosity_count = arg_verbosity2->count;
exitcode = E_SUCCESS_AND_DO;
}
else if (nerrors3==0)
{
if (help3->count > 0)
{
arg_print_errors(stdout,end3,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable3,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
p->program_action = A_TEST;
p->verbosity_count = arg_verbosity3->count;
if (arg_audio2->count > 0)
{
p->disable_audio = true;
}
exitcode = E_SUCCESS_AND_DO;
}
else
{
// hit this if you typed wrong parameters
if (cmd1->count > 0)
{
arg_print_errors(stdout,end1,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
if (cmd2a->count > 0)
{
arg_print_errors(stdout,end2,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
if (cmd3->count > 0)
{
arg_print_errors(stdout,end3,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable3,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
}
// interpret some special cases before we go through trouble of reading the config file
if (help->count > 0)
if (help0->count > 0 || (cmd3->count == 0 && cmd1->count == 0 && cmd2a->count == 0))
{
printf("Usage: %s\n", progname);
printf("Usage 1: %s ", progname);
@@ -230,5 +258,4 @@ cleanup:
arg_freetable(argtable2,sizeof(argtable2)/sizeof(argtable2[0]));
arg_freetable(argtable3,sizeof(argtable3)/sizeof(argtable3[0]));
return exitcode;
}
+1
View File
@@ -17,6 +17,7 @@ typedef struct
SimulatorAPI sim;
bool simon;
bool udp;
bool disable_audio;
char* config_filepath;
char* config_dirpath;
+65 -32
View File
@@ -68,6 +68,9 @@ void SetSettingsFromParameters(Parameters* p, MonocoqueSettings* ms, char* confi
{
ms->program_action = A_CONFIG_TACH;
}
ms->force_udp_mode = false;
ms->disable_audio = p->disable_audio;
}
int main(int argc, char** argv)
@@ -80,19 +83,22 @@ int main(int argc, char** argv)
fprintf(stderr, "You need a home directory");
return 0;
}
Parameters* p = malloc(sizeof(Parameters));
Parameters* p = NULL;
p = malloc(sizeof(Parameters));
MonocoqueSettings* ms = malloc(sizeof(MonocoqueSettings));;
ConfigError ppe = getParameters(argc, argv, p);
if (ppe == E_SUCCESS_AND_EXIT)
if (ppe == E_SUCCESS_AND_EXIT || ppe == E_SOMETHING_BAD)
{
printf("invalid parameters\n");
goto cleanup_final;
}
xdgHandle xdg;
if(!xdgInitHandle(&xdg))
{
fprintf(stderr, "Function xdgInitHandle() failed, is $HOME unset?");
fprintf(stderr, "Function xdgInitHandle() failed, is $HOME unset?\n");
goto cleanup_final;
}
const char* config_home_str = xdgConfigHome(&xdg);
@@ -112,17 +118,21 @@ int main(int argc, char** argv)
cachedir_str = create_user_dir(home_dir_str, ".cache", PROGRAM_NAME);
}
fprintf(stderr, "applying settings\n");
SetSettingsFromParameters(p, ms, configdir_str, cachedir_str);
freeparams(p);
free(p);
//char* config_file_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.config/") + strlen("monocoque/monocoque.config"));
size_t diameters_file_sz = snprintf(NULL, 0, "%s/.config/monocoque/diameters.config", home_dir_str);
diameters_file_sz += 1;
char* diameters_file_str = ( char* ) malloc(diameters_file_sz);
snprintf(diameters_file_str, diameters_file_sz, "%s/.config/monocoque/diameters.config", home_dir_str);
if(cachedir_str != NULL)
{
free(cachedir_str);
}
if(configdir_str != NULL)
{
free(configdir_str);
}
fprintf(stderr, "settings applied\n");
slog_init("monocoque", SLOG_FLAGS_ALL, 1);
slog_config_t slgCfg;
slog_config_get(&slgCfg);
slgCfg.eColorFormat = SLOG_COLORING_TAG;
@@ -144,11 +154,17 @@ int main(int argc, char** argv)
{
slog_disable(SLOG_DEBUG);
}
xdgWipeHandle(&xdg);
slogi("checking for diameters config");
char* diameters_file_str;
asprintf(&diameters_file_str, "%s/.config/monocoque/diameters.config", home_dir_str);
ms->tyre_diameter_config = strdup(diameters_file_str);
free(diameters_file_str);
ms->useconfig = 0;
ms->configcheck = 0;
free(diameters_file_str);
slogi("Testing monocoque config file: %s", ms->config_str);
slogd("using diameters file %s %i", ms->tyre_diameter_config, ms->configcheck);
@@ -157,13 +173,17 @@ int main(int argc, char** argv)
config_setting_t* config_devices = NULL;
if (!config_read_file(&cfg, ms->config_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
sloge("Issue with monocoque config file: %s:%d - %s", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
fprintf(stderr, "Issue with monocoque config file: %s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
slog_destroy();
goto cleanup_final;
}
else
{
slogi("Openend monocoque configuration file");
slogi("Opened and validated monocoque configuration file");
}
config_destroy(&cfg);
@@ -171,7 +191,10 @@ int main(int argc, char** argv)
{
int error = 0;
SimDevice* tachdev = malloc(sizeof(SimDevice));
tachdev->initialized = false;
SimData* sdata = malloc(sizeof(SimData));
SimInfo* siminfo = malloc(sizeof(SimInfo));
simapi_set_faux_siminfo(siminfo);
DeviceSettings* ds = malloc(sizeof(DeviceSettings));
error = devsetup("USB", "Tachometer", "None", ms, ds, NULL);
@@ -183,7 +206,8 @@ int main(int argc, char** argv)
else
{
int devices = 0;
devices = devinit(tachdev, 1, ds, ms);
devices = devinit(tachdev, siminfo, 1, ds, ms);
if(devices < 1)
{
error = MONOCOQUE_ERROR_INVALID_DEV;
@@ -215,18 +239,22 @@ int main(int argc, char** argv)
int error = 0;
error = MONOCOQUE_ERROR_NONE;
setupsound();
//setupsound();
bool pulseaudio = false;
if (ms->program_action == A_PLAY)
{
ms->useconfig = 1;
slogi("running monocoque in gameloop mode..");
#ifdef USE_PULSEAUDIO
pa_threaded_mainloop_unlock(mainloop);
//#ifdef USE_PULSEAUDIO
//pa_threaded_mainloop_unlock(mainloop);
pulseaudio = true;
#endif
//#endif
if(ms->disable_audio == false)
{
setupsound();
}
//error = looper(devices, numdevices, p);
error = monocoque_mainloop(ms);
if (error == MONOCOQUE_ERROR_NONE)
@@ -237,16 +265,19 @@ int main(int argc, char** argv)
{
sloge("Game loop exited with error code: %i", error);
}
if(ms->disable_audio == false)
{
freesound();
}
}
else
{
slogi("running monocoque in test mode...");
#ifdef USE_PULSEAUDIO
pa_threaded_mainloop_unlock(mainloop);
pulseaudio = true;
#endif
if(ms->disable_audio == false)
{
setupsound();
}
ms->useconfig = 0;
int configs = getNumberOfConfigs(ms->config_str);
@@ -259,7 +290,9 @@ int main(int argc, char** argv)
int numdevices = uiloadconfig(ms->config_str, confignum, configureddevices, ms, ds);
SimDevice* simdevices = malloc(numdevices * sizeof(SimDevice));
int initdevices = devinit(simdevices, configureddevices, ds, ms);
SimInfo* siminfo = malloc(sizeof(SimInfo));
simapi_set_faux_siminfo(siminfo);
int initdevices = devinit(simdevices, siminfo, configureddevices, ds, ms);
for( int i = 0; i < configureddevices; i++)
{
settingsfree(ds[i]);
@@ -281,20 +314,20 @@ int main(int argc, char** argv)
simdevices[x].free(&simdevices[x]);
}
}
if(ms->disable_audio == false)
{
freesound();
}
}
if(pulseaudio == true)
{
freesound();
}
slog_destroy();
}
cleanup_final:
monocoquesettingsfree(ms);
free(ms);
freeparams(p);
free(p);
exit(0);
}
+25 -1
View File
@@ -6,16 +6,40 @@ set(simulatorapi_source_files
simapi/include/acdata.h
simapi/include/rf2data.h
simapi/include/pcars2data.h
simapi/include/dirt2data.h
simapi/include/scs2data.h
simapi/include/outgauge.h
simapi/include/f12018.h
simapi/include/rbrdata.h
simapi/include/r3e.h
simapi/include/wreckfest2data.h
simapi/include/forza.h
simapi/simmap/basicmap.h
simapi/simmap/mapacdata.c
simapi/simmap/mapacdata.h
simapi/simapi/ac.h
simapi/simapi/rf2.h
simapi/simapi/dirt2.h
simapi/simapi/pcars2.h
simapi/simapi/scs2.h
simapi/simapi/outgauge.h
simapi/simapi/wreckfest2.h
simapi/simapi/f1.h
simapi/simapi/rbr.h
simapi/simapi/forzadef.h
simapi/simapi/r3edef.h
simapi/simapi/mapping/acmapper.c
simapi/simapi/mapping/rf2mapper.c
simapi/simapi/mapping/r3emapper.c
simapi/simapi/mapping/pcars2mapper.c
simapi/simapi/mapping/dirt2mapper.c
simapi/simapi/mapping/scs2mapper.c
simapi/simapi/mapping/outgaugemapper.c
simapi/simapi/mapping/f12018mapper.c
simapi/simapi/mapping/rbrmapper.c
simapi/simapi/mapping/wreckfest2mapper.c
simapi/simapi/mapping/forzamapper.c
simapi/simapi/getpid.c
simapi/simapi/getpid.h
)
add_library(simulatorapi STATIC ${simulatorapi_source_files})
+1 -1
View File
@@ -19,7 +19,7 @@ int main(int argc, char* argv[])
pid = getpid();
// get shared memory file descriptor (NOT a file)
fd = shm_open(TEST_MEM_FILE_LOCATION, O_RDONLY, S_IRUSR | S_IWUSR);
fd = shm_open(SIMAPI_MEM_FILE, O_RDONLY, S_IRUSR | S_IWUSR);
if (fd == -1)
{
return 10;
+2 -2
View File
@@ -21,7 +21,7 @@ int main(int argc, char* argv[])
pid = getpid();
fd = shm_open(TEST_MEM_FILE_LOCATION, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
fd = shm_open(SIMAPI_MEM_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1)
{
perror("open");
@@ -99,7 +99,7 @@ int main(int argc, char* argv[])
}
tcsetattr(0, TCSANOW, &canonicalmode);
fd = shm_unlink(TEST_MEM_FILE_LOCATION);
fd = shm_unlink(SIMAPI_MEM_FILE);
if (fd == -1)
{
perror("unlink");
+156 -77
View File
@@ -1,16 +1,163 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <libserialport.h>
#include "../src/arduino/shiftlights/shiftlights.h"
/* Helper function for error handling. */
int check(enum sp_return result);
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");
abort();
case SP_ERR_FAIL:
error_message = sp_last_error_message();
printf("Error: Failed: %s\n", 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 WaitForEventOnPort(struct sp_port* port, int event)
{
int retval;
struct sp_event_set* eventSet = NULL;
retval = sp_new_event_set(&eventSet);
if (retval == SP_OK)
{
retval = sp_add_port_events(eventSet, port, event);
if (retval == SP_OK)
{
printf("set event on port\n");
retval = sp_wait(eventSet, 5000);
}
else
{
puts("Unable to add events to port.");
retval = -1;
}
}
else
{
puts("Unable to create new event set.");
retval = -1;
}
sp_free_event_set(eventSet);
return retval;
}
// 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 ReadFromPort(struct sp_port* port, int* numlights)
{
int count = 0;
int bytesWaiting;
char buf[256];
int retval = 0;
int i;
sp_flush(port, SP_BUF_INPUT);
while (count < 4)
{
printf("Attempting to retrieve num lights from port...\n");
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;
printf("Sending message to get num lights\n");
WaitForEventOnPort(port, 6);
result = check(sp_blocking_write(port, &bytes1, bufsize1, timeout));
printf("Attempting to receive response\n");
WaitForEventOnPort(port, 5);
bytesWaiting = sp_input_waiting(port);
if (bytesWaiting > 0)
{
memset(buf, 0, sizeof(buf));
//printf("bytes found on port\n");
retval = sp_blocking_read(port, buf, sizeof(buf)-1, 10);
if (retval < 0)
{
printf("Error reading from serial port: %d\r\n", retval);
retval = -1;
break;
}
else
{
for(i=0; i<retval; i++)
{
printf("%c", buf[i]);
if (buf[i] == 13)
{
count++;
}
}
int ret = atoi(buf);
*numlights = ret;
return retval;
}
}
else
if (bytesWaiting < 0)
{
printf("Error getting bytes available from serial port: %d\r\n", bytesWaiting);
retval = -1;
break;
}
printf("didn't get nothing\n");
//count++;
retval = 0;
}
return retval;
}
int main()
{
char* port_name = "/dev/simdev0";
char* port_name = "/dev/ttyACM0";
/* The ports we will use. */
struct sp_port* port;
@@ -18,9 +165,9 @@ int main()
/* Open and configure each port. */
printf("Looking for port %s.\n", port_name);
check(sp_get_port_by_name(port_name, &port));
printf("Using %s\r\n", sp_get_port_name(port));
printf("Opening port.\n");
check(sp_open(port, SP_MODE_READ_WRITE));
check(sp_open(port, SP_MODE_READ | SP_MODE_WRITE));
printf("Setting port to 115200 8N1, no flow control.\n");
check(sp_set_baudrate(port, 115200));
@@ -28,58 +175,16 @@ int main()
check(sp_set_parity(port, SP_PARITY_NONE));
check(sp_set_stopbits(port, 1));
check(sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE));
check(sp_set_rts(port, 1));
check(sp_set_dtr(port, 1));
ShiftLightsData sd;
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 = 2000;
result = check(sp_blocking_write(port, &bytes1, bufsize1, timeout));
result = check(sp_blocking_read(port, &recv_buf1, recv_bufsize1, timeout));
char numstr[recv_bufsize1];
for(int j = 0; j < recv_bufsize1; j++)
{
numstr[j] = '\0';
}
for(int j = 0; j < recv_bufsize1; j++)
{
printf("%02x\n", recv_buf1[j]);
if(recv_buf1[j] != 0 && recv_buf1[j] != 0x0d && recv_buf1[j] != 0x0a)
{
numstr[j] = recv_buf1[j];
}
}
int numlights = atoi(numstr);
printf("numlights is %s %i\n", numstr, numlights);
//printf("%x\n", recv_buf[0]);
//printf("%x\n", recv_buf[1]);
//printf("%x\n", recv_buf[2]);
//printf("%x\n", recv_buf[3]);
//printf("%x\n", recv_buf[4]);
int numlights = 0;
result = ReadFromPort(port, &numlights);
int timeout = 2000;
size_t bufsize = (numlights * 3) + 14;
char bytes[bufsize];
for(int j = 0; j < bufsize; j++)
@@ -132,30 +237,4 @@ int main()
sp_free_port(port);
}
/* Helper function for error handling. */
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");
abort();
case SP_ERR_FAIL:
error_message = sp_last_error_message();
printf("Error: Failed: %s\n", 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;
}
}
@@ -0,0 +1,8 @@
Package: monocoque
Version: 1
Section: misc
Priority: optional
Architecture: amd64
Maintainer: Paul Jones <paul@spacefreak18.xyz>
Description: Racing Simulator Device Manager
Depends: libpulse0,libconfig11,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2,libxdg-basedir1,libhidapi-hidraw0
@@ -0,0 +1,8 @@
Package: monocoque
Version: 1
Section: misc
Priority: optional
Architecture: amd64
Maintainer: Paul Jones <paul@spacefreak18.xyz>
Description: Racing Simulator Device Manager
Depends: libpulse0,libconfig11,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2,libxdg-basedir1,libhidapi-hidraw0
@@ -0,0 +1,8 @@
Package: monocoque
Version: 1
Section: misc
Priority: optional
Architecture: amd64
Maintainer: Paul Jones <paul@spacefreak18.xyz>
Description: Racing Simulator Device Manager
Depends: libpulse0,libconfig9,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2,libxdg-basedir1,libhidapi-hidraw0
+256
View File
@@ -0,0 +1,256 @@
#!/bin/bash
# Monocoque Distrobox Installer
# For immutable Linux distributions (Bazzite, Silverblue, etc.)
# where native package managers are unavailable.
#
# Installs monocoque and dependencies inside an Arch Linux distrobox container,
# with wrapper scripts on the host for transparent access.
set -euo pipefail
SCRIPT_VERSION="1.0.0"
CONTAINER="simracing"
INSTALL_DIR="$HOME/.local/share/simracing"
BIN_DIR="$HOME/.local/bin"
CONFIG_DIR="$HOME/.config/monocoque"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
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 Distrobox Installer v${SCRIPT_VERSION} ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
}
# --- Main ---
print_header
# Check ~/.local/bin is in PATH
if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
log_error "$BIN_DIR is not in your PATH."
log_info "Add this to your shell config:"
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
exit 1
fi
# Check distrobox is available
if ! command -v distrobox &>/dev/null; then
log_error "distrobox is not installed."
log_info "Install distrobox first: https://distrobox.it/#installation"
exit 1
fi
# Ensure dialout group exists and user is a member
log_info "Checking serial device access (dialout group)..."
if ! grep -q '^dialout:' /etc/group 2>/dev/null; then
if grep -q '^dialout:' /usr/lib/group 2>/dev/null; then
log_info "Adding dialout group to /etc/group (Fedora Atomic fix)..."
grep -E '^dialout:' /usr/lib/group | sudo tee -a /etc/group >/dev/null
else
log_info "Creating dialout group..."
sudo groupadd dialout
fi
fi
if ! groups | grep -qw dialout; then
log_info "Adding $USER to the dialout group for serial device access..."
sudo usermod -aG dialout "$USER"
log_warn "You will need to REBOOT after setup completes for serial access to work."
echo ""
fi
# Prevent ModemManager from grabbing Moza serial devices
UDEV_RULE="/etc/udev/rules.d/99-moza-serial.rules"
if [ ! -f "$UDEV_RULE" ]; then
if systemctl is-active --quiet ModemManager 2>/dev/null; then
echo ""
log_warn "ModemManager is running on your system."
echo " Moza wheel bases use USB serial devices that ModemManager may claim,"
echo " preventing monocoque from communicating with the wheel."
echo ""
echo " A udev rule can tell ModemManager to ignore Moza devices (vendor 346e)."
echo " This only affects Moza USB devices — other modems will work normally."
echo ""
read -rp "Install udev rule to prevent ModemManager from grabbing Moza devices? [Y/n]: " mm_confirm
if [[ "$mm_confirm" =~ ^[Nn]$ ]]; then
echo ""
log_info "Skipped udev rule. If monocoque can't talk to your wheel, run:"
echo " sudo tee $UDEV_RULE <<< 'ACTION==\"add|change\", SUBSYSTEM==\"usb\", ATTR{idVendor}==\"346e\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"'"
echo " sudo udevadm control --reload-rules && sudo udevadm trigger"
echo ""
else
log_info "Installing udev rule..."
echo 'ACTION=="add|change", SUBSYSTEM=="usb", ATTR{idVendor}=="346e", ENV{ID_MM_DEVICE_IGNORE}="1"' | sudo tee "$UDEV_RULE" >/dev/null
sudo udevadm control --reload-rules
sudo udevadm trigger
log_success "ModemManager udev rule installed"
fi
else
# ModemManager not running — install the rule silently
echo 'ACTION=="add|change", SUBSYSTEM=="usb", ATTR{idVendor}=="346e", ENV{ID_MM_DEVICE_IGNORE}="1"' | sudo tee "$UDEV_RULE" >/dev/null
sudo udevadm control --reload-rules
sudo udevadm trigger
fi
fi
# Create the Arch Linux distrobox container
if ! distrobox list 2>/dev/null | grep -q "$CONTAINER"; then
log_info "Creating Arch Linux container '$CONTAINER'..."
distrobox create \
--image docker.io/library/archlinux:latest \
--name "$CONTAINER" \
--root \
--additional-flags "--privileged --security-opt seccomp=unconfined" \
--pre-init-hooks "sed -i 's/^SigLevel.*/SigLevel = Never/' /etc/pacman.conf && pacman -Sy --noconfirm archlinux-keyring && pacman-key --init && pacman-key --populate archlinux && sed -i 's/^SigLevel.*/SigLevel = Required DatabaseOptional/' /etc/pacman.conf"
log_success "Container '$CONTAINER' created"
else
log_info "Container '$CONTAINER' already exists, skipping creation."
fi
# Install packages inside the container
log_info "Installing packages inside container (this may take a few minutes)..."
distrobox enter --root "$CONTAINER" -- bash -c '
set -euo pipefail
# Install build dependencies
echo "Installing build dependencies..."
sudo pacman -Syu --needed --noconfirm base-devel git mingw-w64-gcc
# Install yay (AUR helper) if not present
if ! command -v yay &>/dev/null; then
echo "Installing yay AUR helper..."
TMPDIR=$(mktemp -d)
git clone https://aur.archlinux.org/yay-bin.git "$TMPDIR/yay-bin"
cd "$TMPDIR/yay-bin"
makepkg -si --noconfirm
cd /
rm -rf "$TMPDIR"
fi
# Install AUR packages sequentially (simd depends on simapi, monocoque depends on both)
echo "Installing AUR packages..."
yay -S --needed --noconfirm simapi-git
yay -S --needed --noconfirm simd-git
yay -S --needed --noconfirm monocoque-git
# Build simshmbridge (not on AUR)
INSTALL_DIR="$HOME/.local/share/simracing"
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
if [ ! -d "simshmbridge" ]; then
echo "Cloning simshmbridge..."
git clone --recurse-submodules "https://github.com/Spacefreak18/simshmbridge.git"
else
echo "simshmbridge already cloned, pulling latest..."
cd simshmbridge && git pull && git submodule update --init --recursive && cd ..
fi
echo "Building simshmbridge..."
cd "$INSTALL_DIR/simshmbridge"
git submodule update --init --recursive
make clean 2>/dev/null || true
make -j$(nproc)
echo ""
echo "All components built successfully inside container."
'
# Install wrapper scripts to ~/.local/bin
log_info "Installing wrapper scripts..."
mkdir -p "$BIN_DIR"
cat > "$BIN_DIR/start-simd" << 'EOF'
#!/usr/bin/env bash
exec distrobox enter --root simracing -- simd "$@"
EOF
chmod +x "$BIN_DIR/start-simd"
cat > "$BIN_DIR/start-monocoque" << 'EOF'
#!/usr/bin/env bash
exec distrobox enter --root simracing -- monocoque play "$@"
EOF
chmod +x "$BIN_DIR/start-monocoque"
cat > "$BIN_DIR/test-monocoque" << 'EOF'
#!/usr/bin/env bash
exec distrobox enter --root simracing -- monocoque test -vv "$@"
EOF
chmod +x "$BIN_DIR/test-monocoque"
log_success "Wrapper scripts installed"
# Setup monocoque config
mkdir -p "$CONFIG_DIR"
if [ ! -f "$CONFIG_DIR/monocoque.config" ]; then
log_info "Creating monocoque config template..."
cat > "$CONFIG_DIR/monocoque.config" << 'EOF'
configs = (
{
sim = "default";
car = "default";
devices = (
{
device = "Serial";
type = "Wheel";
subtype = "MozaNew";
devpath = "/dev/serial/by-id/CHANGE_ME_TO_YOUR_MOZA_BASE";
baud = 115200;
});
}
);
EOF
log_success "Config created at $CONFIG_DIR/monocoque.config"
else
log_info "Config already exists, skipping"
fi
# Done
echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Installation Complete! ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
log_success "Monocoque (distrobox) installed!"
echo ""
echo " Installed:"
echo " AUR packages: simapi-git, simd-git, monocoque-git (in '$CONTAINER' container)"
echo " Bridge: $INSTALL_DIR/simshmbridge/"
echo " Config: $CONFIG_DIR/monocoque.config"
echo " Commands: start-simd, start-monocoque, test-monocoque"
echo ""
echo " Next steps:"
echo " 1. Set your device path in $CONFIG_DIR/monocoque.config"
echo " (replace CHANGE_ME_TO_YOUR_MOZA_BASE with your device from /dev/serial/by-id/)"
echo " 2. Test with: test-monocoque"
echo " 3. Set Steam launch options for your games to start the telemetry stack automatically:"
echo " In Steam > Game Properties > Launch Options, add:"
echo " start-simd & start-monocoque & <bridge_path> %command%"
echo " For example, for Assetto Corsa:"
echo " start-simd & start-monocoque & $INSTALL_DIR/simshmbridge/assets/acbridge.exe %command%"
echo ""
echo " For tools that automate steps 1 and 3 (Moza auto-detection, Steam launch"
echo " option configuration, unified game launcher), visit:"
echo " https://github.com/cescofry/simracing-utilities"
echo ""
@@ -0,0 +1,102 @@
#!/bin/bash
# Monocoque Distrobox Uninstaller
# Removes the distrobox-based monocoque installation.
set -euo pipefail
CONTAINER="simracing"
BIN_DIR="$HOME/.local/bin"
INSTALL_DIR="$HOME/.local/share/simracing"
CONFIG_DIR="$HOME/.config/monocoque"
UDEV_RULE="/etc/udev/rules.d/99-moza-serial.rules"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_header() {
echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Monocoque Distrobox Uninstaller ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
}
print_header
log_warn "This will remove:"
echo " - Distrobox container '$CONTAINER'"
echo " - Wrapper scripts ($BIN_DIR/start-simd, start-monocoque, test-monocoque)"
echo " - simshmbridge source ($INSTALL_DIR)"
echo ""
echo "This will NOT remove:"
echo " - Configuration files ($CONFIG_DIR)"
echo " - Convenience scripts (simracing-launch, configure-moza, etc.)"
echo ""
read -rp "Continue with uninstallation? [y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
log_info "Uninstallation cancelled"
exit 0
fi
echo ""
# Remove wrapper scripts
log_info "Removing wrapper scripts..."
rm -f "$BIN_DIR/start-simd"
rm -f "$BIN_DIR/start-monocoque"
rm -f "$BIN_DIR/test-monocoque"
log_success "Wrapper scripts removed"
# Remove simshmbridge source
if [ -d "$INSTALL_DIR" ]; then
log_info "Removing simshmbridge source..."
rm -rf "$INSTALL_DIR"
log_success "simshmbridge source removed"
fi
# Remove distrobox container
if distrobox list --root 2>/dev/null | grep -q "$CONTAINER"; then
log_info "Removing distrobox container '$CONTAINER'..."
distrobox rm --root --force "$CONTAINER"
log_success "Container '$CONTAINER' removed"
else
log_info "Container '$CONTAINER' not found, skipping"
fi
# Optionally remove udev rule
if [ -f "$UDEV_RULE" ]; then
echo ""
read -rp "Remove ModemManager udev rule ($UDEV_RULE)? [y/N]: " remove_udev
if [[ "$remove_udev" =~ ^[Yy]$ ]]; then
sudo rm -f "$UDEV_RULE"
sudo udevadm control --reload-rules
sudo udevadm trigger
log_success "udev rule removed"
else
log_info "Keeping udev rule"
fi
fi
echo ""
log_success "Uninstallation complete!"
echo ""
log_info "Config files preserved at $CONFIG_DIR"
echo " To remove those too: rm -rf $CONFIG_DIR"
echo ""
+39
View File
@@ -0,0 +1,39 @@
#dnf install pulseaudio-libs-devel argtable-devel libconfig-devel hidapi-devel libserialport-devel lua-devel libuv-devel libxdg-basedir-devel libxml2-devel procps-ng-devel
Summary: A device manager for racing sims
Name: monocoque
Version: 0.0.5
Release: 1
License: GPL
Group: Applications/Sound
Source: https://github.com/monocoque
URL: https://spacefreak18.github.io/simapi
Distribution: Fedora Linux
Vendor: spacefreak18
Packager: Paul Jones <paul@spacefreak18.xyz>
Requires: pulseaudio-libs argtable libconfig hidapi libserialport libuv libxdg-basedir lua-libs libxml2 procps-ng
%description
A device manager for Racing sims
%prep
rm -rf $RPM_BUILD_DIR/monocoque
rm -rf $RPM_SOURCE_DIR/monocoque
cd $RPM_SOURCE_DIR
git clone https://github.com/spacefreak18/monocoque
cd monocoque
git submodule update --init --recursive
cd ..
cp -r $RPM_SOURCE_DIR/monocoque $RPM_BUILD_DIR/
%build
cd $RPM_BUILD_DIR/monocoque
cmake -B build
cd build
make
%install
mkdir -p $RPM_BUILD_ROOT/usr/bin
cp $RPM_BUILD_DIR/monocoque/build/monocoque $RPM_BUILD_ROOT/usr/bin/monocoque
%files
/usr/bin/monocoque
+616
View File
@@ -0,0 +1,616 @@
#!/usr/bin/env python3
"""
Monocoque Manager - Simple TUI for managing monocoque telemetry system
"""
import subprocess
import sys
import os
import time
from pathlib import Path
try:
import curses
except ImportError:
print("Error: curses module not available")
sys.exit(1)
# Constants to avoid magic numbers
MIN_TERMINAL_HEIGHT = 20
MIN_TERMINAL_WIDTH = 60
PROCESS_STOP_WAIT_TIME = 1 # seconds
SIMD_START_DELAY = 2 # seconds
class MonocoqueManager:
def __init__(self):
# Mirror the shell script's robust path detection
xdg_data = os.environ.get('XDG_DATA_HOME', os.path.join(Path.home(), ".local/share"))
self.install_dir = Path(os.environ.get('MONOCOQUE_INSTALL_DIR', os.path.join(xdg_data, "monocoque")))
xdg_config = os.environ.get('XDG_CONFIG_HOME', os.path.join(Path.home(), ".config"))
self.config_dir = Path(xdg_config)
self.simd_running = False
self.monocoque_running = False
self.last_cleaned_pid_files = [] # Track cleaned stale PID files
def check_processes(self):
"""Check if simd and monocoque are running using careful detection"""
try:
manager_pid = os.getpid() # Our own PID to exclude
# Method 1: Exact name matching (safest)
result = subprocess.run(['pgrep', '-x', 'simd'],
capture_output=True, text=True)
simd_exact = bool(result.stdout.strip())
result = subprocess.run(['pgrep', '-x', 'monocoque'],
capture_output=True, text=True)
monocoque_exact = bool(result.stdout.strip())
# Method 2: Careful process scanning
simd_found = False
monocoque_found = False
ps_result = subprocess.run(['ps', 'axo', 'pid,comm,args'],
capture_output=True, text=True)
if ps_result.returncode == 0:
for line in ps_result.stdout.split('\n')[1:]: # Skip header
parts = line.strip().split(None, 2)
if len(parts) >= 3:
pid, comm, args = parts
# Skip our own process
if pid == str(manager_pid):
continue
# Look for simd executable
if (comm == 'simd' or
(comm.endswith('simd') and 'manager' not in comm) or
('/simd' in args and 'manager' not in args)):
simd_found = True
# Look for monocoque executable (not manager)
elif ((comm == 'monocoque' or
(comm.endswith('monocoque') and 'manager' not in comm)) and
'manager' not in args and
'monocoque-manager' not in args and
'python' not in args):
monocoque_found = True
# Set final status
self.simd_running = simd_exact or simd_found
self.monocoque_running = monocoque_exact or monocoque_found
# Check for stale PID files and clean them up
self.cleanup_stale_pid_files()
except Exception:
# Fallback: set both to False if detection fails
self.simd_running = False
self.monocoque_running = False
def cleanup_stale_pid_files(self):
"""Clean up stale PID files when processes aren't actually running"""
pid_files = [
('/tmp/simd.pid', self.simd_running),
('/tmp/monocoque.pid', self.monocoque_running),
]
cleaned_files = []
for pid_file_path, process_running in pid_files:
if os.path.exists(pid_file_path) and not process_running:
try:
# Read the PID and check if that process exists
with open(pid_file_path, 'r') as f:
pid_str = f.read().strip()
if pid_str.isdigit():
pid = int(pid_str)
# Check if this PID actually exists and is the right process
try:
ps_result = subprocess.run(['ps', '-p', str(pid), '-o', 'comm='],
capture_output=True, text=True)
if ps_result.returncode != 0:
# Process doesn't exist, PID file is stale
os.remove(pid_file_path)
cleaned_files.append(os.path.basename(pid_file_path))
else:
# Process exists, check if it's actually simd/monocoque
comm = ps_result.stdout.strip()
service_name = os.path.basename(pid_file_path).replace('.pid', '')
if service_name not in comm:
# Wrong process, PID file is stale
os.remove(pid_file_path)
cleaned_files.append(os.path.basename(pid_file_path))
except:
# Error checking process, assume stale and remove
os.remove(pid_file_path)
cleaned_files.append(os.path.basename(pid_file_path))
else:
# Invalid PID format, remove stale file
os.remove(pid_file_path)
cleaned_files.append(os.path.basename(pid_file_path))
except Exception:
# Error reading/processing PID file, try to remove it
try:
os.remove(pid_file_path)
cleaned_files.append(os.path.basename(pid_file_path))
except:
pass
# Store cleaned files for potential user feedback
self.last_cleaned_pid_files = cleaned_files
return cleaned_files
def check_installation(self):
"""Check if monocoque is installed"""
simd_exists = (self.install_dir / "simapi/simd/build/simd").exists() or \
subprocess.run(['which', 'simd'], capture_output=True).returncode == 0
monocoque_exists = (self.install_dir / "monocoque/build/monocoque").exists() or \
subprocess.run(['which', 'monocoque'], capture_output=True).returncode == 0
return simd_exists and monocoque_exists
def draw_header(self, stdscr):
"""Draw header"""
height, width = stdscr.getmaxyx()
title = "Monocoque Manager"
stdscr.attron(curses.color_pair(2) | curses.A_BOLD)
stdscr.addstr(0, (width - len(title)) // 2, title)
stdscr.attroff(curses.color_pair(2) | curses.A_BOLD)
stdscr.addstr(1, 0, "─" * width)
def draw_status(self, stdscr, row):
"""Draw status section"""
self.check_processes()
stdscr.attron(curses.A_BOLD)
stdscr.addstr(row, 2, "Status:")
stdscr.attroff(curses.A_BOLD)
# simd status
status_str = "● simd: "
stdscr.addstr(row + 1, 4, status_str)
if self.simd_running:
stdscr.attron(curses.color_pair(1))
stdscr.addstr("RUNNING")
stdscr.attroff(curses.color_pair(1))
else:
stdscr.attron(curses.color_pair(3))
stdscr.addstr("STOPPED")
stdscr.attroff(curses.color_pair(3))
# monocoque status
status_str = "● monocoque: "
stdscr.addstr(row + 2, 4, status_str)
if self.monocoque_running:
stdscr.attron(curses.color_pair(1))
stdscr.addstr("RUNNING")
stdscr.attroff(curses.color_pair(1))
else:
stdscr.attron(curses.color_pair(3))
stdscr.addstr("STOPPED")
stdscr.attroff(curses.color_pair(3))
# Show notification about cleaned PID files
current_row = row + 3
if self.last_cleaned_pid_files:
cleaned_msg = f"● Cleaned stale PID files: {', '.join(self.last_cleaned_pid_files)}"
stdscr.attron(curses.color_pair(4))
stdscr.addstr(current_row, 4, cleaned_msg)
stdscr.attroff(curses.color_pair(4))
current_row += 1
# Clear the notification after showing it once
self.last_cleaned_pid_files = []
return current_row
def draw_menu(self, stdscr, row, selected):
"""Draw menu options"""
height, width = stdscr.getmaxyx()
stdscr.addstr(row, 0, "─" * width)
row += 1
stdscr.attron(curses.A_BOLD)
stdscr.addstr(row, 2, "Actions:")
stdscr.attroff(curses.A_BOLD)
row += 1
menu_items = [
("1", "Start simd", not self.simd_running),
("2", "Start monocoque", not self.monocoque_running and self.simd_running),
("3", "Test configuration", True),
("4", "Edit monocoque config", True),
("5", "View logs", True),
("6", "Restart services", self.simd_running or self.monocoque_running),
("7", "Stop all", self.simd_running or self.monocoque_running),
("0", "Quit", True),
]
for i, (key, label, enabled) in enumerate(menu_items):
if enabled:
if i == selected:
stdscr.attron(curses.A_REVERSE)
stdscr.addstr(row + i, 4, f"[{key}] {label}")
if i == selected:
stdscr.attroff(curses.A_REVERSE)
else:
stdscr.attron(curses.color_pair(4))
stdscr.addstr(row + i, 4, f"[{key}] {label}")
stdscr.attroff(curses.color_pair(4))
return row + len(menu_items)
def get_enabled_menu_items(self):
"""Get list of currently enabled menu items with their indices"""
menu_items = [
("1", "Start simd", not self.simd_running),
("2", "Start monocoque", not self.monocoque_running and self.simd_running),
("3", "Test configuration", True),
("4", "Edit monocoque config", True),
("5", "View logs", True),
("6", "Restart services", self.simd_running or self.monocoque_running),
("7", "Stop all", self.simd_running or self.monocoque_running),
("0", "Quit", True),
]
enabled_items = []
for i, (key, label, enabled) in enumerate(menu_items):
if enabled:
enabled_items.append((i, key))
return enabled_items
def draw_footer(self, stdscr):
"""Draw footer"""
height, width = stdscr.getmaxyx()
footer = "↑↓/j/k: Navigate | Enter: Select | Numbers: Direct | q/ESC: Quit"
stdscr.addstr(height - 2, 0, "─" * width)
stdscr.attron(curses.color_pair(4))
stdscr.addstr(height - 1, (width - len(footer)) // 2, footer)
stdscr.attroff(curses.color_pair(4))
def run_command(self, cmd, terminal=True):
"""Run a command"""
if terminal:
# Try to find a terminal emulator
terminals = ['konsole', 'gnome-terminal', 'xfce4-terminal',
'alacritty', 'kitty', 'xterm']
for term in terminals:
if subprocess.run(['which', term], capture_output=True).returncode == 0:
if term == 'konsole':
subprocess.Popen([term, '-e', 'bash', '-c', cmd])
elif term == 'gnome-terminal':
subprocess.Popen([term, '--', 'bash', '-c', cmd])
elif term == 'kitty':
# Kitty prefers commands as positional arguments
subprocess.Popen([term, 'bash', '-c', cmd])
elif term in ['xfce4-terminal', 'alacritty', 'xterm']:
# Ensure the whole bash command is quoted correctly for -e
subprocess.Popen([term, '-e', f"bash -c '{cmd}'"])
return True
return False
else:
subprocess.run(cmd, shell=True)
return True
def handle_action(self, action):
"""Handle menu action"""
if action == '1': # Start simd
cmd = "start-simd || $HOME/.local/bin/start-simd; exec bash"
self.run_command(cmd)
elif action == '2': # Start monocoque
cmd = "start-monocoque || $HOME/.local/bin/start-monocoque; exec bash"
self.run_command(cmd)
elif action == '3': # Test configuration
cmd = "test-monocoque || $HOME/.local/bin/test-monocoque; read -p 'Press Enter to close...'"
self.run_command(cmd)
elif action == '4': # Edit config
editor = os.environ.get('EDITOR', 'nano')
config_file = self.config_dir / "monocoque/monocoque.config"
subprocess.run([editor, str(config_file)])
elif action == '5': # View logs
xdg_cache = os.environ.get('XDG_CACHE_HOME', os.path.join(Path.home(), ".cache"))
log_dir = Path(xdg_cache) / "monocoque"
if log_dir.exists():
cmd = f"find {log_dir} -name '*.log' -exec ls -la {{}} + 2>/dev/null; find {log_dir} -name '*.log' -exec tail -f {{}} + 2>/dev/null; read -p 'Press Enter to close...'"
self.run_command(cmd)
else:
# Create a simple message if no logs exist
cmd = f"echo 'No log directory found at {log_dir}'; echo 'Logs will appear here when monocoque runs'; read -p 'Press Enter to close...'"
self.run_command(cmd)
elif action == '6': # Restart services
stopped = self.stop_all_services()
if stopped:
print(f"Stopped: {', '.join(stopped)}")
# Wait a moment for processes to stop
time.sleep(PROCESS_STOP_WAIT_TIME)
# Restart simd first
cmd = f"start-simd || $HOME/.local/bin/start-simd; sleep {SIMD_START_DELAY}; start-monocoque || $HOME/.local/bin/start-monocoque; exec bash"
self.run_command(cmd)
print("Restarting services in terminal window...")
elif action == '7': # Stop all
stopped = self.stop_all_services()
if stopped:
print(f"Stopped: {', '.join(stopped)}")
else:
print("No running services found to stop")
def stop_all_services(self):
"""Carefully stop simd and monocoque processes while preserving the manager"""
stopped_services = []
manager_pid = os.getpid() # Get our own PID to exclude it
# Method 1: Try exact name matching (safest)
for service in ['simd', 'monocoque']:
result = subprocess.run(['pkill', '-x', service],
capture_output=True, text=True)
if result.returncode == 0:
stopped_services.append(f"{service} (exact)")
# Method 2: Carefully search ps output and target specific processes
try:
ps_result = subprocess.run(['ps', 'axo', 'pid,ppid,comm,args'],
capture_output=True, text=True)
if ps_result.returncode == 0:
lines = ps_result.stdout.split('\n')[1:] # Skip header
for line in lines:
parts = line.strip().split(None, 3) # Split into PID, PPID, COMM, ARGS
if len(parts) >= 4:
pid, ppid, comm, args = parts
# Skip our own process
if pid == str(manager_pid):
continue
# Target simd: look for executables ending in 'simd' or containing '/simd'
if (comm == 'simd' or
(comm.endswith('simd') and not 'manager' in comm) or
('/simd' in args and 'manager' not in args)):
try:
subprocess.run(['kill', '-TERM', pid], capture_output=True)
stopped_services.append(f"simd (PID {pid})")
time.sleep(0.1) # Brief pause between kills
except:
pass
# Target monocoque: be very specific to avoid manager
elif ((comm == 'monocoque' or
(comm.endswith('monocoque') and not 'manager' in comm)) and
'manager' not in args and
'monocoque-manager' not in args and
'python' not in args): # Avoid Python scripts
try:
subprocess.run(['kill', '-TERM', pid], capture_output=True)
stopped_services.append(f"monocoque (PID {pid})")
time.sleep(0.1) # Brief pause between kills
except:
pass
except:
pass
# Wait for graceful shutdown
time.sleep(PROCESS_STOP_WAIT_TIME)
# Method 3: Force kill stubborn processes (but still be careful)
try:
# Only force kill exact matches, never pattern matches
for service in ['simd', 'monocoque']:
subprocess.run(['pkill', '-9', '-x', service],
capture_output=True, text=True)
except:
pass
return stopped_services
def main(self, stdscr):
"""Main TUI loop"""
try:
curses.curs_set(0)
except curses.error:
pass # Some terminals don't support cursor visibility changes
try:
stdscr.keypad(True) # Enable keypad mode for arrow keys
except curses.error:
pass # Fallback if keypad mode fails
# Initialize colors if available
if curses.has_colors():
curses.start_color()
try:
curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK)
except curses.error:
pass # Fallback for terminals with limited color support
selected = 0
while True:
stdscr.clear()
height, width = stdscr.getmaxyx()
# Ensure selected index is valid for current state
self.check_processes()
enabled_items = self.get_enabled_menu_items()
enabled_indices = [item[0] for item in enabled_items]
if enabled_indices and selected not in enabled_indices:
selected = enabled_indices[0]
if height < MIN_TERMINAL_HEIGHT or width < MIN_TERMINAL_WIDTH:
stdscr.addstr(0, 0, "Terminal too small!")
stdscr.addstr(1, 0, f"Minimum: {MIN_TERMINAL_WIDTH}x{MIN_TERMINAL_HEIGHT}")
stdscr.refresh()
stdscr.getch()
continue
self.draw_header(stdscr)
row = self.draw_status(stdscr, 3)
row = self.draw_menu(stdscr, row + 1, selected)
self.draw_footer(stdscr)
stdscr.refresh()
key = stdscr.getch()
# Get currently enabled menu items
enabled_items = self.get_enabled_menu_items()
enabled_indices = [item[0] for item in enabled_items]
# Handle arrow key navigation (with multiple key codes for compatibility)
if key in [curses.KEY_UP, 259, ord('k'), ord('K')]: # UP arrow or k/K
if enabled_indices:
current_pos = enabled_indices.index(selected) if selected in enabled_indices else 0
new_pos = (current_pos - 1) % len(enabled_indices)
selected = enabled_indices[new_pos]
elif key in [curses.KEY_DOWN, 258, ord('j'), ord('J')]: # DOWN arrow or j/J
if enabled_indices:
current_pos = enabled_indices.index(selected) if selected in enabled_indices else 0
new_pos = (current_pos + 1) % len(enabled_indices)
selected = enabled_indices[new_pos]
elif key in [curses.KEY_ENTER, ord('\n'), ord('\r'), 10, 13]: # Various ENTER codes
# Execute currently selected action
action_key = None
for idx, key_char in enabled_items:
if idx == selected:
action_key = key_char
break
if action_key and action_key == '0':
break
elif action_key:
self.handle_action(action_key)
# Force a refresh after action
stdscr.clear()
elif key in [ord('q'), ord('Q'), 27]: # q/Q or ESC
break
elif key == ord('1'):
self.handle_action('1')
stdscr.clear() # Force refresh after action
elif key == ord('2'):
self.handle_action('2')
stdscr.clear() # Force refresh after action
elif key == ord('3'):
self.handle_action('3')
stdscr.clear() # Force refresh after action
elif key == ord('4'):
self.handle_action('4')
stdscr.clear() # Force refresh after action
elif key == ord('5'):
self.handle_action('5')
stdscr.clear() # Force refresh after action
elif key == ord('6'):
self.handle_action('6')
stdscr.clear() # Force refresh after action
elif key == ord('7'):
self.handle_action('7')
stdscr.clear() # Force refresh after action
elif key == ord('0'):
break
def text_interface(self):
"""Fallback text-based interface when curses is not available"""
print("Monocoque Manager - Text Mode")
print("=" * 40)
while True:
self.check_processes()
print("\nStatus:")
print(f" simd: {'RUNNING' if self.simd_running else 'STOPPED'}")
print(f" monocoque: {'RUNNING' if self.monocoque_running else 'STOPPED'}")
# Show notification about cleaned PID files
if self.last_cleaned_pid_files:
print(f" Note: Cleaned stale PID files: {', '.join(self.last_cleaned_pid_files)}")
# Clear the notification after showing it once
self.last_cleaned_pid_files = []
print("\nActions:")
menu_items = [
("1", "Start simd", not self.simd_running),
("2", "Start monocoque", not self.monocoque_running and self.simd_running),
("3", "Test configuration", True),
("4", "Edit monocoque config", True),
("5", "View logs", True),
("6", "Restart services", self.simd_running or self.monocoque_running),
("7", "Stop all", self.simd_running or self.monocoque_running),
("0", "Quit", True),
]
for key, label, enabled in menu_items:
if enabled:
print(f" [{key}] {label}")
else:
print(f" [{key}] {label} (disabled)")
try:
choice = input("\nEnter your choice: ").strip()
if choice == '0':
break
elif choice in ['1', '2', '3', '4', '5', '6', '7']:
# Check if action is enabled
for key, label, enabled in menu_items:
if key == choice and enabled:
self.handle_action(choice)
if choice in ['4']: # Actions that don't need terminal
print("Action completed.")
elif choice in ['6', '7']: # Stop/restart actions provide their own feedback
pass # Feedback already provided by handle_action
else:
print("Command executed. Check the terminal output.")
break
elif key == choice and not enabled:
print("That action is currently disabled.")
break
else:
print("Invalid choice. Please try again.")
except (KeyboardInterrupt, EOFError):
print("\nExiting...")
break
def main():
"""Entry point"""
manager = MonocoqueManager()
if not manager.check_installation():
print("Error: Monocoque does not appear to be installed")
print("Run the installer first: ./install.sh")
sys.exit(1)
# Check if we can use curses (proper terminal environment)
term = os.environ.get('TERM', 'dumb')
if term == 'dumb' or not sys.stdout.isatty():
print("Note: Using text mode (terminal doesn't support full TUI)")
manager.text_interface()
else:
try:
curses.wrapper(manager.main)
except KeyboardInterrupt:
print("\nExiting...")
except curses.error as e:
print(f"Terminal error: {e}")
print("Falling back to text mode...")
manager.text_interface()
except Exception as e:
print(f"Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
+128
View File
@@ -0,0 +1,128 @@
#!/bin/bash
# Monocoque Uninstaller
# Removes monocoque and all related components
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
INSTALL_DIR="${MONOCOQUE_INSTALL_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/monocoque}"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}"
CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}"
BIN_DIR="$HOME/.local/bin"
SYSTEMD_DIR="$CONFIG_DIR/systemd/user"
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 Uninstaller ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
}
print_header
log_warn "This will remove:"
echo " • Monocoque installation ($INSTALL_DIR)"
echo " • Configuration files ($CONFIG_DIR/monocoque, $CONFIG_DIR/simd)"
echo " • Launcher scripts ($BIN_DIR/start-*, test-monocoque)"
echo " • systemd service files ($SYSTEMD_DIR/simd.service)"
echo " • Log files ($CACHE_DIR/monocoque)"
echo ""
echo "This will NOT remove:"
echo " • System dependencies (yder, libuv, etc.)"
echo " • Compiled simapi library (/usr/local/lib/libsimapi.so)"
echo ""
read -p "Continue with uninstallation? [y/N]: " confirm
if [[ ! $confirm =~ ^[Yy]$ ]]; then
log_info "Uninstallation cancelled"
exit 0
fi
echo ""
log_info "Starting uninstallation..."
# Stop running services
log_info "Stopping running services..."
pkill -x simd 2>/dev/null || true
pkill -x monocoque 2>/dev/null || true
systemctl --user stop simd.service 2>/dev/null || true
systemctl --user disable simd.service 2>/dev/null || true
# Remove installation directory
if [ -d "$INSTALL_DIR" ]; then
log_info "Removing installation directory..."
rm -rf "$INSTALL_DIR"
log_success "Installation directory removed"
else
log_info "Installation directory not found, skipping"
fi
# Remove configuration (ask first)
if [ -d "$CONFIG_DIR/monocoque" ] || [ -d "$CONFIG_DIR/simd" ]; then
read -p "Remove configuration files? [y/N]: " remove_config
if [[ $remove_config =~ ^[Yy]$ ]]; then
rm -rf "$CONFIG_DIR/monocoque" 2>/dev/null || true
rm -rf "$CONFIG_DIR/simd" 2>/dev/null || true
log_success "Configuration files removed"
else
log_info "Keeping configuration files"
fi
fi
# Remove launcher scripts
log_info "Removing launcher scripts..."
rm -f "$BIN_DIR/start-simd" 2>/dev/null || true
rm -f "$BIN_DIR/start-monocoque" 2>/dev/null || true
rm -f "$BIN_DIR/test-monocoque" 2>/dev/null || true
rm -f "$BIN_DIR/monocoque-manager" 2>/dev/null || true
# Remove systemd services
if [ -f "$SYSTEMD_DIR/simd.service" ]; then
log_info "Removing systemd service files..."
rm -f "$SYSTEMD_DIR/simd.service"
systemctl --user daemon-reload 2>/dev/null || true
fi
# Remove logs
if [ -d "$CACHE_DIR/monocoque" ]; then
read -p "Remove log files? [y/N]: " remove_logs
if [[ $remove_logs =~ ^[Yy]$ ]]; then
rm -rf "$CACHE_DIR/monocoque"
log_success "Log files removed"
else
log_info "Keeping log files"
fi
fi
echo ""
log_success "Uninstallation complete!"
echo ""
log_info "To remove system dependencies and simapi library:"
echo " • Remove packages: yder, libuv, argtable, libserialport, etc."
echo " • Remove library: sudo rm /usr/local/lib/libsimapi.so*"
echo " • Run: sudo ldconfig"
echo ""
+6
View File
@@ -3,3 +3,9 @@ SUBSYSTEMS=="usb",ATTRS{product}=="Rev Burner",GROUP="input",TAG+="uaccess"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0043", ATTRS{serial}=="75033303335351A080F0", SYMLINK+="simdev0", GROUP="input", TAG+="uaccess"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0043", ATTRS{serial}=="242303138313514131F1", SYMLINK+="simdev1", GROUP="input", TAG+="uaccess"
# Simagic P1000
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="0525", MODE="0666"
# Simagic GT NEO
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="3670", ATTRS{idProduct}=="0805", MODE="0666"