开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
一个用 Arduino 实现的完整项目 - 技术翻译 - 开源中国社区

一个用 Arduino 实现的完整项目 【已翻译100%】

标签: Arduino
oschina 推荐于 4年前 (共 14 段, 翻译完成于 02-20) 评论 42
收藏  
278
推荐标签: Arduino 待读

介绍

我有一个上小学的女儿. 作为一位父亲,当然该负起责任. 我喜欢自己作为父亲的角色,对此我毫无疑问. 但因此所带给我的困扰则是无止境的数学学习事务. 有时候这真的让我很头疼. 2+2, 5+6, 4+3,一遍又一遍. 那才刚开始呢, 现在又多了减法: 5-4, 10-4 .. 而每个人都知道这是没有结束可言的. 我抱怨的够多了,因而决定利用此项技术. 你知道的,技术为人而生, 而现在是为我而存在. 到这里来,我亲爱的 Arduino, 我需要你.

在最开始的时候,我希望这个项目能很容易实现. 我预计所有需要做的事情就是写一些函数来展示一些数字,并且为了根据趣味性,也许要来点蜂鸣声和一些LED灯光. 然后,情况在我开始精心考虑它的时候发生了改变, 出现了一些硬件管理问题, 然后是内容管理问题. 我们这个小小的 Arduino 应用程序变成了一个认真把握的东西,这导致我写下了这篇文章. 让我们先从需求开始吧.

LeoXu
 翻译得不错哦!

计划需求

  • 系统可以显示一个菜单,提供一些基本的操作:加、减、乘、除 。

  • 用户(我的女儿)可以用键盘从菜单上选择一种算数运算来学习。

  • 会有一些难度级别:在选择运算后,难度级别会显示出来。

  • 根据选择的难度级别,会随机显示出一些问题,用户可以用键盘回答这些问题。

  • 用户可以在确认前修改自己的答案。

  • 在确认答案后,根据正确与否会显示出一条信息。

  • 如果三次答错,将会显示出正确答案。

  • 用户可以浏览菜单(点开菜单并选择菜单项)。

  • 系统应具有音频和视频警告API,错误信息可以通过该API发送。

  • 每种算术运算有一个限时小测环节。

  • 限时小测随机从简到难给出问题。  

  • 测试后会显示出统计数据(回答了多少题目,答对了多少题目)。

  • 在用户接近时系统可以引起她的注意。

  • 可以有一些数学以外的娱乐环节,比如让她唱首歌、亲自己的爸爸等等。如果不这么做,用户将无法继续使用系统。

  • 警告API可以用来在娱乐环节做一些有趣的事情。

realZ
 翻译得不错哦!

硬件 

我们在这个项目中需要什么: 

  1. Arduino Mega

  2. 字符液晶面板(Serial LCD)

  3. 矩阵键盘 

  4. 模拟键盘 

  5. PIR运动传感器

  6. LED 和 400欧姆电阻

  7. 数字蜂鸣模块

  8. 连接线

看看下面的硬件设计:

注意一些和上面不匹配的部分:

  1. LCD应该是字符LCD。

  2. Arduino UNO应该用Mega替代。

(我用Arduino UNO开启了这个项目,但是因为内存的需要后来改用Arduino Mega继续这个项目。开始的时候,Arduino UNO工作的很好。但是,当代码量增加,我无法将RAM使用量控制在Arduino UNO的容量之内,然后就想你所想的,我最终启用了Arduino Mega,它有8K的SRAM。)

realZ
 翻译得不错哦!

软件设计


 

图1:设计的概览

系统被分为2个主要部分。就像你在图1中看到的,第一个模块负责硬件的管理。

  • 输入系统:我们有两个不同的键盘,他们被统一在一起,对外提供统一的接口。统一的键盘信息将在(矩阵键盘或模拟键盘上的)任何按键被按下时告知注册的客户端。

  • 输出系统:具有附加功能的字符液晶面板。

  • 发信系统:统一发信子系统。它由一个LED和一个数字蜂鸣器组成,它将不同的信号转化为目标客户端的编码。它将不同的信号转换为客户端代码,客户端代码可以根据需求运行各种代码。

  • 运动检测:用PIR传感器实现运动检测。当有人被检测到,它触发一个信号来引起注意。

表现层负责与用户的交互。它包含图形界面的处理(菜单和页面)并包含管理子系统。

  • UI管理:在这个子系统中,我们定义了图像对象。一个菜单列表被显示出来供用户选择。一个菜单项可以显示出子菜单或者一个页面。用户可以通过输入显示在其上的索引来选择菜单项。通过按'Escape'键来返回上级菜单。如果一个菜单项是一个页面,选择后将会将这个页面显示出来。页面可以显示出其上面的信息,并等待用户输入来改变它的内容,此时按下'Escape'键就会显示出用户菜单。如果用户输入错误,可以通过按'Backspace'键删除答案。根据答案的正确与否,相应的信号将会被触发。

  • 内容管理:这个子系统提供显示在屏幕上的内容,包括各种算术运算的各种难度级别的生成算法。客户端代码(页面)会向这个子系统请求内容。

简单的类框图如下。这些图像展示了基本的框架,帮助你更容易的理解实际的类实现。

realZ
 翻译得不错哦!

硬件管理  

MFK_InputDevice将Keypad2和AdKeyboard统一为同一个接口。它处理它们的事件,并向其客户端提供一组新的编码,如下所示。


图2:输入子系统

按键映射:

Keypad Button Key Value (hex) 
Matrix '0' 0x30
Matrix  '1'  0x31
Matrix  '2'   0x32
Matrix  '3'  0x33
Matrix  '4'  0x34
Matrix  '5'  0x35
Matrix  '6'  0x36
Matrix  '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:发信系统

realZ
 翻译得不错哦!

在硬件管理层顶层的是MFK_Hardware类。它只会所有其他硬件设备,对客户端隐藏多余的复杂性。举例来说,PIRMotionandSignalController没有被暴露给客户端。但是输入和输出设备必须要向外界开放,因为UI系统需要对这些功能的直接访问。信号模式也是在这个类里构建的,可以通过索引来访问他们。

图5:硬件管理

realZ
 翻译得不错哦!

表现层

这一层负责与用户进行交互,它提供了视觉元素和内容。

ContentFactory根据toContentTypeEnum和ContentLevelEnum创建ContentProviders。客户端得到ContentFactory的实例,之后他可以请求一个content provider。


 

图6:内容管理

VisualItem是所有的视觉元素(菜单和页面)的基类。它还将硬件管理和呈现结合起来。'show' 和'msgbox'方法通过调用和回调VisualItem提供的方法使用输出设备(MFK_OutputDevice)和输入设备(MFK_InputDevice) 。'msgbox'方法也有能力启动一个信号模式,只要调用硬件(MFK_Hardware)的'signal'方法就可以了。

菜单就像他的名字一样,提供了一系列的菜单项可以选择。用户可以通过一个菜单项前面的索引选择它,然后菜单 'show' VisualItem。

Pageis是显示内容的视觉工具。除了娱乐内容,它会等待用户的输入。用户'Enter'她的答案,显示信息告知她对错。显示信息之后,就需要从内容管理获取新的内容。

realZ
 翻译得不错哦!

Chapter是ContentProvider和Page之间的中间类。当一个页面被第一次显示时,与其相关的chapter和ContentProvider就会被创建。用户的答案直接由chapter处理,并由chapter判断其对错。Chapter也对页面内的学习会话进行统计。FunChapter是一种不向用户要求答案的chapter,QuizChapter是限时的chapter。在一个quiz chapter中,问题只有在时间截止之前才能回答。

图7:UI管理

realZ
 翻译得不错哦!

实现

我希望你已经清楚了系统的通用结构。现在,是时候深入到代码中去,那里是真正的乐趣开始的地方。

我想以MathForKid.ino开始。它是上传到Arduino主板上的主要代码。

// File: MathForKid.ino
// hardware management
MFK_Hardware* hw;
 
// presentation
Menu* 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'函数调用代码来更新它们。

Ley
 翻译得不错哦!

事实上,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;
    };

realZ
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(42)
Ctrl/CMD+Enter

设计模式我都没有翻译成中文,因为一般大家都这么叫
哈哈哈,翁恺老师
good job
直接买个计算器是不是更方便?

引用来自“无量神通”的评论

直接买个计算器是不是更方便?

那能体现出你是程序员么?

引用来自“然则何时而乐”的评论

引用来自“无量神通”的评论

直接买个计算器是不是更方便?

那能体现出你是程序员么?

实用至上……
那不是代码,是父爱。
不明觉厉
还是玩硬件比较有意思
一冲动看成Android
看成android点进来的。
我去,我看成了一个Android项目
看成android( ╯□╰ )
牛掰啊, 很有爱啊!
无法直视~~
吊爆了,这种文章不赞我还是阿姨吗
大爱~

引用来自“chazz”的评论

看成android点进来的。

一样滴!

引用来自“然则何时而乐”的评论

引用来自“无量神通”的评论

直接买个计算器是不是更方便?

那能体现出你是程序员么?

文章体现了作者是程序员!
很有爱
顶部