Curtain Control Node.
-
@suresh-mali
I now reading about that funktions in http://www.airspayce.com/mikem/arduino/AccelStepper/classAccelStepper.html#a344f58fef8cc34ac5aa75ba4b665d21cDid what You said, but getting error: no matching function for call to 'AccelStepper::runToPosition(int)'
I paste here all code, but I think I did everything without mistakes:// Enable debug prints to serial monitor #define MY_DEBUG // Enable and select radio type attached #define MY_NODE_ID 10 #define MY_RADIO_NRF24 //#define MY_RADIO_RFM69 // Enable repeater functionality for this node //#define MY_REPEATER_FEATURE #include <SPI.h> #include <MySensors.h> #include <AccelStepper.h> #define HALFSTEP 8 #define CURTAIN_CLOSED 10000 #define CURTAIN_OPEN 0 #define CHILD_ID 1 // int powerPin = 7; // definicje MySensors MyMessage message(CHILD_ID, S_COVER); // Definicja pinow silnika #define IN1 3 // IN1 #define IN2 4 // IN2 #define IN3 5 // IN3 #define IN4 6 // IN4 AccelStepper stepper1(HALFSTEP, IN1, IN3, IN2, IN4); void setup() { stepper1.setMaxSpeed(1000.0); stepper1.setAcceleration(100.0); stepper1.setSpeed(200); stepper1.runToPosition(CURTAIN_CLOSED); } void presentation() { sendSketchInfo("Roller blinds", "1.0"); present(CHILD_ID, S_COVER); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP } void loop() { //stepper1.run(); //Start } void receive(const MyMessage &message) { stepper1.enableOutputs (); // if message = V_UP start moving until closed if (message.type==V_UP) { if (stepper1.distanceToGo() == 0){ if (stepper1.currentPosition() == CURTAIN_OPEN){ stepper1.runToPosition(CURTAIN_CLOSED); // Store state in eeprom saveState(message.sensor, message.getBool()); request(CHILD_ID, V_UP, 0); // request new values from controller } } } if (message.type==V_DOWN) { stepper1.moveTo(CURTAIN_OPEN); // Store state in eeprom saveState(message.sensor, message.getBool()); request(CHILD_ID, V_DOWN, 0); // request new values from controller } if (message.type==V_STOP) { stepper1.setCurrentPosition(0); // Store state in eeprom saveState(message.sensor, message.getBool()); request(CHILD_ID, V_STOP, 0); // request new values from controller } stepper1.disableOutputs (); }@jacikaas Sorry to revive an old thread, but did you get this resolved and if so how?
I just 3D printed my first motorised roller blind mounts and have tested with arduino stepper only.
Next is to go mysensors with it, but which way is best? Standard arduino stepper or accelstepper?
-
Okey. This is an old thread but I hope it's ok I keep it alive.
I just printed som parts for my blinds.
I use a stepper motor (28BYJ-48 with ULN2003 Driver board) and this sketch found in this thread.
The sketch is awesome.
To my quiestion.
I want to run 2 blinds at the same time from ONE node. Synchronized or maybe individual.
Is this possible?
There isn't any digital pins over for this when using button(s).
I think i saw someone using analog pin connected to the ULN2003 someware.
Is it possible to run 2 motors on one board?
Really hope somone can answer som of my question.
What modifications is needed in the sketch if this is possible.
This is the sketch I use./* PROJECT: MY Sensors curtain controller PROGRAMMER: AWI DATE: march 11, 2016 FILE: AWI stepper1.ino LICENSE: Public domain Hardware: ATMega328p board w/ NRF24l01 and MySensors 2.0 (Development) Special: uses AccelStepper library Summary: Curtain control with stepper motor. Manual operation with 1 push button Calibration with manual button Remarks: Fixed node-id Change log: 20160312 - Cleanup */ // Enable debug prints to serial monitor #define MY_DEBUG #define MY_NODE_ID 13 // fixed node number // Enable and select radio type attached #define MY_RADIO_RF24 //#define MY_RADIO_RFM69 #include <SPI.h> #include <MySensors.h> // stepper library #include <AccelStepper.h> // http://www.airspayce.com/mikem/arduino/AccelStepper/ #define HALFSTEP 8 // Stepper uses "Halfstep" mode // button library // used for: // - manual open close - single click: close/ stop/ open/ stop // - calibration - after long press: open calibarion- until single press - closed calibration - until single press (stop) #include <Button.h> // https://github.com/JChristensen/Button #define CHILD_ID 1 // Id of the sensor child #define SN "Curtain control 13" #define SV "1.0" #define buttonPin1 7 // Arduino pin connected to buttonPin1 #define buttonPin2 A0 // Arduino pin connected to buttonPin2 (fixed to ground) // Motor pin definitions #define motorPin1 3 // IN1 on the ULN2003 driver 1 #define motorPin2 4 // IN2 on the ULN2003 driver 1 #define motorPin3 5 // IN3 on the ULN2003 driver 1 #define motorPin4 6 // IN4 on the ULN2003 driver 1 const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ; // heartbeatinterval unsigned long heartbeatCounter = 0 ; // // helper routines to store and retrieve long in mysensors EEPROM union { // used to convert long to bytes for EEPROM storage long longInt; uint8_t LongByte[4]; } convLongInt ; void saveStateL(int EEposition, long StateL){ convLongInt.longInt = StateL ; for (int y = 0; y < 4 ; y++){ // convert to bytes saveState(EEposition + y , convLongInt.LongByte[y]) ; } Serial.print("State saved: "); Serial.println(StateL); } long loadStateL(int EEposition){ for (int y = 0; y < 4 ; y++){ // convert from bytes convLongInt.LongByte[y] = loadState(EEposition + y) ; } Serial.print("State read: "); Serial.println(convLongInt.longInt); return convLongInt.longInt ; } // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48 AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4); // Initialize button active low, debounce and internal pull-up Button myBtn(buttonPin1, true, true, 40); // Initiate the button (pin, pull_up, invert, debounce_ms) MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE); // used to send updates to controller const long maxRun = 4000000L ; // maximum runway long setPosition = 0 ; // remembers set position, need to be saved in EEPROM const int setPositionEE = 4 ; // eeprom location long openPosition = 0 ; // Position at open, need to be saved in EEPROM? const int openPositionEE = setPositionEE + 4 ; // eeprom location long closedPosition = 120000UL ; // Position at full close, need to be saved in EEPROM const int closedPositionEE = openPositionEE + 4 ; // eeprom location unsigned long idleTimer = millis() ; // return to idle timer unsigned long idleTime = 100000UL ; // return to idle after 100 secs unsigned long printTimer = millis() ; // print timer unsigned long printTime = 1000UL ; // print after 1 secs enum position_t {Open, Close, Idle, Running} ; position_t lastDirection = Open ; // lastDirection only for buttonpress position_t runStatus = Idle ; // indicates current status for running motor. used for status reporting to controller enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ; State_t State = sIdle ; void setup() { // setup buttons pinMode(buttonPin1, OUTPUT); stepper1.setMaxSpeed(2000.0); stepper1.setAcceleration(1000.0); //saveStateL(closedPositionEE, closedPosition) ; // INIT: save closed position in EEPROM closedPosition = loadStateL(closedPositionEE) ; // need to get last values from EEPROM and assume the current position is correct setPosition = loadStateL(setPositionEE) ; stepper1.setCurrentPosition(setPosition ); }//--(end setup )--- void presentation() { present(CHILD_ID, S_COVER, "Curtain"); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP // Register the LED Dimmable Light with the gateway sendSketchInfo(SN, SV); } void loop() { unsigned int now = millis() ; // current time for loop // simple state machine for button press myBtn.read(); switch (State) { // Idle state, waiting for some action // - button press // - idleTimer case sIdle: if (myBtn.wasReleased()){ // idle Serial.println("Button release") ; // if running stop if (stepper1.isRunning()){ setPosition = stepper1.currentPosition(); stepper1.moveTo(setPosition) ; // move to current position (was already there..) } else if (lastDirection == Open) { stepper1.moveTo(closedPosition) ; lastDirection = Close ; } else { // lastDirection == Close stepper1.moveTo(openPosition) ; lastDirection = Open ; } } else if (myBtn.pressedFor(3000)){ // move to calibratete state with long press Serial.println("Button press long") ; idleTimer = now ; // return to idle after ... State = sCalibrateOpen ; stepper1.move(-maxRun) ; // let the stepper open with maximum } break ; // if not running and last action was open close ; else open // if longpress Calibrate open case sCalibrateOpen: // calibration going on if (myBtn.wasPressed()){ stepper1.setCurrentPosition(0 ); // set new 0 position ?? openPosition = setPosition = 0 ; State = sCalibrateClose ; // next is close calibarion stepper1.move(maxRun) ; // let the stepper close with maximum } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; case sCalibrateClose: // calibrate closed position, end with keypress if (myBtn.wasPressed()) { closedPosition = setPosition = stepper1.currentPosition() ; saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM State = sIdle ; stepper1.moveTo(openPosition) ; // return to open after calibration } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; default : break ; } // power off stepper if not running (no need to reenable)) if (!stepper1.isRunning()){ if (runStatus != Idle){ // there was a change in runningstatus, so report to controller setPosition = stepper1.currentPosition() ; // store in EEPROM and report ready to controller saveStateL(setPositionEE, setPosition) ; send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ; runStatus = Idle ; } stepper1.disableOutputs(); } else { runStatus = Running ; } stepper1.run(); /* if (printTimer++ > now + printTime){ printTimer = now ; Serial.println(stepper1.currentPosition()); } */ } // This is called when a message is received void receive(const MyMessage &message) { // We only expect few types of messages from controller, check which switch (message.type) { case V_PERCENTAGE: // Curtain should be opened stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100); Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt()); Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100); break ; case V_STATUS: // Curtain should be opened or closed full stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition); Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(" , value: % "); break ; default : // not recognizable message Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(", Unrecognized "); break ; } } -
You can use an I/O expander like an MCP23017 which is a 16 port expander or a 8 port expander. They're cheap. Keep in mind they consume 1 mAmp. I'm currently working on a product to overcome that power issue for a battery powered Node. Will try to post about it in a while
-
Okey. This is an old thread but I hope it's ok I keep it alive.
I just printed som parts for my blinds.
I use a stepper motor (28BYJ-48 with ULN2003 Driver board) and this sketch found in this thread.
The sketch is awesome.
To my quiestion.
I want to run 2 blinds at the same time from ONE node. Synchronized or maybe individual.
Is this possible?
There isn't any digital pins over for this when using button(s).
I think i saw someone using analog pin connected to the ULN2003 someware.
Is it possible to run 2 motors on one board?
Really hope somone can answer som of my question.
What modifications is needed in the sketch if this is possible.
This is the sketch I use./* PROJECT: MY Sensors curtain controller PROGRAMMER: AWI DATE: march 11, 2016 FILE: AWI stepper1.ino LICENSE: Public domain Hardware: ATMega328p board w/ NRF24l01 and MySensors 2.0 (Development) Special: uses AccelStepper library Summary: Curtain control with stepper motor. Manual operation with 1 push button Calibration with manual button Remarks: Fixed node-id Change log: 20160312 - Cleanup */ // Enable debug prints to serial monitor #define MY_DEBUG #define MY_NODE_ID 13 // fixed node number // Enable and select radio type attached #define MY_RADIO_RF24 //#define MY_RADIO_RFM69 #include <SPI.h> #include <MySensors.h> // stepper library #include <AccelStepper.h> // http://www.airspayce.com/mikem/arduino/AccelStepper/ #define HALFSTEP 8 // Stepper uses "Halfstep" mode // button library // used for: // - manual open close - single click: close/ stop/ open/ stop // - calibration - after long press: open calibarion- until single press - closed calibration - until single press (stop) #include <Button.h> // https://github.com/JChristensen/Button #define CHILD_ID 1 // Id of the sensor child #define SN "Curtain control 13" #define SV "1.0" #define buttonPin1 7 // Arduino pin connected to buttonPin1 #define buttonPin2 A0 // Arduino pin connected to buttonPin2 (fixed to ground) // Motor pin definitions #define motorPin1 3 // IN1 on the ULN2003 driver 1 #define motorPin2 4 // IN2 on the ULN2003 driver 1 #define motorPin3 5 // IN3 on the ULN2003 driver 1 #define motorPin4 6 // IN4 on the ULN2003 driver 1 const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ; // heartbeatinterval unsigned long heartbeatCounter = 0 ; // // helper routines to store and retrieve long in mysensors EEPROM union { // used to convert long to bytes for EEPROM storage long longInt; uint8_t LongByte[4]; } convLongInt ; void saveStateL(int EEposition, long StateL){ convLongInt.longInt = StateL ; for (int y = 0; y < 4 ; y++){ // convert to bytes saveState(EEposition + y , convLongInt.LongByte[y]) ; } Serial.print("State saved: "); Serial.println(StateL); } long loadStateL(int EEposition){ for (int y = 0; y < 4 ; y++){ // convert from bytes convLongInt.LongByte[y] = loadState(EEposition + y) ; } Serial.print("State read: "); Serial.println(convLongInt.longInt); return convLongInt.longInt ; } // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48 AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4); // Initialize button active low, debounce and internal pull-up Button myBtn(buttonPin1, true, true, 40); // Initiate the button (pin, pull_up, invert, debounce_ms) MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE); // used to send updates to controller const long maxRun = 4000000L ; // maximum runway long setPosition = 0 ; // remembers set position, need to be saved in EEPROM const int setPositionEE = 4 ; // eeprom location long openPosition = 0 ; // Position at open, need to be saved in EEPROM? const int openPositionEE = setPositionEE + 4 ; // eeprom location long closedPosition = 120000UL ; // Position at full close, need to be saved in EEPROM const int closedPositionEE = openPositionEE + 4 ; // eeprom location unsigned long idleTimer = millis() ; // return to idle timer unsigned long idleTime = 100000UL ; // return to idle after 100 secs unsigned long printTimer = millis() ; // print timer unsigned long printTime = 1000UL ; // print after 1 secs enum position_t {Open, Close, Idle, Running} ; position_t lastDirection = Open ; // lastDirection only for buttonpress position_t runStatus = Idle ; // indicates current status for running motor. used for status reporting to controller enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ; State_t State = sIdle ; void setup() { // setup buttons pinMode(buttonPin1, OUTPUT); stepper1.setMaxSpeed(2000.0); stepper1.setAcceleration(1000.0); //saveStateL(closedPositionEE, closedPosition) ; // INIT: save closed position in EEPROM closedPosition = loadStateL(closedPositionEE) ; // need to get last values from EEPROM and assume the current position is correct setPosition = loadStateL(setPositionEE) ; stepper1.setCurrentPosition(setPosition ); }//--(end setup )--- void presentation() { present(CHILD_ID, S_COVER, "Curtain"); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP // Register the LED Dimmable Light with the gateway sendSketchInfo(SN, SV); } void loop() { unsigned int now = millis() ; // current time for loop // simple state machine for button press myBtn.read(); switch (State) { // Idle state, waiting for some action // - button press // - idleTimer case sIdle: if (myBtn.wasReleased()){ // idle Serial.println("Button release") ; // if running stop if (stepper1.isRunning()){ setPosition = stepper1.currentPosition(); stepper1.moveTo(setPosition) ; // move to current position (was already there..) } else if (lastDirection == Open) { stepper1.moveTo(closedPosition) ; lastDirection = Close ; } else { // lastDirection == Close stepper1.moveTo(openPosition) ; lastDirection = Open ; } } else if (myBtn.pressedFor(3000)){ // move to calibratete state with long press Serial.println("Button press long") ; idleTimer = now ; // return to idle after ... State = sCalibrateOpen ; stepper1.move(-maxRun) ; // let the stepper open with maximum } break ; // if not running and last action was open close ; else open // if longpress Calibrate open case sCalibrateOpen: // calibration going on if (myBtn.wasPressed()){ stepper1.setCurrentPosition(0 ); // set new 0 position ?? openPosition = setPosition = 0 ; State = sCalibrateClose ; // next is close calibarion stepper1.move(maxRun) ; // let the stepper close with maximum } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; case sCalibrateClose: // calibrate closed position, end with keypress if (myBtn.wasPressed()) { closedPosition = setPosition = stepper1.currentPosition() ; saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM State = sIdle ; stepper1.moveTo(openPosition) ; // return to open after calibration } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; default : break ; } // power off stepper if not running (no need to reenable)) if (!stepper1.isRunning()){ if (runStatus != Idle){ // there was a change in runningstatus, so report to controller setPosition = stepper1.currentPosition() ; // store in EEPROM and report ready to controller saveStateL(setPositionEE, setPosition) ; send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ; runStatus = Idle ; } stepper1.disableOutputs(); } else { runStatus = Running ; } stepper1.run(); /* if (printTimer++ > now + printTime){ printTimer = now ; Serial.println(stepper1.currentPosition()); } */ } // This is called when a message is received void receive(const MyMessage &message) { // We only expect few types of messages from controller, check which switch (message.type) { case V_PERCENTAGE: // Curtain should be opened stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100); Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt()); Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100); break ; case V_STATUS: // Curtain should be opened or closed full stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition); Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(" , value: % "); break ; default : // not recognizable message Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(", Unrecognized "); break ; } } -
DC Motor, very interesting!
Do you have end stops? The sketch doesn't open for some reason...
Cheers
@barduino said in Curtain Control Node.:
DC Motor, very interesting!
Do you have end stops? The sketch doesn't open for some reason...
Cheers
I have been working on a similar project last week. The mechanics and ´looks´ are the most difficult part for me.
-
@ton-rijnaard
Hi. This was a problem for me to. The reason is the library changed name and updates was made.
I use the library version 0.9.
I have like zero skills in arduino. If you use the old library the button will work with the sketch i posted a moth ago.
Looke here: https://github.com/JChristensen/JC_Button/releases
Maybe some of the other older releases will work. Don't remember why o choose V0.9
Look at 2.0.0, "This is a major release that is not backwards-compatible with previous releases." -
Okey. This is an old thread but I hope it's ok I keep it alive.
I just printed som parts for my blinds.
I use a stepper motor (28BYJ-48 with ULN2003 Driver board) and this sketch found in this thread.
The sketch is awesome.
To my quiestion.
I want to run 2 blinds at the same time from ONE node. Synchronized or maybe individual.
Is this possible?
There isn't any digital pins over for this when using button(s).
I think i saw someone using analog pin connected to the ULN2003 someware.
Is it possible to run 2 motors on one board?
Really hope somone can answer som of my question.
What modifications is needed in the sketch if this is possible.
This is the sketch I use./* PROJECT: MY Sensors curtain controller PROGRAMMER: AWI DATE: march 11, 2016 FILE: AWI stepper1.ino LICENSE: Public domain Hardware: ATMega328p board w/ NRF24l01 and MySensors 2.0 (Development) Special: uses AccelStepper library Summary: Curtain control with stepper motor. Manual operation with 1 push button Calibration with manual button Remarks: Fixed node-id Change log: 20160312 - Cleanup */ // Enable debug prints to serial monitor #define MY_DEBUG #define MY_NODE_ID 13 // fixed node number // Enable and select radio type attached #define MY_RADIO_RF24 //#define MY_RADIO_RFM69 #include <SPI.h> #include <MySensors.h> // stepper library #include <AccelStepper.h> // http://www.airspayce.com/mikem/arduino/AccelStepper/ #define HALFSTEP 8 // Stepper uses "Halfstep" mode // button library // used for: // - manual open close - single click: close/ stop/ open/ stop // - calibration - after long press: open calibarion- until single press - closed calibration - until single press (stop) #include <Button.h> // https://github.com/JChristensen/Button #define CHILD_ID 1 // Id of the sensor child #define SN "Curtain control 13" #define SV "1.0" #define buttonPin1 7 // Arduino pin connected to buttonPin1 #define buttonPin2 A0 // Arduino pin connected to buttonPin2 (fixed to ground) // Motor pin definitions #define motorPin1 3 // IN1 on the ULN2003 driver 1 #define motorPin2 4 // IN2 on the ULN2003 driver 1 #define motorPin3 5 // IN3 on the ULN2003 driver 1 #define motorPin4 6 // IN4 on the ULN2003 driver 1 const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ; // heartbeatinterval unsigned long heartbeatCounter = 0 ; // // helper routines to store and retrieve long in mysensors EEPROM union { // used to convert long to bytes for EEPROM storage long longInt; uint8_t LongByte[4]; } convLongInt ; void saveStateL(int EEposition, long StateL){ convLongInt.longInt = StateL ; for (int y = 0; y < 4 ; y++){ // convert to bytes saveState(EEposition + y , convLongInt.LongByte[y]) ; } Serial.print("State saved: "); Serial.println(StateL); } long loadStateL(int EEposition){ for (int y = 0; y < 4 ; y++){ // convert from bytes convLongInt.LongByte[y] = loadState(EEposition + y) ; } Serial.print("State read: "); Serial.println(convLongInt.longInt); return convLongInt.longInt ; } // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48 AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4); // Initialize button active low, debounce and internal pull-up Button myBtn(buttonPin1, true, true, 40); // Initiate the button (pin, pull_up, invert, debounce_ms) MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE); // used to send updates to controller const long maxRun = 4000000L ; // maximum runway long setPosition = 0 ; // remembers set position, need to be saved in EEPROM const int setPositionEE = 4 ; // eeprom location long openPosition = 0 ; // Position at open, need to be saved in EEPROM? const int openPositionEE = setPositionEE + 4 ; // eeprom location long closedPosition = 120000UL ; // Position at full close, need to be saved in EEPROM const int closedPositionEE = openPositionEE + 4 ; // eeprom location unsigned long idleTimer = millis() ; // return to idle timer unsigned long idleTime = 100000UL ; // return to idle after 100 secs unsigned long printTimer = millis() ; // print timer unsigned long printTime = 1000UL ; // print after 1 secs enum position_t {Open, Close, Idle, Running} ; position_t lastDirection = Open ; // lastDirection only for buttonpress position_t runStatus = Idle ; // indicates current status for running motor. used for status reporting to controller enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ; State_t State = sIdle ; void setup() { // setup buttons pinMode(buttonPin1, OUTPUT); stepper1.setMaxSpeed(2000.0); stepper1.setAcceleration(1000.0); //saveStateL(closedPositionEE, closedPosition) ; // INIT: save closed position in EEPROM closedPosition = loadStateL(closedPositionEE) ; // need to get last values from EEPROM and assume the current position is correct setPosition = loadStateL(setPositionEE) ; stepper1.setCurrentPosition(setPosition ); }//--(end setup )--- void presentation() { present(CHILD_ID, S_COVER, "Curtain"); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP // Register the LED Dimmable Light with the gateway sendSketchInfo(SN, SV); } void loop() { unsigned int now = millis() ; // current time for loop // simple state machine for button press myBtn.read(); switch (State) { // Idle state, waiting for some action // - button press // - idleTimer case sIdle: if (myBtn.wasReleased()){ // idle Serial.println("Button release") ; // if running stop if (stepper1.isRunning()){ setPosition = stepper1.currentPosition(); stepper1.moveTo(setPosition) ; // move to current position (was already there..) } else if (lastDirection == Open) { stepper1.moveTo(closedPosition) ; lastDirection = Close ; } else { // lastDirection == Close stepper1.moveTo(openPosition) ; lastDirection = Open ; } } else if (myBtn.pressedFor(3000)){ // move to calibratete state with long press Serial.println("Button press long") ; idleTimer = now ; // return to idle after ... State = sCalibrateOpen ; stepper1.move(-maxRun) ; // let the stepper open with maximum } break ; // if not running and last action was open close ; else open // if longpress Calibrate open case sCalibrateOpen: // calibration going on if (myBtn.wasPressed()){ stepper1.setCurrentPosition(0 ); // set new 0 position ?? openPosition = setPosition = 0 ; State = sCalibrateClose ; // next is close calibarion stepper1.move(maxRun) ; // let the stepper close with maximum } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; case sCalibrateClose: // calibrate closed position, end with keypress if (myBtn.wasPressed()) { closedPosition = setPosition = stepper1.currentPosition() ; saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM State = sIdle ; stepper1.moveTo(openPosition) ; // return to open after calibration } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; default : break ; } // power off stepper if not running (no need to reenable)) if (!stepper1.isRunning()){ if (runStatus != Idle){ // there was a change in runningstatus, so report to controller setPosition = stepper1.currentPosition() ; // store in EEPROM and report ready to controller saveStateL(setPositionEE, setPosition) ; send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ; runStatus = Idle ; } stepper1.disableOutputs(); } else { runStatus = Running ; } stepper1.run(); /* if (printTimer++ > now + printTime){ printTimer = now ; Serial.println(stepper1.currentPosition()); } */ } // This is called when a message is received void receive(const MyMessage &message) { // We only expect few types of messages from controller, check which switch (message.type) { case V_PERCENTAGE: // Curtain should be opened stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100); Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt()); Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100); break ; case V_STATUS: // Curtain should be opened or closed full stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition); Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(" , value: % "); break ; default : // not recognizable message Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(", Unrecognized "); break ; } }@xydix
To answer my own question the stepper runs fine from the analog pins.
Next step is to find some intersted in the same thing and see if we could get the sketch adjusted to run two steppers.
I will try when i get some time but I guess I will have problem to clone the calibration and store-to-eerprom-part. -
@patchmaster This is my current curtain control sketch. I work with the development branch, so the syntax can be a little different.
The Accelstepper library gives you all the abilities to slow start/stop with numerous kinds of steppers.
My sketch includes a calibration function (stored in EEPROM) so the you don't need end stop detection (a stepper knows where it is)/* PROJECT: MY Sensors curtain controller PROGRAMMER: AWI DATE: march 11, 2016 FILE: AWI stepper1.ino LICENSE: Public domain Hardware: ATMega328p board w/ NRF24l01 and MySensors 2.0 (Development) Special: uses AccelStepper library Summary: Curtain control with stepper motor. Manual operation with 1 push button Calibration with manual button Remarks: Fixed node-id Change log: 20160312 - Cleanup */ // Enable debug prints to serial monitor #define MY_DEBUG #define MY_NODE_ID 13 // fixed node number // Enable and select radio type attached #define MY_RADIO_NRF24 //#define MY_RADIO_RFM69 #include <SPI.h> #include <MySensor.h> // stepper library #include <AccelStepper.h> // http://www.airspayce.com/mikem/arduino/AccelStepper/ #define HALFSTEP 8 // Stepper uses "Halfstep" mode // button library // used for: // - manual open close - single click: close/ stop/ open/ stop // - calibration - after long press: open calibarion- until single press - closed calibration - until single press (stop) #include <Button.h> // https://github.com/JChristensen/Button #define CHILD_ID 1 // Id of the sensor child #define SN "Curtain control 13" #define SV "1.0" #define buttonPin1 7 // Arduino pin connected to buttonPin1 #define buttonPin2 A0 // Arduino pin connected to buttonPin2 (fixed to ground) // Motor pin definitions #define motorPin1 3 // IN1 on the ULN2003 driver 1 #define motorPin2 4 // IN2 on the ULN2003 driver 1 #define motorPin3 5 // IN3 on the ULN2003 driver 1 #define motorPin4 6 // IN4 on the ULN2003 driver 1 const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ; // heartbeatinterval unsigned long heartbeatCounter = 0 ; // // helper routines to store and retrieve long in mysensors EEPROM union { // used to convert long to bytes for EEPROM storage long longInt; uint8_t LongByte[4]; } convLongInt ; void saveStateL(int EEposition, long StateL){ convLongInt.longInt = StateL ; for (int y = 0; y < 4 ; y++){ // convert to bytes saveState(EEposition + y , convLongInt.LongByte[y]) ; } Serial.print("State saved: "); Serial.println(StateL); } long loadStateL(int EEposition){ for (int y = 0; y < 4 ; y++){ // convert from bytes convLongInt.LongByte[y] = loadState(EEposition + y) ; } Serial.print("State read: "); Serial.println(convLongInt.longInt); return convLongInt.longInt ; } // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48 AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4); // Initialize button active low, debounce and internal pull-up Button myBtn(buttonPin1, true, true, 40); // Initiate the button (pin, pull_up, invert, debounce_ms) MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE); // used to send updates to controller const long maxRun = 4000000L ; // maximum runway long setPosition = 0 ; // remembers set position, need to be saved in EEPROM const int setPositionEE = 4 ; // eeprom location long openPosition = 0 ; // Position at open, need to be saved in EEPROM? const int openPositionEE = setPositionEE + 4 ; // eeprom location long closedPosition = 120000UL ; // Position at full close, need to be saved in EEPROM const int closedPositionEE = openPositionEE + 4 ; // eeprom location unsigned long idleTimer = millis() ; // return to idle timer unsigned long idleTime = 100000UL ; // return to idle after 100 secs unsigned long printTimer = millis() ; // print timer unsigned long printTime = 1000UL ; // print after 1 secs enum position_t {Open, Close, Idle, Running} ; position_t lastDirection = Open ; // lastDirection only for buttonpress position_t runStatus = Idle ; // indicates current status for running motor. used for status reporting to controller enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ; State_t State = sIdle ; void setup() { // setup buttons pinMode(buttonPin1, OUTPUT); stepper1.setMaxSpeed(2000.0); stepper1.setAcceleration(1000.0); //saveStateL(closedPositionEE, closedPosition) ; // INIT: save closed position in EEPROM closedPosition = loadStateL(closedPositionEE) ; // need to get last values from EEPROM and assume the current position is correct setPosition = loadStateL(setPositionEE) ; stepper1.setCurrentPosition(setPosition ); }//--(end setup )--- void presentation() { present(CHILD_ID, S_COVER, "Curtain"); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP // Register the LED Dimmable Light with the gateway sendSketchInfo(SN, SV); } void loop() { unsigned int now = millis() ; // current time for loop // simple state machine for button press myBtn.read(); switch (State) { // Idle state, waiting for some action // - button press // - idleTimer case sIdle: if (myBtn.wasReleased()){ // idle Serial.println("Button release") ; // if running stop if (stepper1.isRunning()){ setPosition = stepper1.currentPosition(); stepper1.moveTo(setPosition) ; // move to current position (was already there..) } else if (lastDirection == Open) { stepper1.moveTo(closedPosition) ; lastDirection = Close ; } else { // lastDirection == Close stepper1.moveTo(openPosition) ; lastDirection = Open ; } } else if (myBtn.pressedFor(3000)){ // move to calibratete state with long press Serial.println("Button press long") ; idleTimer = now ; // return to idle after ... State = sCalibrateOpen ; stepper1.move(-maxRun) ; // let the stepper open with maximum } break ; // if not running and last action was open close ; else open // if longpress Calibrate open case sCalibrateOpen: // calibration going on if (myBtn.wasPressed()){ stepper1.setCurrentPosition(0 ); // set new 0 position ?? openPosition = setPosition = 0 ; State = sCalibrateClose ; // next is close calibarion stepper1.move(maxRun) ; // let the stepper close with maximum } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; case sCalibrateClose: // calibrate closed position, end with keypress if (myBtn.wasPressed()) { closedPosition = setPosition = stepper1.currentPosition() ; saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM State = sIdle ; stepper1.moveTo(openPosition) ; // return to open after calibration } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; default : break ; } // power off stepper if not running (no need to reenable)) if (!stepper1.isRunning()){ if (runStatus != Idle){ // there was a change in runningstatus, so report to controller setPosition = stepper1.currentPosition() ; // store in EEPROM and report ready to controller saveStateL(setPositionEE, setPosition) ; send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ; runStatus = Idle ; } stepper1.disableOutputs(); } else { runStatus = Running ; } stepper1.run(); /* if (printTimer++ > now + printTime){ printTimer = now ; Serial.println(stepper1.currentPosition()); } */ } // This is called when a message is received void receive(const MyMessage &message) { // We only expect few types of messages from controller, check which switch (message.type) { case V_PERCENTAGE: // Curtain should be opened stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100); Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt()); Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100); break ; case V_STATUS: // Curtain should be opened or closed full stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition); Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(" , value: % "); break ; default : // not recognizable message Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(", Unrecognized "); break ; } }@AWI Sorry for butting in to an old thread, but automating my blinds is top priority!
I've copied your sketch and have everything setup - however, I have eratic behaviour and a few questions:
void setup() { // setup buttons pinMode(buttonPin1, OUTPUT); stepper1.setMaxSpeed(2000.0); stepper1.setAcceleration(1000.0); //saveStateL(closedPositionEE, closedPosition) ; // INIT: save closed position in EEPROM closedPosition = loadStateL(closedPositionEE) ; // need to get last values from EEPROM and assume the current position is correct setPosition = loadStateL(setPositionEE) ; stepper1.setCurrentPosition(setPosition ); }//--(end setup )----
Should the pinMode on buttonPin1 actually be an INPUT?
-
Can you please describe how to go about calibrating the system?
-
Does the //saveStateL(closedPositionEE, closedPosition);
need uncommenting for one boot of the arduino and the blind closed - then the sketch re-uploading with it commented again?
Your help is greatly appreciated as I have a twitching motor and feel I'm almost there!
-
-
@AWI Sorry for butting in to an old thread, but automating my blinds is top priority!
I've copied your sketch and have everything setup - however, I have eratic behaviour and a few questions:
void setup() { // setup buttons pinMode(buttonPin1, OUTPUT); stepper1.setMaxSpeed(2000.0); stepper1.setAcceleration(1000.0); //saveStateL(closedPositionEE, closedPosition) ; // INIT: save closed position in EEPROM closedPosition = loadStateL(closedPositionEE) ; // need to get last values from EEPROM and assume the current position is correct setPosition = loadStateL(setPositionEE) ; stepper1.setCurrentPosition(setPosition ); }//--(end setup )----
Should the pinMode on buttonPin1 actually be an INPUT?
-
Can you please describe how to go about calibrating the system?
-
Does the //saveStateL(closedPositionEE, closedPosition);
need uncommenting for one boot of the arduino and the blind closed - then the sketch re-uploading with it commented again?
Your help is greatly appreciated as I have a twitching motor and feel I'm almost there!
@adds666 What i can tell it was a long time since @AWI was active here.
I use this sketch with my first bild since a couple of days.
For me it works like a charm. But in my case I control the motor from analog pins just to test and it seems OK.
No problem with the button.
The only thing I don't like, is the calibration.
I my case, using this on a blind, when calibrating, first it goes down, then i press the button, the blind stops at once and change it's direction and goes up. But when i reach the top level and want to stop it and save the state, it stops slow and pass the point i pressed the button and then reverse to the right spot. Problem is, the blind can't go further because it is in the top position.
If it would be the other way around it wouldn't be a problem.
Maybe i have the motor one the "wrong" side of the blind but when it is down the "downkey" in my HA is greyed out.
When it i up the "upkey" it greyed out so that seems right.What is not working in your case?
What motor do you use? I use 28BYJ-48 with ULN2003 and had to setstepper1.setMaxSpeed(2000.0);tostepper1.setMaxSpeed(1000.0);because the motor "slipped" at a speed of 2000.
Like i wrote earlier in the thread I tried to figure out how to "dublicate" the sketch and ute it for two motors.
I really don't know where to start to get this done. It would be awesome to use one arduino with two blinds. -
-
Hi @xydix , glad to hear you have the sketch working properly. Do you have the pinmode as OUTPUT or did you change to input?
And do you still have the saveStateL(closedPositionEE, closedPosition); line commented out?
If I change the pinMode to input, I get the system powered up and connected to the mysensors gateway fine - serial print repeats 'Button Release'. If I short press the button, the system does nothing. If I hold the button for 3000ms serial print reads 'Button Long Press' and the motor turns - however If I let go of the button, the motor stop and system returns to its 'button release' state.
I'm not able to get the system in to calibration mode.
-
Hi @xydix , glad to hear you have the sketch working properly. Do you have the pinmode as OUTPUT or did you change to input?
And do you still have the saveStateL(closedPositionEE, closedPosition); line commented out?
If I change the pinMode to input, I get the system powered up and connected to the mysensors gateway fine - serial print repeats 'Button Release'. If I short press the button, the system does nothing. If I hold the button for 3000ms serial print reads 'Button Long Press' and the motor turns - however If I let go of the button, the motor stop and system returns to its 'button release' state.
I'm not able to get the system in to calibration mode.
@adds666
I am still in "testing mode".
I had some problems with the node, it stoped receiving messages but did always work with the button.
Yesterday i built a new node and uploaded the sketch and it seems to work better.
Here is the sketch i use, I changed the pin for the button due to missing digital pins on my pcb.
And I am back on using digital pins for the stepper driver for the same reason.
I greyed out buttonpin 2. I don't think it is needed.
I also greyed out the heartbeat-part as I had problems. Just for testing. Don't know if it is suitable when using Home Assistant./* PROJECT: MY Sensors curtain controller PROGRAMMER: AWI DATE: march 11, 2016 FILE: AWI stepper1.ino LICENSE: Public domain Hardware: ATMega328p board w/ NRF24l01 and MySensors 2.0 (Development) Special: uses AccelStepper library Summary: Curtain control with stepper motor. Manual operation with 1 push button Calibration with manual button Remarks: Fixed node-id Change log: 20160312 - Cleanup */ // Enable debug prints to serial monitor #define MY_DEBUG #define MY_NODE_ID 8 // fixed node number // Enable and select radio type attached #define MY_RADIO_RF24 #define MY_RF24_PA_LEVEL RF24_PA_HIGH //#define MY_RADIO_RFM69 #include <SPI.h> #include <MySensors.h> // stepper library #include <AccelStepper.h> // http://www.airspayce.com/mikem/arduino/AccelStepper/ #define HALFSTEP 8 // Stepper uses "Halfstep" mode // button library // used for: // - manual open close - single click: close/ stop/ open/ stop // - calibration - after long press: open calibarion- until single press - closed calibration - until single press (stop) #include <Button.h> // https://github.com/JChristensen/Button - Använd gammal version. testat med V0.9 och det funkar #define CHILD_ID 1 // Id of the sensor child #define SN "Curtain control" #define SV "1.0" #define buttonPin1 A0 // Arduino pin connected to buttonPin1 //#define buttonPin2 A0 // Arduino pin connected to buttonPin2 (fixed to ground) // Motor pin definitions #define motorPin1 2 // IN1 on the ULN2003 driver 1 #define motorPin2 3 // IN2 on the ULN2003 driver 1 #define motorPin3 5 // IN3 on the ULN2003 driver 1 #define motorPin4 6 // IN4 on the ULN2003 driver 1 //const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ; // heartbeatinterval //unsigned long heartbeatCounter = 0 ; // // helper routines to store and retrieve long in mysensors EEPROM union { // used to convert long to bytes for EEPROM storage long longInt; uint8_t LongByte[4]; } convLongInt ; void saveStateL(int EEposition, long StateL){ convLongInt.longInt = StateL ; for (int y = 0; y < 4 ; y++){ // convert to bytes saveState(EEposition + y , convLongInt.LongByte[y]) ; } Serial.print("State saved: "); Serial.println(StateL); } long loadStateL(int EEposition){ for (int y = 0; y < 4 ; y++){ // convert from bytes convLongInt.LongByte[y] = loadState(EEposition + y) ; } Serial.print("State read: "); Serial.println(convLongInt.longInt); return convLongInt.longInt ; } // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48 AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4); // Initialize button active low, debounce and internal pull-up Button myBtn(buttonPin1, true, true, 40); // Initiate the button (pin, pull_up, invert, debounce_ms) MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE); // used to send updates to controller const long maxRun = 4000000L ; // maximum runway long setPosition = 0 ; // remembers set position, need to be saved in EEPROM const int setPositionEE = 4 ; // eeprom location long openPosition = 0 ; // Position at open, need to be saved in EEPROM? const int openPositionEE = setPositionEE + 4 ; // eeprom location long closedPosition = 120000UL ; // Position at full close, need to be saved in EEPROM const int closedPositionEE = openPositionEE + 4 ; // eeprom location unsigned long idleTimer = millis() ; // return to idle timer unsigned long idleTime = 100000UL ; // return to idle after 100 secs unsigned long printTimer = millis() ; // print timer unsigned long printTime = 1000UL ; // print after 1 secs enum position_t {Open, Close, Idle, Running} ; position_t lastDirection = Open ; // lastDirection only for buttonpress position_t runStatus = Idle ; // indicates current status for running motor. used for status reporting to controller enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ; State_t State = sIdle ; void setup() { // setup buttons pinMode(buttonPin1, OUTPUT); stepper1.setMaxSpeed(1000.0); stepper1.setAcceleration(1000.0); //saveStateL(closedPositionEE, closedPosition) ; // INIT: save closed position in EEPROM closedPosition = loadStateL(closedPositionEE) ; // need to get last values from EEPROM and assume the current position is correct setPosition = loadStateL(setPositionEE) ; stepper1.setCurrentPosition(setPosition ); }//--(end setup )--- void presentation() { present(CHILD_ID, S_COVER, "Curtain"); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP // Register the LED Dimmable Light with the gateway sendSketchInfo(SN, SV); } void loop() { unsigned int now = millis() ; // current time for loop // simple state machine for button press myBtn.read(); switch (State) { // Idle state, waiting for some action // - button press // - idleTimer case sIdle: if (myBtn.wasReleased()){ // idle Serial.println("Button release") ; // if running stop if (stepper1.isRunning()){ setPosition = stepper1.currentPosition(); stepper1.moveTo(setPosition) ; // move to current position (was already there..) } else if (lastDirection == Open) { stepper1.moveTo(closedPosition) ; lastDirection = Close ; } else { // lastDirection == Close stepper1.moveTo(openPosition) ; lastDirection = Open ; } } else if (myBtn.pressedFor(3000)){ // move to calibratete state with long press Serial.println("Button press long") ; idleTimer = now ; // return to idle after ... State = sCalibrateOpen ; stepper1.move(-maxRun) ; // let the stepper open with maximum } break ; // if not running and last action was open close ; else open // if longpress Calibrate open case sCalibrateOpen: // calibration going on if (myBtn.wasPressed()){ stepper1.setCurrentPosition(0 ); // set new 0 position ?? openPosition = setPosition = 0 ; State = sCalibrateClose ; // next is close calibarion stepper1.move(maxRun) ; // let the stepper close with maximum } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; case sCalibrateClose: // calibrate closed position, end with keypress if (myBtn.wasPressed()) { closedPosition = setPosition = stepper1.currentPosition() ; saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM State = sIdle ; stepper1.moveTo(openPosition) ; // return to open after calibration } else if (now > idleTimer + idleTime) { // timer expired -> abort calibration State = sIdle ; } break ; default : break ; } // power off stepper if not running (no need to reenable)) if (!stepper1.isRunning()){ if (runStatus != Idle){ // there was a change in runningstatus, so report to controller setPosition = stepper1.currentPosition() ; // store in EEPROM and report ready to controller saveStateL(setPositionEE, setPosition) ; send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ; runStatus = Idle ; } stepper1.disableOutputs(); } else { runStatus = Running ; } stepper1.run(); /* if (printTimer++ > now + printTime){ printTimer = now ; Serial.println(stepper1.currentPosition()); } */ } // This is called when a message is received void receive(const MyMessage &message) { // We only expect few types of messages from controller, check which switch (message.type) { case V_PERCENTAGE: // Curtain should be opened stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100); Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt()); Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100); break ; case V_STATUS: // Curtain should be opened or closed full stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition); Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(" , value: % "); break ; default : // not recognizable message Serial.print("Message - valid: "); Serial.print(message.sensor); Serial.print(", Unrecognized "); break ; } }