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)