Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. My Project
  3. Radiator booster (heating)

Radiator booster (heating)

Scheduled Pinned Locked Moved My Project
7 Posts 4 Posters 4.7k Views 7 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • AWIA Offline
    AWIA Offline
    AWI
    Hero Member
    wrote on last edited by AWI
    #1

    Winter is cold and energy consumption going up. Time to get out of my cave and start a new project ;-).
    Low temperature heating systems use special radiators which can be benficial also for other heating systems.
    With a few PC vents I built a radiator booster to increase yield of my existing radiators. Still in research stage but results are promising. There are a few commercial options but these are either sub-optimal, expensive and not MySensorized:-1:
    0_1486224457799_upload-32148448-19bf-4fa3-bc2b-9e5390a6cb29 0_1486224551817_upload-e0df9470-6973-4978-910b-e172fce78276 0_1486224672608_upload-e6901de8-736a-43dd-bf72-c228abbdd8b5

    The workbench example built around a compact MySensors node jModule three Dallas temp sensors and PWM circuitry. The whole thing lives in or under the radiator.
    0_1486223384333_upload-15ca957a-7c92-457e-b732-46a1ec6671a1

    0_1486223717517_upload-c5ba7173-eb26-459b-99b9-978284cad0bc

    Working principle:

    • Measure temperatures for water in/ out and environment.
    • Vents start spinning slowly as the "In" temperature reaches a threshold (30 Celcius for now)
    • Vent spinning increase in time for a few minutes and then slowly spin down until constant level.
    • Vents stop spinning when temperature falls below threshold.
    • etc.

    Extra options:

    • Manual mode, started automatically when (MySensors) controller takes over.
    • Fan speed control for tuning

    I am curious if anybody has some hands-on experience on how to best tune the system. And will publish details (sketch) if anyone is interested.

    0_1486224230174_upload-d2dbb63e-1cfc-4a7a-8e14-5c10b6e057090_1486224762708_upload-40a2fd75-5190-4c40-8b63-42160b059d8d

    Andrej_ABA B 2 Replies Last reply
    5
    • AWIA AWI

      Winter is cold and energy consumption going up. Time to get out of my cave and start a new project ;-).
      Low temperature heating systems use special radiators which can be benficial also for other heating systems.
      With a few PC vents I built a radiator booster to increase yield of my existing radiators. Still in research stage but results are promising. There are a few commercial options but these are either sub-optimal, expensive and not MySensorized:-1:
      0_1486224457799_upload-32148448-19bf-4fa3-bc2b-9e5390a6cb29 0_1486224551817_upload-e0df9470-6973-4978-910b-e172fce78276 0_1486224672608_upload-e6901de8-736a-43dd-bf72-c228abbdd8b5

      The workbench example built around a compact MySensors node jModule three Dallas temp sensors and PWM circuitry. The whole thing lives in or under the radiator.
      0_1486223384333_upload-15ca957a-7c92-457e-b732-46a1ec6671a1

      0_1486223717517_upload-c5ba7173-eb26-459b-99b9-978284cad0bc

      Working principle:

      • Measure temperatures for water in/ out and environment.
      • Vents start spinning slowly as the "In" temperature reaches a threshold (30 Celcius for now)
      • Vent spinning increase in time for a few minutes and then slowly spin down until constant level.
      • Vents stop spinning when temperature falls below threshold.
      • etc.

      Extra options:

      • Manual mode, started automatically when (MySensors) controller takes over.
      • Fan speed control for tuning

      I am curious if anybody has some hands-on experience on how to best tune the system. And will publish details (sketch) if anyone is interested.

      0_1486224230174_upload-d2dbb63e-1cfc-4a7a-8e14-5c10b6e057090_1486224762708_upload-40a2fd75-5190-4c40-8b63-42160b059d8d

      Andrej_ABA Offline
      Andrej_ABA Offline
      Andrej_AB
      wrote on last edited by
      #2

      @AWI Hi, Very interesting project, please if you can publish sketch?
      Thanks.

      AWIA 1 Reply Last reply
      0
      • Andrej_ABA Andrej_AB

        @AWI Hi, Very interesting project, please if you can publish sketch?
        Thanks.

        AWIA Offline
        AWIA Offline
        AWI
        Hero Member
        wrote on last edited by AWI
        #3

        @Andrej_AB I think this sketch is doing the job. There are some things to take into account:

        • You can comment out the line #define SPEED_CONTROL_PIN A0 // connect to potentiometer for speed control if you don't want to have manual speed control.
        • If you use a PMW fan (4 wire) you need to adjust the PWM frequency to something near 25kHz. ~32 kHz works for me (see sketch) but some fan's are more sensitive.
        • You can control a non-PWM fan (2 or 3 three wire) with an external MOSFET. In this case you need to lower the PWM frequency to keep a brushless motor in control. ~31 Hz (see sketch) worked for me with the circuit below (sorry for the quality of the drawing ;-). MOSFET is IRLZ44 or almost any other.
          0_1486326818435_upload-4c35d27c-2091-4252-aeed-c8098914afc4
        • Be aware that I changed the transmission channel / set a fixed node/ set a fixed parent for my own network
        /*
         PROJECT: MySensors / Ventilator control
         PROGRAMMER: AWI
         DATE: february 2, 2017/ last update: february 5, 2017
         FILE: AWI_Radiator_Booster.ino
         LICENSE: Public domain
        
         Hardware: MySensors 2.0, MOSFET dimmer, Dallas temperature
        		
         Special:
        	
         SUMMARY:
        
        	Full independent with manual mode
        	Controls ventilator speed with PWM
        	Measure temperature and change speed accordingly
        	
         Remarks:
        	Fixed node-id
        	
         Change log:
         20170202 - Created from Ventilator control
         20170205 - Clean code and change manual mode 
        */
        
        // Enable debug prints to serial monitor
        #define MY_DEBUG 
        #define MY_PARENT_NODE_ID 0
        #define MY_NODE_ID 91											// fixed node number
        #define NODE_TXT "Radiator Boost 91"							// Text to add to sensor name
        #define MY_BAUD_RATE 19200
        // Enable and select radio type attached
        #define MY_RADIO_NRF24
        #define MY_RF24_CHANNEL 83										// 83, radio channel, default = 76
        
        #undef MY_REGISTRATION_FEATURE									// sketch moves on if no registration
        
        #include <SPI.h>
        #include <MySensors.h> 
        #include <OneWire.h>
        #include <DallasTemperature.h>
        
        #define ONE_WIRE_BUS 4 											// Pin where dallas sensor is connected 
        #define MAX_ATTACHED_DS18B20 3
        #define TEMPERATURE_PRECISION TEMP_12_BIT
        #define IN_THERM 0 												// indexes of in thermometer, adapt to you situation
        #define OUT_THERM 2
        #define ENV_THERM 1
        
        #define VENT_PIN1 3      										// Arduino PWM pin attached to MOSFET Gate pin -or- vent PWM pin (adjust PWM-Frequency)
        #define VENT_MIN_LEVEL 20										// lowest pwm level for vent
        #define VENT_MAX_LEVEL 255										// highest pwm level for vent
        
        // #define SPEED_CONTROL_PIN A0									// connect to potentiometer for speed control, if commented out no speed control
        
        #define IN_TEMP_CHILD	0										// measure incoming water temp
        #define OUT_TEMP_CHILD	1										// measure outgoing water temp
        #define ENV_TEMP_CHILD	3										// environment
        #define VENT_CHILD	2											// ventilator
        
        // Helper for Debug:  1 = Serial debug output ; 2 = V_TEXT remote output ; else no debug
        // Use Formats described in fprint() : http://www.cplusplus.com/reference/cstdio/printf/  
        // Example: 	Printf("Temp %2d Hum %2d\n", temperature, humidity);
        // warning: max print size < 24 ; float = NOT supported in Arduino, you need to convert it yourself, ie. dtostrf(Temperature, 5, 2, tempBuf)
        #define _DEBUG 1												// 0 = no output ; 1 = Serial debug output ; 2 = V_TEXT remote output 
        #if _DEBUG == 1													// Serial output
        	char printBuf[24] ;											// need temporary buffer
        	#define Printf(...) { sprintf(printBuf, __VA_ARGS__) ; Serial.print(printBuf);}	// macro to substitute Printf()
        #elif _DEBUG == 2												// if remote debug you need to define a child and present it to the controller
        	#define DEBUG_CHILD_ID 10									// Child id of V_TEXT
        	MyMessage debugMsg(DEBUG_CHILD_ID, V_TEXT);					// get the debug message ready
        	char printBuf[24] ;											// need temporary buffer
        	#define Printf(...) { sprintf(printBuf, __VA_ARGS__) ; send(debugMsg.set(printBuf)); }	// macro to substitute Printf()						
        #else															// No debug wanted
        	#define Printf(...)										
        #endif
        
        const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ;	// heartbeat, let the controller know its alive
        unsigned long heartbeatCounter = 0 ;
        
        const unsigned long updateInterval = 10 * 1000UL ;				// controller update
        unsigned long lastUpdate = 0 ;
        
        const unsigned long manualTime = 60 * 60 * 1000UL ;				// manual timer exit to automatic if not reset by controller command reception 
        unsigned long manualTimer = 0 ;
        boolean manualFlag = false ;									// flag for boost machine to indicate manual operation
        boolean manualStatus = false ;									// actual status of automatic/ manual
        
        const unsigned long boostTime = 30 * 60 * 1000UL ;				// boost timer: period boost is active 
        unsigned long boostTimer = 0 ;
        boolean boostStatus = false ;
        const uint8_t boostTemperature = 30 ;							// temperature on which boost becomes active in Celcius
        const uint8_t boostThreshold = 2 ;								// threshold for temperature in Celcius	
        
        const uint8_t maxVentLevel = 0xFF ;								// level in max boost
        const uint8_t minVentLevel = 20 ;								// level in min boost	
        const uint8_t runVentLevel = 50 ;								// level in running state (rest)	
        
        const unsigned long dimLevelInterval = 10UL ;					// dimlevelinterval
        unsigned long lastDimLevelUpdate = 0 ;
        
        uint8_t lastDimLevel = 0x0 , newDimLevel = 0x0	;				// Current or last dim level...
        
        // flags to indicate if transmission is needed, heartbeat and/or changes > treshold
        boolean txTemperature = true ;									// flags to indicate if transmit is needed (time / change driven)
        const float tempThreshold = 0.01 ; 								// send only if change > treshold (Celcius)
        
        // arrays to hold device addresses and last temp
        DeviceAddress dThermometer[MAX_ATTACHED_DS18B20];				// hold addresses
        float lastTemperature[MAX_ATTACHED_DS18B20];
        
        OneWire oneWire(ONE_WIRE_BUS); 									// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
        DallasTemperature sensors(&oneWire); 							// Pass the oneWire reference to Dallas Temperature. 
        
        MyMessage dimmerMsg(0, V_PERCENTAGE);
        MyMessage lightMsg(0, V_LIGHT);
        MyMessage temperatureMsg(0, V_TEMP);							// Temp 
        
        /***
         * Dimmable LED initialization method
         */
        void setup(){ 
        	// Set PWM for vents, PWM 4 wire vents needs 25kHz, PWM 3 wire (voltage controlled) need low frequency PWM < 100 Hz
        	// comment out frequency here. (https://arduino-info.wikispaces.com/Arduino-PWM-Frequency)
        	pinMode( VENT_PIN1, OUTPUT) ;
        	TCCR2B = TCCR2B & B11111000 | B00000001;    				// set timer 2 divisor to     1 for PWM frequency of 31372.55 Hz	
        	//TCCR2B = TCCR2B & B11111000 | B00000110;    				// pin3, set timer 2 divisor to   256 for PWM frequency of   122.55 Hz
        	//TCCR2B = TCCR2B & B11111000 | B00000111;    				// set timer 2 divisor to  1024 for PWM frequency of    30.64 Hz
        
        
        	// Startup up the OneWire library
        	sensors.begin();
        	sensors.setResolution(TEMPERATURE_PRECISION);
        	for (int i = 0; i < MAX_ATTACHED_DS18B20; i++ ){
        		if (!sensors.getAddress(dThermometer[i], i)){
        			Printf("no address for Device \n") ;
        		}
        	}
        	// requestTemperatures() will not block current thread
        	sensors.setWaitForConversion(false);
        }
        
        void presentation() {
        	// Send the sketch version information to the gateway and Controller
        	sendSketchInfo("AWI " NODE_TXT, "1.0");
        	
        	present(VENT_CHILD, S_DIMMER, "Speed " NODE_TXT);
        	
        	present(IN_TEMP_CHILD, S_TEMP, "T in " NODE_TXT);
        	present(OUT_TEMP_CHILD, S_TEMP, "T out " NODE_TXT);	
        	present(ENV_TEMP_CHILD, S_TEMP, "T env " NODE_TXT);	
        }
        
        /***
         *  Dimmable LED main processing loop 
         */
        void loop(){
        	unsigned long now = millis();
        	if ( now > heartbeatCounter + heartbeatInterval) {				// alive signal to controller
        	    sendHeartbeat();
        		heartbeatCounter = now ; 
        	}
        	if ( now > lastUpdate + updateInterval) {						// update all sensors
        	    readTempHum();
        		lastUpdate = now ;
        		boostMachine() ;
        	}
        	fadeToLevel();													// dimmer fade update
        }
        
        void receive(const MyMessage &message) {
        	if (message.type == V_STATUS){
        		manualStatus = (message.getInt() != 0)?true:false ;
        		manualFlag = true ;											// override automatic operation
        	} else if (message.type == V_DIMMER){
            //  Retrieve the power or dim level from the incoming request message
        		newDimLevel = map(message.getLong(), 0, 100, 0, 255);
        		lastDimLevel = newDimLevel ;
        		manualStatus = true ;
        		manualFlag = true ;											// override automatic operation
        		Printf("Level %3d from %3d\n", newDimLevel, lastDimLevel);
        	}
        }
        
        /* 	Boost state machine
        	States:
        	- Idle:  	nothing waiting armed, vent off
        	- Armed:	active waiting for start
        	- Boost:  	starting fast start-up curve, 
        	- Running:	in action, slow vent 
        	- Manual: 	commands received from controller, exit after manual timer
        */ 
        void boostMachine() {
        	unsigned long now = millis(); 									// loop timer reference
        	enum state_t { idle_s, armed_s, boost_s, running_s, manual_s } ;
        	static state_t State = idle_s ; 								// start in idle
        	if (manualFlag){												// check if manual here is not the best solution, but works
        		State = manual_s ; 											// manualflag needs to be reset within time frame
        		manualTimer = now ;
        		manualFlag = false ;
        	}
        	switch (State) {
        		case idle_s:
        			if(( boostTemperature - lastTemperature[0] ) > boostThreshold ){	// arm when in_temp below boost temperature
        				State = armed_s ;
        				newDimLevel = 0 ;
        				Printf("Armed started\n") ;
        			}
        			break ;
        		case armed_s:												// default state, browse through patterns
        			if (( lastTemperature[0] - boostTemperature) > boostThreshold){		// when temp rises above threshold start boost curve
        				State = boost_s ;
        				boostTimer = now ;									// boost lasts for boost time from now
        				Printf("Boost started\n") ;
        			}
        			break ;
        		case boost_s:												// boost state with vent curve, wait for completion
        			if ( now > boostTimer + boostTime) {					// check if boost time expired
        				newDimLevel = runVentLevel ;						// set vent to running (rest) level
        				Printf("Boost ended on time, to running\n") ;
        				State = running_s ;
        			} else {												// boost curve (can be extended now triangle)
        				int boostProgress = map((now - boostTimer)/1000, 0, boostTime/ 1000, 0, 0xFF ) ; // progress in 0xFF steps
        				const uint8_t maxSpeed = 0x20 ;						//  split point in boost period
        				if (boostProgress < maxSpeed){						// speed increase to max for first period
        					newDimLevel = map(boostProgress, 0, maxSpeed, 0, 0xFF) ;
        				} else {											// slow speed decrease to min for second period
        					newDimLevel = 0xFF - map(boostProgress, maxSpeed, 0xFF, 0, 0xFF) ;
        				} 
        				Printf("Prg %d, Spd %d\n", boostProgress, newDimLevel) ;
        			}
        			break ;
        		case running_s:												// steady running waiting for end condition
        			if (( boostTemperature - lastTemperature[IN_THERM] ) > boostThreshold ){
        				State = idle_s ;
        				Printf("Idle\n") ;
        				newDimLevel = 0 ;
        			}
        			break ;
        		case manual_s:												// reacting on actions from controller. Exit on timer (or controller command)
        			if ( (now > manualTimer + manualTime) || !manualStatus ){
        				State = idle_s ;
        				Printf("Idle\n") ;
        				manualStatus = false ;
        				newDimLevel = 0 ;
        			}
        			break ;
        
        		default :
        			State = idle_s ;
        			break ;
        	}
        }
        
        void readTempHum(void){
        	sensors.requestTemperatures();					// Fetch temperatures from Dallas sensors
        	// query conversion time and sleep until conversion completed
        	int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());
        	//Printf("Conversion time %4d ms \n", conversionTime);
        	wait(conversionTime);
        	
        	// Read temperatures and send them to controller if no error and change > threshold
        	float temperature ; 
        	for (int i = 0; i < MAX_ATTACHED_DS18B20; i++ ){
        		temperature	= sensors.getTempC(dThermometer[i]) ;
        		Printf("Temp %2d %2dC \n", i, (int)temperature) ;
        		if ( abs(temperature - lastTemperature[i]) >= tempThreshold && temperature != -127.00 && temperature != 85.00){ 
        			lastTemperature[i] = temperature ;
        			txTemperature = true ;
        		}
        	}
        	if (txTemperature){
        		send(temperatureMsg.setSensor(IN_TEMP_CHILD).set(lastTemperature[IN_THERM], 2));		// Send in deg C
        		send(temperatureMsg.setSensor(OUT_TEMP_CHILD).set(lastTemperature[OUT_THERM], 2));		// Send in deg C
        		send(temperatureMsg.setSensor(ENV_TEMP_CHILD).set(lastTemperature[ENV_THERM], 2));		// Send in deg C
        		txTemperature = false ;
        	}
        }
        
        /***
         *  gracefull fade to global newDimLevel
         */
        void fadeToLevel() {
          	unsigned long now = millis() ;
        	static uint8_t curDimLevel = 0 ;
        	static uint8_t lastAnalogLevel = 0 ;
        	if (now > lastDimLevelUpdate + dimLevelInterval){					// check if time for update
        		if ( newDimLevel > curDimLevel) {
        			curDimLevel++ ;
        		} else if ( newDimLevel < curDimLevel){
        			curDimLevel-- ;
        		}
        		#ifdef SPEED_CONTROL_PIN										// if speedcontrol connected
        			uint8_t speedSetting = ((long)curDimLevel * analogRead(SPEED_CONTROL_PIN))/ 1024 ;		// adjust level with speed setting
        		#else
        			int speedSetting = curDimLevel ;							// no adjustment
        		#endif
        		uint8_t setAnalogLevel = (speedSetting == 0)?0:map((int)(speedSetting), 0, 255, VENT_MIN_LEVEL, VENT_MAX_LEVEL) ;	// calculate new analog
        		if (setAnalogLevel != lastAnalogLevel){							// avoid writes to PWM (needed ?)
        			Printf("Speed setting: %d\n", speedSetting);
        			Printf("Analog: %d \n", setAnalogLevel) ;
        			analogWrite( VENT_PIN1, setAnalogLevel);
        			lastAnalogLevel = setAnalogLevel ;
        			}
        		lastDimLevelUpdate = now ;
        	}
        }
        

        I just received a load of fans to equip the rest of the radiators :chart_with_upwards_trend:
        0_1486327191487_upload-15c4a7b5-b9b5-4e87-a312-ed8efdf18bcd

        Andrej_ABA 1 Reply Last reply
        1
        • M Offline
          M Offline
          mortommy
          wrote on last edited by
          #4

          Hi,
          very interesting project. How did you choose the fan? Did you calculate the CFM or something else (f.i. why Silent 14 and not 12)? How do you assemble the fans, will you replicate any of the commercial solutions?
          Thank you.

          AWIA 1 Reply Last reply
          0
          • M mortommy

            Hi,
            very interesting project. How did you choose the fan? Did you calculate the CFM or something else (f.i. why Silent 14 and not 12)? How do you assemble the fans, will you replicate any of the commercial solutions?
            Thank you.

            AWIA Offline
            AWIA Offline
            AWI
            Hero Member
            wrote on last edited by AWI
            #5

            @mortommy No real answer here apart from (my) common sense. Large fans are more silent. These were avaliable for a few €. The size is also dependent on the size of the radiator. As my circuit makes it possible to set the fan speed I can avoid resonance noise.

            I can place the fans 'in' most of my radiators of the "Jaga"type below:
            0_1486387630195_upload-65a6907d-4a3d-45cf-8050-e9059f67ab1c
            For other types like below I need to make a construction to attach them at the bottow (possibly wood with rubber dampers)
            0_1486387430510_upload-55c49627-a0fa-46d1-85f5-58e16e440bc8

            Dampers:
            0_1486387761399_upload-970203ee-1f00-4ad9-aa32-438682e9c40d

            1 Reply Last reply
            0
            • AWIA AWI

              @Andrej_AB I think this sketch is doing the job. There are some things to take into account:

              • You can comment out the line #define SPEED_CONTROL_PIN A0 // connect to potentiometer for speed control if you don't want to have manual speed control.
              • If you use a PMW fan (4 wire) you need to adjust the PWM frequency to something near 25kHz. ~32 kHz works for me (see sketch) but some fan's are more sensitive.
              • You can control a non-PWM fan (2 or 3 three wire) with an external MOSFET. In this case you need to lower the PWM frequency to keep a brushless motor in control. ~31 Hz (see sketch) worked for me with the circuit below (sorry for the quality of the drawing ;-). MOSFET is IRLZ44 or almost any other.
                0_1486326818435_upload-4c35d27c-2091-4252-aeed-c8098914afc4
              • Be aware that I changed the transmission channel / set a fixed node/ set a fixed parent for my own network
              /*
               PROJECT: MySensors / Ventilator control
               PROGRAMMER: AWI
               DATE: february 2, 2017/ last update: february 5, 2017
               FILE: AWI_Radiator_Booster.ino
               LICENSE: Public domain
              
               Hardware: MySensors 2.0, MOSFET dimmer, Dallas temperature
              		
               Special:
              	
               SUMMARY:
              
              	Full independent with manual mode
              	Controls ventilator speed with PWM
              	Measure temperature and change speed accordingly
              	
               Remarks:
              	Fixed node-id
              	
               Change log:
               20170202 - Created from Ventilator control
               20170205 - Clean code and change manual mode 
              */
              
              // Enable debug prints to serial monitor
              #define MY_DEBUG 
              #define MY_PARENT_NODE_ID 0
              #define MY_NODE_ID 91											// fixed node number
              #define NODE_TXT "Radiator Boost 91"							// Text to add to sensor name
              #define MY_BAUD_RATE 19200
              // Enable and select radio type attached
              #define MY_RADIO_NRF24
              #define MY_RF24_CHANNEL 83										// 83, radio channel, default = 76
              
              #undef MY_REGISTRATION_FEATURE									// sketch moves on if no registration
              
              #include <SPI.h>
              #include <MySensors.h> 
              #include <OneWire.h>
              #include <DallasTemperature.h>
              
              #define ONE_WIRE_BUS 4 											// Pin where dallas sensor is connected 
              #define MAX_ATTACHED_DS18B20 3
              #define TEMPERATURE_PRECISION TEMP_12_BIT
              #define IN_THERM 0 												// indexes of in thermometer, adapt to you situation
              #define OUT_THERM 2
              #define ENV_THERM 1
              
              #define VENT_PIN1 3      										// Arduino PWM pin attached to MOSFET Gate pin -or- vent PWM pin (adjust PWM-Frequency)
              #define VENT_MIN_LEVEL 20										// lowest pwm level for vent
              #define VENT_MAX_LEVEL 255										// highest pwm level for vent
              
              // #define SPEED_CONTROL_PIN A0									// connect to potentiometer for speed control, if commented out no speed control
              
              #define IN_TEMP_CHILD	0										// measure incoming water temp
              #define OUT_TEMP_CHILD	1										// measure outgoing water temp
              #define ENV_TEMP_CHILD	3										// environment
              #define VENT_CHILD	2											// ventilator
              
              // Helper for Debug:  1 = Serial debug output ; 2 = V_TEXT remote output ; else no debug
              // Use Formats described in fprint() : http://www.cplusplus.com/reference/cstdio/printf/  
              // Example: 	Printf("Temp %2d Hum %2d\n", temperature, humidity);
              // warning: max print size < 24 ; float = NOT supported in Arduino, you need to convert it yourself, ie. dtostrf(Temperature, 5, 2, tempBuf)
              #define _DEBUG 1												// 0 = no output ; 1 = Serial debug output ; 2 = V_TEXT remote output 
              #if _DEBUG == 1													// Serial output
              	char printBuf[24] ;											// need temporary buffer
              	#define Printf(...) { sprintf(printBuf, __VA_ARGS__) ; Serial.print(printBuf);}	// macro to substitute Printf()
              #elif _DEBUG == 2												// if remote debug you need to define a child and present it to the controller
              	#define DEBUG_CHILD_ID 10									// Child id of V_TEXT
              	MyMessage debugMsg(DEBUG_CHILD_ID, V_TEXT);					// get the debug message ready
              	char printBuf[24] ;											// need temporary buffer
              	#define Printf(...) { sprintf(printBuf, __VA_ARGS__) ; send(debugMsg.set(printBuf)); }	// macro to substitute Printf()						
              #else															// No debug wanted
              	#define Printf(...)										
              #endif
              
              const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ;	// heartbeat, let the controller know its alive
              unsigned long heartbeatCounter = 0 ;
              
              const unsigned long updateInterval = 10 * 1000UL ;				// controller update
              unsigned long lastUpdate = 0 ;
              
              const unsigned long manualTime = 60 * 60 * 1000UL ;				// manual timer exit to automatic if not reset by controller command reception 
              unsigned long manualTimer = 0 ;
              boolean manualFlag = false ;									// flag for boost machine to indicate manual operation
              boolean manualStatus = false ;									// actual status of automatic/ manual
              
              const unsigned long boostTime = 30 * 60 * 1000UL ;				// boost timer: period boost is active 
              unsigned long boostTimer = 0 ;
              boolean boostStatus = false ;
              const uint8_t boostTemperature = 30 ;							// temperature on which boost becomes active in Celcius
              const uint8_t boostThreshold = 2 ;								// threshold for temperature in Celcius	
              
              const uint8_t maxVentLevel = 0xFF ;								// level in max boost
              const uint8_t minVentLevel = 20 ;								// level in min boost	
              const uint8_t runVentLevel = 50 ;								// level in running state (rest)	
              
              const unsigned long dimLevelInterval = 10UL ;					// dimlevelinterval
              unsigned long lastDimLevelUpdate = 0 ;
              
              uint8_t lastDimLevel = 0x0 , newDimLevel = 0x0	;				// Current or last dim level...
              
              // flags to indicate if transmission is needed, heartbeat and/or changes > treshold
              boolean txTemperature = true ;									// flags to indicate if transmit is needed (time / change driven)
              const float tempThreshold = 0.01 ; 								// send only if change > treshold (Celcius)
              
              // arrays to hold device addresses and last temp
              DeviceAddress dThermometer[MAX_ATTACHED_DS18B20];				// hold addresses
              float lastTemperature[MAX_ATTACHED_DS18B20];
              
              OneWire oneWire(ONE_WIRE_BUS); 									// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
              DallasTemperature sensors(&oneWire); 							// Pass the oneWire reference to Dallas Temperature. 
              
              MyMessage dimmerMsg(0, V_PERCENTAGE);
              MyMessage lightMsg(0, V_LIGHT);
              MyMessage temperatureMsg(0, V_TEMP);							// Temp 
              
              /***
               * Dimmable LED initialization method
               */
              void setup(){ 
              	// Set PWM for vents, PWM 4 wire vents needs 25kHz, PWM 3 wire (voltage controlled) need low frequency PWM < 100 Hz
              	// comment out frequency here. (https://arduino-info.wikispaces.com/Arduino-PWM-Frequency)
              	pinMode( VENT_PIN1, OUTPUT) ;
              	TCCR2B = TCCR2B & B11111000 | B00000001;    				// set timer 2 divisor to     1 for PWM frequency of 31372.55 Hz	
              	//TCCR2B = TCCR2B & B11111000 | B00000110;    				// pin3, set timer 2 divisor to   256 for PWM frequency of   122.55 Hz
              	//TCCR2B = TCCR2B & B11111000 | B00000111;    				// set timer 2 divisor to  1024 for PWM frequency of    30.64 Hz
              
              
              	// Startup up the OneWire library
              	sensors.begin();
              	sensors.setResolution(TEMPERATURE_PRECISION);
              	for (int i = 0; i < MAX_ATTACHED_DS18B20; i++ ){
              		if (!sensors.getAddress(dThermometer[i], i)){
              			Printf("no address for Device \n") ;
              		}
              	}
              	// requestTemperatures() will not block current thread
              	sensors.setWaitForConversion(false);
              }
              
              void presentation() {
              	// Send the sketch version information to the gateway and Controller
              	sendSketchInfo("AWI " NODE_TXT, "1.0");
              	
              	present(VENT_CHILD, S_DIMMER, "Speed " NODE_TXT);
              	
              	present(IN_TEMP_CHILD, S_TEMP, "T in " NODE_TXT);
              	present(OUT_TEMP_CHILD, S_TEMP, "T out " NODE_TXT);	
              	present(ENV_TEMP_CHILD, S_TEMP, "T env " NODE_TXT);	
              }
              
              /***
               *  Dimmable LED main processing loop 
               */
              void loop(){
              	unsigned long now = millis();
              	if ( now > heartbeatCounter + heartbeatInterval) {				// alive signal to controller
              	    sendHeartbeat();
              		heartbeatCounter = now ; 
              	}
              	if ( now > lastUpdate + updateInterval) {						// update all sensors
              	    readTempHum();
              		lastUpdate = now ;
              		boostMachine() ;
              	}
              	fadeToLevel();													// dimmer fade update
              }
              
              void receive(const MyMessage &message) {
              	if (message.type == V_STATUS){
              		manualStatus = (message.getInt() != 0)?true:false ;
              		manualFlag = true ;											// override automatic operation
              	} else if (message.type == V_DIMMER){
                  //  Retrieve the power or dim level from the incoming request message
              		newDimLevel = map(message.getLong(), 0, 100, 0, 255);
              		lastDimLevel = newDimLevel ;
              		manualStatus = true ;
              		manualFlag = true ;											// override automatic operation
              		Printf("Level %3d from %3d\n", newDimLevel, lastDimLevel);
              	}
              }
              
              /* 	Boost state machine
              	States:
              	- Idle:  	nothing waiting armed, vent off
              	- Armed:	active waiting for start
              	- Boost:  	starting fast start-up curve, 
              	- Running:	in action, slow vent 
              	- Manual: 	commands received from controller, exit after manual timer
              */ 
              void boostMachine() {
              	unsigned long now = millis(); 									// loop timer reference
              	enum state_t { idle_s, armed_s, boost_s, running_s, manual_s } ;
              	static state_t State = idle_s ; 								// start in idle
              	if (manualFlag){												// check if manual here is not the best solution, but works
              		State = manual_s ; 											// manualflag needs to be reset within time frame
              		manualTimer = now ;
              		manualFlag = false ;
              	}
              	switch (State) {
              		case idle_s:
              			if(( boostTemperature - lastTemperature[0] ) > boostThreshold ){	// arm when in_temp below boost temperature
              				State = armed_s ;
              				newDimLevel = 0 ;
              				Printf("Armed started\n") ;
              			}
              			break ;
              		case armed_s:												// default state, browse through patterns
              			if (( lastTemperature[0] - boostTemperature) > boostThreshold){		// when temp rises above threshold start boost curve
              				State = boost_s ;
              				boostTimer = now ;									// boost lasts for boost time from now
              				Printf("Boost started\n") ;
              			}
              			break ;
              		case boost_s:												// boost state with vent curve, wait for completion
              			if ( now > boostTimer + boostTime) {					// check if boost time expired
              				newDimLevel = runVentLevel ;						// set vent to running (rest) level
              				Printf("Boost ended on time, to running\n") ;
              				State = running_s ;
              			} else {												// boost curve (can be extended now triangle)
              				int boostProgress = map((now - boostTimer)/1000, 0, boostTime/ 1000, 0, 0xFF ) ; // progress in 0xFF steps
              				const uint8_t maxSpeed = 0x20 ;						//  split point in boost period
              				if (boostProgress < maxSpeed){						// speed increase to max for first period
              					newDimLevel = map(boostProgress, 0, maxSpeed, 0, 0xFF) ;
              				} else {											// slow speed decrease to min for second period
              					newDimLevel = 0xFF - map(boostProgress, maxSpeed, 0xFF, 0, 0xFF) ;
              				} 
              				Printf("Prg %d, Spd %d\n", boostProgress, newDimLevel) ;
              			}
              			break ;
              		case running_s:												// steady running waiting for end condition
              			if (( boostTemperature - lastTemperature[IN_THERM] ) > boostThreshold ){
              				State = idle_s ;
              				Printf("Idle\n") ;
              				newDimLevel = 0 ;
              			}
              			break ;
              		case manual_s:												// reacting on actions from controller. Exit on timer (or controller command)
              			if ( (now > manualTimer + manualTime) || !manualStatus ){
              				State = idle_s ;
              				Printf("Idle\n") ;
              				manualStatus = false ;
              				newDimLevel = 0 ;
              			}
              			break ;
              
              		default :
              			State = idle_s ;
              			break ;
              	}
              }
              
              void readTempHum(void){
              	sensors.requestTemperatures();					// Fetch temperatures from Dallas sensors
              	// query conversion time and sleep until conversion completed
              	int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());
              	//Printf("Conversion time %4d ms \n", conversionTime);
              	wait(conversionTime);
              	
              	// Read temperatures and send them to controller if no error and change > threshold
              	float temperature ; 
              	for (int i = 0; i < MAX_ATTACHED_DS18B20; i++ ){
              		temperature	= sensors.getTempC(dThermometer[i]) ;
              		Printf("Temp %2d %2dC \n", i, (int)temperature) ;
              		if ( abs(temperature - lastTemperature[i]) >= tempThreshold && temperature != -127.00 && temperature != 85.00){ 
              			lastTemperature[i] = temperature ;
              			txTemperature = true ;
              		}
              	}
              	if (txTemperature){
              		send(temperatureMsg.setSensor(IN_TEMP_CHILD).set(lastTemperature[IN_THERM], 2));		// Send in deg C
              		send(temperatureMsg.setSensor(OUT_TEMP_CHILD).set(lastTemperature[OUT_THERM], 2));		// Send in deg C
              		send(temperatureMsg.setSensor(ENV_TEMP_CHILD).set(lastTemperature[ENV_THERM], 2));		// Send in deg C
              		txTemperature = false ;
              	}
              }
              
              /***
               *  gracefull fade to global newDimLevel
               */
              void fadeToLevel() {
                	unsigned long now = millis() ;
              	static uint8_t curDimLevel = 0 ;
              	static uint8_t lastAnalogLevel = 0 ;
              	if (now > lastDimLevelUpdate + dimLevelInterval){					// check if time for update
              		if ( newDimLevel > curDimLevel) {
              			curDimLevel++ ;
              		} else if ( newDimLevel < curDimLevel){
              			curDimLevel-- ;
              		}
              		#ifdef SPEED_CONTROL_PIN										// if speedcontrol connected
              			uint8_t speedSetting = ((long)curDimLevel * analogRead(SPEED_CONTROL_PIN))/ 1024 ;		// adjust level with speed setting
              		#else
              			int speedSetting = curDimLevel ;							// no adjustment
              		#endif
              		uint8_t setAnalogLevel = (speedSetting == 0)?0:map((int)(speedSetting), 0, 255, VENT_MIN_LEVEL, VENT_MAX_LEVEL) ;	// calculate new analog
              		if (setAnalogLevel != lastAnalogLevel){							// avoid writes to PWM (needed ?)
              			Printf("Speed setting: %d\n", speedSetting);
              			Printf("Analog: %d \n", setAnalogLevel) ;
              			analogWrite( VENT_PIN1, setAnalogLevel);
              			lastAnalogLevel = setAnalogLevel ;
              			}
              		lastDimLevelUpdate = now ;
              	}
              }
              

              I just received a load of fans to equip the rest of the radiators :chart_with_upwards_trend:
              0_1486327191487_upload-15c4a7b5-b9b5-4e87-a312-ed8efdf18bcd

              Andrej_ABA Offline
              Andrej_ABA Offline
              Andrej_AB
              wrote on last edited by
              #6

              @AWI Many thanks for quick response. Will try at soon, just will order fans.

              1 Reply Last reply
              0
              • AWIA AWI

                Winter is cold and energy consumption going up. Time to get out of my cave and start a new project ;-).
                Low temperature heating systems use special radiators which can be benficial also for other heating systems.
                With a few PC vents I built a radiator booster to increase yield of my existing radiators. Still in research stage but results are promising. There are a few commercial options but these are either sub-optimal, expensive and not MySensorized:-1:
                0_1486224457799_upload-32148448-19bf-4fa3-bc2b-9e5390a6cb29 0_1486224551817_upload-e0df9470-6973-4978-910b-e172fce78276 0_1486224672608_upload-e6901de8-736a-43dd-bf72-c228abbdd8b5

                The workbench example built around a compact MySensors node jModule three Dallas temp sensors and PWM circuitry. The whole thing lives in or under the radiator.
                0_1486223384333_upload-15ca957a-7c92-457e-b732-46a1ec6671a1

                0_1486223717517_upload-c5ba7173-eb26-459b-99b9-978284cad0bc

                Working principle:

                • Measure temperatures for water in/ out and environment.
                • Vents start spinning slowly as the "In" temperature reaches a threshold (30 Celcius for now)
                • Vent spinning increase in time for a few minutes and then slowly spin down until constant level.
                • Vents stop spinning when temperature falls below threshold.
                • etc.

                Extra options:

                • Manual mode, started automatically when (MySensors) controller takes over.
                • Fan speed control for tuning

                I am curious if anybody has some hands-on experience on how to best tune the system. And will publish details (sketch) if anyone is interested.

                0_1486224230174_upload-d2dbb63e-1cfc-4a7a-8e14-5c10b6e057090_1486224762708_upload-40a2fd75-5190-4c40-8b63-42160b059d8d

                B Offline
                B Offline
                bilbolodz
                wrote on last edited by
                #7

                @AWI I've made similar project but using RS485 and modbus for communication. Now I'm porting it into mysensors. In my project I've light sensor to lower fan revs during night (because of noise). I will try to post my sketch when I finish it.

                1 Reply Last reply
                0
                Reply
                • Reply as topic
                Log in to reply
                • Oldest to Newest
                • Newest to Oldest
                • Most Votes


                35

                Online

                11.7k

                Users

                11.2k

                Topics

                113.1k

                Posts


                Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                • Login

                • Don't have an account? Register

                • Login or register to search.
                • First post
                  Last post
                0
                • MySensors
                • OpenHardware.io
                • Categories
                • Recent
                • Tags
                • Popular