{"id":25023256,"url":"https://github.com/corey-richardson/microbit-data-logger","last_synced_at":"2026-05-03T06:38:26.360Z","repository":{"id":173373948,"uuid":"650593281","full_name":"corey-richardson/microbit-data-logger","owner":"corey-richardson","description":"In preparation for Work Experience Students coming in, I am using this project to familiarise myself with the BBC micro:bits which we will provide them with. I am also using it as a chance to expand on my data visualisation with Python experience.","archived":false,"fork":false,"pushed_at":"2023-07-04T07:25:57.000Z","size":37054,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-05T14:40:58.758Z","etag":null,"topics":["data-visualization","matplotlib","microbit","pandas","pyplot","signal-processing"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/corey-richardson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-07T11:53:01.000Z","updated_at":"2023-07-07T20:41:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"aa5e2940-6242-4764-8815-f67ba15eb89d","html_url":"https://github.com/corey-richardson/microbit-data-logger","commit_stats":null,"previous_names":["corey-richardson/microbit-data-logger"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corey-richardson%2Fmicrobit-data-logger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corey-richardson%2Fmicrobit-data-logger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corey-richardson%2Fmicrobit-data-logger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corey-richardson%2Fmicrobit-data-logger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/corey-richardson","download_url":"https://codeload.github.com/corey-richardson/microbit-data-logger/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246320130,"owners_count":20758407,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-visualization","matplotlib","microbit","pandas","pyplot","signal-processing"],"created_at":"2025-02-05T14:39:10.308Z","updated_at":"2026-05-03T06:38:26.336Z","avatar_url":"https://github.com/corey-richardson.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Data Logger Project\n\n[![Checker](https://github.com/corey-richardson/microbit-data-logger/actions/workflows/check.yaml/badge.svg)](https://github.com/corey-richardson/microbit-data-logger/actions/workflows/check.yaml)\n\n---\n\n## Contents\n\n- [aims](#aims)\n- [concerns](#concerns)\n- [relevant-practice-projects](#relevant-practice-projects)\n- [microbit-xyz-planes-and-orientations](#microbit-xyz-planes-and-orientation)\n- [flashing-to-microbit-using-web-based-text-editor](#flashing-to-microbit-using-web-based-text-editor)\n\u003cbr\u003e\u003cbr\u003e\n\nData Logging Visualisation via Serial Port\n- [visualing-the-microbit-data-via-usb-cable](#visualing-the-microbit-data-via-usb-cable)\n    - [serial-port-writer-mainpy](#serial-port-writer-mainpy)\n    - [serial-port-reader-and-realtime-visualiser](#serial-port-reader-and-realtime-visualiser)\n    - [serial-port-reader-and-3d-realtime-visualiser](#serial-port-reader-and-3d-realtime-visualiser)\n\u003cbr\u003e\u003cbr\u003e\n\nData Logging Visualisation via CSV Files\n- [visualing-the-microbit-with-csv-data](#visualing-the-microbit-with-csv-data)\n    - [process](#process)\n    - [csv-data-writer-mainpy](#csv-data-writer-mainpy)\n    - [csv-data-visualiser](#csv-data-visualiser)\n    - [csv-data-3d-visualiser](#csv-data-3d-visualiser)\n\u003cbr\u003e\u003cbr\u003e\n\nOutput GIFS\n- [output](#output)\n    - [serial-port-connection](#serial-port-connection)\n    - [from-csv-data](#from-csv-data)\n\u003cbr\u003e\u003cbr\u003e\n\nFinal Project and Presentation - Archery Data Logger\n- [final-project-and-presentation](#final-project-and-presentation)\n    - [project-selection](#project-selection)\n    - [why](#why)\n    - [plan](#plan)\n    - [what-went-well](#what-went-well)\n    - [lessons-learned](#lessons-learned)\n    - [testing-and-results](#testing-and-results)\n    - [potential-application](#potential-application)\n\n---\n\n## Aims\n\n- Find the process of flashing software to the BBC micro:bit\n- Use one of the micro:bit practice projects to familarise myself with the micro:bit capabilities and process; [data-logging-project](https://microbit.org/get-started/user-guide/data-logging/)\n- Use Python scripts to output the data as graphs.\n    - This could be done as a realtime data logger or as plots taken from `.csv` data.\n- Create a presentation on a \"commercial product\" using the micro:bit, writing about the following stages:\n    - Project Selection\n    - Why?\n    - What Went Well\n    - Lessons Learned\n    - Testing and Results\n    - Potential Application\n\u003e This could be used by the students as an example of what their presentations should consist of.\n- All the while, thnk of how this could be tailored to students of varying experience levels and interests.\n    - MakeCode Blocks vs Python\n    - How could it be linked to their interests?\n\n---\n\n## Concerns\n\n- [x] Is micro:bit storage non-volatile? *(Will the logs be erased when it is unplugged from power?)*\n\u003e Data is stored on your micro:bit even when the power is disconnected. It's easy to access - no software is needed. Plug your micro:bit in to a computer, look in the MICROBIT drive and double-click the MY_DATA file to open it in a web browser.\n\n- [x] Does the micro:bit have enough storage space to record data for the time frames I'm hoping to record?\n\u003e Memory: 128 KB. Flash space: 512 KB.\n\n- [x] Can I install Pip / PyPI Modules on a work laptop?\n    \u003e :x: Pip / PyPI cannot be used to install Python packages on a work laptop. A different idea would be to supply the students with an executable version of the various plotting scripts. These `.exe` files could be created with [auto-py-to-exe](https://pypi.org/project/auto-py-to-exe/). \n    - I believe I have all dependencies other than `PySerial` installed, would a USB transfer from an external device be possible?\n\n- [x] Need to verify if micro:bit will work on work laptop.\n    \u003e :red_circle: Works with USB R/W permissions, not with Read Only Permissions. May have to use non-networked laptops OR the BBC micro:bit app.\n\n- [ ] If the students are going to do data analysis via Excel, it will have to be installed for them onto a non-networked laptop. Same applies if they want to use Python or Matlab etc.\n\n---\n\n## Relevant Practice Projects\n\n- [micro:bit Projects](https://microbit.org/projects/make-it-code-it/)\n- [Meet your micro:bit](https://microbit.org/projects/make-it-code-it/meet-your-microbit/?editor=python)\n- [MakeCode Data Logger](https://microbit.org/projects/make-it-code-it/makecode-wireless-data-logger/)\n- [Python Data Logger](https://microbit.org/projects/make-it-code-it/python-wireless-data-logger/)\n- [Max-Min Temperature Logger](https://microbit.org/projects/make-it-code-it/max-min-thermometer/?editor=makecode)\n\n---\n\n## micro:bit XYZ Planes and Orientation\n\n![orientation](https://cdn.sanity.io/images/ajwvhvgo/production/f8184e1a5b4f3226b8b10902356ae62d988ff698-665x782.png?w=653\u0026q=80\u0026fit=max\u0026auto=format)\n\n---\n\n## Flashing to micro:bit using Web Based Text Editor\n\n[Python micro:bit Web Editor](https://python.microbit.org/v/3)\n\nClick `Send to micro:bit`.\n\n![](/README_assets/1_web_editor.jpg)\n\nFollow instructions on screen.\n\n![](/README_assets/2_connect__cable.jpg)\n\nConnect to micro:bit.\n\n![](/README_assets/3_popup.jpg)\n\n---\n\n## Visualing the micro:bit Data via USB Cable\n\n### Serial Port Writer main.py\n\nThis script is flashed onto the BBC micro:bit\n\n```py\nfrom microbit import *\n\nwhile True:\n    sleep(100) # milliseconds\n    print(accelerometer.get_values())\n```\n\nThe `print` command outputs to the USB serial port to be picked up by the connected laptop or computer.\n\n### Serial Port Reader and Realtime Visualiser\n\n**Imports:** \n\n```py\nfrom matplotlib import pyplot as plt\nfrom matplotlib import animation\nfrom numpy import diff\nimport serial\n```\n\n**Configuration:**\n\nThese constants define the serial port to attempt a connection to, the [Baud Rate](https://en.wikipedia.org/wiki/Baud) of the board, the timeout bit, the number of datapoints to display at a time and the rate at which the plot will update.\n\n$$\\text{LIMIT} \\times \\text{RATE} = \\text{Number of Seconds Displayed}$$\n\n*The Baud Rate to use can be found or changed in the Device Manager.*\n\n![device--manager](/README_assets/device_manager.jpg)\n\n```py\n# Windows Device Manager \u003e Ports (COM \u0026 LPT) \u003e \"mbed Serial Port\"\nPORT = 'COM3'\nBAUD_RATE = 115_200 # ENSURE THIS MATCHES VALUE IN DEVICE MANAGER\nSTOP = 1\n\nLIMIT = 300\nRATE = 50 # ms\n```\n\n**Connect to Serial Port:**\n\nCreate a connection to the board. If the connection fails to open, exit the program.\n```py\nser = serial.Serial(PORT, BAUD_RATE, timeout=STOP)\nser.close()\nser.open()\n```\n\n**Create Figure to Plot:**\n\nCreate a figure and add a subplot at position `111`.\n```py\nfig = plt.figure()\nax = fig.add_subplot(1, 1, 1)\n```\n\n**Initialise Lists for x, y and z Data:**\n\n```py\nxs, ys, zs = [0]*LIMIT, [0]*LIMIT, [0]*LIMIT\n```\n\n**Animate Function:**\n\nCreate the function `animate` that will get called as an argument in `FuncAnimate` later.\n\nRead the line from the serial port connection. This is returned as a `byte` object so needs to be decoded with `.decode(\"utf-8\")`. The parenthesis also need to be removed with `.strip()`. Then, cast the values to ints.\n\nCreate a list `idx` with length `LIMIT`. Append the current values to `xs`, `ys` and `zs`. Then, slice the list to only include the `n` most recent values, where `n` is `LIMIT`.\n\nCalculate the deltas using `Numpy`'s `diff()` function. Then, plot the data and the deltas.\n\nOnce the lists have enough values in them, begin plotting.\n\n```py\ndef animate(i, xs, ys, zs):\n        \n    line = ser.readline().decode(\"utf-8\").strip(\"(\").strip(\")\\r\\n\")\n    x, y, z = line.split(\",\")\n    x, y, z = int(x), int(y), int(z)\n    \n    print(x, y, z)\n    \n    idx = range(LIMIT)\n    xs.append(x)\n    ys.append(y)\n    zs.append(z)\n    \n    xs = xs[-LIMIT:]\n    ys = ys[-LIMIT:]\n    zs = zs[-LIMIT:]\n\n    d_xs = diff(xs)\n    d_ys = diff(ys)\n    d_zs = diff(zs)\n    \n    if len(xs) == LIMIT:\n        ax.clear()\n        d_ax.clear()\n\n        ax.plot(idx, xs, color=\"r\", label=\"X\")\n        ax.plot(idx, ys, color=\"g\", label=\"Y\")\n        ax.plot(idx, zs, color=\"b\", label=\"Z\")\n\n        d_ax.plot(d_idx, d_xs, color=\"r\")\n        d_ax.plot(d_idx, d_ys, color=\"g\")\n        d_ax.plot(d_idx, d_zs, color=\"b\")\n        d_ax.axhline(0, color=\"k\")\n\n    ax.set_ylim(-1500, 1500)\n    d_ax.set_ylim(-1500, 1500)\n\n    ax.legend()\n\n    fig.suptitle(\"micro:bit Data Logger\")\n```\n\n**Create and Plot Animation:**\n\nCreate the animation. This will call the `FuncAnimation` `animate` with arguments `xs`, `ys` and `zs` every `RATE` milliseconds. This occurs until the user causes a `KeyboardInterrupt` exception with \u003ckbd\u003eCtrl\u003c/kbd\u003e+\u003ckbd\u003eC\u003c/kbd\u003e.\n\n```py\nani = animation.FuncAnimation(\n    fig, \n    animate, fargs=(xs, ys, zs), \n    interval=RATE )\n\nplt.show()\n\ntry:\n    pass\nexcept KeyboardInterrupt:\n    ser.close()\n```\n\n### Serial Port Reader and 3D Realtime Visualiser\n\nA lot of this code is repeated from the 2D visualiser. Some exceptions include:\n- `RATE` and `LIMIT` values\n    - I had to increase the `RATE` constant (which decreases the rate - 1/n) and decrease the `LIMIT` constant which controls how many datapoints are displayed in order to avoid overloading the animation function. Even now, eventually the animation falls out of sync with the micro:bit's movements.\n- `ax = plt.axes(projection='3d')`\n    - Creates a 3D Axis to plot data on.\n- `ax.plot3D(xs, ys, zs)`\n    - Creates a line plot with `xs`, `ys` and `zs` as data inputs.\n\n\n```py\nfrom matplotlib import pyplot as plt\nfrom matplotlib import animation\nfrom numpy import diff\nimport serial\n\n# Windows Device Manager \u003e Ports (COM \u0026 LPT) \u003e \"mbed Serial Port\"\nPORT = 'COM3'\nBAUD_RATE = 115_200 # ENSURE THIS MATCHES VALUE IN DEVICE MANAGER\nSTOP = 1\n\nLIMIT = 25\nRATE = 5 # ms\n\nser = serial.Serial(PORT, BAUD_RATE, timeout=STOP)\nser.close()\nser.open()\n\nfig = plt.figure()\nax = plt.axes(projection='3d')\n\nxs, ys, zs = [0]*LIMIT, [0]*LIMIT, [0]*LIMIT\n\ndef animate(i, xs, ys, zs):\n    \n    line = ser.readline().decode(\"utf-8\").strip(\"(\").strip(\")\\r\\n\")\n    x, y, z = line.split(\",\")\n    x, y, z = int(x), int(y), int(z)\n    \n    print(x, y, z)\n    \n    idx = range(LIMIT)\n    xs.append(x)\n    ys.append(y)\n    zs.append(z)\n    \n    xs = xs[-LIMIT:]\n    ys = ys[-LIMIT:]\n    zs = zs[-LIMIT:]\n\n    d_xs = diff(xs)\n    d_ys = diff(ys)\n    d_zs = diff(zs)\n\n    if len(xs) == LIMIT:\n        ax.clear()\n        ax.plot3D(xs, ys, zs, label=\"State\")\n        ax.plot3D(d_xs, d_ys, d_zs, label=\"Deltas\")\n        \n    ax.set_xlim(-1500, 1500)\n    ax.set_ylim(-1500, 1500)\n    ax.set_zlim(-1500, 1500)\n    ax.legend()\n\n    ax.plot3D([-1500, 1500], [0, 0], [0, 0], \"k--\", alpha=0.5)\n    ax.plot3D([0, 0], [-1500, 1500], [0, 0], \"k--\", alpha=0.5)\n    ax.plot3D([0, 0], [0, 0], [-1500, 1500], \"k--\", alpha=0.5)\n        \nani = animation.FuncAnimation(\n    fig, \n    animate, fargs=(xs, ys, zs), \n    interval=RATE )\n\nplt.show()\n\ntry:\n    pass\nexcept KeyboardInterrupt:\n    ser.close()\n```\n\n\n---\n\n## Visualing the micro:bit with CSV Data\n\n### Process\n\n1. Run the data logger via battery power.\n2. When you use the data logging feature on the micro:bit V2, an HTML file is created on the `MICROBIT` drive called `MY_DATA` that lets you interact with the logged data.\n3. View `MY_DATA` in your browser. The page displays a set of buttons to interact with the data and a table containing the data that has been logged so far.\n4. Click the \u003ckbd\u003eDownload\u003c/kbd\u003e button. This downloads the data in `.csv` format using a period `.` as the delimiter.\n5. Run the `/not_connected/visualiser.py` script and follow any instructions that appear on screen.\n\n### CSV Data Writer main.py\n\n**Imports:**\n\n```py\nfrom microbit import *\nimport log, os\n```\n\n**Initialise the columns to log to:**\n\n```py\n# Set up columns for logging\nlog.set_labels('x', 'y', 'z')\n```\n\n**Define the `logger` function:**\n\nThis is the function that records the `x`, `y` and `z` values from the accelerometer into `\"MY_DATA.HTM\"`.\n```py\n# Record the x, y, z values\ndef logger():\n    log.add({\n        'x': accelerometer.get_x(),\n        'y': accelerometer.get_y(),\n        'z': accelerometer.get_z()\n    })\n```\n\n**Main Loop:**\n\nThe script begins by initialising `logging` to be `False`. Then, every 50ms* it will question whether any buttons have been pressed.\n\n\u003e *50ms: assuming unchanged since time of writing; this value may be changed if the log storage fills too quickly.\n\nIf \u003ckbd\u003eA\u003c/kbd\u003e has been pressed, `logging` is set to `True`. \u003cbr\u003e\nIf \u003ckbd\u003eB\u003c/kbd\u003e has been pressed, `logging` is set to `False`. \u003cbr\u003e\n\nIf `logging` is set to `True`, the `logger()` function is called. Since this call is within the outer `while True` loop, this function can be called every ~50ms*.\n\u003e *50ms delay + Loop Runtime\n\n```py\n# Pre initialise 'logging' as False / OFF\nlogging = False\n\nwhile True:\n    sleep(50) # milliseconds\n    # On \"A\" button pressed...\n    if button_a.is_pressed():\n        logging = True\n        display.show(Image.YES)\n        \n    # On \"B\" button pressed...\n    # Set logging to False and update displayed icon\n    if button_b.is_pressed():\n        logging = False\n        display.show(Image.NO)\n\n    # If logging is set to True...\n    # Call the logging function and record values\n    if logging:\n        logger()\n```\n\n### CSV Data Visualiser\n\n**Imports:**\n\n```py\nfrom tkinter import filedialog as fd\nimport pandas as pd\nfrom matplotlib import pyplot as plt\nfrom matplotlib.gridspec import GridSpec\nfrom scipy import signal\n```\n\n**File Selection and Reading:**\n\nOpens a File Explorer window allowing the user to select the `.csv` file to plot. This file is read into a `panda` `Dataframe` Object using a comma `,` as the delimiter. `.reset_index()` creates an `index` column which is used as the x-variable during plotting.\n```py\nfile = fd.askopenfilename(\n    filetypes=[(\"CSV files\", \"*.csv\")],title=\"Set input .csv file\" )\n\ndata = pd.read_csv(file, header=0).reset_index()\n```\n\n**Create Figure:**\n\n```py\nfig = plt.figure(figsize=(10, 10))\ngs = GridSpec(9, 6, figure=fig)\n\nax = fig.add_subplot(gs[0:3, 0:3])\nd_ax = fig.add_subplot(gs[0:3, 3:6])\n\ndx_ax = fig.add_subplot(gs[3:6, 0:2])\ndy_ax = fig.add_subplot(gs[3:6, 2:4])\ndz_ax = fig.add_subplot(gs[3:6, 4:6])\n\nxpeak_ax = fig.add_subplot(gs[6:9, 0:2])\nypeak_ax = fig.add_subplot(gs[6:9, 2:4])\nzpeak_ax = fig.add_subplot(gs[6:9, 4:6])\n\nfig.tight_layout(pad=2.5)\n\nax.axhline(0, color=\"k\")\nd_ax.axhline(0, color=\"k\")\n\ndx_ax.axhline(0, color=\"k\")\ndy_ax.axhline(0, color=\"k\")\ndz_ax.axhline(0, color=\"k\")\n\nxpeak_ax.axhline(0, color=\"k\")\nypeak_ax.axhline(0, color=\"k\")\nzpeak_ax.axhline(0, color=\"k\")\n```\n\n**Calculate Rolling Average:**\n\n```py\nROLLER = 4\nALPHA = 0.3\nrolling = data.rolling(ROLLER).mean()\nrolling = rolling.dropna().reset_index()\n```\n\n**Plot Data (Raw and Rolling Values):**\n\n```py\n# Plot the data\nax.plot(data.index, data.x, \"r--\", label=\"X\", alpha=0.3)\nax.plot(data.index, data.y, \"g--\", label=\"Y\", alpha=0.3)\nax.plot(data.index, data.z, \"b--\", label=\"Z\", alpha=0.3)\n# Plot rolling average data \nax.plot(rolling.index + ROLLER/2, rolling.x, \"r\", label=\"X\")\nax.plot(rolling.index + ROLLER/2, rolling.y, \"g\", label=\"Y\")\nax.plot(rolling.index + ROLLER/2, rolling.z, \"b\", label=\"Z\")\n```\n\n**Calculate Deltas:**\n\n```py\n# Calculate deltas\ndata['dx'] = data['x'] - data['x'].shift(-1)\ndata['dy'] = data['y'] - data['y'].shift(-1)\ndata['dz'] = data['z'] - data['z'].shift(-1)\n\nrolling['dx'] = rolling['x'] - rolling['x'].shift(-1)\nrolling['dy'] = rolling['y'] - rolling['y'].shift(-1)\nrolling['dz'] = rolling['z'] - rolling['z'].shift(-1)\n```\n\n**Plot Deltas (Raw and Rolling Values):**:\n\n```py\n# Plot deltas\nd_ax.plot(data.index, data.dx, \"r--\", label=\"X\", alpha=0.3)\nd_ax.plot(data.index, data.dy, \"g--\", label=\"Y\", alpha=0.3)\nd_ax.plot(data.index, data.dz, \"b--\", label=\"Z\", alpha=0.3)\n# Plot rolling deltas shifted to align with the 'data' values\nd_ax.plot(rolling.index + ROLLER/2, rolling.dx, \"r\", label=\"X\")\nd_ax.plot(rolling.index + ROLLER/2, rolling.dy, \"g\", label=\"Y\")\nd_ax.plot(rolling.index + ROLLER/2, rolling.dz, \"b\", label=\"Z\")\n```\n\n**Plot Deltas Seperately:**\n\nThis is done on both the `dn_ax` and the `npeak_ax` axis'.\n```py\ndx_ax.plot(data.index, data.dx, \"r--\", label=\"X\", alpha=ALPHA)\ndy_ax.plot(data.index, data.dy, \"g--\", label=\"Y\", alpha=ALPHA)\ndz_ax.plot(data.index, data.dz, \"b--\", label=\"Z\", alpha=ALPHA)\n\ndx_ax.plot(rolling.index + ROLLER/2, rolling.dx, \"r\", label=\"X\")\ndy_ax.plot(rolling.index + ROLLER/2, rolling.dy, \"g\", label=\"Y\")\ndz_ax.plot(rolling.index + ROLLER/2, rolling.dz, \"b\", label=\"Z\")\n\nxpeak_ax.plot(data.index, data.dx, \"r--\", label=\"X\", alpha=ALPHA)\nypeak_ax.plot(data.index, data.dy, \"g--\", label=\"Y\", alpha=ALPHA)\nzpeak_ax.plot(data.index, data.dz, \"b--\", label=\"Z\", alpha=ALPHA)\n\nxpeak_ax.plot(rolling.index + ROLLER/2, rolling.dx, \"r\", label=\"X\")\nypeak_ax.plot(rolling.index + ROLLER/2, rolling.dy, \"g\", label=\"Y\")\nzpeak_ax.plot(rolling.index + ROLLER/2, rolling.dz, \"b\", label=\"Z\")\n\n```\n\n**Format the Plot:**\n\nFind the highest magnitude values (positive or negative) for the `x`, `y` and `z` as well as the `dx`, `dy` and `dz` columns of `rolling`. Use these +50% as the y-limits for the axis. The added percentage ensures the data is not crowded with the axis borders.\n```py\n# Find max x, y, z magnitude for rolling data, then +50% as leeway\nax_bound = abs( rolling[[\"x\",\"y\",\"z\"]] ).max().max() * 1.5\nax.set_ylim(-ax_bound, ax_bound)\n# Find max x, y, z magnitude for rolling deltas, then +50% as leeway\nd_bound = abs( rolling[[\"dx\",\"dy\",\"dz\"]] ).max().max() * 1.5\nd_ax.set_ylim(-d_bound, d_bound)\n# Find max x, y, z magnitude for rolling deltas, then +10% as leeway\npeak_bound = abs( rolling[[\"dx\",\"dy\",\"dz\"]] ).max().max() * 1.1\n\n# Set titles and create legends\nfig.suptitle(\"micro:bit Data Logger\")\n\nax.set_title(\"Raw Data\")\nd_ax.set_title(\"Deltas\")\n\ndx_ax.set_title(\"dx\")\ndy_ax.set_title(\"dy\")\ndz_ax.set_title(\"dz\")\n\nxpeak_ax.set_title(\"x_peaks\")\nypeak_ax.set_title(\"y_peaks\")\nzpeak_ax.set_title(\"z_peaks\")\n\ndx_ax.set_ylim(-d_bound, d_bound)\ndy_ax.set_ylim(-d_bound, d_bound)\ndz_ax.set_ylim(-d_bound, d_bound)\n\nax.legend()\nd_ax.legend()\n\n```\n\n**Plot Peaks:**\n\nPlot vertical lines at signal peaks +/- 20; this signifies the zone cropped into on the lower plots.\n```py\nfrom scipy import signal\nTHRESHOLD = 200\nx_peaks, x_props = signal.find_peaks(rolling.dx, threshold=THRESHOLD)\ny_peaks, y_props = signal.find_peaks(rolling.dy, threshold=THRESHOLD)\nz_peaks, z_props = signal.find_peaks(rolling.dz, threshold=THRESHOLD)\n\n# This function plots vertical lines on each of the seperated delta plots to \n# indicate which section of the data is being cropped into on \n# the proceeding plot.\n# It then crops into these areas\n# If no peaks are found it will delete that subplot.\ndef plot_peaks(delta_axis, peak_axis, peaks):\n    try:\n        delta_axis.axvline(peaks[0] - 20, color=\"k\", alpha=0.7)\n        delta_axis.axvline(peaks[0] + 20, color=\"k\", alpha=0.7)\n        # Set the x and y bounds for each of the peak plots\n        peak_axis.set_xlim(peaks[len(peaks)//2] - 20, peaks[len(peaks)//2] + 20)\n        peak_axis.set_ylim(-peak_bound, peak_bound)\n    except IndexError:\n        print(\"No peaks found...\")\n        print(peaks)\n        fig.delaxes(peak_axis)\n\nplot_peaks(dx_ax, xpeak_ax, x_peaks)\nplot_peaks(dy_ax, ypeak_ax, y_peaks)\nplot_peaks(dz_ax, zpeak_ax, z_peaks)\n```\n\n**Crop into peaks:**\n\nUse the 'middle index' peak to decide where to crop into.\n```py\nxpeak_ax.set_xlim(x_peaks[len(x_peaks)//2] - 20, x_peaks[len(x_peaks)//2] + 20)\nypeak_ax.set_xlim(y_peaks[len(y_peaks)//2] - 20, y_peaks[len(y_peaks)//2] + 20)\nzpeak_ax.set_xlim(z_peaks[len(z_peaks)//2] - 20, z_peaks[len(z_peaks)//2] + 20)\n\nxpeak_ax.set_ylim(-peak_bound, peak_bound)\nypeak_ax.set_ylim(-peak_bound, peak_bound)\nzpeak_ax.set_ylim(-peak_bound, peak_bound)\n```\n\u003e Uses integer division.\n```\n\u003e\u003e\u003e 1//2\n0\n\u003e\u003e\u003e 2//2\n1\n\u003e\u003e\u003e 3//2\n1\n\u003e\u003e\u003e 4//2\n2\n```\n### CSV Data 3D Visualiser\n\n**Imports:**\n\n```py\nfrom tkinter import filedialog as fd\nimport pandas as pd\nimport matplotlib.pyplot as plt\n```\n\n**File Selection and Reading:**\n\nOpens a File Explorer window allowing the user to select the `.csv` file to plot. This file is read into a `panda` `Dataframe` Object using a comma `,` as the delimiter. `.reset_index()` creates an `index` column which is used as the x-variable during plotting.\n```py\nfile = fd.askopenfilename(\n    filetypes=[(\"CSV files\", \"*.csv\")],title=\"Set input .csv file\" )\n\ndata = pd.read_csv(file, header=0, delimiter=\",\")\n```\n\n**Create 3D Plot:**\n\nCreate the figure `fig` and 3D axis `ax`. Plot the data as a continuous line showing the movement of the micro:bit.\n```py\nfig = plt.figure()\nax = plt.axes(projection='3d')\nax.plot3D(x, y, z, color='k')\n\nax.set_xlim(-1500, 1500)\nax.set_ylim(-1500, 1500)\nax.set_zlim(-1500, 1500)\n\nax.plot3D([-1500, 1500], [0, 0], [0, 0], \"k--\", alpha=0.5)\nax.plot3D([0, 0], [-1500, 1500], [0, 0], \"k--\", alpha=0.5)\nax.plot3D([0, 0], [0, 0], [-1500, 1500], \"k--\", alpha=0.5)\n\nplt.show()\n```\n\n---\n\n## Output\n\n### Serial Port Connection\n\n![connected-2d](/README_assets/connected_2d.gif)\n![connected-3d](/README_assets/connected_3d.gif)\n\n### From CSV Data\n\n![csv-2d](/README_assets/file_2d.gif)\n![csv-2d-w-deltas](/README_assets/raw_and_deltas.png)\n![csv_xyz-seperated](/README_assets/xyz_seperated.png)\n![missing_peaks](/README_assets/missing_peaks.png)\n![recurve-peaks](/README_assets/recurve_peaks.png)\n![csv-3d](/README_assets/file_3d.gif)\n![csv-3d-with-ref-lines](/README_assets/not_connected_3d.png)\n\n---\n\n## Final Project and Presentation\n\nAn interesting scenario to record could be archery. This would show how movement / stabilisation changes throughout the shot process and the vibrations and forces involved during the loose of an arrow. This would be similar to a commercial product called the \"Mantis X8 Archery Training Tool for Marksmanship\" which measures shot feedback, allowing the archer to visualise movements that the human eye will not see.\n\n### Project Selection\n\nArchery Stability Tracker: Measure the vibrations of a bow as it is fired. This could also be used to visualise different stages of the shot process; nocking, setting, aiming, loosing. \n\nThis could also be used to compare other variables:\n- Bowstyle: Recurve Vs. Compound\n- Vibration Dampening: With and without 'Limb Savers'\n- Distance: Do I aim / settle for longer at higher distances?\n\n### Plan\n\n- Write a script for the micro:bit to record accelerometer data in `.csv` format every `n` milliseconds.\n    - When Button \"A\" is pressed, start recording; would it be a good idea to include a 2-3 second delay to avoid recording 'interference'? If so, will need a visual indication and / or countdown.\n    - When Button \"B\" is pressed, stop recording.\n- Carry out signal pre-processing to mitigate random noise. This could be carried out with methods such as convolving (rolling averages) or even Kalman filtering.\n- Use Python's `matplotlib` module to visualise the movement in various formats: 2D and 3D lineplot.\n- Use Python's `matplotlib` module to visualise the rate of movement (deltas between datapoints).\n- Focus in on 'spikes' within the data, likely to be the moment of loosing.\n\u003e DEFINITION - Loosing: The act of shooting an arrow from a bow.\n\n### Why?\n\nI will be using `.csv` data in this project as opposed to serial port transfer between the micro:bit and a computer as this would allow the micro:bit to be a small and standalone unit; a long cable connecting two devices would pose a safety hazard if the wire got caught in the cam system of my compound bow.\n\u003e DEFINITION - Cam System: Pulley/s on the limbs of the bow that 'hold' the draw weight at full draw. This allows you to hold a heavy draw weight as though it was a light draw weight meaning you can stay at a settled full draw for an extended time period. Or, a rotating piece of mechanical linkage that converts rotary motion into linear motion.\n\nReal-time logging could be achieved using the `radio` functionality of *two* micro:bits; one on the bow to record and transmit the data and on connected to a computer / laptop to receive and plot. This is demonstrated in [this](https://microbit.org/projects/make-it-code-it/python-wireless-data-logger/) project.\n\n### What Went Well\n\n### Lessons Learned\n\n### Testing and Results\n\n### Potential Application\n\n---","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcorey-richardson%2Fmicrobit-data-logger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcorey-richardson%2Fmicrobit-data-logger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcorey-richardson%2Fmicrobit-data-logger/lists"}