[Work In Progress]
Hello, Sim Racing Enthusiasts!
I'm excited to share a comprehensive tutorial on building your own wind simulator to enhance your sim racing experience. This project is perfect for those who love DIY projects and are looking to add an extra layer of realism to their racing setup.
I will consider to do a custom PCB and release it open source so you don't have to mess around with protoboards.
Thanks to ChatGPT who helped to keep it clear and organized.
Overview:
This wind simulator interacts with your racing games to adjust the fan speed based on the speed of your car in the game, providing realistic wind feedback. It's a fantastic addition to any sim racing rig.
<img src=" removed link " />
Bill of Materials:
To get started, you'll need the following components:
- Fans (specific model based on your preference for noise and airflow)
- I used Noctua NF-A14 iPPC-3000 as they were suggested on the Thingiverse page
- 3D printed parts for fan housing and mounting
- Microcontroller (e.g., ESP32/ESP8266)
- This tutorial covers the ESP32, but it would also work with the ESP8266
- Power supplies suitable for your fans and microcontroller
- Those fans needs 55mA each, plus the power consumption of the buck converter and the ESP32, a 12V@2A should be sufficient. Keep in mind that you may want to reuse that supply in the future for other addons, so I would aim for about 5A.
- You will also need a buck converter to step down from 12V to 5V, I used a LM2596 sold on Amazon
- Wires, connectors, and other miscellaneous hardware
- Depending on your rig, you will want to use some action camera mounts for the fans.
For the 3D printed parts, you can find the files on Thingiverse: <a href=" removed link " target="_blank" rel="noopener">3D Printed Parts for Wind Simulator.
Firmware:
The brain of our wind simulator is controlled by firmware that communicates with SimHub software to adjust fan speed in real-time. You can download the firmware from GitHub: <a href=" removed link " target="_blank" rel="noopener">ESP-SimHub Firmware.
Procedure:
-
3D Printing: Start by 3D printing the parts from the Thingiverse link provided above. Ensure your prints are solid and without defects for the best durability and performance.
-
Assembly: Once you have all the 3D printed parts, assemble them according to the design. This will involve mounting the fans to the 3D printed housings and securing everything in place.
-
Electronics Setup: Follow this wiring diagram to connect your microcontroller to the fans. Ensure all connections are secure and double-check for any potential short circuits. I used a custom PCB I had laying around but mostly any protoboard will do.
<img src=" removed link " />
-
Firmware Configuration: Flash the firmware to your microcontroller using the instructions provided on the GitHub page. This will involve downloading the ESP-SimHub code, compiling it, and uploading it to your microcontroller using a suitable IDE (e.g., VS Code). Here are the steps to configure it adapt the original code for the ESP32
- In the main.cpp file, edit lines 7 to 15 to add your wifi credentials and lines 791 to 827 to adjust which output of your ESP32 you used. In this example I used 2 fans, connected to pins 25 and 26. Keep in mind that there are <a href=" removed link " target="_blank" rel="noopener">pins that you can't use:
#define INCLUDE_WIFI true // Less secure if you plan to commit or share your files, but saves a bunch of memory. // If you hardcode credentials the device will only work in your network #define USE_HARDCODED_CREDENTIALS true #if INCLUDE_WIFI #if USE_HARDCODED_CREDENTIALS #define WIFI_SSID "SSID" #define WIFI_PASSWORD "Password" #endif
// -------------------- SHAKEIT PWM FANS OUTPUT ---------------------------------------------------------------- // removed link // -------------------------------------------------------------------------------------------------------- #define SHAKEITPWMFANS_ENABLED_MOTORS 2 //{"Group":"SHAKEIT PWM FANS Outputs","Name":"SHAKEITPWMFANS_ENABLED_MOTORS","Title":"ShakeIT direct PWM fans enabled (25khz PWM)\r\nArduino Uno : pins 9 or 10\r\nArduino Leonardo pins : 9, 10 or 11\r\nArduino Mega pins : 11, 12 or 13","DefaultValue":"0","Type":"int","Max":3} #ifdef INCLUDE_SHAKEITPWMFANS #define SHAKEITPWMFANS_O1 25 //{"Name":"SHAKEITPWMFANS_O1","Title":"PWM Output 1 pin","DefaultValue":"9","Type":"pin;ShakeIt PWM Fan 1","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=1"} #define SHAKEITPWMFANS_MIN_OUTPUT_O1 0 //{"Name":"SHAKEITPWMFANS_MIN_OUTPUT_O1","Title":"PWM Output 1 min (lower values will disable output)","DefaultValue":"0","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=1","Max":255} #define SHAKEITPWMFANS_MAX_OUTPUT_O1 255 //{"Name":"SHAKEITPWMFANS_MAX_OUTPUT_O1","Title":"PWM Output 1 max","DefaultValue":"255","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=1","Max":255} #define SHAKEITPWMFANS_RELAY_PIN_01 16 //{"Name":"SHAKEITPWMFANS_RELAY_PIN_01","Title":"PWM Output 1 optional on/off relay pin","DefaultValue":"4","Type":"pin;PWN FAN 1 relay","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=1","Min":-1} #define SHAKEITPWMFANS_RELAY_DELAY_01 2000 //{"Name":"SHAKEITPWMFANS_RELAY_DELAY_01","Title":"PWM Output 1 optional relay off delay (ms)","DefaultValue":"2000","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=1 && SHAKEITPWMFANS_RELAY_PIN_01 >0"} #define SHAKEITPWMFANS_RELAY_REVERSELOGIC_01 0 //{"Name":"SHAKEITPWMFANS_RELAY_REVERSELOGIC_01","Title":"PWM Output 1 optional relay reversed logic\nWhen disabled relay pin will be LOW when the relay must be off,\r\nOtherwise when enabled the pin will be HIGH when the relay is off","DefaultValue":"0","Type":"bool","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=1 && SHAKEITPWMFANS_RELAY_PIN_01 > 0"} #define SHAKEITPWMFANS_O2 26 //{"Name":"SHAKEITPWMFANS_O2","Title":"PWM Output 2 pin","DefaultValue":"10","Type":"pin;ShakeIt PWM Fan 2","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=2"} #define SHAKEITPWMFANS_MIN_OUTPUT_O2 0 //{"Name":"SHAKEITPWMFANS_MIN_OUTPUT_O2","Title":"PWM Output 2 min (lower values will disable output)","DefaultValue":"0","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=2","Max":255} #define SHAKEITPWMFANS_MAX_OUTPUT_O2 255 //{"Name":"SHAKEITPWMFANS_MAX_OUTPUT_O2","Title":"PWM Output 2 max","DefaultValue":"255","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=2","Max":255} #define SHAKEITPWMFANS_RELAY_PIN_02 17 //{"Name":"SHAKEITPWMFANS_RELAY_PIN_02","Title":"PWM Output 2 optional on/off relay pin","DefaultValue":"5","Type":"pin;PWN FAN 2 relay","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=2","Min":-1} #define SHAKEITPWMFANS_RELAY_DELAY_02 2000 //{"Name":"SHAKEITPWMFANS_RELAY_DELAY_02","Title":"PWM Output 2 optional relay off delay (ms)","DefaultValue":"2000","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=2 && SHAKEITPWMFANS_RELAY_PIN_02 >0"} #define SHAKEITPWMFANS_RELAY_REVERSELOGIC_02 0 //{"Name":"SHAKEITPWMFANS_RELAY_REVERSELOGIC_02","Title":"PWM Output 2 optional relay reversed logic\nWhen disabled relay pin will be LOW when the relay is off,\r\nOtherwise when enabled the pin will be HIGH when the relay is off","DefaultValue":"0","Type":"bool","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=2 && SHAKEITPWMFANS_RELAY_PIN_02 > 0"} #define SHAKEITPWMFANS_O3 4 //{"Name":"SHAKEITPWMFANS_O3","Title":"PWM Output 3 pin","DefaultValue":"11","Type":"pin;ShakeIt PWM Fan 3","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=3"} #define SHAKEITPWMFANS_MIN_OUTPUT_O3 0 //{"Name":"SHAKEITPWMFANS_MIN_OUTPUT_O3","Title":"PWM Output 3 min (lower values will disable output)","DefaultValue":"0","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=3","Max":255} #define SHAKEITPWMFANS_MAX_OUTPUT_O3 255 //{"Name":"SHAKEITPWMFANS_MAX_OUTPUT_O3","Title":"PWM Output 3 max","DefaultValue":"255","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=3","Max":255} #define SHAKEITPWMFANS_RELAY_PIN_03 8 //{"Name":"SHAKEITPWMFANS_RELAY_PIN_03","Title":"PWM Output 3 optional on/off relay pin","DefaultValue":"6","Type":"pin;PWN FAN 3 relay","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=3","Min":-1} #define SHAKEITPWMFANS_RELAY_DELAY_03 2000 //{"Name":"SHAKEITPWMFANS_RELAY_DELAY_03","Title":"PWM Output 3 optional relay off delay (ms)","DefaultValue":"2000","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=3 && SHAKEITPWMFANS_RELAY_PIN_03 >0"} #define SHAKEITPWMFANS_RELAY_REVERSELOGIC_03 0 //{"Name":"SHAKEITPWMFANS_RELAY_REVERSELOGIC_03","Title":"PWM Output 3 optional relay reversed logic\nWhen disabled relay pin will be LOW when the relay is off,\r\nOtherwise when enabled the pin will be HIGH when the relay is off","DefaultValue":"0","Type":"bool","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=3 && SHAKEITPWMFANS_RELAY_PIN_03 > 0"} #define SHAKEITPWMFANS_O4 5 //{"Name":"SHAKEITPWMFANS_O4","Title":"PWM Output 4 pin","DefaultValue":"10","Type":"pin;ShakeIt PWM Fan 4","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=4"} #define SHAKEITPWMFANS_MIN_OUTPUT_O4 0 //{"Name":"SHAKEITPWMFANS_MIN_OUTPUT_O4","Title":"PWM Output 4 min (lower values will disable output)","DefaultValue":"0","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=4","Max":255} #define SHAKEITPWMFANS_MAX_OUTPUT_O4 255 //{"Name":"SHAKEITPWMFANS_MAX_OUTPUT_O4","Title":"PWM Output 4 max","DefaultValue":"255","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=4","Max":255} #define SHAKEITPWMFANS_RELAY_PIN_04 9 //{"Name":"SHAKEITPWMFANS_RELAY_PIN_04","Title":"PWM Output 4 optional on/off relay pin","DefaultValue":"7","Type":"pin;PWN FAN 4 relay","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=4","Min":-1} #define SHAKEITPWMFANS_RELAY_DELAY_04 2000 //{"Name":"SHAKEITPWMFANS_RELAY_DELAY_04","Title":"PWM Output 4 optional relay off delay (ms)","DefaultValue":"2000","Type":"int","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=4 && SHAKEITPWMFANS_RELAY_PIN_04 >0"} #define SHAKEITPWMFANS_RELAY_REVERSELOGIC_04 0 //{"Name":"SHAKEITPWMFANS_RELAY_REVERSELOGIC_04","Title":"PWM Output 4 optional relay reversed logic\nWhen disabled relay pin will be LOW when the relay is off,\r\nOtherwise when enabled the pin will be HIGH when the relay is off","DefaultValue":"0","Type":"bool","Condition":"SHAKEITPWMFANS_ENABLED_MOTORS>=4 && SHAKEITPWMFANS_RELAY_PIN_04 > 0"} #include "SHShakeitPWMFans.h" SHShakeitPWMFans shShakeitPWMFans; #endif
- In the main.cpp file, edit lines 7 to 15 to add your wifi credentials and lines 791 to 827 to adjust which output of your ESP32 you used. In this example I used 2 fans, connected to pins 25 and 26. Keep in mind that there are <a href=" removed link " target="_blank" rel="noopener">pins that you can't use:
-
- Replace the content in the SHShakeitPWMfans.h with this:
#ifndef __SHSHAKEITPWMFANS_H__ #define __SHSHAKEITPWMFANS_H__ #include <Arduino.h> #include "SHShakeitBase.h" class SHShakeitPWMFans : public SHShakeitBase { private: byte pins[4]; byte mins[4]; byte maxs[4]; int relays[4]; unsigned long offSince[4]; unsigned long offDelay[4] = {2000, 2000, 2000, 2000}; bool reverseRelayLogic[4] = {false, false, false, false}; byte enabledOutputs; const int freq = 25000; // PWM frequency in Hz const int resolution = 8; // PWM resolution int ledChannels[4] = {0, 1, 2, 3}; // PWM channels public: uint8_t motorCount() { return enabledOutputs; } String providerName() { return "PWMFan"; } void safetyStop() override { SHShakeitBase::safetyStop(); for (int i = 0; i < enabledOutputs; i++) { if (relays[i] > 0) { SetRelayState(i, false); } // Also stop PWM ledcWrite(ledChannels[i], 0); } } void begin(byte pEnabledOutputs, byte pPin01, byte pPin02, byte pPin03, byte pPin04) { pins[0] = pPin01; pins[1] = pPin02; pins[2] = pPin03; pins[3] = pPin04; enabledOutputs = pEnabledOutputs; for (int i = 0; i < pEnabledOutputs; i++) { // Setup PWM for each pin ledcSetup(ledChannels[i], freq, resolution); ledcAttachPin(pins[i], ledChannels[i]); ledcWrite(ledChannels[i], 0); // Start with fan off } } void setMin(byte pMin01, byte pMin02, byte pMin03, byte pMin04) { mins[0] = pMin01; mins[1] = pMin02; mins[2] = pMin03; mins[3] = pMin04; } void setMax(byte pMax01, byte pMax02, byte pMax03, byte pMax04) { maxs[0] = pMax01; maxs[1] = pMax02; maxs[2] = pMax03; maxs[3] = pMax04; } void setRelays( int r01, int r02, int r03, int r04, int d01, int d02, int d03, int d04, bool l01, bool l02, bool l03, bool l04) { relays[0] = r01; relays[1] = r02; relays[2] = r03; relays[3] = r04; offDelay[0] = d01; offDelay[1] = d02; offDelay[2] = d03; offDelay[3] = d04; reverseRelayLogic[0] = l01; reverseRelayLogic[1] = l02; reverseRelayLogic[2] = l03; reverseRelayLogic[3] = l04; for (int i = 0; i < enabledOutputs; i++) { if (relays[i] > 0) { pinMode(relays[i], OUTPUT); SetRelayState(i, false); } offSince[i] = 0; } } protected: void SetRelayState(int relayIdx, bool state) { if (relays[relayIdx] > 0) { digitalWrite(relays[relayIdx], reverseRelayLogic[relayIdx] ? !state : state); } } void setMotorOutput(uint8_t motorIdx, uint8_t value) override { double dutyCycle = value; if (dutyCycle < mins[motorIdx]) { dutyCycle = 0; } else { dutyCycle = ((dutyCycle - mins[motorIdx]) / double(maxs[motorIdx] - mins[motorIdx])) * 255.0; } ledcWrite(ledChannels[motorIdx], static_cast<int>(dutyCycle)); // Relay control logic if (relays[motorIdx] > 0) { if (dutyCycle > 0) { SetRelayState(motorIdx, true); offSince[motorIdx] = 0; } else { if (offSince[motorIdx] == 0) { offSince[motorIdx] = millis(); } if ((millis() - offSince[motorIdx]) > offDelay[motorIdx]) { SetRelayState(motorIdx, false); offSince[motorIdx] = 0; } } } } }; #endif // __SHSHAKEITPWMFANS_H__
- In the platformio.ini file, comment the ESP8266 block and uncomment the ESP32 one:
############################################# # ESP8266 # # Comment out this whole block below if # you're not compiling for this platform # ############################################# ;[env:esp8266] ;platform = espressif8266 ;# this should be set to your flavor of esp8266, for instance d1_mini ;# BOARD_LIST: removed link ;board = nodemcuv2 ;framework = arduino ;lib_deps = ; ${common.lib_deps} ; fastled removed link #newer versions don't seem to work with esp8266 in my tests ; # add any libraries that are specific for the es8266 ; ;build_flags = ; -w -DESP8266=true ;monitor_speed = 115200 ;;upload_port = COM4 ;;build_type = debug # set this to debug only for debugging, as it's slower. ;monitor_filters = esp8266_exception_decoder ;build_unflags = -fno-rtti # comment this out if you need more ram, but you'll need to make some type assumptions ############################################# # ESP32 # # Comment out this whole block below if # you're not compiling for this platform # ############################################# [env:esp32] platform = espressif32@^6.2 # this should be set to your flavor of esp32, for instance wemos_d1_mini32 # BOARD LIST: removed link board = nodemcu-32s board_build.f_cpu = 240000000L framework = arduino lib_deps = ${common.lib_deps} fastled removed link # add any libraries that are specific for the esp32 build_flags = -w -DESP32=true monitor_speed = 115200 ; build_type = debug # set this to debug only for debugging, as it's slower. ; monitor_filters = esp32_exception_decoder ; upload_port = COM7
- Replace the content in the SHShakeitPWMfans.h with this:
-
Configuration: With the firmware installed, you'll need to configure the software to communicate with SimHub. This involves setting up the correct COM port and ensuring your racing software is compatible.
-
Testing: Before diving into a race, test your setup to ensure everything is working correctly. Adjust fan speeds manually to test the response and make any necessary adjustments to the configuration.
Tips and Troubleshooting:
- Ensure your power supply can handle the load of your fans to prevent overheating or damage.
- If you encounter any connectivity issues, double-check your wiring and COM port settings.
- For detailed troubleshooting, refer to the ESP-SimHub GitHub repository's issues section or ask for help on this forum.
Conclusion:
Building your own wind simulator is a rewarding project that brings a new level of immersion to your sim racing experience. By following this tutorial, you'll be able to create a fully functional wind simulator tailored to your setup.
If you have any questions, feel free to ask here. Happy building, and see you on the track!
References:
- 3D Printed Parts: <a href=" removed link " target="_blank" rel="noopener">Thingiverse Link
- Firmware: <a href=" removed link " target="_blank" rel="noopener">ESP-SimHub on GitHub
Hey there. Nice work but all links are removed. Could you update them?
I have a spare ESP32 and a DIY wind sim already. I will consider updating to a wireless one (:
Great project, i've spent some fun time tinkering with getting the VSCode working.
I have a Arduino Uno Rev 3 with Wifi included on board. it's a clone chip obviously, but it's got an ESP8266 baked into it as well. The Uno and ESP8266 can talk to each other if you set the DIP switches right, otherwise you can drive each one of them independantly.
I have a working Windsim with a Motorshield and the Uno's Atmega already programmed and configured. What i'd like to do is figure out what i have to do to have the ESP8266 to just act as the wifi board and virtual port for the connection to simhub. Am I missing somthing really simple?