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



My general approach would be to bin parameters into categories of "Hardware Calibration" (HC) vs. "Control Inputs" (CI).

HC items are items that need to be adjusted during initial installation or when modifying hardware. These events should be rare, so specifying them in the sketch makes sense.

CI items are things that users would want to adjust manually or via automation on a more frequent basis, so they should be accessible in the hubitat driver.

Personal preference and usage case creates some gray zone between these categories. In those cases, I would lean toward binning as CI.

Here's my preference given the two commonly discussed applications, blinds and damper control.

HC parameters set up in sketch:

  • End point mapping. This should be set it and leave it. If it is open to the Hubitat driver, risk for inadvertent changes that could cause servo overload and damage are too high. For ease of setup without compiling the sketch repeatedly, I recommend keeping the degree output that is currently visible in the Hubitat device. During setup, the sketch can be compiled with level 0-100 mapped to 0-180 degrees and initial position 50. Manual adjustment of level in the Hubitat driver can be performed for each endpoint without recompiling the sketch. The corresponding degrees reading can be observed in the device driver, then used for remapping in the sketch. Two rounds of sketch compiling should get the setup done.
  • non linear mapping coefficients.
  • initial position (as a level, not degrees)
  • detach flag

CI parameters set up in Hubitat driver:

  • optional over ride of initial position (this one is in the gray zone)
  • level input
  • duration as time desired for the move to be completed. Make the default 0 so the servo moves as fast as it can under load. Allow user input to select a slower transition duration in seconds. If selected, detach would occur immediately after transition completion.


Sounds good to me! I don't know that I'll have time to work on this any time soon. Please feel free to give it a shot and have some fun with it! I'll help out where I can along the way.


I will see what I can do in small steps. First up is adding arguments for mapping end points. I think that is fairly easy to accomplish, and is the most useful for my application.


Dan, I managed to get the endpoint mapping working properly in terms of servo response and serial monitor data output. I also set everything up to use a maximum level of 100 instead of 99. No issues so far with any of that.

I am stuck on getting the Child Servo to report the correct angle. Your implementation assumed a fixed mapping of 0 to 0 and 99 to 180, so it appears you just calculated the angle within the Child Servo by multipling the Level by 1.81818:

// Update device
sendEvent(name: "level", value: value)
def angleValue = value.toFloat() * 1.81818
angleValue = angleValue.round(2)
sendEvent(name: "angle", value: angleValue)

My 99 to 100 maximum Level adjustments allowed the 1.81818 multiplier to be changed to 1.8, but that only gives correct output for the default 0 degrees and 180 degrees endpoints. I need to figure out how to pass the actual angle from the sketch back to Child Servo so it will display the correct angle when custom endpoints are in use. That might be outside my ability. I will look through other device types to see if I can find a relevant example.


I am making slow but steady progress.
Implemented and working features:

  • Servo endpoint arguments added to sketch and servo output angle mapped to Level (travel limits can be reduced on both ends and direction of travel relative to level can be flipped via the map arguments while maintaining full use of the 0-100 level range)
  • Initial level is specified in sketch instead of initial angle
  • Servo response matches desired angle and level on initialization and for each change sent from Hubitat
  • Hubitat Device receives current Level, Angle, and Duration back from Hubduino and correctly updates states (minor miracle for a hack coder like me)

Next up features:

  • Passing Duration value from Hubitat to Hubduino (this is testing my lack of string parsing skills)
  • Changing use of Duration from a delay waiting for servo movement completion (fraction of a second) to active control of the servo move time (targeting 1 to 10 seconds)
  • Optional non-linear mapping of Angle to Level
  • Adding ST compatibility (heresy)


Dan, I sorted out the parsing of data from Hubitat to Hubduino. I have to thank you for encouraging me to try this project on my own. My understanding of the functions that send data between the hub and micro-controller has increased enough that I am starting to think about more challenging custom projects unrelated to this EX_Servo enhancement. I think everything I have implemented so far is solid enough that you could merge it into your distribution. Before sending it your way, I want to finish or fail on the last three objectives I laid out in prior posts.

Concerning the next step, I'd like to know what you think the approach should be to actively control slow smooth motion of the servo. Bringing communication to a halt while looping multiple steps of servo position change seems like a bad thing to do if there are other time sensitive devices attached the the same controller. My goal is allowing the servo move to take up to 10 seconds, which seems like a long time to block communications. For my first application, this controller will be dedicated to servos, so I can probably use the sloppy loop method.

I'd like this enhancement to have broader appeal in a mixed device usage case. I'm reading up on interrupt techniques trying to plan a way to allow other controller code to run while waiting for a timer based interrupt to trigger the next servo step. Do you think this is the best way to proceed, or do you have other ideas for me to try? I'm hoping there might be some functions I have not yet discovered that would help in this effort.


Sounds like you're making some great progress. That's great to hear! As for the challenging part of of your requirements... I think we have two options worth exploring. The first one is to use interrupts that periodically wake up the EX_Servo device to see if there is any work left to do (i.e. finish completing a servo move over a time period.) The other method would be to have ST_Anything's Everything class wake up the EX_Server every time loop() is executed to see if there is any work left to do. Unfortunately, that will require a bit of a redesign of ST_Anything to make it happen.

So, let's focus on the first option for now.

The interrupt idea is a very nice way to accomplish this. However, it will require the most testing in order to make sure it doesn't break any other part of ST_Anything (e.g. there are some other devices that use an interrupt to count number of pulses on a digital input.) The PS_PulseCounter.cpp object is the one that comes to mind. You might want to look at it as an example of how to attach/use an interrupt. If the two devices conflict for some reason, I would be OK with a disclaimer stating that only one of them can be used at a time. Speaking of one at a time, I wonder if the interrupt idea can support more than one EX_Servo device at a time in the sketch?


I think probabaly not. For the moment I am implementing the sloppy method which is completing all the servo steps in one call of EX_Servo::writeAngleToPin with no interrupts. With only one servo attached, no ill effects are observed even with a duration of 10 seconds. I will set up with the full complement of 14 servos on my bench and test it with some hub automation that updates all servos in a single action to see what happens. Worst case for this will be 10 seconds x 14 servos needing to move. If no data is lost, I assume the servos will respond sequentially over 140 seconds.

Do you expect commands and communication data to be lost, or is ST Anything robust for holding data in the hub output steam until the micro is able to receive it? If this is a disaster, I will think harder about other more elegant solutions.

Through experimentation I found that duration of a move was less critical than angular speed of the servo. To make servo speed consistent for small moves and full range moves, I set the code up to calculate the step rate needed to make a 0-100 level move in the time specified by duration. Now when I call for a small move, it happens quickly, with an angular speed that matches a full range move.


@kilowatts has 3 servos on the same ESP board that are set up in a group in HE. He says they trigger is sequence so there hasn't been any conflict between them in his implementation.

Just out of curiosity, are you using the same parameters that HE passes for a duration of a dimmer change? It might solve the problem I have with my blinds triggering my motion sensors.


Yes. In the currently published Child Servo, the Duration parameter is ignored. I now have it functional. It might address the motion sensor issue. What inspired me to work on that piece of it were comments about sudden shade movements startling people.


LOL... Yeah, I had to put in code where my siren's chime goes off 3 seconds before they move. It will definitely be a trade off though. Part of the reason that they are now able to close all the way is because of the speed that they move. When you have the code ready I'd love to give it a go. I have 4 different servo setups around the house, some are light and able to move easily and others are definitely closer to straining.


I have lots of testing to do still, but you are welcome to debug with me :slightly_smiling_face:


Is there anything needed in the sketch that is different that the default servo library or just the custom servo.h and .cpp files?


The starting angle argument is now a starting level. Two optional arguments are added for servo endpoints. if you leave them off, the default will be 0 and 180 degrees which will behave like the old Servo implementation. The value in using these two arguments is getting full range use of level from 0-100. 0 will be closed blinds. 100 will be full open.

You also need to update the servo driver in HE.

Good Luck


Okay. Yeah, I think on second thought I'm gonna pass for now. I'm good with what I've got. Little too involved for me to screw with what I've got already working. Good luck though man.


Sounds like you have a different version of the driver than I started with. When did you grab Dan's Driver?

Edit: I see that Dan updated the driver with input from you a couple days after I pulled the version I started with. I'll compare them and see if there's a best of both solutions that can be put together.


Yours limits the range of the servo, mine just sets the settings for when the device is on and when it's off. So, they are independent of each other. Both useful. I just don't feel like going through the trouble of having to swap it all out tonight. I only have one board that is easily accessible and it does a lot of other stuff too. Maybe some other day.


I merged the drivers and updated on my GitHub. Seems to work in a limited bench test.

Edit: After more bench testing, I am seeing intermittent loss of communication and ethernet resets on the Mega/W5200 end of things. It results in lost commands and things being dead for a while, then it just starts working again. I want to be sure my hardware is not the issue, so I have switched back to Dan's current distribution and will verify stability for a few hours. If stability looks good, I will reload my version.


Is it a watchdog timer issue? With lengthy interrupts I've had that happen before.


Possibly. Timing issues were a concern from the beginning. I am seeing the same loss of communication when running Dan's distribution, so I think there's a problem with something in my hardware setup. I plan to do a systematic evaluation of those bits, but it won't be today.

What I can say is my version of servo seems to be as reliable as Dan's on my hardware, and it does implement control of servo speed. When communication is solid, it works as I intended. Maybe it will run on your hardware since you are not seeing communication issues with Dan's distribution.