Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Adapted and simplified from Franck Albinet's exercises available here. Also presented by NSRC at APRICOT 2018.

...

While one of the primary purpose of IoT is to collect and exchange data over an inter-connected network, it is as well important to be able to persist information in the IoT device itself: log files of device's activity, Received Signal Strength Activity (RSSI), ... In the case of high-resolution measurements, it's useful to store them before sending them over the network, in order to save on power.

Learning outcomes

You will learn how to: access and operate device's file system; create and write a file in the flash folder; mount and use an SD card as an alternative; handle files safely using with statement; generate programmatically time stamped log file names; make your code robust and flexible with try ... except statement.

...

  • a LoPy or WiPy module
  • a microUSB cable
  • a development PC

The source code is in the src/micro-sd directoryavailable here.

Folder structure

The folder tree is the following:

...

Let's explore and navigate this folder structure interactively. Connect to a Lopy via the Atom console and import upload the source code for this exercise.

Import the basic operating system module (os), then try a few commands:

Code Block
languagepy
titleBasic File System Operations
linenumberstrue
import

...

 os
# to find you current working directory:
os.getcwd()

...

 ## most probably the

...

 /flashfolder
# to list folders and files in your current working directory:
os.listdir()

...


# to create a new folder/directory named "log":
os.mkdir('log')

...

Take a look at os module documentation for a full list of methods.

Now notice that if you list the files and folders under the root folder: os.listdir('/'),you get only the flash directory. There is no SD card mounted yet.

Writing

In the simplest case, to create and write a new file:

...

For further reference on reading and writing files in Python, look at the official documentation here.

But in In essence to handle files in Python, you first need to open a file (even if it does not exist yet)




Code Block
languagepy
linenumberstrue
python f = open('log/my_first_file.log', 'w')


the open function takes as argument: file name 'log/my_first_file.log' (relative or full path) and mode: read, write, ...


Once open, you get a file object to play with and hence can start writing data in it:python  

Code Block
languagepy
linenumberstrue
f.write('Testing write operations in a file.')

Then you need to close the file to free up any system resources taken up by the open file. After calling

Code Block
languagepy
linenumberstrue
f.close()

...

attempts to use the file object will automatically fail.

...

For instance to read the file just created, you can use the following syntax:

Code Block
languagepy
linenumberstrue
with open('log/my_first_file.log', 'r') as f:
    f.readall()

This is much cleaner and safer.

Finally, before creating a folder or a file, we would like to test if it exists already. The code below test it and recap. the whole process:{code

Code Block
languagepy
titlemain.py under `src/micro-sd/flash` directory:
linenumberstrue
import os
file_path = '/flash/log'

try:
    os.listdir('/flash/log')
    print('/flash/log file already exists.')
except OSError:
    print('/flash/log file does not exist. Creating it ...')
    os.mkdir('/flash/log')

name = '/my_first_file.log'

# Writing
with open(file_path + name, 'w') as f:
    f.write('Testing write operations in a file.')

# Reading
with open(file_path + name, 'r') as f:
    print(f.readall())

...

Code Block
languagepy
linenumberstrue
import time 

year, month, day, hour, minute, second, ms, dayinyear = time.localtime()

nameCsv = '/flash/log/acq'
nameCsv = nameCsv + '{:04d}'.format(year)
nameCsv = nameCsv + '{:02d}'.format(month)
nameCsv = nameCsv + '{:02d}'.format(day)
nameCsv = nameCsv + '{:02d}'.format(hour)
nameCsv = nameCsv + '{:02d}'.format(minute)
nameCsv = nameCsv + '{:02d}'.format(second)
nameCsv = nameCsv + 'list.csv'

A second approach more succinct would be to take advantage of Python list comprehensions:

Code Block
languagepy
linenumberstrue
base = '/flash/log/acq/'
time_stamp = ''.join(['{:02d}'.format(i) for i in time.localtime()][:6])
name = base + time_stamp + 'list.csv'

Let's unpack this second

...

implementation:

Code Block
languagepy
linenumberstrue
time.localtime()

outputs a tuple (1970, 1, 1, 0, 21, 40, 3, 1) representing year, month, ...

Code Block
languagepy
linenumberstrue
# formatting integer to string with list comprehension
['{:02d}'.format(i) for i in time.localtime()

outputs ['1970', '01', '01', '00', '24', '05', '03', '01']

Code Block
languagepy
linenumberstrue
# We keep only year, month, day, hour, min., sec. by slicing the list:
['{:02d}'.format(i) for i in time.localtime()][:6]


Code Block
languagepy
linenumberstrue
# last we join the list to a single string
''.join(['{:02d}'.format(i) for i in time.localtime()][:6])

outputs: '19700101002920'

We can now simply concatenate this substring with a prefix and suffix and that's done.

As it is a quite frequent operation, we could even encapsulate it in an helper function as below:

Code Block
languagepy
linenumberstrue
def get_log_filename(prefix, suffix):
    time_stamp = ''.join(['{:02d}'.format(i) for i in time.localtime()][:6])
    return prefix + time_stamp + suffix

and use it when required: get_log_filename('/flash/log/acq', 'list.csv')

Exercise

Write a script writing a file named "log.csv" in /flash/log/ folder so that:

  • if the user pushes the button the pressing time and an incremented counter is saved;
  • it the counter reaches 10, LED is switched on.