1.3. Python Coding for TCLab#

The following cells demonstrate the use of the TCLab hardware. Open a new Jupyter notebook on your laptop, connect the TCLab hardware to the USB port of your laptop, then create and run cells as you follow along in this notebook.

1.3.1. Creating a TCLab instance#

Once installed, the tclab package can be imported into Python and an instance created with the Python statements

from tclab import TCLab
lab = TCLab()
# do something
lab.close()

TCLab() attempts to find a device connected to a serial port and return a connection. An error is generated if no device is found. The connection must be closed when no longer in use.

from tclab import TCLab

lab = TCLab()
# do something
lab.close()
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
TCLab disconnected successfully.

1.3.2. Using the LED#

The following cell demonstrates the process, and uses the tclab LED() function to flash the LED on the Temperature Control Lab for a period of 10 seconds at a 100% brightness level.

from tclab import TCLab

lab = TCLab()
lab.LED(50)
lab.close()
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
TCLab disconnected successfully.

1.3.3. Using TCLab and Python’s with statement#

The Python with statement provides a convenient means of setting up and closing a connection to the Temperature Control Laboratory. In particular, the with statement establishes a context where a tclab instance is created, assigned to a variable, and automatically closed upon completion. The with statement is the preferred way to connect the Temperature Control Laboratory for most uses.

from tclab import TCLab

with TCLab() as lab:
    lab.LED(100)
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
TCLab disconnected successfully.

1.3.4. Reading Temperatures#

Once a tclab instance is created and connected to a device the temperature sensors are acccessed with the attributes .T1 and .T2. Given an instance named lab, the temperatures are accessed as

T1 = lab.T1
T2 = lab.T2

Note that lab.T1 and lab.T2 are read-only properties. Attempt to assign a value will return a Python error.

from tclab import TCLab

with TCLab() as lab:
    T1 = lab.T1
    T2 = lab.T2
    print(f"Temperature 1: {T1:0.2f} C")
    print(f"Temperature 2: {T2:0.2f} C")
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
Temperature 1: 22.83 C
Temperature 2: 21.86 C
TCLab disconnected successfully.

1.3.5. Setting Heater Power#

1.3.5.1. Setting maximum power with .P1 and .P2#

Heater power is specified as a percentage of the maximum power available at each heater. The maximum power to each heater is determined by setting parameters .P1 and .P2 to number in the range 0 and 255. The default settings are

lab.P1 = 200
lab.P2 = 100

Based on laboratory measurements, the power delivered to each heater is approximately 14.5 mW per unit increase in .P1 and .P2. For heater 1 at the default setting of 200, the power is

\[ 200 \times 14.5 \text{mW} \times \frac{\text{1 watt}}{\text{1000 mW}} = 2.9\text{ watts}\]

For heater 2 at the default setting of 100, the power is

\[ 100 \times 14.5 \text{mW} \times \frac{\text{1 watt}}{\text{1000 mW}} = 1.45\text{ watts}\]

Note that the power delivered to the heaters for constant .P1 and .P2 is temperature dependent, and there will be some variation among units.

The default values for .P1 and .P2 were chosen to avoid unnecessarily high temperatures, and to include an asymmetric response between the two heaters.

from tclab import TCLab

with TCLab() as lab:
    P1 = lab.P1
    P2 = lab.P2
    print(f"The maximum power of heater 1 is set to {P1:.0f} corresponding to {P1*0.0145:.2f} watts.")
    print(f"The maximum power of heater 1 is set to {P2:.0f} corresponding to {P2*0.0145:.2f} watts.")
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
The maximum power of heater 1 is set to 200 corresponding to 2.90 watts.
The maximum power of heater 1 is set to 100 corresponding to 1.45 watts.
TCLab disconnected successfully.

1.3.5.2. Setting heater power with .Q1() and .Q2()#

For legacy reasons, there are two ways to set the percentage of maximum power delivered to the heaters. The first way is to the functions.Q1() and .Q2() of a TCLab instance. For example, both heaters can be set to 100% power with the functions

lab = TCLab()
lab.Q1(100)
lab.Q2(100)

The device firmware limits the heaters to a range of 0 to 100%. The current settiing may be accessed via

Q1 = lab.Q1()
Q2 = lab.Q2()

The LED on the temperature control laboratory will turns bright when either heater is on. Closing the TCLab instance turns the heaters off.

from tclab import TCLab
import time

with TCLab() as lab:
    print(f"Starting Temperature 1: {lab.T1:0.2f} C")
    print(f"Starting Temperature 2: {lab.T2:0.2f} C")

    lab.Q1(100)
    lab.Q2(100)
    
    print(f"Set Heater 1: {lab.Q1()} %")
    print(f"Set Heater 2: {lab.Q2()} %")
    
    t_heat = 30
    print(f"Heat for {t_heat} seconds")
    time.sleep(t_heat)

    print("Turn Heaters Off")
    lab.Q1(0)
    lab.Q2(0)

    print("Set Heater 1:", lab.Q1(), "%")
    print("Set Heater 2:", lab.Q2(), "%")
    
    print(f"Final Temperature 1: {lab.T1:0.2f} C")
    print(f"Final Temperature 2: {lab.T2:0.2f} C")
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
Starting Temperature 1: 22.83 C
Starting Temperature 2: 21.86 C
Set Heater 1: 100.0 %
Set Heater 2: 100.0 %
Heat for 30 seconds
Turn Heaters Off
Set Heater 1: 0.0 %
Set Heater 2: 0.0 %
Final Temperature 1: 26.38 C
Final Temperature 2: 24.12 C
TCLab disconnected successfully.

1.3.5.3. Setting heater power with .U1 and .U2#

Alternatively, the percentage of maximum power delivered to the heaters can be set by assigning value to the .U1 and .U2 attributes of a TCLab instances. Getting the value of .U1 and .U2 retrieves the current settings.

lab = TCLab()

print('Setting power levels on heaters 1 and 2')
lab.U1 = 50
lab.U2 = 25

print('Current power level on Heater 1 is: ', lab.U1, '%')
print('Current power level on Heater 1 is: ', lab.U2, '%')

lab.close()
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
Setting power levels on heaters 1 and 2
Current power level on Heater 1 is:  50.0 %
Current power level on Heater 1 is:  25.0 %
TCLab disconnected successfully.

1.3.6. Synchronizing with Real Time using clock#

The tclab module includes clock for synchronizing calculations with real time. clock(t_period, t_step) generates a sequence of evenly spaced time step over a periodt_period seconds that are t_step seconds apart. If t_step is omitted then the default time step is set to 1 second.

from tclab import clock

t_period = 6
t_step = 2
for t in clock(t_period, t_step):
    print(t, "sec.")
0 sec.
2.0 sec.
4.0 sec.
6.01 sec.

There are some considerations to keep in mind when using clock. Most important, by its nature Python is not a real-time environment. clock makes a best effort to stay in sync with evenly spaced ticks of the real time clock. If, for some reason, the loop falls behind the real time clock, then the generator will skip over the event to get back in sync with the real time clock. Thus the total number of iterations may be less than expected. This behavior is demonstrated in the following cell.

from tclab import TCLab, clock

import time

t_period = 12
t_step = 2
for t in clock(t_period, t_step):
    print(t, "sec.")
    
    # insert a long time out between 3 and 5 seconds into the event loop
    if (t > 3) and (t < 5):
        time.sleep(2.2)
0 sec.
2.0 sec.
4.0 sec.
8.0 sec.
10.0 sec.
12.0 sec.

1.3.6.1. Using clock with TCLab#

The following cell demonstrates use of clock to perform a short experiment.

from tclab import TCLab, clock

# length of the experiment in seconds
t_period = 20

with TCLab() as lab:
    # turn heaters on to 100%
    lab.Q1(100)
    lab.Q2(100)
    print(f"Set Heater 1 to {lab.Q1():.1f} %")
    print(f"Set Heater 2 to {lab.Q2():.1f} %")

    # print temperatures each second for tperiod seconds
    for t in clock(t_period):
        print(f"{t:5.1f} sec:  T1 = {lab.T1:4.1f} C   T2 = {lab.T2:4.1f} C")   
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
Set Heater 1 to 100.0 %
Set Heater 2 to 100.0 %
  0.0 sec:  T1 = 29.3 C   T2 = 26.8 C
  1.0 sec:  T1 = 29.3 C   T2 = 26.8 C
  2.0 sec:  T1 = 29.3 C   T2 = 27.1 C
  3.0 sec:  T1 = 29.3 C   T2 = 27.0 C
  4.0 sec:  T1 = 29.2 C   T2 = 27.1 C
  5.0 sec:  T1 = 29.3 C   T2 = 27.1 C
  6.0 sec:  T1 = 29.3 C   T2 = 26.8 C
  7.0 sec:  T1 = 29.3 C   T2 = 26.8 C
  8.0 sec:  T1 = 29.3 C   T2 = 26.9 C
  9.0 sec:  T1 = 29.3 C   T2 = 27.2 C
 10.0 sec:  T1 = 29.3 C   T2 = 27.1 C
 11.0 sec:  T1 = 29.3 C   T2 = 27.1 C
 12.0 sec:  T1 = 29.6 C   T2 = 27.1 C
 13.0 sec:  T1 = 29.6 C   T2 = 27.2 C
 14.0 sec:  T1 = 29.6 C   T2 = 27.3 C
 15.0 sec:  T1 = 29.7 C   T2 = 27.5 C
 16.0 sec:  T1 = 29.9 C   T2 = 27.7 C
 17.0 sec:  T1 = 29.9 C   T2 = 27.7 C
 18.0 sec:  T1 = 30.2 C   T2 = 27.4 C
 19.0 sec:  T1 = 30.2 C   T2 = 27.9 C
 20.0 sec:  T1 = 30.6 C   T2 = 28.0 C
TCLab disconnected successfully.

1.3.7. The TCLab Historian#

The Historian class provides means for logging process data to a database.

Given a list sources of data sources and methods to access the data, Historian(sources) creates an historian that logs data to database on each call to .update(). Given an instance lab of a TCLab object, lab.sources is a default list of data sources and methods for logging temperatures lab.T1 and lab.T2 and power settings lab.U1 and lab.U2.

lab = TCLab()
h = Historian(lab.sources)

The historian automatically initializes a database to log the process data. The database is updated by issuing a command

h.update(t)

where t is variable containing the current time.

To demonstrate, the following cell logs 10 seconds of data with time varying power level applied to heater 1. When the experiment is over, h.to_csv saves the data to a file that be imported in python or a spreadsheet application.

from tclab import TCLab, clock, Historian

with TCLab() as lab:
    h = Historian(lab.sources)
    for t in clock(10):
        lab.Q1(100 if t <= 5 else 0)
        h.update(t)
        
h.to_csv('data.csv')
TCLab version 0.4.10dev
Arduino Leonardo connected on port /dev/cu.usbmodem143101 at 115200 baud.
TCLab Firmware 2.0.1 Arduino Leonardo/Micro.
TCLab disconnected successfully.

Once saved, data can be read and plotted using the Pandas Data Analysis Library as demonstrated in this cell.

%matplotlib inline

import pandas as pd
data = pd.read_csv('data.csv')
data.index = data['Time']
print(data)
data[['Q1', 'Q2']].plot(grid=True)
        Time      T1      T2     Q1   Q2
Time                                    
0.00    0.00  33.692  29.857  100.0  0.0
1.00    1.00  33.789  29.922  100.0  0.0
2.00    2.00  33.757  29.922  100.0  0.0
3.01    3.01  33.789  29.922  100.0  0.0
4.00    4.00  33.789  29.890  100.0  0.0
5.00    5.00  34.111  29.922  100.0  0.0
6.00    6.00  34.111  29.922    0.0  0.0
7.00    7.00  34.111  29.922    0.0  0.0
8.00    8.00  34.176  30.051    0.0  0.0
9.00    9.00  34.434  30.212    0.0  0.0
10.00  10.00  34.434  30.212    0.0  0.0
<AxesSubplot:xlabel='Time'>
../_images/c85bea1aac4e938a2cc7a96e91b54efb28658baeb85f3e7abe891b4cac127c16.png

1.3.8. The TCLab Plotter#

The Plotter class adds a real time plotting of experimental data. A plotter is created from an instance of an historian as follows

h = Historian(lab.sources)
p = Plotter(h)

Updating the plotter also updates the associated historian.

p.update(t)

The following example shows how this works.

%matplotlib inline
import matplotlib.pyplot as plt

from tclab import TCLab, clock, Historian, Plotter

with TCLab() as lab:
    h = Historian(lab.sources)
    p = Plotter(h, 10)
    for t in clock(10):
        lab.Q1(100 if t <= 5 else 0)
        p.update(t)
        
h.to_csv('data.csv')
../_images/907048ae56c6f25f889f8134eec2ce25eb38e364b0a69ecacef2b8c8bff2dcca.png
TCLab disconnected successfully.
../_images/907048ae56c6f25f889f8134eec2ce25eb38e364b0a69ecacef2b8c8bff2dcca.png

1.3.9. Using TCLab Offline#

The tclab library includes a simulation capability. This is useful for circumstances when it isn’t possible to access the hardware. The followinig cell demonstrated the use of setup to use the library in simulation mode. The argument connected is set to True if the hardware is connected, otherwise False. Simulation mode allows the use of the speedup parameter to run experiments at some multiple of real time.

%matplotlib inline
from tclab import clock, setup, Historian, Plotter

t_period = 120
TCLab = setup(connected=False, speedup=20)
        
with TCLab() as lab:
    h = Historian(lab.sources)
    p = Plotter(h, t_period)
    for t in clock(t_period):
        lab.Q1(100 if t % 20 <= 5 else 0)
        p.update(t)
../_images/6a12b117eec5b7eae8eaf36a882fe3080ffbf208a955fef7852cee4c2791747d.png
TCLab Model disconnected successfully.
../_images/6a12b117eec5b7eae8eaf36a882fe3080ffbf208a955fef7852cee4c2791747d.png