I prefer a quiet and calm environment. It's why I prefer listening to the more mellow broadcasts from BBC Radio 4 or Classic FM. It's why I like full break-in CW on PIN diode rigs from Elecraft and Ten-Tec. It's why I run a Mac Mini for macOS and a fanless industrial PC for Windows.
More often than not, equipment cooling and equipment noise go hand in hand. The more cooling required, the more airflow is needed and the more fan noise will be produced. However, fan choice and mounting options will help to mitigate this noise. As will a smart fan cooling system to control fan speed.
This project is an offshoot of a unit which was cobbled up with the objective of cooling communications kit in hot climates. A need to monitor and cool equipment whilst keeping the level of audible nuisance to a discreet level was required. It can be adapted to cool dummy loads, power supplies, computers, lighting...
THE PLANSearching for a solution it was found that the cheap Chinese single board controllers available only provided a simplistic fan curve solution. These controllers just track temperature differences against fan speed. Not many parameters can be modified. On the other end of the scale there are complex cooling controller systems available for gaming computers, using PC software. These are fully configurable systems able to balance the cooling performance required against the generated sound level.
It was hard to find an intermediate solution. One between the simple stand alone boards and the complex systems used by PCs. So, an embedded system was conceived to fulfill my requirements. Starting simple and then building up the design based on specific needs. The project quickly dominoed to include some easily customisable features which can be setup for individual temperature and fan speed requirements.
THE FEATURES- Two fan controller.
- Full fan speed control from 0% to 100% PWM.
- Temperature sensing from 0 to +120degC (32degF to 248degF).
- Fan speed tachometer reading to 9999 Revolutions Per Minute.
- OLED display showing fan and temperature information.
- Fan PWM commanded bargraph (10% intervals).
- Rotary encoder to manipulate configuration menu.
- Define temperature range, minimum to maximum.
- Define fan speed range, minimum to maximum.
- Three secondary Fan-B working options:
- a) Main Fan-A with secondary Fan-B running in tandem (Symmetric).
- b) Main Fan-A with secondary Fan-B running in opposite (Asymmetric).
- c) Main Fan-A with secondary Fan-B configured to Hot-Standby.
- Audible Fan-A failure or stoppage alarm.
- Luminous Fan-A stoppage alarm.
- Luminous DS18B20S sensor bus indicator LED.
- Luminous +12V and +5V power supply LEDs.
- Configuration settings saved in EEPROM, for power off recovery.
- Configuration header to set temperature units (degC or degF)
- Configuration header to set Symmetric, Assymetric or Hot-Standby mode.
- Isolated external P-Input, to enable/disable operation by auxiliary equipment.
- Main Fan-A failure detection, used to switch to Fan-B in Hot-Standby mode.
- Serial port for debugging and firmware uploading.
- In Circuit Serial Programming port for direct burning to memory.
- KF2510 3+1P, fan connector sockets.
- Easily accessible on board Reset switch.
Two versions were designed. Both functionally the same. However, each version has been created to accommodate individual requirements and building skills.
- Shield version: This is the easiest to build as half of the functionalities have already been taken care of by the Arduino board. The shield just connects atop the Uno R3 board like any standard shield. It provides the rest of the circuitry required for operation in a surprisingly compact package. Building the shield version is simpler, although you should still be able to handle the fifteen 0805 SMDs it uses. Code uploading is much more manageable and as usual performed with the Arduino IDE through the USB serial port.
- Embedded version: This is built around an Atmega 328P-AU microcontroller and has the all necessary circuitry to have an independant stand-alone unit. The build complexity is greater. You should be equipped and proficient with SMD project construction. Be aware that you will also need to familiarise yourself with the ICSP port. To upload code to the microcontroller memory, at least initially, until you burn a bootloader if you prefer.
This code was built up, on the fly as needed. From a simple temperature tracking fan speed utility it quickly avalanched into this customisable version. No doubt there are other programming methods to achieve what is desired. It is what it is. Anyway my code can be easily corrected, modified and improved on to each one's satisfaction.
Start Up Sequence
On power up or reset, the unit waits some seconds for any still spinning fan to settle to a stop. The delay needed can depend on how easily a fan spins and its momentum. The delay can be defined in code by modifying the variable spinDownDelay.
The code then runs through a start up sequence which will first test the fan to find its minimum speed value. This will be used in the stoppage detection fanJamCheck() function during operation. This sequence sends incrementing values of PWM to the fan, up to a ratio of 20:100 (20%). Most fans will have started by then. Its progression is displayed on the OLED before finally starting the main code and one of the working modes. Each of these selectable working modes are listed and explained below.
Two basic fan modes are possible:
- Firstly, a passive up and down Sweeping mode which just follows the defined curve slope defined by Fan Minimum and Fan Maximum. The rate of this sweep can be configured but no temperature sensor data is used to control the fan speed.
- Secondly, an active Triggered mode which activates the fan and controls it's speed based on the curve slope defined by Temperature Minimum, Temperature Maximum, Fan Minimum and Fan Maximum. There are two types of active Trigger mode. The first submode takes just sensor temperature input to control the cooling fan. The second submode takes sensor temperature input but also requires an input voltage (P-IN) to enable the cooling fan. This can be used with auxiliary equipment, to only trigger the fan controller when the auxiliary equipment deems it necessary.
Scroll through the graphics below for example configured slopes:
In summary:
One passive Sweeping Mode:
Here the fan speed will sweep up and down across the fan speed slope as previously defined by Fan Minimum and Fan Maximum. No temperature reading is used but the rate of sweep can be changed.
Two active Trigger Modes:
Here the fan will be triggered when Temperature Minimum is reached AND only if the P-IN input is enabled.
Triggered at Temperature Minimum:
a) From Zero fan speed (Fan is OFF until triggered).
b) From Fan Minimum speed (Fan spins at Fan Minimum until triggered).
Triggered at Temperature Minimum, but only when P-IN input is also enabled.
a) From Zero fan speed. (Fan is OFF until triggered).
b) From Fan Minimum speed (Fan spins at Fan Minimum until triggered).
These fan modes and how they relate to speed and temperature can most easily be seen by scrolling through the example temperature/fan speed graphs shown.
THE CONFIGURATIONDefining the unit's modus operandi as well as the cooling slope required is done in the configuration mode. Essentially by defining points on a graph to get the slope we want. We can enter this mode by pushing the rotary encoder button. Then scrolling through the configuration menu, with successive pushes of the same button. Turning the rotary encoder at each parameter to modify its value and thus defining a new mode of operation or temperature slope.
Configuration Menu:
FAN MIN
To set the minimum fan speed value (5% steps).
FAN MAX
To set the maximum fan speed value (5% steps).
TEMP MIN
To set the minimum temperature value, which will trigger the fan to Fan Min.
TEMP MAX
To set the maximum temperature value, where the fan will spin at Fan Max.
MODE
Sweeping OR Triggered
To set either a passive up and down sweeping fan speed. Or a triggered fan speed which actively tracks the temperature data from the DS18B20 sensor.
SWEEP RATE
(available if Sweeping was chosen previously)1/Slow to 10/Fast. Selects how fast the fan speed will sweep from fan minimum to maximum.
TRIGGER
(available if Triggered was chosen previously)
1 Temp/Zero. Temperature triggered from zero. Will trigger at Temp Min and start to spin at Fan Min speed. When not triggered the fan speed will wait at zero.
2 Temp/Fmin. Temperature triggered from fan minimum. Will trigger at Temp Min and start to spin at Fan Min speed. When not triggered the fan speed will wait at Fan Min.
3 Pin/Zero. Temperature triggered from zero, but with an external P-input enable required. Will trigger at Temp Min and start to spin at Fan Min speed, but only when enabled by P-input. When not triggered the fan speed will wait at zero.
4 Pin/Fmin. Temperature triggered from fan minimum, but with an external P-input enable required. Will trigger at Temp Min and start to spin at Fan Min speed, but only when enabled by P-input. When not triggered the fan speed will wait at Fan Min.
TEMP OFFSET
-9 to +9 degrees.To correct for any temperature error from the DS18B20 sensor, this compensation value can be added. The default being 0 degrees for no correction.
SAVE & EXIT
Yes/No.Yes, will exit the configuration mode with a corresponding audible morse 'R' and a brief 'configuration saved' message. Your setup will be saved to EEPROM.No, will cycle through the configuration options again.
Since the values of FAN MIN and FAN MAX are interdependent. It is impossible to set a FAN MAX value below the FAN MIN value or to set a FAN MIN value above the FAN MAX value.Similarly the values of TEMP MIN and TEMP MAX are interdependent. It is impossible to set a TMP MAX value below the TEMP MIN value or to set a TEMP MIN value above the TEMP MAX value.
The code limits the allowed range you are able to set. For example, if you cannot choose a lower FAN MAX value it is probably because you are trying to set it below the FAN MIN value.THE INITIAL SETUP
The following options should rarely need changing after a unit is installed. They are configured by the use of two header pin jumpers whose settings are read at switch ON or reset.
1. Secondary FAN-B setup with Jumper J1/U2:
ASYMetric: Secondary Fan-B will spin at the opposite speed of Primary Fan-A. Thus when Fan-A is at maximum speed, Fan-B will be at minimum speed. These fan speeds will dynamically follow the temperature slope as defined by Fan Min and Fan Max.
SYMMetric: Secondary Fan-B will spin in tandem with Primary Fan-A. Thus when Fan-A is at maximum speed, Fan-B will also be at maximum speed. These fan speeds will dynamically follow the temperature slope as defined by Fan Min and Fan Max.
STandBY: Secondary Fan-B acts as a backup to the primary Fan-A in a hot-standby role. Thus Fan-B will normally stay off up until Fan-A jams or fails. On Fan-A failure Fan-B will take over.
2. Units of temperature setup with Jumper J2/U1:
degF: Temperature in Fahrenheit. As used in the USA, Liberia and the Cayman Islands.
degC: Temperature in Centigrade. As used in the rest of the world.
THE BUILDING BLOCKSMCU:
We don't need the processing might or bells and whistles in an ESP8266 or ESP32. The Arduino platform with its ATmega 328P chip has enough speed, memory space and GPIO pins.
Of course, like the original Arduino UNO R3, an ATmega328P microntroller is used in the embedded circuit. This microcontroller provides 32K of flash memory, 2K of SRAM and 1K of EEPROM storage. It's clock speed of 16Mhz is adequate for running our moderate tasks and it has enough input/output pins for our necessities.
The AU chip package was chosen. It's smaller, cheaper and easier to source than the large PU DIP version. It's ideal for a small embedded board application. Yet not too small to handle when building or indeed testing. During prototyping a QFP/TQFP32 chip adapter was used to mount this Atmega 328P on the breadboard. The legless MU version although even smaller, is harder to handled.
Temperature sensor:
The Dallas Semiconductor DS18B20 might not be the most precise temperature measuring instrument but it's sufficient for our use. It measures temperatures from -55degC to +125degC (-67degF to +257degF), with +/-0.5degC accuracy from -10°C to +85°C.
For our purposes the code caps the temperature range from 0degC to 120degC (32degF to 248degF). Outside this range it will display an Out Of Range (OOR) 'exclamation' icon next to the temperature value. This value will bottom out at 0degC and 120degC (32degF and 248degF).
Judicious use of a heatgun can be used to test the higher temperatures and some electronic component freeze spray to test the colder temperatures. Although I use Decathlon Athletes Muscle Spray, which is what I had handy. It works just as well and smells nicer.
The three pin TO-92 version of the DS18B20 is probably the easiest package type to use. A pullup resistor with LED across the sensor's data line to Vdd improves data transfer and gives some visual feedback of the sensor communicating with the microcontroller. Here, a 2K resistor works well to flash a LED. Some kit modules use a 4.7K value here. It depends on the voltage drop across your particular LED and the brightness needed.
OLED display:
A small 0.91" OLED is used. It has a resolution of 128 x 64 pixels available which when used with the SSD1306 library by Bill Greiman and the 'fixed_bold10x15' font, provides two lines of about 12 text characters each. These OLED displays have an I2C bus which adapts perfectly to our microntroller. The required supply voltage of +5V is convenient as it's the same as the rest of the board. Some OLED boards specify a +3.3V supply voltage requirement and this can be checked by seeing if it has or not already a +3.3V voltage regulator. Mine has a tiny 662K low drop out +3.3V voltage regulator on board confirming it accepts +5V, which it then converts to +3.3V itself. As regards the I2C logic level inputs, they are +5V tolerant and I did not need a level converter.
SSD1306Ascii is an unbuffered character only library for small monochrome OLED displays written by Bill Greiman. I often use it in projects using OLEDs with the fixed_bold10x15 font. It provides a readable character size for my tired eyes on these small displays. For this particular project the FAN_fixed_bold10x15 file is a modified version of the font where I have added some extra icons used with the fan controller. This font file can be included in the same sketch directory and added to the Arduino IDE for easy editing.
FAN_fixed_bold10x15 is the fixed_bold10x15.h font which has been modified by the addition of extra characters used by the OLED display of the fan controller. Reverse engineering this font to create custom characters and icons are detailed later.
Optocoupler:
The PC817C is a generic optocoupler used abundantly in many circuits to provide electrical isolation from auxiliary equipment. A few versions exist of this component. Here the 'C' suffixed version was used. Different versions have different gains. The gain of the optocoupler is expressed as a current transfer ratio (CTR), which is the collector current (IC) of the output divided by the forward current (IF) of the input, then multiplied by 100%. You can use other versions and test functionality by calculating and changing the value of resistor R3 to accommodate different gains.
On this version, a calculated 1K resistor value functions well. A few measured tests confirmed the theory:P-IN voltages of 5V, 9V, 12V and 13.8V gave currents of 3.8mA, 7.8mA, 10.8mA, and 12.6mA respectively. These were deemed the most likely auxiliary input voltages that would be used. They each produce corresponding power values of about 4.5mW, 9.4mW, 13mW and 15mW since the internal LED of this optocoupler drops about 1.2V. Checking the datasheet we are allowed maximum input values of If=50mA, Vr=6V and Pin= 70mW. The 1N4148 placed across the input LED is to protect against a Vr above 6V. The maximum output values allowed are Vce=35V, Vec=6V, Ic=50mA and Pc=150mW. The MCU's internal pullup seems to be about 50k because 0.1mA was measured here. With Ptot=200mW max for this optocoupler (Power in and out combined must not exceed 200mW).
Other optoisolators can be used following similar datasheet recommendations. Alternatively, you can simplify the circuit by doing away with optocoupler isolation and just bring the enable P-IN low. The MCU's Port B0 has its internal pullup activated in the code.
Serial Port:
A serial port is already fitted on UNO R3 boards. This is used for uploading and debugging in the classic Arduino way.
I've also included a serial port on the stand-alone version. These 6 header pins line up perfectly with most FTDI FT232RL USB To Serial Adapter Module. It also permits debugging with the Arduino IDE and indeed sketch uploading, if there is a bootloader available. Don't forget to switch the FTDI USB-Serial board to use +5V (not +3.3V). For USB to Serial programming and debugging the Fan Controller should be powered ON of course. The bootloader program is covered in section 26.6 of the ATmega datasheet.
ICSP Port:
An ICSP port is already fitted on UNO R3 boards. This is required for initial burning code directly to its microcontroller memory and rarely used by us. It is probably how the manufacturer burnt the Arduino bootloader to the UNO.
I've also included an ICSP port on the embedded version. It's the only way to initially burn the bootloadr to the stand-alone version. These 3x2 header pins align perfectly with most of the common ICSP programmers like the USBasp. It permits direct loading of the ATmega 328P. For ICSP USBasp programming the fan controller should be powered OFF, because most programmers like the USBasp will power its target itself.
Be aware, some older ICSP programmers need power from their target to function and in this case the fan controller unit should be powered ON. Some may just need to see a voltage on ICSP pin 2 to function. Your mileage may vary. Always best to check for your particular programmer.
Don't forget to switch this board to use +5V (not +3.3V). The In Circuit Serial Programming of the chip is covered in section 27.8 of the ATmega datasheet.
Tachometer noise filter:
Any noise on the tachometer line coming from the fan's RPM pin 3 can be an issue and give false readings. This noise could be extra switch bounce like pulses. Although tachometer circuits employing hall sensors or optocouplers are usually used rather than a mechanical switch.
Fans running at a low RPM which have these noise pulses present, produce a bigger disparity when assessing the real RPM value.
For example at a low speed, a fan may give about 8 pulses in 1 second. These 8 pulses equate to 4 revs/sec since fans send two pulses per revolution. So, 4 revs/sec is 240 revolutions per minute (4revs x 60secs). A pulse of noise due to bounce may give an extra 9th pulse which incorrectly equates to 270rpm (4.5revs x 60secs). An error of about 12% of the total real value.
At higher revolutions these extra noise pulses in the waveform have less of an effect on calculating the correct RPM value. For example, at a higher speed we may get 266 pulses which actually means 133 revs/sec or 7980 revolutions per minute (133revs x 60secs). An extra noise pulse here giving 267 pulses total would equate to 133.5 revs/sec or 8010 revolutions per minute (133.5revs/sec x 60secs) which is less of a discrepancy from the total value. An error of under 4% of the total real value. This is only an example as most fans run below this at their highest speed.
To mitigate the effect of any noise on the fan's RPM output, an integrator filter formed by R2 and C1 (shield circuit) or R7 and C14 (embedded version) is used. With some calculation and experimentation an adequate time constant was found. Values of 1K and 1nF gave a suitable time constant to filter out any higher frequency noise pulses present on the fan's tachometer signal.
AMS1117 LDO voltage regulator:
This already forms part of the Arduino's power subsystem. It provides a regulated +5V which is also used to power the fan controller shield. It's a tried and tested hardy regulator for its size. Manufactured by different companies and easily available. Different manufacturers issue different datasheets with slightly differing maximum input voltage and power dissipation criteria. I have used it on the stand-alone embedded board also.
THE FANSWe need four pin, fully controllable PWM fans. The other three pins carry power, ground and the RPM tachometer pulse waveform. I've settled on using Noctua fans in projects incorporating this fan controller. They are famously silent, but this is not the only trait which favours them. As not all fans are equal.
When you command a fan to 0% PWM it should stop spinning. Not all fans do this. A lot will continue to spin even at 0% PWM. To stop them spinning completely, extra circuitry methods would be needed.
Similarly at 1% PWM a fan should just start to spin. In fact it doesn't, as most need a minimum PWM to start spinning. The Noctua fans tested started spinning at 6% or 10% PWM depending on the fan model. Anything below and they stop completely. This is an initial dead zone where the fan is unresponsive and stays still.
Different fan models from different manufacturers behave differently. There is no standardisation which is adhered to. Fans from Noctua, Arctic, Xilence and Cooler Master all performed differently. Life is hard.
The fan controller will first try to measure the minimum PMW needed before Fan-A starts responding. The code uses variable fanStopLimit in function findFanStopLimit() to find this value at power ON or reboot. It uses this later in the detection of any abnormal stoppage of main Fan-A due to a jam or failure.
Of course other fan makes and models can be used as long as some caveats are considered. Some fans never stop spinning, even when commanded with a 0% PWM signal. Some will spin at 100RPM or higher but never come to a standstill. Thus their fanStopLimit value, detected at boot time, will always be 0%.
I like the fans to stop dead at near 0% PWM, so have stuck to Noctua. These are easily found and ordered in my part of the world. Other makes and models may exist and will work providing they are of the full four pin and +12V variety.
Fans come in a choice of sizes. Common diameters of 40mm, 80mm, 120mm and 140mm are available. Choose fans that will fit your task. If only one fan is required just plug it in as a primary Fan-A only.
THE CODEThis code was built up and added to with features and functions, over time and as I felt the need. I am not a programmer and there is no doubt some unneeded complexity in program flow akin to the labyrinth at Chartres. The splattering of global variables throughout the code must also add to the inefficiency. It is what it is. It serves my purposes and doesn't visibly slow down the operation any.
#define DEBUG 1 or 0 is used to enable or disable outputting debugging information on the serial port. Your choice will be set at compilation time. When enabled, a serial port debug OK message is the first thing shown. Then followed by the sketch filename with the date and time it was compiled at.
#include "FAN_fixed_bold10x15.h" to allow the use of my adaptation of the fixed_bold10x15.h font. This has special icon characters used by the fan controller to show modes and states. It should be in the same folder as the fan controller sketch.
Some specific libraries required to be installed with the Libraries Manager of the IDE are:
OneWire.h and DallasTemperature.h are used for communicating with the Dallas Semiconductor DS18B20 temperature sensor.
EEPROM.h facilitates access to the microcontroller's nonvolatile EEPROM memory space.
SSD1306Ascii.h and SSD1306AsciiWire.h to use the OLED display and access its I2C bus.
TimedAction.h is needed to be able to protothread different tasks in the code. Three threads will run in pseudo-unison. These are used to read the rotary encoder, to read the RPM speed of Fan-A and to read the temperature information from the sensor.
FUNCTIONS:
writeConfig() and readConfig() will read and write configuration data to the chip's EEPROM memory. EEPROM addresses can can be read an unlimited number of times but only written an average of about 100000 times. If you were to change the configuration 10 times every day that equates to about 27 years of write cycle lifetime. The function firstRunCheck() looks for the key1 and key2 values of 73 and 42. Two arbitrary numbers. A nod to radiohams everywhere and to The Hitchhiker’s Guide to the Galaxy. If not found the chip has never been configured before and the function firstLoadOfEeprom() will populate a new microcontroller chip with valid EEPROM values.
At power ON or reboot, function findFanStopLimit() runs. This finds the PWM value where primary Fan-A starts to spin. It returns this value as minLimit to variable fanStopLimit. This PWM percentage value is used later to detect a stoppage or malfunction of the primary Fan-A.
Functions oneDit() and oneDah() are used with oneRoger() to sound a Roger confirmation 'R'. They also generate the Fan-A malfunction alarm sound. The two functions generate a morse dot and dash sound from the buzzer. This I've done directly by bit manipulating the Port C register, where the passive buzzer is connected to PC0 and PC1. Driving each output out of phase we increase volume level because we effectively have a potential difference of 10V across the passive buzzer's piezo plates.
This is similar to how the excellent ToneAC library works. However, for this simple application, by coding this function directly I avoid using the library. We don't need its volume and frequency control options and escape from its Timer requirements.
Function encoderRead() is a protothread which fires every 50mS to read the rotary encoder.
Using function menuClicked() we detect the pressing of the rotary encoder momentary switch button.
Functions valueChanged() and delimitValue() are used to select each configuration parameter's individual value.
Function oledDisplay() is used to print to the SSD1306 OLED's two line display using the customised font.
Function showTempTrigIcon() displays a thermometer type icon when the temperature of TEMP MIN has been reached and triggered.
Function tachCounter() is a protothread which fires every 1 second to read the RPM tachometer waveform coming from main Fan-A.
Function getTemperature() is a protothread which fires every 3 seconds to query and read the temperature data coming from the DS18B20 sensor.
Function triggerCheck() checks for a temperature trigger and takes into account if we are using the P-input mode and have an enabling signal at the P-IN header.
Function fanBsetup() reads the relevant jumper placement to set secondary Fan-B's working mode of Asymmetric, Symmetric or Standby at switch ON.
Function fanBconversion() calculates what speed Fan-B should be running at compared to Fan-A, taking in to consideration the current operational mode of either Asymmetric, Symmetric or Standby.
Function percentToPWM() converts a percentual speed value to a value appropriate to be fed to the timer register directly for PWM control. This is used to command the speed of Fan-A.
Function percentToBars() displays the PWM commanded to Fan-A, as a moving bar graph on the upper line of the OLED display.
The Function fanJamCheck() checks for a stoppage of Fan-A and signals this after four violations. It then sounds an alarm and flashes the red warning LED.
Function steppedIncrement() and steppedDecrement() increase and decrease the speed of the fan when in the passive sweeping mode.
Function pwm25kHzSet() sets the PWM frequency to 25khz. This change is needed because fans require a PWM signal running at 25KHz and the default Arduino PWM frequency available is only either 490Hz or 980Hz, depending on the pins used.
The ATmega328P has three timers which we can juggle with. A timer is a register which increments (or decrements) for each clock pulse. The 328P has three and their use is juggled between functions like delay(), millis(), micros(), Servo(), Tone() and other libraries. The clock frequency is taken from the 16MHz crystal clock. This clock frequency can be divided by a prescaler before feeding to a timer. Timers will overflow when they have incremented to their maximum register size. For timer0 and timer2 which are 8 bit timers this is 255 (65535 for the 16 bit timer1). The value at which this overflow happens can also be set. We need not wait until the timer register reaches 255 (or 65535) for an overflow to be flagged. Control registers configure timer1 to mode 10, no prescale, count to 320. A value of 320 with our crystal clock of 16Mhz will give us the 25KHz PWM signal. The code converts this from a user friendly percentual fan PWM value using the function percentToPWM().
Interrupt service routine encoderISR() is used in reading the rotary encoder.
Interrupt service routine tachISR() is used to count the pulses eminating from the tachometer RPM waveform sent by main Fan-A.
EEPROM memoryDetails showing default values which are loaded on a first time power ON.
Address 0 key1 73d Dahdahdiddley...Diddleydahdah.
Address 1 key2 42d Life, the universe, and everything.
Address 2 fanMin 10d Fan speed minimum = 10%.
Address 3 fanMax 90d Fan speed maximum = 90%.
Address 4 tempMin(°C) 40d Temperature minimum 40degC.
Address 5 tempMax(°C) 70d Temperature maximum 70degC.
Address 6 tempMin(°F) 100d Temperature minimum 100degF.
Address 7 tempMax(°F) 160d Temperature maximum 160degF.
Address 8 Mode 1d Sweeping.
Address 9 Sweep Rate 5d 5 (Medium speed).
Address 10 Trigger 1d By temperature from 0% fan speed.
Address 11 tempOffset 0d 0 degrees of temperature correction.
Address 12 Save & Exit 0d 0.
The code's read and write functions will skip over addresses 4 and 5 or 6 and 7, depending on which units of temperature have been selected with the hardware jumper (U1 on shield, or J2 on stand-alone PCB).THE CUSTOM CHARACTERS
The fixed_bold10x15 font included in the SSD1306Ascii library is my preferred font. It's clear and easy to read. However it only gives us two lines of about 11 or 12 characters each to display any information on. I resorted to the use of custom icon characters to get more information onto the small 0.91" OLED display. I modified the original fixed_bold10x15 font included in the SSD1306Ascii library. This modified font is FAN_fixed_bold10x15. It's called by the code and should reside in the same sketch folder.
The extra characters will print specific icons used by the fan controller code to the OLED. They include characters that generate the fan speed bar graphic, OOR warning, centigrade and fahrenheit symbols, RPM fan spinning symbol, RPM fan jammed warning, trigger mode in use, sweep speed in use and P-input enable icons.
Scroll through some of the display information examples:
The bargraph displays the speed we are commanding to main Fan-A, to the nearest 10%. Two letters left of this PWM bargraph show our working mode. The bottom line shows temperature and the Fan-A tacho speed in RPM. Out of range OOR temperatures are signaled with an 'exclamation' icon. Any prolonged fan stoppage of Fan-A is also signaled with an 'exclamation' inside the RPM icon. When the fans are triggered on reaching the temperature minimum value, a 'thermometer' icon is displayed. In P-Input mode an 'arrow' icon indicates when an enable signal is present.
These were all designed with pen and paper and then the original font file reverse engineered and modified. Each new character's hexadecimal values were added by editing into the original file.
TWOBOARD VERSIONSAlthough running the same code and so essentially equal, the two board versions described below are quite different.
Piggyback shield version:This Arduino compatible shield which piggybacks on to an UNO R3 makes for a surprisingly compact unit. It is much easier to build and program than the embedded version. Also perfect if you want to easily modify my code and test for your specific needs. It only has fifteen surface mount components which can be soldered just with a hot air gun or hot-bed and some low temperature solder, if you are quite careful.
Primary components:
- Arduino UNO R3 (already has elements like the ATmega 328P microcontroller, clock oscillator, power input and voltage management, reset system, USB–Serial connection, ICSP connection).
- DS18B20 temperature sensor.
- Optocoupler PC817C.
- OLED SSD1306 0, 91" display.
- LED indicators and passive buzzer alarm
- Rotary encoder for menu input.
- Onboard reset button.
- Initial setup jumper headers.
Shield Version Circuit Theory:
The shield provides a neat attachment to the top side of an Uno R3 board. It provides an easy way of connecting the fan controller's specific circuitry to the microcontroller and subsystems already present on the Uno board. Refer to the shield version schematic diagram.
Fan-A and Fan-B have independently controlled PWM output signals which are taken from pins D9 and D10. An RPM tacho waveform is taken from Fan-A, through an RC noise filter and fed to pin D2. This is used to calculate and display the spin speed of the main Fan-A.
A high level on pin D11 will light the red LED4 through its 1K current limiting resistor R4. The code does this when it determines that the main Fan-A has jammed, to signal an anomaly. Similarly it will sound an alarm through the passive buzzer SG3 which straddles A0 and A1. Each output is out of phase to one another thus increasing the apparent volume level. Nevertheless I have a small 220 Ohm resistor R1 to reduce the current here to an acceptable level. This piezo buzzer also sounds at switch ON and to confirm configuration settings have been saved.
The SSD1306 OLED uses an I2C bus. Its Data and Clock lines are connected to pins A4/SDA and A5/SCL of the Uno. Two pullup resistors R5 and R6 of value 5.1K may or may not be needed. Some OLED boards will function without as they already have these built-in.
The rotary encoder SW1 provides a way of scrolling through and setting our required configurations. Lines DT and CLK are read by pin inputs D3 and D4. The momentary switch is taken to ground when pushed. This low is read at pin D6 by the microcontroller for cycling through the menu items.
The output of the PC817C optocoupler U3, on its pin 4, is taken to input D8. It is used to optically isolate the enabling signal from any auxiliary equipment we may choose to connect to header P-IN. This will be low if the optocoupler output side is illuminated by the internal LED at its input. Resistor R3 limits current through the input level to within specification. A value of 1K is suitable for the most common voltages we expect to have in a ham shack. A 1N4148 diode protects the same input diode from inadvertent reverse polarity connections. Without this protection diode the optocoupler's LED would not survive a reverse voltage of more than 6V.
Be aware: The P-IN header connection is different to the embedded version. The external equipment's enable voltage should connect to the header's pin 2. With it's negative line, usually the auxiliary equipment's Ground, going to pin 1 of the P-IN header. This is marked with a '1 G' on the silkscreen.
P5 is where we connect the DS18B20 temperature sensor making sure we respect its pinouts. Its data line DQ is taken to pin D7 which is used as a single line bus to communicate with the sensor. The white LED2 will flash as we interrogate and receive temperature data from the sensor. I found a pullup resistor is needed to ensure good bus dialog.
A value of 2K gave me stable data flow together with adequate illumination of the 0805 surface mount LED which I used. Higher values like 4.7K can be used but it ultimately depends on the voltage dropped across the LED you choose to employ. This can vary between manufacturers, models and colours. Your mileage may vary.
Header U1 is read during switch ON at pin D5. If pins 1 and 2 of this header are bridged by a jumper, D5 is low and the code will use degrees Fahrenheit. If pins 2 and 3 are bridged there is nothing in circuit actually. D5's internal pullup will keep it high and centigrade units will be used.
With header U2 we can choose the mode of working between the two fans. The outer pins are routed to the Uno pins A2 and A3. Reading the state of these inputs will define how the secondary Fan-B will work with Fan-A. Either symmetrically (tandem) or asymmetrically (opposite). The jumper will take one or the other pin to the central ground pin. If no jumper is installed, both sides will be high and a logic 1 will be present on both input pins A2 and A3. Then the Fan-B will wait in hot-standby mode in case of a Fan-A failure.
I included a small tactile switch SW2 on the shield. This connects to the RESET pin of the Uno. Effectively it is in parallel with the Uno's own reset switch which however, is now covered by the shield and practically inaccessible. Like this, a reset is still possible to perform easily.
To power the fans we need +12V. This voltage comes in at the barrel connector, before voltage regulation to +5V by an AMS1117 for powering other UNO circuitry. For the fans we must always power the UNO with around +12V at this barrel connector. The fans' DC input is taken from Vin, which is a pin connection recovered after the input voltage has passed a reverse voltage protection diode marked M7 on the Arduino. This is a standard 1N4007 rectifier diode which will drop the 12V down by about 0.7V. The UNO's AMS7111 +5v LDO regulator is rated at an optimistic 800mA. Ideally we need to be below this value as we are already at the top end of his recommended input voltage (although maximum input is higher). So, the little linear regulator will need to dump about 6V from the main 12V used to power the fans. As an example total current drawn with two fans both turning at 100% was measured at about 270mA. About 100mA for each fan (which agrees with the specified current consumption as specified by the fan manufacturer) and 70mA for the Arduino board. We can see we are well away from the 800mA quoted and so nicely within the AMS1117 power dissipation specs.
Although I have often seen a maximum of +20V quoted, Arduino recommend no more than +12V input to power its UNO board. It all depends on how much current and thus power the on board LDO regulator will have to handle. Using +12V at the barrel connector input and after the diode's voltage drop, we still have about 11.3V which is acceptable to drive fans. This can be checked on fan datasheets. Typically 12V fans are tolerant from 10V to 13.2V (some even 7v to 13.8V). Using 13.8V, which is commonly found in any shack power supply, we have 13.1V. Still within fan specifications. This slight increase doesn't put much more of a dissipation burden on the AMS1117 +5V voltage regulator as we are not running high currents through it.
Green LEDs 1 and 3 synoptically reassure us that we have +5V and +12V present on our board. LED1 uses a 1K current limiting resistor R7 to indicate +5V and LED3 a 2K resistor R8 to indicate +12V. The resistor value discrepancy is on account of the different voltages being indicated. Like this the level of brightness will be very similar.
Shield Version Building:
The board uses a mix of through hole and surface mount components. At a pinch, everything can be soldered by hand if you have good eyesight, a calm demeanor and a steady hand. However ideally a reflow oven, hotplate or a hot air station is best. I prefer to use 0805 package sizes as they are small but still relatively easy to see and work with. The fifteen SMDs R1 to R9, LED1 to LED4, C1 and D1 can be reflowed first. Then the through hole components can be hand solder. No need to configure microcontroller fuses or upload bootloaders as this has been taken care of by the UNO already. The finished stack of these two PCBs provide for a surprisingly compact unit with all the amenities already provided by a classic Arduino.
A headsup: Not all Arduino UNO R3 are equal. Some use USB type-B connectors for the serial communications to USB. USB type-B socket height may touch and short some shield connections. Watch out. A bit of insulation tape might be useful here. Also sometimes an ATmega 16U2 is used as a USB to Serial converter. This is in fact a separate microcontroller which has been programmed in an FTDI role. It often has its own ICSP programming pins at the top left of the Arduino UNO board. These can also physically touch the underside of some shield component pins and cause havoc. I can only assume that some Chinese manufacturers burn the ATmega 16U2 with the FTDI firmware only after the chip is already on the board using these programming pins. Other suppliers avoid mounting these pins by pre-burning before soldering the chip, or use pogo pin connectors later on in fabrication. Personally, when using shields and to avoid these issues, I tend to only use UNO boards that have the much lower profile micro-USB connector and a dedicated USB-UART chip like a CH340.EMBEDDED BOARD VERSION
Stand-alone embedded version:This is on a single board using an embedded ATmega328P-AU with all other subsystems included. Serial and ICSP port connection headers are available to still allow for debugging and for the necessary chip burning. Naturally here there are more surface mount components to solder. This includes of course the 328P-AU. Its 0.8mm pitch TQFP package will be more of a challenge. As will configuring and loading in the initial sketch and/or bootloader.
Primary components:
- ATmega 328P-AU
- Crystal clock oscillator.
- DS18B20 temperature sensor.
- Optocoupler PC817C.
- OLED SSD1306 0, 91" display.
- AS1117 5V voltage regulator.
- LED indicators and passive buzzer alarm
- Rotary encoder for menu input.
- On board power switch.
- On board reset button.
- USB-Serial function is available with an external FTDI (Future Technology Devices International) board, thorough the ‘Serial’ port header pins.
- ICSP fuction is available with an external USBasp board, through the ‘ICSP’ port header pins.
- Initial setup jumper headers.
Embedded Version Circuit Theory:
This version provides a single board platform for the fan controller code. It is a stand-alone variant of the fan controller and built around an ATmega 328P-AU chip. The same as used by many Arduino UNO R3 boards. Refer to embedded board schematic diagram.
The circuit uses the ATmega 328P. Not the 328PB version which has some pinout differences. The suffix AU refers to the small QFP32 package type which I found to still be relatively easy to handle during testing and building.
Since we have lost the comfort of the built-in Arduino bootloader, we will have to use ICSP to upload to the microcontroller's memory. Other functionalities like power management, clock, reset and a serial debug port, ICSP port need to be added also.
Both the shield and embedded circuits work with the same code. This is because all circuit functions and connections have been designed to be compatible with one another. Below I have stated what the equivalent Arduino Uno connection is for each input or output as this is what is referenced to in the sketch.
Fan-A and Fan-B have independently controlled PWM output signals which are taken from pins 13 and 14 of the microcontroller. These are port pins PB1 and PB2 and equivalent to I/O pins D9 and D10 on an Arduino board. The RPM tachometer waveform is taken from Fan-A, through the RC noise filter and fed to pin 32, which is port pin PD2 (Arduino D2). This is used to calculate and display the speed of the main Fan-A.
When the code detects a stopped Fan-A, a high level on pin 15, port pin PB3 (Arduino D11), will light the red LED4 through its 1K current limiting resistor R4. Similarly, it will sound an alarm through the passive buzzer SG3 which straddles pins 23 and 24, port pins PC0 and PC1 (Arduino A0 and A1). Each output is out of phase to one another thus increasing the apparent volume level. Nevertheless there is a small 220 Ohm resistor R1 to reduce the current here to an acceptable level. This piezo buzzer also sounds at switch ON and to confirm configuration settings have been saved.
The SSD1306 OLED uses an I2C bus. Its Data and Clock lines are connected to pins 27 and 28. These are port pins PC4/SDA and PC5/SCL respectively (which are on A4/SDA and A5/SCL of an Uno). Two pullup resistors R5 and R6 of value 5.1K are included for completeness, although some OLED boards will function without these as they already have pullups. My OLED has an +3.3V regulator already and is +5V logic tolerant, so a level converter is not needed.
The rotary encoder SW1 lines DT and CLK are read at the microcontroller's pins 1 and 2. These are port pins PD3 and PD4 (same as Uno's D3 and D4). The momentary switch is taken to ground when pushed. This low is read at pin 10, which is port pin PD6 on the microcontroller (D6 of an Uno), to cycle through the menu items.
The output of PC817C optocoupler U3, on its pin 4, is taken to microcontroller pin 12 which is port pin PB0 (D8 on an Arduino). It is used to optically isolate the enabling signal from any auxiliary equipment we may choose to connect to header P-IN. This will be low if the optocoupler output side is illuminated by the internal LED at its input. Resistor R3 limits current through the input level to within specification and a 1K value is suitable for the most common voltages we expect to have in a ham shack. A 1N4148 diode protects the same input diode from inadvertent reverse polarity connections. Without this protection diode the optocoupler's LED would not survive a reverse voltage of more than 6V being applied.
Be awre: This P-IN header is different to the shield version. Care must be taken when hooking up an external trigger. The external line's negative, ussually Ground, should go to pin 2 of the P-IN header. The external line's positive should go to pin 1. This is marked on the header's silkscreen as '+VE 1'.
P5 is where we connect the DS18B20 temperature sensor making sure we follow its pinouts. Its data line DQ is taken to pin 11, or port pin PD7 (D7 on an Uno). This is a single line bus which communicates with the sensor. The white LED2 will flash as we interrogate and receive temperature data from the sensor. A pullup resistor is needed to ensure good bus communication.
I found that a value of 2K gives stable data flow together with adequate illumination of the 0805 surface mount LED which I used. Higher values like 4.7K can be used but it ultimately depends on the voltage dropped across the LED being used. This can vary between manufacturers, models and colours. Your mileage may vary.
With header J1 we can choose the mode of working between the two fans. This happens at switch ON. The pins 1 and 3 are routed to the ATmega's pins 26 and 25 respectively. These are microcontroller port pins PC3 and PC2 (equivalent to Uno pins A3 and A2). Reading the state of these inputs will define how the secondary Fan-B will work with Fan-A. Either symmetrically (tandem) or asymmetrically (opposite) to main Fan-A. The jumper placement will take one or the other pin to the ground on pin 2. If the jumper is across pins 3 and 4 it is effectively out of circuit. A logic 1 will be present on both input pins A2 and A3 due to their internal pullups having been configured. With the jumper on the left, Asymmetric operation is set up. With the jumper in the middle, Symmetric operation is set up. With the jumper on the right, Hot Standby is set up.
Although we don't in fact need it to bridge pins 3 and 4 here, it is just a convenient way to store the little jumper and keep us from loosing it.
Header J2 is also read during switch ON and routed to pin 9. This is port pin PD5 (Uno D5). If pins 1 and 2 of this header are bridged by a jumper, pin 9 is taken low and the code will subsequently calculate temperature in degrees Fahrenheit. If pins 2 and 3 are bridged there is nothing in circuit actually. Again we are just storing the jumper on pins 2 and 3 to avoid it being lost. Here, pin 9's internal pullup will keep it high and the code will work in Centigrade temperature units.
Switch SW18 connects to the unit's reset circuitry, on pin 29 of the ATmega 328P. Taken low it forces the microcontroller to reset. In normal operation a pullup resistor R2 keeps it high. Diode D2 protects the microcontroller from any voltage spikes which will occur due to capacitor C7 on the DTR line of our serial port. If this DTR goes high we have a brief doubling of voltage on our reset line. The 1N4148 shunts this 10V safely. Capacitor C7 is needed on the DTR line of the serial port and acts as a differentiator. When connecting to the serial port, most USB to serial chips will set the DTR to low and keep it low. We only need a short pulse here to initially restart the microcontroller. The ATmega's reset pin is also used by any ICSP programmer and so there is also a connection to ICSP header pin 5.
Green LEDs 5 and 6 synoptically indicates that we have +12V and +5V reaching our board. LED5 uses a 2K current limiting resistor R10 to indicate +12V and LED6 a 1K resistor R11 to indicate +5V. The resistor value difference is on account of the different voltages being indicated. Like this their level of brightness will be equal.
After the DC barrel connector and power ON switch SW2, an AMS1117 +5V voltage regulator, U36, provides +5V to the board. Versions of this regulator have been quoted as having maximum input voltages anywhere from 15v to 20V. Although the recommended value is lower. Power dissipation and heating will depend on how much current it will be passing. It is kept within power dissipation specifications as the total current through it is relatively low. Despite this I've included an ad-hoc PCB heatsink as I had the board space. The regulator's +5V tab connects to this and it has thermal vias to the bottom PCB layer too.
The 1N4007 diode U1 across the AS1117, is for protection when using the ICSP port. Normally, ICSP programmers will place +5V on pin 2 of the ICSP port. This protects the AMS1117 LDO regulator against backfeeding voltage to it when we are burning the ATmega chip.
To power the fans we need +12V. This voltage comes in at the barrel connector, before voltage regulation to +5V by an AMS1117. Fan voltage is taken after the input voltage has passed a reverse voltage protection diode marked M7. This is a standard 1N4007 rectifier diode which will also drop the 12V down by about 0.7V.
The AMS7111 +5v LDO regulator is rated at an optimistic 800mA. Ideally we need to be below this value as we are already at the top end of his recommended input voltage (although maximum input is much higher). The little linear regulator will need to dump about 6V from the main 12V used to power the fans. However, total current consumption is well below this 800mA maximum figure. So, even with a need to discard 6V, with such a low current passing through it we are nicely below the AMS1117 power dissipation specifications.
As just mentioned the +12V needed to power the fans is taken from the cathode of U37. This is the 1N4007 diode which gives us some simple reverse power protection but will drop about 0.7V. Even with this drop, there is still enough voltage to drive most fans within their specified voltage tolerance range. This can be checked on the fan's datasheet. Typically 12V fans are tolerant from 10V to 13.2V (some even 7v to 13.8V).
Using +12V at the barrel connector input, after the diode's voltage drop we still have about 11.3V which is acceptable. Using 13.8V, which is the normal output of any shack power supply, we have 13.1V. Still within most fan specifications but you may notice an increase in RPM. This small input voltage increase doesn't put much more of a burden on the AMS1117 +5V voltage regulator.
A 16MHz external crystal oscillator, Q6 is used. This option will have to be configured in the ATmega's fuse settings. The 1M resistor R13 across the crystal Q6 improves the starting and sustaining of oscillation for a wide variety of crystal specifications. Some microcontrollers have this built in. It's included for completeness.
Many Arduino boards use a ceramic resonator instead of a crystal. Although not as accurate as a crystal it is smaller and seems to be accurate enough for everyday applications. I chose a crystal because 16MHz crystals are easier to find in my part of the world, as well as being easier to handle than the minuscule ceramic resonators. I'm unsure if the internal ATmega's oscillator is accurate enough for our needs, especially for the serial communication functions.
The serial port header H7 can be used for debugging. It can also be used for uploading new sketches providing a bootloader has been previously burnt to the microcontroller. An FT232RL FTDI USB to TTL serial adapter module, set to work at +5V, is needed. The header pins will line up with most of the adapter modules available. The ground side is on pin 1 which is marked on the serial port silkscreen. Two 1K resistors R8 and R12 on the Tx and Rx lines limit the current. So, if the adapter side goes high and then the microcontroller code sends that corresponding output pin low this will avoid a straight short to ground. The Vcc pin 3 of the serial header is unconnected. The fan controller is powered by its own voltage regulator and the adapter board is powered through the computer's USB connection. Both have a common ground. The 100nF capacitor C7 has already been discussed. It forms part of the reset circuit and ensures that just a brief low pulse from the serial port's DTR pin triggers a reset.
To use the ICSP port J5, an In Circuit Serial Programmer will be needed for burning directly to the microcontroller's memory. With a fresh ATmega 328P-AU this will be the only way of uploading the first code to the microcontroller. If you burn a bootloader you will then be able to use just the serial port and the Arduino IDE to upload sketches in the classic fashion. The ICSP header pinout follows the standard layout, as on Arduino Uno R3 boards. For ICSP programming I use a USBasp programmer which is cheap and simple to use. Other programmers can be used of course providing the pinouts are matched.
When programming, the fan controller board should be powered OFF as it will take it's power from the ICSP programmer. However some older ICSP programmers need to have a voltage at ICSP pin 2 from their target to function. In this case the unit should be powered ON.
The ICSP's MISO and MOSI line are at pins 1 and 4 of the header and run to the microcontrollers MISO and MOSI pins 16 and 15. The clock line SCK at header pin 3 goes to the chip's SCK pin at 17 and the reset line RST at header pin 5 joins the reset circuitry going to Reset pin at 29 of the chip. The red LED4 discussed previously, which is used to signal a stoppage of the main Fan-A during normal operation, will now serve to show us any ICSP activity. Port pin PB3 is also the MOSI pin, so LED4 will flash as we are buning to the chip.
Embedded Version Building:
Again, the board uses a mix of through hole and surface mount components. The 0805 SMD package is preferred as this small size is still handled with relative ease. It might be judicial to practice SMD soldering techniques on a simpler project or even a practice board if it's your first time. The embedded version is not a good first SMD project. There will already be enough traps to watch out for along the route to having a working embedded version. This may cause much frustration to the newcomer. The surface mount or embedded microcontroller novice would be wise to start with the shield version. If you feel confident I will now go through a logical work flow which might avoid homebrewing disgruntlement.
- For the SMDs, I use a small reflow oven and some low temperature leaded solder paste. It's easy to control the heat by using a specific temperature curve matched to the solder being used. You can use a hot plate and a hot air rework station too of course, but attention to melting any plastic housed components by overheating. Be watchful around electrolytic capacitors for example, whose dielectric can be damaged. For solder, the unleaded type is all the hype now but trust me, it's not as forgiving.
Lead-free solder has a tendency to bead up and not flow easily or stick. A big PCB factory is justified in using environmentally friendly solder. For our small home hobby use it's really an environmental drop in the ocean. Health wise the fumes from the flux in solder core is actually more harmful and this is present in both leaded and unleaded solder. In any case, soldering a few dozen boards a year will not push you into an early coffin as long as your workbench is sufficiently ventilated.
- I would suggest reflowing all the SMD components first and then testing and ICSP burning the Atmega 328P-AU straight away. That is, before mounting the larger through hole components. Like this it will be easier to diagnose any programming problem straight away. Without the clutter of a full board if you need to desolder and resolder components. My first completed board had a couple of issues and required me to remove and replace components to track the problems down. Although I resolved these issues and the board worked, it did look a bit like a dogs dinner at the end and not the Michelin star meal we strive to serve up for each of our finished projects.
To start with, reflow all the SMDs: U1, U36, U37, U25, Q6, SW18, D1, D2, LED2, LED4, LED5, LED6, C1, C2, C3, C4, C5, C6, C7, C8, C9, C12, C13, C14, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13. Once this is done, check visually for good solder joints and no solder bridges. Use magnifying glasses or a digital microscope. Invariably there are often bridges across some of the ATmega 328P-AU pins which should be wicked up using some desoldering braid. Once we are happy, we can pass on to the next stage of programming the microcontroller through the ICSP header.
Check there is a connection to your ATmega 328P. If all is well, at least your MISO, MOSI, RST, SCK and power pins of your ICSP to microcontroller are good.
Check the current state of the Fuses and Lock Bits. The factory default of these Fuses are usually never what we want. Here they will need to be set to the requirements for this project. Reprogramming them with care, as a wrong setting could brick the microcontroller and render it difficult to ever normalise again easily.
Configure the ATmega to use the external crystal oscillator at 16MHz, reserve bootloader space, brownout detection, startup delay, boot reset pin, etc. Refer to the bibliography section for the ATmega's datasheet and other resources for configuring the four sets of fuses.
Low Fuses: For CCKDIV8, CKOUT, both SUT and the four CKSEL bits.
High Fuses: For RSTDISBL, DWEN, SPIEN, WDTON, EESAVE and BOOTSZ with BOOTRST.
Extended Fuses: For BOD level.
Lock Bits: To restrict access to the program memory.
I set the fuses for this project using something similar to the following values:
L F7h
H DEh
E FDh
LB FFh
Because the factory defaults on new chips are usually:
L 62h
H D9h
E FFh
LB FFh
(Which amongst other things has the microcontroller fuses set to use its internal RC oscillator, divide clock by 8, brownout detection disabled etc.)
Warning: The RSTDISBL, SPIEN, DWEN fuses and the Lock Bits have the potential to brick the ATmega chip, or at least make the chip very difficult to use again. So, always double check your fuse setting values before clicking the 'Write' button.
- Once communication has been established to the microcontroller and its fuses set we are ready to load in a sketch. Again there are a variety of ways of doing this, although I find it practical to use the Arduino IDE here with the same USBasp ICSP programmer and the excellent MiniCore Arduino core. Once configured in the IDE's board manager it allows programming of many ATmega microcontrollers (including the ATmega 328PB version which is sometimes being used on Arduino boards now). This core uses a custom version of Optiboot, which is a space saving and more efficient alternative to the regular bootloader.
After setting MiniCore up in the boards manager of the IDE we can choose our ATmega 328P in 'Tools' and check its options. The programmer should be set to USBasp(MiniCore) of course. I can choose to upload the bootloader to memory and have the USB port active for later sketch updates via the serial USB-FTDI port. This incurs a 2 second delay at switch ON. During which time the bootloader is called and waits for any sketch uploading. This serial port is also used for debugging at 115200 bauds. We can also choose to not upload a bootloader and do away with serial uploading of sketches. There will be no 2 second delay as the code will start running instantly. Any sketch uploads will only be able to be done using ICSP.
- Initially try uploading a Blink sketch. Enabling Verbose mode in the Arduino IDE Preferences section is a good strategy. Like this, detailed information of what is happening during compilation and uploading of code can be seen. A Blink sketch is small, simple and uploads quickly. The LED on Port B3 of the microcontroller can be used to see if the Blink sketch is working with the board. This should be defined as data pin 11 in your Blink code. It is the red Fan-A warning LED which is on pin 15 of the 328P microcontroller. Incidentally, this LED is also connected to the MOSI line of the ICSP port and so shows us when In Circuit Serial Programming activity occurs.
- Now that the microcontroller side of the project has been configured and tested, the PCB can be completed by hand soldering the remaining through hole components.
- Finally the fan controller sketch can be uploaded to the board and all its functionalities tested.
I am using the fan controller in the following examples. I adapt the fan speed temperature slope for each applications. It ensures adequate cooling with acceptable noise levels.
Antenna Dummy Load
With the advent of cheaply available 50 Ohm ceramic resistors I found some time to build a high power dummy load. It is perfect for testing HF transceivers. It uses a 250N50F Aluminum Nitride (AlN) ceramic resistor. Alternatives include Beryllium Oxide (BeO) resistors. These are all previously used components. Rescued by some enterprising traders in China from scrapped equipment.
WARNING: Datasheet specifications vary somewhat but they can dissipate much wattage with correct heatsinking and cooling. Some supposedly up to 250W or 100degC, whichever comes first. These ratings may seem overly generous.
You should manage the 'Power Time : Rest Time' ratio which you apply to the load. More power during more time means higher temperature and so will require more cooling time.
A large heatsink coupled with a smart cooling solution in the form of these twin fans is ideal. The fan controller board is mounted on one end of this package. The ceramic resistor bolts onto the heatsink and this itself is sandwiched between the two cooling fans. The DS18B20 temperature sensor is clipped to be in direct contact with the heatsink. You can use some zinc oxide thermal paste for good measure. I used nappy cream which is basically the same stuff and what I had at hand.
Industial PC
I hate noise. I use a Mac Mini with macOS and an industrial PC with Windows. Neither make any noise and are completly silent. However it did become quickly apparent that the fanless industrial PC running Windows could heat up quite a lot. Despite being effectively housed in a large heatsink box, the lack of airflow means its temperature does increase substancially when performing certain tasks. This is never good.
I used the fan controller and two fans to promote airflow just enough above the PC. Configuring a temperature/fan speed curve which adequatly lowered its temperature yet still gave no distracting fan noise. The temperature sensor mounts in direct contact with the case of the PC.
Linear Power Supply Unit
My Microset PT-135 35A power supply is large, robust and fanless. It uses linear regulation and is over engineered. It provides a wonderful silence both in RF and audibly in the shack. It has a large heatsink on the rear for heat dissipation. I decided to add some active cooling. Not that it needed it. Just a little aid to help airflow.
Here a shield version is used. Configuring a low fan speed curve keeps any fan noise to undetectable levels. It clips snuggly on the back of the heatsink fins. Another solution could be with the use of small magnets to limpet onto the case. The temperature sensor is air mounted above the board and only picks up the ambient temperature. It only activates the fans on hotter days.
THE CAVEATSNot compliant. Not commercial. For industrial applications, the Arduino hardware and code in general lack the protection circuits often required to protect the controller and associated equipment. Commercial and industrial systems will take into account many potential safety and environmental considerations. They will have many safety features. Arduino is really just a prototyping platform and should not be considered compliant or commercial. Although, many initial projects have become industrially endorsed after many tests and redesigns.
THE BIBLIOGRAPHYATmega 328P Datasheet:
https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
Fan pinouts:
https://noctua.at/en/what-pin-configuration-do-noctua-products-use
Arduino board comparison:
https://core-electronics.com.au/tutorials/compare-arduino-boards.html
RC time constant calculator:
http://referencedesigner.com/rfcal/cal_05.php
PWM specifications:
https://noctua.at/pub/media/wysiwyg/Noctua_PWM_specifications_white_paper.pdf
ATmega 328P timers:
https://www.locoduino.org/spip.php?article84
AMS1117 LDO voltage regulator:
https://file2.dzsc.com/product/21/03/19/1165897_103236172.pdf
MiniCore:
https://github.com/MCUdude/MiniCore
Piezo 10V (2 pin out of phase):
http://www.aurelienr.com/electronique/piezo/applic.pdf
DS18B20 datasheet:
https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf
ATmega 328P fuses:
http://www.martyncurrey.com/arduino-atmega-328p-fuse-settings/
AVR fuse calculator:
http://eleccelerator.com/fusecalc/fusecalc.php?chip=atmega328p
PC817C datasheet:
https://www.alldatasheet.fr/datasheet-pdf/pdf/43376/SHARP/PC817C.html
SSD1306Ascii:
https://github.com/greiman/SSD1306Ascii
TimedAction:
https://playground.arduino.cc/Code/TimedAction/
Comments