Timestamp filenames

Discussion related to "under the hood" OpenMV topics.
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Timestamp filenames

Postby matiasandina » Mon Mar 02, 2020 3:15 pm

I would like to create a timestamp for my captured images.
I wrote these helper functions to do so.

Code: Select all

def year(datetime):
    return str(datetime[0])

def month(datetime):
        return str('%02d' % datetime[1])

def day(datetime):
        return str('%02d' % datetime[2])

def hour(datetime):
    return str('%02d' % datetime[4])

def minute(datetime):
    return str('%02d' % datetime[5])

def second(datetime):
    return str('%02d' % datetime[6])

def timestamp(datetime):
    # join date with "-" so that yyyy-mm-dd
    date_string = '-'.join([year(datetime), month(datetime), day(datetime)])
    # join times with ":" so that HH:MM:SS
    time_string = ':'.join([hour(datetime), minute(datetime), second(datetime)])
    # join strings so that date goes ISO format with "T" in the middle
    return "T".join([date_string, time_string])

def create_filename(datetime, ext=".jpg"):
    return timestamp(datetime) + "_capture" + ext
    
On my while loop, I would like to do something like

Code: Select all

# import ...
rtc = pyb.RTC()

while(True):
    clock.tick()
    img = sensor.snapshot()
    # print(rtc.datetime())
    # print(create_filename(rtc.datetime()))
    img.save(create_filename(rtc.datetime())
I have 2 questions:

1) When I try to do this, I get `OSError: The path name format is invalid`. I think I was able to track this error down to the files containing ":" on their names. Is this the actual reason? I know I could use another pattern (ie., "-" or "_"), but why is this an issue? I was trying to recreate the behavior I get from datetime on python, where I would regularly do `datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "_capture.jpg"`

2) I notice the datetime is wrong, what is the way to set it up?

I'm going through the docs but I don't fully understand my options
The current calendar time may be set using machine.RTC().datetime(tuple) function, and maintained by following means:

By a backup battery (which may be an additional, optional component for a particular board).
Using networked time protocol (requires setup by a port/user).
Set manually by a user on each power-up (many boards then maintain RTC time across hard resets, though some may require setting it again in such case).

If actual calendar time is not maintained with a system/MicroPython RTC, functions below which require reference to current absolute time may behave not as expected.
Could you point me to maintain the clock time, either through an external battery or through network protocol. For network protocol, do I need to add a network shield ?
I would have my openMV cams running `main.py` and saving correctly timestamped images to the SD card, while getting power from a raspberry Pi through the USB cable. If possible, I would like to avoid setting up raspberry/openmv control, the raspberry is running other stuff and It's just conveniently close enough to supply power to the openmv board. Otherwise, how is it possible to set up so that time is kept even though the machine is not running?

On the forums I found this question

https://forums.openmv.io/viewtopic.php? ... tamp#p1946

Regarding using I2C sparkfun clocks but also saying that new version (M7 ?) will have a clock incorporated?
User avatar
iabdalkader
Posts: 1168
Joined: Sun May 24, 2015 3:53 pm

Re: Timestamp filenames

Postby iabdalkader » Tue Mar 03, 2020 6:35 am

matiasandina wrote:
Mon Mar 02, 2020 3:15 pm
1) When I try to do this, I get `OSError: The path name format is invalid`. I think I was able to track this error down to the files containing ":" on their names. Is this the actual reason?
Yes, some characters are reserved.
http://elm-chan.org/fsw/ff/doc/filename.html
matiasandina wrote:
Mon Mar 02, 2020 3:15 pm
2) I notice the datetime is wrong, what is the way to set it up?
You can either set it up manually each time you power on the camera, use a WiFi shield and use NTP or use an external RTC that has a battery.
matiasandina wrote:
Mon Mar 02, 2020 3:15 pm
Regarding using I2C sparkfun clocks but also saying that new version (M7 ?) will have a clock incorporated?
Not the RTC Python module was disabled at some point, I was talking about re-enabling it in software. The STM chip already has an RTC, but you need to keep the power connected to keep the time.
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Tue Mar 03, 2020 10:01 am

OK I will have that in mind for filenames.

The RTC absence is bummer. I know this will add cost, but I think it would be nice to have a RTC module powered by a small battery in future iterations of the board.
Setting the time on each power up is not trivial when deployed.
For many applications, datetime is a crucial variable. Not having it complicates data acquisition from the board or requires a 25 to 50 % more investment (I have 10 boards). It's a substantial increase to just to get the time.

How about this possible hack ?

Because I will be powering my cameras from Raspberry Pis, I can code a python script that listens to things happening on the board's directory. The steps would be

- On power up, openmv board would save to text the datetime it has on rtc.datetime(...)
- python listener would check for modifications of that file or/and detect main.py is available
- If not correct, adjust by modifying the lines on main.py
- Trigger a reset ?

My main purpose is to save pictures (thermal images coming from lepton) with correct timestamps, if I don't have the correct time my data is corrupted and unusable.
User avatar
iabdalkader
Posts: 1168
Joined: Sun May 24, 2015 3:53 pm

Re: Timestamp filenames

Postby iabdalkader » Tue Mar 03, 2020 10:58 am

matiasandina wrote:
Tue Mar 03, 2020 10:01 am
The RTC absence is bummer.
Again just to be clear, the STM has an RTC built-in, the board just doesn't have a backup battery to keep it running, so after you set the datetime once you need to keep the power/battery connected so it doesn't get reset. If that's not an option, you need to set the datetime each time you power off/on the board. As to how you can set the datetime from RPI, you can send it from RPI with serial, I2C or USB_VCP.
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Tue Mar 03, 2020 2:19 pm

Yes, I understand it has one but it is not going to function as a clock that can stay on when power off. Hence, my suggestion of having a powered one for a next iteration.

Sorry if this is too basic but can you help me develop a bit more on the USB_VCP connection? I am checking the example and I can't figure it out.
I would use 2 while loops to keep it separate on the main.py that I put on the board.

Code: Select all

from pyb import USB_VCP
usb = USB_VCP()

# Get the time
while(True):
    time_received = usb.readline()
    # unpack the time received
    # set time using rtc.datetime((year, month, day, wday, HH, MM, SS, 0))
    if (condition_is_met):
    # usb.write("OK")
        break
# Do the camera things
while(True):
...
On the python side I would have

Code: Select all

import serial
import datetime
import time
import sys

port = '/dev/ttyACM0'
sp = serial.Serial(port, baudrate=115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,
             xonxoff=False, rtscts=False, stopbits=serial.STOPBITS_ONE, timeout=None, dsrdtr=True)
sp.setDTR(True) # dsrdtr is ignored on Windows.

while True:
	time.sleep(1)
	now = datetime.datetime.now()#.isoformat()
	message = (now.year, now.month, now.day, now.isoweekday(), now.hour, now.minute, now.second)
	print(message, end="\r")
	sp.write(str(message).encode())

	incoming = sp.read().decode("utf-8")

	if(incoming = "OK"):
		break
User avatar
iabdalkader
Posts: 1168
Joined: Sun May 24, 2015 3:53 pm

Re: Timestamp filenames

Postby iabdalkader » Tue Mar 03, 2020 2:35 pm

Yes, basically just block until the datetime is received, then run the main loop.
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Tue Mar 03, 2020 3:12 pm

Maybe I wasn't specific enough.
I cannot make the connection run...not even the simpler version of just send the "OK" and read it on python, could you help me?
User avatar
iabdalkader
Posts: 1168
Joined: Sun May 24, 2015 3:53 pm

Re: Timestamp filenames

Postby iabdalkader » Tue Mar 03, 2020 4:28 pm

matiasandina wrote: Maybe I wasn't specific enough.
I cannot make the connection run...not even the simpler version of just send the "OK" and read it on python, could you help me?
This isn't specific either, do you get an error a timeout or what happens ? Can you test the unmodified example that comes with the IDE and see if it works ?
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Wed Mar 04, 2020 9:38 am

The unmodified example doesn't work on my end.
At first I got an error about encoding, which prompted me to modify the "snap" string

I added one line

Code: Select all

message = "snap".encode()
sp.write(message)
When I do this, and I call my python script from command line, nothing happens, looking like an infinite while loop that never breaks (similar to the behavior I get when I tried to send "OK" from the board to python). The data never goes to the PC and I get no "img.jpg" written. Same behavior if I do

Code: Select all

sp.write(b'snap')

On the python side, If I add

Code: Select all

print(size)
I see

1885433459

If I do print(img) I see nothing and console looking like the infinite loop.
User avatar
kwagyeman
Posts: 3990
Joined: Sun May 24, 2015 2:10 pm

Re: Timestamp filenames

Postby kwagyeman » Wed Mar 04, 2020 12:17 pm

Hi, when using the VCP port you cannot use OpenMV IDE. How are you debugging the camera?
Nyamekye,
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Wed Mar 04, 2020 1:33 pm

Hi,
I have the OpenMV IDE open but not connected to the camera. If I want to modify things on the micropython side, I then connect the camera, upload the script as main.py and disconnect.
I call my python script from commandline as python3 connect_serial.py.

If I don't disconnect I get `Device or resource busy` type of error when I call my python script
User avatar
iabdalkader
Posts: 1168
Joined: Sun May 24, 2015 3:53 pm

Re: Timestamp filenames

Postby iabdalkader » Wed Mar 04, 2020 1:39 pm

You may need to be in a specific group to access the serial port, maybe try it again with sudo ? Note the VCP example hasn't been tested on a RPI before, and I don't have one to test it, but it's known to work on PC for sure. The host side script is meant to be run with Python2 (explains the encoding exception).
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Wed Mar 04, 2020 1:52 pm

I am running on a Desktop computer (Ubuntu), running with sudo gives the same result.
User avatar
iabdalkader
Posts: 1168
Joined: Sun May 24, 2015 3:53 pm

Re: Timestamp filenames

Postby iabdalkader » Wed Mar 04, 2020 2:31 pm

Can you double check the main.py script ? sometimes it gets corrupted, just cat /mnt/disk/main.py
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Wed Mar 04, 2020 2:39 pm

Looks fine

Code: Select all

choilab@choilab-pc:~/Desktop$ cat /media/choilab/4621-1383/main.py
import sensor, image, time, ustruct
from pyb import USB_VCP
usb = USB_VCP()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
while(True):
	cmd = usb.recv(4, timeout=5000)
	if (cmd == b'snap'):
		img = sensor.snapshot().compress()
		usb.send(ustruct.pack("<L", img.size()))
		usb.send(img)
GL-ITC
Posts: 28
Joined: Tue Feb 25, 2020 7:09 am

Re: Timestamp filenames

Postby GL-ITC » Wed Mar 04, 2020 2:46 pm

Have you confirmed you are receiving the snap command on the board? Turn on the led inside the if statement using

Code: Select all

import sensor, image, time, ustruct
from pyb import USB_VCP
usb = USB_VCP()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
while(True):
	cmd = usb.recv(4, timeout=5000)
	if (cmd == b'snap'):
		pyb.LED(3).on()
		img = sensor.snapshot().compress()
		usb.send(ustruct.pack("<L", img.size()))
		usb.send(img)
		time.sleep(1000)
		pyb.LED(3).off()
	
NOTE: I am assuming your using the H7. I can also confirm that a raspberry pi can be used to communicate with the board using USB VCP, I have it working with a raspberry pi 3B using raspbian buster.
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Wed Mar 04, 2020 3:38 pm

Thank you, that's a great idea! I modified a bit the code.
Yes, I am using the H7
This code produces a blinking blue LED that never turns off (so it's not received the "snap" from python)

Code: Select all

import sensor, image, time, ustruct, pyb
from pyb import USB_VCP
usb = USB_VCP()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
while(True):
	pyb.LED(3).on()
	time.sleep(150)
	pyb.LED(3).off()
	time.sleep(100)
	pyb.LED(3).on()
	cmd = usb.recv(4, timeout=5000)
	if (cmd == b'snap'):
		pyb.LED(3).off()
		img = sensor.snapshot().compress()
		usb.send(ustruct.pack("<L", img.size()))
		usb.send(img)
GL-ITC
Posts: 28
Joined: Tue Feb 25, 2020 7:09 am

Re: Timestamp filenames

Postby GL-ITC » Wed Mar 04, 2020 3:42 pm

Correct. Can you add the code your running from your desktop so I can see how you use the usb port on the desktop?
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Wed Mar 04, 2020 3:47 pm

Here's the code

Code: Select all

import sys, serial, struct
port = '/dev/ttyACM0'
sp = serial.Serial(port, baudrate=115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,
            xonxoff=False, rtscts=False, stopbits=serial.STOPBITS_ONE, timeout=None, dsrdtr=True)
sp.setDTR(True) # dsrdtr is ignored on Windows.
sp.write(b"snap")
sp.flush()
size = struct.unpack('<L', sp.read(4))[0]
img = sp.read(size)
sp.close()

with open("img.jpg", "w") as f:
    f.write(img)
GL-ITC
Posts: 28
Joined: Tue Feb 25, 2020 7:09 am

Re: Timestamp filenames

Postby GL-ITC » Wed Mar 04, 2020 3:59 pm

Try the following code which removes the flush operation and only sends a command if the port is open. I also set dsrdtr to False when the port is created. Let me know how it goes.

Code: Select all


import sys, serial, struct
port = '/dev/ttyACM0'
sp = serial.Serial(port, baudrate=115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,
            xonxoff=False, rtscts=False, stopbits=serial.STOPBITS_ONE, timeout=None, dsrdtr=False)
if sp.isOpen() == True:
	sp.setDTR(True) # dsrdtr is ignored on Windows.
	print('Sending command')
	sp.write(b"snap")
	size = struct.unpack('<L', sp.read(4))[0]
	img = sp.read(size)

	with open("img.jpg", "w") as f:
		f.write(img)


matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Wed Mar 04, 2020 4:30 pm

Nice! it finally works.
I had to modify "wb" instead of "w" because we are trying to write binary, but it works.

Code: Select all

with open("img.jpg", "wb") as f:
    f.write(img)
Now I'll to adapt this to the actual use of sending a date
GL-ITC
Posts: 28
Joined: Tue Feb 25, 2020 7:09 am

Re: Timestamp filenames

Postby GL-ITC » Wed Mar 04, 2020 4:31 pm

Glad to hear its working.
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Thu Mar 05, 2020 3:26 pm

Update:

After a significant amount of struggle, here's a code that would set the time on the board. I didn't add the `break` yet, I was wondering whether it would ever be possible to see the camera or I would need to have a separate script to check the camera.
Dealing with openMV is great if I can use it for debug, in my case, because I can't run this on the IDE, it's quite difficult to debug. If my goal is to point the camera at something, I will probably maintain 2 different scripts, one with camera only and one with trigger(time setting) + camera.

Again, I love your product, but this is quite a lot of trouble just to set the time properly...

Micropython side

Code: Select all

import sensor, image, time, pyb
def year(datetime):
    return str(datetime[0])
def month(datetime):
    return str('%02d' % datetime[1])
def day(datetime):
    return str('%02d' % datetime[2])
def hour(datetime):
    return str('%02d' % datetime[4])
def minute(datetime):
    return str('%02d' % datetime[5])
def second(datetime):
    return str('%02d' % datetime[6])
def timestamp(datetime):
    date_string = '-'.join([year(datetime), month(datetime), day(datetime)])
    time_string = '-'.join([hour(datetime), minute(datetime), second(datetime)])
    return "T".join([date_string, time_string])
def create_filename(datetime, ext=".jpg"):
    return timestamp(datetime) + "_capture" + ext
def blink(led_number, sleep_time = 300):
    pyb.LED(led_number).on()
    pyb.delay(sleep_time)
    pyb.LED(led_number).off()

def parse_date(data):
    # date will come in a string with (datetime object)"
    data = data.replace("(", "")
    data = data.replace(")", "")
    # split using the comma and transform the string into integers
    timestamp = [int(x) for x in data.split(",")]
    # the idea is to return a tupple we could give to the rtc
    timestamp = tuple(timestamp)
    return (timestamp)

def write_binary_time(string):
    # mind that the string will be binary, hence "wb"
    # mind the '/', without it it will write to the PC instead of the openmv SD card
    f=open('/binary_time.txt','wb')
    f.write(string)
    f.close()

def write__time(string):
    # mind the '/', without it it will write to the PC instead of the openmv SD card
    f=open('/time.txt','w')
    f.write(string)
    f.close()


# micropython does not have decode, this is a hack
def decode(binary_string):
    return "".join([chr(char) for char in binary_string])


sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
clock = time.clock()
rtc = pyb.RTC()
from pyb import USB_VCP
usb = USB_VCP()

blink(3)
pyb.delay(500)
blink(3)

while(True):
    pyb.LED(3).on()
    clock.tick()
    data = usb.read()
    if data != None:
        if len(data) >= 4:
            # we want to check that starts with date and last element is ")"
            if data[:4] == b'date':
                pyb.LED(3).off()
                # now we parse the time
                time_stamp = data[5:]
                write_binary_time(time_stamp)
                # now decode into string
                time_stamp = decode(time_stamp)
                # and transform to tuple
                tuple_time = parse_date(time_stamp)
                # set the clock!
                rtc.datetime(tuple_time)
                blink(2)
                pyb.delay(100)
                blink(2)
        else:
            continue
    else:
        continue


Python side (Ubuntu)

Code: Select all

import sys, serial, struct
import datetime
import time

port = '/dev/ttyACM0'
# port might be having issues, like being busy/delayed
prort_ready = False
while(prort_ready is False):
	try:
		sp = serial.Serial(port, baudrate=115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,
    	        xonxoff=False, rtscts=False, stopbits=serial.STOPBITS_ONE, timeout=None, dsrdtr=False)
		# break
		prort_ready = sp.isOpen()
	except:
		print("Port busy or unplugged, retrying in two seconds")
		time.sleep(2)
		pass

while(True):
	if sp.isOpen() == True:
		sp.setDTR(True) # dsrdtr is ignored on Windows.
		#print('Sending command')

		# get the date 
		now = datetime.datetime.now()#.isoformat()
		# we send subsecond 0 for simplicity, we don't need to be THAT accurate
		# year, month, day, weekday, hour, minute, second, subsecond
		message = (now.year, now.month, now.day, now.isoweekday(), now.hour, now.minute, now.second, 0)
		# paste date so we can parse
		message = "date: " + str(message)
		print(message.encode())
		sp.write(message.encode())
		time.sleep(1)
User avatar
kwagyeman
Posts: 3990
Joined: Sun May 24, 2015 2:10 pm

Re: Timestamp filenames

Postby kwagyeman » Fri Mar 06, 2020 12:57 am

I understand your pain. However, it wasn't a design goal. It can just do it.

Sigh, so, the IDE could technically do this for you. It's not hard for us to add a command where the IDE tells the camera what time it is.

If you would like this please add a bug in GitHub for it and it may be added. Note that generally we are quite busy with many other things so unless you are hounding in us getting this done it may take a long time to get worked on however.
Nyamekye,
User avatar
iabdalkader
Posts: 1168
Joined: Sun May 24, 2015 3:53 pm

Re: Timestamp filenames

Postby iabdalkader » Fri Mar 06, 2020 5:57 am

Setting the time with the IDE is possible but it still won't help with the issue of maintaining the time....It's just much easier if you use NTP (with our WiFi shield or ESP) or use an external I2C RTC. I found cheaper alternatives on ebay ($0.5-$1.5 range) ex: https://www.ebay.com/itm/2PCS-I2C-RTC-D ... 1648127571
I think in a future revision, we could break out VBAT on a pin and add a solder jumper so you can cut the VBAT trace and connect it to an external battery.
matiasandina
Posts: 14
Joined: Mon Mar 02, 2020 1:33 pm

Re: Timestamp filenames

Postby matiasandina » Fri Mar 06, 2020 9:32 am

Thank you both. Your work is really great!

I think that for anybody running the cam out of the IDE, setting the cam clock would be nice. I opened an issue in GitHub, I understand you guys have other stuff of higher priority, but just for keeping it there.
It's true that relying on the IDE does not solve the keeping time option. I need to deploy this ASAP and still waiting for the lepton 3.5 to ship...but will explore the real time clock option for later. Since my application needs a "begin trigger" I would anyhow need to run this connected to a computer (on-site raspberry Pi) so my hack can bypass the need for keeping time when unplugged. Without a trigger I would just save a bunch of worthless pictures and create a mess for myself to clean later.

Return to “Technical Discussion”

Who is online

Users browsing this forum: No registered users and 6 guests