diff --git a/.gitignore b/.gitignore index 640dded..ae342d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.pio -CMakeListsPrivate.txt -/cmake-build* -/venv +.ipynb_checkpoints +__pycache__ + + diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index e5b6297..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,77 +0,0 @@ -# !!! WARNING !!! AUTO-GENERATED FILE, PLEASE DO NOT MODIFY IT AND USE -# https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags -# -# If you need to override existing CMake configuration or add extra, -# please create `CMakeListsUser.txt` in the root of project. -# The `CMakeListsUser.txt` will not be overwritten by PlatformIO. - -cmake_minimum_required(VERSION 3.2) -project(firmware) - -include(CMakeListsPrivate.txt) - -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeListsUser.txt) -include(CMakeListsUser.txt) -endif() - -add_custom_target( - PLATFORMIO_BUILD ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_BUILD_VERBOSE ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --verbose - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_UPLOAD ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --target upload - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_CLEAN ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --target clean - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_MONITOR ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion device monitor - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_TEST ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion test - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_PROGRAM ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --target program - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_UPLOADFS ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --target uploadfs - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_UPDATE_ALL ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion update - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_custom_target( - PLATFORMIO_REBUILD_PROJECT_INDEX ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion init --ide clion - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_executable(${PROJECT_NAME} ${SRC_LIST}) diff --git a/README.md b/README.md new file mode 100644 index 0000000..d784583 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +Swimtracker Firmware & Hardware +=============================== + + +Firmware +-------- + +Run tests: +``` +pio test -v -e native +``` + +Compile and upload: +``` +pio run -e d1 -t upload +``` diff --git a/commands.readme b/commands.readme deleted file mode 100644 index 4e387d3..0000000 --- a/commands.readme +++ /dev/null @@ -1,7 +0,0 @@ -Test locally - platformio test -v -e native - - -Device: - platformio device monitor - platformio run -t upload diff --git a/dimensions.txt b/dimensions.txt deleted file mode 100644 index b9b95cb..0000000 --- a/dimensions.txt +++ /dev/null @@ -1,6 +0,0 @@ -92 x 28 x 30 -35x20 - - - 95x 50 x 30 (absolute min) -100 x 55 x 35 \ No newline at end of file diff --git a/example-data/01_real_test.st b/example-data/01_real_test.st new file mode 100644 index 0000000..45c532f Binary files /dev/null and b/example-data/01_real_test.st differ diff --git a/example-data/02_vildosola.st b/example-data/02_vildosola.st new file mode 100644 index 0000000..177c503 Binary files /dev/null and b/example-data/02_vildosola.st differ diff --git a/example-data/03_rebecca.st b/example-data/03_rebecca.st new file mode 100644 index 0000000..a2aa11e Binary files /dev/null and b/example-data/03_rebecca.st differ diff --git a/example-data/04.st b/example-data/04.st new file mode 100644 index 0000000..8e80e08 Binary files /dev/null and b/example-data/04.st differ diff --git a/example-data/05.st b/example-data/05.st new file mode 100644 index 0000000..f535b8e Binary files /dev/null and b/example-data/05.st differ diff --git a/example-data/06.st b/example-data/06.st new file mode 100644 index 0000000..f97ce45 Binary files /dev/null and b/example-data/06.st differ diff --git a/example-data/07.st b/example-data/07.st new file mode 100644 index 0000000..711285b Binary files /dev/null and b/example-data/07.st differ diff --git a/example-data/1589394292.st b/example-data/1589394292.st new file mode 100644 index 0000000..c0142a3 Binary files /dev/null and b/example-data/1589394292.st differ diff --git a/lib/basic/Dtypes.h b/firmware/lib/basic/Dtypes.h similarity index 100% rename from lib/basic/Dtypes.h rename to firmware/lib/basic/Dtypes.h diff --git a/lib/basic/FilesystemAbstraction.h b/firmware/lib/basic/FilesystemAbstraction.h similarity index 100% rename from lib/basic/FilesystemAbstraction.h rename to firmware/lib/basic/FilesystemAbstraction.h diff --git a/lib/basic/MockDtypes.h b/firmware/lib/basic/MockDtypes.h similarity index 100% rename from lib/basic/MockDtypes.h rename to firmware/lib/basic/MockDtypes.h diff --git a/lib/basic/MockSerial.h b/firmware/lib/basic/MockSerial.h similarity index 100% rename from lib/basic/MockSerial.h rename to firmware/lib/basic/MockSerial.h diff --git a/lib/scale/MockScale.h b/firmware/lib/scale/MockScale.h similarity index 100% rename from lib/scale/MockScale.h rename to firmware/lib/scale/MockScale.h diff --git a/lib/scale/Scale.h b/firmware/lib/scale/Scale.h similarity index 100% rename from lib/scale/Scale.h rename to firmware/lib/scale/Scale.h diff --git a/lib/session/MeasurementSession.h b/firmware/lib/session/MeasurementSession.h similarity index 100% rename from lib/session/MeasurementSession.h rename to firmware/lib/session/MeasurementSession.h diff --git a/lib/session/MockStorage.h b/firmware/lib/session/MockStorage.h similarity index 100% rename from lib/session/MockStorage.h rename to firmware/lib/session/MockStorage.h diff --git a/lib/session/SessionChunk.h b/firmware/lib/session/SessionChunk.h similarity index 100% rename from lib/session/SessionChunk.h rename to firmware/lib/session/SessionChunk.h diff --git a/lib/session/SpiffsStorage.h b/firmware/lib/session/SpiffsStorage.h similarity index 100% rename from lib/session/SpiffsStorage.h rename to firmware/lib/session/SpiffsStorage.h diff --git a/lib/session/StreamingMsgPackEncoder.h b/firmware/lib/session/StreamingMsgPackEncoder.h similarity index 100% rename from lib/session/StreamingMsgPackEncoder.h rename to firmware/lib/session/StreamingMsgPackEncoder.h diff --git a/lib/userdb/UserDB.cpp b/firmware/lib/userdb/UserDB.cpp similarity index 100% rename from lib/userdb/UserDB.cpp rename to firmware/lib/userdb/UserDB.cpp diff --git a/lib/userdb/UserDB.h b/firmware/lib/userdb/UserDB.h similarity index 100% rename from lib/userdb/UserDB.h rename to firmware/lib/userdb/UserDB.h diff --git a/lib/webdav/AsyncWebDav.h b/firmware/lib/webdav/AsyncWebDav.h similarity index 100% rename from lib/webdav/AsyncWebDav.h rename to firmware/lib/webdav/AsyncWebDav.h diff --git a/platformio.ini b/firmware/platformio.ini similarity index 100% rename from platformio.ini rename to firmware/platformio.ini diff --git a/src/ConfigHardware.h b/firmware/src/ConfigHardware.h similarity index 100% rename from src/ConfigHardware.h rename to firmware/src/ConfigHardware.h diff --git a/src/ConfigWifi.h b/firmware/src/ConfigWifi.h similarity index 100% rename from src/ConfigWifi.h rename to firmware/src/ConfigWifi.h diff --git a/src/DeviceInfoLog.h b/firmware/src/DeviceInfoLog.h similarity index 100% rename from src/DeviceInfoLog.h rename to firmware/src/DeviceInfoLog.h diff --git a/src/firmware_main.cpp b/firmware/src/firmware_main.cpp similarity index 100% rename from src/firmware_main.cpp rename to firmware/src/firmware_main.cpp diff --git a/test/test_common/sessiontest.cpp b/firmware/test/test_common/sessiontest.cpp similarity index 100% rename from test/test_common/sessiontest.cpp rename to firmware/test/test_common/sessiontest.cpp diff --git a/hardware/OtherBoards.md b/hardware/OtherBoards.md new file mode 100644 index 0000000..e0d66a5 --- /dev/null +++ b/hardware/OtherBoards.md @@ -0,0 +1,2 @@ + +https://github.com/adafruit/Adafruit-HUZZAH32-ESP32-Feather-PCB.git diff --git a/hardware/box.svg b/hardware/box.svg new file mode 100644 index 0000000..5be82d4 --- /dev/null +++ b/hardware/box.svg @@ -0,0 +1,254 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hardware/data_format.ods b/hardware/data_format.ods new file mode 100644 index 0000000..0cf5d8d Binary files /dev/null and b/hardware/data_format.ods differ diff --git a/hardware/hardware.svg b/hardware/hardware.svg new file mode 100644 index 0000000..2f737f8 --- /dev/null +++ b/hardware/hardware.svg @@ -0,0 +1,235 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + D2 D2 G 3V + + GNDDTSCKVCC E+E-A-A+B-B+ + + diff --git a/hardware/hardware.txt b/hardware/hardware.txt new file mode 100644 index 0000000..8d31ef8 --- /dev/null +++ b/hardware/hardware.txt @@ -0,0 +1,26 @@ +Load Cell 50kg +https://de.aliexpress.com/item/32961462970.html?spm=a2g0o.productlist.0.0.46d91cca2fXbja&algo_pvid=b14c8be3-c372-4971-a7e6-b6ba13e03236&algo_expid=b14c8be3-c372-4971-a7e6-b6ba13e03236-1&btsid=0f841574-ad82-47f9-bbbd-fa1c46b4bba4&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_52 +$24 + + +Wemos ESP32 with 18650 battery holder, Groesse: 9.1 cm x 3.0 cm, usb at the side +https://de.aliexpress.com/item/32860090110.html?spm=a2g0o.productlist.0.0.67c732eeceGb5S&algo_pvid=4f5823a3-2d47-4afb-95a5-7ba85055f121&algo_expid=4f5823a3-2d47-4afb-95a5-7ba85055f121-4&btsid=ea1860aa-ff90-4cd7-bb92-ade7cfa85eac&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_52 +$ 5 + + +Akku +https://de.aliexpress.com/item/32908447004.html?spm=a2g0o.productlist.0.0.5f78199bMJJy0k&algo_pvid=b0eb97b9-df11-41ea-bb67-e96c1df7d292&algo_expid=b0eb97b9-df11-41ea-bb67-e96c1df7d292-0&btsid=b910d832-bcde-4807-8a97-edbbcca846df&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_52 +$ 3 + +Connector +https://de.aliexpress.com/item/32899642702.html?spm=a2g0x.search0302.3.54.34441675lAu97T&ws_ab_test=searchweb0_0,searchweb201602_0_453_454_10618_536_317_537_319_10059_10696_10084_10083_10547_10304_10821_10843_10887_10307_321_10548_322_10065_10068_10103_10884_10820_10302,searchweb201603_0,ppcSwitch_0&algo_pvid=d5cf53ae-9cc8-45b9-ab1c-c77f38b7c706&algo_expid=d5cf53ae-9cc8-45b9-ab1c-c77f38b7c706-6 +$1.5 + +HX711 +https://de.aliexpress.com/item/32878181081.html?spm=a2g0o.productlist.0.0.19202f024UQZQP&algo_pvid=b4f3e85e-9190-4b8d-9b9e-0100fa84037e&algo_expid=b4f3e85e-9190-4b8d-9b9e-0100fa84037e-1&btsid=ae27657b-596a-43fa-8cc8-bc9cba1d5bcf&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_52 +$1 + + +Case +https://www.banggood.com/ABS-Plastic-Electronic-Enclosure-Project-Box-Black-103x64x40mm-p-951179.html?rmmds=buy&cur_warehouse=CN +$4-10 diff --git a/python-mock/PeakDetectionAnalysis.ipynb b/python-mock/PeakDetectionAnalysis.ipynb new file mode 100644 index 0000000..0cfcc12 --- /dev/null +++ b/python-mock/PeakDetectionAnalysis.ipynb @@ -0,0 +1,990 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib notebook\n", + "from measurement_session import *\n", + "from peak_detection import *\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Peak Detection Algorithm Comparison" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'sessionStartTime': 1589401905,\n", + " 'startIndex': 0,\n", + " 'values': array([15, 15, 15, ..., 18, 17, 16], dtype=int16)}" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "session_file = '/home/martin/Downloads/1589401905 (1).st'\n", + "session = load_session_from_file(session_file)\n", + "#session = prune_overflown_session(session)\n", + "#session = prune(session, 10, 50)\n", + "session" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "#values[8000:8005]" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('
');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " if (mpl.ratio != 1) {\n", + " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", + " }\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " fig.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '
');\n", + " var titletext = $(\n", + " '
');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('
');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var backingStore = this.context.backingStorePixelRatio ||\n", + "\tthis.context.webkitBackingStorePixelRatio ||\n", + "\tthis.context.mozBackingStorePixelRatio ||\n", + "\tthis.context.msBackingStorePixelRatio ||\n", + "\tthis.context.oBackingStorePixelRatio ||\n", + "\tthis.context.backingStorePixelRatio || 1;\n", + "\n", + " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband = $('');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width * mpl.ratio);\n", + " canvas.attr('height', height * mpl.ratio);\n", + " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('
')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('