Sunday 14 July 2019

My Garage Door Opener - Software

Where the Rubber Hits the Road

(This is a post in my series about my Garage Door Opener.  Yes I wrote a series. About a Garage Door Opener.  Check it out:

Okay, I'm an Electronics Engineer by trade, and while most of my projects are hardware-centric, I'm also comfortable with firmware and C.

So, naturally, I thought I'd give ESP8266 web page development a shot.  How hard can it be?



Well, let's just say it took me longer than expected and I learnt quite a few lessons.  Which I wrote about here

But first, some requirements - this unit has to work independently of an internet connection.  No cloud service, no If This Then That, it just has to work.  it can act as an access point and you connect to it directly, or it can sit on your home network and be access by any device there.

Also, any device that works with HTML5 can be used to access control this unit.  So iPhone, Android, Desktop of choice will work with it.  No special app needed!

Okay, lets go!

I Suck at Art

First big tip - use some one else's template.  I used Bootswatch Slate after this previous attempt:


Yeah, pretty ugly... 



See - much nicer.  There's a great many templates out there so I'm sure you can easily find something that suits you.  

Stuck Button Dilemma

One issue was that with the default behavior of Twitter Bootstrap (what Bootswatch is based on) is that once a button is pressed, it stays pressed.


This wasn't going to do - I wanted the button to reset and I'll be dammed if I could sort this out with default behaviors.

Long story short, it was down to how 'hover' works on mobile devices.  When on a desktop, you pass over a button with a mouse, and the button changes colour to reflect this behaviour.  

When on a mobile device, the system has no idea where your finger is, and thus when you press something, it retains 'hover' and 'active' states until you press something else. 

CSS Mods

So, the first step is to disable 'hover' for buttons in the bootswatch css file like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
.btn,
.btn:hover {
  border-color: rgba(0, 0, 0, 0.6);
  text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
}
.btn-default {
  background-image: -webkit-linear-gradient(#484e55, #3a3f44 60%, #313539);
  background-image: linear-gradient(#484e55, #3a3f44 60%, #313539);
  background-repeat: no-repeat;
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);
  filter: none;
}
/* .btn-default:hover { */
  /* default slate hover */
  /* background-image: -webkit-linear-gradient(#020202, #101112 40%, #191b1d); */
  /* background-image: linear-gradient(#020202, #101112 40%, #191b1d); */
  /* match normal button view */
  /* background-image: -webkit-linear-gradient(#484e55, #3a3f44 60%, #313539);
  background-image: linear-gradient(#484e55, #3a3f44 60%, #313539); */
  /* comment all out so active class is displayed  only */
  /* background-repeat: no-repeat; */
  /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff191b1d', GradientType=0); */
  /* filter: none; */
/* } */
.btn-primary {
  background-image: -webkit-linear-gradient(#8a9196, #7a8288 60%, #70787d);
  background-image: linear-gradient(#8a9196, #7a8288 60%, #70787d);
  background-repeat: no-repeat;
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8a9196', endColorstr='#ff70787d', GradientType=0);
  filter: none;
}

Note I only disabled the hover effect on the default button (as that's all I'm using).  All other buttons are unaffected.

Javascript Magic

But that's not all - I also needed some Javascript to reset the active state on a button - but after 500ms so you get the feedback that you actually pressed it.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    //**************************************************************************
    // Show button is pressed by keepinng active class asserted for 500ms
    //**************************************************************************
    $(document).ready(function() {
      // console.log('Document ready.')
      $('.btn').click(function() {
        $(this).addClass('active');
        setTimeout(function() {
          console.log("removing class active");
          $('.active').removeClass('active');
        }, 500);
      });
    });

I can here you asking why didn't I also remove the 'Hover' class and the answer is God knows I tried, but it was beyond me.  I believe that 'hover' is a pseduo-class and that's why I fail to remove it as required programmatically using Javascript.

Again, if you know how to do this, please let me know!

Home Page

Once connected to the WiFi, and if using an apple product, or have Bonjour for Windows installed on your Windows box, you can connect to the Garage Door Interface (GDI) by typing in http://gdi.local in your address bar.  

Android uses need to know the IPaddress - unless there's a better way that I don't know about how to connect out there....


The home screen shows buttons for each of the door and light relay controls, and also the current temperature of the garage.  

Calibration

However, whenever measuring ambient temperature of an electronic device, you have to take into account any self heating.  So, over time I measured the reported temperature of GDI against a thermometer.  I actually did this over many months, to capture the extreme swings of Summer and Winter -and came up with the following results:


I was expecting some sort of curve, where I'd need some polynomial but was pleasantly surprised to find out how linear the response is. 

Performing a regression on this data (using google sheets) I came up with the following results:

Slope0.9855148193Intercept-4.340888584

I then immediately ignored all the decimal points and went with a slope (multiplier) of 1 and an intercept (offset) of 4.3 - perfect!


From the hamburger menu, you can access the calibration page, and enter your calibration coefficients.  Note that the GDI itself doesn't use this information, but rather supplies it with the current temperature reading to the page requesting it, and Javascript in the Webpage performs the calculation and displays the calibrated reading.  Nice. 

Network

Selecting the Network page from the drop down menu:


Gives you this page.  When loaded it requests a list of SSIDs and the ESP8266 then scans and reports:


You can then easily select a network (just select any entry in the list) and enter the password ad your in.  You can also change the Local Name to whatever you like so you could change it to 'garage' and then connect to http://garage.local afterwards. 


Also, you can use this to configure the GDi to connect to your WiFi network when first started as an access point, but you need to power cycle or reboot it to have it take effect. I thought a 'reboot' button was a bit dangerous so you can type in http://gdi.local/reboot.html and it will reboot.  

I've since realised this is a crappy way of doing it so will add a GUI option to reboot it later. 

More Options

 Because I could, I made the buttons configurable.


You can sent them to be hidden (Show, False) if you're not using all of them in your installation, and can change the name.  


Additionally, the on time of the relay can also be set there.  For example I have the door relays pulse for 500ms and the light is set to 300,000 ms (5 minutes).


Once you save your settings you get confirmation and you can return to the home page. 


You can see here that I've now hidden an unused button now. 

Brightness Control

Lastly, I also have a page where I can set the PWM value of the front buttons.  Very handy.  


And of course, you can get a copy of the code yourself from Github

I've not gone into details as to how the code works - take a look at it for details - as I covered most of my lessons / thoughts in this post here

But in summary:
  • Will boot as access point and let you connect and then configure your wifi credentials
    • or you can keep it as an access point, it will still work, choice is yours!
  • Is discoverable on your network using mDNS
    • Android users will need to deduce it's IP address
  •  Uses JSON files to transfer data using websockets and to read a config file at startup
  • Uses the SPIFFs file system to store webapges, and will serve them when fetched by name
  • Also blinks LEDs.  

Living With It

As with most of my projects here, I primarily did this to learn something new, and that was a crapload, so that alone made this project worth doing to me.

However, it's also come in handy!  I've been locked out of the house while gardening and rather than wail at the door, it's a snap to pop the door open using my phone (I often listen to podcasts while outside).  

I also occasionally take the train to work - and it's liberating to leave my keys at home knowing I can get into the house via the garage.  Neat!  Yes, you should get one too :)





2 comments:

  1. This is an amazing post! Thank you for sharing what you learned along the way. It is definitely not an easy process.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete