Timestamp filenames

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

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

# 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

Regarding using I2C sparkfun clocks but also saying that new version (M7 ?) will have a clock incorporated?

Yes, some characters are reserved.
http://elm-chan.org/fsw/ff/doc/filename.html

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.

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.

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.

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.

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.

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

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

Yes, basically just block until the datetime is received, then run the main loop.

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 ?

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

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

sp.write(b'snap')

On the python side, If I add

print(size)

I see

1885433459

If I do print(img) I see nothing and console looking like the infinite loop.

Hi, when using the VCP port you cannot use OpenMV IDE. How are you debugging the camera?

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

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).

I am running on a Desktop computer (Ubuntu), running with sudo gives the same result.

Can you double check the main.py script ? sometimes it gets corrupted, just cat /mnt/disk/main.py

Looks fine

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)

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

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.

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)

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)

Correct. Can you add the code your running from your desktop so I can see how you use the usb port on the desktop?

Here’s the code

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)

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.

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)