How to properly open and close a serial connection using dash_daq.BooleanSwitch()?

Hi Everybody,

For a project I’m working on, I’m connected to an IMU sensor through my computer’s USB port. I’m using pyserial to read in the data. I’m able to successfully run and print the data to my console when I change the BooleanSwitch from off to on, however, I haven’t figured out a way yet to shut it off. Here is my question:

  1. How would I properly stop reading the data and close the serial connection once the switch goes from True to False?

I’m including some code to help clarify what it is I’m doing:

boolean_switch_serial_read.py

import dash
import dash_daq as daq
import dash_html_components as html
from dash.dependencies import Input, Output
import time
import serial_read

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    daq.BooleanSwitch(
        id='my-boolean-switch',
        on=False
    ),
    html.Div(id='boolean-switch-output'),
    html.Button(id='button',n_clicks=0,style={'display': 'none'}),
    html.Div(id='dummy-div',style={'display':'none'})
])

@app.callback(
    [Output('boolean-switch-output', 'children'),
     Output('button','n_clicks')],
    [Input('my-boolean-switch', 'on')])
def update_output(on):
    if on:
        n=1
    else:
        n=0
    output_string = 'The switch is {}.'.format(on)
    return (output_string,n)

@app.callback(
    Output('dummy-div','children'),
    [Input('button','n_clicks')]
)
def read_serial_data(n):
    try:
        if n==0:
            raise KeyboardInterrupt
        serial_read.save_serial_data('data/test3.csv','foo',n)
        return None
    except:
        raise dash.exceptions.PreventUpdate

if __name__ == '__main__':
    app.run_server(debug=True)

serial_read.py

import serial
import time
import csv

def save_serial_data(fn,port,n):
    filename = fn
    com_port = '/dev/cu.usbserial-DB00X8WD'

    with serial.Serial(com_port, 115200, timeout=0, parity=serial.PARITY_NONE, rtscts=0) as ser:  # this needs to match Arduino settings
        ser.flushInput()

        line = []

        with open(filename, 'w+', newline='') as myfile:
            wr = csv.writer(myfile)
            wr.writerow('6666')
            t = time.time()
            print("The button value being passed is {}".format(n))
            flag=True
            while flag:
                try:
                    for c in ser.read().decode('utf-8'):
                        line.append(c)
                        if c == "\n":
                            newline = ''.join(line)
                            newline4 = newline[0:len(newline) - 3]
                            res = [float(idx) for idx in newline4.split(',')]
                            t_elapsed = time.time() - t
                            t_elapsed = float("{:.4f}".format(t_elapsed))
                            wr.writerow([t_elapsed] + res)
                            print([t_elapsed] + res)
                            line = []

                            break

                except Exception as e:
                    print("Keyboard Interrupt")
                    print(e)
                    ser.close()
                    flag = False
                    print(flag)
        ser.close()

One observation I made is that it seems my code will stop reading the data once I toggle the switch from on to off back to on again, which doesn’t completely make sense to me. I think one of my issues is that I’m not properly closing the serial connection before I open up a new connection. Here is the exception error I’m getting:

The button value being passed is 1
Keyboard Interrupt
read failed: device reports readiness to read but returned no data (device disconnected or multiple access on port?)

If anybody has some ideas how to resolve this issue, I’d really appreciate any suggestions. Thanks!!

Ben

Could you not use a boolean to make sure that serial communication is only set once?

firstTime = True
ser = 1

def beginSerialNow():
    global ser
    global firstTime


    if firstTime:
        firstTime = False
        ser = serial.Serial(port='COM4',baudrate=2000000,timeout=0,)
    print(ser.readline(),flush=True)

@app.callback(
    Output("outputDisplay", "value"),
    [Input("my-toggle-switch", "value"),]
)

def toggle_interval(toggleValue,):
    beginSerialNow()

I am new to Dash so this comment may not be applicable, but doesn’t the documentation explicitly state not to use global variables?

Hi @TollisH,
Thanks for the comment. Yes, you are correct about the global variabables not being recommended. FYI, I was able to solve the issue using flask-caching. See the related post here: