来源:http://www.oschina.net/translate/a-complete-project-with-arduino?utm_source=tuicool
英文原文:A complete project with Arduino
参与翻译 (3人) : realZ, leoxu, Ley
介绍
我有一个上小学的女儿. 作为一位父亲,当然该负起责任. 我喜欢自己作为父亲的角色,对此我毫无疑问. 但因此所带给我的困扰则是无止境的数学学习事务. 有时候这真的让我很头疼. 2+2, 5+6, 4+3,一遍又一遍. 那才刚开始呢, 现在又多了减法: 5-4, 10-4 .. 而每个人都知道这是没有结束可言的. 我抱怨的够多了,因而决定利用此项技术. 你知道的,技术为人而生, 而现在是为我而存在. 到这里来,我亲爱的 Arduino, 我需要你.
在最开始的时候,我希望这个项目能很容易实现. 我预计所有需要做的事情就是写一些函数来展示一些数字,并且为了根据趣味性,也许要来点蜂鸣声和一些LED灯光. 然后,情况在我开始精心考虑它的时候发生了改变, 出现了一些硬件管理问题, 然后是内容管理问题. 我们这个小小的 Arduino 应用程序变成了一个认真把握的东西,这导致我写下了这篇文章. 让我们先从需求开始吧.
计划需求
-
系统可以显示一个菜单,提供一些基本的操作:加、减、乘、除 。
-
用户(我的女儿)可以用键盘从菜单上选择一种算数运算来学习。
-
会有一些难度级别:在选择运算后,难度级别会显示出来。
-
根据选择的难度级别,会随机显示出一些问题,用户可以用键盘回答这些问题。
-
用户可以在确认前修改自己的答案。
-
在确认答案后,根据正确与否会显示出一条信息。
-
如果三次答错,将会显示出正确答案。
-
用户可以浏览菜单(点开菜单并选择菜单项)。
-
系统应具有音频和视频警告API,错误信息可以通过该API发送。
-
每种算术运算有一个限时小测环节。
-
限时小测随机从简到难给出问题。
-
测试后会显示出统计数据(回答了多少题目,答对了多少题目)。
-
在用户接近时系统可以引起她的注意。
-
可以有一些数学以外的娱乐环节,比如让她唱首歌、亲自己的爸爸等等。如果不这么做,用户将无法继续使用系统。
-
警告API可以用来在娱乐环节做一些有趣的事情。
硬件
我们在这个项目中需要什么:
-
Arduino Mega:http://arduino.cc/en/Main/arduinoBoardMega2560
-
字符液晶面板(Serial LCD):http://www.dfrobot.com/index.php?route=product/product&filter_name=adkeyboard&product_id=197#.UvNglbQSHxQ
-
矩阵键盘 :https://www.sparkfun.com/products/8653
-
模拟键盘 :http://www.dfrobot.com/index.php?route=product/product&filter_name=adkeyboard&product_id=197#.UvNglbQSHxQ
-
PIR运动传感器:http://www.cooking-hacks.com/pir-motion-sensor
-
LED 和 400欧姆电阻
-
数字蜂鸣模块:http://www.dfrobot.com/index.php?route=product/product&filter_name=buzzer&product_id=84#.UvNc2rQSHxQ
-
连接线
看看下面的硬件设计:
注意一些和上面不匹配的部分:
-
LCD应该是字符LCD。
-
Arduino UNO应该用Mega替代。
(我用Arduino UNO开启了这个项目,但是因为内存的需要后来改用Arduino Mega继续这个项目。开始的时候,Arduino UNO工作的很好。但是,当代码量增加,我无法将RAM使用量控制在Arduino UNO的容量之内,然后就想你所想的,我最终启用了Arduino Mega,它有8K的SRAM。)
软件设计
图1:设计的概览
系统被分为2个主要部分。就像你在图1中看到的,第一个模块负责硬件的管理。
-
输入系统:我们有两个不同的键盘,他们被统一在一起,对外提供统一的接口。统一的键盘信息将在(矩阵键盘或模拟键盘上的)任何按键被按下时告知注册的客户端。
-
输出系统:具有附加功能的字符液晶面板。
-
发信系统:统一发信子系统。它由一个LED和一个数字蜂鸣器组成,它将不同的信号转化为目标客户端的编码。它将不同的信号转换为客户端代码,客户端代码可以根据需求运行各种代码。
-
运动检测:用PIR传感器实现运动检测。当有人被检测到,它触发一个信号来引起注意。
前面说了,晶振相当于一个人的心脏,如果一个人心跳没了,那么人再好也没有用了。同样,单片机没了晶振部分,程序再好,电源再好也没有用,也没法正常工作。所以说晶振部分是重中之重。
表现层负责与用户的交互。它包含图形界面的处理(菜单和页面)并包含管理子系统。
-
UI管理:在这个子系统中,我们定义了图像对象。一个菜单列表被显示出来供用户选择。一个菜单项可以显示出子菜单或者一个页面。用户可以通过输入显示在其上的索引来选择菜单项。通过按'Escape'键来返回上级菜单。如果一个菜单项是一个页面,选择后将会将这个页面显示出来。页面可以显示出其上面的信息,并等待用户输入来改变它的内容,此时按下'Escape'键就会显示出用户菜单。如果用户输入错误,可以通过按'Backspace'键删除答案。根据答案的正确与否,相应的信号将会被触发。
-
内容管理:这个子系统提供显示在屏幕上的内容,包括各种算术运算的各种难度级别的生成算法。客户端代码(页面)会向这个子系统请求内容。
简单的类框图如下。这些图像展示了基本的框架,帮助你更容易的理解实际的类实现。
硬件管理
MFK_InputDevice将Keypad2和AdKeyboard统一为同一个接口。它处理它们的事件,并向其客户端提供一组新的编码,如下所示。
图2:输入子系统
按键映射:
Keypad | Button | Key | Value (hex) |
Matrix | 0 | '0' | 0x30 |
Matrix | 1 | '1' | 0x31 |
Matrix | 2 | '2' | 0x32 |
Matrix | 3 | '3' | 0x33 |
Matrix | 4 | '4' | 0x34 |
Matrix | 5 | '5' | 0x35 |
Matrix | 6 | '6' | 0x36 |
Matrix | 7 | '7' | 0x37 |
Matrix | 8 | '8' | 0x38 |
Matrix | 9 | '9' | 0x39 |
Matrix | * | Escape | 0x1B |
Matrix | # | Enter | 0x0D |
AD | S1 | Backspace | 0x08 |
AD | S2 | F1 | 0x80 |
AD | S3 | F2 | 0x81 |
AD | S4 | F3 | 0x82 |
AD | S5 | F4 | 0x83 |
MFK_OutputDevice继承自SerialLCD类。它结合SerialLCD类的功能,并对其进行了增强。
图3:输出子系统
一个信号模式从信号源产生。一个模式连同它的索引被储存在信号控制器中。想要启动一个信号模式非常简单,只要用它的索引从信号控制器调用它。
图4:发信系统
在硬件管理层顶层的是MFK_Hardware类。它只会所有其他硬件设备,对客户端隐藏多余的复杂性。举例来说,PIRMotionandSignalController没有被暴露给客户端。但是输入和输出设备必须要向外界开放,因为UI系统需要对这些功能的直接访问。信号模式也是在这个类里构建的,可以通过索引来访问他们。
图5:硬件管理
表现层
这一层负责与用户进行交互,它提供了视觉元素和内容。
ContentFactory根据toContentTypeEnum和ContentLevelEnum创建ContentProviders。客户端得到ContentFactory的实例,之后他可以请求一个content provider。
图6:内容管理
VisualItem是所有的视觉元素(菜单和页面)的基类。它还将硬件管理和呈现结合起来。'show' 和'msgbox'方法通过调用和回调VisualItem提供的方法使用输出设备(MFK_OutputDevice)和输入设备(MFK_InputDevice) 。'msgbox'方法也有能力启动一个信号模式,只要调用硬件(MFK_Hardware)的'signal'方法就可以了。
菜单就像他的名字一样,提供了一系列的菜单项可以选择。用户可以通过一个菜单项前面的索引选择它,然后菜单 'show' VisualItem。
Pageis是显示内容的视觉工具。除了娱乐内容,它会等待用户的输入。用户'Enter'她的答案,显示信息告知她对错。显示信息之后,就需要从内容管理获取新的内容。
Chapter是ContentProvider和Page之间的中间类。当一个页面被第一次显示时,与其相关的chapter和ContentProvider就会被创建。用户的答案直接由chapter处理,并由chapter判断其对错。Chapter也对页面内的学习会话进行统计。FunChapter是一种不向用户要求答案的chapter,QuizChapter是限时的chapter。在一个quiz chapter中,问题只有在时间截止之前才能回答。
图7:UI管理
实现
我希望你已经清楚了系统的通用结构。现在,是时候深入到代码中去,那里是真正的乐趣开始的地方。
我想以MathForKid.ino开始。它是上传到Arduino主板上的主要代码。
// File: MathForKid.ino// hardware managementMFK_Hardware* hw;
// presentationMenu* mainMenu;
void setup() {
// for debugging purposes Serial.begin(9600);
// get the instance and initialize it hw = MFK_Hardware::getInstance();
hw->begin();
// create user interface CreateUI();
// show the main menu mainMenu->show();
}
void loop() {
// update hardware hw->update();
// update active visual item VisualItem *v = VisualItem::getActiveItem();
if(v!=NULL)
v->update();
}
就这么多。在Arduino上运行你的应用吧。好吧,也许解释一下会更好。
正如我在“软件设计”那部分开头所说的,我们有两个部分:一个用来硬件管理,另一个用来展示。它们在代码顶部定义为全局变量,我们在 'setup'函数中将它们初始化。'loop'函数调用代码来更新它们。
事实上,CreateUI方法也是在这个文件中实现的。当用户开始交互时,它创建用户接口。mainMenu、所有的子菜单和一切页面都是这个方法产生的,chapter的属性也是其赋予的。
void CreateUI() {
mainMenu = new Menu("main");
// addition Menu* m = new Menu("+");
mainMenu->addMenuItem(m);
// level-1 page Page* p = new Page("L1");
p->setChapterProperties(Chapter::NormalChapter, \
ContentFactory::Addition, ContentFactory::Level1Content);
m->addMenuItem(p);
// level-2 page p = new Page("L2");
p->setChapterProperties(Chapter::NormalChapter, \
ContentFactory::Addition, ContentFactory::Level2Content);
m->addMenuItem(p);
...
我们接着来看这个应用的设计模式。
就像你所想的,MFK_Hardware是Facade模式的一个例子。它将底层的硬件管理问题隐藏起来,并对客户端提供了干净的接口。它同时也是 Singleton模式的代表,因为整个系统运行时其只产生一个实例。为了实现这个功能,MFK_Hardware的构造器、复制和赋值操作都被声明为私有方法。
// File: MFK_Hardware.h// private constructor to achieve singleton pattern MFK_Hardware();
MFK_Hardware(MFK_Hardware const&); // copy disabled void operator=(MFK_Hardware const&); // assigment disabled
你只能通过getInstance静态方法访问它们,这个方法是公共的:
// File: MFK_Hardware.h// static method to get the instance static MFK_Hardware* getInstance() {
static MFK_Hardware hw;
return &hw;
};
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >