diff --git a/docs/index.rst b/docs/index.rst index f2ed3fbc8..4b3e9a2a4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -78,6 +78,7 @@ Projects related to MicroPython on the BBC micro:bit include: ble.rst button.rst compass.rst + log.rst display.rst filesystem.rst i2c.rst diff --git a/docs/log-html-view.jpeg b/docs/log-html-view.jpeg new file mode 100644 index 000000000..f9b736c36 Binary files /dev/null and b/docs/log-html-view.jpeg differ diff --git a/docs/log-my_data.png b/docs/log-my_data.png new file mode 100644 index 000000000..b64e01a91 Binary files /dev/null and b/docs/log-my_data.png differ diff --git a/docs/log.rst b/docs/log.rst index e0217cb25..6a95a1469 100644 --- a/docs/log.rst +++ b/docs/log.rst @@ -1,106 +1,114 @@ -Data logging **V2** +Data Logging **V2** ******************* .. py:module:: log -This module lets you log data to a ``MY_DATA`` file saved on a -micro:bit **V2**. +This module lets you log data to a ``MY_DATA`` file saved on a micro:bit +**V2** ``MICROBIT`` USB drive. -Further guidance on the feature can be found on the -`data logging page of the microbit.org website `_. +.. image:: log-my_data.png + +The data is structured in a table format and it can be viewed and plotted with +a browser. + +.. image:: log-html-view.jpeg + +Further guidance on this feature can be found on the +`data logging page of the microbit.org website +`_. Functions ========= -.. py:function:: set_labels(value, timestamp=log.MILLISECONDS) +.. py:function:: set_labels(*labels, timestamp=log.SECONDS) + + Set up the log file header. - Set up the log file header. + This function accepts any number of positional arguments, each creates + a column header, e.g. ``log.set_labels("X", "Y", "Z")``. - Each call to this function with positional arguments will generate a new - header entry into the log file. + Ideally this function should be called a single time, before any data is + logged, to configure the data table header once. - If the programme starts and a log file already exists it will compare the - labels setup by this function call to the last headers declared in the - file. If the headers are different it will add a new header entry at the - end of the file. + If a log file already exists when the programme starts, or if this function + is called multiple times, it will check the labels already defined in the + log file. + If this function call contains any new labels not already present, it will + generate a new header row with the additional columns. -* **value**: Positional arguments used to generate the log headers, - which go on the first line of the CSV file. For example, ``set_labels("A","B","C")`` - will create three column headers titled ``A``, ``B`` and ``C`` in that order. -* **timestamp**: Select the timestamp unit that will be automatically - added as the first column in every row. Timestamp values can be one of - ``MILLISECONDS``, ``SECONDS``, ``MINUTES``, ``HOURS``, ``DAYS`` or ``None`` to - disable the timestamp. + By default the first column contains a time stamp for each row. The time + unit can be selected via the ``timestamp`` argument, e.g. + ``log.set_labels("temp", timestamp=log.MINUTES)`` -.. py:function:: set_mirroring(value) + :param \*labels: Any number of positional arguments, each corresponding to + an entry in the log header. + :param timestamp: Select the timestamp unit that will be automatically + added as the first column in every row. Timestamp values can be one of + ``log.MILLISECONDS``, ``log.SECONDS``, ``log.MINUTES``, ``log.HOURS``, + ``log.DAYS`` or ``None`` to disable the timestamp. The default value + is ``log.SECONDS``. - Mirrors the datalogging activity to the serial output. - Serial mirroring is off by default. +.. py:function:: set_mirroring(serial) -* **value**: Boolean. ``True`` will enable mirroring data to the serial output. + Configure mirroring of the data logging activity to the serial output. + + Serial mirroring is disabled by default. When enabled, it will print to + serial each row logged into the log file. + + :param serial: ``True`` enables mirroring data to the serial output. .. py:function:: delete(full=False) - Deletes the contents of the log, including headers. - To add the log headers the ``set_labels`` function has to be called again - after this. + Delete the contents of the log, including headers. + + To add the log headers again the ``set_labels`` function should to be + called after this function. -* **full**: Selects a "full" erase format that removes the data from the - flash storage. If set to ``False`` it uses a "fast" method, - which invalidates the data instead of performing a slower - full erase. + There are two erase modes; "full" completely removes the data from the + physical storage, and "fast" invalidates the data without removing it. -.. py:function:: add({key:value}) - add(key=value) - - There are two ways to add a data row into the log: + :param full: ``True`` selects a "full" erase and ``False`` selects the + "fast" erase method. - 1. From a positional argument dictionary (key == label) - - e.g. log.add({ 'temp': microbit.temperature() }) +.. py:function:: add( data_dictionary, /, *, **kwargs) - 2. From keyword arguments (argument name == label) - - e.g. log.add(temp=microbit.temperature()) + Add a data row to the log. - Each call to this function adds a row to the log. + There are two ways to log data with this function: - New data labels (dictionary keys or keyword arguments) not already - specified via the `set_labels` function, or by a previous call to this - function, will trigger a new header entry to be added to the log with - the extra label. + #. Via keyword arguments, each argument name representing a label. - Labels previously specified and not present in this function call will be - skipped with an empty value in the log row. + * e.g. ``log.add(X=compass.get_x(), Y=compass.get_y())`` -Example -======= + #. Via a dictionary, each dictionary key representing a label. -An example that runs through some of the functions of the log module API:: + * e.g. ``log.add({ "X": compass.get_x(), "Y": compass.get_y() })`` + + The keyword argument option can be easier to use, and the dictionary option + allows the use of spaces (and other special characters), that could not be + used with the keyword arguments. + + New labels not previously specified via the ``set_labels`` function, or by + a previous call to this function, will trigger a new header entry to be + added to the log with the extra labels. + + Labels previously specified and not present in a call to this function will + be skipped with an empty value in the log row. + +Examples +======== + +A minimal example:: from microbit import * import log - # Creates a new "log" file with the given "headers", timestamp added by default - log.set_labels('temperature', 'brightness') - - # Configuring a different time unit for the timestamp - log.set_labels('temperature', 'brightness', timestamp=log.SECONDS) - - # Enables the serial mirroring - log.set_mirroring(True) - - # Set the timer to log data every 1h20m30s50ms - run_every(h=1, min=20, s=30, ms=50) - - while True: - if button_a.is_pressed() and button_b.is_pressed(): - log.delete(full=True) - elif button_a.is_pressed(): - # Records the temperature & brightness every 00:01:20:30:50 (dd:hh:mm:ss:ms). - log.add({ - "temperature": temperature(), - "brightness": display.read_light_level() - }) - display.show(Image.HAPPY) - sleep(500) - else: - display.show(Image.CONFUSED) + # Set the timer to log data every 5 seconds + @run_every(s=5) + def log_temp(): + log.add(temp=temperature()) + +An example that runs through all of the functions of the log module API: + +.. include:: ../examples/data-logging.py + :code: python diff --git a/docs/microbit.rst b/docs/microbit.rst index 8f5bf8735..e693866d7 100644 --- a/docs/microbit.rst +++ b/docs/microbit.rst @@ -89,7 +89,6 @@ Modules compass.rst display.rst i2c.rst - log.rst microphone.rst speaker.rst spi.rst diff --git a/examples/data-logging.py b/examples/data-logging.py new file mode 100644 index 000000000..fcf0fdbd2 --- /dev/null +++ b/examples/data-logging.py @@ -0,0 +1,32 @@ +from microbit import * +import log + +# Configure the labels and select a time unit for the timestamp +log.set_labels('temp', 'brightness', timestamp=log.SECONDS) + +# Send each data row to the serial output +log.set_mirroring(True) + +# This decorator schedules this function to run every 10s 50ms +@run_every(s=10, ms=50) +def log_data(): + """Log the temperature and light level, and display an icon.""" + log.add(temp=temperature(), brightness=display.read_light_level()) + display.show(Image.SURPRISED) + sleep(500) + +while True: + if button_a.is_pressed() and button_b.is_pressed(): + display.show(Image.MEH) + # Delete the log file using the "full" options, which takes + # longer but ensures the data is wiped from the device + log.delete(full=True) + elif button_a.is_pressed(): + display.show(Image.HAPPY) + # Log only the light level, the temp entry will be empty + log.add({ + "brightness": display.read_light_level() + }) + else: + display.show(Image.CONFUSED) + sleep(500)