极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 9391|回复: 5

用 Python 寫 Arduino sketches

[复制链接]
发表于 2015-10-9 22:13:48 | 显示全部楼层 |阅读模式
本帖最后由 叶斌远行 于 2015-11-1 16:07 编辑

刚刚搜索了一遍网站,发现13年已经有人写过这篇文章了,简单的浏览了一遍,感觉挺简单的,还有挺多东西,好像还没有说明白。因为自己最近也玩过一段时间这个Python写arduino sketch的应用。所以在这里再次写下一些过程经历。
以示敬意,提供以前另外一位网友的文章链接:用 Python 寫 Arduino sketches

在这里,为求尽量将详细细节凸显出来,所以打算分几个阶段来写。

如果你想照着来实现这个过程的话,首先就得有一块Arduino的主控板。我的是自己做的Arduino Pro mini板,自己做的,丝印都忘记弄出来了,但是能够勉强将就的使用一下

背面带一个ch340g,usb转串口芯片,方便程序的下载,拿来调试程序没有什么问题。

备注:如果有需要,可以联系我,我可以成本价卖给你,但是不包邮哦!
接下来,我们要往Arduino主控板的烧些一个ArduinoIDE自带的程序,从下面的路径找到这个Firmata:
Arduino IDE 里面找到 文件--》示例 --》Firmmata --》StandardFirmata
ArduinoFirmataStandar.png
打开这个ArduinoIDE自带的程序,像烧些其他程序一样,选好串口和板卡型号,然后将程序烧录到主控板上面。
这个过程还算简单,接下来就没有Arduino IDE的其他事情了。由于Firmata的存在,一旦运行Arduino主控板,这个程序,就负责解释从Python那边传送过来的指令,然后程序自动命令Arduino执行IO口的操作。

如果你对sparkfun的IOIO有所了解的话,这个过程其实很相像的,在那里就是在手机上用Java写程序控制IOIO硬件的,不过不熟悉也一点没有关系。

Firmata是一个简单的通讯协议来的,关于这个协议的更多知识,可以参考一些Arduino官网。但是我们这里可以先忽略这里面的详细细节,因为接下来我们要转到Python上面,去写脚本控制arduino了。

151009

登录Python官网,根据自己的pc系统,找到相应的Python的版本,这里建议选择Python2.7版本的,因为这个版本比较稳定,也比较好用,所以建议下载这个,接下来的操作,也将是在这里执行的。

我的操作系统是xp的,所以,很多细节,都是在这里实现的,至于其他操作系统,可能略有所不同。

下载好之后,在网络上,找一下环境变量的配置,将自己的操作系统配置一下Python的环境变量。

这里为止,假设已经在本地电脑上安装好了Python IDE,从屏幕左下角的“开始”打开python IDLE(Python GUI) , 记住Python一打开的是 Python shell 这是Python的壳,也就是交互操作界面,
右键打开IDELE(PythonGUI).png

因为交互界面不是那么好用,所以我们在此界面,点击一下file--》new file 打开一个新的编辑界面,这个界面是用来编写脚本语言的。
Pythonshell进入到Python脚本界面.png

对比一下这个界面和刚刚打开的Python shell 发现多了一个按键 RUN
Pythonshell和脚本界面对比.png

这是让你直接,执行所有脚本代码用的,在调试代码时,会经常用到,快捷键是 F5。先记住这点,后面会频繁用到。

打开后,观察一下界面的左上角,写着UNtitled 这指的是这个界面还没有保存,且还没有命名,所以接下来,点击file--》 save
在本地电脑上保存一下。 养成经常保存文档这样的好习惯,在电脑意外断电的时候,非常有用。

存储的时候,程序的命名也是非常重要的,一般的原则是为了能够让自己和别人快速从程序名字中,大致了解程序的作用,注意在程序的名字后面带一个后缀名 .py 如果没有的话,编译器并不会把你的文件保存为 .py 。 所以需要自己手动在保存文件时,添加一下。
Python程序命名.png

目前为止, 稍微熟悉了一下Python ,如果还有哪些不太明白的地方,或者我没有说清楚的地方,欢迎留言。
那么,我们来简单的试一下,Python能够做什么啊。

那么将下面的脚本代码,录入到Python IDLE 中,另存为以.py尾缀的文件,然后在用F5,就能执行脚本代码了
[pre lang="ython" line="1" file="ython_test"]# -*- coding: cp936 -*-
'''
reference url http://stackoverflow.com/questions/27397091/how-to-draw-sinus-wave-with-tkinter
plot a function like y = sin(x) with Tkinter Canvas and line
modifed by YeWenWu 2015-10-11 20:32
'''
from Tkinter import *
import math

master = Tk()
master.title("Simple plot using canvas and line")


canvas_width = 500
canvas_height = 400
canvas_center = canvas_height/2

#master.minsize(canvas_height, canvas_width)
# 约束窗口最小的长宽
x_increment = 1
#width stretch 宽度伸展
x_factor = 0.04
#height stretch 高度伸展
y_amplitude = 80

Canvas_ = Canvas(master,
                 width = canvas_width,
                 height = canvas_height,
                 bg = 'white')
# Canvas 窗口组件的参数定义,具体请Google搜索其用法
myLabel = Label(master,
                text = "this is a sin() plot , writed by YeWenWu 2015-10-11",
                anchor = SW)
# Label 窗口组件的参数定义,具体请Google搜索其用法
myLabel.pack(side = BOTTOM)
Canvas_.pack(fill = BOTH, expand = 1)
# pack的作用是管理canvas 和 label两个组件
str1 = "sin(x) = blue" #定义一个字符串,用于在窗口显示一些提示内容
Canvas_.create_text(10, 20, anchor = SW, text = str1)
# 在Canvas里面显示一个字符串str1

center_line = Canvas_.create_line(0,
                                  canvas_center,
                                  canvas_width,
                                  canvas_center,
                                  fill = 'green')
# 在串口中间画一条绿色的直线
# create the coordinate list for the sin() cure, have to been integers
xy1 = []
# 定义一个坐标数组
for x in range(500):
    # x coordinates
    xy1.append(x*x_increment)
    # y coordinates
    xy1.append(int(math.sin(x*x_factor) * y_amplitude) + canvas_center)
#把x,y的坐标使用方法append追加到数组xy1数组中

print xy1

sin_line = Canvas_.create_line(xy1, fill = "blue")
# 使用canvas中的方法create_line, 按照坐标数组中的坐标值,使用蓝色线条画出来
master.mainloop()
#使用mainloop将整个图形显示出来
[/code]
在Python代码界面,点击Run--》Run module F5 执行脚本代码
F5运行Python.png
得到下面的运行结果,这个脚本用到了Python自带的图形编辑界面module Tkinter,Tkinter是用来编写图形化界面,这个module是Python安装的时候,就自带有的,另外脚本用到math 模块,这个也是Python自带有的。 拿这两个module来使用,一个原因是因为方便, 另外也演示了Python能够用做什么,和怎么使用。
脚本的运行界面如下:
Python运行结果.png
得到一个正弦sin的图形,简单的扩展一下,假设电脑能够通过串口获得Arduino的某些传感器的值,是不是可以通过这个代码,简单的修改一下,做一个图形化显示数据的应用呢!
如果你对Python还不熟悉,那么脚本中有很多知识点,对你来说肯定还很陌生,不过没有关系,多参考一下别人怎么写代码,大概几个星期,就能够很快上手了。

选择Tkinter的另外一个原因是,在接下来使用Python写arduino sketch中还会再次使用到,目的就是使用Tkinter来编写Arduino的图形化程序。

如果你不是很熟悉Python里面模块,函数,等的使用方法的话,这里有一个简单快捷的方法可以查到,就是在Python IDLE shell中键入 help() 回车,然后会弹出很多内容出来,比如我想查 Tkinter 究竟有哪些东西, 我就在接下来的地方键入 Tkinter, 那么就会弹出Tkinter的帮助文档了,非常方便,但这并不是唯一获得帮助的方法,我觉得初学还是在Google或者Yahoo中多看一下别人关于某个函数或者module是怎么使用的。这或许是最有效的途径了。
Python的help截图.png

2015-10-11

Python这里需要引入一个模块module : pyfirmata ,这个模块并不是Python自带的,所以需要另外再pyPi里面下载,这里需要打开cmd, 前提是把Python的Windows环境变量配置好才能够使用。
在cmd按照下面的步骤,键入Python lib文件下面的site-packages里面,在这个文件夹里面有一个easy_install.py的文件,这个东西是方便你在本地从Python官网的pyPi里面取回Python的package,然后在本地安装。
进入Python的lib里面的.png
像接下来要用到的Pyfirmata,就是一个包(package)这个东西是建立在包 pyserial上面,主要是针对arduino里面预烧写的firmata程序的。
接下来在cmd里面,我们键入 easy_install.py pyfirmata ,就能够执行安装pyfirmata的任务了,很快,观察一下,在site-packages下面是不是多了一个pyfirmata-1.0.3-py2.7.egg的文件,里面还有两个文件夹,一个是EGG-INF 另外一个是pyfirmata, ok 如果执行到这一步,那么就可以开始编写arduino的sketch了。

注意上面安装pyfirmata是在电脑联网的情况下完成的。如果没有安装pyfirmata的话,你在Python里面执行导入pyfirmata操作是会像下面那样报错的。
强行导入pyfirmata报错.png

但是安装好了之后,重启一下Python,然后在导入import pyfirmata 是没有问题的,就像下面那样,没有什么提示,就是表示已经导入成功了。
执行pyfirmata成功.png

好了,接下来,我们就开始写程序了。 这里我模仿一下帖子《[url=arduino学习笔记31 - processing显示arduino传感器动作实验 http://geek-workshop.com/thread-301-1-1.html]arduino学习笔记31 - processing显示arduino传感器动作实验[/url]》,这里我用Python来代替了processing。
下面Python程序的大意是,获得arduino上某个连接了电位计传感器的ad值,然后在利用Tkinter来绘制一个会运动的红色小球,哈哈,没有什么难的吧,只是把processing,换成了Python了吧,说真的processing,其实我也很喜欢的。
好了,先看一下代码:


2015-10-13

不知道是ch340的原因,导致从arduino里面读取信号的时候,Python程序会出错。又可能像这个arduino官网论坛里面说的,
是没有使用iterator的原因,但是我试着按照网站上说的方法去做了一遍,也没有成功,所以由于还没有搞明白的原因,所以上面说的例子,得查出原因之后,继续更新。、

接下来,就参考一本外文书《Python programming for arduino》character 5 里面的讲到的,使用Tkinter 里面的窗口组件(widget)来写一个按键,控制一个红色LED灯的两灭。(不过很奇怪,Python 能够向arduino发送数据,却没有办法从Arduino那里获得数据,究竟是哪里出问题了呢?)

下面先看一下代码:
[pre lang="ython" line="1" file="Control_led_using_button"]from Tkinter import *
from pyfirmata import Arduino
from time import sleep

master = Tk()
master.title('Button control Led')
master.minsize(width = 192, height = 42)

board = Arduino("COM2")
sleep(1)
ledPin = board.get_pin('d:2')

def onStartButtonPress():
    startButton.config(state = DISABLED)
    ledPin.write(1)
    startButton.config(state = ACTIVE)

def onStopButtonPress():
    stopButton.config(state = DISABLED)
    ledPin.write(0)
    stopButton.config(state = ACTIVE)
   
startButton = Button(master,
                text = 'Start',
                command = onStartButtonPress)
startButton.grid(column = 1, row = 1)
#startButton.pack(side = LEFT)

stopButton = Button(master,
                    text = 'Stop',
                    command = onStopButtonPress)
stopButton.grid(column = 2, row = 1)
#stopButton.pack(side = RIGHT)

master.mainloop()
[/code]

下面是上面代码的运行界面:
using_button_python.png

通过上面的start 和 stop按键就能够控制led灯的亮灭:
using_button_python_实物.png

上面代码的具体解释,在后面会慢慢的解释的,如果急需了解,可以查看python  programming  for  arudino这本书的第五章里面讲解的内容。

20151016更新

上面之前还遗留了一些问题没有解决,Python无法从arduino那里读取数值,一直得到的结果都是None ,也郁闷了很久,最近在github的一篇文章找到了解决问题的方法了,如果你有兴趣,可以去读一下提问者的问题:Timing issue with analog reads(读取模拟串口的时间问题) ,其实Python 脚本如果一开启enable_reporting(开启报告)之后,没有个暂停的话,读取analog的值是会报错的,所以在这里需要使用sleep()函数来减缓速度。

根据文章的提示,我获得的一个比较好的速度是暂停70ms左右,就能读到模拟口的值了。
下面是我具体的代码
[pre lang="ython" line="1"]# -*- coding: cp936 -*-
from pyfirmata import Arduino
from pyfirmata.util import Iterator
import time
board = Arduino("COM5") #声明并建立串口连接

it = Iterator(board) #这里声明了一个线程,作用是当串口board有值传送过来,就开始读
it.start() # 启动线程
board.analog[2].enable_reporting() #开启模拟口读取

'''
while board.analog[2].read() is not  None:
    print board.analog[2].read()
'''

time.sleep(0.07) #暂停70ms ,这里的时间单位是m
print "analog[2]:"
print board.analog[2].read() #开始读取模拟口2的数值,打印的值是以小数为单位的,也就是浮点型

board.exit() # 退出串口连接,如果没有这条语句,你的Python就会一直和arduino连接在一起
[/code]
pyfirmata_analog_read.png

20151021更新
现在终于能够把上面存在的问题都解决掉了,之前出现Python未能从Arduino里面读取模拟值的问题,经过这几天的诸多尝试也搞定了问题,最大的问题是出现在以下几个方面:
1. 未加 Iterator 线程,最开始没有开这个线程,后面的脚本代码都加了,之所以要加这个东西,是因为Arduino是在一直读取模拟值,而Python要一直读取最新的模拟值得话,就需要 Iterator 这个线程来不停的更新
2. 在Python脚本中有些地方需要适当的延时,这样可以避免一些不必要的麻烦!在脚本中有几个地方都用到了Python 自带的延时函数,time.sleep()这个东西。
现在我这里把基础的代码先贴上来,只实现了快速读取Arduino传送过来的模拟值,然后打印出来,就这么简单的一个脚本,程序里面有具体的注释,如果还不能看懂的话,可以联系我!
[pre lang="ython" line="1"]# -*- coding: cp936 -*-
#reference url https://github.com/tino/pyFirmata/issues/16
#http://stackoverflow.com/search?q=pyfirmata+None
# https://www.google.com.hk/search?newwindow=1&safe=strict&q=pyfirmata+none&oq=pyfirmata+none&gs_l=serp.3...437030.438438.0.439329.2.2.0.0.0.0.0.0..0.0....0...1c.1.64.serp..2.0.0.mPX6-vTwM4U

#from pyfirmata.util import Iterator
from pyfirmata import Arduino, util #导入pyfirmata里面的Arduino,和util模块
import time,sys #导入time 和 sys模块

#board = Arduino("COM2",baudrate = 57600)
board = Arduino('COM5') #让这个脚本与串口5建立联系
for i in range(5): #这个for循环其实是让与数字2连接的灯闪烁5下,达到延时的功能
    board.digital[2].write(1)
    time.sleep(0.5)
    board.digital[2].write(0)
    time.sleep(0.5) #延时函数,单位是秒S
iterator = util.Iterator(board) #打开迭代器线程Iterator,并将board作为参数传给迭代器
iterator.start() #启动迭代器
time.sleep(1)
board.analog[2].enable_reporting() #将模拟口2启动

start = time.time() # 使用变量start ,记录程序运行到这里的时间,单位是float类型的
#print start
while True: #这个while是为了让程序进入死循环
    try: #使用try except 进行键盘按键捕捉 使用Ctrl+C就能终止这个循环了
        while board.analog[2].read() is None: # 有时串口会读到None
            pass #这里的意思大概就像是空循环一样
        print board.analog[2].read() #打印出有值传送过来的内容
        #time.sleep(0.1)
    except KeyboardInterrupt:
        print "Bye"
        board.exit() #断开串口连接
        sys.exit()
[/code]
我采用一个103的可调电位计,一丁点的电位计,用螺丝刀拧啊拧的,下面是固定电位计的一个值,非常的稳定,没有有什么拨动。
python_analog_readFromA2.png
这里打印出来的值,是小数,原因是这样子的,Arduino的模拟口读取的值,是0--1023,然后Python再从arduino那里读取这个值得时候,经过线性变换成0--1了,所以就是一些小数,如果看得不舒服,可以乘以1024恢复一下就行了。
下面是硬件的概览:
调整大小 pyfirmata_analog_tools.png
使用螺丝刀拧电位计;
调整大小 螺丝刀拧电位计.jpg
连接到ArduinoD2口的红色LED灯和面包板的大图,这里作为指示灯来使用:
调整大小 面包板.jpg
下面是我的ArduinoPromini板:
调整大小 ArduinoProMini.jpg

最后这里在附上一段视频,关于怎么操作的视频,录了1min多钟,效果不是很好,但是能够演示大概的内容:

视频还没有完全上传完,稍后补充连接:

到目前为止,已经能够顺利采集到Arduino的模拟值了,接下来就是准备把这些值应用起来了,那么就来做上面提到的一个借用这些数值,来控制一个运动的小球吧!


参考书籍|网站:
1. Python programming for arduino 这篇文章里面讲解的例子原型也是参考里面的内容讲解的
2. 可爱的Python哲学 主要查一些Python的变量之类的东西,这本书的电子文档目录编排的很糟糕
3. 和孩子一起编程
4. stack over flow 提问题和查问题的好地方
5. Python的官网
6. Python tutorials point
学习编程的一个方法,就是多自己录入程序,这样能够很好的培养编程手感的,因为自己也是初学Python,所以如果你看到这篇文章,希望对你有帮助,那将会是我的荣幸!

自己的arduinopromini

自己的arduinopromini
回复

使用道具 举报

发表于 2015-10-10 00:26:55 | 显示全部楼层
坐等Lz更新~
回复 支持 反对

使用道具 举报

发表于 2015-10-10 11:15:53 | 显示全部楼层
坐等更新,不错
回复 支持 反对

使用道具 举报

发表于 2015-10-10 20:41:12 | 显示全部楼层
期待更多Python有关的arduino开发
回复 支持 反对

使用道具 举报

发表于 2015-10-11 00:10:28 | 显示全部楼层
简单的看了下,好像要一直运行python脚本在PC机上。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-10-11 07:39:02 | 显示全部楼层
nosilence 发表于 2015-10-11 00:10
简单的看了下,好像要一直运行python脚本在PC机上。

是的,运行Python脚本的时候,需要一直运行Python,当然也可以采用一些办法,生成exe文件,也能避免一直运行Python,不过Python能够在多个平台上方便使用,Python写的脚本其实很方便移植,由于Python非常轻量化,在本地一直运行Python也不是什么麻烦事来的。毕竟支持Python开发的社区特别多,喜欢Python的人也很多,所以Python的功能还是非常方便和强大的。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊 ( 浙ICP备09023225号-2 )

GMT+8, 2021-6-13 13:15 , Processed in 0.094146 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表