Nicnl / homebridge-minimal-http-blinds

GNU General Public License v3.0
12 stars 12 forks source link

Implementation with stepper motor (28byj-48) #4

Closed Kepete closed 6 years ago

Kepete commented 6 years ago

First thank you for the plugin!

I have seen the sample and learned them. I try to build a curtain system with a stepper motor(28byj-48) and a esp8266 controller(NodeMCU) that is running micropython.

I can't figure out how to get it working. I can turn the motor with a python script, choose the direction and how many turns(steps) I want to make can't figure out how to make it work with this plugin and the sample python flask server. Wanted to ask if maybe someone has already done it before?

Nicnl commented 6 years ago

Hey

Sorry for the delay, I have a lot of work

Basically, your esp8266 have to provide a minimalistic HTTP api

You have to code three URLs that will:

I've never used micropython myself, but I looked into it a bit.
Apparently, the Flask framework isn't compatible with micropython.
Fortunately, there's an equivalent called Picoweb: https://github.com/pfalcon/picoweb

Apparently creating an http server using picoweb seems relatively straightforward:

import picoweb

app = picoweb.WebApp(__name__)

@app.route("/")
def index(req, resp):
    yield from picoweb.start_response(resp)
    yield from resp.awrite("This is webapp #1")

if __name__ == "__main__":
    app.run(debug=True)

I don't have an esp8266 available atm for testing it, but here's a draft of what your esp sould run

import picoweb

app = picoweb.WebApp(__name__)

@app.route("/stepper_motor_position")
def stepper_motor_position(req, resp):
    method = req.method

    if method == "POST":
        request_size = int(req.headers[b"Content-Length"])
        request_data = yield from req.reader.read(request_size)
        stepper_percentage = int(request_data)

        # control your stepper and set it to {stepper_percentage}

        yield from picoweb.http_error(resp, "204") # don't worry it's not actually an error, it's just sending back "204 no content"
    elif method == "GET":
        stepper_percentage = 50 # replace 50 by your stepper motor position here in %

        yield from picoweb.start_response(resp)
        yield from writer.awrite(str(int(stepper_percentage)))
    else:
      yield from picoweb.http_error(resp, "405")

@app.route("/stepper_motor_state")
def stepper_motor_state(req, resp):
    yield from picoweb.start_response(resp)
    if your_stepper_motor_is_doing_nothing: # change this
        yield from writer.awrite("2")
    elif your_stepper_motor_is_turning_in_a_direction: # change this
        yield from writer.awrite("1")
    elif your_stepper_motor_is_turning_in_the_other_direction: # change this
        yield from writer.awrite("0")

if __name__ == "__main__":
    app.run(debug=True)

Here is some useful links for you:
https://techtutorialsx.com/2017/09/28/esp32-picoweb-obtaining-the-http-method-of-the-request/
https://github.com/pfalcon/picoweb/blob/8569f04a72a7b363b3543ef1b15c18eea7b8a6f3/picoweb/__init__.py#L60
https://github.com/pfalcon/picoweb/tree/master/examples

Keep me up to date, I'll be glad if everything works out for you

Nicnl commented 6 years ago

Oh well I just realized that, hum

With the plugin's set_target_position_url, the position of the blinds is directly in the URL and not in the body of the HTTP request

I'll post an updated theorical micropython/picoweb code in a minute

Nicnl commented 6 years ago
import picoweb

app = picoweb.WebApp(__name__)

#### Parsing function
def qs_parse(qs):

    parameters = {}
    ampersandSplit = qs.split("&")

    for element in ampersandSplit:
        equalSplit = element.split("=")
        parameters[equalSplit[0]] = equalSplit[1]

    return parameters

@app.route("/stepper_motor_position")
def stepper_motor_position(req, resp):
    method = req.method

    if method == "POST":
        parameters = qs_parse(req.qs)
        stepper_percentage = int(parameters["position")

        # control your stepper and set it to {stepper_percentage}

        yield from picoweb.http_error(resp, "204") # don't worry it's not actually an error, it's just sending back "204 no content"
    elif method == "GET":
        stepper_percentage = 50 # replace 50 by your stepper motor position here in %

        yield from picoweb.start_response(resp)
        yield from writer.awrite(str(int(stepper_percentage)))
    else:
      yield from picoweb.http_error(resp, "405")

@app.route("/stepper_motor_state")
def stepper_motor_state(req, resp):
    yield from picoweb.start_response(resp)
    if your_stepper_motor_is_doing_nothing: # change this
        yield from writer.awrite("2")
    elif your_stepper_motor_is_turning_in_a_direction: # change this
        yield from writer.awrite("1")
    elif your_stepper_motor_is_turning_in_the_other_direction: # change this
        yield from writer.awrite("0")

if __name__ == "__main__":
    app.run(debug=True)

I don't have the hardware to test this code though, you'll have to test it yourself, sorry.

With this piece of code, you'll need a homebridge configuration that looks like that:

"accessories": [
    {
        "name": "Kitchen Blinds",
        "accessory": "MinimalisticHttpBlinds",
        "get_current_position_url": "http://192.168.1.55/stepper_motor_position",
        "set_target_position_url": "http://192.168.1.55/stepper_motor_position?position=%position%",
        "get_current_state_url": "http://192.168.1.55/stepper_motor_state"
    }
],

(Don't forget to replace 192.168.1.55 by your esp ip address.)

I used this website as a reference for reading the URL query parameters:
https://techtutorialsx.com/2017/09/29/esp32-micropython-getting-the-query-parameters-on-a-picoweb-app/

Kepete commented 6 years ago

@Nicnl Thank you for your efforts! I'm currently not near my esp but I will be able to test it in the weekend. I will let you know.

Nicnl commented 6 years ago

I'll close this issue for now as it's been opened for a while and there's no news.
Feel free to respond if you need further assistance.