MVCam H7 to IBM Cloud Services

Hello,

I am trying to get my camera to talk to the IBM cloud services, I started by attempting a MQTT connection to the IOT service. However, when I attempt an connection I get the error:

Traceback (most recent call last):
File “”, line 26, in
File “mqtt.py”, line 100, in connect
MQTTException: 5

client = MQTTClient("Arduino-1", "t29uom.messaging.internetofthings.ibmcloud.com", user ="use-token-auth", password = "password", port=1883)
client.connect()

I’ve found much headache in this process, I am thinking of just using the python SDK for IOT watson, however this requires a pip install of the library. I found a micropython version of library and wanted to see if it was possible to import the library and if so how would i go about that? I can physically download the library files but wasn’t sure where to put the files.

Also does the library have to be micro python?

Hi, you just copy the python file to the SD card or internal flash. Then import the module.

MQTT should work. The library for that is on GitHub under scripts/libraries/MQTT.py

Okay, well noted. Does the library have to be micropython? or can any python library be imported?

MicroPython is python3. So, sure, but, all the depencies need to be met. Which may not be in MicroPython.

Right, sorry I am a bit new to this. How would I go about doing a pip install within the IDE? As i found the dependencies, but instead of manually adding them all I wanted to see if I could pip install the library.

Also where are all the imports stored file wise?

There’s no pip. You just drag the python file onto the upy disk on the camera.

MicroPython is Python3 on a Microcontroller, so, all the package management is not there. However, please see the documentation for all the built-in libraries.

Thank you! So i was successfully able to get the device to speak with IOT IBM Cloud service. I really appreciate your help and patience. I am very new to this type of environment.
As that being completed my last part of communication to IBM cloud services entails using their visual recognition API.

import time, network
import urequests as requests
import json
import ujson

subscription_key = "********"
url = "https://api.us-south.visual-recognition.watson.cloud.ibm.com/instances/05b87183-302f-4038-a81e-4a6720ea34c0"
bearerToken = subscription_key

SSID='Karam2' # Network SSID
KEY='******'  # Network key

# Init wlan module and connect to network
print("Trying to connect... (may take a while)...")

wlan = network.WINC()
wlan.connect(SSID, key=KEY, security=wlan.WPA_PSK)

# We should have a valid IP now via DHCP
print(wlan.ifconfig())
files = {'media': open('test.jpg', 'rb')}

header = {'Authorization': 'Bearer {}'.format(bearerToken)}

r = requests.post(url, headers=header, data=files)
r.text
print(r)

This is my current code. However, I keep getting the error:

mbedtls_ssl_handshake error: -7780
Traceback (most recent call last):
File “”, line 34, in
File “urequests/init.py”, line 115, in post
File “urequests/init.py”, line 60, in request
OSError: [Errno 5] EIO

Hi, an you post the code which has an error? urequests is an external lib.

import usocket

class Response:

    def __init__(self, f):
        self.raw = f
        self.encoding = "utf-8"
        self._cached = None

    def close(self):
        if self.raw:
            self.raw.close()
            self.raw = None
        self._cached = None

    @property
    def content(self):
        if self._cached is None:
            try:
                self._cached = self.raw.read()
            finally:
                self.raw.close()
                self.raw = None
        return self._cached

    @property
    def text(self):
        return str(self.content, self.encoding)

    def json(self):
        import ujson
        return ujson.loads(self.content)


def request(method, url, data=None, json=None, headers={}, stream=None):
    try:
        proto, dummy, host, path = url.split("/", 3)
    except ValueError:
        proto, dummy, host = url.split("/", 2)
        path = ""
    if proto == "http:":
        port = 80
    elif proto == "https:":
        import ussl
        port = 443
    else:
        raise ValueError("Unsupported protocol: " + proto)

    if ":" in host:
        host, port = host.split(":", 1)
        port = int(port)

    ai = usocket.getaddrinfo(host, port)
    ai = ai[0]

    s = usocket.socket(ai[0], ai[1], ai[2])
    try:
        s.connect(ai[-1])
        if proto == "https:":
            s = ussl.wrap_socket(s, server_hostname=host)
        s.write(b"%s /%s HTTP/1.0\r\n" % (method, path))
        if not "Host" in headers:
            s.write(b"Host: %s\r\n" % host)
        # Iterate over keys to avoid tuple alloc
        for k in headers:
            s.write(k)
            s.write(b": ")
            s.write(headers[k])
            s.write(b"\r\n")
        if json is not None:
            assert data is None
            import ujson
            data = ujson.dumps(json)
            s.write(b"Content-Type: application/json\r\n")
        if data:
            s.write(b"Content-Length: %d\r\n" % len(data))
        s.write(b"\r\n")
        if data:
            s.write(data)

        l = s.readline()
        #print(l)
        l = l.split(None, 2)
        status = int(l[1])
        reason = ""
        if len(l) > 2:
            reason = l[2].rstrip()
        while True:
            l = s.readline()
            if not l or l == b"\r\n":
                break
            #print(l)
            if l.startswith(b"Transfer-Encoding:"):
                if b"chunked" in l:
                    raise ValueError("Unsupported " + l)
            elif l.startswith(b"Location:") and not 200 <= status <= 299:
                raise NotImplementedError("Redirects not yet supported")
    except OSError:
        s.close()
        raise

    resp = Response(s)
    resp.status_code = status
    resp.reason = reason
    return resp


def head(url, **kw):
    return request("HEAD", url, **kw)

def get(url, **kw):
    return request("GET", url, **kw)

def post(url, **kw):
    return request("POST", url, **kw)

def put(url, **kw):
    return request("PUT", url, **kw)

def patch(url, **kw):
    return request("PATCH", url, **kw)

def delete(url, **kw):
    return request("DELETE", url, **kw)

Here is the code, however, I believe I found the issue, it was regarding credentials. I got a bit further and now am getting a b’{“code”:401, “error”: “Unauthorized”}’ response. After some research many people recommend adding authorization in the requests.post:
requests.post(url, auth=(‘apikey’, key), data=data)

However, urequests.throws an error on “auth”: TypeError: unexpected keyword argument ‘auth’

Use this instead:

It supports headers.

I reviewed the python files and imported them and am honestly am very confused on implementation the documentation is brief. I was successfully able to get my code to work in python 3 using requests. However, I am confused how to do it with urrlib. This is my code:

import network
import json
import sys
import urllib
import urequests as requests


SSID='Karam2' # Network SSID
KEY='xxxxx'  # Network key

# Init wlan module and connect to network
print("Trying to connect... (may take a while)...")

wlan = network.WINC()
wlan.connect(SSID, key=KEY, security=wlan.WPA_PSK)

# We should have a valid IP now via DHCP
print(wlan.ifconfig())


url = 'https://gateway.watsonplatform.net/visual-recognition/api/v4/analyze'
key = 'xxxxxx'
params = (
    ('version', '2019-02-11'),
)

files = {
    'features': (None, 'objects'),
    'threshold': (None, '0.4'),
    'collection_ids': (None, 'af9f4b2d-6d2f-4606-a746-977076000aae'),
    'images_file': ('test.jpg', open('test.jpg', 'rb')),
}

response = requests.post(url, params=params, data=files, auth=('apikey', key))
print(response.json)
print(response.text)

print(response.json)
print(response.text)

This is my current code for openMV and have had no luck, I also wanted to note that instead of files=files i did data=files, for there was no files parameter, nonetheless I keep getting the error:
Traceback (most recent call last):
File “”, line 38, in
File “urequests.py”, line 107, in post
File “urequests.py”, line 124, in urlopen
File “urequests.py”, line 23, in init
File “urequests.py”, line 193, in b64encode
TypeError: expected bytes, not str

If you could show me how to format my request.post that would be absolutely amazing! I feel that I am very close to accomplishing this.

Hi, yeah, you can see the code in the library I linked… and if you looked at it the library doesn’t support the option you want.

It looks like you’re trying to do multi-part http uploads correct? There’s no support for that in this library.

It has the ability to send a data package string however.

So, what you want to do is totally doable. However, you need to implement the “files” option to urequests yourself.

See the source for requests:

Start from here:

And github now supports following code. So, if you click on a function it will take you to it.

But, that’s honestly really confusing to follow. Multi-part http requests are not that hard to just generate by hand. You can just manually write out all the headers and etc. you need to do.

https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html

I apologize for this being so difficult right now. It’s on my todo list to make this much easier. Note that the point of the OpenMV project wasn’t to re-invent the wheel. Our focus has never been trying to support all the web standards. People want us too however… but, the goal was to do computer vision on microcontrollers. As such, our connectivity isn’t the best.

Thank your for reply, I have to say I am still very confused. I took the day to try to understand how to write a post request by hand in micopython and am very lost, if i write it by had does this mean I don’t need urequests and am I writing in the main file of code. Also am I able to factor in authentication for the api key. Is there any example code you could share or write. I would very much greatly appreciate it.

Hi, you have to manually generate the body of a http post request.

Um, so, an http post request opens a socket with the remote server and just sends a series of lines of text with \r\n at the end.

The first number of these are called headers. The requests module is just generating these headers for you versus you having to supply them.

Then the body typically contains more information or more headers. Which are just \r\n lines. The separation of the headers and body is specified by a blank line between the two.

When you specified the files argument… that typically gets parsed by the library and turned into a sequence of headers and data for you. However, we have no support for that. So, you have to manually do it yourself.

E.g. see this: What should a Multipart HTTP request with multiple files look like? - Stack Overflow

That is what requests is actually generating for you under the hood.

So, the if you have to generate it yourself and make the body part yourself then you need to make the:

--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f
Content-Disposition: form-data; name="datafile1"; filename="r.gif"
Content-Type: image/gif

GIF87a.............,...........D..;
--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f
Content-Disposition: form-data; name="datafile2"; filename="g.gif"
Content-Type: image/gif

GIF87a.............,...........D..;
--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f
Content-Disposition: form-data; name="datafile3"; filename="b.gif"
Content-Type: image/gif

GIF87a.............,...........D..;
--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f--

Part of the file which is the data payload. However, in order to generate a mulit-part http form request you have to use this header for the content:

Content-Type: multipart/form-data; boundary=2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f
Content-Length: 514

Notes: The “GIF87a…,…D…;” is the raw file data. So, basically, you write out the header:

Content-Disposition: form-data; name="datafile2"; filename="g.gif"\r\n
Content-Type: image/gif\r\n\r\n

And then you write out the raw data of the file.

And then you add the boundary:

–2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f

The boundary is a random string that should not appear in the raw data that you specify in the “multipart/form-data;” header.

Yes, this is low-level internet programming. However, if you know what format the remote server expects then everything will work.

Hello,

I have taken a lot of time to try to understand how to work with low level programming and have just lost my self. I think I am going to to just get a raspberry pi to talk to the openmv cam and transfer data back and forth.

I wanted to create another post for this but I have a raspberry pi 3 with a fresh install of raspberry pi OS 32 bit. I wanted to ask where I could find a tutorial on how to properly get the ide running, because after downloading the rasberry pi IDE version, and running the setup.sh and the Openmvide program I have no luck. I followed this: 2. Software Setup — MicroPython 1.15 documentation. I must be missing something.

Also I’m running the IDE on the pi for ease of use instead of UART I want to communicate using the USB and would also appreciate it if you could reference me to how achieve communication back and forth through that.

You don’t need the IDE on the Pi.

Just use this:

It will give you access to the camera over USB.

  1. I thought to have the IDE running on the pi for ease of use instead of jumping back and forth between programming the device using the IDE on the windows machine to plugging it back on the pi for testing.

    \

Great! I downloaded the rpc folder on to my raspberry pi, and installed all the dependencies in the documentation. Now when I first ran the image stream example, after selecting my port I got the error

Traceback (most recent call last):
File “/home/pi/Desktop/Test/rpc_image_transfer_jpg_as_the_controller_device.py”, line 98, in
screen = pygame.display.set_mode((screen_w, screen_h), flags=pygame.RESIZABLE)
TypeError: set_mode() takes no keyword arguments

I went ahead and removed the flags parameter and when I run it I just get a black screen.

I tried the image transfer non stream example and get the same thing, and a print of 0 fps.

Also nothing has been edited to the example otherwise what I mentioned, does something need to be uploaded to the openmv sensor on the ide side?

Hi, you don’t need the IDE for these examples, however, you do need to load the script that pairs with these examples onto the OpenMV Cam. As mentioned in the first comment in the script.

Regarding the flag… Weird, that means the version of pygame you have is old.

The RPC library is a transport medium to move data from the camera to another system. The USB link can be a pain to work with… But, it’s operational.

I’ll be releasing C libraries for the Arduino and Pi shortly.

Regarding your python request module issues. I have a plan to work on that soon after getting the Arduino interface library out.

Hey Quick question I am trying to save a snapshot to my C: drive from the IDE but am running into trouble
sensor.snapshot().save(“C:\Test”)

OSError: The path name format is invalid
Any recommendations?