Compare commits

..

No commits in common. "master" and "v1.7.0" have entirely different histories.

39 changed files with 38979 additions and 39624 deletions

4
.gitignore vendored
View File

@ -1,4 +0,0 @@
*.hex
*.elf
.DS_Store

Binary file not shown.

183
README.md
View File

@ -1,62 +1,23 @@
# Dual magnetic stir controller V2
# Dual magnetic stir controller
## Overview
This is the Arduino project to build a double magnetic stirrer based on 2 fans, a 1602 LCD display, a KY-040 encoder and an Arduino Nano and a special PCB designed in the Hobbybrauer-Forum.
This is the Arduino sketch to build a double magnetic stirrer based on 2 fans, a 1602 LCD display, a KY-040 encoder and an Arduino (e.g. Uno, Leonardo, Pro Micro).
<p align="center">
<img src="https://git.fhi.mpg.de/mike/stir/raw/branch/master/images/stir+pcb.jpg" width=700>
</p>
V2 supports dual voltage for the fans which increases the speed range.
In order to put the stirrer into operation you have to change the settings in stir.ino according to your setup (see below).
The stir V2 software is compatible with V1 hardware if you have used neither the RS232 Api nor the OFF0 and OFF1 relais pins.
## Part List
* 1x PCB "Stir V2.0"
* 1x Arduino Nano V3
* 1x [Voltage regulator TE818 5-24V to 5V 3A step down buck converter](https://www.ebay.de/itm/5x-Voltage-Regulator-5-24V-to-5V-3A-Step-Down-Buck-Converter-Power-Module-TE818/133109140535?ssPageName=STRK%3AMEBIDX%3AIT&_trksid=p2057872.m2749.l2649)
* 2x [Relais Omron G5Q-1-EU 5DC switching printrelais 5 V/DC 5A](https://www.conrad.de/de/p/omron-g5q-1-eu-5dc-printrelais-5-v-dc-5-a-1-wechsler-1-st-503930.html)
* 2x [Diode 1N4148 Diotec](https://www.conrad.de/de/p/stmicroelectronics-schottky-diode-gleichrichter-1n5822-do-201ad-40-v-einzeln-168275.html)
* 1x [Electrolytic capacitor 1000µF/16V Yageo SE016M1000B5S-1015](https://www.conrad.de/de/p/yageo-se016m1000b5s-1015-elektrolyt-kondensator-radial-bedrahtet-5-mm-1000-f-16-v-20-x-h-10-mm-x-15-mm-1-st-445386.html)
* 2x [Diode 1N5822 STMicroelectronics Schottky-Diode](https://www.conrad.de/de/p/diotec-ultraschnelle-si-diode-1n4148-sod-27-75-v-150-ma-162280.html)
* 4x [Metal film resistor 10 KOhm 0,6 W](https://www.conrad.de/de/p/rs-pro-metallschichtwiderstand-10k-0-5-0-6w-807200469.html)
* 2x Fan BeQuiet SilentWings 3 120mm PWM Highspeed or Noctua NF-R8 Redux-1800 80mm PWM
* 1x Power supply 12V-16V DC / 2A
* 1x Built-in socket for plug-in power supply unit
* 1x I2C 16x2 Arduino LCD Display Module
* 1x [Rotary encoder KY-040](https://www.conrad.de/de/p/joy-it-kodierter-drehschalter-rotary-encoder-1695709.html)
* 2x Magnet mount (3D printed)
* 4x Neodymium magnet 20x10 mm (N52, 1 oder 2 mm dick)
In addition, a housing is required that can be individually designed according to possibilities and capabilities.
<p align="center">
<img src="https://git.fhi.mpg.de/mike/stir/raw/branch/master/images/v2_board.jpg" width=350>
<img src="https://github.com/micworg/stir/blob/master/images/stir.jpg" width=500>
</p>
In order to put the stirrer into operation you have to change the settings in stir.ino according to your setup.
## Reference
This project was created based on an idea from the german Hobbybrauer-Forum and would not be possible without the ideas from there.
This project was created based on an idea from the Hobbybrauer-Forum and would not be possible without the ideas from there.
The forum: https://hobbybrauer.de/
The thread: https://hobbybrauer.de/forum/viewtopic.php?f=21&t=1456
Special thanks for development and testing:
* Herbert Schmid
* Adrian Sigel
* Jens Warkentin
* Bastian Werner
## Overview of Functions
All functions are controlled by the KY-040 encoder:
@ -74,13 +35,7 @@ All functions are controlled by the KY-040 encoder:
* **OTIME**: turn changes switch off time in hours (the timer activates immediately, 0 = switch off disabled)
* **RNVAL**: random value range (a random values in this range will be added to speed)
* **Other menu options**:
* **CFG**: Configuration menu
Functions in the congirutation sub menu
* **Short Press**: switch between config option and and value.
* **Long press**: exit configuration menu.
* **Turn**: select configuration option or change value.
* **BRGHT**: LCD brightness
## Display Indicators:
@ -91,39 +46,95 @@ Functions in the congirutation sub menu
## Settings (in stir.ino)
|Value|Config|Description|
|:----------------|:-----|:----------|
|`LGHT` |`LGHT`|LCD display brightness|
|`SPEEDINC1` |`SPI1`|increment of speed values when turning the encoder (1. range)|
|`SPEEDINC2` |`SPI2`|increment of speed values when turning the encoder (2. range)|
|`SPEEDINC3` |`SPI3`|increment of speed values when turning the encoder (3. range)|
|`SPEEDSTEP1` |`SPS1`|speed increment steps (increment changes at these values, separates range 1 and 2)|
|`SPEEDSTEP2` |`SPS2`|speed increment steps (increment changes at these values, separates range 2 and 3))|
|`FANMIN` |`FMIN`|fan minimum speed (should be a value at which the fan runs safely) (rpm)|
|`FANMAX` |`FMAX`|fan maximum speed (it's important that this is the real maximum value of the fan) (rpm)|
|`FANINIT` |`FINI`|set fans to high voltage for 3 sec at boot (0/1)|
|`BOFF` |`BOFF`|if set boost off will also turn the normal mode off (0/1)|
|`BINC` |`BINC`|boost time increment (min)|
|`CINC` |`CINC`|catch time increment (min)|
|`RINC` |`RINC`|rise time increment (sec)|
|`OINC` |`OINC`|off timer increment (hour)|
|`RNDINC` |`RINC`|randon value increment (rpm)|
|`CATCHSTOP` |`CSTP`|stop interval for stir fish catch function (ms)|
|`PWM0`, `PWM1` | |PWM output pins for fan speed|
|`PWM2` | |PWM output pin for LCD brightness control|
|`I0`, `I1` | |interrupts for rpm measurement (0/1 for Uno/Nano, 2/3 for Leonardo/ProMicro)|
|`SWAPENC` |`SWEN`|switch KY-040 encoder direxction (0/1)|
|`CLK`, `DT`, `SW`| |pins for KY-040 encoder|
|`R0`, `R0` | |voltage select relais pin|
|`RTHRES` |`RTHR`|voltage switch threshold (rpm)|
|`RINTERVAL` |`XINT`|regulation interval (ms)|
|`RDELAY` |`XDEL`|extra regulation delay when value changes (ms)|
|`RTOL` |`XTOL`|regulation tolerance (rpm)|
|`RNDINTERVL` |`RINT`|randon value range change interval (ms)|
|`SINTERVAL` |`SINT`|speed measurement interval (ms)|
|`SAVERAGE` |`SAVG`|speed measurement average|
|`SAVETAG` | |parameter save identifier|
|`SAVEDELAY` | |delay in seconds before parameter will be saved to EEPROM|
|`LCDB` | |LCD brightness steps (10 values, 0=off, 255=max)|
|`RESET` |`RSET`|change this value in configuration menu to 1 for factory reset|
|Value|Description|
|:----|:----------|
|`SPEEDINC`|increment of speed values when turning the encoder|
|`FANMIN`|fan minimum speed (should be a value at which the fan runs safely) (rpm)|
|`FANMAX`|maximum speed of the fan (it is important that this is the real maximum value of the fan) (rpm)|
|`BOFF`|if set boost off will also turn the normal mode off|
|`BINC`|boost time increment (min)|
|`CINC`|catch time increment (min)|
|`RINC`|rise time increment (sec)|
|`OINC`|off timer increment (hour)|
|`CATCHSTOP`|stop interval for stir fish catch function (ms)|
|`PWM0`, `PWM1`|PWM output pins for fan speed|
|`PWM2`|PWM output pin for LCD brightness control|
|`I0`, `I1`|interrupts for rpm measurement (2 and 3 for Leonardo and ProMicro / 0 and 1 for Uno)|
|`CLK`, `DT`, `SW`|pins for KY-040 encoder|
|`OFF0`, `OFF1`|these pins will be set if the fan is off|
|`OFFSTATE`|off state (LOW/HIGH)|
|`RINTERVAL`|regulation interval (ms)|
|`RDELAY`|extra regulation delay when value changes (ms)|
|`RTOL`|regulation tolerance (rpm)|
|`RNDINTERVL`|randon value change interval (ms)|
|`SINTERVAL`|speed measurement interval (ms)|
|`SAVERAGE`|speed measurement average|
|`SAVETAG`|parameter save identifier|
|`SAVEDELAY`|delay in seconds before parameter will be saved to EEPROM|
|`LCDB`|LCD brightness steps (10 values, 0=off, 255=max)|
## API commands
Commands are colon separated an can be send via USB/Serial
|Command|Description|
|:------|:----------|
|`info`|returns a colon separated string with all parameters (see below)|
|`version`|returns the software version|
|`on:<0/1>`|switch stirren on|
|`off:<0/1>`|switch stirren off|
|`bon:<0/1>`|switch boost mode on|
|`boff:<0/1>`|switch boost mode off|
|`speed:<0/1>:<rpm>`|set stirrer speed (FANMIN-FANMAX rpm)|
|`bspeed:<0/1>:<rpm>`|set stirrer boost speed (speed-FANMAX rpm)|
|`btime:<0/1>:<min>`|set boost time (0-60)|
|`con:<0/1>`|switch catch mode on|
|`coff:<0/1>`|switch catch mode off|
|`ctime:<0/1>:<min>`|set catch mode interval (60-240 min)|
|`rtime:<0/1>:<sec>`|set speed rise time (0-240 sec)|
|`rnval:<0/1>:<rpm>`|randon value (0-1000 rpm)|
|`otime:<0/1>:<hour>`|switch stirrer off after time in hours (1-99 hour, 0 deactivates switch off)|
All commands return a colon separated string with all current parameters:
|Element|Stirrer|Variable in stir.ino|Description|
|:------|:------|:-------------------|:----------|
| 0|0|`F[0]` |state (0=on, 1=off)|
| 1|0|`v[0]` |speed (rpm)|
| 2|0|`b[0]` |boost speed (rpm)|
| 3|0|`rpm[0]` |rpm|
| 4|0|`xpm[0]` |averaged rpm|
| 5|0|`r[0]` |regulation value|
| 6|0|`bstate[0]`|boost state (0=on, 1=off)|
| 7|0|`btime[0]` |boost time (min)|
| 8|0|`cat[0]` |fish catch mode state (0=on, 1=off)|
| 9|0|`ctime[0]` |fish catch time interval (min)|
|10|0|`rtime[0]` |speed rise time (sec)|
|11|0|`otime[0]` |switch off time (hour)|
|11|0|`rnval[0]` |random value (rpm)|
|12|0| |boost remain (sec)|
|13|0| |off timer remain (sec)|
|14|1|`F[1]` |state (0=on, 1=off)|
|15|1|`v[1]` |speed (rpm)|
|16|1|`b[1]` |boost speed (rpm)|
|17|1|`rpm[1]` |rpm|
|18|1|`xpm[1]` |averaged rpm|
|19|1|`r[1]` |regulation value|
|20|1|`bstate[1]`|boost state (0=on, 1=off)|
|21|1|`btime[1]` |boost time (min)|
|22|1|`cat[1]` |fish catch mode state (0=on, 1=off)|
|23|1|`ctime[1]` |fish catch time interval (min)|
|24|1|`rtime[1]` |speed rise time (sec)|
|25|1|`otime[1]` |switch off time (hour)|
|25|1|`rnval[1]` |random value (rpm)|
|26|1| |boost remain (sec)|
|27|1| |off timer remain (sec)|
|28| | |0=ok, 1=error|
|29| | |software version|
## Schematics
<p align="center">
<img src="https://github.com/micworg/stir/blob/master/images/schematic_leonardo.png" width=500>
<img src="https://github.com/micworg/stir/blob/master/images/schematic_uno.png" width=500>
</p>

View File

@ -1,143 +0,0 @@
# Dual magnetic stir controller V1
## Overview
This is the Arduino sketch to build a double magnetic stirrer based on 2 fans, a 1602 LCD display, a KY-040 encoder and an Arduino (e.g. Uno, Nano, Leonardo, Pro Micro).
<p align="center">
<img src="https://github.com/micworg/stir/blob/master/images/stir.jpg" width=500>
</p>
In order to put the stirrer into operation you have to change the settings in stir.ino according to your setup.
## Reference
This project was created based on an idea from the Hobbybrauer-Forum and would not be possible without the ideas from there.
The forum: https://hobbybrauer.de/
The thread: https://hobbybrauer.de/forum/viewtopic.php?f=21&t=1456
## Overview of Functions
All functions are controlled by the KY-040 encoder:
* **Short Press:** change between menu, stirrer 1 and stirrer 2
* **Turn when menu is selected**: change between menu items (SPEED, BOOST, BTIME, CATCH, CTIME)
* **Long press when menu is selected**: lock all functions (unlock also by long press)
* **When a stirrer is selected**:
* **SPEED**: turn sets speed, long press switches stirrer on or off.
* **BOOST**: turn sets boost speed, long press activates/deactivates the boost function (running time is shown in the display).
* **BTIME**: turn changes the boost time in minutes.
* **CATCH**: turn activates/deactivates the fishing function
* **CTIME**: turn changes the interval in minutes for the fish catching function
* **RTIME**: turn changes speed rise time in seconds (applies to power on, stirrer on, boost on and fish catching)
* **OTIME**: turn changes switch off time in hours (the timer activates immediately, 0 = switch off disabled)
* **RNVAL**: random value range (a random values in this range will be added to speed)
* **Other menu options**:
* **BRGHT**: LCD brightness
## Display Indicators:
* **Bottom Left**: Menu
* **Bottom Center/Right**: displays the set values for the stirrers depending on the selected menu item.
* **Top Center/Right**: displays the current stirrer speed (or 'OFF' or 'CAT' (Fishing)).
* **Top Left**: shows the remaining time of the boost or switch off function.
## Settings (in stir.ino)
|Value|Description|
|:----|:----------|
|`SPEEDINC`|increment of speed values when turning the encoder|
|`FANMIN`|fan minimum speed (should be a value at which the fan runs safely) (rpm)|
|`FANMAX`|maximum speed of the fan (it is important that this is the real maximum value of the fan) (rpm)|
|`BOFF`|if set boost off will also turn the normal mode off|
|`BINC`|boost time increment (min)|
|`CINC`|catch time increment (min)|
|`RINC`|rise time increment (sec)|
|`OINC`|off timer increment (hour)|
|`CATCHSTOP`|stop interval for stir fish catch function (ms)|
|`PWM0`, `PWM1`|PWM output pins for fan speed|
|`PWM2`|PWM output pin for LCD brightness control|
|`I0`, `I1`|interrupts for rpm measurement (2 and 3 for Leonardo and ProMicro / 0 and 1 for Uno)|
|`CLK`, `DT`, `SW`|pins for KY-040 encoder|
|`OFF0`, `OFF1`|these pins will be set if the fan is off|
|`OFFSTATE`|off state (LOW/HIGH)|
|`RINTERVAL`|regulation interval (ms)|
|`RDELAY`|extra regulation delay when value changes (ms)|
|`RTOL`|regulation tolerance (rpm)|
|`RNDINTERVL`|randon value range change interval (ms)|
|`SINTERVAL`|speed measurement interval (ms)|
|`SAVERAGE`|speed measurement average|
|`SAVETAG`|parameter save identifier|
|`SAVEDELAY`|delay in seconds before parameter will be saved to EEPROM|
|`LCDB`|LCD brightness steps (10 values, 0=off, 255=max)|
## API commands
Commands are colon separated an can be send via USB/Serial
|Command|Description|
|:------|:----------|
|`info`|returns a colon separated string with all parameters (see below)|
|`version`|returns the software version|
|`on:<0/1>`|switch stirren on|
|`off:<0/1>`|switch stirren off|
|`bon:<0/1>`|switch boost mode on|
|`boff:<0/1>`|switch boost mode off|
|`speed:<0/1>:<rpm>`|set stirrer speed (FANMIN-FANMAX rpm)|
|`bspeed:<0/1>:<rpm>`|set stirrer boost speed (speed-FANMAX rpm)|
|`btime:<0/1>:<min>`|set boost time (0-60)|
|`con:<0/1>`|switch catch mode on|
|`coff:<0/1>`|switch catch mode off|
|`ctime:<0/1>:<min>`|set catch mode interval (60-240 min)|
|`rtime:<0/1>:<sec>`|set speed rise time (0-240 sec)|
|`rnval:<0/1>:<rpm>`|set randon value range (0-1000 rpm)|
|`otime:<0/1>:<hour>`|switch stirrer off after time in hours (1-99 hour, 0 deactivates switch off)|
All commands return a colon separated string with all current parameters:
|Element|Stirrer|Variable in stir.ino|Description|
|:------|:------|:-------------------|:----------|
| 0|0|`F[0]` |state (0=on, 1=off)|
| 1|0|`v[0]` |speed (rpm)|
| 2|0|`b[0]` |boost speed (rpm)|
| 3|0|`rpm[0]` |rpm|
| 4|0|`xpm[0]` |averaged rpm|
| 5|0|`r[0]` |regulation value|
| 6|0|`bstate[0]`|boost state (0=on, 1=off)|
| 7|0|`btime[0]` |boost time (min)|
| 8|0|`cat[0]` |fish catch mode state (0=on, 1=off)|
| 9|0|`ctime[0]` |fish catch time interval (min)|
|10|0|`rtime[0]` |speed rise time (sec)|
|11|0|`otime[0]` |switch off time (hour)|
|12|0|`rnval[0]` |random value range (rpm)|
|13|0|`rnd[0]` |current random value (rpm)|
|14|0| |boost remain (sec)|
|15|0| |off timer remain (sec)|
|16|1|`F[1]` |state (0=on, 1=off)|
|17|1|`v[1]` |speed (rpm)|
|18|1|`b[1]` |boost speed (rpm)|
|19|1|`rpm[1]` |rpm|
|20|1|`xpm[1]` |averaged rpm|
|21|1|`r[1]` |regulation value|
|22|1|`bstate[1]`|boost state (0=on, 1=off)|
|23|1|`btime[1]` |boost time (min)|
|24|1|`cat[1]` |fish catch mode state (0=on, 1=off)|
|25|1|`ctime[1]` |fish catch time interval (min)|
|26|1|`rtime[1]` |speed rise time (sec)|
|27|1|`otime[1]` |switch off time (hour)|
|28|1|`rnval[1]` |random value range (rpm)|
|29|1|`rnd[1]` |current random value (rpm)|
|30|1| |boost remain (sec)|
|31|1| |off timer remain (sec)|
|32| | |software version|
|33| | |uptime (ms)|
|34| | |0=ok, 1=error|
## Schematics
<p align="center">
<img src="https://github.com/micworg/stir/blob/master/images/schematic_leonardo.png" width=500>
<img src="https://github.com/micworg/stir/blob/master/images/schematic_uno.png" width=500>
</p>

View File

@ -1,15 +0,0 @@
# Stir Shield
Stir shields for Arduino NANO from Jens Warkentin.
<p align="center">
<B>Gerber_Stir.Nano+StepDown.V3_.zip<B><BR>
<img src="https://github.com/micworg/stir/blob/master/shields/nano/images/topv3.png" width=300>
</p>
<p align="center">
<B>Gerber_Stir_Arduino_Nano.zip<B><BR>
<img src="https://github.com/micworg/stir/blob/master/shields/nano/images/top.png" width=300>
</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

View File

@ -1,13 +0,0 @@
# Stir Shield
Stir shield for Arduino UNO from Adrian Sigel.
<p align="center">
<img src="https://github.com/micworg/stir/blob/master/shields/uno/images/pcb_top.png" width=500>
</p>
<p align="center">
<img src="https://github.com/micworg/stir/blob/master/shields/uno/images/pcb_bottom.png" width=500>
</p>

View File

@ -1,441 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////// Stir Control (mwx'2019)
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
#define MS (long)millis()
#define SX Serial.print
#define SXN Serial.println
String VERSION = "1.7.3";
int SPEEDINC = 50; // speed increment (rpm)
int FANMIN = 200; // fan minimum speed (should be a value at which the fan runs safely) (rpm)
int FANMAX = 1600; // fan maximum speed (should be the real maximum value of the fan) (rpm)
int BOFF = 0; // if set boost off will also turn the normal mode off
int BINC = 1; // BTIME increment (min)
int CINC = 10; // CTIME increment (min)
int RINC = 10; // RTIME increment (sec)
int OINC = 3; // OTIME increment (hour)
long CATCHSTOP = 20000; // fish catch stop period (ms)
int PWM0 = 9; // PWM pin for 1. fan
int PWM1 = 10; // PWM pin for 2. fan
int PWM2 = 11; // PWM pin for LCD brightness
int I0 = 2; // interrupt for fan 0 rpm signal (use 2 for Leonardo/ProMicro and 0 for Uno)
int I1 = 3; // interrupt for fan 1 rpm signal (use 3 for Leonardo/ProMicro and 1 for Uno)
int CLK = 5; // clk on KY-040 encoder
int DT = 6; // dt on KY-040 encoder
int SW = 4; // sw on KY-040 encoder
int OFF0 = 7; // off state pin 1. fan
int OFF1 = 8; // off state pin 2. fan
int OFFSTATE = LOW; // off state (LOW/HIGH)
long RINTERVAL = 5000; // regulation internval (ms)
long RDELAY = 3000; // regulation delay on changes (ms)
int RTOL = 8; // regulation tolerance (rpm)
long RNDINTERVAL = 300000; // randon value range change interval (ms)
long SINTERVAL = 2000; // speed measurement internval (ms)
int SAVERAGE = 4; // speed measurement average
int SAVETAG = 1014; // save tag
long SAVEDELAY = 10000; // EEPROM save delay (ms)
byte LCDB[] = {4,8,16,24,32,64,96,128,192,255}; // LCD brightness steps (10 values, 0=off, 255=max)
byte aright[] = {0x00,0x08,0x0C,0x0E,0x0C,0x08,0x00,0x00}; // LCD character
byte aup[] = {0x04,0x0E,0x1F,0x00,0x00,0x00,0x00,0x00};
byte arnd[] = {0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00,0x00};
LiquidCrystal_I2C lcd(0x27,16,2); // LCD display (connect to SDA/SCL)
int v[2],b[2],r[2]={0};double q,rpm[2]={0},xpm[2]={0},xb[2]={0},xv[2]={0},rtime[2]; // speed and regulation
long ac[2]={0},bc[2]={0}; // interrupt rpm counter
long xts,sts,rts,swts,buts,savets,catts[2],stop[2],bts[2],vts[2],ots[2],rndts[2]; // timing
int bdelay,bprocess=0,enclast,encval,M=2,S=0; // button/encoder processing
int F[2],bstate[2]={0},btime[2],cat[2],ctime[2],cstate[2]={0},bclr=0,SAVE=0,LOCK=0; // operating states
int ostate[2]={0},otime[2]={0}; // off timer
int brght; // LCD brightness
int rnd[2]={0},rnval[2]={0};long seed; // random speed
char form[8],out[20];String cmd[8];int icmd[8]; // string buffer
void setup() { ////////////////////////////////////////////////////////////////////////////////////////// SETUP
Serial.begin(9600); // start serial
lcd.init();lcd.clear();lcd.backlight(); // initialize lcd
lcd.createChar(0,aright);lcd.createChar(1,aup);lcd.createChar(2,arnd); // load lcd characters
pinMode(PWM0,OUTPUT);pinMode(PWM1,OUTPUT);pinMode(PWM2,OUTPUT); // setup PWM pins
pinMode(CLK,INPUT);pinMode(DT,INPUT);pinMode(SW,INPUT); // setup KY-040 pins
digitalWrite(CLK,true);digitalWrite(DT,true);digitalWrite(SW,true); // turn ON pullup resistors
pinMode(OFF0,OUTPUT);pinMode(OFF1,OUTPUT); // setup off state pins
attachInterrupt(I0,rpmint0,FALLING); // setup interrupts vor rpm in
attachInterrupt(I1,rpmint1,FALLING);
TCCR1A=0;TCCR1B=0;TCNT1=0; // setup timer for 25 kHz PWM
TCCR1A=_BV(COM1A1)|_BV(COM1B1)|_BV(WGM11);TCCR1B=_BV(WGM13)|_BV(CS10);ICR1=320;
SAVE=0; // load/initialize settings
if (eer(0)!=SAVETAG) {
for (int i=0;i<2;i++) {;v[i]=300;b[i]=700;btime[i]=30;cat[i]=0;ctime[i]=120;rtime[i]=60;F[i]=0;rnval[i]=0;}
brght=5;seed=0;
eew(0,SAVETAG);save();
} else {
for (int i=0;i<2;i++) {
v[i]=eer(1+i);b[i]=eer(3+i);btime[i]=eer(5+i);cat[i]=eer(7+i);
ctime[i]=eer(9+i);F[i]=eer(11+i);rtime[i]=eer(13+i);rnval[i]=eer(15+i);
}
seed=eer(100)+1;eew(100,seed);randomSeed(seed);
brght=eer(101);
}
lset();
enclast=digitalRead(CLK); // get encoder state
for (int i=0;i<2;i++) catts[i]=stop[i]=vts[i]=ots[i]=rndts[i]=MS;xts=sts=rts=swts=buts=savets=MS; // timer
updatePWM();updatelcd();updatespeed();updatemarker(); // update all
slcd(0,0,5,VERSION); // show version
}
void loop() { //////////////////////////////////////////////////////////////////////////////////////////// LOOP
if (Serial.available() > 0) { ////////////////////////////////////////////////////////// serial communication
int n=cutcmd(Serial.readString());
int err=1;
int i=cut(icmd[1],0,1);
if (cmd[0]=="info" && n==1) {;err=0;}
if (cmd[0]=="version" && n==1) {;SXN(VERSION);return;}
if (cmd[0]=="save" && n==1) {;save();}
if (cmd[0]=="speed" && n==3) {;v[i]=cut(icmd[2],FANMIN,FANMAX);err=0;}
if (cmd[0]=="bspeed" && n==3) {;b[i]=cut(icmd[2],FANMIN,FANMAX);err=0;}
if (cmd[0]=="btime" && n==3) {;btime[i]=cut(icmd[2],0,60);err=0;}
if (cmd[0]=="ctime" && n==3) {;ctime[i]=cut(icmd[2],60,240);err=0;}
if (cmd[0]=="rtime" && n==3) {;rtime[i]=cut(icmd[2],0,240);err=0;}
if (cmd[0]=="rnval" && n==3) {;rnval[i]=cut(icmd[2],0,1000);err=0;}
if (cmd[0]=="on" && n==2) {;fset(i,1);err=0;}
if (cmd[0]=="off" && n==2) {;fset(i,0);err=0;}
if (cmd[0]=="bon" && n==2) {;bset(i,1);err=0;}
if (cmd[0]=="boff" && n==2) {;bset(i,0);err=0;}
if (cmd[0]=="con" && n==2) {;cat[i]=1;catts[i]=MS;err=0;}
if (cmd[0]=="coff" && n==2) {;cat[i]=0;catts[i]=MS;err=0;}
if (cmd[0]=="otime" && n==3) {;oset(i,icmd[2]);err=0;}
updatelcd();
for (int i=0;i<2;i++) {
SX(F[i]);SX(":"); // 0, 16
SX((int)(v[i]));SX(":"); // 1, 17
SX((int)(b[i]));SX(":"); // 2, 18
SX(rpm[i]);SX(":"); // 3, 19
SX(xpm[i]);SX(":"); // 4, 20
SX(r[i]);SX(":"); // 5, 21
SX(bstate[i]);SX(":"); // 6, 22
SX(btime[i]);SX(":"); // 7, 23
SX(cat[i]);SX(":"); // 8, 24
SX(ctime[i]);SX(":"); // 9, 25
SX(rtime[i]);SX(":"); // 10, 26
SX(otime[i]);SX(":"); // 11, 27
SX(rnval[i]);SX(":"); // 12, 28
SX(rnd[i]);SX(":"); // 13, 29
if (bstate[i]) SX((((long)btime[i]*60000)-(MS-(long)bts[i]))/1000+1); else SX(0);SX(":"); // 14, 30
if (ostate[i]) SX((((long)otime[i]*3600000)-(MS-(long)ots[i]))/1000+1); else SX(0);SX(":"); // 15, 31
}
SX(VERSION);SX(":"); // 32
SX(MS);SX(":"); // 33
SXN(err); // 34
save();
}
if (SAVE>0 && MS-savets>SAVEDELAY) {;save();SAVE=0;savets=MS;} ////////////////////// save settings if needed
if (MS-savets>SAVEDELAY/5) bclr=1;
for (int i=0;i<2;i++) {
if (MS-rndts[i]>RNDINTERVAL) { /////////////////////////////////////////////////////////////// random timer
rnd[i]=int(random(0,rnval[i]+1)/10)*10;rndts[i]=MS;updatelcd();
}
if (ostate[i] && MS-ots[i]>(long)otime[i]*3600000) { ////////////////////////////////////// check off timer
otime[i]=0;ostate[i]=0;fset(i,0);
}
if (cat[i] && MS-catts[i]>(long)ctime[i]*60000 && F[i]==1) { //////////////////////////// initiate cat fish
catts[i]=MS;cstate[i]=1;F[i]=0;stop[i]=MS;updatePWM();updatespeed();
}
if (cstate[i] && MS-stop[i]>CATCHSTOP && F[i]==0) { ////////////////////////// stop cat fish and start over
cstate[i]=0;F[i]=1;vts[i]=MS;rts=MS+2000;updatePWM();updatespeed();
}
if (bstate[i] && MS-bts[i]>(long)btime[i]*60000) {;bstate[i]=0;S=0;updatelcd();} //////// check boost state
}
if (MS-xts>SINTERVAL) { /////////////////////////////////////////////////////////////////// speed measurement
for (int i=0;i<2;i++) xpm[i]=xpm[i]*(SAVERAGE-1)/SAVERAGE+(bc[i]/((MS-xts)/1000.0)*30.0)/SAVERAGE;
updatespeed();xts=MS;bc[0]=0;bc[1]=0;
}
if (MS-rts>RINTERVAL) { //////////////////////////////////////////////////////////////////// speed regulation
for (int i=0;i<2;i++) {
rpm[i]=ac[i]/((MS-sts)/1000.0)*30.0;
ac[i]=0;
calcramp(i);
if (!F[i]) {;r[i]=0;setPWM(i,0);}
else {
q=(bstate[i]?xb[i]:xv[i])-rpm[i];
if (abs(q)>RTOL) r[i]=q<0?r[i]-1-abs(q)/10:r[i]+1+abs(q)/10;
setPWM(i,cut((bstate[i]?xb[i]:xv[i])/(FANMAX/320.0)+r[i],0,320));
}
}
sts=MS;rts=MS;
updatespeed();
}
bdelay=0; //////////////////////////////////////////////////////////////////////////////////// process switch
if (!bprocess) {
while (!digitalRead(SW)){
bdelay++;delay(15);bprocess=1;if (bdelay>20) break;
}
}
if (bdelay>0 && MS-buts>100) { // long button press
if (bdelay>20) {
if (M==2) { // lock/unlock
if (LOCK==0) LOCK=1;
else LOCK=0;
updatemarker();
} else {
if (S==0 && !LOCK) { // fan on/off
if (F[M]==0) fset(M,1); else fset(M,0);
}
if (S==1 && !LOCK) { // boost on/off
if (bstate[M]==0) bset(M,1); else {;bset(M,0);if (BOFF) fset(M,0);}
bclr=1;
}
}
save();updatespeed();bdelay=0;
} else if (bdelay>0 && bdelay<20 && !LOCK) { // short button press, switch: menu -> fan 0 -> fan 1
M++;if (S==8 && M==1) M=2;if (M>2) M=0;
updatemarker();
bdelay=0;
}
SAVE++;buts=MS;
}
if (digitalRead(SW)) bprocess=0;
encval = digitalRead(CLK); ////////////////////////////////////////////////////////////////// process encoder
if (encval != enclast && !LOCK) {
if(!encval){
if (digitalRead(DT) != encval) { // turn encoder clockwise
if (M==2) S++; // scroll menu
else {
if (S==0) v[M]+=SPEEDINC; // fan speed up
if (S==1) b[M]+=SPEEDINC; // boost speed up
if (S==2) btime[M]+=BINC; // boost time up
if (S==3) cat[M]++; // cat on/off
if (S==4) ctime[M]+=CINC; // cat time up
if (S==5) rtime[M]+=RINC; // rise time up
if (S==6) otime[M]+=OINC; // off time up
if (S==7) rnval[M]+=SPEEDINC; // random value
if (S==8) brght++; // increase LCD brightness
}
} else { // turn encoder counterclockwise
if (M==2) S--; // scroll menu
else {
if (S==0) v[M]-=SPEEDINC; // fan speed down
if (S==1) b[M]-=SPEEDINC; // boost speed down
if (S==2) btime[M]-=BINC; // boost time down
if (S==3) cat[M]--; // cat on/off
if (S==4) ctime[M]-=CINC; // cat time down
if (S==5) rtime[M]-=RINC; // rise time down
if (S==6) otime[M]-=OINC; // off time down
if (S==7) rnval[M]-=SPEEDINC; // random value
if (S==8) brght--; // decrease LCD brightness
}
}
for (int i=0;i<2;i++) if (b[i]<v[i]) b[i]=v[i];
if (M!=2) {
if (S<=1) {;updatePWM();rts=MS+RDELAY;} // apply (boost) speed change
if (S==2) btime[M]=cut(btime[M],0,99); // check boost time
if (S==3) {;cat[M]=cut(cat[M],0,1);catts[M]=MS;} // check cat
if (S==4) ctime[M]=cut(ctime[M],60,240); // check cat time
if (S==5) rtime[M]=cut(rtime[M],0,240); // check rise time
if (S==6) oset(M,otime[M]); // check off time
if (S==7) {;rnval[M]=cut(rnval[M],0,1000);updatePWM();} // apply random value change
if (S==8) lset(); // set LCD brightness
}
if (M==2) S=cut(S,0,8); // check menu mode
SAVE++;updatelcd();delay(25);
}
}
enclast=encval;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////// SUPPORT
void lset() { ////////////////////////////////////////////////////////////////////////////// set LCD brightness
brght=cut(brght,0,9);
analogWrite(PWM2,LCDB[brght]);
}
void oset(int n,int t) { ///////////////////////////////////////////////////////////////// set switch off timer
otime[n]=cut(t,0,99);ots[n]=MS;ostate[n]=otime[n]?1:0;
SAVE++;
}
void fset(int n,int s) { /////////////////////////////////////////////////////////////// set fan state (on/off)
if (s==1) {
rts=MS+RDELAY;F[n]=1;catts[n]=vts[n]=MS;r[n]=0;
} else {
F[n]=0;bstate[n]=0;
}
updatePWM();
SAVE++;
}
void bset(int n,int s) { ///////////////////////////////////////////////////////////// set boost state (on/off)
if (s==1) {
rts=MS+RDELAY;bstate[n]=1;bts[n]=MS;F[n]=1;vts[n]=MS;
} else {
bstate[n]=0;rts=MS+RDELAY;
}
updatePWM();
SAVE++;
}
void updatelcd() { ///////////////////////////////////////////////////////////////////////////////// update LCD
for (int i=0;i<2;i++) {
if (S==0) {;slcd(1,1,5,"SPEED");ilcd(7+i*5,1,-4,v[i]);}
if (S==1) {;slcd(1,1,5,"BOOST");ilcd(7+i*5,1,-4,int(b[i]));}
if (S==2) {;slcd(1,1,5,"BTIME");ilcd(7+i*5,1,-4,int(btime[i]));}
if (S==3) {
slcd(1,1,5,"CATCH");
if (cat[i]==0) slcd( 7+i*5,1,-4,"OFF"); else slcd( 7+i*5,1,-3,"ON");
}
if (S==4) {;slcd(1,1,5,"CTIME");ilcd(7+i*5,1,-4,int(ctime[i]));}
if (S==5) {;slcd(1,1,5,"RTIME");ilcd(7+i*5,1,-4,int(rtime[i]));}
if (S==6) {;slcd(1,1,5,"OTIME");ilcd(7+i*5,1,-4,int(otime[i]));}
if (S==7) {;slcd(1,1,5,"RNVAL");ilcd(7+i*5,1,-4,int(rnval[i]));}
}
if (S==8) {;slcd(1,1,5,"BRGHT");ilcd(7,1,-4,brght);slcd(7+5,1,-4," ");}
}
void updatemarker() { ////////////////////////////////////////////////////////////////////// update menu marker
slcd(0,1,1," ");slcd(6,1,1," ");slcd(11,1,1," ");
int m[]={6,11,0};if (!LOCK) clcd(m[M],1,0);
}
void updatespeed() { ///////////////////////////////////////////////////////////////////////// update fan speed
if (bclr) slcd(0,0,6," ");
for (int i=0;i<2;i++) {
slcd(6+i*5,0,1," ");
if (cstate[i]) slcd(7+i*5,0,-4,"CAT");
else {
if (F[i]) {
if (bstate[i]) ilcd(1+i*3,0,-2, (((long)btime[i]*60000)-(MS-(long)bts[i]))/1000/60+1);
else if (ostate[i]) ilcd(1+i*3,0,-2, (((long)otime[i]*3600000)-(MS-(long)ots[i]))/1000/60/60+1);
ilcd(7+i*5,0,-4,round(xpm[i]));
if ((!bstate[i] && xv[i]<v[i]) || (bstate[i] && xb[i]<b[i])) clcd(6+i*5,0,1);
else {
if (xv[i]>=v[i] && rnval[i]>0 && !bstate[i]) clcd(6+i*5,0,2);
}
} else slcd(7+i*5,0,-4,"OFF");
}
}
}
void clcd(int x,int y, char v) { /////////////////////////////////////////////////////// write character to LCD
lcd.setCursor(x,y);lcd.write(v);
}
void ilcd(int x,int y, int l,int v) { //////////////////////////////////////////////////// write integer to LCD
sprintf(form,"%%%dd",l);sprintf(out,form,v);lcd.setCursor(x,y);lcd.print(out);
}
void slcd(int x,int y, int l,String s) { ////////////////////////////////////////////////// write string to LCD
sprintf(form,"%%%ds",l);sprintf(out,form,s.c_str());lcd.setCursor(x,y);lcd.print(out);
}
void calcramp(int i) { ///////////////////////////////////////////////////////////// calculate speed ramp value
xb[i]=b[i];
int rt=rtime[i]?rtime[i]:1;
if (bstate[i]) xb[i]=cut(((b[i]-v[i])/(rt*1000.0)*(MS-bts[i]))+v[i],v[i],b[i]);
xv[i]=v[i];
xv[i]=cut(((v[i]-FANMIN)/(rt*1000.0)*(MS-vts[i]))+FANMIN,FANMIN,v[i]);
if (rnval[i]>0 && xv[i]>=v[i]) xv[i]=cut(xv[i]+rnd[i],FANMIN,FANMAX);
}
void setPWM(int n,int v) { /////////////////////////////////////////// set value to OCR1x and states to off pin
if (n==0) {
OCR1A=v;
if (v==0) digitalWrite(OFF0,OFFSTATE?HIGH:LOW);
else digitalWrite(OFF0,OFFSTATE?LOW:HIGH);
}
if (n==1) {
OCR1B=v;
if (v==0) digitalWrite(OFF1,OFFSTATE?HIGH:LOW);
else digitalWrite(OFF1,OFFSTATE?LOW:HIGH);
}
}
void updatePWM() { ////////////////////////////////////////////////////////////// update PWM output (fan speed)
for (int i=0;i<2;i++) {
v[i]=cut(v[i],FANMIN,FANMAX);
b[i]=cut(b[i],FANMIN,FANMAX);
calcramp(i);
if (!F[i]) setPWM(i,0); else setPWM(i,cut((bstate[i]?xb[i]:xv[i])/(FANMAX/320.0)+r[i],0,320));
}
}
void rpmint0() {;ac[0]++;bc[0]++;} ///////////////////////////////////////////////////////////// rpm interrupts
void rpmint1() {;ac[1]++;bc[1]++;}
double cut(double v,double min,double max) {;return v>max?max:v<min?min:v>max?max:v;} ///// cut values to limit
int eer(int adr) {;return EEPROM.read(adr*2)+EEPROM.read(adr*2+1)*256;} /////////////////////////// read EEPROM
void eew(int adr, int val) {;EEPROM.write(adr*2,val%256);EEPROM.write(adr*2+1,val/256);} /////// save to EEPROM
void save() { /////////////////////////////////////////////////////////////////////////////////// save settings
for (int i=0;i<2;i++) {
eew(1+i,v[i]);eew(3+i,b[i]);eew(5+i,btime[i]);eew(7+i,cat[i]);
eew(9+i,ctime[i]);eew(11+i,F[i]);eew(13+i,rtime[i]);eew(15+i,rnval[i]);
}
eew(100,seed);
eew(101,brght);
}
int cutcmd(String data) { ///////////////////////////////////////////////////////////// get saperated substring
int mi=data.length(),n=0,j=0;
for (int i=0;i<=mi;i++) {
if (data.charAt(i) == ':' || i == mi) {;cmd[n]=data.substring(j,i);j=i+1;n++;}
}
for (int i=1;i<n;i++) icmd[i]=cmd[i].toInt();
return n;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////// END

View File

@ -1,70 +0,0 @@
#!/usr/bin/perl
###################################################################################### stir controll (mwx'2019)
use Device::SerialPort;
use Time::HiRes qw( usleep );
$dev='/dev/ttyACM0';
my $port = Device::SerialPort->new($dev); ################################################### setup serial port
$port->baudrate(9600);
$port->databits(8);
$port->parity("none");
$port->stopbits(1);
$port->lookfor();
$cmd=join(':',@ARGV);$cmd='info' if ($cmd=~/^\s*$/);
@r=split(':',&scmd($cmd));
print " LEFT RIGHT\n";
printf("Stirrer On: %6d %6d\n", $r[0],$r[16]);
printf("Speed: %6d %6d\n", $r[1],$r[17]);
printf("Boost Speed: %6d %6d\n", $r[2],$r[18]);
printf("RPM: %6d %6d\n", $r[3],$r[19]);
printf("Average RPM: %6d %6d\n", $r[4],$r[20]);
printf("Regulation: %6d %6d\n", $r[5],$r[21]);
printf("Boost On: %6d %6d\n", $r[6],$r[22]);
printf("Boost Time: %6d %6d\n", $r[7],$r[23]);
printf("Catch On: %6d %6d\n", $r[8],$r[24]);
printf("Catch Time: %6d %6d\n", $r[9],$r[25]);
printf("Rise Time: %6d %6d\n", $r[10],$r[26]);
printf("Off Time: %6d %6d\n", $r[11],$r[27]);
printf("Random Range: %6d %6d\n", $r[12],$r[28]);
printf("Random Value: %6d %6d\n", $r[13],$r[29]);
printf("Boost Remain: %6d %6d\n", $r[14],$r[30]);
printf("Off Remain: %6d %6d\n", $r[15],$r[31]);
printf("VERSION: %s\n",$r[32]);
printf("UPTIME: %s\n",&dhms($r[33]/1000));
printf("ERROR: %d\n",$r[34]);
sub scmd() { ##################################################################################### send command
my($cmd,$quiet)=@_;
$port->write($cmd);
my $founddata=0;
$msg="";
for ($i=0;$i<1000;$i++) {
$in = $port->lookfor();
$founddata=1 if ($in ne "");
$msg.=$in;
last if ($in eq "" && $founddata);
&usleep(2500);
}
$msg=~s/[\r\n]+//g;
return $msg;
}
sub dhms { ######################################################################## convert secs to dd:hh:mm:ss
my($t)=@_;my ($d,$h,$m,$s);
$s=$t%60;$t=($t-$s)/60;$m=$t%60;$t=($t-$m)/60;$h=$t%24;$t=($t-$h)/24;$d=$t;
return sprintf("%d:%02d:%02d:%02d",$d,$h,$m,$s);
}
########################################################################################################### END

View File

@ -1,7 +1,7 @@
# CAD Files
CAD files for 3D printed magnet moun. Compatible for magnets with size 20x10x5 mm and
*be quiet! Silent Wings 3 120/140 mm* fans.
CAD files for 3D printed magnet holder. Compatible for magnets with size 20x10x5 mm and
*be quiet! Silent Wings 3 140 mm* fans.
<p align="center">
<img src="https://github.com/micworg/stir/blob/master/images/magnetmount.jpg" width=500>

View File

@ -0,0 +1,52 @@
$fn=128;
g = 10; // magnet gap
fw = 48.3; // fan width
h = 4; // base height
r = 5; // edge width
pn = 7; // number of pins
ph = 7; // pin height
pa = 20; // pin angle
mw = 10.25; // magnet width
ml = 20.25; // magnet length
mb = 17; // magnet bar width
d=fw+r+r; // total diameter
cl=d-r; // magnet bar length
a=360/pn; // segment angle
e=(d+g-10)/d; // elliptic scale factor
difference() {
union() {
difference() {
union() {
translate([0,0,0]) cylinder(h+ph,d/2,d/2);
translate([0,0,0]) scale([e, 1, 1]) cylinder(h,d/2,d/2);
}
translate([0,0,-1]) cylinder(h+ph+2,d/2-r,d/2-r);
for (i =[0:1:pn-1]) {
pin(a*i);
}
}
translate([-cl/2,-mb/2,0]) cube([cl,mb,h]);
rotate ([0,0,90]) translate([-cl/2,-(mb-mw)/4-d/7,0]) cube([cl,(mb-mw)/2,h]);
rotate ([0,0,90]) translate([-cl/2,-(mb-mw)/4+d/7,0]) cube([cl,(mb-mw)/2,h]);
}
translate([-ml-g/2,-mw/2,-1]) cube([ml,mw,h]);
translate([g/2,-mw/2,-1]) cube([ml,mw,h]);
}
module pin(r) {
translate([0,0,0.01]) rotate([0,0,r]) hull() {
translate([0,0,h]) cylinder(h+ph,0.1,0.1);
translate([-d,0,h]) cylinder(h+ph,0.1,0.1);
rotate ([0,0,a-pa])union() {
translate([0,0,h]) cylinder(h+ph,0.1,0.1);
translate([-d,0,h]) cylinder(h+ph,0.1,0.1);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +0,0 @@
/// magnet mount for for magnets with size 20x10x5 mm and be quiet! Silent Wings 3 120/140 mm fans
$fn=128;
g = 10; // magnet gap
fan = 120; // fan size (120 or 140)
h = 4; // base height
r = 5; // edge width
pn = 7; // number of pins
ph = 7; // pin height
pa = 20; // pin angle
mw = 10.25; // magnet width
ml = 20.25; // magnet length
mb = 17; // magnet bar width
fw= fan==140 ? 48.3 : 42.5; // fan center width
eo= fan==140 ? 10 : 5; // elliptic extra offset
d=fw+r+r; // total diameter
cl=d-r; // magnet bar length
a=360/pn; // segment angle
e=(d+g-eo)/d; // elliptic scale factor
difference() {
union() {
difference() {
union() {
translate([0,0,0]) cylinder(h+ph,d/2,d/2);
translate([0,0,0]) scale([e, 1, 1]) cylinder(h,d/2,d/2);
}
translate([0,0,-1]) cylinder(h+ph+2,d/2-r,d/2-r);
for (i =[0:1:pn-1]) {
pin(a*i);
}
}
translate([-cl/2,-mb/2,0]) cube([cl,mb,h]);
rotate ([0,0,90]) translate([-cl/2,-(mb-mw)/4-d/7,0]) cube([cl,(mb-mw)/2,h]);
rotate ([0,0,90]) translate([-cl/2,-(mb-mw)/4+d/7,0]) cube([cl,(mb-mw)/2,h]);
}
translate([-ml-g/2,-mw/2,-1]) cube([ml,mw,h]);
translate([g/2,-mw/2,-1]) cube([ml,mw,h]);
}
module pin(r) {
translate([0,0,0.01]) rotate([0,0,r]) hull() {
translate([0,0,h]) cylinder(h+ph,0.1,0.1);
translate([-d,0,h]) cylinder(h+ph,0.1,0.1);
rotate ([0,0,a-pa])union() {
translate([0,0,h]) cylinder(h+ph,0.1,0.1);
translate([-d,0,h]) cylinder(h+ph,0.1,0.1);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 KiB

311
stir.ino
View File

@ -1,125 +1,84 @@
//////////////////////////////////////////////////////////////////////////////////// Stir Control V2 (mwx'2019)
/////////////////////////////////////////////////////////////////////////////////////// Stir Control (mwx'2019)
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
#define MS (long)millis()
#define NP 25
String VERSION = "2.1.2";
#define SX Serial.print
#define SXN Serial.println
int FANINIT = 0; // initialize fan with high voltage (0/1)
String VERSION = "1.7.0";
int SPEEDINC1 = 20; // speed increment values
int SPEEDINC2 = 50;
int SPEEDINC3 = 100;
int SPEEDSTEP1 = 400; // speed increment steps (increment changes at these values)
int SPEEDSTEP2 = 1000;
int RNDINC = 50; // random value increment (rpm)
int SPEEDINC = 50; // speed increment (rpm)
int FANMIN = 200; // fan minimum speed (should be a value at which the fan runs safely) (rpm)
int FANMAX = 2500; // fan maximum speed (should be the real maximum value of the fan) (rpm)
int FANMAX = 1600; // fan maximum speed (should be the real maximum value of the fan) (rpm)
int BOFF = 0; // if set boost off will also turn the normal mode off (0/1)
int BOFF = 0; // if set boost off will also turn the normal mode off
int BINC = 1; // BTIME increment (min)
int CINC = 10; // CTIME increment (min)
int RINC = 10; // RTIME increment (sec)
int OINC = 3; // OTIME increment (hour)
int CATCHSTOP = 20; // fish catch stop period (sec)
long CATCHSTOP = 20000; // fish catch stop period (ms)
int PWM0 = 9; // PWM pin for 1. fan
int PWM1 = 10; // PWM pin for 2. fan
int PWM2 = 11; // PWM pin for LCD brightness
int I0 = 0; // interrupt for fan 0 rpm signal (use 2 for Leonardo/ProMicro and 0 for Uno)
int I1 = 1; // interrupt for fan 1 rpm signal (use 3 for Leonardo/ProMicro and 1 for Uno)
int SWAPENC = 1; // swap encoder direction
int I0 = 2; // interrupt for fan 0 rpm signal (use 2 for Leonardo/ProMicro and 0 for Uno)
int I1 = 3; // interrupt for fan 1 rpm signal (use 3 for Leonardo/ProMicro and 1 for Uno)
int CLK = 5; // clk on KY-040 encoder (swap clk and dt to invert direction)
int CLK = 5; // clk on KY-040 encoder
int DT = 6; // dt on KY-040 encoder
int SW = 4; // sw on KY-040 encoder
int R0 = 7; // voltage select relais 0
int R1 = 8; // voltage select relais 1
int RTHRES = 700; // voltage switch threshold (rpm)
int OFF0 = 7; // off state pin 1. fan
int OFF1 = 8; // off state pin 2. fan
int OFFSTATE = LOW; // off state (LOW/HIGH)
int RINTERVAL = 50; // regulation internval (ds)
int RDELAY = 30; // regulation delay on changes (ds)
long RINTERVAL = 5000; // regulation internval (ms)
long RDELAY = 3000; // regulation delay on changes (ms)
int RTOL = 8; // regulation tolerance (rpm)
int RNDINTERVAL = 300; // randon value range change interval (sec)
long RNDINTERVL = 300000; // randon value change interval
int SINTERVAL = 20; // speed measurement internval (ds)
long SINTERVAL = 2000; // speed measurement internval
int SAVERAGE = 4; // speed measurement average
int SAVETAG = 2017; // save tag
int SAVETAG = 1014; // save tag
long SAVEDELAY = 10000; // EEPROM save delay (ms)
int RESET = 0; // factory reset, DON'T CHANGE THIS HERE (used from configuration menu)
int LGHT = 5; // LCD brightness
byte LCDB[] = {4,8,16,24,32,64,96,128,192,255}; // LCD brightness steps (10 values, 0=off, 255=max)
byte aright[] = {0x00,0x08,0x0C,0x0E,0x0C,0x08,0x00,0x00}; // LCD character
byte aup[] = {0x04,0x0E,0x1F,0x00,0x00,0x00,0x00,0x00};
byte arnd[] = {0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00,0x00};
LiquidCrystal_I2C lcd(0x27,16,2); // LCD display (connect to SDA/SCL)
int v[2],b[2],r[2]={0};double q,rpm[2]={0},xpm[2]={0},xb[2]={0},xv[2]={0},rtime[2]; // speed and regulation
long ac[2]={0},bc[2]={0}; // interrupt rpm counter
long xts,sts,rts,swts,buts,savets,catts[2],stop[2],bts[2],vts[2],ots[2],rndts[2]; // timing
int bdelay,bprocess=0,enclast,encval,M=2,S=0,C=0; // button/encoder processing
int bdelay,bprocess=0,enclast,encval,M=2,S=0; // button/encoder processing
int F[2],bstate[2]={0},btime[2],cat[2],ctime[2],cstate[2]={0},bclr=0,SAVE=0,LOCK=0; // operating states
int ostate[2]={0},otime[2]={0}; // off timer
int brght; // LCD brightness
int rnd[2]={0},rnval[2]={0};long seed; // random speed
char form[8],out[20]; // string buffer
String CF[NP];int *P[NP],Cinc[NP],Cmin[NP],Cmax[NP]; // configuration menu
int clkorg,dtorg; // encoder direction
char form[8],out[20];String cmd[8];int icmd[8]; // string buffer
void(* resetFunc) (void) = 0;
void setup() { ////////////////////////////////////////////////////////////////////////////////////////// SETUP
int i=0;
P[i]=&LGHT; CF[i]=F("LGHT"); defcon(i, 1, 0, 9 ); i++;
P[i]=&SPEEDINC1; CF[i]=F("SPI1"); defcon(i, 10, 10, 500 ); i++;
P[i]=&SPEEDINC2; CF[i]=F("SPI2"); defcon(i, 10, 10, 500 ); i++;
P[i]=&SPEEDINC3; CF[i]=F("SPI3"); defcon(i, 10, 10, 500 ); i++;
P[i]=&SPEEDSTEP1; CF[i]=F("SPS1"); defcon(i, 50, 100, 2000 ); i++;
P[i]=&SPEEDSTEP2; CF[i]=F("SPS2"); defcon(i, 50, 100, 2000 ); i++;
P[i]=&SWAPENC; CF[i]=F("SWEN"); defcon(i, 1, 0, 1 ); i++;
P[i]=&RNDINC; CF[i]=F("RINC"); defcon(i, 1, 0, 9 ); i++;
P[i]=&CATCHSTOP; CF[i]=F("CSTP"); defcon(i, 10, 0, 1000 ); i++;
P[i]=&FANMIN; CF[i]=F("FMIN"); defcon(i, 50, 100, 1000 ); i++;
P[i]=&FANMAX; CF[i]=F("FMAX"); defcon(i, 50, 500, 4000 ); i++;
P[i]=&FANINIT; CF[i]=F("FINI"); defcon(i, 1, 0, 1 ); i++;
P[i]=&RTHRES; CF[i]=F("RTHR"); defcon(i, 50, 0, 1500 ); i++;
P[i]=&RNDINTERVAL; CF[i]=F("RINT"); defcon(i, 1, 0, 60 ); i++;
P[i]=&BOFF; CF[i]=F("BOFF"); defcon(i, 1, 0, 1 ); i++;
P[i]=&BINC; CF[i]=F("BINC"); defcon(i, 1, 1, 60 ); i++;
P[i]=&CINC; CF[i]=F("CINC"); defcon(i, 1, 1, 60 ); i++;
P[i]=&RINC; CF[i]=F("RINC"); defcon(i, 1, 1, 60 ); i++;
P[i]=&OINC; CF[i]=F("OINC"); defcon(i, 1, 1, 60 ); i++;
P[i]=&RINTERVAL; CF[i]=F("XINT"); defcon(i, 1, 10, 250 ); i++;
P[i]=&RDELAY; CF[i]=F("XDEL"); defcon(i, 1, 0, 100 ); i++;
P[i]=&RTOL; CF[i]=F("XTOL"); defcon(i, 1, 1, 100 ); i++;
P[i]=&SINTERVAL; CF[i]=F("SINT"); defcon(i, 1, 0, 100 ); i++;
P[i]=&SAVERAGE; CF[i]=F("SAVG"); defcon(i, 1, 1, 100 ); i++;
P[i]=&RESET; CF[i]=F("RSET"); defcon(i, 1, 0, 1 ); i++;
Serial.begin(9600); // start serial
lcd.init();lcd.clear();lcd.backlight(); // initialize lcd
lcd.createChar(0,aright);lcd.createChar(1,aup);lcd.createChar(2,arnd); // load lcd characters
pinMode(PWM0,OUTPUT);pinMode(PWM1,OUTPUT);pinMode(PWM2,OUTPUT); // setup PWM pins
pinMode(CLK,INPUT);pinMode(DT,INPUT);pinMode(SW,INPUT); // setup KY-040 pins
digitalWrite(CLK,true);digitalWrite(DT,true);digitalWrite(SW,true); // turn ON pullup resistors
pinMode(R0,OUTPUT);pinMode(R1,OUTPUT); // setup voltage selcet relais pins
pinMode(OFF0,OUTPUT);pinMode(OFF1,OUTPUT); // setup off state pins
attachInterrupt(I0,rpmint0,FALLING); // setup interrupts vor rpm in
attachInterrupt(I1,rpmint1,FALLING);
@ -129,42 +88,69 @@ void setup() { /////////////////////////////////////////////////////////////////
SAVE=0; // load/initialize settings
if (eer(0)!=SAVETAG) {
for (i=0;i<2;i++) {;v[i]=300;b[i]=700;btime[i]=30;cat[i]=0;ctime[i]=120;rtime[i]=60;F[i]=0;rnval[i]=0;}
seed=0;
for (int i=0;i<2;i++) {;v[i]=300;b[i]=700;btime[i]=30;cat[i]=0;ctime[i]=120;rtime[i]=60;F[i]=0;rnval[i]=0;}
brght=5;seed=0;
eew(0,SAVETAG);save();
} else {
for (i=0;i<2;i++) {
for (int i=0;i<2;i++) {
v[i]=eer(1+i);b[i]=eer(3+i);btime[i]=eer(5+i);cat[i]=eer(7+i);
ctime[i]=eer(9+i);F[i]=eer(11+i);rtime[i]=eer(13+i);rnval[i]=eer(15+i);
}
seed=eer(100)+1;eew(100,seed);randomSeed(seed);
for (i=0;i<NP;i++) *P[i]=eer(200+i);
brght=eer(101);
}
clkorg=CLK;dtorg=DT;if (SWAPENC) {;DT=clkorg;CLK=dtorg;} // swap encoder direction
lset();
enclast=digitalRead(CLK); // get encoder state
for (i=0;i<2;i++) catts[i]=stop[i]=vts[i]=ots[i]=rndts[i]=MS;xts=sts=rts=swts=buts=savets=MS; // timer
for (int i=0;i<2;i++) catts[i]=stop[i]=vts[i]=ots[i]=rndts[i]=MS;xts=sts=rts=swts=buts=savets=MS; // timer
updatePWM();updatelcd();updatespeed();updatemarker(); // update all
slcd(0,0,5,VERSION); // show version
if (FANINIT) { // setup fans
digitalWrite(R0,LOW);digitalWrite(R1,LOW);delay(3000);digitalWrite(R0,HIGH);digitalWrite(R1,HIGH);
}
}
void loop() { //////////////////////////////////////////////////////////////////////////////////////////// LOOP
if (Serial.available() > 0) { ////////////////////////////////////////////////////////// serial communication
int n=cutcmd(Serial.readString());
int err=1;
int i=cut(icmd[1],0,1);
if (cmd[0]=="info" && n==1) {;err=0;}
if (cmd[0]=="version" && n==1) {;SXN(VERSION);return;}
if (cmd[0]=="save" && n==1) {;save();}
if (cmd[0]=="speed" && n==3) {;v[i]=cut(icmd[2],FANMIN,FANMAX);err=0;}
if (cmd[0]=="bspeed" && n==3) {;b[i]=cut(icmd[2],FANMIN,FANMAX);err=0;}
if (cmd[0]=="btime" && n==3) {;btime[i]=cut(icmd[2],0,60);err=0;}
if (cmd[0]=="ctime" && n==3) {;ctime[i]=cut(icmd[2],60,240);err=0;}
if (cmd[0]=="rtime" && n==3) {;rtime[i]=cut(icmd[2],0,240);err=0;}
if (cmd[0]=="rnval" && n==3) {;rnval[i]=cut(icmd[2],0,1000);err=0;}
if (cmd[0]=="on" && n==2) {;fset(i,1);err=0;}
if (cmd[0]=="off" && n==2) {;fset(i,0);err=0;}
if (cmd[0]=="bon" && n==2) {;bset(i,1);err=0;}
if (cmd[0]=="boff" && n==2) {;bset(i,0);err=0;}
if (cmd[0]=="con" && n==2) {;cat[i]=1;catts[i]=MS;err=0;}
if (cmd[0]=="coff" && n==2) {;cat[i]=0;catts[i]=MS;err=0;}
if (cmd[0]=="otime" && n==3) {;oset(i,icmd[2]);err=0;}
updatelcd();
for (int i=0;i<2;i++) {
SX(F[i]);SX(":");SX((int)(v[i]));SX(":");SX((int)(b[i]));SX(":");SX(rpm[i]);SX(":");
SX(xpm[i]);SX(":");SX(r[i]);SX(":");SX(bstate[i]);SX(":");SX(btime[i]);SX(":");
SX(cat[i]);SX(":");SX(ctime[i]);SX(":");SX(rtime[i]);SX(":");SX(otime[i]);SX(":");SX(rnval[i]);SX(":");
if (bstate[i]) SX((((long)btime[i]*60000)-(MS-(long)bts[i]))/1000+1); else SX(0);SX(":");
if (ostate[i]) SX((((long)otime[i]*3600000)-(MS-(long)ots[i]))/1000+1); else SX(0);SX(":");
}
SX(VERSION);SX(":");SXN(err);
}
if (SAVE>0 && MS-savets>SAVEDELAY) {;save();SAVE=0;savets=MS;} ////////////////////// save settings if needed
if (MS-savets>SAVEDELAY/5) bclr=1;
for (int i=0;i<2;i++) {
if (MS-rndts[i]>((long)RNDINTERVAL*1000)) { ////////////////////////////////////////////////// random timer
if (MS-rndts[i]>RNDINTERVL) { //////////////////////////////////////////////////////////////// random timer
rnd[i]=int(random(0,rnval[i]+1)/10)*10;rndts[i]=MS;updatelcd();
}
@ -176,7 +162,7 @@ void loop() { //////////////////////////////////////////////////////////////////
catts[i]=MS;cstate[i]=1;F[i]=0;stop[i]=MS;updatePWM();updatespeed();
}
if (cstate[i] && MS-stop[i]>((long)CATCHSTOP*1000) && F[i]==0) { ///////////// stop cat fish and start over
if (cstate[i] && MS-stop[i]>CATCHSTOP && F[i]==0) { ////////////////////////// stop cat fish and start over
cstate[i]=0;F[i]=1;vts[i]=MS;rts=MS+2000;updatePWM();updatespeed();
}
@ -184,12 +170,12 @@ void loop() { //////////////////////////////////////////////////////////////////
}
if (MS-xts>(SINTERVAL*100)) { ///////////////////////////////////////////////////////////// speed measurement
if (MS-xts>SINTERVAL) { /////////////////////////////////////////////////////////////////// speed measurement
for (int i=0;i<2;i++) xpm[i]=xpm[i]*(SAVERAGE-1)/SAVERAGE+(bc[i]/((MS-xts)/1000.0)*30.0)/SAVERAGE;
updatespeed();xts=MS;bc[0]=0;bc[1]=0;
}
if (MS-rts>(RINTERVAL*100)) { ////////////////////////////////////////////////////////////// speed regulation
if (MS-rts>RINTERVAL) { //////////////////////////////////////////////////////////////////// speed regulation
for (int i=0;i<2;i++) {
rpm[i]=ac[i]/((MS-sts)/1000.0)*30.0;
ac[i]=0;
@ -209,7 +195,7 @@ void loop() { //////////////////////////////////////////////////////////////////
bdelay=0; //////////////////////////////////////////////////////////////////////////////////// process switch
if (!bprocess) {
while (!digitalRead(SW)){
bdelay++;delay(15);bprocess=1;if (bdelay>20) break;
bdelay++;delay(25);bprocess=1;if (bdelay>20) break;
}
}
@ -228,20 +214,12 @@ void loop() { //////////////////////////////////////////////////////////////////
if (bstate[M]==0) bset(M,1); else {;bset(M,0);if (BOFF) fset(M,0);}
bclr=1;
}
if (S==8 && !LOCK) { // exit config mode
M=2;save();updatemarker();
if (SWAPENC) {;DT=clkorg;CLK=dtorg;}
else {;DT=dtorg;CLK=clkorg;}
if (RESET==1) {;RESET=0;eew(0,0);save();resetFunc();}
}
}
save();updatespeed();bdelay=0;
} else if (bdelay>0 && bdelay<20 && !LOCK) { // short button press, switch: menu -> fan 0 -> fan 1
M++;
if (S==8 && M==2) M=0;
if (M>2) M=0;
M++;if (S==8 && M==1) M=2;if (M>2) M=0;
updatemarker();
bdelay=0;
}
@ -253,57 +231,51 @@ void loop() { //////////////////////////////////////////////////////////////////
encval = digitalRead(CLK); ////////////////////////////////////////////////////////////////// process encoder
if (encval != enclast && !LOCK) {
if(!encval){
if (digitalRead(DT) != encval) { // turn encoder clockwise
if (M==2) S++; // scroll menu
if (M==2) S++; // scroll menu
else {
if (S==0) v[M]=speedinc(v[M],1); // fan speed up
if (S==1) b[M]=speedinc(b[M],1); // boost speed up
if (S==2) btime[M]+=BINC; // boost time up
if (S==3) cat[M]++; // cat on/off
if (S==4) ctime[M]+=CINC; // cat time up
if (S==5) rtime[M]+=RINC; // rise time up
if (S==6) otime[M]+=OINC; // off time up
if (S==7) rnval[M]+=RNDINC; // random value
if (S==8 && M==0) C++; // increase config menu
if (S==8 && M==1) *P[C]=cut(*P[C]+Cinc[C],Cmin[C],Cmax[C]); // increase configuration value
if (S==0) v[M]+=SPEEDINC; // fan speed up
if (S==1) b[M]+=SPEEDINC; // boost speed up
if (S==2) btime[M]+=BINC; // boost time up
if (S==3) cat[M]++; // cat on/off
if (S==4) ctime[M]+=CINC; // cat time up
if (S==5) rtime[M]+=RINC; // rise time up
if (S==6) otime[M]+=OINC; // off time up
if (S==7) rnval[M]+=SPEEDINC; // random value
if (S==8) brght++; // increase LCD brightness
}
} else { // turn encoder counterclockwise
if (M==2) S--; // scroll menu
if (M==2) S--; // scroll menu
else {
if (S==0) v[M]=speedinc(v[M],0); // fan speed down
if (S==1) b[M]=speedinc(b[M],0); // boost speed down
if (S==2) btime[M]-=BINC; // boost time down
if (S==3) cat[M]--; // cat on/off
if (S==4) ctime[M]-=CINC; // cat time down
if (S==5) rtime[M]-=RINC; // rise time down
if (S==6) otime[M]-=OINC; // off time down
if (S==7) rnval[M]-=RNDINC; // random value
if (S==8 && M==0) C--; // decrease config menu
if (S==8 && M==1) *P[C]=cut(*P[C]-Cinc[C],Cmin[C],Cmax[C]); // decrease configuration value
if (S==0) v[M]-=SPEEDINC; // fan speed down
if (S==1) b[M]-=SPEEDINC; // boost speed down
if (S==2) btime[M]-=BINC; // boost time down
if (S==3) cat[M]--; // cat on/off
if (S==4) ctime[M]-=CINC; // cat time down
if (S==5) rtime[M]-=RINC; // rise time down
if (S==6) otime[M]-=OINC; // off time down
if (S==7) rnval[M]-=SPEEDINC; // random value
if (S==8) brght--; // decrease LCD brightness
}
}
for (int i=0;i<2;i++) if (b[i]<v[i]) b[i]=v[i];
if (M!=2) {
if (S<=1) {;updatePWM();rts=MS+(RDELAY*100);} // apply (boost) speed change
if (S<=1) {;updatePWM();rts=MS+RDELAY;} // apply (boost) speed change
if (S==2) btime[M]=cut(btime[M],0,99); // check boost time
if (S==3) {;cat[M]=cut(cat[M],0,1);catts[M]=MS;} // check cat
if (S==4) ctime[M]=cut(ctime[M],60,240); // check cat time
if (S==5) rtime[M]=cut(rtime[M],0,240); // check rise time
if (S==6) oset(M,otime[M]); // check off time
if (S==7) {;rnval[M]=cut(rnval[M],0,1000);updatePWM();} // apply random value change
if (S==8 && C==0) lset(); // set LCD brightness
if (S==8) lset(); // set LCD brightness
}
C=cut(C,0,NP-1); // check configuration mode
if (M==2) S=cut(S,0,8); // check menu mode
SAVE++;updatelcd();delay(25);
SAVE++;updatelcd();delay(50);
}
}
enclast=encval;
@ -312,27 +284,9 @@ void loop() { //////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////// SUPPORT
void defcon(int n, int inc, int min, int max) { ///////////////////////////////////////////// set config limits
Cinc[n]=inc; Cmin[n]=min; Cmax[n]=max;
}
int speedinc(int s,int mode) { ////////////////////////////////////////////////////// calculate speed increment
if (mode==0) {
if (s<=SPEEDSTEP1) return s-SPEEDINC1;
if (s<=SPEEDSTEP2) return s-SPEEDINC2;
return s-SPEEDINC3;
}
if (mode==1) {
if (s<SPEEDSTEP1) return s+SPEEDINC1;
if (s<SPEEDSTEP2) return s+SPEEDINC2;
return s+SPEEDINC3;
}
return s;
}
void lset() { ////////////////////////////////////////////////////////////////////////////// set LCD brightness
LGHT=cut(LGHT,0,9);
analogWrite(PWM2,LCDB[LGHT]);
brght=cut(brght,0,9);
analogWrite(PWM2,LCDB[brght]);
}
void oset(int n,int t) { ///////////////////////////////////////////////////////////////// set switch off timer
@ -342,7 +296,7 @@ void oset(int n,int t) { ///////////////////////////////////////////////////////
void fset(int n,int s) { /////////////////////////////////////////////////////////////// set fan state (on/off)
if (s==1) {
rts=MS+(RDELAY*100);F[n]=1;catts[n]=vts[n]=MS;r[n]=0;
rts=MS+RDELAY;F[n]=1;catts[n]=vts[n]=MS;r[n]=0;
} else {
F[n]=0;bstate[n]=0;
}
@ -352,42 +306,41 @@ void fset(int n,int s) { ///////////////////////////////////////////////////////
void bset(int n,int s) { ///////////////////////////////////////////////////////////// set boost state (on/off)
if (s==1) {
rts=MS+(RDELAY*100);bstate[n]=1;bts[n]=MS;F[n]=1;vts[n]=MS;
rts=MS+RDELAY;bstate[n]=1;bts[n]=MS;F[n]=1;vts[n]=MS;
} else {
bstate[n]=0;rts=MS+(RDELAY*100);
bstate[n]=0;rts=MS+RDELAY;
}
updatePWM();
SAVE++;
}
void updatelcd() { ///////////////////////////////////////////////////////////////////////////////// update LCD
for (int i=0;i<2;i++) {
if (S==0) {;slcd(1,1,5,F("SPEED"));ilcd(7+i*5,1,-4,v[i]);}
if (S==1) {;slcd(1,1,5,F("BOOST"));ilcd(7+i*5,1,-4,int(b[i]));}
if (S==2) {;slcd(1,1,5,F("BTIME"));ilcd(7+i*5,1,-4,int(btime[i]));}
if (S==0) {;slcd(1,1,5,"SPEED");ilcd(7+i*5,1,-4,v[i]);}
if (S==1) {;slcd(1,1,5,"BOOST");ilcd(7+i*5,1,-4,int(b[i]));}
if (S==2) {;slcd(1,1,5,"BTIME");ilcd(7+i*5,1,-4,int(btime[i]));}
if (S==3) {
slcd(1,1,5,F("CATCH"));
if (cat[i]==0) slcd( 7+i*5,1,-4,F("OFF")); else slcd( 7+i*5,1,-3,F("ON"));
slcd(1,1,5,"CATCH");
if (cat[i]==0) slcd( 7+i*5,1,-4,"OFF"); else slcd( 7+i*5,1,-3,"ON");
}
if (S==4) {;slcd(1,1,5,F("CTIME"));ilcd(7+i*5,1,-4,int(ctime[i]));}
if (S==5) {;slcd(1,1,5,F("RTIME"));ilcd(7+i*5,1,-4,int(rtime[i]));}
if (S==6) {;slcd(1,1,5,F("OTIME"));ilcd(7+i*5,1,-4,int(otime[i]));}
if (S==7) {;slcd(1,1,5,F("RNVAL"));ilcd(7+i*5,1,-4,int(rnval[i]));}
if (S==4) {;slcd(1,1,5,"CTIME");ilcd(7+i*5,1,-4,int(ctime[i]));}
if (S==5) {;slcd(1,1,5,"RTIME");ilcd(7+i*5,1,-4,int(rtime[i]));}
if (S==6) {;slcd(1,1,5,"OTIME");ilcd(7+i*5,1,-4,int(otime[i]));}
if (S==7) {;slcd(1,1,5,"RNVAL");ilcd(7+i*5,1,-4,int(rnval[i]));}
}
if (S==8) {;slcd(1,1,-5,F("CFG"));slcd(7,1,-4,CF[C]);ilcd(7+5,1,-4,*P[C]);}
if (S==8) {;slcd(1,1,5,"BRGHT");ilcd(7,1,-4,brght);slcd(7+5,1,-4," ");}
}
void updatemarker() { ////////////////////////////////////////////////////////////////////// update menu marker
slcd(0,1,1,F(" "));slcd(6,1,1,F(" "));slcd(11,1,1,F(" "));
slcd(0,1,1," ");slcd(6,1,1," ");slcd(11,1,1," ");
int m[]={6,11,0};if (!LOCK) clcd(m[M],1,0);
}
void updatespeed() { ///////////////////////////////////////////////////////////////////////// update fan speed
if (bclr) slcd(0,0,6,F(" "));
if (bclr) slcd(0,0,6," ");
for (int i=0;i<2;i++) {
slcd(6+i*5,0,1," ");
if (cstate[i]) slcd(7+i*5,0,-4,F("CAT"));
if (cstate[i]) slcd(7+i*5,0,-4,"CAT");
else {
if (F[i]) {
if (bstate[i]) ilcd(1+i*3,0,-2, (((long)btime[i]*60000)-(MS-(long)bts[i]))/1000/60+1);
@ -397,7 +350,7 @@ void updatespeed() { ///////////////////////////////////////////////////////////
else {
if (xv[i]>=v[i] && rnval[i]>0 && !bstate[i]) clcd(6+i*5,0,2);
}
} else slcd(7+i*5,0,-4,F("OFF"));
} else slcd(7+i*5,0,-4,"OFF");
}
}
}
@ -415,17 +368,22 @@ void slcd(int x,int y, int l,String s) { ///////////////////////////////////////
}
void calcramp(int i) { ///////////////////////////////////////////////////////////// calculate speed ramp value
xb[i]=b[i];
int rt=rtime[i]?rtime[i]:1;
if (bstate[i]) xb[i]=cut(((b[i]-v[i])/(rt*1000.0)*(MS-bts[i]))+v[i],v[i],b[i]);
xv[i]=v[i];
xv[i]=cut(((v[i]-FANMIN)/(rt*1000.0)*(MS-vts[i]))+FANMIN,FANMIN,v[i]);
xb[i]=b[i];if (bstate[i]) xb[i]=cut(((b[i]-v[i])/(rtime[i]*1000.0)*(MS-bts[i]))+v[i],v[i],b[i]);
xv[i]=v[i];xv[i]=cut(((v[i]-FANMIN)/(rtime[i]*1000.0)*(MS-vts[i]))+FANMIN,FANMIN,v[i]);
if (rnval[i]>0 && xv[i]>=v[i]) xv[i]=cut(xv[i]+rnd[i],FANMIN,FANMAX);
}
void setPWM(int n,int v) { /////////////////////////////////////////// set value to OCR1x and states to off pin
if (n==0) OCR1A=v;
if (n==1) OCR1B=v;
if (n==0) {
OCR1A=v;
if (v==0) digitalWrite(OFF0,OFFSTATE?HIGH:LOW);
else digitalWrite(OFF0,OFFSTATE?LOW:HIGH);
}
if (n==1) {
OCR1B=v;
if (v==0) digitalWrite(OFF1,OFFSTATE?HIGH:LOW);
else digitalWrite(OFF1,OFFSTATE?LOW:HIGH);
}
}
void updatePWM() { ////////////////////////////////////////////////////////////// update PWM output (fan speed)
@ -433,14 +391,8 @@ void updatePWM() { /////////////////////////////////////////////////////////////
v[i]=cut(v[i],FANMIN,FANMAX);
b[i]=cut(b[i],FANMIN,FANMAX);
calcramp(i);
if (!F[i]) setPWM(i,0); else setPWM(i,cut((bstate[i]?xb[i]:xv[i])/(FANMAX/320.0)+r[i],1,320));
if (!F[i]) setPWM(i,0); else setPWM(i,cut((bstate[i]?xb[i]:xv[i])/(FANMAX/320.0)+r[i],0,320));
}
if ((bstate[0]?b[0]:v[0])>=RTHRES) digitalWrite(R0,LOW);
else digitalWrite(R0,HIGH);
if ((bstate[1]?b[1]:v[1])>=RTHRES) digitalWrite(R1,LOW);
else digitalWrite(R1,HIGH);
}
void rpmint0() {;ac[0]++;bc[0]++;} ///////////////////////////////////////////////////////////// rpm interrupts
@ -458,7 +410,16 @@ void save() { //////////////////////////////////////////////////////////////////
eew(9+i,ctime[i]);eew(11+i,F[i]);eew(13+i,rtime[i]);eew(15+i,rnval[i]);
}
eew(100,seed);
for (int i=0;i<NP;i++) eew(200+i,*P[i]);
eew(101,brght);
}
int cutcmd(String data) { ///////////////////////////////////////////////////////////// get saperated substring
int mi=data.length(),n=0,j=0;
for (int i=0;i<=mi;i++) {
if (data.charAt(i) == ':' || i == mi) {;cmd[n]=data.substring(j,i);j=i+1;n++;}
}
for (int i=1;i<n;i++) icmd[i]=cmd[i].toInt();
return n;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////// END

61
stirctl Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/perl
###################################################################################### stir controll (mwx'2019)
use Device::SerialPort;
use Time::HiRes qw( usleep );
$dev='/dev/ttyACM0';
my $port = Device::SerialPort->new($dev); ################################################### setup serial port
$port->baudrate(9600);
$port->databits(8);
$port->parity("none");
$port->stopbits(1);
$port->lookfor();
$cmd=join(':',@ARGV);$cmd='info' if ($cmd=~/^\s*$/);
@r=split(':',&scmd($cmd));
print " Left Right\n";
printf("Stirrer On: %6d %6d\n", $r[0],$r[15]);
printf("Speed: %6d %6d\n", $r[1],$r[16]);
printf("Boost Speed: %6d %6d\n", $r[2],$r[17]);
printf("RPM: %6d %6d\n", $r[3],$r[18]);
printf("Average RPM: %6d %6d\n", $r[4],$r[19]);
printf("Regulation: %6d %6d\n", $r[5],$r[20]);
printf("Boost On: %6d %6d\n", $r[6],$r[21]);
printf("Boost Time: %6d %6d\n", $r[7],$r[22]);
printf("Catch On: %6d %6d\n", $r[8],$r[23]);
printf("Catch Time: %6d %6d\n", $r[9],$r[24]);
printf("Rise Time: %6d %6d\n", $r[10],$r[25]);
printf("Off Time: %6d %6d\n", $r[11],$r[26]);
printf("Random Value: %6d %6d\n", $r[12],$r[27]);
printf("Boost Remain: %6d %6d\n", $r[13],$r[28]);
printf("Off Remain: %6d %6d\n", $r[14],$r[29]);
printf("Error: %d\nVersion: %s\n", $r[30],$r[31]);
sub scmd() { ##################################################################################### send command
my($cmd,$quiet)=@_;
$port->write($cmd);
my $founddata=0;
$msg="";
for ($i=0;$i<1000;$i++) {
$in = $port->lookfor();
$founddata=1 if ($in ne "");
$msg.=$in;
last if ($in eq "" && $founddata);
&usleep(2500);
}
$msg=~s/[\r\n]+//g;
return $msg;
}
########################################################################################################### END

13
unoshield/README.md Normal file
View File

@ -0,0 +1,13 @@
# Stir Shield
Stir shield for Arduino UNO from Adrian Sigel.
<p align="center">
<img src="https://github.com/micworg/stir/blob/master/unoshield/images/pcb_top.png" width=500>
</p>
<p align="center">
<img src="https://github.com/micworg/stir/blob/master/unoshield/images/pcb_bottom.png" width=500>
</p>

View File

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB