├── .github └── workflows │ └── python-publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── bin ├── arduplot └── arduplot.cmd ├── pyproject.toml ├── setup.cfg └── src └── arduplot ├── __init__.py ├── filter_plotter.py └── plotserialdata.py /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.x' 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install build 33 | - name: Build package 34 | run: python -m build 35 | - name: Publish package 36 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 37 | with: 38 | user: __token__ 39 | password: ${{ secrets.PYPI_API_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | dist 3 | src/arduplot.egg-info 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Yoonseok Hur 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arduplot 2 | 3 | ### No Serial Plotter for PlatformIO/VSCode ??? 4 | 5 |

The following picture shows the Arduino IDE's serial plotter which plots the data coming through the serial port.

6 | 7 | ![Arduino Serial Plotter](https://user-images.githubusercontent.com/13171662/133396210-a3c486cc-1c94-4cdc-abd9-7f56042f0f2f.png) 8 | 9 | 10 |

But there is no built-in equivalent tool for the PlatformIO and/or VSCode. Hence arduplot(Arduino Plot) is made to support the equivalent funcitionality.

11 |

This tool can be run stand alone with the usage below. This needs to be started in the PIO Terminal Panel, specifically in the PIO bundled python venv.

12 |
 13 | Usage: arduplot [OPTIONS] [LABELS]...
 14 | 
 15 | Options:
 16 |   -w, --width INTEGER   Plotter Width
 17 |   -i, --width INTEGER   Plotter Y Axis Min
 18 |   -x, --width INTEGER   Plotter Y Axis Max
 19 |   -e, --period INTEGER  Plotter sample period (ms), default=1000"
 20 |   -t, --title TEXT      Plotter Title
 21 |   -s, --socket INTEGER  TCP Socket Port number
 22 |   -n, --stdin           Standard input pipe
 23 |   -p, --port TEXT       Serial Port, a number or a device name
 24 |   -b, --baud INTEGER    Set baudrate, default=115200
 25 |   --help                Show this message and exit.
 26 | 
27 | As an example, you can build and run https://github.com/iotlab101/pio_filter_dht22 on an esp8266 and run the following command. 28 | 29 |
 30 | arduplot -p COM5 -t Thermometer -w 100 Temperature Humidity
 31 | 
32 | Here -t Thermometer is the title of the plot chart, -w 100 is the width of the plot, and Temperature and the Humidity are the labels of the plotting data. 33 | And you'll see see the plot like this 34 | 35 | ![Screen Shot 2021-11-13 at 9 59 48 PM](https://user-images.githubusercontent.com/13171662/141644699-778221fe-7eb4-4760-bc6b-3f3671e2724d.png) 36 | 37 | (And you can plot the data from a TCP connection as well instead of the serial port if you use the **-s** option. Use the **-s** option to open and wait on a socket, then feed the data to the socket. The data format should be the same as the Serial port case) 38 | 39 | ### Optional Plot Configuration 40 | There is an optional configuration file where you can set the setting for the plotting for the project. If you create a json file named **'plotcfg.json'** under the the PIO project directory, you don't have to pass the parameters every time you invoke the tool. 41 |
 42 | {
 43 |     "label": [
 44 |         "temperature",
 45 |         "humidity"
 46 |     ],
 47 |     "title": "Thermmeter",
 48 |     "width": 100
 49 | }
 50 | 
51 | This configuration would be helpful, if you want to run this tool over the TCP port via some other tool where it's not easy to pass-through the setting information. 52 | ## Installation and Prerequisite 53 | * This plotter uses the following dependancies, and they will be installed when you install this tool.. 54 |
 55 |          matplotlib
 56 |          click
 57 |          pyserial
 58 | 
59 | You can install this tool with the pip as follows 60 |
 61 | pip install arduplot
 62 | 
63 | 64 | ## Running it as part of PlatformIO monitor filter ## 65 | **1**. Install the arduplot first
pip install arduplot
66 | **2**. Configure the tool. There are three ways to configure. 67 |
    68 |
  1. configure every time you create a pio project
  2. 69 |
  3. configure your platform wise like esp8266 or esp32
  4. 70 |
  5. or you just configure globally by setting an environment variable.
  6. 71 |
72 |
73 | For i), you create a folder 'monitor' under your pio project folder, and copy ~/.platformio/penv/lib/python3.9/site-packages/arduplot/filter_plotter.py script to that folder. 74 |
<Project>/monitor
75 | 76 | For ii), you create the ~/.platformio/platform/espressif8266/monitor folder and copy ~/.platformio/penv/lib/python3.9/site-packages/arduplot/filter_plotter.py to that folder. If you're using other platform like esp32, then create the ~/.platformio/platform/espressif32/monitor folder and copy to that folder. 77 |
 78 | ~/.platformio/platform/espressif8266/monitor (or ~/.platformio/platform/espressif32/monitor for esp32)
 79 | 
80 | 81 | And for iii), you can just set the environment variable as below and run this without copying. For Windows, you set the environment variable as such in the Windows way. 82 |
export PLATFORMIO_MONITOR_DIR=${HOME}/.platformio/penv/lib/python3.9/site-packages/arduplot/
83 | 84 | 85 | **3**. With the above steps done, run
pio device monitor -f plotter
And you will get this plot. 86 | 87 | ### Windows usage with Version Core 6.1.5·Home 3.4.3 88 | In GUI go to PlatformIO sidebar 89 | -> Select New Terminal in quick access at the bottom 90 | In the Terminal window run the following 91 | -> `pip install arduplot` 92 | Close the terminal or type exit 93 | 94 | Edit the platform.ini file (with VS or your preference) 95 | add or ammend (add ,plotter if the entry line already exists) plotter to the monitor filter flag 96 | -> `monitor_filters = plotter` 97 | 98 | Happy hunting guide addition compliments of @cybertza 99 | (please note the process will stay not responding in windows until data has been recieved from the serial) 100 | 101 | Screen Shot 2021-11-13 at 9 46 49 PM 102 | 103 | ### New Features for arduplot 0.2.8 ### 104 | Thanks to Antonio(@ancebfer), arduplot has two new features. That is `-e` and `-n` option. 105 | 106 | `-e` is for the rendering time interval in milli seconds unit 107 | 108 | and 109 | 110 | The original arduplot takes the input from either a serial port or some tcp port(this is used in `pio device monitor -f plotter` command). Now 111 | 112 | `-n` introduce another input, that is the standard input. What we can do with it is to pipe some data into the arduplot. 113 | 114 | For example, here is an example python code named 'wave.py' that generates the data. 115 | ```python 116 | #!/usr/bin/env python3 117 | import math 118 | import time 119 | 120 | freq = 1 # Hz 121 | period = 0.01 # s 122 | 123 | t = 0 124 | while True: 125 | x = 2 * math.pi * freq * t 126 | y1 = math.sin(x) 127 | y2 = math.cos(x) 128 | print(y1, y2) 129 | t += period 130 | time.sleep(period) 131 | ``` 132 | 133 | And we can use the arduplot to plot the graph with the fed data by 134 | ``` 135 | python waves.py | arduplot -n -w 500 -e 10 136 | ``` 137 | This sample is also taken from Antoino's work in the pull request. 138 | And thank you @thijstriemstra and @cybertza for your contribution as well. 139 | -------------------------------------------------------------------------------- /bin/arduplot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | import re 23 | import sys 24 | from arduplot.plotserialdata import main 25 | if __name__ == '__main__': 26 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 27 | sys.exit(main()) -------------------------------------------------------------------------------- /bin/arduplot.cmd: -------------------------------------------------------------------------------- 1 | @setlocal 2 | @echo off 3 | REM Copyright (c) 4 | REM 5 | REM Permission is hereby granted, free of charge, to any person obtaining a copy 6 | REM of this software and associated documentation files (the "Software"), to deal 7 | REM in the Software without restriction, including without limitation the rights 8 | REM to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | REM copies of the Software, and to permit persons to whom the Software is 10 | REM furnished to do so, subject to the following conditions: 11 | REM 12 | REM The above copyright notice and this permission notice shall be included in all 13 | REM copies or substantial portions of the Software. 14 | REM 15 | REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | REM IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | REM FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | REM AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | REM LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | REM OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | REM SOFTWARE. 22 | python "%~dp0\arduplot" %* 23 | @echo on 24 | @endlocal -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=42", 4 | "wheel" 5 | ] 6 | build-backend = "setuptools.build_meta" -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = arduplot 3 | version = 0.3.3 4 | author = Yoonseok Hur 5 | author_email = yoonseok@gmail.com 6 | description = This tool listens to the serial port and draws the data to matplotlib. 7 | long_description = file: README.md 8 | long_description_content_type = text/markdown 9 | url = https://github.com/yhur/arduplot 10 | project_urls = 11 | Bug Tracker = https://github.com/yhur/arduplot/issues 12 | classifiers = 13 | Programming Language :: Python :: 3 14 | License :: OSI Approved :: MIT License 15 | Operating System :: POSIX :: Linux 16 | Operating System :: Microsoft :: Windows 17 | Operating System :: MacOS 18 | 19 | [options] 20 | package_dir = 21 | = src 22 | packages = find: 23 | python_requires = >=3.6 24 | install_requires = 25 | matplotlib 26 | pyserial 27 | click 28 | scripts = 29 | bin/arduplot 30 | bin/arduplot.cmd 31 | 32 | [options.packages.find] 33 | where = src 34 | -------------------------------------------------------------------------------- /src/arduplot/__init__.py: -------------------------------------------------------------------------------- 1 | #from serialplotter import main 2 | # Copyright (c) 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | from arduplot.plotserialdata import main -------------------------------------------------------------------------------- /src/arduplot/filter_plotter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ''' 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | ''' 23 | 24 | import subprocess 25 | import socket 26 | import os 27 | import signal 28 | import sys 29 | from platformio.commands.device import DeviceMonitorFilter 30 | from platformio.project.config import ProjectConfig 31 | 32 | PORT = 19200 33 | 34 | class SerialPlotter(DeviceMonitorFilter): 35 | NAME = "plotter" 36 | 37 | def __init__(self, *args, **kwargs): 38 | super(SerialPlotter, self).__init__(*args, **kwargs) 39 | self.buffer = '' 40 | self.arduplot = 'arduplot' 41 | self.plot = None 42 | self.plot_sock = '' 43 | self.plot = '' 44 | 45 | def __call__(self): 46 | pio_root = ProjectConfig.get_instance().get_optional_dir("core") 47 | if sys.platform == 'win32': 48 | self.arduplot = os.path.join(pio_root, 'penv', 'Scripts' , self.arduplot + '.cmd') 49 | else: 50 | self.arduplot = os.path.join(pio_root, 'penv', 'bin' , self.arduplot) 51 | print('--- serial_plotter is starting') 52 | self.plot = subprocess.Popen([self.arduplot, '-s', str(PORT)]) 53 | try: 54 | self.plot_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 55 | self.plot_sock.connect(('localhost', PORT)) 56 | except socket.error: 57 | pass 58 | return self 59 | 60 | def __del__(self): 61 | if self.plot: 62 | if sys.platform == 'win32': 63 | self.plot.send_signal(signal.CTRL_C_EVENT) 64 | self.plot.kill() 65 | 66 | def rx(self, text): 67 | if self.plot.poll() is None: # None means the child is running 68 | self.buffer += text 69 | if '\n' in self.buffer: 70 | try: 71 | self.plot_sock.send(bytes(self.buffer, 'utf-8')) 72 | except BrokenPipeError: 73 | try: 74 | self.plot_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 75 | self.plot_sock.connect(('localhost', PORT)) 76 | except socket.error: 77 | pass 78 | self.buffer = '' 79 | else: 80 | os.kill(os.getpid(), signal.SIGINT) 81 | return text -------------------------------------------------------------------------------- /src/arduplot/plotserialdata.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ''' 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | ''' 23 | 24 | import os 25 | import sys 26 | import signal 27 | import json 28 | import socket 29 | import matplotlib.pyplot as plt 30 | from matplotlib import animation 31 | import serial 32 | import click 33 | from serial.serialutil import SerialException 34 | try: 35 | from platformio.project.config import ProjectConfig 36 | PIO_MODE = True 37 | except ImportError: 38 | PIO_MODE = False 39 | 40 | def sighandler(signum, frame): 41 | '''signal handler for Ctrl-C''' 42 | sys.exit(9) 43 | signal.signal(signal.SIGINT, sighandler) 44 | 45 | def value_by_key(j, key, value): 46 | '''utility function to get the plotter configuration setting''' 47 | if key in j: 48 | return j[key] 49 | return value 50 | 51 | # BEGIN MAIN FUNCTION 52 | @click.command(context_settings=dict(help_option_names=["-h", "--help"]), 53 | help="arduplot(ver:0.2.9) plots serial data from the serial port, TCP socket or standard input") 54 | @click.option("--width", "-w", type=int, help="Plotter Width") 55 | @click.option("--ymin", "-i", type=int, help="Plotter Y axis Min") 56 | @click.option("--ymax", "-x", type=int, help="Plotter Y axis Max") 57 | @click.option("--period", "-e", type=int, help="Plotter sample period (ms), default=1000") 58 | @click.option("--title", "-t", help="Plotter Title") 59 | @click.option("--stdin", "-n", is_flag=True, help="Standard input pipe") 60 | @click.option("--socket", "-s", type=int, help="TCP Socket Port number") 61 | @click.option("--port", "-p", help="Serial Port, a number or a device name") 62 | @click.option("--baud", "-b", type=int, help="Set baudrate, default=115200") 63 | @click.argument("labels", nargs=-1) 64 | def main(**kwargs): 65 | '''main function''' 66 | # Reading data function from the Serial Port 67 | def uart_in(): 68 | return ser.readline().decode('utf-8') 69 | 70 | # Reading data function from the TCP socket 71 | def tcp_in(): 72 | return client_socket.recv(1024).decode('utf-8') 73 | 74 | # Reading data function from the Standard Input 75 | def pipe_in(): 76 | return sys.stdin.readline() 77 | 78 | # Callback function for plotting the data by animation.FuncAnimation 79 | def animate(self): 80 | ax.clear() 81 | buffer = get_input().split('\n') 82 | line = buffer[-2 if len(buffer[-1]) == 0 else -1].split() 83 | # data labelling 84 | if len(line) > len(data_label): 85 | for i in range(len(line) - len(data_label)): 86 | data_label.append(f'data{len(data_label) + 1}') 87 | 88 | # Prepare the data for plotting 89 | for idx, l in enumerate(line): 90 | try: 91 | l = float(l) 92 | except ValueError: 93 | print(f"Can't convert {l} to float. Zeroed out.") 94 | l = 0.0 95 | if len(data) <= idx: 96 | data.append([]) 97 | data[idx].append(l) 98 | data[idx] = data[idx][-width:] # Truncate to graph width 99 | ax.plot(data[idx], label=data_label[idx]) 100 | 101 | # plotting 102 | plt.title(title) 103 | plt.xticks(rotation=90, ha='right') 104 | plt.legend() 105 | plt.axis([0, width, ymin, ymax]) 106 | plt.grid(color='gray', linestyle='dotted', linewidth=1) 107 | fig.tight_layout(pad=2.5) 108 | 109 | # main control 110 | # main variabls 111 | data = [] 112 | width = 50 113 | ymin = None 114 | ymax = None 115 | period = 1000 116 | title = 'Serial Data Plot' 117 | data_label = [] 118 | stdin_pipe = kwargs['stdin'] or None 119 | tcp_socket = kwargs['socket'] or None 120 | 121 | # check and get the plotter config if the config file exists 122 | try: 123 | with open('plotcfg.json', 'r', encoding='utf-8') as jfile: 124 | plot_cfg = json.load(jfile) 125 | title = value_by_key(plot_cfg, 'title', title) 126 | width = value_by_key(plot_cfg, 'width', width) 127 | ymin = value_by_key(plot_cfg, 'ymin', ymin) 128 | ymax = value_by_key(plot_cfg, 'ymax', ymax) 129 | period = value_by_key(plot_cfg, 'period', period) 130 | data_label = value_by_key(plot_cfg, 'label', data_label) 131 | except FileNotFoundError: 132 | pass 133 | title = kwargs['title'] or title 134 | width = kwargs['width'] or width 135 | ymin = kwargs['ymin'] or ymin 136 | ymax = kwargs['ymax'] or ymax 137 | period = kwargs['period'] or period 138 | data_label = list(kwargs['labels']) or data_label 139 | 140 | if stdin_pipe: 141 | get_input = pipe_in 142 | elif tcp_socket: 143 | get_input = tcp_in 144 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 145 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 146 | server_socket.bind(('localhost', tcp_socket)) 147 | server_socket.listen() 148 | client_socket, addr = server_socket.accept() 149 | else: 150 | get_input = uart_in 151 | ser = serial.Serial() 152 | ser.timeout = 10 153 | ser.port = None 154 | ser.baudrate = 115200 155 | if PIO_MODE: 156 | if os.path.isfile(ProjectConfig.get_default_path()): 157 | config = ProjectConfig.get_instance() # PIO project config 158 | for s in config.sections(): 159 | ser.port = config.get(s, 'monitor_port') or ser.port 160 | ser.baudrate = config.get(s, 'monitor_speed') or ser.baudrate 161 | ser.port = kwargs['port'] or ser.port 162 | ser.baudrate = kwargs['baud'] or ser.baudrate 163 | if ser.port is None: 164 | print("Please check the platformio.ini for the 'monitor_port or the -p option") 165 | sys.exit(2) 166 | else: 167 | ser.baudrate = kwargs['baud'] or ser.baudrate 168 | ser.port = kwargs['port'] 169 | if not ser.port: 170 | print('\nPlease provide the serial port information\n') 171 | print('\t arduplot -p /dev/cu.usbserail-ABCDEEF or arduplot -p COM3\n') 172 | sys.exit(3) 173 | try: 174 | ser.open() 175 | if ser.is_open is True: 176 | print('\nSerial port listening:') 177 | print(f'\tport: {ser.port}, baud: {ser.baudrate}\n') 178 | except SerialException: 179 | print(f'Serial Device {ser.port} is not found') 180 | sys.exit(4) 181 | 182 | fig = plt.figure() 183 | if stdin_pipe: 184 | fig.canvas.manager.set_window_title('Standard input') 185 | elif tcp_socket: 186 | fig.canvas.manager.set_window_title('tcp://localhost:'+str(tcp_socket)) 187 | else: 188 | fig.canvas.manager.set_window_title(ser.port) 189 | 190 | ax = fig.subplots() 191 | ani = animation.FuncAnimation(fig, animate, interval=period, cache_frame_data=False) 192 | plt.show() 193 | # END MAIN FUNCTION 194 | 195 | if __name__ == '__main__': 196 | main() 197 | --------------------------------------------------------------------------------