Blindcontroller with PB local control



  • Hello,
    This sketch can be used for control a blind (local + remote) it is working but there is still room for improvement ... to say the least
    Any suggestions remarks are welcome
    heu it is not tested in arduino dev (now it is 2016/08/03) (i use eclipse arduino IDE)

    Update 2016/08/03:
    insert files as requested by TheoL (and removed the ZIP file
    **ATTENTION ** i could not make it in one post so you need also part 2 !!

    This is the main sketch (and corrected smal bug)

    "Rfm69_BlindController.ino"

    // Do not remove the include below
    #include "Rfm69_BlindController.h"
    #include "Bl_Control.h"
    
    
     /*
      BlindController for MySensors (or any other local/remote control )
    
      This is a adaption off this sketch (garage door control)
      original is @ https://forum.mysensors.org/topic/4059/automated-garage-door/21
      from user https://forum.mysensors.org/user/dbemowsk
      However not much is still in it
    
    
      July 20,2016
    
      This will allow the MySensors gateway to monitor and control your blinds.  This sketch monitors the actual position
      of the blind(s). It CAN use a sens input to detect current flowing (true/false)
      when the blind is running and current stops (current gets zero) we know the demanded position limit is reached,
      in the required direction (hard limit up/down).
    
      This sketch features the following:
    
      Allows you to monitor and control the blind position and return it's status based on the following:
    
        1   - use two local inputs (buttons) to increase or decrease the position
    
        2   - sending a setpoint/position request by a host (Mysensor here)
    
      3 - Send a Factor (gain) to fine tune the time run up/down)
    
      PARTS LIST:(for 2 blinds)
      Moteino
    
      4 3 or 5v relay
      4 2N3904 transistor for relay
      4 1k resistor
      2 2k7 Resistor (for Led)
      2 Led (error /run)
    
    HW CONFIG;  (here moteino)
      Relay UP      D5  (Blind1)
      Relay Down      D6  (Blind1)
      Error/Run Led     D7  (Blind1)
    
      InputUP       D14 (Blind1)
      InputDown       D15 (Blind1)
      Currentsense    D16 (Blind1)
    
      Relay UP      D3  (Blind2)
      Relay Down      D4  (Blind2)
      Error/Run Led     D17 (Blind2)
    
      InputUP       D18 (Blind2)
      InputDown     D19 (Blind2)
      Currentsense    A6  (Blind2)
    
      Version history:
      0.1 : Refractor original source (garage opener see ref)
      0.2 :   Build original concept without object
      0.3 :   Rebuil d with object class
      0.4 :   Added a factor (gain) to allow a wider use  and removed "magic numbers" in code
      0.5 : Added code for retrieving factor from EEPROM
            : added fix for nasty RT-bug when gain gets higher then 131 (integer overflow)
    */
    
    #include <MyTransportRFM69.h>
    #include <MyHwATMega328.h>
    #include <MySensor.h>
    //#include <SPI.h>
    
    
    
    #define SKETCH_NAME "BlindController"
    #define SKETCH_VERSION "0.4"
    
    
    #define NODE_ID         85  //Define The node <> 0 (i don't want a auto assignment see setup @mySensors)
    
    #define CHILD_ID_SP_BL1     52  //Here we receive the setpoint
    #define CHILD_ID_GAIN_BL1     53  //This is the gain (value from 10 to 1000%!)
    
    
    #define CHILD_ID_SP_BL2     55  //Here we receive the setpoint
    #define CHILD_ID_GAIN_BL2     56  //This is the gain (value from 10 to 1000%!)
    
    #define EPROM_GAIN_BL1      1 //This is the location where the factor is stored  (Blind1)
                      //After a power failure this value will be restored
    #define EPROM_GAIN_BL2      3 //This is the location where the factor is stored (Blind2)
    
    
    
    #define DI_BL1_UP         14  //Pin used for command up
    #define DI_BL1_DWN        15  //Pin used for command Down
    #define AI_BL1_SENS       16  //Pin used Sensing the current (Yes/no)
    
    #define DO_BL1_MOTOR_UP     5 //Pin activate relay up
    #define DO_BL1_MOTOR_DWN    6 //Pin activate Relay Down
    #define DO_BL1_ERR_RUN      7 //Pin activate Run/error
    
    
    #define DI_BL2_UP         18  //Pin used for command up
    #define DI_BL2_DWN        19  //Pin used for command Down
    #define AI_BL2_SENS       A6  //Pin used Sensing the current (Yes/no)
    
    #define DO_BL2_MOTOR_UP     3 //Pin activate relay up
    #define DO_BL2_MOTOR_DWN    4 //Pin activate Relay Down
    #define DO_BL2_ERR_RUN      17  //Pin activate Run/error
    
    
    
    
    #define MOTOR_ON        0 // GPIO value to write to turn on attached relay
    #define MOTOR_OFF         1   // GPIO value to write to turn off attached relay
    
    
    #define DEBUG_BLINDCONTROLLER   0 // Set to non zero for debugging
    
    // new V_TEXT variable type (development 20150905)
    //const int V_TEXT = 47 ;
    // new S_INFO sensor type (development 20150905)
    //const int S_INFO = 36 ;
    
    
    //Some timer logic to do updates on the inputs
    unsigned long bl_previoustMillis_inp_scan =0;
    
    
    
    MyMessage msg(CHILD_ID_SP_BL1, V_PERCENTAGE);// global response object
    
    MyTransportRFM69 transport;       //Attention most users will use a NRF here!!
    // Hardware profile
    MyHwATMega328 hw;
    
    MySensor gw(transport,hw);
    
    //MyMessage msgStatus(CHILD_ID_STATUS, V_TEXT);
    
    Bl_Control blind1,blind2;     //Handle to controller Instance
    
    /**
     * Function Prototypes
     */
    // hmm.. seems not needed in Arduino
    
    /**
     * setup - Initialize the HW
     */
    void setup() {
    
    //BLIND1
      //initialize the inputs Blind1
      pinMode(DI_BL1_UP, INPUT_PULLUP);
      pinMode(DI_BL1_DWN, INPUT_PULLUP);
      pinMode(AI_BL1_SENS,INPUT_PULLUP);
      //Set outputs to safe state and init
      digitalWrite( DO_BL1_MOTOR_UP,MOTOR_OFF);
      digitalWrite( DO_BL1_MOTOR_DWN,MOTOR_OFF);
      digitalWrite(DO_BL1_ERR_RUN ,MOTOR_OFF);
    
      pinMode(DO_BL1_MOTOR_UP, OUTPUT);
      pinMode(DO_BL1_MOTOR_DWN, OUTPUT);
      pinMode(DO_BL1_ERR_RUN, OUTPUT);
    
    //BLIND2
    //TODO: Test extra controller
      pinMode(DI_BL2_UP, INPUT_PULLUP);
      pinMode(DI_BL2_DWN, INPUT_PULLUP);
      //pinMode(AI_BL2_SENS,INPUT_PULLUP);
      //Set outputs to safe state and init
      digitalWrite( DO_BL2_MOTOR_UP,MOTOR_OFF);
      digitalWrite( DO_BL2_MOTOR_DWN,MOTOR_OFF);
      digitalWrite(DO_BL2_ERR_RUN ,MOTOR_OFF);
    
      pinMode(DO_BL2_MOTOR_UP, OUTPUT);
      pinMode(DO_BL2_MOTOR_DWN, OUTPUT);
      pinMode(DO_BL2_ERR_RUN, OUTPUT);
    
    
    
    #if NODE_ID
      gw.begin( incomingMessage, NODE_ID,false); //No repeater NODE_ID is defined
    #else
      gw.begin( incomingMessage);         //No NODE_ID auto from GW (Host) will give you one
    #endif
      gw.sendSketchInfo( SKETCH_NAME, SKETCH_VERSION );
    
      // Register the blind to the gateway
      gw.present( CHILD_ID_SP_BL1, S_DIMMER,"Position" );
      gw.present(CHILD_ID_GAIN_BL1, S_DIMMER, "Gain");
    
      gw.present( CHILD_ID_SP_BL2, S_DIMMER,"Blind 2 Pos" );
      gw.present(CHILD_ID_GAIN_BL2, S_DIMMER, "Blind 2 Gain");
    
      //init the scan clock
      bl_previoustMillis_inp_scan = millis()+1000; //Give a offset from the current moment (1000msec) to complete the setup
    
      //Restore from local EEprom the factor/gain
      int Gain=gw.loadState(EPROM_GAIN_BL1);    //Get the gain from the EEPROM
      Gain = highByte(Gain) ;
      Gain|=  lowByte( gw.loadState(EPROM_GAIN_BL1+1)); //Get the gain from the EEPROM
      blind1.Set_Gain_PV((uint16_t ) Gain);   //Store the gain in controller obj Blind1
      msg.setSensor(CHILD_ID_GAIN_BL1);     //Set startup gain to host
      msg.set(Gain);
      gw.send( msg);                //send it to the host
    
      Gain=gw.loadState(EPROM_GAIN_BL2);      //Get the gain from the EEPROM
      Gain = highByte(Gain);
      Gain|=  lowByte( gw.loadState(EPROM_GAIN_BL2+1)); //Get the gain from the EEPROM
      blind2.Set_Gain_PV((uint16_t ) Gain);   //Store the gain in controller obj Blind1
      msg.setSensor(CHILD_ID_GAIN_BL2);     //Set startup gain to host
      msg.set(Gain);
      gw.send( msg);                //send it to the host
    
    
      // Pull the gateway's current Blind level - restore level upon sender node power-up
      gw.request( CHILD_ID_SP_BL1, V_DIMMER );
    
      // Pull the gateway's current Blind level - restore level upon sender node power-up
      gw.request( CHILD_ID_SP_BL2, V_DIMMER );
    
    
    }
    
    
    void tasks (){
    
      uint8_t tmp_Current , tmp_Current2;
    
      uint8_t tmp_up, tmp_up2;
      uint8_t tmp_dwn , tmp_dwn2;
    
      static uint8_t st_PrevOutput_State  =0; //Local memory only update when something
      static uint8_t st_PrevOutput_State2 =0;
    
      uint8_t  tmp_CurrentOutput_State,tmp_CurrentOutput_State2;
    
      //Read local inputs blind1
      tmp_up = digitalRead(DI_BL1_UP )==false;
      tmp_dwn = digitalRead(DI_BL1_DWN)==false;
    //TODO: Test proof of concept:
      tmp_Current =1;
      // digitalRead(t_BL_InBits::CURSNS)==false;
    
      //Read local inputs blind1
      tmp_up2 = digitalRead(DI_BL2_UP )==false;
      tmp_dwn2 = digitalRead(DI_BL2_DWN)==false;
    
      tmp_Current2 =1;
      //tmp_Current2= analogRead(AI_BL2_SENS) > 100 ? 0 : 1;
    
    
      blind1.bl_SetInputs(tmp_up,tmp_dwn,tmp_Current);
      tmp_CurrentOutput_State =blind1.bl_GetOutputs();
    
      blind2.bl_SetInputs(tmp_up2,tmp_dwn2,tmp_Current2);
      tmp_CurrentOutput_State2 = blind2.bl_GetOutputs();
    
    
      // Somethings changed do actions
      if (st_PrevOutput_State != tmp_CurrentOutput_State){
    
        if (bitRead(tmp_CurrentOutput_State, t_BL_OutBits::BL_RN_ERR )) {
          digitalWrite(DO_BL1_MOTOR_UP,MOTOR_OFF);
          digitalWrite(DO_BL1_MOTOR_DWN,MOTOR_OFF);
          digitalWrite(DO_BL1_ERR_RUN,MOTOR_ON);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RN_ERR");
    #endif
    
    
        }
        if (bitRead(tmp_CurrentOutput_State, t_BL_OutBits::BL_RN_UP )) {
          digitalWrite(DO_BL1_MOTOR_UP,MOTOR_ON);
          digitalWrite(DO_BL1_MOTOR_DWN,MOTOR_OFF);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RN_U");
    #endif
    
        }else{
          digitalWrite(DO_BL1_MOTOR_UP,MOTOR_OFF);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RN_S1");
    #endif
    
        }
    
        if (bitRead(tmp_CurrentOutput_State, t_BL_OutBits::BL_RN_DWN )) {
          digitalWrite(DO_BL1_MOTOR_UP,MOTOR_OFF);
          digitalWrite(DO_BL1_MOTOR_DWN,MOTOR_ON);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RN_D");
    #endif
    
        }else{
          digitalWrite(DO_BL1_MOTOR_DWN,MOTOR_OFF);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RN_S2");
    #endif
        }
        st_PrevOutput_State = tmp_CurrentOutput_State;
        if (blind1.Get_PV() >100){
          msg.setSensor(CHILD_ID_SP_BL1);
          msg.setType(V_PERCENTAGE);
    
          gw.send( msg.set(100) );    //Inform the host that we are on the max
          }
        else{
          msg.setSensor(CHILD_ID_SP_BL1);
          msg.setType(V_PERCENTAGE);
          gw.send( msg.set(blind1.Get_PV()) );
        }
    
      }
    
      // Somethings changed for blind2 do actions
      if (st_PrevOutput_State2 != tmp_CurrentOutput_State2){
    
        if (bitRead(tmp_CurrentOutput_State2, t_BL_OutBits::BL_RN_ERR )) {
          digitalWrite(DO_BL2_MOTOR_UP,MOTOR_OFF);
          digitalWrite(DO_BL2_MOTOR_DWN,MOTOR_OFF);
          digitalWrite(DO_BL2_ERR_RUN,MOTOR_ON);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RNERR2");
    #endif
    
    
        }
        if (bitRead(tmp_CurrentOutput_State2, t_BL_OutBits::BL_RN_UP )) {
          digitalWrite(DO_BL2_MOTOR_UP,MOTOR_ON);
          digitalWrite(DO_BL2_MOTOR_DWN,MOTOR_OFF);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RNU2");
    #endif
    
        }else{
          digitalWrite(DO_BL2_MOTOR_UP,MOTOR_OFF);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RNS12");
    #endif
        }
    
        if (bitRead(tmp_CurrentOutput_State2, t_BL_OutBits::BL_RN_DWN )) {
          digitalWrite(DO_BL2_MOTOR_UP,MOTOR_OFF);
          digitalWrite(DO_BL2_MOTOR_DWN,MOTOR_ON);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RN_D2");
    #endif
    
        }else{
          digitalWrite(DO_BL2_MOTOR_DWN,MOTOR_OFF);
    #if DEBUG_BLINDCONTROLLER
          Serial.println("RNS2");
    #endif
    
        }
        st_PrevOutput_State2 = tmp_CurrentOutput_State2;
        if (blind2.Get_PV() >100){
          msg.setSensor(CHILD_ID_SP_BL2);
          msg.setType(V_PERCENTAGE);
    
          gw.send( msg.set(100) );    //Inform the host that we are on the max
          }
        else{
          msg.setSensor(CHILD_ID_SP_BL2);
          msg.setType(V_PERCENTAGE);
          gw.send( msg.set(blind2.Get_PV()) );
        }
    
      }
    
    
      //Update the eye candy (feedback) Blind1
      if  (blind1.bl_RunErrorLed()  ){
        digitalWrite(DO_BL1_ERR_RUN,MOTOR_ON);
      }else{
        digitalWrite(DO_BL1_ERR_RUN,MOTOR_OFF);
      }
    
      //Update the eye candy (feedback) Blind2
      if  (blind2.bl_RunErrorLed()  ){
        digitalWrite(DO_BL2_ERR_RUN,MOTOR_ON);
      }else{
        digitalWrite(DO_BL2_ERR_RUN,MOTOR_OFF);
      }
    
    }
    
    /**
     * loop - The main program loop
     */
    void loop() {
    
      // Alway process incoming messages whenever possible
      gw.process();
    
      blind1.bl_Process();
      blind2.bl_Process();
    
      tasks();
    
    }
    
    
    
    /**
     * incomingMessage - Process the incoming messages
     * From the host
     */
    void incomingMessage( const MyMessage &message ) {
      uint16_t tmp_Faktor;
    
      uint8_t New_sp =0;
      uint8_t New_Cmd=0;
    
      if (message.sensor ==CHILD_ID_SP_BL1 ) {
        if (message.type == V_DIMMER) { // This is a SP change request
            int val = message.getInt();//retrieve SP from message
            bitSet(New_Cmd,0);
            New_sp = 0xff & val; //set SP to the requested value
    
          }else if (message.type==V_STATUS) {//Position request (full open / full closed)
                if (!message.getBool()) { //I Use a inverted blind on the GUI
                  bitSet(New_Cmd,1);    //Fire to full close
             }else{
               bitSet(New_Cmd,2);       //Fire to full open
             }
          }
          blind1.UpdateRemote_cmd(New_sp,New_Cmd);//Send it to the controller
    
      }else if (message.sensor ==CHILD_ID_GAIN_BL1 ){
          tmp_Faktor = message.getInt();
          if(tmp_Faktor != 0)
            blind1.Set_Gain_PV(tmp_Faktor);
            gw.saveState(EPROM_GAIN_BL1,highByte( tmp_Faktor));
            gw.saveState(EPROM_GAIN_BL1+1,lowByte( tmp_Faktor));
    
      }else if (message.sensor ==CHILD_ID_SP_BL2 ){
        if (message.type == V_DIMMER) { // This is a SP change request
            int val = message.getInt();//retrieve SP from message
            bitSet(New_Cmd,0);
            New_sp = 0xff & val; //set SP to the requested value
    
          }else if (message.type==V_STATUS) {//Position request (full open / full closed)
                if (!message.getBool()) { //I Use a inverted blind on the GUI
                  bitSet(New_Cmd,1);    //Fire to full close
             }else{
               bitSet(New_Cmd,2);       //Fire to full open
             }
          }
          blind2.UpdateRemote_cmd(New_sp,New_Cmd);//Send it to the controller
    
      }else if (message.sensor ==CHILD_ID_GAIN_BL2 ){
    
          tmp_Faktor = message.getInt();
          if(tmp_Faktor != 0)
          blind2.Set_Gain_PV(tmp_Faktor);
          gw.saveState(EPROM_GAIN_BL2,highByte( tmp_Faktor));
          gw.saveState(EPROM_GAIN_BL2+1,lowByte( tmp_Faktor));
        }
    
    }
    
    

    Header file ""Rfm69_BlindController.h"

    
    // Only modify this file to include
    // - function definitions (prototypes)
    // - include files
    // - extern variable definitions
    // In the appropriate section
    
    #ifndef _BlindController_H_
    #define _BlindController_H_
    #include "Arduino.h"
    //add your includes for the project BlindController here
    
    #include "MySensor.h"
    
    //end of add your includes here
    
    
    
    
    
    
    
    //Do not add code below this line
    #endif /* _BlindController_H_ */
    
    

  • Contest Winner

    @stedew It sounds as a great project. But I want to ask you to include the full sketch on the forum. You can do this be beginning a line with 4 characters. Then paste the code on the next line and add a new line with 4 characters.

    I'm personally not fund of downloading links. I downloaded viruses this way. Also people can see your sketch immediately without having to download. Thanx in advance.



  • This is part 2:

    Under is the controller object it was (and is) for me an excercice how to make a modular c++ program
    Al hw dependency is left out (except millis & Serial.print) everything should behave like defined on a different platform.
    There is plenty room to improve this so shoot: be critic but fair wat i can improve (and how)

    File: "Bl_Control.cpp"

    
    /*
     * Bl_Control.cpp
     *
     *  Created on: 20 jul. 2016
     *      Author: Stefan
     *
     *
     *      Setpoint controller up/down
     * 		Allows you to monitor and control a setpoint vs PV
     * 		1 	- use a local inputs byte to increase or decrease the position
     * 		2 	- send a setpoint/position request by a host
     * 		3	- Set a Factor (gain) to change the ratio PV /SP
     *
     *
     *	Version history:
     *	0.1	:	build object class
     *	0.2	: 	Added a factor (gain) to allow a wider use  and removed "magic numbers" in code
     *	0.3	:	Fix for nasty RT-bug when gain gets higher then 131 >>integer overflow when multiply with BL_MAX_TIME_UP (250)
     *
     *
     *	TODO:
     *		:optimization use off variables
     *		:implemnt auto teach sequence
     */
    
    #if defined(ARDUINO) && ARDUINO >= 100
    #include "Arduino.h"
    #else
    #include "WProgram.h"
    #endif
    
    #include "Bl_Control.h"
    
    
    Bl_Control::Bl_Control(){
    
    		_previoustMillis_inp_scan= millis();//init the scan clock when object is created
    		_SP =BL_SP_MIN; 			//Current active SP
    		_Remote_SP =BL_SP_MIN; 	//Last received SP received from the Host
    		_Remote_CMD = t_BL_Cmd::BL_CMD_NONE;//Last received Command from the Host
    		_Local_CMD =  t_BL_Cmd::BL_CMD_NONE; ;	//Last received Command from the inputs
    		_Runtime = 0;				//Time Controller is running
    
    		_Error =0;					//Module in error
    		_PV =0; 					//Global internall Process_Value (PV) aka the actual (or estimated) position off the blind
    		_ErrRunTmr =0;
    		_Error=0;
    		_tmrLocalCmddwn =t_BL_Cmd::BL_CMD_NONE;
    		_tmrLocalCmdup =t_BL_Cmd::BL_CMD_NONE;
    		_prev_rem_cmd= t_BL_Cmd::BL_CMD_NONE;
    		_inputs=0;
    		_ouputs=0;
    		_RunCtrlCmd = t_BL_Cmd::BL_CMD_NONE;
    		_RunCtrlprevLocCmd = t_BL_Cmd::BL_CMD_NONE;
    		_Gain_PV =100;			//Sets the controller to center gain (1/1)= 100%
    
    
    
    }
    
    /**
     * Get_Gain_PV	:	Returns the ratio (in %) _PV to _SP
     * inputs are 	:	None
     * Chances  	:	None
     * Returns 		: 	Factor (min)
     */
    uint16_t Bl_Control::Get_Gain_PV(){
    	return _Gain_PV;
    }
    
    /**
     * Set_Gain_PV	:	Set the ratio _PV to _SP
     * inputs are 	:	Newgain 10(%) min to 1000% max
     * Chances  	:	_Gain_PV
     * Returns 		: 	None
     */
    void Bl_Control::Set_Gain_PV (uint16_t NewGain){
    
    	if(_Gain_PV!=NewGain){
    #if BL_DEBUG -1
    		Serial.print("N_Max_PV ");
    		Serial.println(NewGain);
    #endif
    	//We avoid float by multiply the result by 10
    	// 	10 is 10%
    	//	1000 is 1000%
     //TODO: Remove test //(Domoticz gives 1 to 100 and not 10 to 1000
    		NewGain = NewGain *10;
    
    		if (NewGain >BL_PV_MAX ) {//Limit is 1000%
    			NewGain = BL_PV_MAX  ;
    		}else if (NewGain > BL_PV_MIN  ){
    			NewGain=(NewGain );
    		}else{
    			NewGain = (BL_PV_MIN ) ;//Limit is 10
    		}
    		_Gain_PV=NewGain;
    
    #if BL_DEBUG -1
    		Serial.print("New_Gain: ");
    		Serial.println(_Gain_PV,DEC );
    #endif
    	}
    
    }
    
    /**
     * bl_Process	 Main worker when called the period elapsed is evaluated if expired the internal routines
     * 				 shall be executed
     * inputs are 	:	None
     *
     * Chances  	:	_previoustMillis_inp_scan (Time keeper)
     * 				:
     * Returns 		: 	None
     */
    void Bl_Control::bl_Process (){
    
    	unsigned long currentMillis = millis();
    	if (currentMillis - _previoustMillis_inp_scan >= UPDATE_INPUTS_ms) {
    
    		Update_Local_cmd();
    		Run_ctrl( );
    		_previoustMillis_inp_scan = currentMillis;
    	}
    
    
    }
    
    
    /**
     * Update_Local_cmd	 - Evaluate the local input byte and update the _Local_CMD off the blind
     * inputs are 	:	none
     * 				:	(except the internal _inputs)
     * outputs are 	:	none
     * 				:
     * Returns 		: 	_Local_CMD
     *
     * routine is called on a regular (timed)interval and manipulates _Local_CMD
     * The routine sets the cmd_state and stores the last command executed
     * 1° a Short (input)cmd_up/cmd_dwn gives a run to the direction requested (MAX/MIN)
     * 2° Long cmd_up/cmd_down gives a run state UP/DWN until release (tipping)
     * 3° cmd_up && cmd_dwn at same time : >> reset Error state
     * 4° A retrigger (when running to the max/min) >> stop is executed
     *
     */
    uint8_t Bl_Control::Update_Local_cmd (void)
    {
    	uint8_t tmp_cmd = _Local_CMD;
    //First test if there is a error reset request (both cmd_up && cmd_dwn active)
    	if (((bitRead(_inputs, t_BL_InBits::INCR ) != 0 )&& (bitRead(_inputs, t_BL_InBits::DECR) !=0))) {
    		_tmrLocalCmddwn =0;//Reset counters /Timers
    		_tmrLocalCmdup = 0;
    		_Local_CMD = t_BL_Cmd::BL_CMD_RST;//Store the last action
    		_Error = false;//
    		return _Local_CMD;
    
    	}
    	if (_Error) {
    		return _Local_CMD;
    	}
    
    	// Blind up evaluation
    	if (bitRead(_inputs,t_BL_InBits::INCR)) {
    		if (_tmrLocalCmdup <= BL_T_Hold) { 			//Increase hold counter
    			_tmrLocalCmdup++;
    			tmp_cmd = t_BL_Cmd::BL_CMD_INC;				//Sets a increase marker (tipping mode)
    			if ( _Local_CMD == t_BL_Cmd::BL_CMD_UP)  {	//Repeated impulse command change to tipping mode
    				_tmrLocalCmdup = BL_T_Hold;			//set timer to holdlimit so we can stop at PB release
    			}
    		}else{
    		}
    
    	} else {
    		if ((_tmrLocalCmdup !=0) && (_tmrLocalCmdup < BL_T_Hold)){
    		// cmd_up is impulsed
    			tmp_cmd = t_BL_Cmd::BL_CMD_UP;
    		}else{
    			// button was hold longer then T_Hold (Tipping) but now released
    			if (_tmrLocalCmdup >=BL_T_Hold) tmp_cmd = t_BL_Cmd::BL_CMD_STOP;
    		}
    		// When button is released reset the counter
    		_tmrLocalCmdup =0;
    	}
    
    	// Blind Down evaluation
    	if (bitRead(_inputs, t_BL_InBits::DECR )) {
    		//st_tmrhld_Localup=0;	// When we go down up is reset
    		if (_tmrLocalCmddwn <= BL_T_Hold){
    			_tmrLocalCmddwn++; //Increase hold
    			tmp_cmd = t_BL_Cmd::BL_CMD_DEC;
    			if ( _Local_CMD == t_BL_Cmd::BL_CMD_DWN)  {//Repeated impulse command change to tipping mode
    				_tmrLocalCmddwn = BL_T_Hold;			//set timer to holdlimit so we can stop at PB release
    			}
    		}
    	} else {
    		if ((_tmrLocalCmddwn !=0) && (_tmrLocalCmddwn < BL_T_Hold)){
    		// cmd_dwn is impulsed
    			tmp_cmd = t_BL_Cmd::BL_CMD_DWN;
    		}else{
    		// button was hold longer then T_Hold (tipping) but now released >>release Tip
    			if (_tmrLocalCmddwn >=BL_T_Hold) tmp_cmd = t_BL_Cmd::BL_CMD_STOP;
    		}
    		// When button is released reset the counter
    		_tmrLocalCmddwn =0;
    
    	}
    
    	if (tmp_cmd != _Local_CMD)
    	{
    		_Local_CMD = tmp_cmd;
    	}
    
    	return _Local_CMD;
    }
    
    /**
     * bl_GetOutputs	Returns the current output bits to the caller
     * inputs are 	:	None
     *
     * Chances  	:	None
     * 				:
     * Returns 		: 	Output bits
     */
    uint8_t Bl_Control::bl_GetOutputs (void)
    {
    	return _ouputs;
    }
    
    /**
     * UpdateRemote_cmd	 - Evaluate the messages from the host (here domoticsz)
     * inputs are 	:	MyMessage
     *
     * Chances  	:	_Remote_SP,
     * 				:
     * Returns 		: 	remote_Commandstate
     *
     * routine is called when a message from the host is received
     * The routine sets the cmd_stat and stores the last command if changed
     * The routine interprets the message and send a open/close or run to new setpoint
     */
    
    uint8_t Bl_Control::UpdateRemote_cmd (uint8_t Sp, uint8_t cmd_Type )
    {
    	uint8_t tmp_cmd=  _Remote_CMD;//Current command
    	uint8_t tmp_SP ;// Current Setpoint
    
    	tmp_SP = _Remote_SP;//Store the current SP
    
    
    	if (bitRead(cmd_Type ,0)) { // This is a SP change request
    
    		 if (_Remote_SP != Sp) {//Is SP from host different to current one?
    #if BL_DEBUG -1
    				Serial.println("BL_RM");
    				Serial.print("New SP: ");
    				Serial.println(val);
    #endif
    
    			tmp_SP = Sp; 		//set SP to the requested value
    			tmp_cmd = BL_CMD_RUN;
    		 }else {//Nothing to do except change local static
    			 tmp_cmd = BL_CMD_NONE;//reception off same as previous SP
    		}
    	}
    	else if (bitRead(cmd_Type ,1)|| bitRead(cmd_Type ,2)) {//Position request (full open / full closed)
    
    			if (bitRead(cmd_Type ,1)) { //I Use a inverted blind on the GUI
    				if (_Remote_CMD ==BL_CMD_DWN ) {//Re-fire in Run will trigger a stop
    					tmp_cmd = BL_CMD_STOP;//Second push to full position >> stop
    #if BL_DEBUG -1
    					Serial.println("BL STP");
    #endif
    				}else{
    					tmp_SP = BL_SP_MIN ;//adjust SP to min (close)
    					tmp_cmd= BL_CMD_DWN;
    #if BL_DEBUG -1
    					Serial.println("BL_DWN");
    #endif
    				}
    			} else {
    				if (_Remote_CMD ==BL_CMD_UP) {
    					tmp_cmd = BL_CMD_STOP;//Second push to full position >> stop
    #if BL_DEBUG -1
    					Serial.println("BL STP");
    #endif
    				} else {
    					tmp_SP = BL_SP_MAX ;//adjust SP to max
    					tmp_cmd= BL_CMD_UP;
    #if BL_DEBUG -1
    					Serial.println("BL_UP");
    #endif
    				}
    			}
    	 }
    	if (_Remote_CMD == tmp_cmd){
    		if (_Remote_SP != tmp_SP) {
    			_Remote_SP = tmp_SP;				//Overwrite back (modified) SP to caller
    			_Remote_CMD = BL_CMD_RUN;			//Set Run State to start
    			return _Remote_CMD;
    		} else {
    			return BL_CMD_NONE;					//Nothing changed
    		}
    	}else{
    		_Remote_SP = tmp_SP;					//write back (modified) SP to caller
    		_Remote_CMD = tmp_cmd;
    		return _Remote_CMD;
    	}
    
    }
    
    /**
     * SetInputs	:	changes the local inputbits (input command word)
     * inputs are 	:	inc
     * 				:	dec
     * 				:	RunCurrent
     *
     * outputs are 	:	none (manipulates the internal input byte)
     * Returns 		: 	none
     *
     * call this routine to reflect input changes to the ( internal )input controller
     */
    void Bl_Control::bl_SetInputs(uint8_t inc , uint8_t dec, uint8_t RunCurrent  ){
    	if (inc) 	bitSet(_inputs,t_BL_InBits::INCR);	else		bitClear(_inputs,t_BL_InBits::INCR);
    	if (dec)	bitSet(_inputs,t_BL_InBits::DECR);  else	bitClear(_inputs,t_BL_InBits::DECR);
    	if (RunCurrent) 	bitSet(_inputs,t_BL_InBits::CURSNS);	else		bitClear(_inputs,t_BL_InBits::CURSNS);
    }
    /**
     * bl_RunErrorLed	:Give some feedback for the user
     * inputs are 	:	inc
     * 				:	dec
     * 				:	RunCurrent
     *
     * outputs are 	:	none
     * 				:
     * Returns 		: 	State for the error/run Led
     *
     * call this routine for getting the led state (In Run slow blink / In error fast blink)
     *
     */
    uint8_t Bl_Control::bl_RunErrorLed(void){
    
    
    
    	if (_Error){
    
    		if (bitRead(_ErrRunTmr,1)){
    			return true;
    		}else{
    			return false;
    		}
    	}else{
    
    		if ((bitRead(_ouputs,t_BL_OutBits::BL_RN_UP))||bitRead(_ouputs,t_BL_OutBits::BL_RN_DWN)  ) {
    			if (bitRead(_Runtime,2)){
    				return true;
    			}else{
    				return false;
    			}
    			return true;
    		}else {
    			if (_ErrRunTmr) _ErrRunTmr=0;
    			return false;
    		}
    	}
    }
    /**
     * Run_ctrl	 	- main worker controls the movement off the blind
     * inputs are 	: _Error, _SP, _PV (internal)
     * 				:
     * outputs are 	: none
     * 				:
     * Returns 		: 	_ouputs (for I/0)
     *
     * routine is called on a regular (timed)interval and is controlled by the inputs  byte(s)
     * Commands are stored and executed if a change occurs compared to previous (call)
     * Also an error will cause a stop immediately
     * Total runtime is controlled and generates a error if exceeded >> in Error no remote commands are accepted only the local reset cmd
     *
     */
    uint8_t Bl_Control::Run_ctrl (void){
    
    	uint32_t tmp_l1 = 0;//Local scratch var
    
    //First Test for a pending error
    	if (_Error  ) {
    		_RunCtrlCmd=BL_RN_ERR;
    		_Runtime =0;//Stop internal timer
    		if (_ErrRunTmr == 0xff) {
    			_ErrRunTmr = 1;
    		} else {
    			_ErrRunTmr++;
    		}
    
    		return _RunCtrlCmd;//Exit here no need to continue
    	}else{
    		_ErrRunTmr=0;
    
    	}
    //TODO: interlocking the remote /local should be optimized
    //No errors: evaluate witch command must be executed check for (new) local/remote command
    	if (_Local_CMD !=_RunCtrlprevLocCmd ) {
    		//Local command different from current run state store in _RunCtrlCmd
    		_RunCtrlCmd = _Local_CMD; //Make the new stat the active one
    #if BL_DEBUG-1
    		Serial.print("BL_LC");
    		Serial.println(_RunCtrlCmd);
    #endif
    	}
    		else if (_Remote_CMD != _prev_rem_cmd) {
    #if BL_DEBUG-1
    			Serial.println("BL_RM");
    #endif
    			_RunCtrlCmd =_Remote_CMD;//copy the remote cmd to local
    	}else{
    		//Leave it there is no new cmd
    	}
    
    //Setpoint manipulator/Controller (depends on (new)input command
    	switch (_RunCtrlCmd) {
    	case BL_CMD_STOP:
    		_SP = _PV;//Assign the actual PV to the internal SP
    		break;
    	case BL_CMD_INC ://increase Setpoint
    		 //only increase if SP is smaller then max PV
    			if (_SP < (_Gain_PV)){
    				_SP++ ;
    				if (_Runtime ==0) _Runtime =1;
    			}
    
    		break;
    	case BL_CMD_UP://Setpoint to max
    		if (_SP != _Gain_PV) {
    			_SP = _Gain_PV ;
    			if (_Runtime ==0) _Runtime =1;			//Run ...Run
    		}
    		break;
    	case BL_CMD_DEC ://Setpoint decrease (tipping)
    		if (_SP != BL_SP_MIN){
    			if(_SP > (BL_SP_MIN) ){
    				_SP-- ;
    				if (_Runtime ==0) _Runtime =1;
    			}
    #if BL_DEBUG-1
    
    		Serial.print("DSP: ");
    		Serial.println(_SP,DEC );
    #endif
    		}
    		break;
    	case BL_CMD_DWN ://Setpoint to min
    		if (_SP != BL_SP_MIN) {
    			_SP = BL_SP_MIN;
    			if (_Runtime ==0) _Runtime =1;
    		}
    
    		break;
    	case BL_CMD_RUN :								//Setpoint received from host
    
    		tmp_l1 =(_Remote_SP * _Gain_PV /BL_SP_MAX);	//First calculate the scaled setpoint from the host
    		if (_SP != tmp_l1){							//is this a different setpoint ?
    
    			_SP = tmp_l1;
    #if BL_DEBUG-1
    				Serial.print("Sp: ");
    				Serial.println(_SP,DEC);
    				Serial.print("_PV: ");
    				Serial.println(_PV,DEC );
    #endif
    			if (_Runtime ==0) _Runtime =1;
    		}
    		break;
    	default:
    		break;
    	}
    
    
    //Position (PV) Generator  there is no feedback position so it is simulated by counters/timers
    	if (_SP ==( _PV)   ){
    		bitClear(_ouputs,  t_BL_OutBits::BL_RN_DWN);
    		bitClear(_ouputs,  t_BL_OutBits::BL_RN_UP);
    		_Runtime =0;//Stop
    	}else{
    
    		if ((_SP ) > (_PV) ){
    			bitClear(_ouputs,  t_BL_OutBits::BL_RN_DWN);
    			bitSet(_ouputs,  t_BL_OutBits::BL_RN_UP);
    			if ((_PV < _Gain_PV) && (_Runtime !=0))_PV++;
    
    		}else{
    			//(_SP < _PV)
    			bitClear(_ouputs,  t_BL_OutBits::BL_RN_UP);
    			bitSet(_ouputs,  t_BL_OutBits::BL_RN_DWN);
    			if (_PV>=BL_SP_MIN) _PV--;
    		}
    	}
    	// Feedback that there is still current flowing
    	if (bitRead(_inputs,t_BL_InBits::CURSNS )){
    		// We still are driving the outputs increase run time
    		if (_Runtime){
    			// FIX 0.3	: the line beneath uses a cast (uint32_t) on a constants omitting this will give you a few hours joy...
    			tmp_l1 =(uint32_t)BL_MAX_TIME_UP * _Gain_PV / (uint32_t)BL_SP_MAX ;	//First calculate the max (scaled) Run time
    #if BL_DEBUG -1
    			if (_Runtime ==2) {
    				Serial.print("Max_PV: ");
    				Serial.println(tmp_l1,DEC);
    			}
    #endif
    			if  ( _Runtime < tmp_l1)_Runtime ++;				//If not overtime increase timer
    #if BL_DEBUG -1
    				Serial.print("RT: ");
    				Serial.println(_Runtime,DEC);
    				Serial.print("PV: ");
    				Serial.println(_PV,DEC);
    #endif
    		}
    
    	}else{
    		//Current is dropped >> Motor off or (in this case) end limit reached
    		if ((  bitRead(_ouputs, t_BL_OutBits::BL_RN_UP ) ||bitRead(_ouputs, t_BL_OutBits::BL_RN_DWN )) && (_Runtime>WAITFORCURRENT) )
    		{
    //Todo: when testing this with feedback maybe this can be optimized to _PV = _SP; (as this will probably be true )
    			//When the current is gone we have reached a end limit
    			if (  bitRead(_ouputs, t_BL_OutBits::BL_RN_UP )){
    					bitClear(_ouputs,  t_BL_OutBits::BL_RN_UP);
    					_PV = _Gain_PV;//Reached the Max limit ...inform the host
    			}
    			if (  bitRead(_ouputs, t_BL_OutBits::BL_RN_DWN )){
    				bitClear(_ouputs,  t_BL_OutBits::BL_RN_DWN);
    				_PV = BL_SP_MIN;//Reached the min limit ...inform the host
    			}
    			//The current is gone but still driving UP/DOWN stop it (should be redundant here)
    			bitClear(_ouputs,  t_BL_OutBits::BL_RN_DWN);
    			bitClear(_ouputs,  t_BL_OutBits::BL_RN_UP);
    		}
    	}
    
    	// Guard the max run time (Up/down time)
    	if (_Runtime){
    		// FIX 0.3	: the line beneath uses a cast (uint32_t) on a constants omitting this will give you a few hours joy...
    		tmp_l1  = (uint32_t) BL_MAX_TIME_UP *_Gain_PV / (uint32_t)BL_SP_MAX;//First calculate the max (scaled) Run time
    
    		if ( _Runtime >= tmp_l1) {
    			bitClear(_ouputs,  t_BL_OutBits::BL_RN_DWN);	//Overrun (blokking?) >> stop
    			bitClear(_ouputs,  t_BL_OutBits::BL_RN_UP);
    			bitSet(_ouputs,  t_BL_OutBits::BL_RN_ERR);
    			_Error = true;
    #if BL_DEBUG-1
    			Serial.print("RT: ");
    			Serial.print(_Runtime);
    			Serial.print(" HL: ");
    			Serial.print(tmp_l1,DEC);
    			Serial.print(" G: ");
    			Serial.print(_Gain_PV);
    
    			Serial.println(" TIME_OUT ");
    #endif
    		}
    	}
    
    	if (_Local_CMD !=_RunCtrlprevLocCmd ) {					//Update the command switcher for next entry
    		_RunCtrlprevLocCmd = _Local_CMD;
    	}else if  (_Remote_CMD !=_prev_rem_cmd ){
    		_prev_rem_cmd = _Remote_CMD;
    	}
    	return _ouputs;// return  the Run_Result
    }
    /**
     * Get_PV	 	:	Returns the current ProcessValue (actual state) or feedback in %
     * inputs are 	: 	None
     * Changes		: 	None
     * 				:
     * Returns 		: 	_PV
     *
     */
    uint8_t Bl_Control::Get_PV (void){
    	return ((_PV*BL_SP_MAX)/_Gain_PV);
    }
    

    At last the header File "Bl_Control.h"

    // - function definitions (prototypes)
    // - include files
    // - extern variable definitions
    // In the appropriate section
    
    #ifndef _BL_Control_H_
    #define _BL_Control_H_
    #include "Arduino.h"
    //add your includes for the project BlindController here
    #include <inttypes.h>
    
    //end of add your includes here
    
    
    
    //add your function definitions for the project BlindController here
    
    #define BL_DEBUG 1			//Enable/disable debugging msg (to serial)
    
    #define UPDATE_INPUTS_ms 50UL	// scan time inputs for change
    
    #define WAITFORCURRENT 3 	//Allow the current to come up when outputs activate
    
    #define BL_T_Hold 5			// This is the number off scans before a button is considered as hold (Tipping)
    #define BL_SP_MIN 0x00		// SP_LL no comment....
    #define BL_SP_MAX 100		// SP_H This value is the value considered as 100% output
    
    
    #define BL_MAX_TIME_UP 250 	//With a scan time off 50mSec the this give only 12Sec (1000mSec/50 * t_Scan_SeC < 0xFF)
    							//on the other hand there is also a factor (
    
    #define BL_PV_MIN 10		// Min gain percentage (10%)
    #define BL_PV_MAX 1000UL	// Min gain percentage (1000%)
    
    
    
    #if BL_MAX_TIME_UP <= BL_SP_MAX //When changing parameters test if the SP is reachable without error
    	#error "Attention !! you can't reach the max allowed SP val in one run with this setting!"
    #endif
    
    typedef enum BL_OutBits  { BL_RN_UP, BL_RN_DWN, BL_RN_ERR}t_BL_OutBits;
    typedef enum BL_Cmd {BL_CMD_NONE, BL_CMD_STOP,BL_CMD_INC, BL_CMD_UP, BL_CMD_DEC ,BL_CMD_DWN,BL_CMD_RST ,BL_CMD_RUN} t_BL_Cmd ;
    
    typedef enum  BL_InBits{ INCR, DECR, CURSNS}t_BL_InBits;
    
    class Bl_Control
    {
    
    public:
    	// Create an instance of the BlindController 
    	Bl_Control(); 
    	// call this to run the timers
    	void bl_Process (void);
    	
    	//Get the outputbits
    	uint8_t bl_GetOutputs (void);
    	//Update info from the host (updates the remote cmd bits)
    	uint8_t UpdateRemote_cmd (uint8_t Sp, uint8_t cmd_Type );
    	//Set the inputs 
    	void bl_SetInputs (uint8_t inc , uint8_t dec, uint8_t RunCurrent  );
    	uint8_t bl_RunErrorLed (void);
    	uint8_t Get_PV (void);
    	uint16_t Get_Gain_PV ();
    	void Set_Gain_PV (uint16_t fakt);
    
    	
    
    private:
    	//SP and PV updater
    	uint8_t Run_ctrl (void);
    	// evaluate the inputcmd (timers)
    	uint8_t Update_Local_cmd (void);
      
    
    
      
      	
    protected:
    	 uint8_t 	_tmrLocalCmdup;				//Timer off the PB UP
    	 uint8_t  	_tmrLocalCmddwn;			//Timer off the PB DWN
    	 uint8_t 	_ErrRunTmr;					//Timer off the error/Run
    
    	 uint8_t 	_RunCtrlCmd;				//the active command off Controller
    	 uint8_t 	_RunCtrlprevLocCmd ;		//Store the previous local state
    
    	 uint8_t 	_prev_rem_cmd;				//Store the previous state at end off routine
    	 uint8_t 	_inputs;					//InputBits
    	 uint8_t  	_ouputs;					//OutputBits
    	 
    	 //timer logic to schedule updates ,cmd ,inputs,...
    	 unsigned long _previoustMillis_inp_scan ;
    	
    	uint8_t _Remote_SP; 					//Last received SP received from the Host
    	uint8_t _Remote_CMD ;					//Last received Command from the Host
    	uint8_t _Local_CMD ;					//Last received Command from the inputs
    	uint16_t _Runtime ;						//Time Controller is running
    	
    	uint16_t _SP ; 							//internal Setpoint
    	uint16_t _PV; 							//Global Process_Value (PV) aka the actual (or estimated) position
    		
    	uint8_t _Error;							//Module in error
    	uint16_t _Gain_PV ;						//Factor for _PV to reach the setpoint (in %)
    	
    };
    
    
    //Do not add code below this line
    #endif /* _BL_Control_H_ */
    
    


  • @TheoL

    Hello TheoL i think you are right, but it is hard to copy/past a project to a forum. But why not i changed the post and now almost everybody can see how crapy code i write 😃
    Regards,
    Stefan.


  • Contest Winner

    @stedew Thank you for embedding the code on the forum.

    I'm not sure what your question is, but I took a quick peek at the code. I haven't examined everything thoroughly. But here's something I noticed. I can tell you already know about C(++).

      pinMode(DI_BL1_UP, INPUT_PULLUP);
      pinMode(DI_BL1_DWN, INPUT_PULLUP);
      pinMode(AI_BL1_SENS,INPUT_PULLUP);
      //Set outputs to safe state and init
      digitalWrite( DO_BL1_MOTOR_UP,MOTOR_OFF);
      digitalWrite( DO_BL1_MOTOR_DWN,MOTOR_OFF);
      digitalWrite(DO_BL1_ERR_RUN ,MOTOR_OFF);
    
      pinMode(DO_BL1_MOTOR_UP, OUTPUT);
      pinMode(DO_BL1_MOTOR_DWN, OUTPUT);
      pinMode(DO_BL1_ERR_RUN, OUTPUT);
    
    //BLIND2
    //TODO: Test extra controller
      pinMode(DI_BL2_UP, INPUT_PULLUP);
      pinMode(DI_BL2_DWN, INPUT_PULLUP);
      //pinMode(AI_BL2_SENS,INPUT_PULLUP);
      //Set outputs to safe state and init
      digitalWrite( DO_BL2_MOTOR_UP,MOTOR_OFF);
      digitalWrite( DO_BL2_MOTOR_DWN,MOTOR_OFF);
      digitalWrite(DO_BL2_ERR_RUN ,MOTOR_OFF);
    
      pinMode(DO_BL2_MOTOR_UP, OUTPUT);
      pinMode(DO_BL2_MOTOR_DWN, OUTPUT);
      pinMode(DO_BL2_ERR_RUN, OUTPUT);
    

    If I remember correctly, the Arduino needs the pinMode state before you write to the pin. So it's better to turn them into this order:

      pinMode(DI_BL1_UP, INPUT_PULLUP);
      pinMode(DI_BL1_DWN, INPUT_PULLUP);
      pinMode(AI_BL1_SENS,INPUT_PULLUP);
      //Set outputs to safe state and init
      pinMode(DO_BL1_MOTOR_UP, OUTPUT);
      pinMode(DO_BL1_MOTOR_DWN, OUTPUT);
      pinMode(DO_BL1_ERR_RUN, OUTPUT);
    
      digitalWrite( DO_BL1_MOTOR_UP,MOTOR_OFF);
      digitalWrite( DO_BL1_MOTOR_DWN,MOTOR_OFF);
      digitalWrite(DO_BL1_ERR_RUN ,MOTOR_OFF);
    
    
    //BLIND2
    //TODO: Test extra controller
      pinMode(DI_BL2_UP, INPUT_PULLUP);
      pinMode(DI_BL2_DWN, INPUT_PULLUP);
      //pinMode(AI_BL2_SENS,INPUT_PULLUP);
      //Set outputs to safe state and init
    
      pinMode(DO_BL2_MOTOR_UP, OUTPUT);
      pinMode(DO_BL2_MOTOR_DWN, OUTPUT);
      pinMode(DO_BL2_ERR_RUN, OUTPUT);
    
      digitalWrite( DO_BL2_MOTOR_UP,MOTOR_OFF);
      digitalWrite( DO_BL2_MOTOR_DWN,MOTOR_OFF);
      digitalWrite(DO_BL2_ERR_RUN ,MOTOR_OFF);
    
    

    There's als a S_COVER cover type, which looks like a sensor type you've implemented. But I'm not sure if this type is supported by controllers.

    I really like what you did. But I'm not sure if it works. The .h file seems to lack the class definition. And it's been a while since a learned C++, but as I've always have learned the class definition needs to be put in a .h file.

    Also very curious about your decision not to include the logic for controlling the motors and pin initialization in the class itself but to keep it in the main sketch? It's not wrong but why do all the work for creating a class without putting the logic where it belongs (in the class).

    I do love OO programming. But for the Arduino I only use plain C. It consumes less memory.



  • Again right.. but the class definition is in the BL_Control.h file (line 47) The reason not to put the logic is that i yust want some sceleton for this kind of physical controller now i don' t have to include write and reads in the class and i can re-use it maybe for a PB dimmer. That off the declaration with the pinmodes is a habbit from the plain C period and assembler adventures with the AVR. There i set my outputs to a safe state before setting the DDR(x) register. As far as i assume the DDR is only relevant when you read the input (or want to do a logic operation on the input/output state. And it is overkill to write this in C++. But for me i consider it as a good excercise. And no i don't have a question I only wanted it to publish so others can steal maybe a idea Cheers,


Log in to reply
 

Suggested Topics

  • 8
  • 1
  • 29
  • 1
  • 2
  • 90

24
Online

11.2k
Users

11.1k
Topics

112.5k
Posts