Wednesday, 28 August 2019

Using python rather than C/C++ in the Arduino IDE

Initial setting up.

Download arduino-1.8.9-linux64.tar.xz or latest version and unpack.
cd arduino-1.8.9
sudo ./install.sh

In the Arduino IDE upload Standard Firmata

(file->examples->firmata->standard firmata)
Can kill IDE as the program stays loaded in the Arduino even after disconnecting.

Use Synaptic package manger to install python3-serial and python3-setuptools

Install newest pyFirmata

git clone https://github.com/tino/pyFirmata
cd pyFirmata
sudo python3 setup.py install


To test python can control the on-board LED on pin 13.

python3
from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
board.digital[13].write(1)
board.digital[13].write(0)

Useful trick.

>>> board.digital[8].write(1)
>>> board.digital[8].read()
1
>>> board.digital[8].write(0)
>>> board.digital[8].read()
0

Flashing the LED.

import time
from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
while True:
      board.digital[13].write(1)
      time.sleep(1)
      board.digital[13].write(0)
      time.sleep(2.0)

Bit neater way to refer to pins. In this example wire LED in series with weak resistor (R1) from pin 8 to GND and wire from 5V to switch to pin 2. Put strong resistor (R2) between pin 2 and GND. This way the switch can control the LED through the program. 



The strong resistor (R2) is known as a pull-down because it lowers the voltage at the sensor pin to zero. Note with a resistor, no current means no potential drop across it, so the sensor pin is at low voltage until it is connected to 5V when it goes high and in this situation, the pull-down resistor prevents a short. 



In the picture, a rheostat is being used as the switch. 

from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata, time
it = pyfirmata.util.Iterator(board)  # keep updating pin status
it.start()
sw = board.get_pin('d:2:i')   # digital pin 2 used as input
led = board.get_pin('d:8:o')  # digital pin 8 used as output
while True:
    time.sleep(0.3)   # stops cpu going mad
    value = sw.read()
    if value == 1:
        led.write(1)
    else:
        led.write(0)

Using analogue input pin as a voltmeter. Wire from 5V to one end of a pot, wire other end to GND and wire slider to pin A0. 


from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata, time
it = pyfirmata.util.Iterator(board)
it.start()
analog_0 = board.get_pin('a:0:i')
while True:
    time.sleep(1)
    print(analog_0.read()*5)  # analogue read in range 0 to 1 so scale.

Measure current. Wire 5V to resistor and LED in series to GND. Wire A0 to + side of resistor and A1 to - side. 



from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata, time
it = pyfirmata.util.Iterator(board)
it.start()
analog_0 = board.get_pin('a:0:i')
analog_1 = board.get_pin('a:1:i')
while True:
    time.sleep(1)
    V=5*(analog_0.read()-analog_1.read())
    print("Voltage drop across resistor: ", V)
    print("Current flowing (V/R): ", V/220)   # R=220 Ohms

Vary voltage coming from digital pin. Brightness of LED made to fluctuate by pulse-width modulation (PWM). Wire LED and low resistor in series between digital pin 11 and GND. 



from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import time, math
digital_0 = board.get_pin('d:11:p')  # p for PWM
thet=0
while True:
    time.sleep(0.05)
    value=(1+math.sin(thet))/2  # range 0 to 1
    digital_0.write(value)
    thet+=0.1 

Measure capacitance. Idea is to charge the cap from a digital output pin and use an analogue input pin to measure how long it takes to get 63.2% charged. The capacitance in Farads is the time in seconds divided by the charging resistance in Ohms.

  1. Wire digital pin 2 (charging) to 100k resistor then to + side of the cap.
  2. Wire this side of the cap to analogue pin 0.
  3. Wire the same side of the cap to a weak resistor and then to digital pin 4 which will be used to discharge the cap afterwards.
  4. Wire - side of cap to GND. 

from pyfirmata import Arduino, util, INPUT, OUTPUT
board=Arduino("/dev/ttyUSB0")
import pyfirmata, time
it = pyfirmata.util.Iterator(board)
it.start()
dis_pin = board.get_pin('d:4:i')   # discharge pin set to high impedence
cha_pin = board.get_pin('d:2:o')   # charge pin set to low impedence
analog_0 = board.get_pin('a:0:i')  # sensor pin
t=0
cha_pin.write(1)  # Charge the cap
while True:
    V=analog_0.read()
    if V != None:  # pain, pin gives None before 0
      if V < 0.632:
         print("Voltage before capacitor: ", V)
         time.sleep(1)
         t+=1
      else: break
print("Time elapsed to reach 63 % charged (sec): ", t)
# Discharge the cap
#cha_pin = board.get_pin('d:2:i')  # gives Pin Already Taken Error
#dis_pin = board.get_pin('d:4:o')  # gives same error
#cha_pin.mode = pyfirmata.INPUT    # works
cha_pin.mode = INPUT     # works if have imported INPUT
dis_pin.mode = OUTPUT    # works if have imported OUTPUT
while True:
    V=analog_0.read()
    if V==0:
          print("Capacitor discharged")
          break
    else: time.sleep(1)
board.exit() 

An alternative way of doing it is to charge the capacitor through a weak resistor and then time it to discharge to 36.8% through a 100k resistor.



Note: D2 and D4 swapped in the circuit diagram. 


This sort of thing works (similar to above but may give some clues):

from pyfirmata import Arduino, util, INPUT, OUTPUT
board=Arduino("/dev/ttyUSB0")
import pyfirmata, time
it = pyfirmata.util.Iterator(board)
it.start()
dis_pin = board.get_pin('d:4:i')   # discharge pin set to high impedence
cha_pin = board.get_pin('d:2:o')   # charge pin set to low impedence
analog_0 = board.get_pin('a:0:i')  # sensor pin
t=0
cha_pin.write(1)  # Charge the cap through 1k resistor
while True:
    V=analog_0.read()
    if V != None:  # pain, pin gives None before 0
      if V < 1.0:
         print("Charging voltage: ", V)
         time.sleep(1)
         t+=1
      else: break
cha_pin.write(0)  # Stop charging the cap
# Discharge the cap through 100k resistor
cha_pin.mode = INPUT     # set pin resistance high
dis_pin.mode = OUTPUT    # set pin resistance low
#dis_pin.write(0) # not needed
print("Charging pin state: ",cha_pin.read())
print("Discharging pin state: ",dis_pin.read())
t=0
while True:
      V=analog_0.read()
      if V > 0.368:
         print("Discharging voltage: ", V)
         time.sleep(1)
         t+=1
      else: break
print("Time elapsed to reach 37 % charged (sec): ", t)
board.exit()

Measure resistance. Wire from 5V to unknown resistor (R1) in series with known resistor (R2) and then to GND. Wire from junction between R1 and R2 to A0. The method assumes that R1 is within an order of magnitude or two of R2. 



from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata, time
it = pyfirmata.util.Iterator(board)
it.start()
analog_0 = board.get_pin('a:0:i')
R2=100000    # known resistor
while True:
    time.sleep(1)
    print("R1 = ", R2*(1/analog_0.read()-1))

Use photodiode as logic switch. Wire 5V to + side of photodiode (PD), wire - side of PD to D2 and wire the same side of PD to 1 megaOhm resistor and thence to GND.



from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata, time
it = pyfirmata.util.Iterator(board)  # keep updating pin status
it.start()
sw = board.get_pin('d:2:i')   # digital pin 2 used as input
while True:
    time.sleep(0.3)   # stops cpu going mad
    value = sw.read()
    print(value) 

This way can control the logic state of D2 with a torch i.e. a light makes it go True. The web says it is better to use the PD in reverse bias mode (i.e. backwards) but this made it too insensitive. 


Using a photodiode as a trigger to read the logic state of another. The red PD is connected to pin D2 and when illuminated this triggers the program to read the logic state of the green PD which is connected to D7. Sorry, the PD's are drawn in reverse by mistake.


from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata, time
it = pyfirmata.util.Iterator(board)  # keep updating pin status
it.start()
trigger = board.get_pin('d:2:i')   # digital pin 2 used as trigger
data1 = board.get_pin('d:7:i')     # digital pin 7 treated as data
oldstate=False
while True:
    #print("trigger: ", trigger.read(), "data: ", data1.read())
    time.sleep(0.3)   # stops cpu going mad
    value = trigger.read()
    if value==True and oldstate==False:  # positive transitions trigger read
                   data=data1.read()
                   print(data)
    oldstate=value

Real-time plotting of readings.* Set up circuit as below. 


The following code will give a real-time plot of the fractional voltage at A0 versus time in seconds using the animation function of matplotlib. The interval in the ani command (1000) is in milliseconds, so the custom animate function is called every second to read the voltage and plot a graph of the 20 most recent readings. 

import matplotlib.pyplot as plt
import matplotlib.animation as animation
from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata
it = pyfirmata.util.Iterator(board)
it.start()
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
xar = []
yar = []
a0=board.get_pin('a:0:i')
def animate(i,xar,yar,a0):  # i is the frame number always sent by ani below
    xar.append(i)
    yar.append(a0.read())   # append values into a list
    xar = xar[-20:]
    yar = yar[-20:]         # keep only 20 most recent points
    ax1.clear()
    ax1.plot(xar,yar)
ani = animation.FuncAnimation(fig, animate, fargs=(xar, yar,a0), interval=1000)
plt.show()

Doing the same thing with drawnow.* Drawnow is nicer but it was a bit of pain to work out how to install it, although it is pretty easy when you know how. Here's how to do it for python3:
  1. sudo apt-get install python3-pip
  2. sudo pip3 install drawnow
The code looks like this: 

import matplotlib.pyplot as plt
from drawnow import drawnow
from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata
it = pyfirmata.util.Iterator(board)
it.start()
yar = []   # Just storing y-values in this example.
plt.ion()  # Tells matplotlib to do interactive plots.
def makeFig():
    plt.title('Voltage')   # Other plotting commands go here.
    plt.plot(yar)          # Just doing line graph.
a0=board.get_pin('a:0:i')  
while True:
    yar.append(a0.read())
    yar = yar[-20:]
    drawnow(makeFig)
    plt.pause(1)

* Aaaggh, you don't need animate or drawnow after all. Take this circuit to measure the voltage after a photodiode in real time. Just a bit trickier to sort the x-axis limits. Hardly seems worth installing all the extra bloat as you only have to add an extra line or two of code overall. 


import matplotlib.pyplot as plt
from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata
it = pyfirmata.util.Iterator(board)
it.start()
n=0
xar, yar = [], []
plt.ion()
def makeFig(x,y):
    plt.title('Voltage')
    plt.xlim(x[0],x[-1])        # Give the indices for min/max-x.
    plt.plot(x,y, color="blue") # Needed or graph changes colour.
a0=board.get_pin('a:0:i')
while True:
    xar.append(n)
    yar.append(a0.read())
    if n>20:
           xar.pop(0)  # Another way to limit the list size.
           yar.pop(0)
    makeFig(xar,yar)
    plt.pause(1)
    n+=1

Study output from an oscillator. Here's a fairly simple oscillator made with a 555 (just blagged from the web). Idea is to read the output voltage from pin 3 with A0 for a couple of seconds and plot the result.


import time
import matplotlib.pyplot as plt
from pyfirmata import Arduino, util
board=Arduino("/dev/ttyUSB0")
import pyfirmata
it = pyfirmata.util.Iterator(board)
it.start()
n=0
xar, yar = [], []
a0=board.get_pin('a:0:i')
start=time.time()
while True:
    now=time.time()
    timestep=now-start
    xar.append(timestep)
    yar.append(a0.read())
    n+=1
    if timestep>2: break
    time.sleep(0.001)
print("Time elapsed (s): ", timestep, "\nRead cycles: ", n, \
      "\nSec per read cycle: ", timestep/n)
plt.plot(xar,yar)
plt.show()

It seems to work. Note, keeping a careful note of the time because the time required to read the data, run the program, etc, becomes significant if the sleep time is very short. 



You can control the amount of time the LED spends on or off by having another resistor and a diode in parallel with R1. In the example below, we use a small resistor connected to the diode which will allow the capacitor to either charge or discharge quickly. Whether it is the charging or the discharging of C1 that is affected is determined by the direction of the diode. Hence we can either have a very short on-time or a very short off-time by simply reversing the direction of the diode on the breadboard. 



The effect of reversing the diode is shown below. 



Experimenting with making this kind of two-transistor oscillator. Many problems, but eventually got it to work! The principle is that if the base of Q1 goes high then Q1 will conduct. This will cause C1 to charge through R2 and, when the voltage on the right-hand side of C1 gets high enough, Q2 will turn on. This will dump the charge in C2 and the drop in voltage on the left hand side of C2 will cause Q1 to turn off. C2 now charges through R3 until the voltage on its left hand side is enough to turn Q1 on again. 


It's all about getting the connections right on the npn transistors. Some notes below.



A bit of amplitude modulation. Found it was a bit dodgy doing this with an astable oscillator, so went back to the 555 timer.



Adjusting R2 and using the last script gives traces like this.





No comments:

Post a Comment