Compare commits

...

420 Commits

Author SHA1 Message Date
Paul Dino Jones da7a37855a add some directives that seem necessary for debian 2026-05-25 23:58:33 -04:00
Paul Dino Jones a864f240d1 fix lua linking 2026-05-25 23:36:57 -04:00
Paul Dino Jones 68cb9aacbc rename lua in headers 2026-05-25 23:13:53 -04:00
Paul Dino Jones b5faec69f5 add more lua variables back to cmake 2026-05-25 23:11:04 -04:00
Paul Dino Jones 1ceb8a750b 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 bcc2d410a7 refer explicitly to lua 5.4 in headers 2026-05-25 20:46:47 -04:00
Paul Dino Jones c38cf44f05 add more dependencies to install.sh 2026-05-25 19:54:08 -04:00
Paul Dino Jones f96a94ecb7 force lua 5.4 in cmake 2026-05-25 19:53:47 -04:00
Paul Dino Jones 897ce57e1c Improve lua dependency detection for linking 2026-05-25 19:17:47 -04:00
Brandan Cotton 738d532dac
make additional data available to lua scripts (#45) 2026-05-25 08:42:19 -04:00
Paul Dino Jones e9e1e8591a updated to latest simapi for process detection 2026-05-23 12:40:42 -04:00
Paul Dino Jones a806e394d0 build simapi in install script 2026-05-22 17:45:36 -04:00
Paul Dino Jones abe8830989 remove deprectated getpid.h file 2026-04-26 19:38:15 -04:00
Paul Dino Jones 99d2d6140a Update to latest simapi 2026-04-26 19:16:29 -04:00
Paul Dino Jones 8862bbd5dd Fix cmake compile definitions 2026-04-23 17:35:50 -04:00
Paul Dino Jones 7996b25621 fix cmake boolean 2026-04-23 17:20:57 -04:00
Paul Dino Jones 1bcbd08c6c fix proc2 detection 2026-04-23 16:02:02 -04:00
Paul Dino Jones 93ccca069f fix lua version issue 2026-04-23 15:25:21 -04:00
Paul Dino Jones 2121f7e4fe Add procps dependency. Update to latest simapi 2026-04-22 16:35:43 -04:00
Tony Miceli 18f043a62e
Fix multiple serial device init issues (#43) 2026-04-20 10:35:36 -04:00
Francesco fddbf726d7
Distrobox install (#41) 2026-04-20 10:26:16 -04:00
Simon Zengerling e3487b3652
Feat add moza ks pro wheel (#42) 2026-04-11 16:07:17 -04:00
Paul Dino Jones 0ee114dc23 Add config entry for Moza R3 2026-04-04 10:02:16 -04:00
Tony Miceli 581163c00b
add configurable fanpower for simwind and new PWM sketch (#37) 2026-04-03 18:03:08 -04:00
Paul Dino Jones 10c172d3e1 disable installing systemd service via cmake 2026-03-20 19:47:31 -04:00
Paul Dino Jones 1c79a70722 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 361d73eba6 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 a7327625df Refactor how sim calculate tyre diameter data if necessary 2026-03-18 11:10:35 -04:00
Paul Dino Jones 925cb0a2f1 Use sim provided slip ratio if available for slip based effects 2026-03-17 10:32:27 -04:00
Paul Dino Jones f21bced7c6 Fix regression braking udp support 2026-03-17 10:31:32 -04:00
Paul Dino Jones 99bb88a035 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 0a35099bd1 bring in fixes to crashes caused by new simapi 2026-03-11 23:57:01 -04:00
Paul Dino Jones 8b301402cc Update to latest simapi 2026-03-11 22:40:01 -04:00
Paul Dino Jones 699354cc7e Remove more debug code 2026-03-04 01:09:45 -05:00
Paul Dino Jones 7f776f279b fix compilation error 2026-03-03 23:42:18 -05:00
Paul Dino Jones f96277ef71 clean up debug code in pulseaudio mainloop start 2026-03-03 23:37:45 -05:00
Paul Dino Jones 4caabd24a7 Allow to automatically spawn pulseaudio daemon 2026-03-03 23:08:00 -05:00
Paul Dino Jones 8aadf0c1b0 Update to latest simapi for RaceRoom support 2026-02-28 16:04:31 -05:00
Paul Dino Jones a8919b9e55 Add link to guide for reverse engineering 2026-02-14 13:45:02 -05:00
dafteran4 75b42e876b
Noise for haptics (#31) 2026-02-14 12:32:14 -05:00
dafteran4 8527ce9fa7
Low latency (#32) 2026-02-13 11:13:33 -05:00
dafteran4 a83ac2c670
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 696547dadf Update to latest simapi for Forza support 2026-02-11 10:52:13 -05:00
Paul Dino Jones 586c039bfd use separate debian control for each build 2026-02-08 11:42:37 -05:00
Paul Dino Jones d0a14c5f59 Added new files to build from new version of simapi 2026-02-07 10:57:21 -05:00
Paul Dino Jones 65049d5ce7 updated to lastest simapi for Wreckfest 2 Support 2026-02-07 10:47:57 -05:00
Paul Dino Jones 273e91ac71 restructure some libuv stuff 2026-02-06 17:54:17 -05:00
Paul Dino Jones 835b04fc13 Improve handling of shutdown when in udp mode 2026-01-27 01:43:01 -05:00
Paul Dino Jones d047543c19 update to latest simapi 2026-01-25 19:09:51 -05:00
Paul Dino Jones dbda035a07 Improve verbiage around invalid config file 2026-01-20 18:11:55 -05:00
Paul Dino Jones 44ff462fdc fix small issues with command line parameters 2026-01-20 18:11:55 -05:00
Paul Dino Jones b5b0355fc5 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 9815420c96 move finished rpm to github workspace 2026-01-12 15:31:52 -05:00
Paul Dino Jones 19a585b42c hardcode directory of generated rpm file 2026-01-12 15:22:22 -05:00
Paul Dino Jones e359c01e8d run on creation of any new tag 2026-01-12 15:14:50 -05:00
Paul Dino Jones 2d04b57b06 fix typo on file type 2026-01-12 15:11:58 -05:00
Paul Dino Jones 25e7dd1045 create rpm build dirs first 2026-01-12 15:08:22 -05:00
Paul Dino Jones 5520d5ba3e fix checkout command 2026-01-12 15:06:22 -05:00
Paul Dino Jones 532baed356 Add rpm workflow 2026-01-12 15:04:37 -05:00
Paul Dino Jones a6df3a7fef add libxml2 dependency 2026-01-12 12:00:37 -05:00
Paul Dino Jones 254804516d adding first attempt at rpm spec file 2026-01-12 11:47:43 -05:00
Paul Dino Jones 13f1a558dd fix spelling error 2026-01-10 15:34:37 -05:00
Paul Dino Jones 76904d383a matrix based package name 2026-01-10 15:33:54 -05:00
Paul Dino Jones 1e0d9f1461 added system info action 2026-01-10 15:15:19 -05:00
Paul Dino Jones 1d9cde26c6 update package release names 2026-01-10 15:09:05 -05:00
Paul Dino Jones aab0396f61 update to build latest ubuntu and debian packages 2026-01-10 15:02:33 -05:00
Paul Dino Jones 66f9f56a3c update to build ubuntu and debian packages 2026-01-10 15:00:24 -05:00
Paul Dino Jones a9cec10923 update to build ubuntu and debian packages 2026-01-10 14:59:33 -05:00
Paul Dino Jones ebc4671cb9 update to build ubuntu and debian packages 2026-01-10 14:54:58 -05:00
Paul Dino Jones a51a0ae475 update to build ubuntu and debian packages 2026-01-10 14:51:10 -05:00
Paul Dino Jones ddf81371cd update to use self hosted runner debian-latest 2026-01-10 12:47:46 -05:00
Paul Dino Jones 0b9ba29e39 update to use self hosted runner 2026-01-10 12:05:03 -05:00
Paul Dino Jones ada9c9e24f fix typo 2026-01-09 17:33:56 -05:00
Paul Dino Jones 9dc6337211 use -p to create dirs 2026-01-09 17:23:39 -05:00
Paul Dino Jones c4ea4737f7 try to apt update 2026-01-09 17:20:20 -05:00
Paul Dino Jones 659c7cfd65 try ubuntu 24 2026-01-09 17:16:41 -05:00
Paul Dino Jones c82c391d79 try ubuntu latest 2026-01-09 17:14:59 -05:00
Paul Dino Jones f45c5e9b0a Fix bad label 2026-01-09 17:12:14 -05:00
Paul Dino Jones b03bfdf2e6 Add libpulse dependency 2026-01-09 17:03:18 -05:00
Paul Dino Jones 7b97ba16df Add step to checkout submodultes 2026-01-09 17:01:03 -05:00
Paul Dino Jones b02c5b1e47 Add step to install dependencies 2026-01-09 16:57:42 -05:00
Paul Dino Jones 467b85ae4a fix indentation problem 2026-01-09 16:52:13 -05:00
Paul Dino Jones 8ebc851e82 fix indentation problem 2026-01-09 16:51:04 -05:00
Paul Dino Jones f0860a7383 fix indentation problem 2026-01-09 16:49:35 -05:00
Paul Dino Jones 5c2f1d7b3f update README 2026-01-09 16:41:58 -05:00
Paul Dino Jones f462f8554a monocoque debian package 2026-01-09 16:40:12 -05:00
Paul Dino Jones 34ccffc8ab Adding debian package control file 2026-01-09 12:47:32 -05:00
Paul Dino Jones 34f04adcae updated readme 2026-01-07 01:01:08 -05:00
Paul Dino Jones 6c7c3231a7 Adding support for Logitech G29 Wheel 2026-01-06 21:14:10 -05:00
Paul Dino Jones 62bda5a58c updated readme to point to new supported sims chart 2026-01-05 13:05:11 -05:00
Paul Dino Jones 9194b7173b update to latest simapi for Dirt2 support 2025-12-30 11:40:40 -05:00
Paul Dino Jones 681d456c51 allow for non set cars to atleast use provided tyre diameters 2025-12-24 16:23:03 -05:00
M4X1K02 d984e5ee86
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 a962d658a3 move duplicate loop release code to its own function 2025-12-22 22:44:47 -05:00
Paul Dino Jones f58d8cbc42 update to latest simapi 2025-12-22 22:44:19 -05:00
Paul Dino Jones efbafb07c2 Update to latest simapi for car from outgauge 2025-12-18 00:23:45 -05:00
Paul Dino Jones f062a53e7f update to latest simapi for ACEvo and ACRally support 2025-12-14 17:46:27 -05:00
Paul Dino Jones 3797357844 update to latest simapi for Assetto Corsa Rally support 2025-12-13 13:04:38 -05:00
Paul Dino Jones 88f0f4005f fix flipped rpm and maxrpm in moza device update signature 2025-12-13 11:51:44 -05:00
Paul Dino Jones 0772821f62 update to latest simapi to fix outgauge mapping 2025-12-12 15:39:57 -05:00
Paul Dino Jones 6c6ac81a2c update to latest simapi for BeamNG workaround 2025-12-12 15:33:07 -05:00
Paul Dino Jones 82a246a4b1 thread showstats so it does not go crazy 2025-12-12 15:26:50 -05:00
Paul Dino Jones 03f807ac34 update to latest simapi to fix small issue using simd with udp sims 2025-12-12 12:22:59 -05:00
Paul Dino Jones 503781673b pull in simapi update to fix mapping issue with outgauge 2025-12-12 10:22:57 -05:00
Paul Dino Jones 7cac9e9fa0 add support for BeamNG 2025-12-11 20:21:36 -05:00
Paul Dino Jones 25bfe2325c Add basic support for Live For Speed through OutGauge and Outsim protocols 2025-12-11 17:49:39 -05:00
Paul Dino Jones fea3bee324 pull latest simapi to fix too many files bug 2025-11-27 19:12:32 -05:00
Paul Dino Jones 5c30844709 update to latest simapi to fix gear int value 2025-11-23 13:50:35 -05:00
Paul Dino Jones 0700ab9a31 add lua dependency to README 2025-11-22 11:39:41 -05:00
Paul Dino Jones 817e0f2001 updated to latest simapi 2025-11-22 09:28:20 -05:00
Paul Dino Jones a44fd51bda update to latest simapi 2025-11-18 12:25:28 -05:00
Paul Dino Jones 34ad2f63db fix type for fourth gear 2025-11-10 13:02:15 -05:00
Paul Dino Jones f716e30633 use new enums for flag and gear 2025-11-10 11:27:10 -05:00
Paul Dino Jones 10d572f227 fix issue with diameters.config not using gameid. Update to latest simapi 2025-10-28 12:18:59 -04:00
Olly Smith 98bc0b57c9
Initial support for latest Moza firmware LED controls (#25) 2025-10-28 01:39:33 -04:00
Paul Dino Jones 1c1323961c init and destroy logging with thread safety 2025-10-28 00:26:04 -04:00
Paul Dino Jones a98bcbcedb add compilation targets for new mapping structure 2025-10-23 14:01:18 -04:00
Paul Dino Jones ead22efbc8 Updated to latest simapi 2025-10-23 13:52:24 -04:00
Paul Dino Jones f7143e2024 Add gearc to lua and to test sequence 2025-10-22 14:36:59 -04:00
Paul Dino Jones 0780b001f6 working custom serial 2025-10-20 19:37:28 -04:00
Paul Dino Jones 7f3727d2f1 consolidate cleanup gotos 2025-09-01 19:30:43 -04:00
Paul Dino Jones a3059b30ba clean up argument parsing a little 2025-09-01 17:24:26 -04:00
Paul Dino Jones 0e5498f220 fix freeing of xdg handle when help is specified 2025-09-01 11:52:52 -04:00
Paul Dino Jones e380f3a9d4 Add beginnings of custom arduino devices. 2025-09-01 11:38:36 -04:00
Paul Dino Jones 433e2423e8 update to lastest simapi 2025-09-01 10:59:03 -04:00
Sebastian Noe a730096e86
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 e4a8a998e8 fix formatting of monocoque.config 2025-08-31 19:20:58 -04:00
Paul Dino Jones 7e923354ba add newer and better examples to monocoque.config 2025-08-31 19:14:33 -04:00
Paul Dino Jones 7f7a24269a Update to latest simapi 2025-08-29 19:15:15 -04:00
Paul Dino Jones a6b039c292 Update to latest simapi to fix tyre diameters 2025-08-29 19:01:46 -04:00
Paul Dino Jones d7dbab7956 updated to latest simapi for tyre diameter and turbo boost 2025-08-28 17:00:08 -04:00
Paul Dino Jones 88f3e0ebb9 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 811ce77da8 updated to latest simapi 2025-08-25 20:28:16 -04:00
Paul Dino Jones 442a1b88a2 Update to latest simapi 2025-08-25 17:46:48 -04:00
Paul Dino Jones 4684ee49c3 Add disable_audio flag 2025-08-15 04:19:39 -04:00
Paul Dino Jones 10ecfa858a Fix gear and engine effects remove some unnecessary code 2025-07-30 03:32:41 -04:00
Morgan Garcia 23f45d8f56 Update moza.c
Change MOZA_MAGIC_VALUE back to 0x0d from 13.
2025-07-29 20:43:49 -04:00
Morgan Garcia 9f51f03efa Update moza.c
Changed MOZA_TIMEOUT back to 1000.
2025-07-29 20:43:49 -04:00
Morgan Garcia 223cf26e28 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 6bf80673ce 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 d28719929c Update gameloop.c
Text output update
2025-07-29 20:43:49 -04:00
Morgan Garcia 19f7a12027 Update moza.c
Fixed error in BIT number increasing.
2025-07-29 20:43:49 -04:00
Morgan Garcia 218b8c255c 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 0201b29c49 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 1d9b6cfe1a 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 0abd5e9b68 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 d2fa72f47c fix serial receiving 2025-07-14 00:15:47 -04:00
Paul Dino Jones f6a2607273 fix misleading log message 2025-06-16 13:19:13 -04:00
Paul Dino Jones c7c86adf94 accept usbwheel for wheel on usb devices in config 2025-06-16 13:02:34 -04:00
Oleg Makarenko 145b4aed78 Add Simagic GT Neo to the udev rules, opening hidraw 2025-06-15 22:59:41 -04:00
Oleg Makarenko de51a27708 Add lua config for Simagic GT Neo 2025-06-15 22:59:41 -04:00
Oleg Makarenko b90c396d34 Add GT Neo LED update feature 2025-06-15 22:59:41 -04:00
Paul Dino Jones 6c902b4c2b update to latest simapi for lmu support 2025-06-14 19:11:44 -04:00
Paul Dino Jones d08ebfa6db Update to latest simapi for improvements to tyre diameter detection 2025-04-30 19:07:05 -04:00
Paul Dino Jones c7cbce7c7b update to latest simapi to fix detecting diameters while simd is running 2025-04-29 00:01:39 -04:00
Paul Dino Jones b5dd003f88 fix code around generating diameters config 2025-04-29 00:00:58 -04:00
Paul Dino Jones 3d4f1914c6 Re-enable shared memory test prog 2025-04-28 20:38:34 -04:00
Mon ouïe c6f6a63ed7 Add udev rule for Simagic P1000 support 2025-04-21 14:09:46 -04:00
Mon ouïe 108d21d30a Fix HID send report commands for Simagic P1000 Haptic modules 2025-04-21 14:09:46 -04:00
Paul Dino Jones 04f01366b1 add function to expand tilde and expand tilde on device specific config files 2025-04-07 10:56:38 -04:00
Paul Dino Jones e627392dda small cleanup to fix memory leaks 2025-04-07 10:56:38 -04:00
Paul Dino Jones 1e96c04de4 add -c for specified config file option 2025-04-07 10:56:38 -04:00
Paul Dino Jones 7ead5ac9f5 fix issue with freeing lua file on cammus c12 2025-03-27 17:12:23 -04:00
Paul Dino Jones a8e6a219bf Add extra log information on revburner 2025-03-27 15:52:51 -04:00
Paul Dino Jones 0d388d96b1 improvements for cammus c12 2025-03-27 15:38:35 -04:00
Paul Dino Jones e80b12eb47 fix arduino lua device free 2025-03-27 14:47:34 -04:00
Paul Dino Jones df39832f4f 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 4e6bf4684a Move release of serial adapter to before underlying device is released 2025-03-26 18:25:26 -04:00
Paul Dino Jones 14fb81ee43 Fix some pulseaudio stuff 2025-03-26 17:35:05 -04:00
Paul Dino Jones 2aed9f5b51 Improve logging over device specific configs 2025-03-26 16:41:18 -04:00
Paul Dino Jones ebf0965381 remove debug build hardcode 2025-03-26 15:20:43 -04:00
Paul Dino Jones c850efbde5 actually pass the config file around 2025-03-26 15:16:04 -04:00
Paul Dino Jones f782823c12 Move device specific config file test to more logical spot 2025-03-26 15:01:06 -04:00
Paul Dino Jones 3f02f09238 Fix tachometer config 2025-03-26 13:03:50 -04:00
Paul Dino Jones e2b5e4b5d1 Fix leak in arduino lua 2025-03-26 12:35:16 -04:00
Paul Dino Jones d1f43aace5 Remove log entries 2025-03-26 11:58:39 -04:00
Paul Dino Jones b380d44521 Cleanup pulseaudio code 2025-03-26 11:58:05 -04:00
Paul Dino Jones 02699d3c47 Fix leak in xdglib 2025-03-26 11:57:40 -04:00
Paul Dino Jones 66886b4d7e fix leak in config 2025-03-26 11:56:31 -04:00
Paul Dino Jones 0d9a171193 fix leak in cammus c12 2025-03-26 11:56:04 -04:00
Paul Dino Jones 4382e2f71f fix leak in wheel device 2025-03-26 11:55:43 -04:00
Paul Dino Jones 7bf2f8b28f Fix leak in serial adapter 2025-03-26 11:55:18 -04:00
Paul Dino Jones 5ed23d1cba Fix segfault freeing cammus c12 2025-03-25 14:57:52 -04:00
Paul Dino Jones e75c57636a Rename to car radar.lua 2025-03-25 14:03:58 -04:00
Paul Dino Jones f87fe218d0 fix colors 2025-03-25 13:54:16 -04:00
Paul Dino Jones f0d501370e Fix off by one on rgb math 2025-03-25 13:52:22 -04:00
Paul Dino Jones 18f41e4cd0 Add example for cars alongside 2025-03-25 13:25:11 -04:00
Paul Dino Jones 1a22c10b06 Add flags to test loop 2025-03-25 12:57:55 -04:00
Paul Dino Jones ccae4c5f89 Do not write to rpm leds on cammus c12 2025-03-25 12:31:00 -04:00
Paul Dino Jones 46cb401e6b Always write to led 2025-03-25 10:09:39 -04:00
Paul Dino Jones 7afd7f12ed Huge refactor to improve memory efficiency mostly around pulseaudio 2025-03-25 10:04:39 -04:00
Paul Dino Jones e0d9c78f60 Remove some unnecessary variables from lua variable setup 2025-03-25 10:04:14 -04:00
Paul Dino Jones a26ee4da2f Properly release lua if it is used on cammus 2025-03-25 10:03:38 -04:00
Paul Dino Jones 4ed25d7112 Fix logging spam 2025-03-24 16:43:09 -04:00
Paul Dino Jones 88db6c5f04 Fix segfaults on device specific config files 2025-03-24 15:58:37 -04:00
Paul Dino Jones c3a88f3b07 Fix issue with serial devices opening when they have already failed once 2025-03-24 15:57:50 -04:00
Paul Dino Jones 22a41d1ddb Fix issue with not found configs 2025-03-24 14:22:20 -04:00
Paul Dino Jones ed671ecedc Fix off by one error. Fix log format 2025-03-24 14:07:16 -04:00
Paul Dino Jones 7c8ebb0984 Increase cammus c12 leds 2025-03-24 13:22:45 -04:00
Paul Dino Jones f7e92083f2 Make sure leds clear each time 2025-03-24 12:13:39 -04:00
Paul Dino Jones 22930163ec Fix to use lua on c12 if lua script loads properly 2025-03-24 12:05:49 -04:00
Paul Dino Jones b776e05f66 Fix issue with arduino leds not clearing 2025-03-24 11:44:44 -04:00
Paul Dino Jones 336d240be4 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 8c764358b7 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 1e6bde9da1 Fix some issues with sound device code 2025-03-22 19:24:15 -04:00
Paul Dino Jones 5dc00eb4ce Add ability to use lua on cammus c12 2025-03-22 19:16:54 -04:00
Paul Dino Jones 367c6fed34 Add Suspension travel effect 2025-03-21 19:45:34 -04:00
Paul Dino Jones 8e577c0f87 Fix issues with config segfaults 2025-03-21 19:44:55 -04:00
Paul Dino Jones 3063d0a668 Add ability to use car proximity for lights 2025-03-21 13:55:56 -04:00
Paul Dino Jones a597f29c1f Add more logging to config loading 2025-03-21 13:55:21 -04:00
Paul Dino Jones bec6433969 Add AMPLIFY modulation 2025-03-15 23:42:15 -04:00
Paul Dino Jones 161f311494 Add lua include dir to cmake 2025-03-11 12:23:02 -04:00
Paul Dino Jones 11bd2f71ef Fix lua cmake dependency. Use specified frequency as base frequency for engine effects 2025-03-11 12:01:24 -04:00
Paul Dino Jones 84f336f40c Updating configuration to reflect new simled code 2025-02-25 22:13:49 -05:00
Paul Dino Jones 9c0cadffdf Add support for simhub protocol and custom lua scripts to control leds 2025-02-25 18:41:10 -05:00
Paul Dino Jones d0f4a48c35 Updated to latest simapi for getpid improvements 2025-02-19 15:46:44 -05:00
Paul Dino Jones fdb318bfae Update sketches to use 115200 baud 2025-02-12 15:04:38 -05:00
Paul Dino Jones e7751b4e16 fix improper detecting of usb haptic device 2025-02-01 17:01:50 -05:00
Paul Dino Jones 7a5ca94209 Refactor serial devices so a single open handle is shared throughout 2025-01-25 09:52:38 -05:00
Paul Dino Jones 8bf98fae1e moved some confusing code 2025-01-25 09:51:20 -05:00
Paul Dino Jones 0e47f53f88 improve process of closing sound devices 2025-01-25 09:50:11 -05:00
Paul Dino Jones 414b9708f4 pulling in latest simapi for improvements to pcars2 2025-01-21 17:23:28 -05:00
Paul Dino Jones fc95f044bf Adding max frequency and modulation to sound devices 2025-01-21 17:23:10 -05:00
Paul Dino Jones 10c5d2c597 Fix bug causing no tyre effects 2025-01-20 12:13:49 -05:00
Paul Dino Jones f2ba345cab Update to latest simapi, allow sim strings in configs 2025-01-20 10:53:57 -05:00
Paul Dino Jones d97118e19b updated to latest simapi 2025-01-19 15:16:30 -05:00
Paul Dino Jones d927726f77 Updated to latest simapi for AC EVO support 2025-01-19 09:57:57 -05:00
Paul Dino Jones 5969b8f34b fixed sound readme again 2025-01-19 09:57:23 -05:00
Paul Dino Jones 3b0d75423f Removed calls to abort 2025-01-18 19:01:52 -05:00
Paul Dino Jones 0c5a953787 moved to latest simapi 2025-01-18 13:36:03 -05:00
Paul Dino Jones 6d24c5e7f2 Add numlights setting 2025-01-18 13:20:56 -05:00
Paul Dino Jones d3618c758d fix revburner typo 2025-01-18 12:26:23 -05:00
Paul Dino Jones 052ee12178 Initially adding support for SIMAGIC P1000 Pedals 2025-01-18 03:30:11 -05:00
Paul Dino Jones 6d0b027171 lock pulseaudio mainloop while freeing sound devices 2025-01-17 19:41:12 -05:00
Paul Dino Jones 6f14b09f1a use more standard channel map 2025-01-17 18:22:51 -05:00
Paul Dino Jones bb0d2bfe4c more details in README 2025-01-17 10:41:18 -05:00
Paul Dino Jones 83e92391f4 added global refresh rate -f 2025-01-17 10:29:27 -05:00
Paul Dino Jones 44274084d3 added a few commands to help with sound setup 2025-01-17 10:28:25 -05:00
Paul Dino Jones 0a498f0c64 move logging around to make output a little cleaner 2025-01-16 22:22:49 -05:00
Paul Dino Jones 13d7a03acb remove old commented code 2025-01-16 22:10:44 -05:00
Paul Dino Jones 4df9787c21 I knew there was a reason it was commented out. It makes valgrind happy but causes the program to segfault on exit. 2025-01-16 17:00:15 -05:00
Paul Dino Jones 7f371ebf15 fix more malloc complaints 2025-01-16 16:57:34 -05:00
Paul Dino Jones 6145efd2a9 fix bad memory management around device threading model 2025-01-16 16:45:37 -05:00
Paul Dino Jones f90b173e8c updated readme for getting device name 2025-01-16 14:28:29 -05:00
Paul Dino Jones 0ad78b4ef1 sound devices stay on main thread 2025-01-16 01:08:00 -05:00
Paul Dino Jones 44dcc703e3 fix some effects in test loop 2025-01-15 23:43:25 -05:00
Paul Dino Jones 327cd0eaad add sim entry to diameters file 2025-01-15 23:42:57 -05:00
Paul Dino Jones a30247eb6f add logging for haptic effects, fix consistency of test sequence 2025-01-15 18:16:07 -05:00
Paul Dino Jones eabfef6efe fix bug in tyre effects and its test 2025-01-15 14:11:42 -05:00
Paul Dino Jones 5d67611b98 fixed return of checkbyte for moza 2025-01-15 12:24:08 -05:00
Paul Dino Jones 8c65f8b26d Updated PKGBUILD files to use libpulse 2025-01-15 12:23:34 -05:00
Paul Dino Jones 8dfc4c0435 fix floating point issue on moza 2025-01-14 20:23:43 -05:00
Paul Dino Jones f2a5872520 temporarily add another step with rpms at 7000 to assist in debugging some wheels 2025-01-14 20:23:11 -05:00
Paul Dino Jones 5b003b606d updated to latest simapi 2025-01-14 18:25:42 -05:00
Paul Dino Jones f64fde023a fix issues with serial wheel 2025-01-14 18:09:35 -05:00
Paul Dino Jones 2f32501bf8 Improve config handling 2025-01-14 17:40:07 -05:00
Paul Dino Jones 30cb1def72 removed unused jansson header 2025-01-13 19:17:42 -05:00
Paul Dino Jones def2bfbb89 Add support for per device fps settings. 2025-01-13 15:16:57 -05:00
Paul Dino Jones 317adf2187 updated monocoque PKGBUILDS 2025-01-11 03:19:19 -05:00
Paul Dino Jones 17b4bd142a moved PKGBUILD to stable folder 2025-01-11 03:18:09 -05:00
Paul Dino Jones 735e09a872 add example pacman command for dependencies 2025-01-11 03:06:27 -05:00
Paul Dino Jones 79b3aba157 Use STARTLED default of 1 2025-01-02 16:21:15 -05:00
Paul Dino Jones 0f29394aa7 Update simlight test 2025-01-02 15:57:13 -05:00
Paul Dino Jones b90dfdfc31 Add start led 2025-01-02 15:54:46 -05:00
Paul Dino Jones 326c1b7bf8 updated TODO 2024-12-04 14:44:09 -05:00
Paul Dino Jones 71cd512fd1 updated TODO 2024-12-04 14:42:37 -05:00
Paul Dino Jones c51b88ca4a disable all tests for now 2024-12-04 12:34:21 -05:00
Paul Dino Jones a2ff24afd7 add code to smooth out frequencies between audio frames 2024-12-04 11:20:21 -05:00
Paul Dino Jones c8e40e9b80 persist phase to stop disjointed sine waves 2024-12-03 20:47:57 -05:00
Paul Dino Jones c1375ae0c9 improve stability of config file loading when multiple configs are in use 2024-11-22 16:21:30 -05:00
Paul Dino Jones 7caca1d362 fix issues with simapi mapping correctly when using simd 2024-11-22 16:21:08 -05:00
Paul Dino Jones b8ac911d44 Use more generic sysfs path to find csl elite v3 pedals 2024-11-19 11:57:58 -05:00
Paul Dino Jones 2b6812b3a9 Use latest simapi to improve tyre slip effects 2024-11-19 11:55:55 -05:00
Paul Dino Jones ecf3a59d6b stable pkgbuild 2024-11-18 16:53:27 -05:00
Paul Dino Jones 12509e9709 Added ampfactor to serial haptic log message 2024-10-10 15:00:39 -04:00
Paul Dino Jones 27fcea8f0d Add xdg to dependencies 2024-10-06 00:52:17 -04:00
Paul Dino Jones d507749f39 upgraed to latest simapi 2024-10-06 00:49:08 -04:00
Paul Dino Jones 4b9679dec7 various readme updates 2024-10-06 00:12:50 -04:00
Paul Dino Jones ec8bbbbd0d upgraded to new simapi 2024-10-05 17:10:00 -04:00
Paul Dino Jones f4a4c635e1 Updated to latest simapi 2024-10-05 08:32:01 -04:00
Paul Dino Jones 144a670dbb Updated to latest simapi 2024-10-05 08:28:26 -04:00
Paul Dino Jones 2a897d930a UDP support and upgrade to latest simapi for udp and logging support 2024-10-01 12:46:41 -04:00
Paul Dino Jones 558b298a78 Fix issue not setting correct baud rate through to libserialport 2024-09-30 22:21:35 -04:00
Paul Dino Jones 32a15eff50 set simhaptic baud to 9600 2024-09-30 20:34:29 -04:00
Paul Dino Jones d054f032fd forgot to add submodules 2024-09-19 21:44:22 -04:00
Paul Dino Jones ec6cb073ee Fixed overlook device settings in config processing 2024-09-19 21:22:00 -04:00
Paul Dino Jones c4a1cbcfb8 Updated pkgbuild to actually install 2024-09-19 21:21:30 -04:00
Paul Dino Jones 128d02e71a Fix a couple things i missed in the merge 2024-09-19 18:59:46 -04:00
Paul Dino Jones c0755aef54 Reorganization and config updates for Moza wheel 2024-09-19 18:54:16 -04:00
Paul Dino Jones 525dd68b4b Add ability to specify baud rate in config file 2024-09-19 18:54:12 -04:00
Lawstorant f76fe53a3b Initial work 2024-09-19 18:53:04 -04:00
Paul Jones 545168c3ab
Serial8 (#11)
* Using 8 bits per int for serial devices

* Refactor simwind code slightly to improve performance
2024-09-19 17:05:42 -04:00
Paul Dino Jones 2446fc9af2 Moved baud rate to serial device struct and fixed log message 2024-09-17 10:17:40 -04:00
Paul Dino Jones e1b71850bb Fix velocity bytes on cammus c12 hid message 2024-09-16 16:48:17 -04:00
Paul Dino Jones a81004418d Support for cammus c12 2024-09-16 16:14:18 -04:00
Paul Dino Jones 910e225a9a Add some robustness to diameters file and slip effect code 2024-09-16 14:08:58 -04:00
Paul Dino Jones 695581b66d Fix issue with test not having a car set 2024-09-15 12:24:23 -04:00
Paul Dino Jones 88e762b718 Added more checks to make sure sims do not try to set tyre diameters without a car set 2024-09-08 18:49:01 -04:00
Paul Dino Jones 55dfa4b086 Do not compare car if it is null 2024-09-08 18:44:21 -04:00
Paul Dino Jones d1629dfcbd Added more robustness to config lookup 2024-09-06 14:50:00 -04:00
Paul Dino Jones 9e09a2792a Added more logging to config load 2024-09-06 14:30:12 -04:00
Paul Dino Jones 9f45697564 Put default configs closer to the bottom 2024-09-04 16:22:36 -04:00
Paul Dino Jones 1ffbd84721 Fix some issues with config loading 2024-09-04 16:15:34 -04:00
Paul Dino Jones f474027f0a Add ability to specify baud rate in config file 2024-09-04 12:56:02 -04:00
Paul Dino Jones 2fb0383091 Update loop to 60fps. Update some arduino devices to 115200. Refactor shiftlights code for performance. 2024-09-04 12:16:21 -04:00
Paul Dino Jones f31947dc69 Updated config example to support profiles 2024-09-03 16:59:48 -04:00
Paul Dino Jones 8b63346071 Update CMake so project build 2024-09-02 13:24:34 -04:00
Paul Dino Jones 5cd82e35eb Updated to latest simapi 2024-09-02 13:20:28 -04:00
spikerguy b5ea1bb061
Add supported games list (#9)
* Add supported games list

* readme: get car tyre diameter

* typo
2024-09-02 12:53:22 -04:00
Paul Dino Jones c0f0abaa6b Added link to Linux Sim Racing Matrix space 2024-09-02 12:50:06 -04:00
Paul Dino Jones e0c3d822a5 Added arch linux PKGBUILD 2024-09-02 12:47:17 -04:00
Paul Dino Jones 07e1a840be Update to latest simapi 2024-09-02 12:40:40 -04:00
Paul Dino Jones b5eee247b3 Updated to latest simapi 2024-09-02 12:09:18 -04:00
Paul Dino Jones 08209a96bd Initialize some data so tyre diameter detection works, and motors spin down. 2024-09-02 11:15:19 -04:00
Paul Dino Jones fd1264611c Set gameloop to 8fps for now to avoid issues 2024-09-02 11:13:19 -04:00
Paul Dino Jones df0ad1f5c9 Improving loop management to prevent double freeing. Fixing other memory issues 2024-09-02 10:43:36 -04:00
Paul Dino Jones 07d797f24c making log statement more accurate 2024-08-31 16:14:33 -04:00
Paul Dino Jones 572a21b3d6 Move some things around and add another indicator to prevent double frees 2024-08-31 16:02:34 -04:00
Paul Dino Jones d1f252d4e0 Fix log statement so it actual outputs the amount of slip 2024-08-31 16:02:04 -04:00
Paul Dino Jones 40726c06c8 Major overhault to add libuv event loop. Enhancements to config file to support profiles. 2024-08-30 13:46:56 -04:00
Paul Dino Jones 607a7ad7c3 update to latest simapi 2024-08-12 23:50:18 -04:00
Paul Dino Jones 3032433f7d Update to latest simapi to fix gear in truck sims 2024-08-11 23:48:00 -04:00
Paul Dino Jones 9e21c3bc43 Add SCS Truck Sim support 2024-08-11 23:28:29 -04:00
Paul Dino Jones 8e319629eb Move from libusb to hidraw backend hid api 2024-08-11 20:12:27 -04:00
Paul Dino Jones 2e538b0855 Refactored wheel diameters config logic 2024-08-08 21:19:52 -04:00
Paul Dino Jones 6c66c8880e Set useconfig before initializing devices 2024-08-08 15:02:55 -04:00
Paul Dino Jones 27cb95c86d Fixing issue with diameters file. Disabling diameters config checking during monocoque test. Additonal logging 2024-08-08 00:28:54 -04:00
Paul Dino Jones a9012a07fb setting uninitialized values on sound devices 2024-08-08 00:28:03 -04:00
Paul Dino Jones 366b8cb5b1 No longer abort on one failed write to a serial device. Log error instead. 2024-08-06 16:45:29 -04:00
Paul Dino Jones 2086108de6 Add Reverse for Cammus C5 2024-08-06 10:06:49 -04:00
Paul Dino Jones fda4c0dbcd Update gameloop so it sets gear to neutral on shutdown. Add enum for gears. 2024-08-06 10:06:24 -04:00
Paul Dino Jones aff76a3dd9 Fix issue with bad data being send to cammus wheel on quit 2024-08-05 17:08:23 -04:00
Paul Dino Jones 3bcf2ee87e Fix include paths 2024-08-05 17:07:04 -04:00
Paul Dino Jones 316ff0c273 Add more logging for cammus 2024-08-05 16:43:47 -04:00
Paul Dino Jones bf8dddd89f clear bytes on each update 2024-08-05 16:31:58 -04:00
Paul Dino Jones b4ebcc078b Fix gear 2024-08-05 16:18:51 -04:00
Paul Dino Jones 45b336ecbc Using correct pid for cammus 2024-08-05 16:11:29 -04:00
Paul Dino Jones db1caa2b8d Small fixes for speed and gear 2024-08-05 16:10:15 -04:00
Paul Dino Jones 492484ac7c rpm margin must return as an integer 2024-08-05 14:49:32 -04:00
Paul Dino Jones fdad56db31 Initial attempt at adding support CammusC5 wheel with underlying code for future wheels. 2024-08-05 13:42:17 -04:00
Paul Dino Jones 47cf4b3fa9 Reverse regression in previous commit 2024-08-04 17:07:09 -04:00
Paul Dino Jones 6b9c688bcc Refactor motor haptic effect slightly. Add logging. 2024-08-04 14:58:01 -04:00
Paul Dino Jones 14da8d22e7 Pull in and use latest simapi 2024-07-28 19:02:40 -04:00
Paul Dino Jones 40d4c19a26 Start sound devices with frequency set to 0 to avoid loud start ups 2024-07-28 11:18:41 -04:00
Paul Dino Jones 53cdcc592f Fix small buffer overflow on the diameters file string 2024-07-28 11:00:26 -04:00
Paul Dino Jones 80a2afb675 Update to latest simapi to fix issues with file descriptors 2024-07-28 02:23:45 -04:00
Paul Dino Jones a07bb24730 Add more logging for mem freeing 2024-07-28 02:08:18 -04:00
Paul Dino Jones 6989881b00 update to latest simapi to bypass wrong behavior 2024-07-28 02:06:44 -04:00
Paul Dino Jones e15f7e0865 Added extra logging. Added code to free mapped memory and file descriptors 2024-07-27 20:32:09 -04:00
Paul Dino Jones bc6b4825e9 Fix issue with tyre effects on sound devices 2024-07-27 20:09:24 -04:00
Paul Dino Jones 73d739fb20 update to latest simapi 2024-07-27 19:51:17 -04:00
Paul Dino Jones 3ab4e69d64 More support for beyond stereo 2024-07-22 21:45:57 -04:00
Paul Dino Jones d581e3eeab Better handle volume math 2024-07-20 20:15:16 -04:00
Paul Dino Jones 40189f3226 Updated readme with some instructions on bass shakers 2024-07-20 16:39:53 -04:00
Paul Dino Jones 30a3c3a73f Add ability to use sound cards with more than 2 channels. 2024-07-20 16:31:17 -04:00
Paul Dino Jones dd20fe3eb4 Updated slog dependency 2024-07-17 17:30:00 -04:00
spikerguy da7902b460
Add Arduino upload, Test cmd and log location (#6)
* add log, arduino upload and Test

* use correct path

* add haptic nodes

* readme: add haptic and arduino motor connection
2024-07-17 10:14:28 -04:00
Paul Dino Jones 6b122c45f3 Added log statement for ampfactor 2024-07-13 14:46:36 -04:00
Paul Dino Jones 45db6670c9 Add ampfactor to control final motor power 2024-07-13 14:21:35 -04:00
Paul Dino Jones e8e8b2495d Add abs brake test 2024-07-13 12:48:05 -04:00
Paul Dino Jones f1ddbf2fc0 Cleanup and fix issue that could cause abs to not work. 2024-07-13 11:36:21 -04:00
Paul Dino Jones 4f8870427f Set the fallback threshold for tyre slip effects to zero 2024-07-13 09:40:27 -04:00
Paul Dino Jones f3e7674aa7 Update so multiple defined serial haptic effects do not conflict with eachother. 2024-07-13 09:38:00 -04:00
Paul Dino Jones 42d63ffe89 Fix issues with data not being correctly sent to motors for sim haptic. 2024-07-12 20:42:55 -04:00
Paul Dino Jones 03b96e84b0 Fix dumb issues with haptic arduino code. Add tyre slip vibrations to test script. 2024-07-09 17:14:40 -04:00
Paul Dino Jones f413e749b9 Add sim haptic arduino logic and config variables 2024-07-09 15:46:51 -04:00
Paul Dino Jones 1bde76e3c7 Move haptic effect data into it's own structure. 2024-07-03 19:18:39 +00:00
Paul Dino Jones ea100ca2cf Add haptic effect to serial haptic device type 2024-07-01 22:45:54 +00:00
Paul Dino Jones 1ebffa9f63 Add basic support for arduino haptic motors 2024-07-01 15:23:46 +00:00
Paul Dino Jones 62e619780f Improve handling of devices referenced in config but not actually able to be used for some reason 2024-06-27 20:20:13 +00:00
Paul Dino Jones 8f4729c77a Refactor diameter save file into libconfig to save a dependency. 2024-06-11 16:42:29 -04:00
Paul Dino Jones dd23468593 Updated config file example 2024-06-11 10:53:26 -04:00
Paul Dino Jones 674d23a5e9 Send string around for diameters file 2024-06-11 14:41:30 +00:00
Paul Dino Jones ccd7fa8bd0 Fix some issues with pulseaudio not stopping properly. 2024-06-11 14:29:09 +00:00
Paul Dino Jones 4ad6a1339c Add support for save file for tyre diameters 2024-06-10 00:46:11 +00:00
Paul Dino Jones a26b090935 Update to latest simapi 2024-06-10 00:45:21 +00:00
Paul Dino Jones db5de27fa7 Initialize tyre diameters 2024-06-10 00:44:51 +00:00
Paul Dino Jones e211b35c9d Remove need to specify values for usb pedal rumble effects. 2024-06-07 17:40:53 +00:00
Paul Dino Jones 8cbc21b1a0 Add attempt at ABS effect. Small refactorings 2024-06-07 04:21:08 +00:00
Paul Dino Jones 4530627fc2 Use correct sysfs path for csl elite v3 pedals 2024-06-07 03:02:53 +00:00
Paul Dino Jones 77a9368307 Refactor implementation of CSL Elite V3 Pedals 2024-06-06 21:42:08 +00:00
Paul Dino Jones b99773a7ee Refactor tyre effects to take threshold input from config file. Spell threshold correctly. 2024-06-05 04:21:39 +00:00
Paul Dino Jones 6c17c56813 Update valgrindrc to ignore libs outside project 2024-06-05 04:20:23 +00:00
Paul Dino Jones 980860eb60 Refactor effects into separate module 2024-06-05 03:43:48 +00:00
Paul Dino Jones 1bfe8c94ac Refactored tyre slip calculations 2024-06-03 18:42:16 -04:00
Paul Dino Jones 01277823ba Update to latest simapi to support new tyre slip calculations. 2024-06-03 18:40:50 -04:00
Paul Dino Jones 95a7b40184 Cleanup tyre locking implementation 2024-06-01 18:07:37 +00:00
Paul Dino Jones 557269c9f5 Implement tyre locking. Cleanup test gameloop 2024-05-30 20:39:47 +00:00
Paul Dino Jones ac215bfed3 Update to latest submodule 2024-05-30 18:27:41 +00:00
Paul Dino Jones 5bd816bdb1 Fix tachometer config error handling 2024-05-30 18:25:47 +00:00
Paul Dino Jones 54dde79ad8 Fix wheel slip calculations 2024-05-30 18:03:26 +00:00
Paul Dino Jones b01e0f461c Fix compilation error on portaudio codepath 2024-05-30 17:25:07 +00:00
Paul Dino Jones 580e6752ce Add usbhaptic support 2024-05-30 17:07:44 +00:00
Paul Dino Jones f5aac7d4b4 Add wheelslip and abs to test loop 2024-05-28 16:12:48 +00:00
Paul Dino Jones 5b4a77e510 Updated to latest simapi 2024-03-09 13:39:12 -05:00
Paul Dino Jones d7eb07e682 Updated to latest simapi 2024-03-09 13:36:18 -05:00
Paul Dino Jones b70475039f Fix wrong data type for velocity 2024-03-05 17:23:29 -05:00
Paul Dino Jones a621aa04d9 Fix compilation issue for wheel slip 2024-03-05 00:36:34 -05:00
Paul Dino Jones 04b368321f Fix compilation issues for arduino devices 2024-03-05 00:34:29 -05:00
Paul Dino Jones f1ad4f5f91 Refactored serial arduino devices. Created custom structs for sending data to arduino devices. 2024-03-05 04:51:49 +00:00
Paul Dino Jones 150c53c14b Added preliminary support for tyre slip and abs vibrations 2024-02-29 19:19:57 +00:00
Paul Dino Jones 47a091d9b4 Fixed simapi version 2024-02-28 14:10:13 -05:00
Paul Dino Jones 29ffafc4e9 Add makefiles for arduino and related documentation 2024-02-27 17:36:02 -05:00
Paul Dino Jones e71480ff4a Fixed signal handleing 2024-02-26 22:05:41 +00:00
Paul Dino Jones 635796c543 Add more user variables in arduino code 2024-02-17 18:50:57 +00:00
Paul Dino Jones 85ba9fcd56 Updated to latest simapi for wheel slip and abs 2024-02-16 16:16:26 +00:00
Paul Dino Jones ddb493fdc4 Slight refactor of detecting running sim 2024-01-02 19:25:11 +00:00
Paul Dino Jones 5e443a562a Fix latency on pulseaudio backend 2023-11-14 15:31:44 +00:00
Paul Dino Jones 534b03b781 Add basic info for pulseaudio backend into readme 2023-11-13 07:50:10 +00:00
Paul Dino Jones 66b9ecab59 Updated config for new pulseaudio backend 2023-11-13 07:46:04 +00:00
Paul Dino Jones 34f6f1eb79 Fix possible causes of leaks and segfaults 2023-11-12 13:27:17 -05:00
Paul Dino Jones 14a26fb165 Adding pulseaudio backend 2023-11-11 18:19:39 +00:00
Paul Dino Jones fbf71ce528 Improve game detection in gameloop 2023-10-12 05:26:55 +00:00
Paul Dino Jones c688cac20d Update to latest simapi to fix compilation issue 2023-10-11 15:57:55 +00:00
Paul Dino Jones 88041b2a95 Updated to latest simapi for ACC Support and more RFactor2 support 2023-10-07 19:38:03 +00:00
Paul Dino Jones 836fe4a3f5 Updating to latest simapi for RFactor2 fixes. 2023-10-01 15:03:19 +00:00
Paul Dino Jones 4a566d8996 Updating to latest simapi to support RFactor2 detection. 2023-09-29 21:26:03 +00:00
condaatje 854c0df56c Update README.md
remove workaround in favour of default success
2023-09-29 12:14:08 -04:00
condaatje c88828ca44 Update .gitmodules
ssh -> https gitmodule
2023-09-29 12:12:47 -04:00
condaatje dd302f7c49 add workaround for microsoft ssh clone requirements 2023-09-21 10:06:48 -04:00
110 changed files with 10963 additions and 1070 deletions

81
.github/workflows/ci.yaml vendored Normal file
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

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
/build
/src/monocoque/tags
/src/monocoque/simulatorapi/simapi
.vscode

3
.gitmodules vendored
View File

@ -1,3 +1,4 @@
[submodule "src/monocoque/simulatorapi/simapi"]
path = src/monocoque/simulatorapi/simapi
url = git@github.com:spacefreak18/simapi
url = https://github.com/spacefreak18/simapi
# url = git@github.com:spacefreak18/simapi # ssh is better but Microsoft requires an account

View File

@ -1,3 +1,4 @@
# small known issue with argtable2
{
argtable arg_parse
Memcheck:Leak
@ -107,3 +108,15 @@
...
...
}
{
ignore_unversioned_libs
Memcheck:Leak
...
obj:*/lib*/lib*.so
}
{
ignore_versioned_libs
Memcheck:Leak
...
obj:*/lib*/lib*.so.*
}

View File

@ -1,7 +1,4 @@
# minimum CMake version required for C++20 support, among other things
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.18)
# detect if Monocoque is being used as a sub-project of another CMake project
if(NOT DEFINED PROJECT_NAME)
set(MONOCOQUE_SUBPROJECT OFF)
@ -20,54 +17,98 @@ set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed -ldl")
set(LIBUSB_INCLUDE_DIR /usr/include/libusb-1.0)
set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
HINTS $ENV{LIBUSB_ROOT}
PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
PATH_SUFFIXES include)
FIND_PACKAGE(Lua)
set(INCLUDE_DIRS ${LUA_INCLUDE_DIR})
include_directories(${LUA_INCLUDE_DIR})
if(Lua_FOUND AND NOT TARGET Lua::Lua)
add_library(Lua::Lua INTERFACE IMPORTED)
set_target_properties(
Lua::Lua
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
)
FIND_LIBRARY(LIBUSB_LIBRARY NAMES usb-1.0
HINTS $ENV{LIBUSB_ROOT}
PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}
PATH_SUFFIXES lib)
endif()
message("Lua ${Lua_VERSION} found")
if (Lua_VERSION VERSION_GREATER_EQUAL "5.5.0")
add_compile_definitions(USE_LUA_55)
else()
remove_definitions(USE_LUA_55)
endif()
set(HIDAPI_WITH_LIBUSB TRUE) # surely will be used only on Linux
#FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
# HINTS $ENV{LIBUSB_ROOT}
# PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
# PATH_SUFFIXES include)
#
#FIND_LIBRARY(LIBUSB_LIBRARY NAMES usb-1.0
# HINTS $ENV{LIBUSB_ROOT}
# PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}
# PATH_SUFFIXES lib)
#
set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux
set(BUILD_SHARED_LIBS TRUE) # HIDAPI as static library on all platforms
add_executable(monocoque src/monocoque/monocoque.c)
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(FATAL_ERROR "Either libprocps or libproc2 is required")
endif()
#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)
add_subdirectory(src/monocoque/helper)
add_subdirectory(src/monocoque/devices)
add_subdirectory(src/monocoque/slog)
add_executable(monocoque src/monocoque/monocoque.c)
target_include_directories(monocoque PUBLIC config ${LIBUSB_INCLUDE_DIR} ${LIBXML_INCLUDE_DIR})
target_link_libraries(monocoque m ${LIBUSB_LIBRARY} hidapi-libusb portaudio serialport xml2 argtable2 config gameloop helper devices slog simulatorapi)
#add_executable(listusb tests/testlibusb.c)
#target_include_directories(listusb PUBLIC)
#target_link_libraries(listusb portaudio hidapi-hidraw)
#add_test(listusb list-usb-devices listusb)
add_executable(listusb tests/testlibusb.c)
target_include_directories(listusb PUBLIC ${LIBUSB_INCLUDE_DIR})
target_link_libraries(listusb ${LIBUSB_LIBRARY} portaudio)
add_test(listusb list-usb-devices listusb)
add_executable(testrevburner tests/testrevburner.c)
target_include_directories(testrevburner PUBLIC ${LIBUSB_INCLUDE_DIR})
target_link_libraries(testrevburner ${LIBUSB_LIBRARY})
add_test(testrevburner testrevburner)
add_executable(listsound tests/pa_devs.c)
target_include_directories(listsound PUBLIC)
target_link_libraries(listsound m portaudio)
add_test(list-sound-devices listsound)
add_executable(longsine tests/patest_longsine.c)
target_include_directories(longsine PUBLIC ${LIBUSB_INCLUDE_DIR} ${LIBXML_INCLUDE_DIR})
target_link_libraries(longsine ${LIBUSB_LIBRARY} m portaudio)
add_test(longsine longsine)
add_executable(parserevburnerxml tests/revburnerparsetest.c)
target_include_directories(parserevburnerxml PUBLIC ${LIBXML_INCLUDE_DIR})
target_link_libraries(parserevburnerxml ${LIBUSB_LIBRARY} portaudio xml2)
add_test(parserevburnerxml parserevburnerxml)
#add_executable(testrevburner tests/testrevburner.c)
#target_include_directories(testrevburner PUBLIC)
#target_link_libraries(testrevburner hidapi-hidraw)
#add_test(testrevburner testrevburner)
#add_executable(listsound tests/pa_devs.c)
#target_include_directories(listsound PUBLIC)
#target_link_libraries(listsound m portaudio)
#add_test(list-sound-devices listsound)
#
#add_executable(longsine tests/patest_longsine.c)
#target_include_directories(longsine PUBLIC ${LIBXML_INCLUDE_DIR})
#target_link_libraries(longsine m portaudio)
#add_test(longsine longsine)
#
#add_executable(parserevburnerxml tests/revburnerparsetest.c)
#target_include_directories(parserevburnerxml PUBLIC ${LIBXML_INCLUDE_DIR})
#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)
@ -77,17 +118,17 @@ add_executable(getmem tests/getmem.c)
target_include_directories(getmem PUBLIC)
target_link_libraries(getmem)
add_test(getmem getmem)
add_executable(setsimdata tests/setsimdata.c)
target_include_directories(setsimdata PUBLIC)
target_link_libraries(setsimdata)
add_test(setsimdata setsimdata)
add_executable(hidtest tests/hidtest.c)
target_include_directories(hidtest PUBLIC ${LIBUSB_INCLUDE_DIR})
target_link_libraries(hidtest ${LIBUSB_LIBRARY} hidapi-libusb)
add_test(hidtest hidtest)
#
#add_executable(setsimdata tests/setsimdata.c)
#target_include_directories(setsimdata PUBLIC)
#target_link_libraries(setsimdata)
#add_test(setsimdata setsimdata)
#
#add_executable(hidtest tests/hidtest.c)
#target_include_directories(hidtest PUBLIC)
#target_link_libraries(hidtest hidapi-hidraw)
#add_test(hidtest hidtest)
#
add_executable(simlighttest tests/simlighttest.c)
target_include_directories(simlighttest PUBLIC)
target_link_libraries(simlighttest serialport)

39
HOW-TO-USE.md Normal file
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

View File

@ -8,43 +8,109 @@ _ / / / / /_/ / / / / /_/ / /__ / /_/ / /_/ // /_/ // __/
```
Cross Platform device manager for driving and flight simulators, for use with common simulator software titles.
📚 **Usage Documentation:** [spacefreak18.github.io/simapi/](https://spacefreak18.github.io/simapi/)
## Features
- Updates at 120 frames per seconds.
- Updates at 60 frames per seconds.
- Modular design for support with various titles and devices.
- Supports bass shakers, tachometers, simlights, simwind etc, through usb and arduino serial.
- Supports bass shakers, tachometers, several wheels and pedals, simlights, simwind etc, through usb and arduino serial.
- Tachometer support is currently limited to the Revburner model. Supports existing revburner xml configuration files.
- Includes utility to configure revburner tachometer
- Can send data to any serial device. So far only tested with arduino. Includes sample arduino sketch for sim lights.
- The support for haptic bass shakers is limited and needs the most work. So far the engine rev is a simple sine wave, which I find convincing. The gear shift event works but not convincing enough for me.
- Can send data to any serial device. So far only tested with arduino. and ESP32. Includes sample arduino sketch for sim lights, simwind, and simhaptic effects for motors.
- Support for custom arduino (or any serial) device, with a [custom lua format](https://spacefreak18.github.io/simapi/serial_custom) for sending data
- Convincing shaker effects for noise tranducers for wheel slip, wheel lock, and abs, as well as engine rpm and gear shifts.
- Support for many [wheels and pedals](https://spacefreak18.github.io/simapi/thirdpartydevices) including Clubsport Elite V3, [Logitech G29](https://spacefreak18.github.io/simapi/logitechg29), and Moza R5.
## Dependencies
## Adding More Devices
- If a device isn't already supported, feel free to assist in the reverse engineering process by submitting a pcap or even better working code in a pull request!
https://santeri.pikarinen.com/pages/usb_hid_reverse_engineering/
## Quick Install
**One-Line Installation:**
```bash
curl -fsSL https://raw.githubusercontent.com/Spacefreak18/monocoque/master/install.sh | bash
```
***AUR Package following git master:***
```https://aur.archlinux.org/packages/monocoque-git```
**Or download and review first:**
```bash
wget https://raw.githubusercontent.com/Spacefreak18/monocoque/master/install.sh
chmod +x install.sh
./install.sh
```
### Installation on Immutable Distros (Bazzite, Silverblue, etc.)
On immutable distributions where native package managers are unavailable,
monocoque can be installed inside a distrobox container:
```bash
bash tools/distro/distrobox/install-distrobox.sh
```
This creates an Arch Linux container, installs all packages via AUR, and sets up
wrapper scripts (`start-simd`, `start-monocoque`, `test-monocoque`) in `~/.local/bin/`.
To uninstall: `bash tools/distro/distrobox/uninstall-distrobox.sh`
**TUI Manager:**
After installation, use the interactive manager:
```bash
monocoque-manager
```
**Supported Games**
[Supported Sims](https://spacefreak18.github.io/simapi/supportedsims)
***please note on Linux some titles will require a compatibility exe from simshmbridge to be setup. Please follow the linked Documentation
for installation instructions***
## Building
### Dependencies
- libserialport - arduino serial devices
- hidapi - usb hid devices
- libusb - used by hidapi
- hidapi - usb hid devices (hidraw)
- portaudio - sound devices (haptic bass shakers)
- libenet - UDP support (not yet implemented)
- libpulse - sound devices (haptic bass shakers)
- libuv base event loop
- libxml2
- argtable2
- libconfig
- xdg-basedir
- lua
- libproc2
- [slog](https://github.com/kala13x/slog) (static)
- [wine-linux-shm-adapter](https://github.com/spacefreak18/simshmbridge) - for sims that need shared memory mapping like AC.
- [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 lua libproc2
```
## Building
This code depends on the shared memory data headers in the simapi [repo](https://github.com/spacefreak18/simapi). When pulling lastest if the submodule does not download run:
```
git submodule sync --recursive
git submodule update --init --recursive
```
Then to compile simply:
```
mkdir build; cd build
cmake ..
make
```
## Testing
### Setting up Your Arduino Device
## 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
```
@ -53,15 +119,20 @@ make
cmake -Danalyze=on ..
make
```
### Valgrind
```
cd build
valgrind -v --leak-check=full --show-leak-kinds=all --suppressions=../.valgrindrc ./monocoque play
```
## Join the Discussion
[Sim Racing Matrix Space](https://matrix.to/#/#simracing:matrix.org)
## ToDo
- add frequency cap (low pass filter) to sound haptic effects
- add road and kerb sound haptic effects
- windows port
- more memory testing
- move config code around
- cleanup tests which are basically just copies of the example from their respective projects
- much, much more

29
conf/basic_rpms.lua Normal file
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
conf/car_radar.lua Normal file
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
conf/custom.lua Normal file
View File

@ -0,0 +1 @@
Message = simdata.velocity .. ";" .. simdata.gear .. ";" .. simdata.rpm .. ";"

View File

@ -1,21 +1,246 @@
devices = ( { device = "USB";
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 = 4;
config = "/home/paul/.config/monocoque/revburner1.xml"; },
{ device = "Sound";
type = "Engine"
devid = "98FD:83AC"; },
{ device = "Sound";
type = "Gear"
devid = "98FD:83AC"; },
{ device = "Serial";
type = "ShiftLights";
config = "None";
devpath = "/dev/ttyACM0"; } ),
{ device = "Serial";
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";
devpath = "/dev/ttyACM1"; } );
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
conf/neo.lua Normal file
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
conf/rpms_and_flags.lua Normal file
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
conf/rpms_and_radar.lua Normal file
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

554
install.sh Executable file
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
src/arduino/custom/Makefile Normal file
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

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() {
}

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

View File

@ -0,0 +1,28 @@
#ifndef _SHIFTLIGHTSDATA_H
#define _SHIFTLIGHTSDATA_H
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
uint8_t litleds;
//unsigned char color_1_red;
//unsigned char color_1_green;
//unsigned char color_1_blue;
//unsigned char color_2_red;
//unsigned char color_2_green;
//unsigned char color_2_blue;
//unsigned char color_3_red;
//unsigned char color_3_green;
//unsigned char color_3_blue;
//unsigned char space_1;
//unsigned char space_2;
//unsigned char space_3;
//uint32_t maxrpm;
//uint32_t rpm;
}
ShiftLightsData;
#endif

View File

@ -1,24 +1,35 @@
#include <FastLED.h>
#include "simdata.h"
#include "shiftlights.h"
#define SIMDATA_SIZE sizeof(SimData)
#define SIMDATA_SIZE sizeof(ShiftLightsData)
#define LED_PIN 7
#define NUM_LEDS 6
#define BRIGHTNESS 40
#define STARTLED 1
#define COLOR1R 0
#define COLOR1G 255
#define COLOR1B 0
#define COLOR2R 255
#define COLOR2G 255
#define COLOR2B 0
#define COLOR3R 255
#define COLOR3G 0
#define COLOR3B 0
CRGB leds[NUM_LEDS];
SimData sd;
ShiftLightsData sd;
int maxrpm = 0;
int rpm = 0;
int numlights = NUM_LEDS;
int pin = LED_PIN;
int lights[6];
int lights[NUM_LEDS];
void setup()
{
Serial.begin(9600);
Serial.begin(115200);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
FastLED.setBrightness(BRIGHTNESS);
@ -29,67 +40,45 @@ void setup()
}
FastLED.clear();
sd.rpms = 0;
sd.maxrpm = 6500;
sd.altitude = 10;
sd.pulses = 40000;
sd.velocity = 10;
sd.litleds = 0;
//sd.rpm = 0;
//sd.maxrpm = 6500;
}
void loop()
{
int l = 0;
int lit = sd.litleds;
char buff[SIMDATA_SIZE];
if (Serial.available() >= SIMDATA_SIZE)
{
Serial.readBytes(buff, SIMDATA_SIZE);
memcpy(&sd, &buff, SIMDATA_SIZE);
rpm = sd.rpms;
maxrpm = sd.maxrpm;
}
while (l < numlights)
{
lights[l] = 0;
l++;
}
l = -1;
int rpmlights = 0;
while (rpm > rpmlights)
{
if (l>=0)
{
lights[l] = 1;
}
l++;
rpmlights = rpmlights + (((maxrpm-250)/numlights));
lit = sd.litleds;
}
l = 0;
FastLED.clear();
while (l < numlights)
while (l < lit)
{
if (l >= numlights / 2)
{
leds[l] = CRGB ( 0, 0, 255);
leds[l+STARTLED-1] = CRGB ( COLOR2R, COLOR2G, COLOR2B);
}
if (l < numlights / 2)
{
leds[l] = CRGB ( 0, 255, 0);
leds[l+STARTLED-1] = CRGB ( COLOR1R, COLOR1G, COLOR1B);
}
if (l == numlights - 1)
{
leds[l] = CRGB ( 255, 0, 0);
leds[l+STARTLED-1] = CRGB ( COLOR3R, COLOR3G, COLOR3B);
}
if (lights[l] <= 0)
{
leds[l] = CRGB ( 0, 0, 0);
}
FastLED.show();
//if (lights[l] <= 0)
//{
// leds[l] = CRGB ( 0, 0, 0);
//}
l++;
}
FastLED.show();
}

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

View File

@ -0,0 +1 @@
../../monocoque/simulatorapi/simapi/simapi/simdata.h

View File

@ -0,0 +1,21 @@
#ifndef _SIMHAPTICDATA_H
#define _SIMHAPTICDATA_H
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
uint8_t motor1;
uint8_t effect1;
uint8_t motor2;
uint8_t effect2;
uint8_t motor3;
uint8_t effect3;
uint8_t motor4;
uint8_t effect4;
}
SimHapticData;
#endif

View File

@ -0,0 +1,81 @@
#include <Adafruit_MotorShield.h>
#include "simhaptic.h"
#define BYTE_SIZE sizeof(SimHapticData)
#define POWER .6
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *myMotor1 = AFMS.getMotor(1);
//Adafruit_DCMotor *myMotor2 = AFMS.getMotor(2);
Adafruit_DCMotor *myMotor3 = AFMS.getMotor(3);
//Adafruit_DCMotor *myMotor4 = AFMS.getMotor(4);
SimHapticData sd;
int motor1 = 0;
int motor2 = 0;
int motor3 = 0;
int motor4 = 0;
int effect1 = 0;
int effect2 = 0;
int effect3 = 0;
int effect4 = 0;
void setup() {
Serial.begin(115200);
if (!AFMS.begin()) {
Serial.println("Could not find Motor Shield. Check wiring.");
while (1);
}
sd.motor1 = 0;
sd.motor2 = 0;
sd.motor3 = 0;
sd.motor4 = 0;
myMotor1->setSpeed(0);
myMotor1->run(FORWARD);
//myMotor2->setSpeed(0);
//myMotor2->run(FORWARD);
myMotor3->setSpeed(0);
myMotor3->run(FORWARD);
//myMotor4->setSpeed(0);
//myMotor4->run(FORWARD);
}
void loop() {
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE)
{
Serial.readBytes(buff, BYTE_SIZE);
memcpy(&sd, &buff, BYTE_SIZE);
motor1 = sd.motor1;
motor2 = sd.motor2;
motor3 = sd.motor3;
motor4 = sd.motor4;
effect1 = sd.effect1;
effect2 = sd.effect2;
effect3 = sd.effect3;
effect4 = sd.effect4;
}
if (motor1 >= 1)
{
myMotor1->setSpeed(effect1);
}
//if (motor2 >= 1)
//{
// myMotor2->setSpeed(effect2);
//}
if (motor3 >= 1)
{
myMotor3->setSpeed(effect3);
}
//if (motor4 >= 1)
//{
// myMotor4->setSpeed(effect4);
//}
}

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
src/arduino/simwind/simwind.h Executable file
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

View File

@ -1,28 +1,24 @@
#include <Adafruit_MotorShield.h>
#include "simdata.h"
#include "simwind.h"
#define BYTE_SIZE sizeof(SimData)
#define BYTE_SIZE sizeof(SimWindData)
#define KPHTOMPH .621317
#define FANPOWER .6
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *myMotor1 = AFMS.getMotor(1);
Adafruit_DCMotor *myMotor2 = AFMS.getMotor(3);
SimData sd;
SimWindData sd;
int velocity = 0;
void setup() {
Serial.begin(9600);
Serial.begin(115200);
if (!AFMS.begin()) {
Serial.println("Could not find Motor Shield. Check wiring.");
while (1);
}
sd.rpms = 0;
sd.maxrpm = 6500;
sd.altitude = 10;
sd.pulses = 40000;
sd.velocity = 10;
myMotor1->setSpeed(0);
@ -41,11 +37,7 @@ void loop() {
memcpy(&sd, &buff, BYTE_SIZE);
velocity = sd.velocity;
}
int v = ceil(velocity * KPHTOMPH);
if (v >= 255)
{
v = 255;
}
myMotor1->setSpeed(v);
myMotor2->setSpeed(v);
myMotor1->setSpeed(velocity*FANPOWER);
myMotor2->setSpeed(velocity*FANPOWER);
}

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

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

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;
}

View File

@ -7,14 +7,43 @@ set(devices_source_files
sounddevice.c
serialdevice.h
serialdevice.c
serialadapter.h
serialadapter.c
tachdevice.h
tachdevice.c
wheeldevice.h
wheeldevice.c
usbhapticdevice.h
usbhapticdevice.c
hapticeffect.h
hapticeffect.c
sound.h
sound.c
usb/revburner.h
usb/revburner.c
sound/usb_generic_shaker.h
sound/usb_generic_shaker.c
usb/cslelitev3.h
usb/cslelitev3.c
usb/simagicp1000.h
usb/simagicp1000.c
usb/wheels/logitechg29.h
usb/wheels/logitechg29.c
usb/wheels/cammusc5.h
usb/wheels/cammusc5.c
usb/wheels/cammusc12.h
usb/wheels/cammusc12.c
usb/wheels/gtneo.h
usb/wheels/gtneo.c
sound/usb_generic_shaker_pulse.c
serial/arduino.h
serial/arduino.c
serial/arduinoledlua.h
serial/arduinoledlua.c
serial/moza.h
serial/moza.c
serial/moza_new.h
serial/moza_new.c
serial/moza_ks_pro_wheel.h
serial/moza_ks_pro_wheel.c
)
include_directories("." "usb" "sound" "serial")

View File

@ -0,0 +1,422 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <math.h>
#include "usbhapticdevice.h"
#include "../../helper/confighelper.h"
#include "../../simulatorapi/simapi/simapi/simdata.h"
#include "../../simulatorapi/simapi/simapi/simmapper.h"
#include "../../slog/slog.h"
#define kmhtoms 0.277778
#define minspeedinms 0.5
#define minvelocity 50
#define maxbrake 0
#define maxthrottle 0
#define maxXvelocity 0.001
bool hasTyreDiameter(SimData* simdata)
{
if (simdata->tyrediameter[0] <= 0 || simdata->tyrediameter[1] <= 0 || simdata->tyrediameter[2] <= 0 || simdata->tyrediameter[3] <= 0)
{
slogt("failed to find tyre diameter data");
return false;
}
slogt("tyre diameter data found");
return true;
}
int loadtyreconfig(SimData* simdata, char* configfile, bool setDiameters)
{
config_t cfg;
config_init(&cfg);
const config_setting_t* config_cars = NULL;
if (!config_read_file(&cfg, configfile))
{
slogw("Could not open diameters save file.");
config_destroy(&cfg);
return 1;
}
config_cars = config_lookup(&cfg, "cars");
if(config_cars == NULL)
{
slogd("diameters config file corrupted");
config_destroy(&cfg);
return 1;
}
slogt("parsing diameters config file");
const int cars = config_setting_length(config_cars);
bool foundCar = false;
int i = 0;
while (i<cars)
{
DeviceSettings settings;
config_setting_t* config_car = config_setting_get_elem(config_cars, i);
if(config_car != NULL)
{
const char* car;
const char* simstr;
int sim = 0;
double tyre0;
double tyre1;
double tyre2;
double tyre3;
config_setting_lookup_string(config_car, "car", &car);
config_setting_lookup_float(config_car, "tyre0", &tyre0);
config_setting_lookup_float(config_car, "tyre1", &tyre1);
config_setting_lookup_float(config_car, "tyre2", &tyre2);
config_setting_lookup_float(config_car, "tyre3", &tyre3);
int found = config_setting_lookup_int(config_car, "sim", &sim);
//if(found == CONFIG_FALSE)
//{
// int found = config_setting_lookup_int(config_car, "sim", &sim);
//}
//else
//{
// sim = simapi_strtogame(simstr);
//}
if(simdata->car != NULL && car != NULL)
{
if(simdata->car[0] != '\0' && car[0] != '\0')
{
slogt("%s %s %i %i", simdata->car, car, simdata->simexe, sim);
if (strcicmp(car, simdata->car) == 0 && sim == simdata->simexe)
{
slogi("found saved car %s with tyre diameters %f %f %f %f", car, tyre0, tyre1, tyre2, tyre3);
foundCar = true;
if(setDiameters == true)
{
simdata->tyrediameter[0] = tyre0;
simdata->tyrediameter[1] = tyre1;
simdata->tyrediameter[2] = tyre2;
simdata->tyrediameter[3] = tyre3;
}
break;
}
}
}
}
else
{
slogw("Possible corruption in config file on entry %i attempting to continue", i+1);
}
i++;
}
config_destroy(&cfg);
if(foundCar == true)
{
return i;
}
return -1;
}
int savetyreconfig(SimData* simdata, char* configfile)
{
config_t cfg;
config_setting_t* root;
config_setting_t* array;
config_setting_t* carobject;
config_setting_t* setting;
config_init(&cfg);
if (!config_read_file(&cfg, configfile))
{
slogw("Could not open diameters save file, creating new.");
root = config_root_setting(&cfg);
array = config_setting_add(root, "cars", CONFIG_TYPE_LIST);
}
else
{
array = config_lookup(&cfg, "cars");
}
// TODO add check to not add same car-sim combination twice
carobject = config_setting_add(array, "cars", CONFIG_TYPE_GROUP);
setting = config_setting_add(carobject, "car", CONFIG_TYPE_STRING);
config_setting_set_string(setting, simdata->car);
setting = config_setting_add(carobject, "sim", CONFIG_TYPE_INT64);
config_setting_set_int64(setting, simdata->simexe);
setting = config_setting_add(carobject, "tyre0", CONFIG_TYPE_FLOAT);
config_setting_set_float(setting, simdata->tyrediameter[0]);
setting = config_setting_add(carobject, "tyre1", CONFIG_TYPE_FLOAT);
config_setting_set_float(setting, simdata->tyrediameter[1]);
setting = config_setting_add(carobject, "tyre2", CONFIG_TYPE_FLOAT);
config_setting_set_float(setting, simdata->tyrediameter[2]);
setting = config_setting_add(carobject, "tyre3", CONFIG_TYPE_FLOAT);
config_setting_set_float(setting, simdata->tyrediameter[3]);
/* Write out the new configuration. */
if(! config_write_file(&cfg, configfile))
{
slogi("Error while writing file.");
config_destroy(&cfg);
}
slogi("New configuration successfully written to: %s for sim %i, car %s\n", configfile, simdata->simexe, simdata->car);
config_destroy(&cfg);
return 0;
}
void getTyreDiameter(SimData* simdata)
{
if(simdata->velocity > minvelocity && simdata->brake <= maxbrake && simdata->gas <= maxthrottle)
{
double Speedms = kmhtoms * simdata->velocity;
if (simdata->Xvelocity/Speedms < maxXvelocity)
{
for(int i = 0; i < 4; i++)
{
simdata->tyrediameter[i] = Speedms / simdata->tyreRPS[i] * 2;
}
slogi("Successfully set tyre diameters for wheel slip effects.");
}
}
}
double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold, int useconfig, int* configcheck, char* configfile)
{
double play = 0;
double wheelslip[4];
wheelslip[0] = 0;
wheelslip[1] = 0;
wheelslip[2] = 0;
wheelslip[3] = 0;
int sim_slip_ratio =0;
for (int i = 0; i < 4; i++)
{
sim_slip_ratio = abs(simdata->tyreslipratio[i]);
}
if(sim_slip_ratio == 0)
{
slogt("wheel vibration calculation with wheel config set to %i configchecked %i configfile %s car %s sim %i", useconfig, *configcheck, configfile, simdata->car, simdata->simexe);
switch (effecttype)
{
case (EFFECT_TYRESLIP):
case (EFFECT_TYRELOCK):
case (EFFECT_ABSBRAKES):
if(hasTyreDiameter(simdata)==true)
{
double Speedms = kmhtoms * simdata->velocity;
slogt("attempting wheel slip calculation");
if (Speedms > minspeedinms)
{
for(int i = 0; i < 4; i++)
{
wheelslip[i] = (Speedms - simdata->tyrediameter[i] * simdata->tyreRPS[i] / 2) / Speedms;
}
}
else
{
for(int i = 0; i < 4; i++)
{
wheelslip[i] = 0;
}
}
slogt("wheelslip values are %f %f %f %f", wheelslip[0], wheelslip[1], wheelslip[2], wheelslip[3]);
slogt("velocities (x,y,z) are %f %f %f", simdata->Xvelocity, simdata->Yvelocity, simdata->Zvelocity);
}
break;
case EFFECT_SUSPENSION:
break;
default:
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]);
}
switch (effecttype)
{
case (EFFECT_TYRESLIP):
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[0] < -threshold)
{
play += fabs(wheelslip[0]) - fabs(threshold);
slogt("slip is %f", play);
}
}
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[1] < -threshold)
{
play += fabs(wheelslip[1]) - fabs(threshold);
slogt("slip is %f", play);
}
}
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[2] < -threshold)
{
play += fabs(wheelslip[2]) - fabs(threshold);
slogt("slip is %f", play);
}
}
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[3] < -threshold)
{
play += fabs(wheelslip[3]) - fabs(threshold);
slogt("slip is %f", play);
}
}
break;
case (EFFECT_TYRELOCK):
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[0] > threshold)
{
play += wheelslip[0] - threshold;
slogt("lock is %f", play);
}
}
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[1] > threshold)
{
play += wheelslip[1] - threshold;
slogt("lock is %f", play);
}
}
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[2] > threshold)
{
play += wheelslip[2] - threshold;
slogt("lock is %f", play);
}
}
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[3] > threshold)
{
play += wheelslip[3] - threshold;
slogt("lock is %f", play);
}
}
break;
case (EFFECT_ABSBRAKES):
threshold = simdata->abs + threshold;
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[0] > threshold)
{
play += wheelslip[0] - threshold;
slogt("abs is %f", play);
}
}
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[1] > threshold)
{
play += wheelslip[1] - threshold;
slogt("abs is %f", play);
}
}
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[2] > threshold)
{
play += wheelslip[2] - threshold;
slogt("abs is %f", play);
}
}
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[3] > threshold)
{
play += wheelslip[3] - threshold;
slogt("abs is %f", play);
}
}
if(simdata->abs <= 0)
{
play = 0;
}
break;
case (EFFECT_SUSPENSION):
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
if(simdata->suspension[0] > threshold)
{
play += simdata->suspension[0] - threshold;
slogt("suspension is %f", play);
}
}
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
{
if(simdata->suspension[1] > threshold)
{
play += simdata->suspension[1] - threshold;
slogt("suspension is %f", play);
}
}
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
{
if(simdata->suspension[2] > threshold)
{
play += simdata->suspension[2] - threshold;
slogt("suspension is %f", play);
}
}
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
{
if(simdata->suspension[3] > threshold)
{
play += simdata->suspension[3] - threshold;
slogt("suspension is %f", play);
}
}
break;
}
return play;
}

View File

@ -0,0 +1,25 @@
#ifndef _HAPTICEFFECT_H
#define _HAPTICEFFECT_H
#include <stdio.h>
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
typedef struct
{
double threshold;
VibrationEffectType effecttype;
MonocoqueTyreIdentifier tyre;
int useconfig;
int* configcheck;
char* tyrediameterconfig;
}
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

View File

@ -2,81 +2,429 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "arduino.h"
#include "arduinoledlua.h"
#include "../serialadapter.h"
#include "../../slog/slog.h"
#define arduino_timeout 2000
#define arduino_timeout 9000
int arduino_update(SerialDevice* serialdevice, SimData* simdata)
int arduino_check(enum sp_return result)
{
int result = 1;
if (serialdevice->port)
{
result = check(sp_blocking_write(serialdevice->port, simdata, sizeof(SimData), arduino_timeout));
}
return result;
}
int arduino_init(SerialDevice* serialdevice, const char* portdev)
{
slogi("initializing arduino serial device...");
int error = 0;
char* port_name = strdup(portdev);
slogd("Looking for port %s.\n", port_name);
error = check(sp_get_port_by_name(port_name, &serialdevice->port));
if (error != 0)
{
return error;
}
slogd("Opening port.\n");
check(sp_open(serialdevice->port, SP_MODE_READ_WRITE));
slogd("Setting port to 9600 8N1, no flow control.\n");
check(sp_set_baudrate(serialdevice->port, 9600));
check(sp_set_bits(serialdevice->port, 8));
check(sp_set_parity(serialdevice->port, SP_PARITY_NONE));
check(sp_set_stopbits(serialdevice->port, 1));
check(sp_set_flowcontrol(serialdevice->port, SP_FLOWCONTROL_NONE));
free(port_name);
slogd("Successfully setup arduino serial device...");
return 0;
}
int arduino_free(SerialDevice* serialdevice)
{
check(sp_close(serialdevice->port));
sp_free_port(serialdevice->port);
}
int check(enum sp_return result)
{
/* For this example we'll just exit on any error by calling abort(). */
char* error_message;
switch (result)
{
case SP_ERR_ARG:
//printf("Error: Invalid argument.\n");
return 1;
//abort();
case SP_ERR_FAIL:
error_message = sp_last_error_message();
printf("Error: Failed: %s\n", error_message);
sloge("error: serial write failed: %s", error_message);
sp_free_error_message(error_message);
abort();
case SP_ERR_SUPP:
printf("Error: Not supported.\n");
abort();
case SP_ERR_MEM:
printf("Error: Couldn't allocate memory.\n");
abort();
case SP_OK:
default:
return result;
}
}
int arduino_update(SerialDevice* serialdevice, void* data, size_t size)
{
int result = 1;
slogt("copying %i bytes to arduino device", size);
result = monocoque_serial_write(serialdevice->id, data, size, arduino_timeout);
return result;
}
// the input event waiting and flushing is the right way to do this
// most of the time i'm just "bit blasting" and the only receiving
// happens initially to get the number of lights
int GetNumberOfLeds(SerialDevice* serialdevice, int* numlights)
{
int count = 0;
int bytesWaiting;
char buf[256];
int retval = 0;
int i;
//sp_flush(port, SP_BUF_INPUT);
while (count < 4)
{
slogi("Attempting to retrieve num lights from port...");
size_t bufsize1 = 11;
size_t recv_bufsize1 = 5;
char recv_buf1[recv_bufsize1];
char bytes1[bufsize1];
for(int j = 0; j < bufsize1; j++)
{
bytes1[j] = 0x00;
}
bytes1[0] = 0xff;
bytes1[1] = 0xff;
bytes1[2] = 0xff;
bytes1[3] = 0xff;
bytes1[4] = 0xff;
bytes1[5] = 0xff;
bytes1[6] = 0x6c;
bytes1[7] = 0x65;
bytes1[8] = 0x64;
bytes1[9] = 0x73;
bytes1[10] = 0x63;
int result = 0;
unsigned int timeout = 6000;
slogd("Sending message to get num lights");
monocoque_wait_for_event(serialdevice->id, 6);
result = monocoque_serial_write(serialdevice->id, &bytes1, bufsize1, arduino_timeout);
monocoque_wait_for_event(serialdevice->id, 5);
bytesWaiting = monocoque_input_wait(serialdevice->id);
if (bytesWaiting > 0)
{
memset(buf, 0, sizeof(buf));
retval = monocoque_serial_read_block(serialdevice->id, buf, sizeof(buf)-1, 10);
if (retval < 0)
{
retval = -1;
break;
}
else
{
for(i=0; i<retval; i++)
{
if (buf[i] == 13)
{
count++;
}
}
int ret = atoi(buf);
*numlights = ret;
return retval;
}
}
else
if (bytesWaiting < 0)
{
sloge("Error getting bytes available from serial port: %d", bytesWaiting);
retval = -1;
break;
}
retval = 0;
}
return retval;
}
int arduino_init(SerialDevice* serialdevice, const char* portdev)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
return serialdevice->id;
}
int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool uselights)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
int numlights = 0;
monocoque_serial_device serialdev = monocoque_serial_devices[serialdevice->id];
int error = 0;
if(uselights == true)
{
error = GetNumberOfLeds(serialdevice, &numlights);
slogi("numlights is %i\n", numlights);
// close, error and free if numlights is 0
serialdevice->numleds = numlights;
}
if(luafile == NULL)
{
return serialdevice->id;
}
slogi("LUA config specified for this device... initializing...");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
int top=lua_gettop(L);
int status = luaL_loadfile(L, luafile);
if (status) {
/* If something went wrong, error message is at the top of */
/* the stack */
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
lua_close(serialdevice->m.L);
return -1;
exit(1);
}
lua_setglobal(L,"myFunc");
serialdevice->m.L = L;
slogi("LUA config setup successful.");
return serialdevice->id;
}
int arduino_simled_update(SerialDevice* serialdevice, SimData* simdata)
{
int result = 1;
int total_leds = serialdevice->numleds;
size_t bufsize = (total_leds * 3) + 14;
char bytes[bufsize];
int endled = serialdevice->endled;
int startled = serialdevice->startled;
if(endled == 0)
{
endled = total_leds;
}
int num_avail_leds = endled - startled + 1;
int rpm = simdata->rpms;
int maxrpm = simdata->maxrpm;
int litleds = 0;
if(rpm > 0 && maxrpm > 0)
{
int rpmmargin = ceil(.05*maxrpm);
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds);
for (int l = 1; l <= (num_avail_leds); l++)
{
if(rpm >= (rpminterval * l))
{
litleds = l;
}
}
for(int j = 0; j < bufsize; j++)
{
bytes[j] = 0x00;
}
bytes[0] = 0xff;
bytes[1] = 0xff;
bytes[2] = 0xff;
bytes[3] = 0xff;
bytes[4] = 0xff;
bytes[5] = 0xff;
bytes[6] = 0x73;
bytes[7] = 0x6c;
bytes[8] = 0x65;
bytes[9] = 0x64;
bytes[10] = 0x73;
bytes[bufsize-1] = 0xfd;
bytes[bufsize-2] = 0xfe;
bytes[bufsize-3] = 0xff;
for(int i = 0; i < litleds; i++)
{
if(i < ((num_avail_leds) / 2))
{
//green
bytes[11 + ((i + startled - 1) * 3) + 1] = 0xff;
}
else
{
if(i < num_avail_leds - 1)
{
//yellow
bytes[11 + ((i + startled - 1) * 3) + 0] = 0xff;
bytes[11 + ((i + startled - 1) * 3) + 1] = 0xff;
}
}
if(i == num_avail_leds - 1)
{
//red
bytes[11 + ((i + startled - 1) * 3) + 0] = 0xff;
}
}
}
slogt("Updating arduino device lights to %i", litleds);
// we can add configs to set all the colors
size_t size = sizeof(bytes);
result = monocoque_serial_write(serialdevice->id, &bytes, size, arduino_timeout);
return result;
}
int arduino_customled_update(SerialDevice* serialdevice, SimData* simdata)
{
int result = 1;
int total_leds = serialdevice->numleds;
size_t bufsize = (total_leds * 3) + 14;
char ledbytes[total_leds * 3];
char bytes[bufsize];
for(int j = 0; j < bufsize; j++)
{
bytes[j] = 0x00;
}
for(int j = 0; j < total_leds * 3; j++)
{
ledbytes[j] = 0x00;
}
bytes[0] = 0xff;
bytes[1] = 0xff;
bytes[2] = 0xff;
bytes[3] = 0xff;
bytes[4] = 0xff;
bytes[5] = 0xff;
bytes[6] = 0x73;
bytes[7] = 0x6c;
bytes[8] = 0x65;
bytes[9] = 0x64;
bytes[10] = 0x73;
bytes[bufsize-1] = 0xfd;
bytes[bufsize-2] = 0xfe;
bytes[bufsize-3] = 0xff;
lua_State* L = serialdevice->m.L;
lua_pushstring(L, "buff");
lua_pushlightuserdata(L, &ledbytes);
lua_settable(L, LUA_REGISTRYINDEX);
simdata_to_lua(L, simdata);
lua_setglobal(L, "simdata");
lua_pushinteger(L, total_leds);
lua_setglobal(L, "TotalLeds");
lua_register(L, "set_led_to_color", set_led_to_color);
lua_register(L, "set_led_range_to_color", set_led_range_to_color);
lua_register(L, "set_led_to_rgb_color", set_led_to_rgb_color);
lua_register(L, "set_led_range_to_rgb_color", set_led_range_to_rgb_color);
lua_register(L, "led_clear_all", led_clear_all);
lua_register(L, "led_clear_range", led_clear_all);
lua_pushinteger(L, LUALEDCOLOR_RED);
lua_setglobal(L, "RED");
lua_pushinteger(L, LUALEDCOLOR_GREEN);
lua_setglobal(L, "GREEN");
lua_pushinteger(L, LUALEDCOLOR_BLUE);
lua_setglobal(L, "BLUE");
lua_pushinteger(L, LUALEDCOLOR_YELLOW);
lua_setglobal(L, "YELLOW");
lua_pushinteger(L, LUALEDCOLOR_ORANGE);
lua_setglobal(L, "ORANGE");
lua_getglobal(L,"myFunc");
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
}
for(int i = 0; i < total_leds; i++)
{
bytes[11 + (i * 3) + 0] = ledbytes[(i * 3) + 0];
bytes[11 + (i * 3) + 1] = ledbytes[(i * 3) + 1];
bytes[11 + (i * 3) + 2] = ledbytes[(i * 3) + 2];
}
size_t size = sizeof(bytes);
result = arduino_update(serialdevice, &bytes, size);
slogt("custom led wrote %i bytes", result);
return result;
}
int arduino_customled_free(SerialDevice* serialdevice, bool lua)
{
size_t bufsize = (serialdevice->numleds * 3) + 14;
char bytes[bufsize];
int endled = serialdevice->endled;
int startled = serialdevice->startled;
if(endled == 0)
for(int j = 0; j < bufsize; j++)
{
bytes[j] = 0x00;
}
bytes[0] = 0xff;
bytes[1] = 0xff;
bytes[2] = 0xff;
bytes[3] = 0xff;
bytes[4] = 0xff;
bytes[5] = 0xff;
bytes[6] = 0x73;
bytes[7] = 0x6c;
bytes[8] = 0x65;
bytes[9] = 0x64;
bytes[10] = 0x73;
bytes[bufsize-1] = 0xfd;
bytes[bufsize-2] = 0xfe;
bytes[bufsize-3] = 0xff;
for(int i = 0; i < serialdevice->numleds; i++)
{
bytes[11 + (i * 3) + 0] = 0x00;
bytes[11 + (i * 3) + 1] = 0x00;
bytes[11 + (i * 3) + 2] = 0x00;
}
size_t size = sizeof(bytes);
int result = monocoque_serial_write_block(serialdevice->id, &bytes, size, arduino_timeout);
if(lua == false)
{
return result;
}
lua_close(serialdevice->m.L);
return result;
}
int arduino_custom_update(SerialDevice* serialdevice, SimData* simdata)
{
size_t bufsize = 14;
char bytes[bufsize];
lua_State* L = serialdevice->m.L;
simdata_to_lua(L, simdata);
lua_setglobal(L, "simdata");
lua_getglobal(L,"myFunc");
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
}
lua_getglobal(L, "Message");
int result = 0;
if (lua_isstring(L, -1)) {
const char *msg = lua_tostring(L, -1);
size_t size = strlen(msg);
result = arduino_update(serialdevice, (void*) msg, size);
slogt("custom arduino wrote message %s of %i bytes", msg, result);
}
// Clean up
lua_pop(L, 1);
return result;
}

View File

@ -4,9 +4,14 @@
#include "../simdevice.h"
#include "../serialdevice.h"
int arduino_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_simled_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_update(SerialDevice* serialdevice, void* data, size_t size);
int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool useleds);
int arduino_customled_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_custom_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_init(SerialDevice* serialdevice, const char* portdev);
int arduino_free(SerialDevice* serialdevice);
int check(enum sp_return result);
int arduino_customled_free(SerialDevice* serialdevice, bool lua);
#endif

View File

@ -0,0 +1,430 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include "arduinoledlua.h"
#include "arduino.h"
#include "../../slog/slog.h"
long long ledTimeInMilliseconds(void) {
struct timeval tv;
gettimeofday(&tv,NULL);
return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
}
int simdata_to_lua(lua_State *L, SimData* simdata) {
// make the time up now if we are running in test mode
if(simdata->mtick == 0)
{
simdata->mtick = ledTimeInMilliseconds();
}
lua_newtable(L);
lua_pushstring(L, simdata->gearc);
lua_setfield(L, -2, "gearc");
lua_pushinteger(L, simdata->playerflag);
lua_setfield(L, -2, "playerflag");
lua_pushinteger(L, simdata->rpms);
lua_setfield(L, -2, "rpm");
lua_pushinteger(L, simdata->gear);
lua_setfield(L, -2, "gear");
lua_pushinteger(L, simdata->velocity);
lua_setfield(L, -2, "velocity");
lua_pushinteger(L, simdata->mtick);
lua_setfield(L, -2, "mtick");
lua_pushinteger(L, simdata->maxrpm);
lua_setfield(L, -2, "maxrpm");
lua_pushinteger(L, PROXCARS);
lua_setfield(L, -2, "proxcars");
lua_newtable(L);
for(int i = 0; i < PROXCARS; i++)
{
lua_newtable(L);
lua_pushinteger(L, simdata->pd[i].radius);
lua_setfield(L, -2, "radius");
lua_pushinteger(L, simdata->pd[i].theta);
lua_setfield(L, -2, "theta");
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "pd");
lua_pushnumber(L, simdata->gas);
lua_setfield(L, -2, "gas");
lua_pushnumber(L, simdata->fuel);
lua_setfield(L, -2, "fuel");
lua_pushnumber(L, simdata->turboboost);
lua_setfield(L, -2, "turboboost");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyreRPS[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyreRPS");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyrediameter[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyrediameter");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyretemp[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyretemp");
return 0; // Return the table to Lua
}
uint8_t get_color_rgb_value(int color, int rgb)
{
switch (color)
{
case LUALEDCOLOR_RED:
switch (rgb)
{
case 0:
return 255;
case 1:
return 0;
case 2:
return 0;
}
case LUALEDCOLOR_GREEN:
switch (rgb)
{
case 0:
return 0;
case 1:
return 255;
case 2:
return 0;
}
case LUALEDCOLOR_BLUE:
switch (rgb)
{
case 0:
return 0;
case 1:
return 0;
case 2:
return 255;
}
case LUALEDCOLOR_YELLOW:
switch (rgb)
{
case 0:
return 255;
case 1:
return 255;
case 2:
return 0;
}
case LUALEDCOLOR_ORANGE:
switch (rgb)
{
case 0:
return 255;
case 1:
return 165;
case 2:
return 0;
}
default:
return 0;
}
}
int set_led_range_to_color(lua_State *L)
{
slogt("lua called c function set_led_range_to_color");
int range_start = lua_tonumber(L, 1);
int range_end = lua_tonumber(L, 2);
int color = lua_tonumber(L, 3);
slogd("lua range start is %i", range_start);
slogd("lua range end is %i", range_end);
slogd("lua color is %i", color);
range_start = range_start - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(range_end > numlights)
{
range_end = numlights;
}
if(range_end == 0)
{
slogt("Invalid range, doing nothing");
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
uint8_t color0 = get_color_rgb_value(color, 0);
uint8_t color1 = get_color_rgb_value(color, 1);
uint8_t color2 = get_color_rgb_value(color, 2);
for( int i = range_start; i < range_end; i++)
{
bytes[(i * 3) + 0] = color0;
bytes[(i * 3) + 1] = color1;
bytes[(i * 3) + 2] = color2;
}
}
int set_led_range_to_rgb_color(lua_State *L)
{
slogt("lua called c function set_led_range_to_rgb_color");
int range_start = lua_tonumber(L, 1);
int range_end = lua_tonumber(L, 2);
int color = lua_tonumber(L, 3);
uint8_t color0 = (color >> 16) & 0xff;
uint8_t color1 = (color >> 8) & 0xff;
uint8_t color2 = (color >> 0) & 0xff;
//int color0 = lua_tonumber(L, 3);
//int color1 = lua_tonumber(L, 4);
//int color2 = lua_tonumber(L, 5);
slogd("lua range start is %i", range_start);
slogd("lua range end is %i", range_end);
slogd("lua color0 is %i", color0);
slogd("lua color1 is %i", color1);
slogd("lua color2 is %i", color2);
range_start = range_start - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(range_end > numlights)
{
range_end = numlights;
}
if(range_end == 0)
{
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
for( int i = range_start; i < range_end; i++)
{
bytes[(i * 3) + 0] = color0;
bytes[(i * 3) + 1] = color1;
bytes[(i * 3) + 2] = color2;
}
}
int set_led_to_color(lua_State *L)
{
slogt("lua called c function set_led_to_rgb_color");
int led = lua_tonumber(L, 1);
int color = lua_tonumber(L, 2);
slogd("lua led is %i", led);
slogd("lua color is %i", color);
led = led - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(led > numlights)
{
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
uint8_t color0 = get_color_rgb_value(color, 0);
uint8_t color1 = get_color_rgb_value(color, 1);
uint8_t color2 = get_color_rgb_value(color, 2);
bytes[(led * 3) + 0] = color0;
bytes[(led * 3) + 1] = color1;
bytes[(led * 3) + 2] = color2;
}
int set_led_to_rgb_color(lua_State *L)
{
slogt("lua called c function set_led_to_rgb_color");
int led = lua_tonumber(L, 1);
int color = lua_tonumber(L, 2);
uint8_t color0 = (color >> 16) & 0xff;
uint8_t color1 = (color >> 8) & 0xff;
uint8_t color2 = (color >> 0) & 0xff;
slogd("lua led is %i", led);
slogd("lua color0 is %i", color0);
slogd("lua color1 is %i", color1);
slogd("lua color2 is %i", color2);
led = led - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(led > numlights)
{
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
bytes[(led * 3) + 0] = color0;
bytes[(led * 3) + 1] = color1;
bytes[(led * 3) + 2] = color2;
}
int led_clear_all(lua_State *L)
{
slogt("lua called c function led_clear_all");
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
for( int i = 0; i < numlights; i++)
{
bytes[(i * 3) + 0] = 0x00;
bytes[(i * 3) + 1] = 0x00;
bytes[(i * 3) + 2] = 0x00;
}
}
int led_clear_range(lua_State *L)
{
slogt("lua called c function led_clear_range");
int range_start = lua_tonumber(L, 1);
int range_end = lua_tonumber(L, 2);
slogd("lua range start is %i", range_start);
slogd("lua range end is %i", range_end);
range_start = range_start - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(range_end > numlights)
{
range_end = numlights;
}
if(range_end == 0)
{
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
for( int i = 0; i < numlights; i++)
{
bytes[(i * 3) + 0] = 0x00;
bytes[(i * 3) + 1] = 0x00;
bytes[(i * 3) + 2] = 0x00;
}
}

View File

@ -0,0 +1,31 @@
#ifndef _ARDUINOLEDLUA_H
#define _ARDUINOLEDLUA_H
//#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#ifdef USE_LUA_55
/* open all libraries */
#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
#endif
#include "../../simulatorapi/simapi/simapi/simdata.h"
typedef enum {
LUALEDCOLOR_RED = 1,
LUALEDCOLOR_GREEN,
LUALEDCOLOR_BLUE,
LUALEDCOLOR_YELLOW,
LUALEDCOLOR_ORANGE,
} LUALEDColor;
int simdata_to_lua(lua_State *L, SimData* simdata);
int set_led_range_to_color(lua_State *L);
int set_led_to_color(lua_State *L);
int set_led_range_to_rgb_color(lua_State *L);
int set_led_to_rgb_color(lua_State *L);
int led_clear_all(lua_State *L);
int led_clear_range(lua_State *L);
#endif

View File

@ -0,0 +1,91 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <hidapi/hidapi.h>
#include "moza.h"
#include "../serialadapter.h"
#include "../../slog/slog.h"
#define MOZA_TIMEOUT 1000
#define MOZA_SERIAL_TEMPLATE {0x7e, 0x06, 0x41, 0x13, 0xfd, 0xde, 0x0, 0x0, 0x0, 0x0, 0x0}
#define MOZA_NUM_AVAILABLE_LEDS 10
#define MOZA_BLINKING_BIT 7
#define MOZA_PAYLOAD_SIZE 11
#define MOZA_MAGIC_VALUE 0x0d
#define BIT(nr) (1UL << (nr))
unsigned char moza_checksum(unsigned char *data, unsigned short size)
{
unsigned int ret = MOZA_MAGIC_VALUE;
for (unsigned short i = 0; i < size; i++)
{
ret += data[i];
}
return ret % 0x100;
}
int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short maxrpm)
{
unsigned char bytes[] = MOZA_SERIAL_TEMPLATE;
unsigned short size = MOZA_PAYLOAD_SIZE;
float perctflt = ((float)rpm/(float)maxrpm)*100;
int perct = round(perctflt);
if (perct >= 10)
bytes[9] |= BIT(0);
if (perct >= 20)
bytes[9] |= BIT(1);
if (perct >= 30)
bytes[9] |= BIT(2);
if (perct >= 40)
bytes[9] |= BIT(3);
if (perct >= 50)
bytes[9] |= BIT(4);
if (perct >= 60)
bytes[9] |= BIT(5);
if (perct >= 70)
bytes[9] |= BIT(6);
if (perct >= 80)
bytes[9] |= BIT(7);
if (perct >= 90)
bytes[8] |= BIT(0);
if (perct >= 92)
bytes[8] |= BIT(1);
// blinking
if (perct >= 94)
bytes[8] |= BIT(MOZA_BLINKING_BIT);
bytes[10] = moza_checksum(bytes, size);
int result = 1;
if (serialdevice->port)
{
slogd("copying %i bytes to moza device", MOZA_PAYLOAD_SIZE);
slogt("writing bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x from rpm %i maxrpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], rpm, maxrpm);
result = monocoque_serial_write(serialdevice->id, bytes, size, MOZA_TIMEOUT);
}
return result;
}
int moza_init(SerialDevice* serialdevice, const char* portdev)
{
int id = monocoque_serial_open(serialdevice, portdev);
if (id < 0) return id;
serialdevice->id = id;
return 0;
}

View File

@ -0,0 +1,11 @@
#ifndef _MOZA_H
#define _MOZA_H
#include "../serialdevice.h"
#include "../simdevice.h"
int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short maxrpm);
int moza_init(SerialDevice* serialdevice, const char* portdev);
unsigned char moza_checksum(unsigned char *data, unsigned short size);
#endif

View File

@ -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;
}

View File

@ -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

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;
}

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

View File

@ -0,0 +1,280 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "serialadapter.h"
#include "../slog/slog.h"
int msastrcicmp(char const *a, char const *b)
{
for (;; a++, b++) {
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
if (d != 0 || !*a)
return d;
}
}
int check(enum sp_return result)
{
char* error_message;
switch (result)
{
case SP_ERR_ARG:
return 1;
case SP_ERR_FAIL:
error_message = sp_last_error_message();
sloge("error: serial write failed: %s", error_message);
sp_free_error_message(error_message);
return result;
case SP_ERR_SUPP:
printf("Error: Not supported.\n");
return result;
case SP_ERR_MEM:
printf("Error: Couldn't allocate memory.\n");
return result;
case SP_OK:
default:
return result;
}
}
int monocoque_serial_free(SerialDevice* serialdevice)
{
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevice->id];
if(monocoque_serial_dev.open == true)
{
while(monocoque_serial_dev.busy == true)
{
slogt("hopefully this doesn't happen long");
continue;
}
monocoque_serial_devices[serialdevice->id].busy = true;
monocoque_serial_devices[serialdevice->id].refs--;
if(monocoque_serial_devices[serialdevice->id].refs == 0)
{
slogd("freeing physical device %s", monocoque_serial_devices[serialdevice->id].portname);
sp_close(monocoque_serial_devices[serialdevice->id].port);
sp_free_port(monocoque_serial_devices[serialdevice->id].port);
free(monocoque_serial_devices[serialdevice->id].portname);
monocoque_serial_devices[serialdevice->id].port = NULL;
monocoque_serial_devices[serialdevice->id].portname = NULL;
monocoque_serial_devices[serialdevice->id].busy = false;
monocoque_serial_devices[serialdevice->id].open = false;
monocoque_serial_devices[serialdevice->id].openfail = false;
}
else
{
monocoque_serial_devices[serialdevice->id].busy = false;
}
}
}
int monocoque_wait_for_event(uint8_t serialdevicenum, int event)
{
slogt("serial device id %i", serialdevicenum);
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
int retval;
struct sp_event_set* eventSet = NULL;
retval = sp_new_event_set(&eventSet);
if (retval == SP_OK)
{
retval = sp_add_port_events(eventSet, monocoque_serial_dev.port, event);
if (retval == SP_OK)
{
slogd("set event on port");
retval = sp_wait(eventSet, 5000);
}
else
{
sloge("Unable to add events to port.");
retval = -1;
}
}
else
{
sloge("Unable to create new event set.");
retval = -1;
}
sp_free_event_set(eventSet);
return retval;
}
int monocoque_input_wait(uint8_t serialdevicenum)
{
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
return sp_input_waiting(monocoque_serial_dev.port);
}
// Helper function to get and validate device
static monocoque_serial_device* monocoque_get_serial_device(uint8_t serialdevicenum)
{
monocoque_serial_device* dev = &monocoque_serial_devices[serialdevicenum];
slogt("serial device id %i", serialdevicenum);
slogt("port name: %s, busy %i, open %i, openfail %i", dev->portname, dev->busy, dev->open, dev->openfail);
if(dev->port == NULL)
{
sloge("port is null");
}
return dev;
}
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1;
if(dev->busy == false && dev->open == true)
{
dev->busy = true;
result = sp_blocking_write(dev->port, data, size, timeout);
}
else
{
slogw("serial device data update ignored due to busy or lost device");
result = -1;
}
slogt("write result is %i", result);
dev->busy = false;
return result;
}
int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1;
if(dev->open == true)
{
while(dev->busy == true)
{
slogt("hopefully this doesn't happen long");
continue;
}
dev->busy = true;
result = sp_blocking_write(dev->port, data, size, timeout);
slogi("actually performed write");
}
dev->busy = false;
return result;
}
int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1;
if(dev->open == true)
{
while(dev->busy == true)
{
slogt("hopefully this doesn't happen long");
continue;
}
dev->busy = true;
result = sp_blocking_read(dev->port, data, size, timeout);
slogi("actually performed read");
}
dev->busy = false;
return result;
}
int monocoque_serial_open(SerialDevice* serialdevice, const char* portdev)
{
int serial_device_num = -1;
bool notfound = true;
slogi("looking to open physical serialdevice %s", portdev);
for(int i = 0; i < 10; i++)
{
if(monocoque_serial_devices[i].open == true && monocoque_serial_devices[i].openfail == false)
{
if(msastrcicmp(monocoque_serial_devices[i].portname, portdev) == 0)
{
notfound = false;
serial_device_num = i;
monocoque_serial_devices[i].refs++;
slogd("found exisiting handle to serial device %s", portdev);
}
}
}
if(notfound == true)
{
slogd("no existing connections found, looking to create new");
int i = -1;
bool avail = false;
while(avail == false)
{
i++;
if(monocoque_serial_devices[i].open == false && monocoque_serial_devices[i].openfail == false)
{
avail = true;
break;
}
}
slogi("opening physical serial device...");
int error = 0;
char* port_name = strdup(portdev);
monocoque_serial_devices[i].portname = strdup(port_name);
struct sp_port* sp;
slogd("Looking for port %s", port_name);
error = check(sp_get_port_by_name(port_name, &sp));
if (error != 0)
{
sloge("Error opening serial port");
free(port_name);
free(monocoque_serial_devices[i].portname);
monocoque_serial_devices[i].portname = NULL;
monocoque_serial_devices[i].open = false;
monocoque_serial_devices[i].openfail = false;
return -1;
}
slogd("Opening port");
check(sp_open(sp, SP_MODE_READ | SP_MODE_WRITE));
slogd("Setting port to %i 8N1, no flow control", serialdevice->baudrate);
check(sp_set_baudrate(sp, serialdevice->baudrate));
check(sp_set_bits(sp, 8));
check(sp_set_parity(sp, SP_PARITY_NONE));
check(sp_set_stopbits(sp, 1));
check(sp_set_flowcontrol(sp, SP_FLOWCONTROL_NONE));
check(sp_set_rts(sp, 1));
check(sp_set_dtr(sp, 1));
monocoque_serial_devices[i].port = sp;
monocoque_serial_devices[i].open = true;
monocoque_serial_devices[i].openfail = false;
monocoque_serial_devices[i].busy = false;
monocoque_serial_devices[i].refs++;
serial_device_num = i;
free(port_name);
slogd("Successfully setup monocoque serial device...");
}
return serial_device_num;
}

View File

@ -0,0 +1,31 @@
#ifndef _SERIALADAPTER_H
#define _SERIALADAPTER_H
#include <stdint.h>
#include <stdbool.h>
#include <libserialport.h>
#include "simdevice.h"
typedef struct
{
char* portname;
struct sp_port* port;
uint8_t refs;
bool open;
bool openfail;
bool busy;
}
monocoque_serial_device;
static monocoque_serial_device monocoque_serial_devices[20];
int monocoque_input_wait(uint8_t serialdevicenum);
int monocoque_wait_for_event(uint8_t serialdevicenum, int event);
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout);
int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout);
int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout);
int monocoque_serial_open(SerialDevice* serialdevice, const char* port);
int monocoque_serial_free(SerialDevice* serialdevice);
#endif

View File

@ -6,45 +6,286 @@
#include "simdevice.h"
#include "serialdevice.h"
#include "serialadapter.h"
#include "hapticeffect.h"
#include "serial/arduino.h"
#include "serial/moza.h"
#include "serial/moza_new.h"
#include "serial/moza_ks_pro_wheel.h"
#include "../helper/parameters.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../slog/slog.h"
#define KPHTOMPH .621317
int serial_wheel_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
switch (serialdevice->devicetype) {
case SIMDEVSUBTYPE_MOZA_NEW:
moza_new_update(serialdevice, simdata);
break;
case SERIALDEV__MOZA_KS_PRO_WHEEL:
moza_ks_pro_wheel_update(serialdevice, simdata);
break;
case SIMDEVSUBTYPE_MOZAR5:
default:
moza_update(serialdevice, simdata->rpms, simdata->maxrpm);
break;
}
return 0;
}
int serialdev_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
arduino_update(serialdevice, simdata);
arduino_update(serialdevice, simdata, sizeof(SimData));
return 0;
}
int arduino_custom_updater(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
arduino_custom_update(serialdevice, simdata);
return 0;
}
int arduino_customled_updater(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
arduino_customled_update(serialdevice, simdata);
return 0;
}
int arduino_simled_updater(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
arduino_simled_update(serialdevice, simdata);
return 0;
}
int arduino_shiftlights_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
int result = 1;
int num_avail_leds = serialdevice->numlights;
int rpm = simdata->rpms;
int maxrpm = simdata->maxrpm;
int litleds = 0;
if(rpm > 0 && maxrpm > 0)
{
int rpmmargin = ceil(.05*maxrpm);
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds);
for (int l = 1; l <= (num_avail_leds); l++)
{
if(rpm >= (rpminterval * l))
{
litleds = l;
}
}
}
serialdevice->u.shiftlightsdata.litleds = litleds;
//serialdevice->u.shiftlightsdata.rpm = simdata->rpms;
slogt("Updating arduino device lights to %i", serialdevice->u.shiftlightsdata.litleds);
// we can add configs to set all the colors
// i can move the size to the initialization since it should not change
size_t size = sizeof(ShiftLightsData);
arduino_update(serialdevice, &serialdevice->u.shiftlightsdata, size);
return result;
}
int arduino_simwind_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
int result = 1;
// sending over as mph, when you consider 255mph to be a reasonable upper limit
serialdevice->u.simwinddata.velocity = ceil(simdata->velocity*KPHTOMPH);
slogt("Updating arduino device speed to %i", serialdevice->u.simwinddata.velocity);
serialdevice->u.simwinddata.fanpower = (int)(serialdevice->fanpower * 255);
slogt("Sending fanpower: %i (from config: %f)", serialdevice->u.simwinddata.fanpower, serialdevice->fanpower);
size_t size = sizeof(SimWindData);
arduino_update(serialdevice, &serialdevice->u.simwinddata, size);
return result;
}
int arduino_simhaptic_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
int result = 1;
slogt("arduino haptic device updating");
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
double rplay = play;
play = play * serialdevice->ampfactor;
if(play > 1.0)
{
play = 1.0;
}
int effectspeed = ceil(255 * play);
int motor = serialdevice->motorsposition;
if (play != serialdevice->state)
{
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);
}
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);
}
serialdevice->state = play;
}
size_t size = sizeof(SimHapticData);
arduino_update(serialdevice, &serialdevice->u.simhapticdata, size);
return result;
}
int serialdev_free(SimDevice* this)
{
SerialDevice* serialdevice = (void *) this->derived;
arduino_free(serialdevice);
switch (serialdevice->devicetype) {
case (ARDUINODEV__HAPTIC):
serialdevice->u.simhapticdata.effect1 = 0;
serialdevice->u.simhapticdata.motor1 = 1;
serialdevice->u.simhapticdata.effect2 = 0;
serialdevice->u.simhapticdata.motor2 = 1;
serialdevice->u.simhapticdata.effect3 = 0;
serialdevice->u.simhapticdata.motor3 = 1;
serialdevice->u.simhapticdata.effect4 = 0;
serialdevice->u.simhapticdata.motor4 = 1;
serialdevice->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);
free(serialdevice->m.device_specific_config_file);
break;
case ARDUINODEV__SIMLED:
arduino_customled_free(serialdevice, false);
break;
}
monocoque_serial_free(serialdevice);
free(serialdevice);
return 0;
}
int serialdev_init(SerialDevice* serialdevice, const char* portdev)
int serial_wheel_free(SimDevice* this)
{
slogi("initializing serial device...");
SerialDevice* serialdevice = (void *) this->derived;
monocoque_serial_free(serialdevice);
free(serialdevice);
return 0;
}
int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds, SimInfo* siminfo)
{
slogi("initializing serial device on port %s to %i...", ds->serialdevsettings.portdev, ds->serialdevsettings.baud);
int error = 0;
serialdevice->type = SERIALDEV_UNKNOWN;
serialdevice->type = SERIALDEV_ARDUINO;
error = arduino_init(serialdevice, portdev);
serialdevice->motorsposition = ds->serialdevsettings.motorsposition;
serialdevice->baudrate = ds->serialdevsettings.baud;
switch (serialdevice->devicetype)
{
case SERIALDEV__MOZAR5:
// the wheel stuff assumed it was a usb
//error = wheeldev_init(&serialdevice->u.wheeldevice, ds);
// maybe this call a more generic serial wheel init first
error = moza_init(serialdevice, ds->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:
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);
}
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);
}
break;
case ARDUINODEV__SIMLED:
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, NULL, false);
break;
default:
error = arduino_init(serialdevice, ds->serialdevsettings.portdev);
break;
}
return error;
}
static const vtable serial_simdevice_vtable = { &serialdev_update, &serialdev_free };
static const vtable arduino_shiftlights_vtable = { &arduino_shiftlights_update, &serialdev_free };
static const vtable arduino_simled_vtable = { &arduino_simled_updater, &serialdev_free };
static const vtable arduino_custom_vtable = { &arduino_custom_updater, &serialdev_free };
static const vtable arduino_simled_custom_vtable = { &arduino_customled_updater, &serialdev_free };
static const vtable arduino_simwind_vtable = { &arduino_simwind_update, &serialdev_free };
static const vtable arduino_simhaptic_vtable = { &arduino_simhaptic_update, &serialdev_free };
static const vtable serialwheel_vtable = { &serial_wheel_update, &serial_wheel_free };
SerialDevice* new_serial_device(DeviceSettings* ds) {
SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
SerialDevice* this = (SerialDevice*) malloc(sizeof(SerialDevice));
@ -52,11 +293,113 @@ SerialDevice* new_serial_device(DeviceSettings* ds) {
this->m.free = &simdevfree;
this->m.derived = this;
this->m.vtable = &serial_simdevice_vtable;
this->type = SERIALDEV_ARDUINO;
int error = 0;
int error = serialdev_init(this, ds->serialdevsettings.portdev);
slogt("Attempting to configure arduino device with subtype: %i", ds->dev_subtype);
switch (ds->dev_subtype) {
case (SIMDEVTYPE_SHIFTLIGHTS):
this->devicetype = ARDUINODEV__SHIFTLIGHTS;
this->numlights = ds->serialdevsettings.numlights;
this->m.vtable = &arduino_shiftlights_vtable;
slogi("Initializing arduino device for shiftlights.");
break;
case (SIMDEVTYPE_SIMLED):
if(ds->has_config == true)
{
this->devicetype = ARDUINODEV__SIMLED__CUSTOM;
this->startled = ds->serialdevsettings.startled;
this->endled = ds->serialdevsettings.endled;
this->m.vtable = &arduino_simled_custom_vtable;
slogi("Initializing arduino device for custom simled.");
}
else
{
this->devicetype = ARDUINODEV__SIMLED;
this->numleds = ds->serialdevsettings.numleds;
this->startled = ds->serialdevsettings.startled;
this->endled = ds->serialdevsettings.endled;
this->m.vtable = &arduino_simled_vtable;
slogi("Initializing arduino device for simled.");
}
break;
case (SIMDEVTYPE_SIMWIND):
this->devicetype = ARDUINODEV__SIMWIND;
this->m.vtable = &arduino_simwind_vtable;
this->fanpower = ds->serialdevsettings.fanpower;
slogi("Initializing arduino devices for sim wind with fanpower: %f", this->fanpower);
break;
case (SIMDEVTYPE_ARDUINOCUSTOM):
this->devicetype = ARDUINODEV__CUSTOM;
this->m.vtable = &arduino_custom_vtable;
slogi("Initializing custom arduino device.");
break;
case (SIMDEVTYPE_SERIALHAPTIC):
if(siminfo->SimSupportsHapticEffects == false)
{
error = MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
}
this->devicetype = ARDUINODEV__HAPTIC;
this->m.vtable = &arduino_simhaptic_vtable;
this->u.simhapticdata.motor1 = 0;
this->u.simhapticdata.motor2 = 0;
this->u.simhapticdata.motor3 = 0;
this->u.simhapticdata.motor4 = 0;
this->u.simhapticdata.effect1 = 0;
this->u.simhapticdata.effect2 = 0;
this->u.simhapticdata.effect3 = 0;
this->u.simhapticdata.effect4 = 0;
this->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;
switch (ds->dev_subsubtype) {
case SIMDEVSUBTYPE_MOZA_NEW:
slogi("Initializing new firmware Moza serial wheel device.");
this->devicetype = SERIALDEV__MOZA_NEW;
this->m.vtable = &serialwheel_vtable;
break;
case SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL:
slogi("Initializing new firmware Moza serial wheel device.");
this->devicetype = SERIALDEV__MOZA_KS_PRO_WHEEL;
this->m.vtable = &serialwheel_vtable;
break;
case SIMDEVSUBTYPE_MOZAR5:
default:
//move this stuff to wheeldevice or it's own serial wheel device module
slogi("Initializing Moza serial wheel device.");
this->devicetype = SERIALDEV__MOZAR5;
this->m.vtable = &serialwheel_vtable;
break;
}
}
if(this->devicetype == ARDUINODEV__HAPTIC && error == 0)
{
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.effecttype = ds->effect_type;
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;
}
if(error == 0)
{
slogi("Starting serial device initialization");
error = serialdev_init(this, ds, siminfo);
}
if (error != 0)
{
slogw("Did not initialize usb device due to error code %i", error);
free(this);
return NULL;
}

View File

@ -2,6 +2,20 @@
#define _SERIALDEVICE_H
#include <libserialport.h>
#include "wheeldevice.h"
typedef enum
{
ARDUINODEV__SHIFTLIGHTS = 0,
ARDUINODEV__SIMWIND = 1,
ARDUINODEV__HAPTIC = 2,
SERIALDEV__MOZAR5 = 3,
ARDUINODEV__SIMLED = 4,
ARDUINODEV__SIMLED__CUSTOM = 5,
ARDUINODEV__CUSTOM = 6,
SERIALDEV__MOZA_NEW = 7,
SERIALDEV__MOZA_KS_PRO_WHEEL = 8
}
SerialDeviceType;
#endif

View File

@ -24,30 +24,9 @@ int devupdate(SimDevice* this, SimData* simdata)
return 0;
}
int devfree(SimDevice* simdevices, int numdevices)
int devinit(SimDevice* simdevices, SimInfo* siminfo, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms)
{
slogi("freeing %i simdevices...", numdevices);
int devices = 0;
int freed = 0;
for (int j = 0; j < 1; j++)
{
SimDevice simdev = simdevices[j];
if (simdev.initialized == true)
{
simdev.free(&simdev);
}
}
//free(simdevices);
return 0;
}
int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds)
{
slogi("initializing simdevices...");
slogi("initializing simdevices for simapi %i...", siminfo->simulatorapi);
int devices = 0;
for (int j = 0; j < numdevices; j++)
@ -55,12 +34,13 @@ int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds)
simdevices[j].initialized = false;
if (ds[j].dev_type == SIMDEV_USB) {
USBDevice* sim = new_usb_device(&ds[j]);
USBDevice* sim = new_usb_device(&ds[j], ms, siminfo);
if (sim != NULL)
{
simdevices[j] = sim->m;
simdevices[j].initialized = true;
simdevices[j].type = SIMDEV_USB;
simdevices[j].fps = ds[j].fps;
devices++;
}
else
@ -70,14 +50,20 @@ int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds)
}
if (ds[j].dev_type == SIMDEV_SOUND) {
SoundDevice* sim = new_sound_device(&ds[j]);
if(ms->disable_audio == true)
{
slogi("skipping configured sound device due to disable_audio being specified...");
}
else
{
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
@ -85,15 +71,17 @@ int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds)
slogw("Could not initialize Sound Device");
}
}
}
if (ds[j].dev_type == SIMDEV_SERIAL) {
SerialDevice* sim = new_serial_device(&ds[j]);
SerialDevice* sim = new_serial_device(&ds[j], ms, siminfo);
if (sim != NULL)
{
simdevices[j] = sim->m;
simdevices[j].initialized = true;
simdevices[j].type = SIMDEV_SERIAL;
simdevices[j].fps = ds[j].fps;
devices++;
}

View File

@ -2,13 +2,23 @@
#define _SIMDEVICE_H
#include <stdbool.h>
#include <hidapi/hidapi.h>
#include <lua.h>
#include "usbdevice.h"
#include "sounddevice.h"
#include "serialdevice.h"
#include "hapticeffect.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../../arduino/simwind/simwind.h"
#include "../../arduino/simhaptic/simhaptic.h"
#include "../../arduino/shiftlights/shiftlights.h"
typedef struct SimDevice SimDevice;
struct SimDevice
@ -18,9 +28,12 @@ struct SimDevice
int (*free)(SimDevice*);
void* derived;
int id;
int fps;
bool initialized;
lua_State* L;
char* device_specific_config_file;
DeviceType type;
HapticEffect hapticeffect;
};
typedef struct {
@ -32,7 +45,8 @@ typedef struct {
typedef enum
{
SERIALDEV_UNKNOWN = 0,
SERIALDEV_ARDUINO = 1
SERIALDEV_ARDUINO = 1,
SERIALDEV_WHEEL = 2,
}
SerialType;
@ -42,19 +56,42 @@ typedef struct
int id;
SerialType type;
struct sp_port* port;
SerialDeviceType devicetype;
// move these two they only apply to the haptic device
int motorsposition;
int numlights;
int numleds;
int startled;
int endled;
int baudrate;
double ampfactor;
double fanpower;
double state;
union
{
SimWindData simwinddata;
SimHapticData simhapticdata;
ShiftLightsData shiftlightsdata;
WheelDevice wheeldevice;
} u;
}
SerialDevice;
int serialdev_update(SimDevice* this, SimData* simdata);
int arduino_shiftlights_update(SimDevice* this, SimData* simdata);
int arduino_simwind_update(SimDevice* this, SimData* simdata);
int arduino_simhaptic_update(SimDevice* this, SimData* simdata);
int serialdev_free(SimDevice* this);
SerialDevice* new_serial_device(DeviceSettings* ds);
SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/********* USB HID Devices *****/
typedef enum
{
USBDEV_UNKNOWN = 0,
USBDEV_TACHOMETER = 1
USBDEV_TACHOMETER = 1,
USBDEV_GENERICHAPTIC = 2,
USBDEV_WHEEL = 3
}
USBType;
@ -62,10 +99,13 @@ typedef struct
{
SimDevice m;
int id;
hid_device* handle;
USBType type;
union
{
TachDevice tachdevice;
WheelDevice wheeldevice;
USBGenericHapticDevice hapticdevice;
} u;
}
USBDevice;
@ -73,7 +113,7 @@ USBDevice;
int usbdev_update(SimDevice* this, SimData* simdata);
int usbdev_free(SimDevice* this);
USBDevice* new_usb_device(DeviceSettings* ds);
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/********* Sound Devices *****/
@ -81,19 +121,24 @@ typedef struct
{
SimDevice m;
int id;
SoundType type;
VibrationEffectType effecttype;
PATestData sounddata;
PaStreamParameters outputParameters;
PaStream* stream;
int configcheck;
SoundEffectModulationType modulationType;
SoundData sounddata;
//#ifdef USE_PULSEAUDIO
pa_stream *stream;
//#else
//PaStreamParameters outputParameters;
//PaStream* stream;
//#endif
}
SoundDevice;
int sounddev_engine_update(SimDevice* this, SimData* simdata);
int sounddev_gearshift_update(SimDevice* this, SimData* simdata);
int sounddev_tyreslip_update(SimDevice* this, SimData* simdata);
int sounddev_free(SimDevice* this);
SoundDevice* new_sound_device(DeviceSettings* ds);
SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/***** Generic Methods *********/
@ -101,10 +146,22 @@ int update(SimDevice* simdevice, SimData* simdata);
int devupdate(SimDevice* simdevice, SimData* simdata);
int devinit(SimDevice* simdevices, int numdevices, DeviceSettings* ds);
int devinit(SimDevice* simdevices, SimInfo* siminfo, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms);
int devfree(SimDevice* simdevices, int numdevices);
int simdevfree(SimDevice* this);
/****** USB SubTypes ****************/
int 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

View File

@ -0,0 +1,77 @@
#include "sound.h"
#include "../slog/slog.h"
pa_threaded_mainloop* mainloop;
pa_context* context;
void context_state_cb(pa_context* context, void* mainloop) {
pa_threaded_mainloop_signal(mainloop, 0);
}
int setupsound()
{
pa_mainloop_api *mainloop_api;
slogi("connecting pulseaudio...");
// Get a mainloop and its context
mainloop = pa_threaded_mainloop_new();
mainloop_api = pa_threaded_mainloop_get_api(mainloop);
context = pa_context_new(mainloop_api, "Monocoque");
pa_context_set_state_callback(context, &context_state_cb, mainloop);
// Lock the mainloop so that it does not run and crash before the context is ready
pa_threaded_mainloop_lock(mainloop);
// Start the mainloop
pa_threaded_mainloop_start(mainloop);
pa_context_connect(context, NULL, 0, NULL);
// Wait for the context to be ready
for(;;) {
pa_context_state_t context_state = pa_context_get_state(context);
if (context_state == PA_CONTEXT_READY) break;
pa_threaded_mainloop_wait(mainloop);
}
pa_threaded_mainloop_unlock(mainloop);
slogi("successfully connected pulseaudio...");
return 0;
}
int freesound()
{
if (mainloop) {
pa_threaded_mainloop_lock(mainloop);
}
if (context)
{
pa_context_unref(context);
slogt("freed pulseaudio context");
}
if (mainloop) {
pa_signal_done();
pa_threaded_mainloop_unlock(mainloop);
pa_threaded_mainloop_free(mainloop);
}
return 0;
}
//int setupsound()
//{
// slogi("connecting portaudio...");
//}
//
//
//int freesound()
//{
//
//}

View File

@ -0,0 +1,13 @@
#ifndef _SOUND_H
#define _SOUND_H
#include <pulse/pulseaudio.h>
extern pa_threaded_mainloop* mainloop;
extern pa_context* context;
int setupsound();
int freesound();
#endif

View File

@ -1,3 +1,5 @@
#ifndef USE_PULSEAUDIO
#include <stdio.h>
#include <string.h>
#include <math.h>
@ -10,6 +12,9 @@
#define SAMPLE_RATE (48000)
#define DURATION 4.0
#define AMPLITUDE .5
#ifndef M_PI
#define M_PI (3.14159265)
#endif
@ -22,39 +27,27 @@ int patestCallbackEngineRPM(const void* inputBuffer,
PaStreamCallbackFlags statusFlags,
void* userData)
{
PATestData* data = (PATestData*)userData;
SoundData* data = (SoundData*)userData;
float* out = (float*)outputBuffer;
memset(out, 0, framesPerBuffer * 2 * sizeof(float));
unsigned int i;
unsigned int n;
n = data->n;
//n = data->n;
(void) inputBuffer; /* Prevent unused argument warning. */
for( i=0; i<framesPerBuffer; i++,n++ )
{
float v = 0;
v = data->amp * sin (2 * M_PI * ((float) n) / (float) SAMPLE_RATE);
static double t = 0.0;
double sample = AMPLITUDE * 32767.0 * sin(2.0 * M_PI * data->curr_frequency * t);
if (n>=data->table_size)
{
n=0;
t += 1.0 / SAMPLE_RATE;
if (t >= DURATION) {
t = 0.0;
}
*out++ = sample;
*out++ = sample;
}
if (data->gear_sound_data > 0)
{
*out++ = 0;
*out++ = 0;
}
else
{
*out++ = v;
*out++ = v;
}
}
data->gear_sound_data = 0;
data->n=n;
return 0;
}
@ -66,39 +59,33 @@ int patestCallbackGearShift(const void* inputBuffer,
PaStreamCallbackFlags statusFlags,
void* userData)
{
PATestData* data = (PATestData*)userData;
SoundData* data = (SoundData*)userData;
float* out = (float*)outputBuffer;
memset(out, 0, framesPerBuffer * 2 * sizeof(float));
unsigned int i;
unsigned int n;
n = data->n;
//n = data->n;
(void) inputBuffer; /* Prevent unused argument warning. */
for( i=0; i<framesPerBuffer; i++,n++ )
{
float v = 0;
v = data->amp * sin (2 * M_PI * ((float) n) / (float) SAMPLE_RATE);
if (n>=data->table_size)
static double t = 0.0;
double sample = 0;
if (data->frequency>0)
{
n=0;
sample = AMPLITUDE * 32767.0 * sin(2.0 * M_PI * data->curr_frequency * data->curr_duration);
}
if(data->gear_sound_data > 0)
{
*out++ = v;
*out++ = v;
}
else
{
*out++ = 0;
*out++ = 0;
}
}
data->gear_sound_data = 0;
data->n=n;
return 0;
data->curr_duration += 1.0 / SAMPLE_RATE;
if (data->curr_duration >= data->duration) {
data->curr_duration = 0.0;
data->curr_frequency = 0.0;
}
*out++ = sample;
*out++ = sample;
}
}
int usb_generic_shaker_free(SoundDevice* sounddevice)
@ -129,7 +116,7 @@ int usb_generic_shaker_init(SoundDevice* sounddevice)
sounddevice->outputParameters.suggestedLatency = Pa_GetDeviceInfo( sounddevice->outputParameters.device )->defaultLowOutputLatency;
sounddevice->outputParameters.hostApiSpecificStreamInfo = NULL;
if (sounddevice->effecttype == SOUNDEFFECT_GEARSHIFT)
if (sounddevice->m.hapticeffect.effecttype == EFFECT_GEARSHIFT)
{
err = Pa_OpenStream( &sounddevice->stream,
NULL, /* No input. */
@ -169,3 +156,5 @@ error:
Pa_Terminate();
return err;
}
#endif

View File

@ -1,10 +1,14 @@
#ifndef _USB_GENERIC_SHAKER_H
#define _USB_GENERIC_SHAKER_H
//#include "../sounddevice.h"
#include "../simdevice.h"
int usb_generic_shaker_init(SoundDevice* sounddevice);
int usb_generic_shaker_free(SoundDevice* sounddevice);
//#ifdef USE_PULSEAUDIO
int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop, pa_context* context, const char* devname, int volume, int pan, int channels, const char* streamname);
int usb_generic_shaker_free(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop);
//#else
//int usb_generic_shaker_init(SoundDevice* sounddevice);
//int usb_generic_shaker_free(SoundDevice* sounddevice);
//#endif
#endif

View File

@ -0,0 +1,231 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include "usb_generic_shaker.h"
#include "../sounddevice.h"
#define FORMAT PA_SAMPLE_S16LE
#define SAMPLE_RATE (44100)
#define AMPLITUDE 1
#define DURATION 1.0
#ifndef M_PI
#define M_PI (3.14159265)
#endif
static double apply_noise(double base_freq, double noise_amount) {
if (noise_amount > 0.0) {
double r = (double) rand() / RAND_MAX * 2.0 - 1.0;
return base_freq + r * noise_amount;
} else {
return base_freq;
}
}
void gear_sound_stream(pa_stream *s, size_t length, void *userdata) {
SoundData* data = (SoundData*)userdata;
size_t num_samples = length / sizeof(int16_t);
int16_t buffer[num_samples];
for (size_t i = 0; i < num_samples; i++) {
double sample = 0;
if (data->curr_frequency>0.0)
{
double t = data->phase;
double a = (double)data->curr_amplitude/100;
// fade-out 5ms to prevent discontinuity
static double fade_out_duration = 0.005;
double remaining = data->duration - data->curr_duration;
if (remaining < fade_out_duration) {
a *= remaining / fade_out_duration;
}
sample = a * 32767.0 * sin(2.0 * M_PI * t);
double f = apply_noise(data->curr_frequency, data->noise);
t += f / SAMPLE_RATE;
if (t >= 1.0) {
t -= floor(t);
}
data->phase = t;
data->curr_duration += 1.0 / SAMPLE_RATE;
if (data->curr_duration >= data->duration) {
data->curr_duration = 0.0;
data->curr_frequency = 0.0;
data->phase = 0.0;
}
}
buffer[i] = (int16_t)sample;
}
pa_stream_write(s, buffer, length, NULL, 0LL, PA_SEEK_RELATIVE);
}
void engine_sound_stream(pa_stream *s, size_t length, void *userdata) {
SoundData* data = (SoundData*)userdata;
size_t num_samples = length / sizeof(int16_t);
//size_t num_samples = (size_t) (DURATION * SAMPLE_RATE);
int16_t buffer[num_samples];
//double t = 0;
for (size_t i = 0; i < num_samples; i++) {
double t = data->phase;
double sample = ((double)data->curr_amplitude/100) * 32767.0 * sin( 2.0 * M_PI * t );
buffer[i] = (int16_t)sample;
double f = apply_noise(data->curr_frequency, data->noise);
t += f / SAMPLE_RATE;
if (t >= 1.0) {
t -= floor(t);
}
data->phase = t;
}
pa_stream_write(s, buffer, length, NULL, 0LL, PA_SEEK_RELATIVE);
}
void stream_success_cb(pa_stream *stream, int success, void *userdata) {
return;
}
void stream_state_cb(pa_stream *s, void *mainloop) {
pa_threaded_mainloop_signal(mainloop, 0);
}
int usb_generic_shaker_free(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop)
{
if(!mainloop)
{
// if this happens we are in trouble
return 1;
}
pa_threaded_mainloop_lock(mainloop);
int err = 0;
if (sounddevice->stream)
{
pa_stream_disconnect(sounddevice->stream);
pa_stream_unref(sounddevice->stream);
// why is this wrong
}
pa_threaded_mainloop_unlock(mainloop);
return err;
}
int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop, pa_context* context, const char* devname, int volume, int pan, int channels, const char* streamname)
{
pa_threaded_mainloop_lock(mainloop);
pa_stream *stream;
// Create a playback stream
pa_sample_spec sample_specifications;
sample_specifications.format = FORMAT;
sample_specifications.rate = SAMPLE_RATE;
sample_specifications.channels = channels;
pa_channel_map channel_map;
pa_channel_map_init_auto(&channel_map, channels, PA_CHANNEL_MAP_DEFAULT);
//pa_channel_map_init_stereo(&channel_map);
// not sure about what to do here
if(channels == 2)
{
pa_channel_map_parse(&channel_map, "front-left,front-right");
}
if(channels == 4)
{
pa_channel_map_parse(&channel_map, "front-left,front-right,rear-left,rear-right");
}
if(channels == 6)
{
pa_channel_map_parse(&channel_map, "front-left,front-right,front-center,lfe,rear-left,rear-right");
}
if(channels == 8)
{
pa_channel_map_parse(&channel_map, "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right");
}
stream = pa_stream_new(context, streamname, &sample_specifications, &channel_map);
pa_stream_set_state_callback(stream, stream_state_cb, mainloop);
if (sounddevice->m.hapticeffect.effecttype == EFFECT_GEARSHIFT)
{
pa_stream_set_write_callback(stream, gear_sound_stream, &sounddevice->sounddata);
}
else
{
pa_stream_set_write_callback(stream, engine_sound_stream, &sounddevice->sounddata);
}
// recommended settings, i.e. server uses sensible values
pa_buffer_attr buffer_attr;
buffer_attr.maxlength = (uint32_t) 32767;
buffer_attr.tlength = (uint32_t) 2048;
buffer_attr.prebuf = (uint32_t) -1;
buffer_attr.minreq = (uint32_t) -1;
pa_cvolume cv;
pa_cvolume_mute(&cv, channels);
pa_volume_t channel_volume = PA_CLAMP_VOLUME((pa_volume_t)((volume/100.d)*PA_VOLUME_NORM));
pa_stream_flags_t stream_flags;
stream_flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_NOT_MONOTONIC | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY | PA_STREAM_START_CORKED;
// for now i'm only supporting playing on one specified channel which is the concept you should build your setups around
cv.values[pan] = channel_volume;
pa_stream_connect_playback(stream, devname, &buffer_attr, stream_flags, &cv, NULL);
//pa_stream_connect_playback(stream, devname, &buffer_attr, stream_flags, &cv, NULL);
// Wait for the stream to be ready
for(;;)
{
pa_stream_state_t stream_state = pa_stream_get_state(stream);
PA_STREAM_IS_GOOD(stream_state);
//PA_STREAM_IS_GOOD(stream_state);
if (stream_state == PA_STREAM_READY) break;
pa_threaded_mainloop_wait(mainloop);
}
// Uncork the stream so it will start playing
pa_stream_cork(stream, 0, stream_success_cb, mainloop);
sounddevice->stream = stream;
pa_threaded_mainloop_unlock(mainloop);
return 0;
}

View File

@ -4,9 +4,12 @@
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include "sound.h"
#include "simdevice.h"
#include "sounddevice.h"
#include "hapticeffect.h"
#include "sound/usb_generic_shaker.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../helper/parameters.h"
@ -14,11 +17,44 @@
int gear_sound_set(SoundDevice* sounddevice, SimData* simdata)
{
if (sounddevice->sounddata.last_gear != simdata->gear && simdata->gear != 0)
if (sounddevice->sounddata.last_gear != simdata->gear && simdata->gear > 1)
{
sounddevice->sounddata.gear_sound_data = 3.14;
//sounddevice->sounddata.gear_sound_data = 3.14;
sounddevice->sounddata.curr_frequency = 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 %f", sounddevice->sounddata.curr_frequency);
}
double modulate(SoundDevice* sounddevice, double raw_effect, SoundEffectModulationType modulation)
{
double modulated_effect = raw_effect;
switch (modulation)
{
case SOUND_EFFECT_MODULATION_FREQUENCY:
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:
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
@ -26,9 +62,87 @@ int sounddev_engine_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
gear_sound_set(sounddevice, simdata);
double effect = ((double)simdata->rpms/60)/((double)simdata->maxrpm/60);
slogt("Set base effect of %f from rpms of %i", effect, simdata->rpms);
modulate(sounddevice, effect, sounddevice->modulationType);
}
int sounddev_tyreslip_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);
slogt("Updating sound device frequency from original tyre slip effect %f", effect);
if (effect > 0)
{
modulate(sounddevice, effect, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
int sounddev_tyrelock_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
slogt("Updating sound device frequency from original tyre lock effect %f", play);
if (play > 0)
{
modulate(sounddevice, play, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
int sounddev_absbrakes_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
slogt("Updating sound device frequency from original abs effect %f", play);
if (play > 0)
{
modulate(sounddevice, play, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
int sounddev_suspension_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double effect = slipeffect(simdata, this->hapticeffect.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;
}
sounddevice->sounddata.table_size = 44100/(simdata->rpms/60);
}
int sounddev_gearshift_update(SimDevice* this, SimData* simdata)
@ -38,67 +152,191 @@ int sounddev_gearshift_update(SimDevice* this, SimData* simdata)
gear_sound_set(sounddevice, simdata);
}
int sounddev_free(SimDevice* this)
{
SoundDevice* sounddevice = (void *) this->derived;
usb_generic_shaker_free(sounddevice);
usb_generic_shaker_free(sounddevice, mainloop);
free(sounddevice);
return 0;
}
int sounddev_init(SoundDevice* sounddevice)
int sounddev_init(SoundDevice* sounddevice, const char* devname, MonocoqueTyreIdentifier tyre, SoundDeviceSettings sds)
{
slogi("initializing standalone sound device...");
sounddevice->sounddata.pitch = 1;
sounddevice->sounddata.pitch = 261.626;
sounddevice->sounddata.amp = 32;
sounddevice->sounddata.left_phase = sounddevice->sounddata.right_phase = 0;
sounddevice->sounddata.table_size = 44100/(100/60);
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.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) {
case (EFFECT_GEARSHIFT):
sounddevice->sounddata.last_gear = 0;
switch (sounddevice->effecttype) {
case (SOUNDEFFECT_GEARSHIFT):
sounddevice->sounddata.pitch = 500;
sounddevice->sounddata.amp = 128;
sounddevice->sounddata.left_phase = sounddevice->sounddata.right_phase = 0;
sounddevice->sounddata.table_size = 44100/(1);
sounddevice->sounddata.duration = sds.duration;
streamname = "Gear";
break;
case (EFFECT_TYRESLIP):
streamname = "TyreSlip";
break;
case (EFFECT_TYRELOCK):
streamname = "TyreLock";
break;
case (EFFECT_ABSBRAKES):
streamname = "ABS";
break;
case (EFFECT_SUSPENSION):
streamname = "Suspension";
break;
case (EFFECT_ENGINERPM):
default:
streamname = "Engine";
break;
}
usb_generic_shaker_init(sounddevice);
usb_generic_shaker_init(sounddevice, mainloop, context, devname, sds.volume, sds.pan, sds.channels, streamname);
//usb_generic_shaker_init(sounddevice);
}
static const vtable engine_sound_simdevice_vtable = { &sounddev_engine_update, &sounddev_free };
static const vtable gear_sound_simdevice_vtable = { &sounddev_gearshift_update, &sounddev_free };
static const vtable tyreslip_sound_simdevice_vtable = { &sounddev_tyreslip_update, &sounddev_free };
static const vtable tyrelock_sound_simdevice_vtable = { &sounddev_tyrelock_update, &sounddev_free };
static const vtable absbrakes_sound_simdevice_vtable = { &sounddev_absbrakes_update, &sounddev_free };
static const vtable suspension_sound_simdevice_vtable = { &sounddev_suspension_update, &sounddev_free };
SoundDevice* new_sound_device(DeviceSettings* ds) {
SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
SoundDevice* this = (SoundDevice*) malloc(sizeof(SoundDevice));
this->m.update = &update;
this->m.free = &simdevfree;
this->m.derived = this;
int error = 0;
slogt("Attempting to configure sound device with subtype: %i", ds->dev_subtype);
switch (ds->dev_subtype) {
case (SIMDEVTYPE_ENGINESOUND):
this->effecttype = SOUNDEFFECT_ENGINERPM;
switch (ds->effect_type)
{
case (EFFECT_TYRESLIP):
case (EFFECT_TYRELOCK):
case (EFFECT_ABSBRAKES):
case (EFFECT_SUSPENSION):
if(siminfo->SimSupportsHapticEffects == false)
{
slogw("Skipping sound effect setup because sim does not support haptic effects");
error = MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
}
defaut:
error = 0;
}
if(error == 0)
{
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 (SIMDEVTYPE_GEARSOUND):
this->effecttype = SOUNDEFFECT_GEARSHIFT;
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);
}
int error = sounddev_init(this);
if (error != 0)
{
free(this);
@ -107,4 +345,3 @@ SoundDevice* new_sound_device(DeviceSettings* ds) {
return this;
}

View File

@ -1,35 +1,37 @@
#ifndef _SOUNDDEVICE_H
#define _SOUNDDEVICE_H
#include "portaudio.h"
//#ifdef USE_PULSEAUDIO
#include <pulse/pulseaudio.h>
//#else
//#include "portaudio.h"
//#endif
typedef enum
{
SOUNDDEV_UNKNOWN = 0,
SOUNDDEV_SHAKER = 1
SOUND_EFFECT_MODULATION_NONE = 0,
SOUND_EFFECT_MODULATION_FREQUENCY = 1,
SOUND_EFFECT_MODULATION_AMPLIFY = 2,
}
SoundType;
typedef enum
{
SOUNDEFFECT_ENGINERPM = 0,
SOUNDEFFECT_GEARSHIFT = 1
}
VibrationEffectType;
SoundEffectModulationType;
#define MAX_TABLE_SIZE (6000)
typedef struct
{
float sine[MAX_TABLE_SIZE];
float pitch;
int last_gear;
int left_phase;
int right_phase;
int n;
int table_size;
int amp;
int gear_sound_data;
uint8_t last_gear;
int volume;
uint32_t frequency;
uint32_t frequencyMax;
uint32_t amplitude;
uint32_t amplitudeMax;
double duration;
double curr_frequency;
uint32_t curr_amplitude;
double curr_duration;
double phase;
double noise;
}
PATestData;
SoundData;
#endif

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;
}

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

View File

@ -0,0 +1,105 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include "usbhapticdevice.h"
#include "../../helper/confighelper.h"
#include "../slog/slog.h"
const char* SYSFSRUMBLEPATH = "/sys/module/hid_fanatec/drivers/hid:f*/0003:0EB7:183B.*/rumble";
int cslelitev3_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play)
{
int res = 0;
int value = 0;
if (play > 0)
{
switch (effecttype)
{
case (EFFECT_TYRESLIP):
value = 0xff0000;
break;
case (EFFECT_TYRELOCK):
value = 0xff00;
break;
case (EFFECT_ABSBRAKES):
value = 0xffff00;
break;
}
}
fprintf(usbhapticdevice->filehandle, "%i\n", value);
fflush(usbhapticdevice->filehandle);
return res;
}
int cslelitev3_free(USBGenericHapticDevice* usbhapticdevice)
{
int res = 0;
free(usbhapticdevice->dev);
fflush(usbhapticdevice->filehandle);
fclose(usbhapticdevice->filehandle);
return res;
}
int cslelitev3_init(USBGenericHapticDevice* usbhapticdevice)
{
slogi("initializing CSL Elite V3 Pedals...");
int res = 0;
glob_t globlist;
int i = 0;
if (glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_NOSPACE || glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_NOMATCH)
{
res = 1;
}
if (glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_ABORTED)
{
res = 2;
}
if (res == 0)
{
while (globlist.gl_pathv[i])
{
if (i == 0)
{
usbhapticdevice->dev = strdup(globlist.gl_pathv[i]);
}
i++;
}
}
globfree(&globlist);
if (res == 1) {
sloge("Could not find attach Club Sport Elite V3 Pedals");
return res;
}
if (res == 2) {
sloge("Permissions issue finding Club Sport Elite V3 Pedals");
return res;
}
usbhapticdevice->filehandle = fopen(usbhapticdevice->dev, "w");
if (!usbhapticdevice->filehandle)
{
sloge("Could not open pedal device...");
return res;
}
slogd("CSL Elite V3 Pedals Successfully initialized...");
return res;
}

View File

@ -0,0 +1,8 @@
#ifndef _CSLELITEV3_H
#define _CSLELITEV3_H
int cslelitev3_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play);
int cslelitev3_init(USBGenericHapticDevice* usbhapticdevice);
int cslelitev3_free(USBGenericHapticDevice* usbhapticdevice);
#endif

View File

@ -2,19 +2,19 @@
#include <hidapi/hidapi.h>
#include "tachdevice.h"
#include "../slog/slog.h"
#include "../simdevice.h"
#include "../../slog/slog.h"
const int buf_size = 65;
const size_t REVBURNER_BUFSIZE = 8;
int revburner_update(TachDevice* tachdevice, int pulses)
int revburner_update(USBDevice* tachdevice, int pulses)
{
int res = 0;
unsigned char bytes[buf_size];
for (int x = 0; x < buf_size; x++)
unsigned char bytes[REVBURNER_BUFSIZE];
for (int x = 0; x < REVBURNER_BUFSIZE; x++)
{
bytes[x] = 0x00;
}
@ -27,7 +27,7 @@ int revburner_update(TachDevice* tachdevice, int pulses)
if (tachdevice->handle)
{
res = hid_write(tachdevice->handle, bytes, buf_size);
res = hid_write(tachdevice->handle, bytes, REVBURNER_BUFSIZE);
}
else
{
@ -37,7 +37,7 @@ int revburner_update(TachDevice* tachdevice, int pulses)
return res;
}
int revburner_free(TachDevice* tachdevice)
int revburner_free(USBDevice* tachdevice)
{
int res = 0;
@ -47,7 +47,7 @@ int revburner_free(TachDevice* tachdevice)
return res;
}
int revburner_init(TachDevice* tachdevice)
int revburner_init(USBDevice* tachdevice)
{
slogi("initializing revburner tachometer...");
//tachdevice->update_tachometer = revburner_device_update;
@ -64,6 +64,6 @@ int revburner_init(TachDevice* tachdevice)
res = hid_exit();
return 1;
}
slogd("Found RevBurner Tachometer...");
slogd("Found RevBurner Tachometer handle %i...", tachdevice->handle);
return res;
}

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

View File

@ -0,0 +1,136 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hidapi/hidapi.h>
#include "usbhapticdevice.h"
#include "../../helper/confighelper.h"
#include "../slog/slog.h"
#define SIMAGIC_VENDOR_ID 0x0483
#define SIMAGIC_PRODUCT_ID_P1000 0x0525
const size_t SIMAGIC_P1000_BUFSIZE = 49;
const char* SIMAGICP1000DEVSTRING = "SIMAGIC P1000 Pedals";
int senddevreport(USBGenericHapticDevice* usbhapticdevice, unsigned char* buf, size_t bufsize)
{
int res = 0;
if (usbhapticdevice->handle)
{
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]);
}
else
{
slogd("no handle");
}
return res != bufsize;
}
int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play)
{
int res = 0;
int value = 0;
unsigned char bytes[SIMAGIC_P1000_BUFSIZE];
for (int x = 0; x < SIMAGIC_P1000_BUFSIZE; x++)
{
bytes[x] = 0x00;
}
if (play > 0)
{
bytes[0] = 241;
bytes[1] = 0xEC;
// we can add config settings for these values
bytes[3] = 0x01;
bytes[4] = 0x0A;
bytes[5] = 0xFF;
switch (effecttype)
{
case (EFFECT_TYRESLIP):
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
case (EFFECT_TYRELOCK):
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
case (EFFECT_ABSBRAKES):
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
}
}
else
{
bytes[0] = 241;
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
}
return res;
}
int simagicp1000_free(USBGenericHapticDevice* usbhapticdevice)
{
int res = 0;
hid_close(usbhapticdevice->handle);
res = hid_exit();
return res;
return res;
}
int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice)
{
slogi("initializing %s...", SIMAGICP1000DEVSTRING);
int res = 0;
res = hid_init();
usbhapticdevice->handle = hid_open(SIMAGIC_VENDOR_ID, SIMAGIC_PRODUCT_ID_P1000, NULL);
if (!usbhapticdevice->handle)
{
sloge("Could not find attached %s", SIMAGICP1000DEVSTRING);
res = hid_exit();
return 1;
}
else
{
unsigned char bytes[SIMAGIC_P1000_BUFSIZE];
for (int x = 0; x < SIMAGIC_P1000_BUFSIZE; x++)
{
bytes[x] = 0x00;
}
bytes[0] = 241;
bytes[1] = 0xF1;
bytes[2] = 0x17;
bytes[3] = 0x00;
bytes[4] = 0x00;
bytes[5] = 0x00;
bytes[6] = 0x01;
bytes[7] = 0x02;
res = senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
slogd("Initialization returned %i", res);
if(res != 0)
{
slogw("Problem with initialization of %s", SIMAGICP1000DEVSTRING);
}
}
slogd("Found %s...", SIMAGICP1000DEVSTRING);
return res;
}

View File

@ -0,0 +1,8 @@
#ifndef _SIMAGICP1000_H
#define _SIMAGICP1000_H
int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play);
int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice);
int simagicp1000_free(USBGenericHapticDevice* usbhapticdevice);
#endif

View File

@ -0,0 +1,231 @@
#include <stdio.h>
#include <math.h>
#include <hidapi/hidapi.h>
#include "cammusc12.h"
#include "../../serial/arduinoledlua.h"
#include "../../../slog/slog.h"
const int cammusc12_hidupdate_buf_size = 16;
const int cammusc12_hidledupdate_buf_size = 7;
const int cammusc12_total_leds = 23;
int cammusc12_update(USBDevice* usbdevice, int maxrpm, int rpm, int gear, int velocity)
{
int res = 0;
unsigned char bytes[cammusc12_hidupdate_buf_size];
for (int x = 0; x < cammusc12_hidupdate_buf_size; x++)
{
bytes[x] = 0x00;
}
// byte 1 must be fc it seems
bytes[0] = 0xFA;
bytes[1] = 0xFB;
bytes[2] = 0xD4;
int perct = 0;
if(rpm > 0 && maxrpm > 0)
{
double rpmperct = (double) rpm / (double) maxrpm;
perct = trunc(nearbyint( rpmperct * 100 ));
}
bytes[3] = perct;
// bytes 2 and 3 are a 16 bit velocity
if ( velocity > 0 )
{
bytes[5] = (velocity >> 8) & 0xFF;
bytes[4] = velocity & 0xFF;
}
// byte 4 is gear
bytes[6] = gear-1;
if (usbdevice->handle)
{
slogt("writing bytes x%02xx%02xx%02xx%02xx%02xx%02xx%02x from rpm %i velocity %i gear %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], rpm, velocity, gear);
res = hid_write(usbdevice->handle, bytes, cammusc12_hidupdate_buf_size);
}
else
{
slogd("no handle");
}
return res;
}
int cammusc12_free(USBDevice* wheeldevice)
{
int res = 0;
hid_close(wheeldevice->handle);
res = hid_exit();
if(wheeldevice->u.wheeldevice.useLua == true)
{
slogt("closing lua");
lua_close(wheeldevice->m.L);
}
return res;
}
int cammusc12_init_(USBDevice* wheeldevice)
{
slogi("initializing cammus c12 wheel...");
int res = 0;
res = hid_init();
wheeldevice->handle = hid_open(0x3416, 0x1023, NULL);
if (!wheeldevice->handle)
{
sloge("Could not find attached Cammus C12 Wheel");
res = hid_exit();
return 1;
}
slogd("Found Cammus C12 Wheel...");
return res;
}
int cammusc12_init(USBDevice* wheeldevice, const char* luafile)
{
wheeldevice->u.wheeldevice.useLua = false;
int res = cammusc12_init_(wheeldevice);
if(!wheeldevice->handle)
{
return 1;
}
if(luafile == NULL)
{
return res;
}
slogt("Using lua file for cammus c12 device");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
//int top=lua_gettop(L);
int status = luaL_loadfile(L, luafile);
if (status) {
/* If something went wrong, error message is at the top of */
/* the stack */
sloge("There is an issue with your lua script");
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
lua_close(L);
return -1;
//exit(1);
}
wheeldevice->u.wheeldevice.useLua = true;
lua_setglobal(L,"myFunc");
wheeldevice->m.L = L;
return res;
}
int cammusc12_customled_update(USBDevice* usbdevice, SimData* simdata)
{
int result = 1;
size_t bufsize = (cammusc12_total_leds * 3);
char ledbytes[bufsize];
for(int x = 0; x < bufsize; x++)
{
ledbytes[x] = 0x00;
}
lua_State* L = usbdevice->m.L;
lua_pushstring(L, "buff");
lua_pushlightuserdata(L, &ledbytes);
lua_settable(L, LUA_REGISTRYINDEX);
simdata_to_lua(L, simdata);
lua_setglobal(L, "simdata");
lua_pushinteger(L, cammusc12_total_leds);
lua_setglobal(L, "TotalLeds");
lua_register(L, "set_led_to_color", set_led_to_color);
lua_register(L, "set_led_range_to_color", set_led_range_to_color);
lua_register(L, "set_led_to_rgb_color", set_led_to_rgb_color);
lua_register(L, "set_led_range_to_rgb_color", set_led_range_to_rgb_color);
lua_register(L, "led_clear_all", led_clear_all);
lua_pushinteger(L, LUALEDCOLOR_RED);
lua_setglobal(L, "RED");
lua_pushinteger(L, LUALEDCOLOR_GREEN);
lua_setglobal(L, "GREEN");
lua_pushinteger(L, LUALEDCOLOR_BLUE);
lua_setglobal(L, "BLUE");
lua_pushinteger(L, LUALEDCOLOR_YELLOW);
lua_setglobal(L, "YELLOW");
lua_pushinteger(L, LUALEDCOLOR_ORANGE);
lua_setglobal(L, "ORANGE");
lua_getglobal(L,"myFunc");
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
}
int res = 0;
unsigned char bytes[cammusc12_hidledupdate_buf_size];
for(int x = 0; x < cammusc12_hidledupdate_buf_size; x++)
{
bytes[x] = 0x00;
}
bytes[0] = 0xFA;
bytes[1] = 0xFB;
bytes[2] = 0x02;
for(int i = 0; i < cammusc12_total_leds; i++)
{
if(i >= 15) // make this a setting
{
for (int x = 3; x < cammusc12_hidledupdate_buf_size; x++)
{
bytes[x] = 0x00;
}
uint8_t led = i+1;
uint8_t red = ledbytes[(i * 3) + 0];
uint8_t green = ledbytes[(i * 3) + 1];
uint8_t blue = ledbytes[(i * 3) + 2];
bytes[3] = led;
bytes[4] = red;
bytes[5] = green;
bytes[6] = blue;
if (usbdevice->handle)
{
res = hid_write(usbdevice->handle, bytes, cammusc12_hidupdate_buf_size);
slogt("writing bytes x%02xx%02xx%02xx%02xx%02xx%02xx%02x from red %i green %i blue %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], red, green, blue);
}
else
{
slogd("no handle");
}
}
}
return res;
}

View File

@ -0,0 +1,13 @@
#ifndef _CAMMUSC12_H
#define _CAMMUSC12_H
#include "../../wheeldevice.h"
#include "../../simdevice.h"
int cammusc12_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
int cammusc12_customled_update(USBDevice* usbdevice, SimData* simdata);
int cammusc12_init(USBDevice* wheeldevice, const char* luafile);
int cammusc12_free(USBDevice* wheeldevice);
#endif

View File

@ -0,0 +1,99 @@
#include <stdio.h>
#include <math.h>
#include <hidapi/hidapi.h>
#include "cammusc5.h"
#include "../../../slog/slog.h"
const int cammusc5_hidupdate_buf_size = 14;
const int num_avail_leds = 9;
int cammusc5_update(USBDevice* usbdevice, int maxrpm, int rpm, int gear, int velocity)
{
int res = 0;
unsigned char bytes[cammusc5_hidupdate_buf_size];
for (int x = 0; x < cammusc5_hidupdate_buf_size; x++)
{
bytes[x] = 0x00;
}
// byte 1 must be fc it seems
bytes[0] = 0xFC;
// byte 2 is number of lit leds, assuming 9 available leds,
// if we send 10, all leds will blink singling a gear change
// attempting to build in a margin before the maxrpm is achieved
int litleds = 0;
if(rpm > 0 && maxrpm > 0)
{
int rpmmargin = ceil(.05*maxrpm);
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds+1);
for (int l = 1; l <= (num_avail_leds+1); l++)
{
if(rpm >= (rpminterval * l))
{
litleds = l;
}
}
}
bytes[1] = litleds;
// bytes 2 and 3 are a 16 bit velocity
if ( velocity > 0 )
{
bytes[2] = (velocity >> 8) & 0xFF;
bytes[3] = velocity & 0xFF;
}
// byte 4 is gear
bytes[4] = gear-1;
slogt("writing bytes x%02xx%02xx%02xx%02xx%02x from rpm %i velocity %i gear %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], rpm, velocity, gear);
if (usbdevice->handle)
{
res = hid_write(usbdevice->handle, bytes, cammusc5_hidupdate_buf_size);
}
else
{
slogd("no handle");
}
return res;
}
int cammusc5_free(USBDevice* usbdevice)
{
int res = 0;
hid_close(usbdevice->handle);
res = hid_exit();
return res;
}
int cammusc5_init(USBDevice* usbdevice)
{
slogi("initializing cammus c5 wheel...");
int res = 0;
res = hid_init();
usbdevice->handle = hid_open(0x3416, 0x1021, NULL);
if (!usbdevice->handle)
{
sloge("Could not find attached Cammus C5 Wheel");
res = hid_exit();
return 1;
}
slogd("Found Cammus C5 Wheel...");
return res;
}

View File

@ -0,0 +1,10 @@
#ifndef _CAMMUSC5_H
#define _CAMMUSC5_H
#include "../../simdevice.h"
int cammusc5_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
int cammusc5_init(USBDevice* wheeldevice);
int cammusc5_free(USBDevice* wheeldevice);
#endif

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;
}

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

View File

@ -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;
}

View File

@ -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

View File

@ -17,7 +17,13 @@ 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, simdata);
break;
case USBDEV_GENERICHAPTIC :
usbhapticdev_update(&usbdevice->u.hapticdevice, simdata, this->hapticeffect.tyre, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
break;
}
@ -28,11 +34,18 @@ int usbdev_free(SimDevice* this)
{
USBDevice* usbdevice = (void *) this->derived;
slogt("Usb device free");
switch ( usbdevice->type )
{
case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER :
tachdev_free(&usbdevice->u.tachdevice);
tachdev_free(usbdevice);
break;
case USBDEV_WHEEL :
wheeldev_free(usbdevice);
break;
case USBDEV_GENERICHAPTIC :
usbhapticdev_free(&usbdevice->u.hapticdevice);
break;
}
@ -41,17 +54,23 @@ int usbdev_free(SimDevice* this)
return 0;
}
int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds)
int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds, SimInfo* siminfo)
{
slogi("initializing usb device...");
int error = 0;
usbdevice->type = USBDEV_TACHOMETER;
//usbdevice->type = USBDEV_TACHOMETER;
switch ( usbdevice->type )
{
case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER :
error = tachdev_init(&usbdevice->u.tachdevice, ds);
error = tachdev_init(usbdevice, ds);
break;
case USBDEV_WHEEL :
error = wheeldev_init(usbdevice, ds);
break;
case USBDEV_GENERICHAPTIC :
error = usbhapticdev_init(&usbdevice->u.hapticdevice, ds, siminfo);
break;
}
@ -60,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) {
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
USBDevice* this = (USBDevice*) malloc(sizeof(USBDevice));
@ -69,12 +88,39 @@ USBDevice* new_usb_device(DeviceSettings* ds) {
this->m.derived = this;
this->m.vtable = &usb_simdevice_vtable;
int error = usbdev_init(this, ds);
// TODO: turn this into a switch when we get more devices
this->type = USBDEV_TACHOMETER;
if(ds->dev_subtype == SIMDEVTYPE_USBWHEEL)
{
this->type = USBDEV_WHEEL;
}
// really generic haptic isn't and shouldn't be it's own type
// it's an attribute that is added to a device via composition
// same if that haptic device is a serial device
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC)
{
this->m.hapticeffect.threshold = ds->threshold;
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->type = USBDEV_GENERICHAPTIC;
}
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;
}
this->m.initialized = true;
return this;
}

View File

@ -2,5 +2,7 @@
#define _USBDEVICE_H
#include "tachdevice.h"
#include "wheeldevice.h"
#include "usbhapticdevice.h"
#endif

View File

@ -0,0 +1,115 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "usbhapticdevice.h"
#include "hapticeffect.h"
#include "usb/cslelitev3.h"
#include "usb/simagicp1000.h"
#include "../../helper/confighelper.h"
#include "../../simulatorapi/simapi/simapi/simdata.h"
#include "../../slog/slog.h"
int usbhapticdev_update(USBGenericHapticDevice* usbhapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile)
{
double play = slipeffect(simdata, usbhapticdevice->effecttype, tyre, usbhapticdevice->threshold, useconfig, configcheck, configfile);
if (play != usbhapticdevice->state)
{
int rplay = 0;
if(play > 0)
{
rplay = 1;
switch ( usbhapticdevice->type )
{
case USBHAPTIC_CSLELITEV3PEDALS:
cslelitev3_update(usbhapticdevice, usbhapticdevice->effecttype, rplay);
break;
case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_update(usbhapticdevice, usbhapticdevice->effecttype, rplay);
break;
}
}
else
{
switch ( usbhapticdevice->type )
{
case USBHAPTIC_CSLELITEV3PEDALS:
cslelitev3_update(usbhapticdevice, usbhapticdevice->effecttype, rplay);
break;
case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_update(usbhapticdevice, usbhapticdevice->effecttype, rplay);
break;
}
}
usbhapticdevice->state = play;
}
return 0;
}
int usbhapticdev_free(USBGenericHapticDevice* usbhapticdevice)
{
slogt("closing usb haptic device");
switch ( usbhapticdevice->type )
{
case USBHAPTIC_CSLELITEV3PEDALS:
cslelitev3_free(usbhapticdevice);
break;
case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_free(usbhapticdevice);
break;
}
return 0;
}
int usbhapticdev_init(USBGenericHapticDevice* usbhapticdevice, DeviceSettings* ds, SimInfo* siminfo)
{
if(siminfo->SimSupportsHapticEffects == false)
{
slogi("This sim does not support haptic effects");
return MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
}
int error = 0;
usbhapticdevice->state = 0;
usbhapticdevice->value0 = ds->usbdevsettings.value0;
usbhapticdevice->value1 = ds->usbdevsettings.value1;
usbhapticdevice->state = usbhapticdevice->value0;
usbhapticdevice->effecttype = ds->effect_type;
usbhapticdevice->threshold = ds->threshold;
if(ds->effect_type == EFFECT_TYRESLIP)
{
usbhapticdevice->effecttype = EFFECT_TYRESLIP;
}
if(ds->effect_type == EFFECT_TYRELOCK)
{
usbhapticdevice->effecttype = EFFECT_TYRELOCK;
}
if(ds->effect_type == EFFECT_ABSBRAKES)
{
usbhapticdevice->effecttype = EFFECT_ABSBRAKES;
}
slogi("initializing standalone usb haptic device...");
// detection of usb device model
switch (ds->dev_subsubtype) {
case (SIMDEVSUBTYPE_SIMAGICP1000PEDALS):
usbhapticdevice->type = USBHAPTIC_SIMAGICP1000PEDALS;
error = simagicp1000_init(usbhapticdevice);
break;
case (SIMDEVSUBTYPE_CSLELITEV3PEDALS):
usbhapticdevice->type = USBHAPTIC_CSLELITEV3PEDALS;
error = cslelitev3_init(usbhapticdevice);
break;
default:
slogw("Possibly unknown device");
}
return error;
}

View File

@ -0,0 +1,37 @@
#ifndef _USBHAPTICDEVICE_H
#define _USBHAPTICDEVICE_H
#include <hidapi/hidapi.h>
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
typedef enum
{
USBHAPTIC_UNKNOWN = 0,
USBHAPTIC_CSLELITEV3PEDALS = 1,
USBHAPTIC_SIMAGICP1000PEDALS = 2
}
HapticType;
typedef struct
{
int id;
HapticType type;
double state;
double threshold;
int value0;
int value1;
VibrationEffectType effecttype;
FILE* filehandle;
hid_device* handle;
char* dev;
}
USBGenericHapticDevice;
int usbhapticdev_update(USBGenericHapticDevice* hapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile);
int usbhapticdev_init(USBGenericHapticDevice* hapticdevice, DeviceSettings* ds, SimInfo* siminfo);
int usbhapticdev_free(USBGenericHapticDevice* hapticdevice);
#endif

View File

@ -0,0 +1,151 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "usb/wheels/logitechg29.h"
#include "usb/wheels/cammusc5.h"
#include "usb/wheels/cammusc12.h"
#include "usb/wheels/gtneo.h"
#include "serial/moza.h"
#include "simdevice.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../slog/slog.h"
int wheeldev_update(USBDevice* usbdevice, SimData* simdata)
{
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
switch ( wheeldevice->type )
{
case WHEELDEV_UNKNOWN :
case WHEELDEV_CAMMUSC5 :
cammusc5_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
break;
case WHEELDEV_LOGITECHG29 :
logitechg29_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
break;
case WHEELDEV_CAMMUSC12 :
if(usbdevice->u.wheeldevice.useLua == true)
{
cammusc12_customled_update(usbdevice, simdata);
}
else
{
cammusc12_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
}
break;
case WHEELDEV_SIMAGICGTNEO :
if(usbdevice->u.wheeldevice.useLua == true)
{
simagic_gtneo_customled_update(usbdevice, simdata);
}
else
{
sloge("GT Neo requires config file");
}
break;
}
return 0;
}
int wheeldev_free(USBDevice* usbdevice)
{
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
slogt("wheel device free");
switch ( wheeldevice->type )
{
case WHEELDEV_UNKNOWN :
case WHEELDEV_CAMMUSC5 :
cammusc5_update(usbdevice, 0, 0, 0, 0);
cammusc5_free(usbdevice);
break;
case WHEELDEV_LOGITECHG29 :
logitechg29_update(usbdevice, 0, 0, 0, 0);
logitechg29_free(usbdevice);
break;
case WHEELDEV_CAMMUSC12 :
cammusc12_free(usbdevice);
if(wheeldevice->useLua == true)
{
free(usbdevice->m.device_specific_config_file);
}
break;
}
return 0;
}
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(usbdevice);
break;
case SIMDEVSUBTYPE_LOGITECH_G29:
wheeldevice->type = WHEELDEV_LOGITECHG29;
slogi("Attempting to initialize Logitech G29");
error = logitechg29_init(usbdevice);
break;
case SIMDEVSUBTYPE_CAMMUSC12:
wheeldevice->type = WHEELDEV_CAMMUSC12;
slogi("Attempting to initialize cammus C12");
if(ds->has_config == true)
{
usbdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = cammusc12_init(usbdevice, usbdevice->m.device_specific_config_file);
if(error != 0)
{
if(usbdevice->m.device_specific_config_file != NULL)
{
free(usbdevice->m.device_specific_config_file);
}
}
}
else
{
error = cammusc12_init(usbdevice, NULL);
}
break;
case SIMDEVSUBTYPE_SIMAGICGTNEO:
wheeldevice->type = WHEELDEV_SIMAGICGTNEO;
slogi("Attempting to initialize Simagic GT Neo");
if(ds->has_config == true)
{
usbdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = simagic_gtneo_init(usbdevice, usbdevice->m.device_specific_config_file);
if(error != 0)
{
if(usbdevice->m.device_specific_config_file != NULL)
{
free(usbdevice->m.device_specific_config_file);
}
}
}
else
{
sloge("Simagic GT Neo requires lua config file to function");
error = -1;
}
break;
default:
wheeldevice->type = WHEELDEV_UNKNOWN;
slogw("Unknown cammus wheel detected, trying C5");
error = cammusc5_init(usbdevice);
break;
}
return error;
}

View File

@ -0,0 +1,30 @@
#ifndef _WHEELDEVICE_H
#define _WHEELDEVICE_H
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
//typedef int (*tachdev_update)(int revs);
typedef enum
{
WHEELDEV_UNKNOWN = 0,
WHEELDEV_CAMMUSC5 = 1,
WHEELDEV_CAMMUSC12 = 2,
WHEELDEV_MOZAR5 = 3,
WHEELDEV_SIMAGICGTNEO = 4,
WHEELDEV_LOGITECHG29 = 5
}
WheelType;
typedef struct
{
int id;
WheelType type;
bool useLua;
char* port;
}
WheelDevice;
#endif

View File

@ -3,6 +3,7 @@ set(gameloop_source_files
gameloop.h
tachconfig.c
tachconfig.h
loopdata.h
)
set(LIBXML_INCLUDE_DIR /usr/include/libxml2)

View File

@ -6,17 +6,49 @@
#include <sys/stat.h>
#include <poll.h>
#include <termios.h>
#include <signal.h>
#include <uv.h>
#include "gameloop.h"
#include "../helper/parameters.h"
#include "loopdata.h"
#include "../helper/confighelper.h"
#include "../devices/simdevice.h"
#include "../devices/hapticeffect.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../simulatorapi/simapi/simapi/simmapper.h"
#include "../slog/slog.h"
#define DEFAULT_UPDATE_RATE 240.0
#define SIM_CHECK_RATE 1.0
bool go = false;
bool go2 = false;
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)
{
@ -167,80 +199,441 @@ int showstats(SimData* simdata)
fflush(stdout);
}
int clilooper(SimDevice* devices, int numdevices, Parameters* p, SimData* simdata, SimMap* simmap)
void simapilib_loginfo(char* message)
{
slogi(message);
}
slogi("preparing game loop with %i devices...", numdevices);
void simapilib_logdebug(char* message)
{
slogd(message);
}
void simapilib_logtrace(char* message)
{
slog_display(SLOG_TRACE, 1, message);
}
void on_timer_close_complete(uv_handle_t* handle)
{
free(handle);
}
slogi("sending initial data to devices");
simdata->velocity = 16;
simdata->rpms = 100;
void devicetimercallback(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;
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 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->siminfo.simulatorapi);
}
if(confignum == -1)
{
slogi("looking for ui config %s pass 3", ms->config_str);
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, &f->siminfo, configureddevices, ds, ms);
slogi("initialized %i devices", initdevices);
for( int i = 0; i < configureddevices; i++)
{
settingsfree(ds[i]);
}
free(ds);
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++)
{
devices[x].update(&devices[x], simdata);
}
sleep(3);
struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode);
newsettings = canonicalmode;
newsettings.c_lflag &= (~ICANON & ~ECHO);
newsettings.c_cc[VMIN] = 1;
newsettings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &newsettings);
char ch;
struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI };
double update_rate = DEFAULT_UPDATE_RATE;
int t=0;
int s=0;
int go = true;
while (go == true)
if (devices[x].initialized == true)
{
device_loop_data* dld = &f->device_batons[x];
dld->simdevice = &devices[x];
dld->simdata = simdata;
uv_timer_t* dt = &f->device_timers[x];
uv_timer_init(uv_default_loop(), dt);
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 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;
}
}
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;
if(appstate == 2)
{
simdatamap(simdata, simmap, p->sim);
showstats(simdata);
t++;
s++;
if(simdata->rpms<100)
{
simdata->rpms=100;
}
}
void releaseloop(loop_data* f, SimData* simdata, SimMap* simmap)
{
slogi("release loop");
if(f->releasing == false)
{
f->releasing = true;
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;
for (int x = 0; x < numdevices; x++)
{
devices[x].update(&devices[x], simdata);
}
if( poll(&mypoll, 1, 1000.0/update_rate) )
if (devices[x].initialized == true)
{
uv_timer_t* dt = &f->device_timers[x];
slogt("attempting device timer stop and release");
slogt("timer active status %i", uv_is_active((uv_handle_t*) dt));
uv_timer_stop(dt);
//uv_close((uv_handle_t*) dt, on_timer_close_complete);
}
}
free(f->device_batons);
free(f->device_timers);
slogt("stopped device timers");
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(1);
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].free(&devices[x]);
}
}
free(devices);
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");
//stopui(ms->ui_type, f);
// free loop data
if(appstate > 0)
{
slogi("restarting checking for data...");
uv_timer_start(&datachecktimer, datacheckcallback, 0, 1000);
}
f->releasing = false;
if(appstate > 1)
{
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);
}
}
void on_alloc(uv_handle_t* client, size_t suggested_size, uv_buf_t* buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
bzero(buf->base, suggested_size);
slogt("udp malloc:%lu %p\n",buf->len,buf->base);
}
static void on_udp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) {
if (nread > 0) {
slogt("udp data received");
}
if (nread <= 0) {
free(rcvbuf->base);
return;
}
char* a;
a = rcvbuf->base;
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;
if (appstate == 2)
{
simapi_datamap(simdata, simmap, f->siminfo.mapapi, true, a);
looprun(ms, f, simdata);
}
if (f->siminfo.isSimOn == false || simdata->simstatus <= 1 || appstate <= 1)
{
releaseloop(f, simdata, simmap);
}
slogt("udp free :%lu %p\n",rcvbuf->len,rcvbuf->base);
free(rcvbuf->base);
}
int startudp(int port)
{
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);
slogt("initial udp error is %i", err);
return err;
}
void udpstart(MonocoqueSettings* sms, loop_data* f, SimData* simdata, SimMap* simmap)
{
if (appstate == 2)
{
simapi_datamap(simdata, simmap, f->siminfo.simulatorapi, true, NULL);
if (doui == true)
{
looprun(sms, f, simdata);
}
}
}
void datacheckcallback(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;
if ( appstate == 1 )
{
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->siminfo.isSimOn == true && simdata->simstatus >= 2)
{
if ( appstate == 1 )
{
appstate++;
doui = true;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
if(f->use_udp == true || f->siminfo.SimUsesUDP == true)
{
slogt("starting udp receive loop");
udpstart(f->ms, f, simdata, simmap);
uv_udp_recv_start(&recv_socket, on_alloc, on_udp_recv);
slogt("udp receive loop started");
}
else
{
int interval = 1000 / f->ms->fps;
slogd("starting telemetry mapping at %i fps (%i ms ticks)", f->ms->fps, interval);
uv_timer_start(&datamaptimer, shmdatamapcallback, 2000, interval);
}
}
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("stopped checking for data");
uv_timer_stop(handle);
}
}
void cb(uv_poll_t* handle, int status, int events)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
loop_data* f = (loop_data*) b;
char ch;
scanf("%c", &ch);
if (ch == 'q')
{
go = false;
}
}
}
fprintf(stdout, "\n");
fflush(stdout);
tcsetattr(0, TCSANOW, &canonicalmode);
simdata->velocity = 0;
simdata->rpms = 100;
for (int x = 0; x < numdevices; x++)
if(f->releasing == false && doui == false)
{
devices[x].update(&devices[x], simdata);
appstate--;
fprintf(stdout, "\nUser requested stop appstate is now %i\n", appstate);
fflush(stdout);
slogi("User requested stop appstate is now %i", appstate);
}
}
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);
}
}
return 0;
}
int looper(SimDevice* devices, int numdevices, Parameters* p)
int monocoque_mainloop(MonocoqueSettings* ms)
{
SimData* simdata = malloc(sizeof(SimData));
SimMap* simmap = malloc(sizeof(SimMap));
SimMap* simmap = simapi_simmap_create();
struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode);
@ -252,47 +645,61 @@ int looper(SimDevice* devices, int numdevices, Parameters* p)
char ch;
struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI };
uv_poll_t* poll = (uv_poll_t*) malloc(uv_handle_size(UV_POLL));
loop_data* baton = (loop_data*) malloc(sizeof(loop_data));
baton->simmap = simmap;
baton->simdata = simdata;
baton->ms = ms;
baton->uion = false;
baton->releasing = false;
baton->use_udp = false;
baton->req.data = (void*) baton;
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);
fprintf(stdout, "Searching for sim data... Press q to quit...\n");
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
p->simon = false;
double update_rate = SIM_CHECK_RATE;
int go = true;
while (go == true)
{
if (p->simon == false)
{
getSim(simdata, simmap, &p->simon, &p->sim);
}
if (p->simon == true)
{
clilooper(devices, numdevices, p, simdata, simmap);
}
if (p->simon == true)
{
p->simon = false;
fprintf(stdout, "Searching for sim data... Press q again to quit...\n");
sleep(2);
}
if( poll(&mypoll, 1, 1000.0/update_rate) )
{
scanf("%c", &ch);
if(ch == 'q')
{
go = false;
}
}
}
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);
free(simmap);
@ -314,122 +721,296 @@ int tester(SimDevice* devices, int numdevices)
tcsetattr(0, TCSANOW, &newsettings);
fprintf(stdout, "\n");
simdata->gear = 0;
simdata->car[0] = 'C';
simdata->car[1] = 'A';
simdata->car[2] = 'R';
simdata->car[3] = '\0';
simdata->gear = SIMAPI_GEAR_NEUTRAL;
simdata->gearc[0] = 0x4e;
simdata->gearc[1] = 0;
simdata->velocity = 16;
simdata->rpms = 100;
simdata->maxrpm = 8000;
sleep(3);
simdata->abs = 0;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->Xvelocity = 0;
simdata->Yvelocity = 100;
simdata->Zvelocity = 0;
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;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Green Flag!\n");
simdata->playerflag = SIMAPI_FLAG_GREEN;
fprintf(stdout, "Shifting into first gear\n");
simdata->gear = 1;
simdata->gear = SIMAPI_GEAR_FIRST;
simdata->gearc[0] = 0x31;
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++)
{
devices[x].update(&devices[x], simdata);
}
sleep(3);
fprintf(stdout, "Setting speed to 100\n");
simdata->velocity = 100;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "testing wheel spin\n");
simdata->velocity = 15;
simdata->tyreRPS[0] = 50;
simdata->tyreRPS[1] = 50;
simdata->tyreRPS[2] = 50;
simdata->tyreRPS[3] = 50;
simdata->tyrediameter[0] = 0.638636385206394;
simdata->tyrediameter[1] = 0.633384434597093;
simdata->tyrediameter[2] = 0.710475735564615;
simdata->tyrediameter[3] = 0.710475735564615;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Testing wheel Lock\n");
simdata->tyreRPS[0] = 25;
simdata->tyreRPS[1] = 25;
simdata->tyreRPS[2] = 25;
simdata->tyreRPS[3] = 25;
simdata->velocity = 150;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Shifting into second gear\n");
simdata->gear = 2;
simdata->tyreRPS[0] = 0;
simdata->tyreRPS[1] = 0;
simdata->tyreRPS[2] = 0;
simdata->tyreRPS[3] = 0;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->abs = 0;
simdata->gear = SIMAPI_GEAR_SECOND;
simdata->gearc[0] = 0x32;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Testing abs brake lock Lock\n");
simdata->tyreRPS[0] = 50;
simdata->tyreRPS[1] = 50;
simdata->tyreRPS[2] = 50;
simdata->tyreRPS[3] = 50;
simdata->tyrediameter[0] = 0.638636385206394;
simdata->tyrediameter[1] = 0.633384434597093;
simdata->tyrediameter[2] = 0.710475735564615;
simdata->tyrediameter[3] = 0.710475735564615;
simdata->abs = .11;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Setting speed to 200\n");
simdata->tyreRPS[0] = 0;
simdata->tyreRPS[1] = 0;
simdata->tyreRPS[2] = 0;
simdata->tyreRPS[3] = 0;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->abs = 0;
simdata->velocity = 200;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Yellow Flag!\n");
simdata->playerflag = SIMAPI_FLAG_YELLOW;
fprintf(stdout, "Shifting into third gear\n");
simdata->gear = 3;
simdata->gear = SIMAPI_GEAR_THIRD;
simdata->gearc[0] = 0x33;
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++)
{
devices[x].update(&devices[x], simdata);
}
sleep(3);
fprintf(stdout, "Setting rpms to 2000\n");
simdata->rpms = 2000;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
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++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Shifting into fourth gear\n");
simdata->gear = 4;
simdata->gear = SIMAPI_GEAR_FOURTH;
simdata->gearc[0] = 0x34;
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++)
{
devices[x].update(&devices[x], simdata);
}
sleep(3);
fprintf(stdout, "Setting speed to 300\n");
simdata->velocity = 300;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Setting rpms to 7000\n");
simdata->rpms = 7000;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
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++)
{
if (devices[x].initialized == true)
{
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 = SIMAPI_GEAR_NEUTRAL;
simdata->gearc[0] = 0x4e;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(1);
fflush(stdout);

View File

@ -3,3 +3,5 @@
int tester(SimDevice* devices, int numdevices);
int looper(SimDevice* devices, int numdevices, Parameters* p);
int monocoque_mainloop(MonocoqueSettings* ms);

View File

@ -0,0 +1,44 @@
#ifndef _LOOPDATA_H
#define _LOOPDATA_H
#include <uv.h>
#include "../helper/parameters.h"
#include "../helper/confighelper.h"
#include "../devices/simdevice.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../simulatorapi/simapi/simapi/simmapper.h"
typedef struct device_loop_data
{
// these are all pointers to pointers and freed independently, usually in releaseloop
uv_work_t req;
SimData* simdata;
SimDevice* simdevice;
MonocoqueSettings* ms;
SimInfo siminfo;
} device_loop_data;
typedef struct loop_data
{
uv_work_t req;
bool use_udp;
bool uion;
bool releasing;
bool started_tyre_calc_thread;
int numdevices;
SimInfo siminfo;
// monocoque settings is a pointer from monocoque.c and freed there
MonocoqueSettings* ms;
// these are freed at the end of the main gameloop
SimData* simdata;
SimMap* simmap;
// these all get malloced in looprun and freed in releaseloop
SimDevice* simdevices;
uv_timer_t* device_timers;
device_loop_data* device_batons;
device_loop_data* tyrebaton;
} loop_data;
#endif

View File

@ -1,3 +1,4 @@
#include <dirent.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
@ -9,10 +10,17 @@
#include <libxml/tree.h>
#include "confighelper.h"
#include "dirhelper.h"
#include "../slog/slog.h"
#include "parameters.h"
#include "../simulatorapi/simapi/simapi/simmapper.h"
#include <pulse/pulseaudio.h>
int strcicmp(char const *a, char const *b)
{
for (;; a++, b++) {
@ -22,30 +30,119 @@ int strcicmp(char const *a, char const *b)
}
}
int strtoeffecttype(const char* effect, DeviceSettings* ds)
{
ds->is_valid = false;
int strtogame(const char* game, MonocoqueSettings* ms)
if (strcicmp(effect, "Engine") == 0)
{
slogd("Checking for %s in list of supported simulators.", game);
if (strcicmp(game, "ac") == 0)
{
slogd("Setting simulator to Assetto Corsa");
ms->sim_name = SIMULATOR_ASSETTO_CORSA;
ds->is_valid = true;
ds->effect_type = EFFECT_ENGINERPM;
}
else if (strcicmp(game, "rf2") == 0)
if (strcicmp(effect, "Gear") == 0)
{
slogd("Setting simulator to RFactor 2");
ms->sim_name = SIMULATOR_RFACTOR2;
ds->is_valid = true;
ds->effect_type = EFFECT_GEARSHIFT;
}
else
if (strcicmp(game, "test") == 0)
if (strcicmp(effect, "Suspension") == 0)
{
slogd("Setting simulator to Test Data");
ms->sim_name = SIMULATOR_SIMAPI_TEST;
ds->is_valid = true;
ds->effect_type = EFFECT_SUSPENSION;
}
else
if (strcicmp(effect, "ABS") == 0)
{
slogi("%s does not appear to be a supported simulator.", game);
return MONOCOQUE_ERROR_INVALID_SIM;
ds->is_valid = true;
slogt("found abas effect set");
ds->effect_type = EFFECT_ABSBRAKES;
}
if ((strcicmp(effect, "SLIP") == 0) || (strcicmp(effect, "TYRESLIP") == 0) || (strcicmp(effect, "TIRESLIP") == 0))
{
ds->is_valid = true;
slogt("found tyreslip effect set");
ds->effect_type = EFFECT_TYRESLIP;
}
if ((strcicmp(effect, "LOCK") == 0) || (strcicmp(effect, "TYRELOCK") == 0) || (strcicmp(effect, "TIRELOCK") == 0))
{
ds->is_valid = true;
slogt("found tyreslock effect set");
ds->effect_type = EFFECT_TYRELOCK;
}
if (ds->is_valid == false)
{
slogw("effect %s is not a valid effect", effect);
}
ds->is_valid = true;
return MONOCOQUE_ERROR_NONE;
}
int strtodevsubsubtype(const char* device_subsubtype, DeviceSettings* ds)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_UNKNOWN;
bool devfound = false;
if (strcicmp(device_subsubtype, "CammusC5") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_CAMMUSC5;
devfound = true;
}
if (strcicmp(device_subsubtype, "CammusC12") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_CAMMUSC12;
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;
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;
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;
devfound = true;
}
if (strcicmp(device_subsubtype, "SIMAGICP1000PEDALS") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICP1000PEDALS;
devfound = true;
}
if (strcicmp(device_subsubtype, "SIMAGICGTNEO") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICGTNEO;
devfound = true;
}
if(devfound == false)
{
slogw("%s does not appear to be a valid device sub sub type, but attempting to continue with other devices", device_subsubtype);
return MONOCOQUE_ERROR_INVALID_DEV;
}
return MONOCOQUE_ERROR_NONE;
}
@ -62,28 +159,51 @@ int strtodevsubtype(const char* device_subtype, DeviceSettings* ds, int simdev)
ds->dev_subtype = SIMDEVTYPE_TACHOMETER;
break;
}
if (strcicmp(device_subtype, "Wheel") == 0 || strcicmp(device_subtype, "UsbWheel") == 0)
{
ds->dev_subtype = SIMDEVTYPE_USBWHEEL;
break;
}
if (strcicmp(device_subtype, "UsbHaptic") == 0 || strcicmp(device_subtype, "Haptic") == 0)
{
ds->dev_subtype = SIMDEVTYPE_USBHAPTIC;
break;
}
case SIMDEV_SERIAL:
if (strcicmp(device_subtype, "ShiftLights") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SHIFTLIGHTS;
break;
}
if (strcicmp(device_subtype, "Simleds") == 0)
{
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;
break;
}
if (strcicmp(device_subtype, "SerialHaptic") == 0 || strcicmp(device_subtype, "Haptic") == 0)
{
slogt("found serial haptic device settings");
ds->dev_subtype = SIMDEVTYPE_SERIALHAPTIC;
break;
}
if (strcicmp(device_subtype, "Wheel") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SERIALWHEEL;
break;
}
case SIMDEV_SOUND:
if (strcicmp(device_subtype, "Engine") == 0)
{
ds->dev_subtype = SIMDEVTYPE_ENGINESOUND;
ds->is_valid = true;
break;
}
if (strcicmp(device_subtype, "Gear") == 0)
{
ds->dev_subtype = SIMDEVTYPE_GEARSOUND;
break;
}
default:
ds->is_valid = false;
slogw("%s does not appear to be a valid device sub type, but attempting to continue with other devices", device_subtype);
@ -123,7 +243,252 @@ int strtodev(const char* device_type, const char* device_subtype, DeviceSettings
return MONOCOQUE_ERROR_NONE;
}
int loadtachconfig(const char* config_file, DeviceSettings* ds)
int getsimfromconfig(config_setting_t* c)
{
int sim = 0;
const char* simstr = NULL;
int found = config_setting_lookup_string(c, "sim", &simstr);
if(found == 0)
{
int found = config_setting_lookup_int(c, "sim", &sim);
}
else
{
sim = simapi_strtogame(simstr);
}
return sim;
}
int getNumberOfConfigs(const char* config_file_str)
{
config_t cfg;
config_init(&cfg);
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));
return -1;
}
config_setting_t* config = NULL;
config_setting_t* config_widgets = NULL;
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");
int configs = config_setting_length(config);
const char* temp;
config_setting_t* config_config = NULL;
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return -1;
}
int confignum = -1;
slogt("Multiple configs found");
for (j = 0; j < configs; j++)
{
config_config = config_setting_get_elem(config, j);
int found = 0;
int csim = 0;
slogt("sim is %i", sim);
csim = getsimfromconfig(config_config);
if (csim != sim)
{
slogt("rejected config %i", j);
continue;
}
slogt("checking if car is matched %i", j);
temp = NULL;
found = config_setting_lookup_string(config_config, "car", &temp);
slogt("config car is %s found is %i", temp, found);
if(temp != NULL && found > 0 && car > 0 && car != NULL)
{
slogt("checking against sim car of %s", car);
if(strcicmp(temp, car) == 0)
{
confignum = j;
}
}
if(confignum>=0)
{
break;
}
}
config_destroy(&cfg);
return confignum;
}
int getconfigtouse1(const char* config_file_str, char* car, int sim)
{
config_t cfg;
config_init(&cfg);
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");
int configs = config_setting_length(config);
const char* temp;
config_setting_t* config_config = NULL;
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return -1;
}
int confignum = -1;
slogt("Multiple configs found");
for (j = 0; j < configs; j++)
{
config_config = config_setting_get_elem(config, j);
int found = 0;
int csim = 0;
slogt("sim is %i", sim);
csim = getsimfromconfig(config_config);
if (csim != sim)
{
slogt("rejected config %i", j);
continue;
}
slogt("checking if car is matched %i", j);
temp = NULL;
found = config_setting_lookup_string(config_config, "car", &temp);
slogt("config car is %s found is %i", temp, found);
if(temp != NULL && found > 0 && car > 0 && car != NULL)
{
slogt("checking against sim car of %s", car);
if(strcicmp(temp, car) == 0)
{
confignum = j;
}
if(strcicmp("default", temp) == 0)
{
slogt("matched default car");
confignum = j;
}
}
else
{
slogt("assuming default car");
confignum = j;
}
slogt("bomb");
if(confignum>=0)
{
break;
}
}
config_destroy(&cfg);
return confignum;
}
int getconfigtouse(const char* config_file_str, char* car, int sim)
{
config_t cfg;
config_init(&cfg);
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");
int configs = config_setting_length(config);
const char* temp;
config_setting_t* config_config = NULL;
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return 0;
}
int confignum = 0;
slogt("Multiple configs found");
for (j = 0; j < configs; j++)
{
config_config = config_setting_get_elem(config, j);
int found = 0;
int csim = 0;
slogt("sim is %i", sim);
csim = getsimfromconfig(config_config);
if (csim != sim && csim != 0)
{
slogt("rejected config %i", j);
continue;
}
slogt("checking if car is matched %i", j);
temp = NULL;
found = config_setting_lookup_string(config_config, "car", &temp);
slogt("config car is %s found is %i", temp, found);
if(temp != NULL && found > 0 && car > 0 && car != NULL)
{
slogt("checking against sim car of %s", car);
if(strcicmp(temp, car) == 0)
{
confignum = j;
}
if(strcicmp("default", temp) == 0)
{
slogt("matched default car");
confignum = j;
}
}
else
{
slogt("assuming default car");
confignum = j;
}
slogt("bomb");
if(confignum<configs-1)
{
break;
}
}
config_destroy(&cfg);
return confignum;
}
int loadtachconfig(char* config_file, DeviceSettings* ds)
{
@ -216,22 +581,108 @@ int loadtachconfig(const char* config_file, DeviceSettings* ds)
return 0;
}
int loadconfig(const char* config_file, DeviceSettings* ds)
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;
}
if (strcicmp(temp, "REARS") == 0)
{
ds->tyre = REARS;
}
if (strcicmp(temp, "FRONTLEFT") == 0)
{
ds->tyre = FRONTLEFT;
}
if (strcicmp(temp, "FRONTRIGHT") == 0)
{
ds->tyre = FRONTRIGHT;
}
if (strcicmp(temp, "REARLEFT") == 0)
{
ds->tyre = REARLEFT;
}
if (strcicmp(temp, "REARRIGHT") == 0)
{
ds->tyre = REARRIGHT;
}
}
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;
}
int configcheck(const char* config_file_str, int confignum, int* devices)
{
slogt("ui config check");
config_t cfg;
config_init(&cfg);
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_setting_t* config = NULL;
config = config_lookup(&cfg, "configs");
config_setting_t* selectedconfig = config_setting_get_elem(config, confignum);
slogt("selected num %i", confignum);
config_setting_t* config_devices = NULL;
config_devices = config_setting_lookup(selectedconfig, "devices");
*devices = config_setting_length(config_devices);
config_destroy(&cfg);
return 0;
//return cfg;
}
int devsetup(const char* device_type, const char* device_subtype, const char* config_file, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings)
{
int error = MONOCOQUE_ERROR_NONE;
slogi("Called device setup with %s %s %s", device_type, device_subtype, config_file);
//slogt("Called device setup with %s %s %s", device_type, device_subtype, config_file);
ds->dev_type = SIMDEV_UNKNOWN;
error = strtodev(device_type, device_subtype, ds);
if (error != MONOCOQUE_ERROR_NONE)
{
return error;
@ -239,13 +690,17 @@ 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)
{
return error;
}
ds->fps = 60;
config_setting_lookup_int(device_settings, "fps", &ds->fps);
if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER)
{
if (device_settings != NULL)
@ -264,29 +719,280 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
ds->tachsettings.use_pulses = false;
}
}
if (ds->dev_subtype == SIMDEVTYPE_SIMWIND || ds->dev_subtype == SIMDEVTYPE_SHIFTLIGHTS)
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_subtype == SIMDEVTYPE_USBWHEEL || ds->dev_subtype == SIMDEVTYPE_SERIALWHEEL)
{
// logic for different devices
int b = 0;
const char* temp;
int found = config_setting_lookup_string(device_settings, "subtype", &temp);
if(temp != NULL && found > 0)
{
b = strtodevsubsubtype(temp, ds);
}
}
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_type == SIMDEV_SOUND || ds->dev_subtype == SIMDEVTYPE_SERIALHAPTIC)
{
slogt("analysing haptic effect settings");
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 || ds->effect_type == EFFECT_SUSPENSION )
{
gettyre(device_settings, ds);
ds->threshold = 0;
int found = config_setting_lookup_float(device_settings, "threshold", &ds->threshold);
}
if (ds->dev_type == SIMDEV_SOUND)
{
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;
}
if (device_settings != NULL)
{
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 = NULL;
int found = 0;
found = config_setting_lookup_string(device_settings, "devid", &temp);
if (found == CONFIG_FALSE)
{
ds->sounddevsettings.dev = NULL;
}
else
{
if(temp != NULL)
{
ds->sounddevsettings.dev = strdup(temp);
}
}
found = config_setting_lookup_string(device_settings, "modulation", &temp);
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
if (found == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
slogd("Effect modulation not found, set to none");
}
else
{
if(strcicmp(temp, "FREQUENCY") == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_FREQUENCY;
if(ds->sounddevsettings.frequencyMax == 0 || ds->sounddevsettings.frequencyMax < ds->sounddevsettings.frequency)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
slogw("Falling back to no frequency modulation since frequencyMax is either not set or set below target frequency");
}
else
{
slogi("Effect modulation found, set to FREQUENCY");
}
}
else if(strcicmp(temp, "AMPLIFY") == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_AMPLIFY;
slogi("Effect modulation found, set to AMPLIFY");
}
else
{
slogw("%s is not a valid modulation type, falling back to no effect modulation");
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
}
}
}
}
}
if (ds->dev_type == SIMDEV_SERIAL)
{
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);
int motorposition = 8;
config_setting_lookup_int(device_settings, "motors", &motorposition);
ds->serialdevsettings.motorsposition = motorposition;
int numlights = 6;
config_setting_lookup_int(device_settings, "numlights", &numlights);
ds->serialdevsettings.numlights = numlights;
int numleds = 6;
config_setting_lookup_int(device_settings, "numleds", &numleds);
ds->serialdevsettings.numleds = numleds;
int startled = 1;
config_setting_lookup_int(device_settings, "startled", &startled);
ds->serialdevsettings.startled = startled;
int endled = 1;
config_setting_lookup_int(device_settings, "endled", &endled);
ds->serialdevsettings.endled = endled;
int baud = 9600;
config_setting_lookup_int(device_settings, "baud", &baud);
ds->serialdevsettings.baud = baud;
double ampfactor = 1.0;
ds->serialdevsettings.ampfactor = 1.0;
found = config_setting_lookup_float(device_settings, "ampfactor", &ampfactor);
ds->serialdevsettings.ampfactor = 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);
}
}
return error;
}
int uiloadconfig(const char* config_file_str, int confignum, int configureddevices, MonocoqueSettings* ms, DeviceSettings* ds)
{
int numdevices = 0;
config_t cfg;
config_init(&cfg);
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));
}
else
{
slogi("Parsing config file");
config_setting_t* config = NULL;
config = config_lookup(&cfg, "configs");
config_setting_t* selectedconfig = config_setting_get_elem(config, confignum);
config_setting_t* config_devices = NULL;
config_devices = config_setting_lookup(selectedconfig, "devices");
int i = 0;
int error = MONOCOQUE_ERROR_NONE;
while (i<configureddevices)
{
error = MONOCOQUE_ERROR_NONE;
DeviceSettings settings;
config_setting_t* config_device = config_setting_get_elem(config_devices, i);
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);
found = config_setting_lookup_string(config_device, "config", &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);
}
if (error == MONOCOQUE_ERROR_NONE)
{
numdevices++;
}
ds[i] = settings;
i++;
}
}
config_destroy(&cfg);
return numdevices;
}
int settingsfree(DeviceSettings ds)
{
if (ds.dev_subtype == SIMDEVTYPE_SIMWIND || ds.dev_subtype == SIMDEVTYPE_SHIFTLIGHTS)
if (ds.dev_type == SIMDEV_SERIAL)
{
if (ds.serialdevsettings.portdev != NULL)
{
free(ds.serialdevsettings.portdev);
}
}
if (ds.dev_type == SIMDEV_SOUND)
{
if (ds.sounddevsettings.dev != NULL)
{
free(ds.sounddevsettings.dev);
}
}
if(ds.has_config && ds.specific_config_file != NULL)
{
free(ds.specific_config_file);
}
return 0;
}
int monocoquesettingsfree(MonocoqueSettings* ms)
{
if(ms->tyre_diameter_config != NULL)
{
free(ms->tyre_diameter_config);
}
if(ms->config_str != NULL)
{
free(ms->config_str);
}
if(ms->log_filename_str != NULL)
{
free(ms->log_filename_str);
}
if(ms->log_dirname_str != NULL)
{
free(ms->log_dirname_str);
}
}

View File

@ -1,6 +1,7 @@
#ifndef _CONFIGHELPER_H
#define _CONFIGHELPER_H
#include <pulse/channelmap.h>
#include <stdbool.h>
#include <stdint.h>
@ -8,6 +9,8 @@
#include "parameters.h"
#include "../devices/sounddevice.h"
typedef enum
{
SIMDEV_UNKNOWN = 0,
@ -21,13 +24,32 @@ typedef enum
{
SIMDEVTYPE_UNKNOWN = 0,
SIMDEVTYPE_TACHOMETER = 1,
SIMDEVTYPE_SHIFTLIGHTS = 2,
SIMDEVTYPE_SIMWIND = 3,
SIMDEVTYPE_ENGINESOUND = 4,
SIMDEVTYPE_GEARSOUND = 5
SIMDEVTYPE_USBHAPTIC = 2,
SIMDEVTYPE_SHIFTLIGHTS = 3,
SIMDEVTYPE_SIMWIND = 4,
SIMDEVTYPE_SERIALHAPTIC = 5,
SIMDEVTYPE_USBWHEEL = 6,
SIMDEVTYPE_SERIALWHEEL = 7,
SIMDEVTYPE_SIMLED = 8,
SIMDEVTYPE_ARDUINOCUSTOM = 9
}
DeviceSubType;
typedef enum
{
SIMDEVSUBTYPE_UNKNOWN = 0,
SIMDEVSUBTYPE_CAMMUSC5 = 1,
SIMDEVSUBTYPE_CAMMUSC12 = 2,
SIMDEVSUBTYPE_MOZAR5 = 3,
SIMDEVSUBTYPE_CSLELITEV3PEDALS = 4,
SIMDEVSUBTYPE_SIMAGICP1000PEDALS = 5,
SIMDEVSUBTYPE_SIMAGICGTNEO = 6,
SIMDEVSUBTYPE_MOZA_NEW = 7,
SIMDEVSUBTYPE_LOGITECH_G29 = 8,
SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL = 9
}
DeviceSubSubType;
typedef enum
{
@ -40,6 +62,52 @@ typedef enum
}
SimulatorUpdate;
typedef enum
{
EFFECT_ENGINERPM = 0,
EFFECT_GEARSHIFT = 1,
EFFECT_ABSBRAKES = 2,
EFFECT_TYRESLIP = 3,
EFFECT_TYRELOCK = 4,
EFFECT_SUSPENSION = 5
}
VibrationEffectType;
typedef enum
{
MOTOR_1 = 0,
MOTOR_2 = 1,
MOTOR_3 = 2,
MOTOR_4 = 3,
MOTOR_1_4 = 4,
MOTOR_2_4 = 5,
MOTOR_3_4 = 6,
MOTOR_1_2 = 7,
MOTOR_1_3 = 8,
MOTOR_2_3 = 9,
MOTOR_1_2_3_4 = 10,
MOTOR_1_2_3 = 11,
MOTOR_2_3_4 = 12,
MOTOR_1_2_4 = 13,
MOTOR_1_3_4 = 14
}
MotorPosition;
typedef enum
{
MONOCOQUE_GEAR_REVERSE = 0,
MONOCOQUE_GEAR_NEUTRAL = 1,
MONOCOQUE_GEAR_ONE = 2,
MONOCOQUE_GEAR_TWO = 3,
MONOCOQUE_GEAR_THREE = 4,
MONOCOQUE_GEAR_FOUR = 5,
MONOCOQUE_GEAR_FIVE = 6,
MONOCOQUE_GEAR_SIX = 7,
MONOCOQUE_GEAR_SEVEN = 8,
MONOCOQUE_GEAR_EIGHT = 9,
}
MonocoqueGear;
typedef enum
{
MONOCOQUE_ERROR_NONE = 0,
@ -47,14 +115,37 @@ typedef enum
MONOCOQUE_ERROR_INVALID_SIM = 2,
MONOCOQUE_ERROR_INVALID_DEV = 3,
MONOCOQUE_ERROR_NODATA = 4,
MONOCOQUE_ERROR_UNKNOWN_DEV = 5
MONOCOQUE_ERROR_UNKNOWN_DEV = 5,
MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE = 6,
}
MonocoqueError;
typedef enum
{
FRONTLEFT = 0,
FRONTRIGHT = 1,
REARLEFT = 2,
REARRIGHT = 3,
FRONTS = 4,
REARS = 5,
ALLFOUR = 6
}
MonocoqueTyreIdentifier;
typedef struct
{
ProgramAction program_action;
Simulator sim_name;
SimulatorAPI sim_name;
int configcheck;
int useconfig;
int verbosity_count;
int fps;
bool force_udp_mode;
bool disable_audio;
char* tyre_diameter_config;
char* config_str;
char* log_filename_str;
char* log_dirname_str;
}
MonocoqueSettings;
@ -71,16 +162,63 @@ TachometerSettings;
typedef struct
{
char* portdev;
MotorPosition motorsposition;
int numlights;
int numleds;
int startled;
int endled;
float ampfactor;
float fanpower;
int baud;
}
SerialDeviceSettings;
typedef struct
{
uint32_t frequency;
uint32_t volume;
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;
typedef struct
{
int value0;
int value1;
char* dev;
}
USBDeviceSettings;
typedef struct
{
bool is_valid;
int fps;
DeviceType dev_type;
DeviceSubType dev_subtype;
DeviceSubSubType dev_subsubtype;
// to get really fancy move the effect information to it's own structure that would be a member of
// any device settings member structure that can carry an effect
VibrationEffectType effect_type;
MonocoqueTyreIdentifier tyre;
double threshold;
bool has_config;
char* specific_config_file;
// union?
TachometerSettings tachsettings;
SerialDeviceSettings serialdevsettings;
SoundDeviceSettings sounddevsettings;
USBDeviceSettings usbdevsettings;
}
DeviceSettings;
@ -90,4 +228,17 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
int settingsfree(DeviceSettings ds);
int monocoquesettingsfree(MonocoqueSettings* ms);
int strcicmp(char const *a, char const *b);
int getconfigtouse2(const char* config_file_str, char* car, int sim);
int getconfigtouse1(const char* config_file_str, char* car, int sim);
int getconfigtouse(const char* config_file_str, char* car, int sim);
int configcheck(const char* config_file_str, int confignum, int* devices);
int uiloadconfig(const char* config_file_str, int confignum, int configureddevices, MonocoqueSettings* ms, DeviceSettings* ds);
int getNumberOfConfigs(const char* config_file_str);
#endif

View File

@ -13,24 +13,47 @@
#include <string.h>
void create_dir(char* dir)
{
struct stat st = {0};
if (stat(dir, &st) == -1)
{
mkdir(dir, 0700);
}
}
void create_xdg_dir(const char* dir)
{
struct stat st = {0};
if (stat(dir, &st) == -1)
{
mkdir(dir, 0700);
}
}
char* create_user_dir(char* home_dir_str, const char* dirtype, const char* programname)
{
// +3 for slashes
size_t ss = (4 + strlen(home_dir_str) + strlen(dirtype) + strlen(programname));
char* config_dir_str = malloc(ss);
snprintf (config_dir_str, ss, "%s/%s/%s/", home_dir_str, dirtype, programname);
create_dir(config_dir_str);
return config_dir_str;
}
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;
@ -175,28 +198,94 @@ void restrict_folders_to_cache(char* path, int cachesize)
}
bool does_directory_exist(char* path, char* dirname)
bool does_directory_exist(char* path)
{
struct dirent* de;
DIR* dr = opendir(path);
if (dr == NULL)
DIR* dir = opendir(path);
if (dir)
{
printf("Could not open current directory");
// Directory exists
closedir(dir);
return true;
}
else
{
// Directory does not exist or cannot be opened
return false;
}
}
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
bool answer = false;
while ((de = readdir(dr)) != NULL)
bool does_file_exist(const char* file)
{
if (strcmp(dirname,de->d_name) == 0)
if (file == NULL)
{
answer = true;
return false;
}
#if defined(OS_WIN)
#if defined(WIN_API)
// if you want the WinAPI, versus CRT
if (strnlen(file, MAX_PATH+1) > MAX_PATH)
{
// ... throw error here or ...
return false;
}
DWORD res = GetFileAttributesA(file);
return (res != INVALID_FILE_ATTRIBUTES &&
!(res& FILE_ATTRIBUTE_DIRECTORY));
#else
// Use Win CRT
struct stat fi;
if (_stat(file, &fi) == 0)
{
#if defined(S_ISSOCK)
// sockets come back as a 'file' on some systems
// so make sure it's not a socket or directory
// (in other words, make sure it's an actual file)
return !(S_ISDIR(fi.st_mode)) &&
!(S_ISSOCK(fi.st_mode));
#else
return !(S_ISDIR(fi.st_mode));
#endif
}
return false;
#endif
#else
struct stat fi;
if (stat(file, &fi) == 0)
{
#if defined(S_ISSOCK)
return !(S_ISDIR(fi.st_mode)) &&
!(S_ISSOCK(fi.st_mode));
#else
return !(S_ISDIR(fi.st_mode));
#endif
}
return false;
#endif
}
closedir(dr);
return answer;
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;
}

View File

@ -3,10 +3,15 @@
#include <stdbool.h>
void create_dir(char* dir);
void create_xdg_dir(const char* dir);
char* create_user_dir(char* home_dir_str, const char* dirtype, const char* programpath);
char* gethome();
char* str2md5(const char* str, int length);
bool does_directory_exist(char* path, char* dirname);
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

View File

@ -2,12 +2,39 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <libconfig.h>
#include <argtable2.h>
#include <regex.h>
int freeparams(Parameters* p)
{
if(p->config_dirpath != NULL)
{
free(p->config_dirpath);
}
if(p->config_filepath != NULL)
{
free(p->config_filepath);
}
if(p->log_filename_str != NULL)
{
free(p->log_filename_str);
}
if(p->log_fullfilename_str != NULL)
{
free(p->log_fullfilename_str);
}
if(p->log_dirname_str != NULL)
{
free(p->log_dirname_str);
}
return 0;
}
ConfigError getParameters(int argc, char** argv, Parameters* p)
{
@ -17,6 +44,14 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
p->program_action = 0;
p->max_revs = 0;
p->verbosity_count = 0;
p->fps = 60;
p->disable_audio = false;
p->udp = false;
p->user_specified_config_file = false;
p->user_specified_log_file = false;
p->user_specified_config_dir = false;
// setup argument handling structures
const char* progname = "monocoque";
@ -26,11 +61,16 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
struct arg_lit* arg_verbosity3 = arg_litn("v","verbose", 0, 2, "increase logging verbosity");
struct arg_rex* cmd1 = arg_rex1(NULL, NULL, "play", NULL, REG_ICASE, NULL);
struct arg_str* arg_sim = arg_strn("s", "sim", "<gamename>", 0, 1, NULL);
struct arg_lit* help = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_lit* arg_udp = arg_lit0("d", "udp", "force udp mode for sims which support it");
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_str1(NULL, NULL, "configdir", "<config_dir>");
struct arg_int* arg_fps = arg_int0("f", "fps", "fps", "main data refresh rate");
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_sim,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);
@ -45,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");
@ -73,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);
@ -82,14 +121,57 @@ 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->sim_string = arg_sim->sval[0];
p->verbosity_count = arg_verbosity1->count;
if(arg_conf->count > 0)
{
p->config_filepath = strdup(arg_conf->filename[0]);
p->user_specified_config_file = true;
}
if (arg_udp->count > 0)
{
p->udp = true;
}
if (arg_audio1->count > 0)
{
p->disable_audio = true;
}
if(arg_fps->count > 0)
{
p->fps = arg_fps->ival[0];
}
if(arg_log->count > 0)
{
char* filename = strdup(arg_log->filename[0]);
p->log_fullfilename_str = strdup(arg_log->filename[0]);
p->log_filename_str = strdup(arg_log->basename[0]);
char* dname;
dname = dirname(filename);
p->log_dirname_str = strdup(dname);
p->user_specified_log_file = true;
free(filename);
}
exitcode = E_SUCCESS_AND_DO;
}
else
if (nerrors2==0)
else if (nerrors2==0)
{
if (help2->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;
}
p->program_action = A_CONFIG_TACH;
p->max_revs = arg_max_revs->ival[0];
p->granularity = 1;
@ -103,43 +185,52 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
}
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;
}
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");
}
}
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);
@ -167,5 +258,4 @@ cleanup:
arg_freetable(argtable2,sizeof(argtable2)/sizeof(argtable2[0]));
arg_freetable(argtable3,sizeof(argtable3)/sizeof(argtable3[0]));
return exitcode;
}

View File

@ -9,12 +9,25 @@ typedef struct
int program_action;
const char* sim_string;
const char* save_file;
int fps;
int max_revs;
int granularity;
int verbosity_count;
Simulator sim;
SimulatorAPI sim;
bool simon;
bool udp;
bool disable_audio;
char* config_filepath;
char* config_dirpath;
bool user_specified_config_file;
bool user_specified_config_dir;
bool user_specified_log_file;
char* log_filename_str;
char* log_fullfilename_str;
char* log_dirname_str;
}
Parameters;
@ -35,6 +48,7 @@ typedef enum
}
ConfigError;
int freeparams(Parameters* p);
ConfigError getParameters(int argc, char** argv, Parameters* p);
struct _errordesc

View File

@ -2,37 +2,20 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <basedir_fs.h>
#include <libconfig.h>
#include "gameloop/gameloop.h"
#include "gameloop/tachconfig.h"
#include "devices/simdevice.h"
#include "devices/sound.h"
#include "helper/parameters.h"
#include "helper/dirhelper.h"
#include "helper/confighelper.h"
#include "simulatorapi/simapi/simapi/simdata.h"
#include "slog/slog.h"
int create_dir(char* dir)
{
struct stat st = {0};
if (stat(dir, &st) == -1)
{
mkdir(dir, 0700);
}
}
char* create_user_dir(char* dirtype)
{
char* home_dir_str = gethome();
char* config_dir_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen(dirtype) + strlen("monocoque/"));
strcpy(config_dir_str, home_dir_str);
strcat(config_dir_str, dirtype);
strcat(config_dir_str, "monocoque");
create_dir(config_dir_str);
free(config_dir_str);
}
#define PROGRAM_NAME "monocoque"
void display_banner()
{
@ -43,37 +26,119 @@ void display_banner()
printf("/_/ /_/ \\____/ /_/ |_/ \\____/ \\____/ \\____/ \\___\\_\\\\____/ /_____/ \n");
}
void SetSettingsFromParameters(Parameters* p, MonocoqueSettings* ms, char* configdir_str, char* cachedir_str)
{
if(p->user_specified_config_file == true && does_file_exist(p->config_filepath))
{
ms->config_str = strdup(p->config_filepath);
}
else
{
if(p->user_specified_config_dir == true && does_directory_exist(p->config_dirpath))
{
asprintf(&ms->config_str, "%s/%s", p->config_dirpath, "monocoque.config");
}
else
{
asprintf(&ms->config_str, "%s%s", configdir_str, "monocoque.config");
}
}
if(p->user_specified_log_file == true && does_file_exist(p->log_fullfilename_str))
{
ms->log_dirname_str = strdup(p->log_dirname_str);
ms->log_filename_str = strdup(p->log_filename_str);
}
else
{
ms->log_dirname_str = strdup(cachedir_str);
ms->log_filename_str = strdup("monocoque.log");
}
ms->fps = p->fps;
ms->verbosity_count = p->verbosity_count;
ms->program_action = A_TEST;
if (p->program_action == A_PLAY)
{
ms->program_action = A_PLAY;
}
if (p->program_action == A_CONFIG_TACH)
{
ms->program_action = A_CONFIG_TACH;
}
ms->force_udp_mode = false;
ms->disable_audio = p->disable_audio;
}
int main(int argc, char** argv)
{
display_banner();
Parameters* p = malloc(sizeof(Parameters));
char* home_dir_str = gethome();
if(home_dir_str == NULL)
{
fprintf(stderr, "You need a home directory");
return 0;
}
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;
}
ms->program_action = p->program_action;
char* home_dir_str = gethome();
create_user_dir("/.config/");
create_user_dir("/.cache/");
char* config_file_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.config/") + strlen("monocoque/monocoque.config"));
char* cache_dir_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.cache/monocoque/"));
strcpy(config_file_str, home_dir_str);
strcat(config_file_str, "/.config/");
strcpy(cache_dir_str, home_dir_str);
strcat(cache_dir_str, "/.cache/monocoque/");
strcat(config_file_str, "monocoque/monocoque.config");
xdgHandle xdg;
if(!xdgInitHandle(&xdg))
{
fprintf(stderr, "Function xdgInitHandle() failed, is $HOME unset?\n");
goto cleanup_final;
}
const char* config_home_str = xdgConfigHome(&xdg);
const char* cache_home_str = xdgCacheHome(&xdg);
char* cachedir_str = NULL;
char* configdir_str = NULL;
if(p->user_specified_config_file == false && p->user_specified_config_dir == false)
{
create_xdg_dir(config_home_str);
configdir_str = create_user_dir(home_dir_str, ".config", PROGRAM_NAME);
}
if(p->user_specified_log_file == false)
{
create_xdg_dir(cache_home_str);
cachedir_str = create_user_dir(home_dir_str, ".cache", PROGRAM_NAME);
}
fprintf(stderr, "applying settings\n");
SetSettingsFromParameters(p, ms, configdir_str, cachedir_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;
slgCfg.eDateControl = SLOG_TIME_ONLY;
strcpy(slgCfg.sFileName, "monocoque.log");
strcpy(slgCfg.sFilePath, cache_dir_str);
strcpy(slgCfg.sFileName, ms->log_filename_str);
strcpy(slgCfg.sFilePath, ms->log_dirname_str);
slgCfg.nTraceTid = 0;
slgCfg.nToScreen = 1;
slgCfg.nUseHeap = 0;
@ -81,37 +146,55 @@ int main(int argc, char** argv)
slgCfg.nFlush = 0;
slgCfg.nFlags = SLOG_FLAGS_ALL;
slog_config_set(&slgCfg);
if (p->verbosity_count < 2)
if (ms->verbosity_count < 2)
{
slog_disable(SLOG_TRACE);
}
if (p->verbosity_count < 1)
if (ms->verbosity_count < 1)
{
slog_disable(SLOG_DEBUG);
}
xdgWipeHandle(&xdg);
slogi("Loading configuration file: %s", config_file_str);
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;
slogi("Testing monocoque config file: %s", ms->config_str);
slogd("using diameters file %s %i", ms->tyre_diameter_config, ms->configcheck);
config_t cfg;
config_init(&cfg);
config_setting_t* config_devices = NULL;
if (!config_read_file(&cfg, config_file_str))
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");
config_devices = config_lookup(&cfg, "devices");
slogi("Opened and validated monocoque configuration file");
}
free(config_file_str);
free(cache_dir_str);
config_destroy(&cfg);
if (p->program_action == A_CONFIG_TACH)
if (ms->program_action == A_CONFIG_TACH)
{
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);
@ -122,7 +205,13 @@ int main(int argc, char** argv)
}
else
{
error = devinit(tachdev, 1, ds);
int devices = 0;
devices = devinit(tachdev, siminfo, 1, ds, ms);
if(devices < 1)
{
error = MONOCOQUE_ERROR_INVALID_DEV;
}
}
if (error != MONOCOQUE_ERROR_NONE)
@ -134,7 +223,13 @@ int main(int argc, char** argv)
slogi("configuring tachometer with max revs: %i, granularity: %i, saving to %s", p->max_revs, p->granularity, p->save_file);
config_tachometer(p->max_revs, p->granularity, p->save_file, tachdev, sdata);
}
devfree(tachdev, 1);
slogt("freeing devices if necessary");
if(tachdev->initialized == true)
{
slogt("freeing tachmoeter device");
tachdev->free(tachdev);
}
//free(tachdev);
free(sdata);
free(ds);
@ -143,56 +238,25 @@ int main(int argc, char** argv)
{
int error = 0;
int configureddevices = config_setting_length(config_devices);
int numdevices = 0;
DeviceSettings ds[configureddevices];
slogi("found %i devices in configuration", configureddevices);
int i = 0;
while (i<configureddevices)
{
error = MONOCOQUE_ERROR_NONE;
DeviceSettings settings;
//setupsound();
bool pulseaudio = false;
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;
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);
if (error == MONOCOQUE_ERROR_NONE)
if (ms->program_action == A_PLAY)
{
error = devsetup(device_type, device_subtype, device_config_file, ms, &settings, config_device);
}
if (error == MONOCOQUE_ERROR_NONE)
ms->useconfig = 1;
slogi("running monocoque in gameloop mode..");
//#ifdef USE_PULSEAUDIO
//pa_threaded_mainloop_unlock(mainloop);
pulseaudio = true;
//#endif
if(ms->disable_audio == false)
{
numdevices++;
setupsound();
}
ds[i] = settings;
i++;
}
i = 0;
int j = 0;
error = MONOCOQUE_ERROR_NONE;
SimDevice* devices = malloc(numdevices * sizeof(SimDevice));
int initdevices = devinit(devices, configureddevices, ds);
if (p->program_action == A_PLAY)
{
//slogi("running monocoque in gameloop mode..");
//error = strtogame(p->sim_string, ms);
//if (error != MONOCOQUE_ERROR_NONE)
//{
// goto cleanup_final;
//}
error = looper(devices, initdevices, p);
//error = looper(devices, numdevices, p);
error = monocoque_mainloop(ms);
if (error == MONOCOQUE_ERROR_NONE)
{
slogi("Game loop exited succesfully with error code: %i", error);
@ -201,11 +265,40 @@ 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...");
error = tester(devices, initdevices);
if(ms->disable_audio == false)
{
setupsound();
}
ms->useconfig = 0;
int configs = getNumberOfConfigs(ms->config_str);
int confignum = getconfigtouse(ms->config_str, "default", configs-1);
int configureddevices;
configcheck(ms->config_str, confignum, &configureddevices);
DeviceSettings* ds = malloc(configureddevices * sizeof(DeviceSettings));
slogd("loading confignum %i, with %i devices.", confignum, configureddevices);
int numdevices = uiloadconfig(ms->config_str, confignum, configureddevices, ms, ds);
SimDevice* simdevices = malloc(numdevices * sizeof(SimDevice));
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]);
}
free(ds);
error = tester(simdevices, numdevices);
if (error == MONOCOQUE_ERROR_NONE)
{
slogi("Test exited succesfully with error code: %i", error);
@ -214,29 +307,27 @@ int main(int argc, char** argv)
{
sloge("Test exited with error code: %i", error);
}
}
devfree(devices, initdevices);
i = 0;
// improve the api around the config helper
// i don't like that i stack allocated but hid a malloc inside
for( i = 0; i < configureddevices; i++)
for (int x = 0; x < numdevices; x++)
{
settingsfree(ds[i]);
if (simdevices[x].initialized == true)
{
simdevices[x].free(&simdevices[x]);
}
}
if(ms->disable_audio == false)
{
freesound();
}
}
slog_destroy();
}
configcleanup:
config_destroy(&cfg);
cleanup_final:
monocoquesettingsfree(ms);
free(ms);
freeparams(p);
free(p);
exit(0);
}

View File

@ -5,11 +5,41 @@ set(simulatorapi_source_files
simapi/simapi/test.h
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
)
add_library(simulatorapi STATIC ${simulatorapi_source_files})

@ -1 +1 @@
Subproject commit ee41c498ee24c410bc551cf09ea68a9fbde0cee7
Subproject commit ea44d0e57f855a12242b35553623250e22aa0f3c

View File

@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
* Copyleft (C) 2015-2020 Sun Dro (f4tb0y@protonmail.com)
* Copyleft (C) 2015-2023 Sun Dro (s.kalatoz@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -27,7 +27,6 @@
#endif
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@ -37,9 +36,10 @@
#include <time.h>
#include "slog.h"
#if !defined(__APPLE__) && !defined(DARWIN) && !defined(WIN32)
#ifdef __linux__
#include <syscall.h>
#endif
#include <sys/time.h>
#ifdef WIN32
@ -50,30 +50,35 @@
#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
#endif
typedef struct slog
{
unsigned int nTdSafe:1;
typedef struct slog_file {
uint8_t nCurrDay;
FILE *pHandle;
} slog_file_t;
typedef struct slog {
pthread_mutex_t mutex;
slog_config_t config;
slog_file_t logFile;
uint8_t nTdSafe;
} slog_t;
typedef struct XLogCtx
{
typedef struct slog_context {
const char *pFormat;
slog_flag_t eFlag;
slog_date_t date;
uint8_t nFullColor;
uint8_t nNewLine;
} slog_context_t;
static volatile int g_nHaveSlogVerShort = 0;
static volatile int g_nHaveSlogVerLong = 0;
static char g_slogVerShort[128];
static char g_slogVerLong[256];
static slog_t g_slog;
static void slog_sync_init(slog_t *pSlog)
{
if (!pSlog->nTdSafe)
{
return;
}
if (!pSlog->nTdSafe) return;
pthread_mutexattr_t mutexAttr;
if (pthread_mutexattr_init(&mutexAttr) ||
@ -82,7 +87,7 @@ static void slog_sync_init(slog_t* pSlog)
pthread_mutexattr_destroy(&mutexAttr))
{
printf("<%s:%d> %s: [ERROR] Can not initialize mutex: %d\n",
__FILE__, __LINE__, __FUNCTION__, errno);
__FILE__, __LINE__, __func__, errno);
exit(EXIT_FAILURE);
}
@ -93,7 +98,7 @@ static void slog_lock(slog_t* pSlog)
if (pSlog->nTdSafe && pthread_mutex_lock(&pSlog->mutex))
{
printf("<%s:%d> %s: [ERROR] Can not lock mutex: %d\n",
__FILE__, __LINE__, __FUNCTION__, errno);
__FILE__, __LINE__, __func__, errno);
exit(EXIT_FAILURE);
}
@ -104,7 +109,7 @@ static void slog_unlock(slog_t* pSlog)
if (pSlog->nTdSafe && pthread_mutex_unlock(&pSlog->mutex))
{
printf("<%s:%d> %s: [ERROR] Can not unlock mutex: %d\n",
__FILE__, __LINE__, __FUNCTION__, errno);
__FILE__, __LINE__, __func__, errno);
exit(EXIT_FAILURE);
}
@ -113,10 +118,7 @@ static void slog_unlock(slog_t* pSlog)
static const char *slog_get_indent(slog_flag_t eFlag)
{
slog_config_t *pCfg = &g_slog.config;
if (!pCfg->nIndent)
{
return SLOG_EMPTY;
}
if (!pCfg->nIndent) return SLOG_EMPTY;
switch (eFlag)
{
@ -130,8 +132,7 @@ static const char* slog_get_indent(slog_flag_t eFlag)
case SLOG_TRACE:
case SLOG_FATAL:
case SLOG_ERROR:
default:
break;
default: break;
}
return SLOG_EMPTY;
@ -141,22 +142,14 @@ static const char* slog_get_tag(slog_flag_t eFlag)
{
switch (eFlag)
{
case SLOG_NOTE:
return "note";
case SLOG_INFO:
return "info";
case SLOG_WARN:
return "warn";
case SLOG_DEBUG:
return "debug";
case SLOG_ERROR:
return "error";
case SLOG_TRACE:
return "trace";
case SLOG_FATAL:
return "fatal";
default:
break;
case SLOG_NOTE: return "note";
case SLOG_INFO: return "info";
case SLOG_WARN: return "warn";
case SLOG_DEBUG: return "debug";
case SLOG_ERROR: return "error";
case SLOG_TRACE: return "trace";
case SLOG_FATAL: return "fatal";
default: break;
}
return NULL;
@ -167,35 +160,59 @@ static const char* slog_get_color(slog_flag_t eFlag)
switch (eFlag)
{
case SLOG_NOTAG:
case SLOG_NOTE:
return SLOG_EMPTY;
case SLOG_INFO:
return SLOG_COLOR_GREEN;
case SLOG_WARN:
return SLOG_COLOR_YELLOW;
case SLOG_DEBUG:
return SLOG_COLOR_BLUE;
case SLOG_ERROR:
return SLOG_COLOR_RED;
case SLOG_TRACE:
return SLOG_COLOR_CYAN;
case SLOG_FATAL:
return SLOG_COLOR_MAGENTA;
default:
break;
case SLOG_NOTE: return SLOG_EMPTY;
case SLOG_INFO: return SLOG_COLOR_GREEN;
case SLOG_WARN: return SLOG_COLOR_YELLOW;
case SLOG_DEBUG: return SLOG_COLOR_BLUE;
case SLOG_ERROR: return SLOG_COLOR_RED;
case SLOG_TRACE: return SLOG_COLOR_CYAN;
case SLOG_FATAL: return SLOG_COLOR_MAGENTA;
default: break;
}
return SLOG_EMPTY;
}
uint8_t slog_get_usec()
static void slog_close_file(slog_file_t *pFile)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
if (pFile->pHandle != NULL)
{
fclose(pFile->pHandle);
pFile->pHandle = NULL;
}
}
static uint8_t slog_open_file(slog_file_t *pFile, const slog_config_t *pCfg, const slog_date_t *pDate)
{
slog_close_file(pFile);
char sFilePath[SLOG_PATH_MAX + SLOG_NAME_MAX + SLOG_DATE_MAX];
snprintf(sFilePath, sizeof(sFilePath), "%s/%s-%04d-%02d-%02d.log",
pCfg->sFilePath, pCfg->sFileName, pDate->nYear, pDate->nMonth, pDate->nDay);
#ifdef _WIN32
if (fopen_s(&pFile->pHandle, sFilePath, "a")) pFile->pHandle = NULL;
#else
pFile->pHandle = fopen(sFilePath, "a");
#endif
if (pFile->pHandle == NULL)
{
printf("<%s:%d> %s: [ERROR] Failed to open file: %s (%s)\n",
__FILE__, __LINE__, __func__, sFilePath, strerror(errno));
return 0;
}
return (uint8_t)(tv.tv_usec / 10000);
pFile->nCurrDay = pDate->nDay;
return 1;
}
uint16_t slog_get_usec()
{
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) return 0;
return (uint16_t)(tv.tv_usec / 1000);
}
void slog_get_date(slog_date_t *pDate)
@ -217,12 +234,12 @@ void slog_get_date(slog_date_t* pDate)
pDate->nUsec = slog_get_usec();
}
static uint32_t slog_get_tid()
static size_t slog_get_tid()
{
#if defined(__APPLE__) || defined(DARWIN) || defined(WIN32)
return (uint32_t)pthread_self();
#ifdef __linux__
return (size_t)syscall(__NR_gettid);
#else
return syscall(__NR_gettid);
return (size_t)pthread_self();
#endif
}
@ -236,51 +253,49 @@ static void slog_create_tag(char* pOut, size_t nSize, slog_flag_t eFlag, const c
if (pTag == NULL)
{
snprintf(pOut, nSize, pIndent);
snprintf(pOut, nSize, "%s", pIndent);
return;
}
if (pCfg->eColorFormat != SLOG_COLORING_TAG)
{
snprintf(pOut, nSize, "<%s>%s", pTag, pIndent);
}
else
{
snprintf(pOut, nSize, "%s<%s>%s%s", pColor, pTag, SLOG_COLOR_RESET, pIndent);
}
if (pCfg->eColorFormat != SLOG_COLORING_TAG) snprintf(pOut, nSize, "<%s>%s", pTag, pIndent);
else snprintf(pOut, nSize, "%s<%s>%s%s", pColor, pTag, SLOG_COLOR_RESET, pIndent);
}
static void slog_create_tid(char *pOut, int nSize, uint8_t nTraceTid)
{
if (!nTraceTid)
{
pOut[0] = SLOG_NUL;
}
else
{
snprintf(pOut, nSize, "(%u) ", slog_get_tid());
}
if (!nTraceTid) pOut[0] = SLOG_NUL;
else snprintf(pOut, nSize, "(%zu) ", slog_get_tid());
}
static void slog_display_message(const slog_context_t *pCtx, const char *pInfo, int nInfoLen, const char *pInput)
{
slog_config_t *pCfg = &g_slog.config;
slog_file_t *pFile = &g_slog.logFile;
int nCbVal = 1;
uint8_t nFullColor = pCfg->eColorFormat == SLOG_COLORING_FULL ? 1 : 0;
const char *pSeparator = nInfoLen > 0 ? pCfg->sSeparator : SLOG_EMPTY;
const char* pReset = pCtx->nFullColor ? SLOG_COLOR_RESET : SLOG_EMPTY;
const char *pNewLine = pCtx->nNewLine ? SLOG_NEWLINE : SLOG_EMPTY;
const char *pMessage = pInput != NULL ? pInput : SLOG_EMPTY;
const char *pReset = nFullColor ? SLOG_COLOR_RESET : SLOG_EMPTY;
if (pCfg->logCallback != NULL)
{
size_t nLength = 0;
char *pLog = NULL;
nLength += asprintf(&pLog, "%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine);
nLength += asprintf(&pLog, "%s%s%s%s%s", pInfo,
pSeparator, pMessage, pReset, pNewLine);
if (pLog != NULL)
{
nCbVal = pCfg->logCallback(pLog, nLength, pCtx->eFlag, pCfg->pCallbackCtx);
nCbVal = pCfg->logCallback (
pLog,
nLength,
pCtx->eFlag,
pCfg->pCallbackCtx
);
free(pLog);
}
}
@ -288,30 +303,20 @@ static void slog_display_message(const slog_context_t* pCtx, const char* pInfo,
if (pCfg->nToScreen && nCbVal > 0)
{
printf("%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine);
if (pCfg->nFlush)
{
fflush(stdout);
}
if (pCfg->nFlush) fflush(stdout);
}
if (!pCfg->nToFile || nCbVal < 0)
{
return;
}
if (!pCfg->nToFile || nCbVal < 0) return;
const slog_date_t *pDate = &pCtx->date;
char sFilePath[SLOG_PATH_MAX + SLOG_NAME_MAX + SLOG_DATE_MAX];
snprintf(sFilePath, sizeof(sFilePath), "%s/%s-%04d-%02d-%02d.log",
pCfg->sFilePath, pCfg->sFileName, pDate->nYear, pDate->nMonth, pDate->nDay);
if (pFile->nCurrDay != pDate->nDay && pCfg->nRotate) slog_close_file(pFile);
if (pFile->pHandle == NULL && !slog_open_file(pFile, pCfg, pDate)) return;
FILE* pFile = fopen(sFilePath, "a");
if (pFile == NULL)
{
return;
}
fprintf(pFile->pHandle, "%s%s%s%s%s", pInfo,
pSeparator, pMessage, pReset, pNewLine);
fprintf(pFile, "%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine);
fclose(pFile);
if (pCfg->nFlush) fflush(pFile->pHandle);
if (!pCfg->nKeepOpen) slog_close_file(pFile);
}
static int slog_create_info(const slog_context_t *pCtx, char* pOut, size_t nSize)
@ -327,8 +332,7 @@ static int slog_create_info(const slog_context_t* pCtx, char* pOut, size_t nSize
snprintf(sDate, sizeof(sDate), "%02d:%02d:%02d.%03d ",
pDate->nHour, pDate->nMin, pDate->nSec, pDate->nUsec);
}
else
if (pCfg->eDateControl == SLOG_DATE_FULL)
else if (pCfg->eDateControl == SLOG_DATE_FULL)
{
snprintf(sDate, sizeof(sDate), "%04d.%02d.%02d-%02d:%02d:%02d.%03d ",
pDate->nYear, pDate->nMonth, pDate->nDay, pDate->nHour,
@ -336,8 +340,10 @@ static int slog_create_info(const slog_context_t* pCtx, char* pOut, size_t nSize
}
char sTid[SLOG_TAG_MAX], sTag[SLOG_TAG_MAX];
uint8_t nFullColor = pCfg->eColorFormat == SLOG_COLORING_FULL ? 1 : 0;
const char *pColorCode = slog_get_color(pCtx->eFlag);
const char* pColor = pCtx->nFullColor ? pColorCode : SLOG_EMPTY;
const char *pColor = nFullColor ? pColorCode : SLOG_EMPTY;
slog_create_tid(sTid, sizeof(sTid), pCfg->nTraceTid);
slog_create_tag(sTag, sizeof(sTag), pCtx->eFlag, pColorCode);
@ -356,17 +362,14 @@ static void slog_display_heap(const slog_context_t* pCtx, va_list args)
if (pMessage == NULL)
{
printf("<%s:%d> %s<error>%s %s: Can not allocate memory for input: errno(%d)\n",
__FILE__, __LINE__, SLOG_COLOR_RED, SLOG_COLOR_RESET, __FUNCTION__, errno);
__FILE__, __LINE__, SLOG_COLOR_RED, SLOG_COLOR_RESET, __func__, errno);
return;
}
int nLength = slog_create_info(pCtx, sLogInfo, sizeof(sLogInfo));
slog_display_message(pCtx, sLogInfo, nLength, pMessage);
if (pMessage != NULL)
{
free(pMessage);
}
if (pMessage != NULL) free(pMessage);
}
static void slog_display_stack(const slog_context_t *pCtx, va_list args)
@ -379,7 +382,7 @@ static void slog_display_stack(const slog_context_t* pCtx, va_list args)
slog_display_message(pCtx, sLogInfo, nLength, sMessage);
}
void slog_display(slog_flag_t eFlag, uint8_t nNewLine, const char* pFormat, ...)
void slog_display(slog_flag_t eFlag, uint8_t nNewLine, char *pFormat, ...)
{
slog_lock(&g_slog);
slog_config_t *pCfg = &g_slog.config;
@ -393,7 +396,6 @@ void slog_display(slog_flag_t eFlag, uint8_t nNewLine, const char* pFormat, ...)
ctx.eFlag = eFlag;
ctx.pFormat = pFormat;
ctx.nNewLine = nNewLine;
ctx.nFullColor = pCfg->eColorFormat == SLOG_COLORING_FULL ? 1 : 0;
void(*slog_display_args)(const slog_context_t *pCtx, va_list args);
slog_display_args = pCfg->nUseHeap ? slog_display_heap : slog_display_stack;
@ -407,21 +409,30 @@ void slog_display(slog_flag_t eFlag, uint8_t nNewLine, const char* pFormat, ...)
slog_unlock(&g_slog);
}
size_t slog_version(char* pDest, size_t nSize, uint8_t nMin)
const char* slog_version(uint8_t nShort)
{
size_t nLength = 0;
if (nShort)
{
if (!g_nHaveSlogVerShort)
{
snprintf(g_slogVerShort, sizeof(g_slogVerShort), "%d.%d.%d",
SLOG_VERSION_MAJOR, SLOG_VERSION_MINOR, SLOG_BUILD_NUMBER);
/* Version short */
if (nMin)
nLength = snprintf(pDest, nSize, "%d.%d.%d",
SLOG_VERSION_MAJOR, SLOG_VERSION_MINOR, SLOG_BUILD_NUM);
g_nHaveSlogVerShort = 1;
}
/* Version long */
else
nLength = snprintf(pDest, nSize, "%d.%d build %d (%s)",
SLOG_VERSION_MAJOR, SLOG_VERSION_MINOR, SLOG_BUILD_NUM, __DATE__);
return g_slogVerShort;
}
return nLength;
if (!g_nHaveSlogVerLong)
{
snprintf(g_slogVerLong, sizeof(g_slogVerLong), "%d.%d build %d (%s)",
SLOG_VERSION_MAJOR, SLOG_VERSION_MINOR, SLOG_BUILD_NUMBER, __DATE__);
g_nHaveSlogVerLong = 1;
}
return g_slogVerLong;
}
void slog_config_get(slog_config_t *pCfg)
@ -434,6 +445,14 @@ void slog_config_get(slog_config_t* pCfg)
void slog_config_set(slog_config_t *pCfg)
{
slog_lock(&g_slog);
slog_config_t *pOldCfg = &g_slog.config;
slog_file_t *pFile = &g_slog.logFile;
if (!pCfg->nToFile ||
strncmp(pOldCfg->sFilePath, pCfg->sFilePath, sizeof(pOldCfg->sFilePath)) ||
strncmp(pOldCfg->sFileName, pCfg->sFileName, sizeof(pOldCfg->sFileName)))
slog_close_file(pFile); /* Log function will open it again if required */
g_slog.config = *pCfg;
slog_unlock(&g_slog);
}
@ -441,11 +460,11 @@ void slog_config_set(slog_config_t* pCfg)
void slog_enable(slog_flag_t eFlag)
{
slog_lock(&g_slog);
slog_config_t *pCfg = &g_slog.config;
if (!SLOG_FLAGS_CHECK(g_slog.config.nFlags, eFlag))
{
g_slog.config.nFlags |= eFlag;
}
if (eFlag == SLOG_FLAGS_ALL) pCfg->nFlags = SLOG_FLAGS_ALL;
else if (!SLOG_FLAGS_CHECK(pCfg->nFlags, eFlag)) pCfg->nFlags |= eFlag;
slog_unlock(&g_slog);
}
@ -453,11 +472,10 @@ void slog_enable(slog_flag_t eFlag)
void slog_disable(slog_flag_t eFlag)
{
slog_lock(&g_slog);
slog_config_t *pCfg = &g_slog.config;
if (SLOG_FLAGS_CHECK(g_slog.config.nFlags, eFlag))
{
g_slog.config.nFlags &= ~eFlag;
}
if (eFlag == SLOG_FLAGS_ALL) pCfg->nFlags = 0;
else if (SLOG_FLAGS_CHECK(pCfg->nFlags, eFlag)) pCfg->nFlags &= ~eFlag;
slog_unlock(&g_slog);
}
@ -498,8 +516,10 @@ void slog_callback_set(slog_cb_t callback, void* pContext)
void slog_init(const char* pName, uint16_t nFlags, uint8_t nTdSafe)
{
/* Set up default values */
slog_config_t *pCfg = &g_slog.config;
slog_file_t *pFile = &g_slog.logFile;
/* Set up default values */
pCfg->eColorFormat = SLOG_COLORING_TAG;
pCfg->eDateControl = SLOG_TIME_ONLY;
pCfg->pCallbackCtx = NULL;
@ -508,19 +528,24 @@ void slog_init(const char* pName, uint16_t nFlags, uint8_t nTdSafe)
pCfg->sSeparator[1] = '\0';
pCfg->sFilePath[0] = '.';
pCfg->sFilePath[1] = '\0';
pCfg->nKeepOpen = 0;
pCfg->nTraceTid = 0;
pCfg->nToScreen = 1;
pCfg->nUseHeap = 0;
pCfg->nToFile = 0;
pCfg->nIndent = 0;
pCfg->nRotate = 1;
pCfg->nFlush = 0;
pCfg->nFlags = nFlags;
const char *pFileName = (pName != NULL) ? pName : SLOG_NAME_DEFAULT;
snprintf(pCfg->sFileName, sizeof(pCfg->sFileName), "%s", pFileName);
pFile->pHandle = NULL;
pFile->nCurrDay = 0;
#ifdef WIN32
// Enable color support
/* Enable color support */
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOutput, &dwMode);
@ -535,8 +560,14 @@ void slog_init(const char* pName, uint16_t nFlags, uint8_t nTdSafe)
void slog_destroy()
{
slog_lock(&g_slog);
memset(&g_slog.config, 0, sizeof(g_slog.config));
g_slog.config.pCallbackCtx = NULL;
g_slog.config.logCallback = NULL;
slog_close_file(&g_slog.logFile);
slog_unlock(&g_slog);
if (g_slog.nTdSafe)
{

View File

@ -31,11 +31,12 @@ extern "C" {
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
/* SLog version information */
#define SLOG_VERSION_MAJOR 1
#define SLOG_VERSION_MINOR 8
#define SLOG_BUILD_NUM 26
#define SLOG_BUILD_NUMBER 37
/* Supported colors */
#define SLOG_COLOR_NORMAL "\x1B[0m"
@ -73,18 +74,17 @@ extern "C" {
#define SLOG_EMPTY ""
#define SLOG_NUL '\0'
typedef struct SLogDate
{
typedef struct slog_date {
uint16_t nYear;
uint8_t nMonth;
uint8_t nDay;
uint8_t nHour;
uint8_t nMin;
uint8_t nSec;
uint8_t nUsec;
uint16_t nUsec;
} slog_date_t;
uint8_t slog_get_usec();
uint16_t slog_get_usec();
void slog_get_date(slog_date_t *pDate);
/* Log level flags */
@ -117,32 +117,25 @@ typedef enum
SLOG_DATE_FULL
} slog_date_ctrl_t;
#define slog(...) \
slog_display(SLOG_NOTAG, 1, __VA_ARGS__)
/* Slog function definitions */
#define slog(...) slog_display(SLOG_NOTAG, 1, __VA_ARGS__)
#define slog_note(...) slog_display(SLOG_NOTE, 1, __VA_ARGS__)
#define slog_info(...) slog_display(SLOG_INFO, 1, __VA_ARGS__)
#define slog_warn(...) slog_display(SLOG_WARN, 1, __VA_ARGS__)
#define slog_debug(...) slog_display(SLOG_DEBUG, 1, __VA_ARGS__)
#define slog_error(...) slog_display(SLOG_ERROR, 1, __VA_ARGS__)
#define slog_trace(...) slog_display(SLOG_TRACE, 1, SLOG_THROW_LOCATION __VA_ARGS__)
#define slog_fatal(...) slog_display(SLOG_FATAL, 1, SLOG_THROW_LOCATION __VA_ARGS__)
#define slogwn(...) \
slog_display(SLOG_NOTAG, 0, __VA_ARGS__)
#define slog_note(...) \
slog_display(SLOG_NOTE, 1, __VA_ARGS__)
#define slog_info(...) \
slog_display(SLOG_INFO, 1, __VA_ARGS__)
#define slog_warn(...) \
slog_display(SLOG_WARN, 1, __VA_ARGS__)
#define slog_debug(...) \
slog_display(SLOG_DEBUG, 1, __VA_ARGS__)
#define slog_error(...) \
slog_display(SLOG_ERROR, 1, __VA_ARGS__)
#define slog_trace(...) \
slog_display(SLOG_TRACE, 1, SLOG_THROW_LOCATION __VA_ARGS__)
#define slog_fatal(...) \
slog_display(SLOG_FATAL, 1, SLOG_THROW_LOCATION __VA_ARGS__)
/* No new line definitions */
#define slog_wn(...) slog_display(SLOG_NOTAG, 0, __VA_ARGS__)
#define slog_note_wn(...) slog_display(SLOG_NOTE, 0, __VA_ARGS__)
#define slog_info_wn(...) slog_display(SLOG_INFO, 0, __VA_ARGS__)
#define slog_warn_wn(...) slog_display(SLOG_WARN, 0, __VA_ARGS__)
#define slog_debug_wn(...) slog_display(SLOG_DEBUG, 0, __VA_ARGS__)
#define slog_error_wn(...) slog_display(SLOG_ERROR, 0, __VA_ARGS__)
#define slog_trace_wn(...) slog_display(SLOG_TRACE, 0, SLOG_THROW_LOCATION __VA_ARGS__)
#define slog_fatal_wn(...) slog_display(SLOG_FATAL, 0, SLOG_THROW_LOCATION __VA_ARGS__)
/* Short name definitions */
#define slogn(...) slog_note(__VA_ARGS__)
@ -153,18 +146,28 @@ typedef enum
#define slogt(...) slog_trace(__VA_ARGS__)
#define slogf(...) slog_fatal(__VA_ARGS__)
typedef struct SLogConfig
{
/* Short name definitions without new line */
#define slogn_wn(...) slog_note_wn(__VA_ARGS__)
#define slogi_wn(...) slog_info_wn(__VA_ARGS__)
#define slogw_wn(...) slog_warn_wn(__VA_ARGS__)
#define slogd_wn(...) slog_debug_wn( __VA_ARGS__)
#define sloge_wn(...) slog_error_wn( __VA_ARGS__)
#define slogt_wn(...) slog_trace_wn(__VA_ARGS__)
#define slogf_wn(...) slog_fatal_wn(__VA_ARGS__)
typedef struct SLogConfig {
slog_date_ctrl_t eDateControl; // Display output with date format
slog_coloring_t eColorFormat; // Output color format control
slog_cb_t logCallback; // Log callback to collect logs
void* pCallbackCtx; // Data pointer passed to log callback
uint8_t nKeepOpen; // Keep file handle open for next file writes
uint8_t nTraceTid; // Trace thread ID and display in output
uint8_t nToScreen; // Enable screen logging
uint8_t nUseHeap; // Use dynamic allocation
uint8_t nToFile; // Enable file logging
uint8_t nIndent; // Enable indentations
uint8_t nRotate; // Enable log rotation
uint8_t nFlush; // Flush stdout after screen log
uint16_t nFlags; // Allowed log level flags
@ -173,7 +176,7 @@ typedef struct SLogConfig
char sFilePath[SLOG_PATH_MAX]; // Output file path for logs
} slog_config_t;
size_t slog_version(char* pDest, size_t nSize, uint8_t nMin);
const char* slog_version(uint8_t nShort);
void slog_config_get(slog_config_t *pCfg);
void slog_config_set(slog_config_t *pCfg);
@ -185,8 +188,8 @@ void slog_enable(slog_flag_t eFlag);
void slog_disable(slog_flag_t eFlag);
void slog_init(const char* pName, uint16_t nFlags, uint8_t nTdSafe);
void slog_display(slog_flag_t eFlag, uint8_t nNewLine, const char* pFormat, ...);
void slog_destroy(); // Needed only if the slog_init() function argument nTdSafe > 0
void slog_display(slog_flag_t eFlag, uint8_t nNewLine, char *pFormat, ...);
void slog_destroy(); // Required only if (nTdSafe > 0 || nKeepOpen > 0)
#ifdef __cplusplus
}

BIN
tests/a.out Executable file

Binary file not shown.

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;

BIN
tests/producer Executable file

Binary file not shown.

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");

View File

@ -0,0 +1,20 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <errno.h>
void display(char* prog, char* bytes, int n);
int main(void)
{
const char* name = "acpmf_physics"; // file name
shm_unlink(name);
}

View File

@ -1,81 +1,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <libserialport.h>
#include "../src/monocoque/simulatorapi/simapi/simapi/simdata.h"
/* Helper function for error handling. */
int check(enum sp_return result);
int main()
{
SimData sd;
sd.maxrpm = 6500;
sd.rpms = 0;
sd.altitude = 10;
sd.gear = 1;
sd.velocity = 74;
char* port_name = "/dev/ttyACM0";
/* The ports we will use. */
struct sp_port* port;
/* Open and configure each port. */
printf("Looking for port %s.\n", port_name);
check(sp_get_port_by_name(port_name, &port));
printf("Opening port.\n");
check(sp_open(port, SP_MODE_READ_WRITE));
printf("Setting port to 9600 8N1, no flow control.\n");
check(sp_set_baudrate(port, 9600));
check(sp_set_bits(port, 8));
check(sp_set_parity(port, SP_PARITY_NONE));
check(sp_set_stopbits(port, 1));
check(sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE));
while ( sd.rpms <= sd.maxrpm )
{
unsigned int timeout = 2000;
/* On success, sp_blocking_write() and sp_blocking_read()
* return the number of bytes sent/received before the
* timeout expired. We'll store that result here. */
int result;
int size = sizeof(SimData);
/* Send data. */
result = check(sp_blocking_write(port, &sd, size, timeout));
/* Check whether we sent all of the data. */
if (result == size)
{
printf("Sent %d bytes successfully, %i rpm.\n", size, sd.rpms);
}
else
{
printf("Timed out, %d/%d bytes sent.\n", result, size);
}
sd.rpms += 1000;
if ( sd.rpms == sd.maxrpm + 1000 )
{
sd.rpms = 0;
}
if ( sd.rpms > sd.maxrpm )
{
sd.rpms = sd.maxrpm;
}
sleep(1);
}
check(sp_close(port));
sp_free_port(port);
}
#include "../src/arduino/shiftlights/shiftlights.h"
/* Helper function for error handling. */
int check(enum sp_return result)
@ -104,3 +32,209 @@ int check(enum sp_return result)
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/ttyACM0";
/* The ports we will use. */
struct sp_port* port;
/* 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 | SP_MODE_WRITE));
printf("Setting port to 115200 8N1, no flow control.\n");
check(sp_set_baudrate(port, 115200));
check(sp_set_bits(port, 8));
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;
int result = 0;
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++)
{
bytes[j] = 0x00;
}
bytes[0] = 0xff;
bytes[1] = 0xff;
bytes[2] = 0xff;
bytes[3] = 0xff;
bytes[4] = 0xff;
bytes[5] = 0xff;
bytes[6] = 0x73;
bytes[7] = 0x6c;
bytes[8] = 0x65;
bytes[9] = 0x64;
bytes[10] = 0x73;
bytes[bufsize-1] = 0xfd;
bytes[bufsize-2] = 0xfe;
bytes[bufsize-3] = 0xff;
for( int i = 0; i < numlights; i++)
{
bytes[(i * 3) + 11 + 1] = 0xff;
/* Send data. */
result = check(sp_blocking_write(port, &bytes, bufsize, timeout));
/* Check whether we sent all of the data. */
if (result == bufsize)
{
printf("Sent %d bytes successfully, %i lit leds.\n", bufsize, numlights);
}
else
{
printf("Timed out, %d/%d bytes sent.\n", result, bufsize);
}
sleep(2);
}
for( int i = 0; i < numlights; i++)
{
bytes[(i * 3) + 11 + 1] = 0x00;
}
result = check(sp_blocking_write(port, &bytes, bufsize, timeout));
check(sp_close(port));
sp_free_port(port);
}

View File

@ -0,0 +1,47 @@
# Maintainer: Paul Jones <paul@spacefreak18.xyz>
_reponame=monocoque
pkgname=monocoque-git
pkgver=0.1.0r94
pkgrel=2
pkgdesc="Device Manager for Racing Sims"
arch=('x86_64')
url="https://github.com/spacefreak18/monocoque"
license=('GPL3')
depends=(
hidapi
libserialport
libxml2
argtable
libconfig
libpulse
libxdg-basedir
libuv
)
makedepends=(
git
cmake
)
source=(
git+https://github.com/spacefreak18/monocoque
)
sha256sums=(
'SKIP'
)
pkgver() {
cd "$srcdir/$_reponame"
git describe --long --tags | cut -d "-" -f 1-2 | tr "-" "r"
}
package() {
cd "$srcdir/$_reponame" || exit 1
git submodule sync --recursive
git submodule update --init --recursive
mkdir -p build
cd build
cmake -DUSE_PULSEAUDIO=yes ..
make
mkdir -p "${pkgdir}/usr/bin/"
cp "$srcdir/$_reponame"/build/$_reponame "${pkgdir}/usr/bin/$_reponame"
}

Some files were not shown because too many files have changed in this diff Show More