[Release] HubDuino v1.1.9 - Hubitat to Arduino / ESP8266 / ESP32 / ThingShield Integration (ST_Anything)

I hate to ask a stupid question but you are attaching power when they are free floating (gear unconnected), right?

Output is free on the bench when powered up and when running. Is your lower end of travel set to 0 degrees? At 15 degrees, mine did not bind up. Anything less than that caused a bounce.

I could have proceeded using a range of 15-180 degrees, but did not want to waste 30+ degrees of travel on the upper end. If you have a servo that is not installed, set it to 180 degrees and then detach. See how much extra rotation you can get out of the servo rotating by hand until you hit the physical stop.

I don't have an extra one of those unfortunately. Just a few mini servos. I do have mine set for 0-180. I can't see any overdriving. But I will try the same sketch with one of the mini servos and see what I get.

Oh...one other question...what type of board are you using?

Yeah....I am seeing the same thing with an SG90 servo. I didn't even notice it since mine were already tied into my blinds. Very strange!

I have my constructor in the sketch set up like this:

static st::EX_Servo executor1(F("servo1"), PIN_SERVO_1, 0, true, 800, 0, 180, 0);

What I'm actually seeing though, is that it is going past 180 degrees up to like 200. Then it's coming back to zero fine. But if you have it hooked to a setup that only allows 0-180, it's going to be short on the back trip. When I have the constructor set to 0-150 I get 180 degrees of travel between 0% and 100%. So, I don't think you're getting "jipped" out of any rotation.

Okay, so I don't think it was introduced in the last update. Because I just went back in and tried the old servo library. Anything over 50% results in about a 10% "backslide" after the servo is adjusted. But, if I disable the "servo detach", the problem goes away. Now, I don't really want my servos running 24 hours a day...they'll burn out pretty damn quick. So, I'm not sure what's going on to be honest.

I can confirm that this is backwards compatible and does appear to fix the problem.

I testing on a Mega given my hope to hang 14 servos on one controller. I wish someone would sort out the ESP32 PWM output issue when using the Arduino IDE. I am getting up to speed on ESP based micros and intend to migrate to them where compatible. I usually start with a Mega for most projects just because I have a long history with it. Once things work there, I look for opportunities to move to less expensive hardware for long term usage.

I spent some time reviewing servo data sheets as well as online commentary last night. I now have a better understanding of control limits and how to address the variability when using the Arduino servo library.

Servos generally are designed to operate with a minimum control bandwidth of at least 1000-2000 microseconds, and 1500 as centered position. Most servos exceed this with 750-2250 being more typical. Some servos go wider than that.

The Arduino servo library defaults to a control range of 544-2400 with 1472 microseconds as centered if the input parameters are in degrees, 0-180. The servo library is set up with 3 ways to address servos that do not match the default parameters.

  1. if working with degrees input, the servo attach call accepts optional arguments for min and max microseconds to associate with 0 and 180 degrees.
  2. if working with microseconds input, the servo library has a microseconds write command option that can be used instead of the write command.
  3. this one is a not so obvious feature. The servo write command will assume input is degrees if the parameters are in the range 0-543, and will truncate 181-543 down to 180. If the input parameter is 544 or greater, it assumes input is in microseconds. I have not found the upper limit for microseconds in the code, but experimentally I found improper servo response for values higher than 2915 microseconds. I found that when using this method, values greater than 2400 are truncated to 2400 unless the attach command max argument is set equal or greater than the max value you want to command.

This 3rd option is what my proposed EX_Servo.cpp changes utilize to allow sketch parameters to be either degrees or microseconds. A negative consequence for backwards compatibility is 0-180 degrees will map to 544-2915 microseconds with center at 1730 instead of the current mapping of 544-2400 with center at 1472.

Jeff,

I have implemented your change requests as optional parameters to the constructor. This should keep everything backwards compatible. I also tweaked some of the existing min and max angle checking logic to be more in line with your code. Take a look at the EX_Servo.cpp file and let me know what you think. It tested fine on my workbench.

Thanks for your inputs!
Dan

//  Summary:  EX_Servo is a class which implements the SmartThings/Hubitat "Switch Level" device capability.
//			  It inherits from the st::Executor class.
//
//			  Create an instance of this class in your sketch's global variable section
//			  For Example:  st::EX_Servo executor1(F("servo1"), PIN_SERVO, 90, true, 1000, 0, 180, 2000, 544, 2400);
//
//			  st::EX_Servo() constructor requires the following arguments
//				- String &name - REQUIRED - the name of the object - must match the Groovy ST_Anything DeviceType tile name
//				- byte pin_pwm - REQUIRED - the Arduino Pin to be used as a pwm output
//				- int startingAngle - OPTIONAL - the value desired for the initial angle of the servo motor (0 to 180, defaults to 90)
//              - bool detachAfterMove - OPTIONAL - determines if servo motor is powered down after move (defaults to false) 
//              - int servoDetachTime - OPTIONAL - determines how long after the servo is moved that the servo is powered down if detachAfterMove is true (defaults to 1000ms)
//				- int minLevelAngle - OPTIONAL - servo angle in degrees to map to level 0 (defaults to 0 degrees)
//				- int maxLevelAngle - OPTIONAL - servo angle in degrees to map to level 100 (defaults to 180 degrees)
//              - int servoRate - OPTIONAL - initial servo rate in ms/degree (defaults to 2000, used to ensure a gentle move during startup, afterwards comes from SmartThings/Hubitat with each move request)
//              - int minPulseWidth - OPTIONAL - minimum pulse width in milliseconds, defaults to 544 (see Arduino servo attach() function)
//              - int maxPulseWidth - OPTIONAL - maximum pulse width in milliseconds, defaults to 2400 (see Arduino servo attach() function)
2 Likes

I can confirm...the new servo library works like a charm.

3 Likes

Dan, Code looks good. Appears you enabled use of the servo library Easter egg for microsecond fans while keeping complete transparency for degree users. I will test tonight.

1 Like

In the midst of all this servo talk I but in with a request for the humidity device driver. I have a BME280 connected. The Humidity reading flickers from 50.9 to 49.908948736271 once in about every 2 -3 minutes. I took a look at th ecode but some of it is greek to me

Give this new version of the Child Humidity driver a try and let me know if it solves the issue, please.

https://raw.githubusercontent.com/DanielOgorchock/ST_Anything/master/HubDuino/Drivers/child-humidity-sensor.groovy

Dan, EX_Servo seems to work properly as updated. Thankyou for continuing to incorporate suggestions.

I found a way to add a feature for reversing servo endpoints associated with level 0 and level 100. With the changes, all the user has to do is swap the values for min level angle and max level angle arguments and the associated servo will travel in the opposite direction. This will enable more flexibility when connecting a servo to mechanical hardware.

What is the preferred method for me to submit the edited EX_Servo.cpp file for your consideration?

You can Private Message the files to me, or issue a GitHub Pull Request. Your choice.

I submitted my first ever pull request. Hope I did it properly.

1 Like

Your PR has been merged. Thanks!

1 Like

New HE user, first post. Successfully setup 2 of my blinds. Waiting on more servos to arrive. Thank you

I purchased 270 degree servos as my blinds needed that amount of rotation. The code moves my servos and blinds through their full rotation properly. However the angle reported is incorrect as the code seems to be designed around 180 degree range of motion.

Is a fix possible?

After looking through the Arduino reference, it seems like only values of 0 to 180 degrees are accepted. Does it really matter though? As long as you can use the 0 to 100% level to control your blinds, perhaps the angle reading correctly doesn't matter? You could map the 0 to 180 that is returned to the Child Servo driver, to 0 to 270 degrees for readability.

@VooDooFiveTwo is fast becoming the Servo SME around here. He may have some ideas.

I did look at the servo libraries. I will try when my next batch of servos arrive. No your right, what's reported doesn't matter, just aesthetics.

Thanks Dan I will let you know

You could simply multiply the angle returned in the Child Servo parse routine by 1.5 as shown below. (Note - this has not been tested.)

def parse(String description) {
    if (logEnable) log.debug "parse(${description}) called"
    def parts = description.split(" ")
    def name  = parts.length>0?parts[0].trim():null
    def value = parts.length>1?parts[1].trim():null
    if (name && value) {   
        // Update device
        def myValues = value.split(':')
        def tmpAngle = myValues[1].toInteger() * 1.5
        sendEvent(name: "level",value: myValues[0].toInteger())
        sendEvent(name: "angle", value: tmpAngle.toInteger())
        sendEvent(name: "rate", value: myValues[2].toInteger())
        if (myValues[0].toInteger() <= offvalue.toInteger()){
            sendEvent(name: "switch", value: "off")
        }
        else {
            sendEvent(name: "switch", value: "on")
        }
      	// Update lastUpdated date and time
        def nowDay = new Date().format("MMM dd", location.timeZone)
        def nowTime = new Date().format("h:mm a", location.timeZone)
        sendEvent(name: "lastUpdated", value: nowDay + " at " + nowTime, displayed: false)
    }
    else {
    	log.error "Missing either name or value.  Cannot parse!"
    }
}