电子说
想要为您的下一个桌面角色扮演游戏带来一些独特之处吗?带有自定义图形的电子D20如何处理重大打击和遗漏?今天,我将向您展示如何使用Arduino和一些简单的零件来构建自己的产品。
如果您以前从未使用过Arduino,请不要担心,我们有一个入门指南。
构建计划
这是一个简单的项目。 Arduino将驱动OLED显示屏,并且一个按钮将使模具死掉。自定义图形将显示关键命中或关键错位。您可以轻松地将代码修改为D8,D10或D12。
您需要的内容
1 x Arduino
1 x 0.96英寸I2C OLED显示屏
1 x按钮
1 x 10k?电阻器
1 x面包板
各种连接线
此处完整代码,如果您不想完全按照书面说明进行操作。
这些是构建自己的D20所需的核心部分。您可能希望将其安装在外壳中(如下所述),并将电路焊接到更永久的状态。以下是您需要执行此操作的其他零件:
4 x M2 x 10mm(0.4英寸)螺栓
4 x M2螺母
4个7毫米(0.28英寸)垫圈
9V电池卡扣(或合适的替代品)
各种热缩管
这些OLED显示器非常凉。通常可以购买白色,蓝色,黄色或这三种的混合物。我购买了蓝色的,以匹配我的情况。确保您获得的是 I2C 模型而不是 SPI 。
几乎所有Arduino都适用。我选择了Nano,因为它们足够小,可以放入箱子中。请查看我们的购买指南以获取有关Arduino型号的更多信息。
电路
这是您需要的电路:
将OLED显示屏上的 VCC 和 GND 连接到Arduino + 5V 和接地。将Arduino上的模拟4 连接到标有 SDA 的引脚。将模拟5 连接到 SCL 引脚。这些引脚包含使用I2C总线驱动显示器所需的电路。确切的引脚会因型号而异,但是Nano和Uno会使用A4和A5。如果您未使用Uno或Nano,请查看模型的线库文档。
将电池接地并使用 VIN 引脚。这表示输入电压,可以接受各种不同的DC电压-但请先检查您的特定型号,有时可能会略有不同。
将按钮连接到数字引脚2 。注意10k如何?电阻接地。这个非常重要!这称为下拉电阻,它可以防止Arduino在按下按钮时检测到虚假数据或干扰。它还可以保护电路板。如果不使用该电阻,则+ 5V会直接接地。这被称为短短路,是杀死Arduino的简便方法。
如果要焊接此电路,请使用热缩管保护连接:
请确保不要将其加热过多,并且只有在确定电路能够正常工作后才进行加热。您可能还希望将电缆成对绞合。这样可以使它们保持整洁并有助于保护它们免受不必要的压力:
按钮测试
现在,您已经建立了电路,请上传此测试代码(请确保从 Tools》 Board 和 Tools》 Port 菜单中选择正确的电路板和端口):
const int buttonPin = 2; // the number of the button pin
void setup() {
pinMode(buttonPin, INPUT); // setup button
Serial.begin(9600); // setup serial
}
void loop(){
if(digitalRead(buttonPin) == HIGH) {
Serial.print(“It Works”);
delay(250);
}
}
上传后,保持Arduino通过USB连接并打开串行监视器(右上角》串行监视器)。每次按下按钮,您应该会看到它有效字样。
如果什么也没发生,请仔细检查电路。
OLED设置
您需要安装两个库来驱动显示。从Github下载Adafruit_SSD1306和Adafruit-GFX [不再可用]库,并将它们保存到您的库文件夹中。如果不确定库文件夹在哪里,请阅读我的复古游戏教程,在该教程中我会更详细地配置相同的显示器。
重新启动Arduino IDE并从 File上传测试草图》示例菜单。选择 Adafruit SSD1306 ,然后选择 ssd1306_128x64_i2c 。上载此代码(需要一些时间),您应该会在显示器上看到许多形状和图案:
如果什么都没有发生,请仔细检查您的联系。如果在检查后仍然无法使用,则需要修改示例代码。
更改此行(在 setup 函数的开头):
display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
对此:
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
这会告诉库有关正在使用的显示的特定详细信息。现在应该将所有内容设置为继续进行构建。
案例
如果要在面包板上构建,或者不希望将其装箱,则可以跳过此步骤。
我设计了此框并进行了3D打印。在Thingiverse上获取文件。如果您没有3D打印机,请不要担心-在线服务3D集线器和Shapeways提供在线打印服务。
您可以轻松地用木头制作此盒子,也可以购买塑料工程盒。
盖子是简单的推入配合设计,并且包含一些用于硬件的切口:
代码 》
现在一切准备就绪,是时候编写代码了。以下是它在伪代码中的工作方式:
if button is pressed
generate random number
if random number is 20
show graphic
else if random number is 1
show graphic
else
show number
为使其正常工作,需要生成一个随机数-这就是死机。 Arduino有一个称为 random 的随机数生成器,但不应使用它。尽管对于基本的随机任务已经足够了,但对于电子芯片来说,随机性还不够。原因有些复杂,但是如果您对boallen.com感兴趣,可以阅读更多内容。
通过sirleech在Github上下载TrueRandom库。将此添加到您的库文件夹中,然后重新启动IDE。
现在创建一个新文件并设置您的初始代码(或只是从GitHub上获取完成的代码):
#include
#include
#include
#include
#include
Adafruit_SSD1306 display(4);
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // setup the OLED
pinMode(buttonPin, INPUT); // setup button
}
void loop() {
}
此代码配置OLED,并包括与之通信所需的所有库以及新的随机数库。现在将其添加到主循环中:
if(digitalRead(buttonPin) == HIGH) {
delay(15);
if(digitalRead(buttonPin) == HIGH) {
display.fillScreen(BLACK); // erase the whole display
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.println(TrueRandom.random(1, 21)); // print random number
display.display(); // write to display
delay(100);
}
}
这在目前是很基本的,但是它是可以正常工作的D20。每当按下按钮时,屏幕上就会显示一个介于1到20之间的随机数:
这很好,但是有点无聊。让我们做得更好。创建两个新方法 drawDie 和 eraseDie :
void drawDie() {
display.drawRect(32, 0, 64, 64, WHITE);
}
这些将在屏幕中间绘制一个骰子。您可能希望通过绘制D20或D12等使事情变得更复杂,但是绘制基本的六面模具更简单。基本用法如下:
drawDie();
接下来,修改您的主循环以绘制随机数,该随机数仅在中间较大。将文本大小和光标更改为:
display.setTextColor(WHITE);
display.setCursor(57, 21);
现在看起来好多了:
唯一的问题是大于9的数字:
解决方法很简单。小于10的任何数字都将光标设置在与10或更大的数字不同的位置。替换此行:
display.setCursor(57, 21);
与此:
int roll = TrueRandom.random(1, 21); // store the random number
if (roll 《 10) {
// single character number
display.setCursor(57, 21);
}
else {
// dual character number
display.setCursor(47, 21);
}
这是现在的样子:
现在剩下的只是当您击中关键命中或未命中时的图像。涉及几个步骤,但这是一个足够简单的过程。
找到您要使用的合适图像(越简单越好,因为显示仅是单色的)。这是我使用的图像:
《图id =“ attachment_617869” aria- describeby =“ caption-attachment-617869” class =“ wp-caption aligncenter”》
图片来源:publicdomainvectors.org
您要使用的任何图片都需要转换到十六进制数组。这是代码形式的图像表示。有许多工具可以执行此操作,其中一些是专门为OLED显示器编写的。最简单的方法是使用PicturetoC_Hex在线工具。以下是所需的设置:
上传图像,并将代码格式设置为 HEX:0x 。将用于的设置为所有绘制图像功能的黑色/白色。将所有其他选项保留为默认值。您可以根据需要在此处调整图像大小。按获取C字符串,您应该会看到图像数据出现:
您将在一分钟内需要此生成的数据。创建两个名为 drawExplosion 和 drawSkull 的函数(或适合您的版本的名称)。这是代码:
void drawExplosion() {
// store image in EEPROM
static const unsigned char PROGMEM imExp[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x78,0x7f,0xff,0xc0,0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xf0,0x00,0x00,0x00,0x3f,0xff,0xff,0xff,0xfb,0x00,0x00,0x00,0x7f,0xff,0xff,0xff,0xff,0xc0,0x00,0x00,0x7f,0xff,0xff,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x07,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x07,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x07,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x07,0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x1f,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x1f,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0x07,0xff,0xff,0xf9,0xff,0xd8,0x00,0x00,0x00,0x3f,0xff,0xf0,0x0f,0x00,0x00,0x00,0x00,0x1f,0x1f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0xff,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xff,0x00,0x00,0x00,0x07,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x0f,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0xfc,0x00,0x00,0x01,0xbf,0xff,0xff,0xff,0x30,0x00,0x00,0x00,0x13,0xf7,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
display.drawBitmap(0, 0, imExp, 64, 62, 1); // draw mushroom cloud
}
void drawSkull() {
// store image in EEPROM
static const unsigned char PROGMEM imSku[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0xf0,0x00,0x00,0x00,0x00,0x78,0x00,0x07,0xf0,0x00,0x00,0x00,0x00,0xfc,0x00,0x07,0xf8,0x00,0x00,0x00,0x00,0xfe,0x00,0x07,0xf8,0x00,0x00,0x00,0x01,0xfe,0x00,0x07,0xfc,0x00,0x00,0x00,0x01,0xfe,0x00,0x07,0xfe,0x00,0x3f,0xc0,0x03,0xfe,0x00,0x01,0xff,0x81,0xff,0xfc,0x07,0xec,0x00,0x00,0x3f,0xc7,0xff,0xff,0x1f,0xc0,0x00,0x00,0x0f,0xcf,0xff,0xff,0xdf,0x00,0x00,0x00,0x07,0xbf,0xff,0xff,0xee,0x00,0x00,0x00,0x01,0x7f,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xf8,0x00,0x00,0x00,0x01,0xff,0xff,0xff,0xf8,0x00,0x00,0x00,0x03,0xff,0xff,0xff,0xfc,0x00,0x00,0x00,0x07,0xff,0xff,0xff,0xfe,0x00,0x00,0x00,0x0f,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x0f,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0x80,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0x80,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0x80,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0x80,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0x80,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0x80,0x00,0x00,0x1e,0x3f,0xff,0x3f,0xc7,0x80,0x00,0x00,0x1e,0x0c,0x0f,0x00,0x07,0x80,0x00,0x00,0x1e,0x00,0x0f,0x00,0x0f,0x80,0x00,0x00,0x1e,0x00,0x19,0x80,0x0f,0x00,0x00,0x00,0x0f,0x00,0x19,0x80,0x0f,0x00,0x00,0x00,0x0d,0x00,0x30,0xc0,0x1f,0x00,0x00,0x00,0x05,0x80,0x70,0xc0,0x1e,0x00,0x00,0x00,0x05,0xf0,0xe0,0xe0,0x36,0x00,0x00,0x00,0x01,0xff,0xe0,0x7f,0xf0,0x00,0x00,0x00,0x03,0xff,0xc4,0x7f,0xf0,0x00,0x00,0x00,0x03,0xff,0xcc,0x7f,0xf0,0x00,0x00,0x00,0x03,0xff,0xcc,0x7f,0xf0,0x00,0x00,0x00,0x03,0xff,0x9e,0x7f,0xf0,0x00,0x00,0x00,0x00,0xff,0xfe,0x7f,0xc0,0x00,0x00,0x00,0x00,0x01,0xff,0xf8,0x1c,0x00,0x00,0x00,0x03,0xe0,0x3f,0x01,0xbf,0x00,0x00,0x00,0x07,0xa6,0x40,0x09,0x9f,0x80,0x00,0x00,0x1f,0x27,0x5a,0x39,0x9f,0xf8,0x00,0x01,0xff,0x27,0xdb,0x39,0x0f,0xfc,0x00,0x03,0xfe,0x31,0x7f,0x39,0x07,0xfc,0x00,0x03,0xfc,0x10,0x1a,0x02,0x03,0xf8,0x00,0x03,0xf8,0x10,0x00,0x02,0x01,0xf0,0x00,0x01,0xf8,0x10,0x00,0x02,0x01,0xe0,0x00,0x00,0x78,0x10,0x00,0x02,0x00,0xe0,0x00,0x00,0x70,0x30,0x00,0x02,0x00,0x00,0x00,0x00,0x30,0x20,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x1b,0x00,0x00,0x00,0x00,0x00,0x73,0x55,0x63,0x00,0x00,0x00,0x00,0x00,0xf9,0x55,0x4f,0x00,0x00,0x00,0x00,0x00,0x7f,0x14,0x1f,0x00,0x00,0x00,0x00,0x00,0x1f,0xe0,0xfe,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xfc,0x00,0x00,0x00,0x00,0x00,0x07,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x03,0xff,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
display.drawBitmap(0, 0, imSku, 60, 64, 1); // draw skull cloud
}
如果您想使用我使用过的图像,请继续并复制代码。如果要使用自己生成的图像,请根据需要将字节码复制到 imSku 和 imExp 数组中。
以下是这些图像在显示屏上看起来像:
该代码最重要的部分是这一行:
static const unsigned char PROGMEM imSku[]
这告诉Arduino将图像存储在EEPROM(什么是EEPROM?)中而不是其RAM中(RAM的快速指南)。这样做的原因是简单的; Arduino的内存有限,并且全部用于存储图像可能不会留下任何剩余代码供您执行
修改主 if 语句以在出现以下情况时显示这些新图形:一卷或二十卷。请注意以下代码行,以显示与图像一起滚动的数字:
if(roll == 20) {
drawExplosion();
display.setCursor(80, 21);
display.println(“20”);
}
else if(roll == 1) {
display.setCursor(24, 21);
display.println(“1”);
drawSkull();
}
else if (roll 《 10) {
// single character number
display.setCursor(57, 21);
display.println(roll); // write the roll
drawDie(); // draw the outline
}
else {
// dual character number
display.setCursor(47, 21);
display.println(roll); // write the roll
drawDie(); // draw the outline
}
这些新滚动的外观如下:
这就是代码方面的全部内容(如果您跳过了所有代码,请从GitHub获取代码)。您可以轻松地将其修改为D12,D8等。
最终装配
现在,其他所有操作都已完成,现在该将所有内容整理好了。将显示屏固定在螺栓上,确保不要过度拧紧螺栓。这可能是最困难的部分。我这样做是为了使显示器破裂,所以您不妨使用一些塑料垫圈。我从Plasticard中切出了一些方块:
小的螺母和螺栓可能很难连接。 提示:在螺丝起子的末端使用一小块Blu-Tack首先固定螺母:
拧紧按下按钮,连接电池并合上盖子。注意不要夹住任何电线,或将它们捆得太紧,以免造成短路。根据尾线的长度,您可能需要使用某种绝缘保护裸露的连接(串行盒效果很好):
看起来像里面:
这是成品:
您现在应该是电子D20的骄傲拥有者!
责任编辑:wv
全部0条评论
快来发表一下你的评论吧 !