December 5, 2021

Week 12:
Networking

Soil Moisture and Python live plot

I am utilizing the soil moisture sensor and the esp32 board from previous weeks to do this assignment. I chose python just because this is a language that I am familiar with. The code is simple on both the arduino side and python side. Firstly, I have the esp32 to read raw data from my moisture sensor via one of the adc pins and transmit the data through serial to my laptop. On python, I used matplotlib and animation to execute a simple time series live plot. Here are the codes:

Arduino


const int sensor = 13;
#include 
int n = 1;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("starting up"); 
  delay(2000);
}

void loop() {
  // put your main code here, to run repeatedly:
  //Serial.print(n);Serial.print(": ");
  Serial.println(analogRead(sensor));
  n++;
  delay(1000);
}
                                

Python


import serial
import datetime as dt
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style

data= []

# Read and record the data
ser = serial.Serial('COM5',115200)
# comment this section out if not in use
b = ser.readline()         # read a byte string
string_n = b.decode()  # decode byte string into Unicode  
string = string_n.rstrip() # remove \n and \r
flt = float(string)        # convert string to float
data.append(flt)           # add to the end of data list
time.sleep(0.1)            # wait (sleep) 0.1 seconds


# Create figure for plotting
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
xs = []
ys = []


# This function is called periodically from FuncAnimation
def animate(i, xs, ys):
    
    # Read temperature (Celsius) from TMP102
    b = ser.readline()
    string_n = b.decode()  # decode byte string into Unicode  
    string = string_n.rstrip() # remove \n and \r
    flt = float(string)
    moisture = flt

    # Add x and y to lists
    xs.append(dt.datetime.now().strftime('%H:%M:%S'))
    ys.append(moisture)

    # Limit x and y lists to 20 items
    xs = xs[-20:]
    ys = ys[-20:]

    # Draw x and y lists
    ax.clear()
    ax.plot(xs, ys)

    # Format plot
    plt.xticks(rotation=45, ha='right')
    plt.subplots_adjust(bottom=0.30)
    plt.title('DIY moisture sensor reading')
    plt.ylabel('Raw reading')

# Set up plot to call animate() function periodically
ani = animation.FuncAnimation(fig, animate, fargs=(xs, ys), interval=1000)
plt.show()
                                

The only issue I ran into while running this code is about port management. I did not know that serial port can only talk to one application at a time. If the Arduino serial command window is on, python will throw an error like this:


serial.serialutil.SerialException: could not open port 'COM5'
                                

Also, the sequence of opening the serial port, reading data, and closing port is important when embeding serial into a function. After trial and error, I think the best way to do it is to open serial port outside the recurring function and not closing it after reading it inside the recurring function. I ran into serial port open/close errors when the order of operations isn't correct. I got my program to work, it looks like this:

I have not gotten to calibrate this sensor, but I plan on adding a simple calibration function into my script and maybe two buttons for

Today I Learned:

1) One serial port, one application. Applications will fight for the port and throw errors. 2) Adding interacive features like buttons to my GUI is something I want to do in the future.