├── .gitattributes ├── .gitignore ├── Controlling-a-fan.ino └── WindTunnel.ino /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /Controlling-a-fan.ino: -------------------------------------------------------------------------------- 1 | /* This sketch is just an example to show how to measure RPM and provide a PWM signal to a fan 2 | * It has other parts which will be used in my wind tunnel project. These parts are not discussed in the 3 | * Youtube video and can be deleted if you wish. However, they should not create problems 4 | * 5 | * The sketch is for Leonardos because it uses the keyboard function. If this function is removed it runs also on other Arduinos 6 | * 7 | * 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define ANEMO_INT 4 16 | #define VENT_PWM 10 17 | #define RPM_RAW 3 18 | #define HEATER 5 19 | #define KEYB_PIN 15 20 | #define TESTPIN 16 21 | 22 | Adafruit_ADS1115 ads; /* Use this for the 16-bit version */ 23 | 24 | 25 | //Aneometer 26 | volatile unsigned long lastAnemo = micros(); 27 | volatile unsigned long duratAnemo = 300000; 28 | volatile boolean validAneo = false; 29 | float averageAnemo = 0; 30 | 31 | //Ventilator 32 | volatile unsigned long lastVent = micros(); 33 | volatile unsigned long duratVent = 300000; 34 | volatile boolean validVent = false; 35 | float averageVent = 0; 36 | int ventPwm = 500; 37 | int ii = 0; 38 | long integral = 0; 39 | boolean keyb = false; 40 | float actSpeed, lastSpeed; 41 | int iii; 42 | long lastEntry = 0; 43 | int pmw; 44 | 45 | 46 | void setup() 47 | { 48 | Timer1.pwm(VENT_PWM, 1023); 49 | Serial.begin(115200); 50 | while (!Serial) {} 51 | 52 | Serial.println("Start Setup"); 53 | Keyboard.begin(); 54 | delay(5000); 55 | 56 | 57 | // ADS1115 58 | ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV 59 | ads.begin(); 60 | 61 | 62 | // Aneometer 63 | lastAnemo = micros(); 64 | pinMode(ANEMO_INT, INPUT); 65 | attachInterrupt(ANEMO_INT, countAnemo , RISING); 66 | 67 | // Ventilator 68 | Timer1.initialize(62); // 62 us = 16 kHz 69 | Timer1.pwm(VENT_PWM, 10); 70 | //pinMode(RPM_RAW,INPUT_PULLUP); 71 | attachInterrupt(RPM_RAW, countVent, RISING); 72 | 73 | // Heater 74 | pinMode(9, INPUT); // nur altes Board wegen Fehler 75 | pinMode(14, INPUT); // nur altes Board wegen Fehler 76 | pinMode(KEYB_PIN, INPUT_PULLUP); 77 | Timer3.initialize(500000); // 0.5 sec 78 | pinMode(TESTPIN, OUTPUT); 79 | setHeater(1); 80 | // while (speedVent() > 0.0); 81 | Serial.println(" Start "); 82 | lastEntry = millis(); 83 | } 84 | 85 | void loop() 86 | { 87 | 88 | for (iii = 50; iii < 1100; iii = iii + 50) { 89 | 90 | pmw = iii; 91 | if (pmw > 1023) pmw = 1023; 92 | 93 | Timer1.pwm(VENT_PWM, pmw); 94 | 95 | while ((millis() - lastEntry) < 500) { 96 | checkkeyb(); 97 | actSpeed = speedVent(); 98 | } 99 | lastEntry = millis(); 100 | actSpeed = speedVent(); 101 | 102 | if (keyb) keybOut(); 103 | 104 | Serial.print(" PWM "); 105 | Serial.println(pmw); 106 | Serial.print(" actSpeed "); 107 | Serial.print(actSpeed); 108 | } 109 | 110 | Timer1.pwm(VENT_PWM, 1); 111 | boolean keyb = false; 112 | while (1 == 1); 113 | 114 | } 115 | 116 | void checkkeyb() { 117 | if (digitalRead(KEYB_PIN) == false) { 118 | keyb = !keyb; 119 | delay(100); 120 | while (digitalRead(KEYB_PIN) == false); 121 | } 122 | } 123 | 124 | 125 | float speedPitot() { 126 | 127 | return 0.000125 * ads.readADC_SingleEnded(2); 128 | } 129 | 130 | 131 | float speedAnemo() { 132 | if (validAneo == true) { 133 | float speed = 8.2 * 33300.0 / duratAnemo; 134 | averageAnemo = ((4.0 * averageAnemo) + speed) / 5.0; 135 | if (speed < 0.9) averageAnemo = 0.0; 136 | validAneo = false; 137 | } 138 | return averageAnemo; 139 | } 140 | 141 | float speedVent() { 142 | long eingang = millis(); 143 | while ((millis() - eingang) < 150 && (validVent == false)) ; 144 | 145 | if (validVent == 1) { 146 | float speedVent = 30000000.0 / duratVent; 147 | averageVent = ((3.0 * averageVent) + speedVent) / 4.0; 148 | validVent = false; 149 | } else averageVent = 0.0; 150 | return averageVent; 151 | } 152 | 153 | 154 | void countAnemo (void) 155 | { 156 | long tt = micros() - lastAnemo; 157 | if (tt > 0) duratAnemo = tt; 158 | lastAnemo = micros(); 159 | validAneo = true; 160 | 161 | } 162 | 163 | void countVent(void) 164 | { 165 | long tt = micros() - lastVent; 166 | 167 | digitalWrite(TESTPIN, 0); 168 | duratVent = tt; 169 | lastVent = micros(); 170 | validVent = true; 171 | } 172 | 173 | void setSpeed(float _speed) { 174 | 175 | #define MAX 100000 176 | 177 | float _p = 0.010; 178 | float _i = 0.00004; 179 | 180 | float abweichung = actSpeed - _speed ; 181 | integral += abweichung; 182 | 183 | if (integral > MAX) integral = MAX; 184 | if (integral < -MAX) integral = -MAX; 185 | 186 | float pPart = (_p * abweichung); 187 | float iPart = (_i * integral); 188 | 189 | 190 | float delta = pPart + iPart; 191 | int target = (int)(ventPwm - delta); 192 | 193 | if (target > 1023) target = 1023; 194 | if (target < 1) target = 1; 195 | 196 | ventPwm = target; 197 | if (ventPwm < 50) ventPwm = 50; 198 | 199 | 200 | 201 | Timer1.pwm(VENT_PWM, ventPwm); 202 | 203 | if ( ii == 10) { 204 | Serial.print(" _Speed "); 205 | Serial.print(_speed); 206 | Serial.print(" actSpeed "); 207 | Serial.print(actSpeed); 208 | Serial.print(" abweichung "); 209 | Serial.print(abweichung); 210 | Serial.print(" pPart "); 211 | Serial.print(pPart); 212 | Serial.print(" iPart "); 213 | Serial.print(iPart); 214 | Serial.print(" delta "); 215 | Serial.print(delta); 216 | Serial.print(" ventPwm "); 217 | Serial.println(ventPwm); 218 | ii = 0; 219 | } 220 | ii++; 221 | } 222 | 223 | void keybOut() { 224 | 225 | Keyboard.print(pmw); 226 | Keyboard.print(char(9)); 227 | Keyboard.print(actSpeed); 228 | Keyboard.print(char(9)); 229 | Keyboard.println(); 230 | 231 | } 232 | 233 | void setHeater(int duty) { 234 | float tt = duty / 100.0 * 1023.0 ; 235 | /* Serial.print(duty); 236 | Serial.print(" "); 237 | Serial.println(tt); 238 | */ 239 | Timer3.pwm(HEATER, (int)tt ); 240 | } 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /WindTunnel.ino: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define ANEMO_INT 4 9 | #define VENT_PWM 10 10 | #define RPM_RAW 3 11 | #define HEATER 5 12 | #define KEYB_PIN 15 13 | #define TESTPIN1 16 14 | #define TESTPIN2 8 15 | 16 | 17 | Adafruit_ADS1115 ads; /* Use this for the 16-bit version */ 18 | 19 | 20 | //Aneometer 21 | volatile unsigned long lastAnemo = micros(); 22 | volatile unsigned long duratAnemo = 300000; 23 | volatile boolean validAneo = false; 24 | float averageAnemo = 0; 25 | 26 | //Ventilator 27 | volatile unsigned long lastVent = micros(); 28 | volatile unsigned long duratVent = 300000; 29 | volatile boolean validVent = false; 30 | float averageVent = 0; 31 | int ventPwm; 32 | int lastVentPwm = 0; 33 | int ii = 0; 34 | boolean keyb = true; 35 | 36 | //PID 37 | long integral = 0; 38 | int lastSpeed; 39 | float targetPWM; 40 | 41 | long lastEntry = 0; 42 | int pmw; 43 | unsigned long lastTime, lastKeyb; 44 | boolean first = true; 45 | int lastDeltaSpeed = 0; 46 | 47 | 48 | void setup() 49 | { 50 | Timer1.pwm(VENT_PWM, 0); 51 | Serial.begin(115200); 52 | while (!Serial) {} 53 | 54 | Serial.println("Start Setup"); 55 | Keyboard.begin(); 56 | 57 | 58 | // ADS1115 59 | ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV 60 | ads.begin(); 61 | 62 | 63 | // Aneometer 64 | lastAnemo = micros(); 65 | pinMode(ANEMO_INT, INPUT); 66 | attachInterrupt(ANEMO_INT, countAnemo , RISING); 67 | 68 | // Ventilator 69 | Timer1.initialize(62); // 62 us = 16 kHz 70 | Timer1.pwm(VENT_PWM, 10); 71 | //pinMode(RPM_RAW,INPUT_PULLUP); 72 | attachInterrupt(RPM_RAW, countVent, RISING); 73 | 74 | // Heater 75 | pinMode(9, INPUT); // nur altes Board wegen Fehler 76 | pinMode(14, INPUT); // nur altes Board wegen Fehler 77 | pinMode(KEYB_PIN, INPUT_PULLUP); 78 | Timer3.initialize(500000); // 0.5 sec 79 | pinMode(TESTPIN1, OUTPUT); 80 | pinMode(TESTPIN2, OUTPUT); 81 | digitalWrite(TESTPIN2, HIGH); 82 | setHeater(1); 83 | while (speedVent() > 0.0); 84 | Serial.println(" Start "); 85 | lastEntry = millis(); 86 | Timer1.pwm(VENT_PWM, 0); 87 | lastSpeed = speedVent(); 88 | } 89 | 90 | void loop() 91 | { 92 | if (first) { 93 | // Keyboard.print("i"); 94 | // Keyboard.print(char(9)); 95 | Keyboard.print("PWM"); 96 | Keyboard.print(char(9)); 97 | Keyboard.print("Speed"); 98 | Keyboard.println(char(9)); 99 | 100 | first = false; 101 | } 102 | setSpeed(2000); 103 | //delay(600); 104 | checkkeyb(); 105 | } 106 | 107 | void checkkeyb() { 108 | if (digitalRead(KEYB_PIN) == false) { 109 | keyb = false; 110 | digitalWrite(TESTPIN2, LOW); 111 | delay(100); 112 | while (digitalRead(KEYB_PIN) == false); 113 | } 114 | } 115 | 116 | 117 | float speedPitot() { 118 | 119 | return 0.000125 * ads.readADC_SingleEnded(2); 120 | } 121 | 122 | 123 | float speedAnemo() { 124 | if (validAneo == true) { 125 | float speed = 8.2 * 33300.0 / duratAnemo; 126 | averageAnemo = ((4.0 * averageAnemo) + speed) / 5.0; 127 | if (speed < 0.9) averageAnemo = 0.0; 128 | validAneo = false; 129 | } 130 | return averageAnemo; 131 | } 132 | 133 | int speedVent() { 134 | long eingang = millis(); 135 | while ((millis() - eingang) < 150 && (validVent == false)) ; 136 | 137 | if (validVent == 1) { 138 | float speedVent = 30000000.0 / duratVent; 139 | averageVent = ((3.0 * averageVent) + speedVent) / 4.0; 140 | validVent = false; 141 | } else averageVent = 0.0; 142 | return int(averageVent); 143 | } 144 | 145 | 146 | void countAnemo (void) 147 | { 148 | long tt = micros() - lastAnemo; 149 | if (tt > 0) duratAnemo = tt; 150 | lastAnemo = micros(); 151 | validAneo = true; 152 | 153 | } 154 | 155 | void countVent(void) 156 | { 157 | long tt = micros() - lastVent; 158 | 159 | digitalWrite(TESTPIN1, 0); 160 | duratVent = tt; 161 | lastVent = micros(); 162 | validVent = true; 163 | } 164 | 165 | void setSpeed(int _speed) { 166 | 167 | 168 | #define MAX 100000 169 | 170 | 171 | float _p = 0.1; 172 | float _i = 0.001; 173 | float _d = 1.0; 174 | 175 | 176 | int _deltaTime = millis() - lastTime; 177 | 178 | // Calculate delta speed 179 | int actSpeed = speedVent(); 180 | int _deltaSpeed = _speed - actSpeed; 181 | 182 | 183 | float hi = ( _deltaSpeed - lastDeltaSpeed); 184 | 185 | float _deltaSpeedDiff = hi; 186 | 187 | // if (abs(_deltaSpeedDiff) < 10) _deltaSpeedDiff = 0; 188 | 189 | integral += (_deltaSpeed); 190 | 191 | float pPart = (_p * float(_deltaSpeed)); 192 | float iPart = (_i * float(integral)); 193 | float dPart = (_d * float(_deltaSpeedDiff)); 194 | 195 | 196 | /* Serial.print(actSpeed); 197 | Serial.print(" deltaSpeed "); 198 | Serial.print(_deltaSpeed); 199 | Serial.print(" lastDeltaSpeed "); 200 | Serial.print(lastDeltaSpeed); 201 | Serial.print(" dd "); 202 | Serial.print(lastDeltaSpeed - _deltaSpeed); 203 | Serial.print(" hi "); 204 | Serial.print(hi); 205 | Serial.print(" dPart "); 206 | Serial.print(dPart); 207 | Serial.print(" Diff "); 208 | Serial.println(_deltaSpeedDiff); 209 | */ 210 | 211 | // pPart = 0.0; 212 | // iPart = 0.0; 213 | dPart = 0.0; 214 | 215 | 216 | float delta = pPart + iPart + dPart; 217 | 218 | targetPWM = 5.75 * delta; 219 | 220 | if (targetPWM > 1023.0) targetPWM = 1023.0; 221 | if (targetPWM < 1.0) targetPWM = 1.0; 222 | 223 | ventPwm = int(targetPWM); 224 | if (ventPwm < 50) ventPwm = 50; 225 | 226 | Timer1.pwm(VENT_PWM, ventPwm); 227 | 228 | if (keyb) { 229 | if (millis() - lastKeyb > 700) { 230 | 231 | Keyboard.print(ventPwm); 232 | Keyboard.print(char(9)); 233 | Keyboard.print(actSpeed); 234 | Keyboard.println(char(9)); 235 | lastKeyb = millis(); 236 | } 237 | /* else { 238 | Serial.print(" _Speed "); 239 | Serial.print(_speed); 240 | Serial.print(" actSpeed "); 241 | Serial.print(actSpeed); 242 | Serial.print(" _deltaSpeed "); 243 | Serial.print(_deltaSpeed); 244 | Serial.print(" pPart "); 245 | Serial.print(pPart); 246 | Serial.print(" iPart "); 247 | Serial.print(iPart); 248 | Serial.print(" dPart "); 249 | Serial.print(dPart); 250 | Serial.print(" delta "); 251 | Serial.print(delta); 252 | Serial.print(" ventPwm "); 253 | Serial.println(ventPwm); 254 | } 255 | */ 256 | } 257 | ii++; 258 | lastSpeed = actSpeed; 259 | lastDeltaSpeed = _deltaSpeed; 260 | lastTime = millis(); 261 | lastVentPwm = ventPwm; 262 | } 263 | 264 | void setHeater(int duty) { 265 | float tt = duty / 100.0 * 1023.0 ; 266 | /* Serial.print(duty); 267 | Serial.print(" "); 268 | Serial.println(tt); 269 | */ 270 | Timer3.pwm(HEATER, (int)tt ); 271 | } 272 | 273 | 274 | 275 | 276 | --------------------------------------------------------------------------------