LED blink decoding

Hi!

My project is an ordinary gate opening relay. And I need some help to decode the main gate controller LED status light.
It’s built on wemos (esp8266) using EasyESP core and MQTT for transport.
The main gate controller has a status LED which is on when the gate is open and blinking when it is closing or opening or got an error. I would like to decode that values and display in the sitemap.
How can I count the blinks and intervals between them?

Thanks a lot!!!
Sasha

My .items:

Switch OSB_gate_relay <fts_sliding_gate> {mqtt=">[mqtt:/osb-gates2/gpio/5:command:ON:1],>[mqtt:/osb-gates2/gpio/5:command:OFF:0]"}

Number OSB_gate_status "Gate Status [%s]" <edit_settings> {mqtt="<[mqtt:/osb-gates2/gate-status/Switch:state:default]"}

String OSB_gate_status_display "Gate Status [%s]" <edit_settings>

.sitemap

Switch item=OSB_gate_relay label="Main Gates" mappings=[ON=TRIGGER]

Text item=OSB_gate_status_display

and .rules

rule "Garage Gates"
when
	Item OSB_gate_relay received update ON
then
	Thread::sleep(2000)
	sendCommand(OSB_gate_relay, OFF)
end


rule "Gate status updates"
when
	Item OSB_gate_status received update
then
	if OSB_gate_status.state == 0 {
   		Thread::sleep(2000)
    	postUpdate(OSB_gate_status_display, "OPEN") }
    else {
    	Thread::sleep(2000)
    	postUpdate(OSB_gate_status_display, "CLOSED") }
end

Hello Sasha,
not quite what you are asking for but did you think about decoding directly on your microcontroller? You have a potent one on your hands and blink-detection is done in just a few lines of code! You attached a list of “Gate status LED” states. That should be very easy to differentiate in code, following a translation to MQTT messages.

Thanks for a fast reply.

The issue was that I want to use default EasyESP code because I’m not confident in esp coding.
Another thing is that the box in the controller is not easily accessible so I decided to decode it later in OpenHAB.

Of course, the main goal is to detect either the gate is open or closed, but during the opening and closing, the LED is blinking and I got the item getting On and OFF commands frequently so I want to filter them somehow. That’s why I have the delay in the rule but it doesn’t really work.

Understandable.

Okay. You want to sit out the “bouncy” LED blinking and wait for a steady state.

Let’s say your LED is blinking at a rate of one second. You need a timer with a deadline at say three seconds (e.g.) in the future. This timer will be set and thereby reset at every blinking event (bouncy state) but reached as soon as no events happened for more than three seconds (steady state). At this point you can update your OSB_gate_status_display.

Let us know if you need help building the rule.

This isn’t going to be easy, but I think we can make it work. Though as @ThomDietrich suggests, the code will be far easier in the ESP than coding on the OH side. And IMHO, that is where that sort of code belongs anyway.

First of all, enable persistence on these Items so we can keep track of when they were last updated without a bunch of local vars to keep state, which would get really ugly really fast.

The real challenge though is going to be telling when the light is just ON and not blinking, because we only ever get the one ON event (note this is trivial on the ESP because you get a constant signal while the light is ON) It will also be a challenge to determine the difference between a Continuous slow/fast flash and error flash. What constitutes “slow” versus “fast” anyway and how does that compare to 1 every two seconds and 4 every two seconds?

How long does the light stay on in each case?

What I’m thinking is having a moving two second window and count the number of ONs in that window, with there being a special case where there is an ON without a following OFF. But this is going to require you knowing how long the light stays on when it is flashing (so you can tell the difference between flashing and just ON) in both the fast, slow, and error cases.

So the rule would work something like this:

  • when a command is received check to see if it is an ON command
  • check to see when the last OFF command was received, if it is longer than two seconds ago we know this is the start of a new event (you can narrow that time from two seconds to the maximum amount of time the light stays on when it is flashing)
  • if this is an ongoing event, count the number of ONs in the past two seconds.
    • 1 - Pillar light override
    • 2 - No mains present (does your device have power in this case?)
    • 3 - Battery low
    • 4 - Multiple collisions
    • ?>4 - Gate is opening
    • ?>5 - Gate is closing
  • if it has been more than two seconds since the last ON and there haven’t been any OFFs Gate is open
  • if it has been more than two seconds since the last OFF and there haven’t been any ONs Gate is closed

NOTE: It will take two seconds for this system to decide on a state.

You will probably need to use Timers to detect the ON and OFF states.

You might be able to take advantage of differences in how long the light is ON during continuous slow and fast flashing to detect those states instead of using the moving two second window. this would let you detect those states much faster.

This is really going to be very complex code if you implement this in openHAB. Since blink detection is already implemented for you on the ESP I really do think it will be much much easier for you to implement it there.

One more addition. You are using a ESP8266 --> which is wifi flashable --> which might make programming acceptable for you? I can’t stretch enough how much easier this will be on the esp :wink:

Thanks for the very detailed explanation!

The problem is I’m on a copy-paste rule programming stage. So yes, you are right, decoding the blinking in OH would be a problem. Your help of writing the actual rule would be very appreciated!!

I will call this stage 1 of my controller. :slight_smile:
And now I want only to see two statuses: open or closed.

I guess I need to use something like this:

rule "Gate status updates 2"
    when
        Item OSB_gate_status changed
    then
        if(OSB_gate_status.state==0) {
            timer = createTimer(now.plusSeconds(10)) [|
               postUpdate(OSB_gate_status_display, "OPEN")
            ]
        } else {
            if(timer!=null) {
                timer.cancel
                timer = null
            }
        }
end

Also this is how it looks like in the logs:

2016-09-27 21:48:40 - OSB_gate_status state updated to 1
2016-09-27 21:48:41 - OSB_gate_status state updated to 0
2016-09-27 21:48:42 - OSB_gate_status state updated to 1
2016-09-27 21:48:42 - OSB_gate_status_display state updated to CLOSED
2016-09-27 21:48:43 - OSB_gate_status state updated to 0
2016-09-27 21:48:43 - OSB_gate_status_display state updated to OPEN
2016-09-27 21:48:44 - OSB_gate_status state updated to 1
2016-09-27 21:48:44 - OSB_gate_status_display state updated to CLOSED
2016-09-27 21:48:45 - OSB_gate_status state updated to 0
2016-09-27 21:48:45 - OSB_gate_status_display state updated to OPEN
2016-09-27 21:48:46 - OSB_gate_status state updated to 1
2016-09-27 21:48:46 - OSB_gate_status_display state updated to CLOSED
2016-09-27 21:48:47 - OSB_gate_status state updated to 0
2016-09-27 21:48:47 - OSB_gate_status_display state updated to OPEN
2016-09-27 21:48:48 - OSB_gate_status state updated to 1
2016-09-27 21:48:48 - OSB_gate_status_display state updated to CLOSED
2016-09-27 21:48:49 - OSB_gate_status state updated to 0
2016-09-27 21:48:49 - OSB_gate_status_display state updated to OPEN
2016-09-27 21:48:50 - OSB_gate_status state updated to 1
2016-09-27 21:48:50 - OSB_gate_status_display state updated to CLOSED
2016-09-27 21:48:51 - OSB_gate_status state updated to 0
2016-09-27 21:48:51 - OSB_gate_status_display state updated to OPEN
2016-09-27 21:48:52 - OSB_gate_status state updated to 1
2016-09-27 21:48:52 - OSB_gate_status_display state updated to CLOSED
2016-09-27 21:48:53 - OSB_gate_status_display state updated to OPEN
2016-09-27 21:48:54 - OSB_gate_status_display state updated to CLOSED

This might be a bit above your skill level, at least to code this in OH. While I can help some with the logic, there is no substitute for coding something like this with direct access to the device and that isn’t something I can do remotely.

Now, if all you want to do is debounce the states so you know when it is Open or Closed, we can do that pretty simply.

First create a new Item to represent the debounced state, I’ll call it OSB_gate_status_proxy. Put this Item on your sitemap and query it in your rules, not the other one.

val Timer debounceTimer = null

rule "Debounce Gate Status"
when
    Item OSB_gate_status changed
then

    if(debounceTimer != null && !debounceTimer.hasTerminated){
        debounceTimer.reschedule(now.plusSeconds(3))
    }
    else {
        debounceTimer = createTimer(now.plusSeconds(3), [|
            OSB_gate_status_proxy.sendCommand(OSB_gate_status.state)
            debounceTimer = null
        ]
    }
end

The Theory of Operation:

When an event occurs from the device, set a timer to go off in three seconds. For each subsequent event that takes place before the timer goes off, reschedule it further into the future. Consequently the Timer will trigger three seconds after the last event without any following events was received (i.e. it has stopped blinking). At that time, set the state of the proxy to match whatever that last/current state is.

Using a Timer in this way is called the Latch Design Pattern.

Save the implementation of decoding the blinking until you are comfortable implementing it on the ESP itself.

Hm, maybe that will be a dumb question, but I dont see OSB_gate_status_proxy item in the rule…

Typo, I corrected it. It is the sendCommand inside the Timer.

That;s what I get now. Much better :slight_smile:
Thanks a lot Rich!!!

Maybe I can increse the timer?

2016-09-28 11:59:47 - OSB_gate_status state updated to 0
2016-09-28 11:59:48 - OSB_gate_status state updated to 1
2016-09-28 11:59:49 - OSB_gate_status state updated to 0
2016-09-28 11:59:50 - OSB_gate_status_display state updated to OPEN
2016-09-28 11:59:50 - OSB_gate_status state updated to 1
2016-09-28 11:59:51 - OSB_gate_status_display state updated to CLOSED
2016-09-28 11:59:51 - OSB_gate_status state updated to 0
2016-09-28 11:59:51 - OSB_gate_status_display state updated to OPEN
2016-09-28 11:59:51 - OSB_gate_status_proxy received command 0
2016-09-28 11:59:52 - OSB_gate_status state updated to 1
2016-09-28 11:59:52 - OSB_gate_status_display state updated to CLOSED
2016-09-28 11:59:53 - OSB_gate_status state updated to 0
2016-09-28 11:59:53 - OSB_gate_status_display state updated to OPEN
2016-09-28 11:59:54 - OSB_gate_status state updated to 1
2016-09-28 11:59:54 - OSB_gate_status_display state updated to CLOSED
2016-09-28 11:59:54 - OSB_gate_status_proxy received command 1
2016-09-28 11:59:55 - OSB_gate_status state updated to 0
2016-09-28 11:59:55 - OSB_gate_status_display state updated to OPEN
2016-09-28 11:59:56 - OSB_gate_status state updated to 1
2016-09-28 11:59:56 - OSB_gate_status_display state updated to CLOSED
2016-09-28 11:59:57 - OSB_gate_status_display state updated to OPEN
2016-09-28 11:59:57 - OSB_gate_status_proxy received command 1
2016-09-28 11:59:57 - OSB_gate_status state updated to 0
2016-09-28 11:59:58 - OSB_gate_status_display state updated to CLOSED
2016-09-28 11:59:58 - OSB_gate_status state updated to 1
2016-09-28 11:59:59 - OSB_gate_status_proxy received command 1
2016-09-28 11:59:59 - OSB_gate_status state updated to 0
2016-09-28 11:59:59 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:00:00 - OSB_gate_status state updated to 1
2016-09-28 12:00:00 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:00:01 - OSB_gate_status state updated to 0
2016-09-28 12:00:01 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:00:01 - OSB_gate_status_proxy received command 0
2016-09-28 12:00:02 - OSB_gate_status state updated to 1
2016-09-28 12:00:02 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:00:03 - OSB_gate_status state updated to 0
2016-09-28 12:00:03 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:00:03 - OSB_gate_status_proxy received command 0
2016-09-28 12:00:04 - OSB_gate_status state updated to 1
2016-09-28 12:00:04 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:00:05 - OSB_gate_status state updated to 0
2016-09-28 12:00:05 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:00:06 - OSB_gate_status_proxy received command 0
2016-09-28 12:00:06 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:00:07 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:00:08 - OSB_gate_status_proxy received command 0

I’ve done nothing but it got better :slight_smile:

2016-09-28 12:53:13 - OSB_gate_status state updated to 0
2016-09-28 12:53:14 - OSB_gate_status state updated to 1
2016-09-28 12:53:15 - OSB_gate_status state updated to 0
2016-09-28 12:53:15 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:53:16 - OSB_gate_status state updated to 1
2016-09-28 12:53:16 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:53:17 - OSB_gate_status state updated to 0
2016-09-28 12:53:17 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:53:18 - OSB_gate_status state updated to 1
2016-09-28 12:53:18 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:53:19 - OSB_gate_status state updated to 0
2016-09-28 12:53:19 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:53:20 - OSB_gate_status state updated to 1
2016-09-28 12:53:20 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:53:21 - OSB_gate_status state updated to 0
2016-09-28 12:53:21 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:53:22 - OSB_gate_status state updated to 1
2016-09-28 12:53:22 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:53:23 - OSB_gate_status state updated to 0
2016-09-28 12:53:23 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:53:24 - OSB_gate_status state updated to 1
2016-09-28 12:53:24 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:53:25 - OSB_gate_status_display state updated to OPEN
2016-09-28 12:53:26 - OSB_gate_status_display state updated to CLOSED
2016-09-28 12:53:28 - OSB_gate_status_proxy received command 1

Keep an eye on it. I’m not sure what was going on I in that first log.

Thanks, Rich

Another thing I’ve noticed is that my values on the sitemap are not updating automatically. I have to restart the app and only after this I can see the change.
Any idea what can it be?

You should only have to navigate to a subframe and back to see the updates. If so, assuming OH 1 this is a known issue. If OH 2 I don’t know what is the problem.

yes it’s OH1
Thanks!

Rich,

I’m doing something wrong again…

I’ve decided to upgrade a rule a bit and make it edit the string value of an item. But is always return CLOSED state

rule "Debounce Gate Status"
when
    Item OSB_gate_status changed
then
    if (debounceTimer != null && !debounceTimer.hasTerminated){
        debounceTimer.reschedule(now.plusSeconds(3))
    }
    else {
        debounceTimer = createTimer(now.plusSeconds(3), [|
        	if OSB_gate_status.state == "0" {
	        	postUpdate(OSB_gate_status_sitemap, "OPEN") 
	        	}
			else {
				postUpdate(OSB_gate_status_sitemap, "CLOSED")
//          OSB_gate_status_display.sendCommand(OSB_gate_status.state)
            debounceTimer = null
        ]
    }
end

items:

Number OSB_gate_status {mqtt="<[mqtt:/osb-gates2/gate-status/Switch:state:default]"}

Number OSB_gate_status_display	<edit_settings>
String OSB_gate_status_sitemap	"Gate Status [%s]" <edit_settings>

Don’t worry about that.
Done with mapping

Thanks!

Oh, now I’m getting issues with charts.
What can be wrong?

Logs:

2016-09-28 17:09:46.738 [INFO ] [ui.internal.chart.ChartServlet] - Illegal argument in chart: {}
java.lang.IllegalArgumentException: Persistence service not found 'null'.
        at org.openhab.ui.internal.chart.DefaultChartProvider.createChart(DefaultChartProvider.java:176) ~[na:na]
        at org.openhab.ui.internal.chart.ChartServlet.doGet(ChartServlet.java:245) ~[na:na]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:735) [javax.servlet_3.0.0.v201112011016.jar:na]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:848) [javax.servlet_3.0.0.v201112011016.jar:na]
        at org.eclipse.equinox.http.servlet.internal.ServletRegistration.service(ServletRegistration.java:61) [org.eclipse.equinox.http.servlet_1.1.300.v20120522-1841.jar:na]
        at org.eclipse.equinox.http.servlet.internal.ProxyServlet.processAlias(ProxyServlet.java:128) [org.eclipse.equinox.http.servlet_1.1.300.v20120522-1841.jar:na]
        at org.eclipse.equinox.http.servlet.internal.ProxyServlet.service(ProxyServlet.java:60) [org.eclipse.equinox.http.servlet_1.1.300.v20120522-1841.jar:na]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:848) [javax.servlet_3.0.0.v201112011016.jar:na]
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:598) [org.eclipse.jetty.servlet_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:486) [org.eclipse.jetty.servlet_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1065) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:413) [org.eclipse.jetty.servlet_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:192) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:999) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.Server.handle(Server.java:350) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:890) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:944) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:630) [org.eclipse.jetty.http_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:230) [org.eclipse.jetty.http_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:77) [org.eclipse.jetty.server_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:606) [org.eclipse.jetty.io_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:46) [org.eclipse.jetty.io_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:603) [org.eclipse.jetty.util_8.1.3.v20120522.jar:8.1.3.v20120522]
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:538) [org.eclipse.jetty.util_8.1.3.v20120522.jar:8.1.3.v20120522]
        at java.lang.Thread.run(Thread.java:745) [na:1.8.0_65]

Sitemap:

Chart item=OSB_gate_status_display period=12h refresh=60000

rrd4j.persist:

// persistence strategies have a name and a definition and are referred to in the "Items" section
Strategies {
    everyDay  : "0 0 0 * * ?"
    everyHour : "0 0 * * * ?"
    everyMinute : "0 * * * * ?"

    // if no strategy is specified for an item entry below, the default list will be used
    default = everyChange
}

/* 
 * Each line in this section defines for which item(s) which strategy(ies) should be applied.
 * You can list single items, use "*" for all items or "groupitem*" for all members of a group
 * item (excl. the group item itself).
 */
Items {
    // persist all items once a day and on every change and restore them from the db at startup
    * : strategy = everyChange, everyMinute, restoreOnStartup


}

Do you have rrd4j binding installed? Have you configured it as the default? Do you see any other errors in the logs when OH starts up?