Python with Arduino
Table of Contents
ANACONDA Install Confirmation
Before getting started with serial communication with Python, you must make sure pip is installed. However, pip will be automatically installed if ANACONDA is installed. Thus, you will have to do this step only if you have not done it last time. Below is the link for the instructions given last time.
pip pyserial Installation
Open the cmd window (press the windows key + r and type 'cmd'), and type in the following:
python -m pip install pyserial
The download process will begin soon, and the pyserial module will be successively installed.
When you are finished installing pyserial, install bs4 using the same method. Type the following into the command window
python -m pip install bs4
Starting from this section, we will be using Python along with Arduino. As mentioned in previous classes, the codes being uploaded to the Arduino may have some limitations regarding the functionality. Starting from simple tasks such as byte sending through serial communication, we will be able to plot real time graphs based on the streamed data from Arduino.
$$
\large \text{Python in PC} \quad \overset{\text{Serial}}{\longleftrightarrow} \quad \text{Arduino} \quad \overset{\text{Digital Output}}{\longleftrightarrow} \quad \text{LED}$$
Lab 1: LED ON/OFF - Transmit (numeric) data from Python
Arduino code
int ON = 0;
const int pin = 9;
void setup() {
pinMode(pin, OUTPUT);
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
ON = Serial.read();
if (ON == 1)
digitalWrite(pin, HIGH);
else if (ON == 0)
digitalWrite(pin, LOW);
Serial.println(ON);
}
}
Python code
Before running this section, please make sure you are using the correct port. The sample code will be using 'COM4'.
import serial
import time
ser = serial.Serial('COM4', 9600, timeout=1)
ser.write(bytes([1]))
ser.write(bytes([0]))
You have to close ser to disconnect. Otherwise other devices cannot use this serial communication.
ser.close()
Now we are ready to combine all into python with serial communication to Arduino to control LEDs.
ser = serial.Serial('COM4', 9600, timeout=1)
for i in range(10):
ser.write(bytes([1]))
time.sleep(0.5)
ser.write(bytes([0]))
time.sleep(0.5)
ser.close()
Lab 2: LED ON/OFF - Transmit (string) data from Python
Arduino code
String LEDcmd = "";
const int pin = 9;
void setup() {
pinMode(pin, OUTPUT);
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
LEDcmd = Serial.readStringUntil('\n');
if (LEDcmd == "ON")
digitalWrite(pin, HIGH);
else if (LEDcmd == "OFF")
digitalWrite(pin, LOW);
}
}
Python code
str('ON').encode('UTF-8')
ser = serial.Serial('COM4', 9600, timeout=1)
ser.write(str('ON').encode('UTF-8'))
ser.write(str('OFF').encode('UTF-8'))
ser.close()
ser = serial.Serial('COM14', 9600, timeout=1)
for i in range(10):
ser.write(str('ON').encode('UTF-8'))
time.sleep(0.5)
ser.write(str('OFF').encode('UTF-8'))
time.sleep(0.5)
ser.close()
Lab 3: LED Brightness
Arduino code
int brightness = 0;
const int pin = 9;
void setup() {
pinMode(pin, OUTPUT);
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
brightness = Serial.read();
analogWrite(pin, brightness);
}
}
Python code
ser = serial.Serial('COM18', 9600, timeout=1)
for i in range(255):
ser.write(bytes([i]))
time.sleep(0.1)
for i in range(255):
ser.write(bytes([255-i]))
time.sleep(0.1)
ser.close()
Lab 4: Current Time on LCD Display
Arduino code
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
String LCDRead = "";
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
lcd.setCursor(0,0);
// Print a message to the LCD.
lcd.print("The current time");
Serial.begin(9600);
}
void loop() {
if (Serial.available()) {
LCDRead = Serial.readStringUntil('\n');
lcd.setCursor(0,1);
lcd.print(LCDRead);
}
}
Python code
import serial
import time
from datetime import datetime
ser = serial.Serial('COM18', 9600, timeout=1)
print(datetime.now().strftime('%H:%M:%S'))
ser.write((datetime.now().strftime('%H:%M:%S') + '\n').encode('UTF-8'))
ser.close()
import serial
import time
from datetime import datetime
ser = serial.Serial('COM18', 9600, timeout=1)
for i in range(15):
print(datetime.now().strftime('%H:%M:%S').encode('UTF-8'))
ser.write((datetime.now().strftime('%H:%M:%S') + '\n').encode('UTF-8'))
time.sleep(1)
ser.close()
Lab 5: Current temperature on LCD Display from the internet
Arduino code
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
String LEDRead = "";
void setup() {
lcd.begin(16,2);
Serial.begin(9600);
}
void loop() {
if (Serial.available()) {
LEDRead = Serial.readStringUntil('\n');
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Temperature in");
lcd.setCursor(0,1);
lcd.print("Ulsan: ");
lcd.setCursor(7,1);
lcd.print(LEDRead);
lcd.setCursor(9,1);
lcd.print("Degrees");
}
}
Python code
import serial
import time
ser = serial.Serial('COM14', 9600, timeout=1)
from bs4 import BeautifulSoup
from urllib.request import Request, urlopen
url = 'https://www.google.co.kr/search?q=ulsan+temperature'
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
html = urlopen(req).read()
soup = BeautifulSoup(html, 'html.parser')
result = soup.find_all('span', 'wob_t')
for i in range(len(result)):
print(result[i].text)
ser.write(str(result[0].text[:-2]).encode('utf-8'))
$$
\large \text{Python in PC} \quad \overset{\text{serial}}{\longleftrightarrow} \quad \text{arduino} \quad \overset{\text{PWM}}{\longleftrightarrow} \quad \text{servo}$$
Arduino code
#include <Servo.h>
Servo myservo;
const int pint = 9;
int pos = 0;
void setup() {
myservo.attach(pin);
myservo.write(0);
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
pos = Serial.read();
myservo.write(pos);
delay(50);
}
}
Python code
ser = serial.Serial('COM18', 9600, timeout=1)
ser.write(bytes([90]))
ser.write(bytes([180]))
ser.close()
ser = serial.Serial('COM18', 9600, timeout=1)
for angle in range(0,180):
ser.write(bytes([angle]))
time.sleep(0.1)
for angle in range(180,0,-1):
ser.write(bytes([angle]))
time.sleep(0.1)
ser.close()
import serial
import time
def move(angle):
if (0 <= angle <= 180):
ser.write(bytes([angle]))
else:
print("Servo angle must be an integer between 30 and 180.\n")
# Start the serial port to communicate with arduino
ser = serial.Serial('COM18', 9600, timeout=1)
print("The initial servo angle is 30, type 'end' if you want to stop")
while 1:
angle = input("Enter your angle: ")
if angle == "end":
print("Finished")
ser.close()
break
else:
move(int(angle))
time.sleep(0.01)
print("The current servo angle is " + angle)
Reference: http://playground.arduino.cc/Main/MPU-6050
The InvenSense MPU-6050 sensor contains a MEMS accelerometer and a MEMS gyro in a single chip. It is very accurate, as it contains 16-bits analog to digital conversion hardware for each channel. Therefor it captures the x, y, and z channel at the same time. The sensor uses the I2C-bus to interface with the Arduino.
Before plotting the streaming data from the MPU6050, we will try plotting data after saving it in a buffer. The example code below saves 50 data sets before actually plotting it.
Arduino Code
#include <Wire.h>
const int MPU = 0x68; //Default address of I2C for MPU 6050
int16_t AcX, AcY, AcZ;
void setup() {
Wire.begin(); // Wire library initialization
Wire.beginTransmission(MPU); // Begin transmission to MPU
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // MPU-6050 to start mode
Wire.endTransmission(true);
Serial.begin(9600);
}
void loop() {
Wire.beginTransmission(MPU); // Start transfer
Wire.write(0x3B); // register 0x3B (ACCEL_XOUT_H), records data in queue
Wire.endTransmission(false); // Maintain connection
Wire.requestFrom(MPU, 14, true); // Request data to MPU
//Reads byte by byte
AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
//Prints values on Serial
Serial.print(AcX);
Serial.print(",");
Serial.print(AcY);
Serial.print(",");
Serial.println(AcZ);
delay(20);
}
Python Code
import serial
import numpy as np
import matplotlib.pyplot as plt
%matplotlib qt
ser = serial.Serial('COM4',9600)
# Define Variables
Accx = []
Accy = []
Accz = []
len = 51
for i in range (len): # Wait until the buffer is filled up to 50 values
while (ser.inWaiting() == 0):
pass
arduinoString = ser.readline().decode("utf-8") #.strip()
dataArray = (arduinoString.split(',')) # Since the values are being sent consecutively,
# the values must be split into x,y,and z
# Data conversion from string to float
temp1 = float(dataArray[0])
temp2 = float(dataArray[1])
temp3 = float(dataArray[2])
# Convert second element to floating number and put in P
Accx.append(temp1)
Accy.append(temp2)
Accz.append(temp3)
print(Accx)
plt.figure(1)
plt.subplot(311) # Set y min and max values
plt.title('Saved data') # Plot the title
plt.grid(True) # Turn the grid on
plt.ylabel('Acceleration') # Set ylabels
plt.plot(Accx, 'bo-', label='X') # plot the temperature
plt.legend(loc='upper left') # plot the legend
plt.subplot(312) # Set y min and max values
plt.grid(True) # Turn the grid on
plt.ylabel('Acceleration') # Set ylabels
plt.plot(Accy, 'ro-', label='Y') # plot the temperature
plt.legend(loc='upper left')
plt.subplot(313) # Set y min and max values
plt.grid(True) # Turn the grid on
plt.ylabel('Acceleration') # Set ylabels
plt.plot(Accz, 'go-', label='Z') # plot the temperature
plt.legend(loc='upper left')
plt.show()
ser.close()
Arduino code
#include <Wire.h>
const int MPU = 0x68; //Default address of I2C for MPU 6050
int16_t AcX, AcY, AcZ;
void setup() {
Wire.begin(); // Wire library initialization
Wire.beginTransmission(MPU); // Begin transmission to MPU
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // MPU-6050 to start mode
Wire.endTransmission(true);
Serial.begin(9600);
}
void loop() {
Wire.beginTransmission(MPU); // Start transfer
Wire.write(0x3B); // register 0x3B (ACCEL_XOUT_H), records data in queue
Wire.endTransmission(false); // Maintain connection
Wire.requestFrom(MPU, 14, true); // Request data to MPU
//Reads byte by byte
AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
//Prints values on Serial
Serial.print(AcX);
Serial.print(",");
Serial.print(AcY);
Serial.print(",");
Serial.println(AcZ);
delay(20);
}
Python code
# Plot 3 signals
import serial
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
ser = serial.Serial('COM4', 9600)
leng = 201
fig = plt.figure(figsize=(12, 6))
ax = plt.axes(xlim=(0,leng-1), ylim=(-2, 2))
plt.title('Real-time sensor data')
plt.xlabel('Data points')
plt.ylabel('Acceleration [G]')
ax.grid(True)
graphX, = ax.plot([], [], 'b', label = 'X')
graphY, = ax.plot([], [], 'r', label = 'Y')
graphZ, = ax.plot([], [], 'g', label = 'Z')
ax.legend(loc='upper right')
ax.legend(loc='upper right')
ax.legend(loc='upper right')
t = list(range(0, leng))
accX = []
accY = []
accZ = []
for i in range(0, leng):
accX.append(0)
accY.append(0)
accZ.append(0)
def init():
graphX.set_data([], [])
graphY.set_data([], [])
graphZ.set_data([], [])
return graphX, graphY, graphZ
def animate(i):
global t, accX, accY, accZ
while (ser.inWaiting() == 0):
pass
arduinoString = ser.readline().decode("utf-8")
dataArray = arduinoString.split(',')
accX.append(float(dataArray[0])/(32767/2))
accY.append(float(dataArray[1])/(32767/2))
accZ.append(float(dataArray[2])/(32767/2))
accX.pop(0)
accY.pop(0)
accZ.pop(0)
graphX.set_data(t, accX)
graphY.set_data(t, accY)
graphZ.set_data(t, accZ)
return graphX, graphY, graphZ
delay = 20
anim = animation.FuncAnimation(fig, animate, init_func=init,
interval=delay, blit=True)
plt.show()
ser.close()
# Subplot
import serial
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
ser = serial.Serial('COM4', 9600)
leng = 201
fig = plt.figure(figsize=(12, 9))
ax1 = fig.add_subplot(3, 1, 1)
ax2 = fig.add_subplot(3, 1, 2)
ax3 = fig.add_subplot(3, 1, 3)
graphX, = ax1.plot([], [], 'b', label = 'X')
graphY, = ax2.plot([], [], 'r', label = 'Y')
graphZ, = ax3.plot([], [], 'g', label = 'Z')
axes = [ax1, ax2, ax3]
for ax in axes:
ax.set_xlim(0, leng-1)
ax.set_ylim(-2, 2)
ax.set_ylabel('Acceleration [G]')
ax.legend(loc='upper right')
ax.grid(True)
ax1.set_title('Real-time sensor data')
ax3.set_xlabel('Data points')
t = list(range(0, leng))
accX = []
accY = []
accZ = []
for i in range(0, leng):
accX.append(0)
accY.append(0)
accZ.append(0)
def init():
graphX.set_data([], [])
graphY.set_data([], [])
graphZ.set_data([], [])
return graphX, graphY, graphZ
def animate(i):
global t, accX, accY, accZ
while (ser.inWaiting() == 0):
pass
arduinoString = ser.readline().decode("utf-8")
dataArray = arduinoString.split(',')
accX.append(float(dataArray[0])/(32767/2))
accY.append(float(dataArray[1])/(32767/2))
accZ.append(float(dataArray[2])/(32767/2))
accX.pop(0)
accY.pop(0)
accZ.pop(0)
graphX.set_data(t, accX)
graphY.set_data(t, accY)
graphZ.set_data(t, accZ)
return graphX, graphY, graphZ
delay = 20
anim = animation.FuncAnimation(fig, animate, init_func=init,
interval=delay, blit=True)
plt.show()
ser.close()
%%html
<iframe src="https://www.youtube.com/embed/jemrZxfKVIY"
width="560" height="315" frameborder="0" allowfullscreen></iframe>
%%javascript
$.getScript('https://kmahelona.github.io/ipython_notebook_goodies/ipython_notebook_toc.js')