本文作者: & 铁熊

在这个共享经济时代,万物皆可共享,这其中包括了共享单车、共享充电宝、共享雨伞等服务。所有这些共享经济产品的背后,都用到了扫码支付相关的技术。

作为个人 DIY 玩家,有没有办法在自己的作品上增加扫码支付相关的功能呢?比如通过扫码支付控制设备实现不同的功能呢。想法虽然美好,但现实却是残酷的,因为大多数支付接口并不针对个人开放,个人想要实现扫码支付效果,一般只能通过第三方服务实现。本教程中,我将介绍一种第三方服务:奇迹码支付(http://pay.qj61.cn/login),它是一个独立开发者个人即时到账收款平台,可以通过监测手机通知从而验证支付状态,验证支付成功之后,我们就可以 DIY 各种扫码付费项目啦。

本教程中,我给大家制作了一个掌上 POS 机为大家演示扫码收款功能,效果如下:

Arduino实现全网最迷你收米工具

在这个项目中,我们实现了类似 POS 机的效果,不仅可以设置收款金额,而且还可以选择收款方式,比如支付宝、微信、QQ等,用户扫码后就可以实现支付。

预期目标及功能

  • 触摸键盘功能;
  • 支付图标显示;
  • 支付方式选择;
  • 二维码生成;
  • 网络状态反馈;
  • 触摸震动反馈;

所用硬件

  • M5Core2 模块

    M5Core2 具有如下特点:

  • 基于 ESP32 开发,支持 WiFi,蓝牙;

  • 16M 闪存,8M PSRAM;

  • 内置扬声器,电源指示灯,震动马达,RTC,I2S 功放,电容式触摸屏,电源键,复位键;

  • TF 卡插槽(支持最大 16GB);

  • 内置锂电池,配备电源管理芯片;

  • 独立小板内置 6 轴 IMU,PDM 麦克风;

  • M-Bus 总线插座。

程序设计

下面开始详细讲解程序设计过程。

开发环境

我们使用 Arduino IDE 来编写本项目的程序,上传程序时开发板选择 M5Stack-Core2,编程过程中需要用到的软件及库,将会打包作为附件给大家下载,详见文末下载说明。

程序思路

为了实现项目的所有功能,我们先根据预期的目标绘制思维导图,再根据思维导图逐步实现自制 POS 结算终端机的功能。

下面我们将具体讨论自制结算终端的各个子功能是如何实现的。

触摸按键测试程序

我们想要使用触摸屏实现金额的输入以及支付方式的选择,离不开设计触摸按键。M5Core2 为我们提供了成熟的解决方案,我们能够轻易地绘制一个按键,并且设置指定的区域,触摸按键的使用示例如下:

#include <M5Core2.h>ButtonColors on_clrs = {YELLOW, WHITE, WHITE};
ButtonColors off_clrs = {BLACK, WHITE, WHITE};
Button tl(0, 0, 0, 0, false , "Button", off_clrs, on_clrs, MC_DATUM);void setup() {M5.begin();M5.Buttons.addHandler(eventDisplay, E_ALL - E_MOVE);doButtons();
}void loop() {M5.update();
}void doButtons() {uint8_t but_w = 100;uint8_t but_h = 60;tl.set(110, 90, but_w, but_h); // 设置按键的显示坐标以及长和宽M5.Buttons.draw();
}void eventDisplay(Event& e) {Serial.printf("%-12s finger%d  %-18s (%3d, %3d) --> (%3d, %3d)   ",e.typeName(), e.finger, e.objName(), e.from.x, e.from.y,e.to.x, e.to.y);Serial.printf("( dir %d deg, dist %d, %d ms )\n", e.direction(),e.distance(), e.duration);
}

其中:

  • ButtonColors on_clrs = {YELLOW, WHITE, WHITE} 定义了按键按下时的颜色以及按键框的颜色;

  • ButtonColors off_clrs = {BLACK, WHITE, WHITE} 定义了按键释放时的颜色以及按键框的颜色;

  • Button tl(0, 0, 0, 0, false , "Button", off_clrs, on_clrs, MC_DATUM) 定义了按键的显示文本以及文本显示方式;

  • doButtons() 函数绘制了按钮;

  • eventDisplay(Event& e) 函数用来侦测屏幕触摸事件。

如果你想要实现更多个性化设置,请参考 M5Core2.h 库文件进行设置。

触摸按键效果测试

上传上面的测试程序,打开串口监视器,点击 M5Core2 屏幕上的触摸按键可看到下图所示内容:

可以看到:

  • 当点击程序定义的触摸按键时,串口会返回按键的标签字符串 Button 以及按下的持续时间以及坐标区域;

  • 当点击未被程序设置的区域时,返回的字符串是 background;

  • 当点击触摸屏上的另外三个默认触摸按键时,返回 BtnA,BtnB 或 BtnC。

这里我们重点关注串口打印的 e.typeName() (触发类型)和 e.objName() (触发按键名),后面我们将重点利用这两个返回值,可以根据返回值区分我们按下的每一个按键。触发类型我们关注 E_RELEASE 这个返回值,该字符串代表了按键被释放,可以用来检测按键是否点击结束。

主界面 UI 设计

知道了如何利用程序定义一个触摸按钮之后,接下来我们来设计该主界面的 UI。根据前面所学的按钮绘制以及定义方法,绘制出所有数字输入按键、支付按键、清除按键以及确认按键,结合圆角矩形绘制函数 M5.Lcd.drawRoundRect()绘制出主界面,主界面设计如下:

图标显示

显示图标有两种方式,第一种方式是单色图标,第二种方式是彩色图标。单色图标可以直接使用 M5.Lcd.drawXBitmap() 函数绘制,彩色图标则可以使用 M5.Lcd.drawBitmap() 函数进行绘制。本项目中,单色图标有 WiFi 图标以及货币的符号 ¥。

对于单色图标,可以通过取模软件 Image2Lcd 取模。在 Image2Lcd 软件中,选择需要取模的图片,根据自己的屏幕类型,调整取模方式、取模大小、亮度,最后导出取模数据。设置如下:

对于货币符号 ¥ 我们使用 Mixly 软件中的取模工具获取字模,取模设置如下:

对于彩色图片,可以使用 ImageConverter 软件获取彩色图片取模数据,其界面如下,选择需要取模的图片并调整其大小,最后导出为 C 语言取模数据即可:

按键功能以及 UI 设计

现在根据前面的按键 UI 设计示例、以及图像显示函数设计出按键的处理程序,我们先定义一个输入字符串变量 Input_data 代表输入的字符串,当我们按下数字按键以及小数点时对输入的字符进行连接,按下删除按键 DEL 删除 Input_data 的最后一个字符,按下对应的支付方式时显示对应的图标,具体程序如下:

#include <M5Core2.h>extern const unsigned short success_icon[0x125C0];
extern const unsigned short failure_icon[0xE100];
extern const unsigned char currency[0x78];
extern const unsigned short QQ_icon[0x3A2];
extern const unsigned short WX_icon[0x384];
extern const unsigned short ZFB_icon[0x384];
extern const unsigned char wifi[0x78];String Input_data;ButtonColors on_clrs = {YELLOW, WHITE, WHITE};
ButtonColors off_clrs = {BLACK, WHITE, WHITE};
Button tl(0, 0, 0, 0, false , "7", off_clrs, on_clrs, MC_DATUM);
Button t2(0, 0, 0, 0, false, "8", off_clrs, on_clrs, MC_DATUM);
Button t3(0, 0, 0, 0, false, "9", off_clrs, on_clrs, MC_DATUM);
Button t4(0, 0, 0, 0, false, "QQ", off_clrs, on_clrs, MC_DATUM);
Button t5(0, 0, 0, 0, false , "4", off_clrs, on_clrs, MC_DATUM);
Button t6(0, 0, 0, 0, false, "5", off_clrs, on_clrs, MC_DATUM);
Button t7(0, 0, 0, 0, false, "6", off_clrs, on_clrs, MC_DATUM);
Button t8(0, 0, 0, 0, false, "WX", off_clrs, on_clrs, MC_DATUM);
Button t9(0, 0, 0, 0, false , "1", off_clrs, on_clrs, MC_DATUM);
Button t10(0, 0, 0, 0, false, "2", off_clrs, on_clrs, MC_DATUM);
Button t11(0, 0, 0, 0, false, "3", off_clrs, on_clrs, MC_DATUM);
Button t12(0, 0, 0, 0, false, "ZFB", off_clrs, on_clrs, MC_DATUM);
Button t13(0, 0, 0, 0, false , "0", off_clrs, on_clrs, MC_DATUM);
Button t14(0, 0, 0, 0, false, ".", off_clrs, on_clrs, MC_DATUM);
Button t15(0, 0, 0, 0, false, "DEL", off_clrs, on_clrs, MC_DATUM);
Button t16(0, 0, 0, 0, false, "CON", off_clrs, on_clrs, MC_DATUM);void setup() {M5.begin();M5.Lcd.fillScreen(BLACK); // 设置背景颜色M5.Buttons.addHandler(eventDisplay, E_ALL - E_MOVE); // 注册按键动作检测M5.Lcd.drawRoundRect(0, 0, 315, 43, 5, WHITE); // 绘制显示文本框M5.Lcd.drawXBitmap(10, 6, currency, 30, 30, WHITE); // 显示货币图标M5.Lcd.setTextColor(WHITE, BLACK); // 设置显示文本颜色M5.Lcd.setTextSize(2); // 设置显示字体大小M5.Lcd.drawBitmap(262, 6, 30, 30, WX_icon); // 显示默认微信图标doButtons(); // 设置按钮属性及绘制按钮
}void loop() {M5.update();
}void doButtons() {uint8_t but_w = 75;uint8_t but_h = 43;tl.set(0, 48, but_w, but_h);t2.set(80, 48, but_w, but_h);t3.set(160, 48, but_w, but_h);t4.set(240, 48, but_w, but_h);t5.set(0, 96, but_w, but_h);t6.set(80, 96, but_w, but_h);t7.set(160, 96, but_w, but_h);t8.set(240, 96, but_w, but_h);t9.set(0, 144, but_w, but_h);t10.set(80, 144, but_w, but_h);t11.set(160, 144, but_w, but_h);t12.set(240, 144, but_w, but_h);t13.set(0, 192, but_w, but_h);t14.set(80, 192, but_w, but_h);t15.set(160, 192, but_w, but_h);t16.set(240, 192, but_w, but_h);M5.Buttons.draw();
}void eventDisplay(Event& e) {Serial.printf("%-12s finger%d  %-18s (%3d, %3d) --> (%3d, %3d)   ",e.typeName(), e.finger, e.objName(), e.from.x, e.from.y,e.to.x, e.to.y);Serial.printf("( dir %d deg, dist %d, %d ms )\n", e.direction(),e.distance(), e.duration);if (String(e.objName()).equals(String("BtnA"))) {// 执行BtnA事件delay(50);} else {if (String(e.objName()).equals(String("BtnB"))) {// 执行BtnB事件delay(50);} else {if (String(e.objName()).equals(String("BtnC"))) {// 执行BtnC事件delay(50);} else {if (String(e.typeName()).equals(String("E_RELEASE"))) { // 检测是否释放屏幕if (!String(e.objName()).equals(String("background"))) { // 检测是否为已注册按键区域if (String(e.objName()).equals(String("QQ"))) { // 检测是否为按下QQ按键Serial.println("QQ");M5.Lcd.drawBitmap(262, 6, 30, 31, QQ_icon); // 显示QQ图标delay(50);} else {if (String(e.objName()).equals(String("WX"))) { // 检测是否为按下WX按键Serial.println("WX");M5.Lcd.drawBitmap(262, 6, 30, 30, WX_icon); // 显示微信图标delay(50);} else {if (String(e.objName()).equals(String("ZFB"))) { // 检测是否为按下ZFB按键Serial.println("ZFB");M5.Lcd.drawBitmap(262, 6, 30, 30, ZFB_icon); // 显示支付宝图标delay(50);} else {if (String(e.objName()).equals(String("CON"))) { // 检测是否为按下CON确认按键Serial.println("CON");if (!Input_data.equals(String("")) && Input_data.toFloat() > 0) { // 当输入不为空且数字大于0生成订单Serial.println("Amount:" + Input_data);// 生成订单}delay(50);} else {if (String(e.objName()).equals(String("DEL"))) { // 检测是否为按下DEL删除按键,按下删除末尾字符Input_data = String(Input_data).substring(0, (String(Input_data).length() - 1)); // 删除输入字符串末尾字符M5.Lcd.fillRoundRect(40, 0, 180, 43, 5, BLACK); // 清空显示字符串M5.Lcd.drawRoundRect(0, 0, 315, 43, 5, WHITE); // 重新绘制文本显示框M5.Lcd.setCursor(40, 33); // 设置文本显示坐标M5.Lcd.printf(Input_data.c_str()); // 显示输入文本delay(50);} else {if (String(Input_data).length() <= 7) { // 检测到数字按键及小数点Input_data = String(Input_data) + String(e.objName()); // 连接字符串if (Input_data.startsWith(".")) { // 输入首位字符为小数点Input_data = ""; // 清空字符串}M5.Lcd.fillRoundRect(40, 0, 180, 43, 5, BLACK);M5.Lcd.drawRoundRect(0, 0, 315, 43, 5, WHITE);M5.Lcd.setCursor(40, 33);M5.Lcd.printf(Input_data.c_str());delay(50);}}}}}}}}}}}
}

在上面的程序当中,我们还应该考虑输入字符串的特殊情况例如,首位不能是小数点,首位不能连续两个 0 等情况,其他情况请自行分析,此处省略了取模数据,上传该程序效果如下:

WiFi 连接反馈

当没有连接网络时,网络应当自动连接并且反馈当前的连接状态,实现代码如下:

if (!(WiFi.status() != WL_CONNECTED)) { // 检测网络连接状态M5.Lcd.drawXBitmap(220, 6, wifi, 30, 30, GREEN); // 显示绿色wifi图标
} else {M5.Lcd.drawXBitmap(220, 6, wifi, 30, 30, RED); // 显示红色wifi图标WiFi.disconnect(); // 断开连接WiFi.mode(WIFI_STA); // 设置为STA模式WiFi.begin(STASSID, STAPSW); // 设置wifi并连接delay(1000); // 等待网络连接
}

使用此方式连接网络比直接初始化连接网络更好,其主函数不会受网络状况的影响且能够自动连接网络,在以后使用 ESP 系列连接网络都建议使用此种方式。加上连网反馈后效果如下:

发起支付请求

完成上面的程序后,当我们输入金额并确认支付方式时,按下 CON 确认键会发出订单请求,生成订单号并显示支付二维码。

这里我们用到了第三方服务奇迹码支付(http://pay.qj61.cn/login),进入官网,按提示注册以及上传自己的收款二维码,并下载安装其软件监听通知,按提示进行设置,并保证监听软件与收款账号为同一个手机。微信可以开启店员功能,则无需保证同一手机,监听软件必须保证不被后台清除。完成上面步骤后,再来简单了解下其 API 的构成,我们重点查看其订单生成与支付状态验证。

订单生成

通过对文档分析可以得到订单生成的 API 为:

http://pay.qj61.cn/createOrder?mid=&payId=&type=&price=&sign=&param=&isHtml=0

其中 sign 为订单信息与密钥的 MD5 加密信息,MD5 是一种加密技术,可以通过百度获取其加密原理,在这里我们可以通过 MD5 在线加密获取这个加密信息,替换参数就能生成这个订单。上面的例子中给我们演示了 MD5 加密和加密后的情况,在这里我们通过 MD5 加密库文件对原数据进行加密,加密程序如下:

#include <MD5_String.h>void setup()
{Serial.begin(9600);Serial.println(md5_string("1547129707139vone66620.1a7cc8678193ee9c70ae3d75fd04ae6a9"));
}void loop()
{}

上传程序后,我们便能够得到加密后的结果与给定的示例一致,值得注意的是 md5_string() 函数接受的是字符数组,不能通过字符串变量的形式传入字符串,加密的字符串必须先转换为字符数组,其转换的方法将在附件的完整程序进行说明。

订单返回数据

等我们成功生成订单并返回数据后,我们需要对返回的 JSON 数据进行分析,从而得到我们想要的数据。在反馈的数据当中,我们希望获取的是云端订单号,我们将通过查询该订单号来获取是否支付成功,在这里我们通过 ArduinoJson 库对返回的数据进行分析,我们可以通过 ArduinoJson 助手(https://arduinojson.org/v6/assistant/)在线反序列 JSON 数据得到想要的结果。此处获得解析结果的代码如下:

// std::string input;StaticJsonDocument<512> doc;DeserializationError error = deserializeJson(doc, input);if (error) {Serial.print(F("deserializeJson() failed: "));Serial.println(error.f_str());return;
}int code = doc["code"]; // 1
const char* msg = doc["msg"]; // "成功"JsonObject data = doc["data"];
const char* data_payId = data["payId"]; // "4037819497"
const char* data_orderId = data["orderId"]; // "202104210204575941"
int data_payType = data["payType"]; // 1
const char* data_price = data["price"]; // "0.10"
float data_reallyPrice = data["reallyPrice"]; // 0.1
const char* data_payUrl = data["payUrl"]; // "wxp://f2f0FaA-i_PQr6TJHgnqKVy8ICbYQQB4Zpj6"
int data_isAuto = data["isAuto"]; // 1
int data_state = data["state"]; // 2
const char* data_timeOut = data["timeOut"]; // "5"
long data_date = data["date"]; // 1618943457

我们对以上结果进行分析,删除无关数据后处理结果如下:

StaticJsonDocument<512> doc;
DeserializationError error = deserializeJson(doc, input);
if (error) {Serial.print(F("deserializeJson() failed: "));Serial.println(error.f_str());return;
}
JsonObject data = doc["data"];
const char* data_orderId = data["orderId"]; // "202104210204575941"

订单状态验证

通过对文档的分析可得到查询订单状态的 API 为:

http://pay.qj61.cn/getOrder?orderId=<order_id>

同理我们用 ArduinoJson 助手解析返回的数据,可以得到当返回的订单状态 state,当 state 大于 0 那么就认为支付成功。

发送 API 请求

ESP32 发送 get 请求方法如下:

#include <WiFi.h>
#include <HTTPClient.h>void setup() {Serial.begin(9600);WiFi.begin("ssid", "password");while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");}Serial.println("Local IP:");Serial.print(WiFi.localIP());
}void loop() {if (WiFi.status() == WL_CONNECTED) {HTTPClient http;http.begin("http://jsonplaceholder.typicode.com/posts/1");int httpCode = http.GET();if (httpCode > 0) { //请求成功String Request_result = http.getString();Serial.println(Request_result); //打印获取到的数据}else {Serial.println("Invalid response!"); //请求失败,打印提示}http.end();}delay(5000);
}

显示二维码

通过对上面文档的分析和 ESP32 发送 get 请求的案例,我们已经能够生成订单并且获取订单支付结果了。当订单生成后,我们需要将订单转换成二维码。

对于 M5Core2,我们可以通过内置二维码显示函数M5.Lcd.qrcode()来生成收款二维码,只需要输入想要显示的字符串就可以全屏显示二维码了。此处我们使用了一个小技巧,因为云端已经上传过收款码,生成订单后,返回 URL 为我们的收款码图片地址,由于我们的单片机性能有限,并不能够直接显示网络图片,故此处我们先将收款码通过草料二维码平台(https://cli.im/deqr)在线解析出二维码数据,最后通过二维码显示函数直接显示这个收款码数据字符串,便实现了间接显示收款码图片了。

另外,我们还应当根据选择的支付类型显示不同的收款二维码。如果您使用的是其他开发板,不能够使用现成的函数显示二维码,则可以通过附录文件的 qrcode 库文件(https://github.com/ricmoo/QRCode),经过简单的修改,即可将显示二维码功能快速移植到你自己的显示屏中。

订单验证

显示完二维码以后,我们给定一个超时时间,例如 60 秒:

  • 若 60 秒内完成付款,应当显示付款成功的提示图标,并停留一段时间后返回主界面;
  • 若超时限定时间,则显示付款失败提示图标,并停留一段时间后返回主界面。

此处我们可以用一个 for 循环来验证订单状态,为了确保能够随时取消订单,for 循环应当调用 M5.update()函数用来更新触摸屏的检测状态,我们可以令任意 ABC 3 个按钮被按下时主动跳出订单验证的 for 循环,当二维码显示的时候,我们触摸屏幕会发现原来定义的触摸按键还在生效,按下屏幕会显示按键,显示完二维码后应当禁用屏幕的触摸功能,最直接的方法就是重新设置所有按键的属性,我们把按键显示的起点坐标与按钮长宽均设置为 0,如此所有按钮将失效,当我们付款成功或者是超时的时候重新设置回原来的按钮属性,并口重新启用按键功能,以上方法将在附录的完整程序中演示。

震动反馈

M5Core2 内置了一个振动马达,在这里我们按下定义的按键以及显示二维码时,可以让振动马达发出震动,增强作品的体感效果,控制振动马达的主要程序如下:

M5.Axp.SetLDOEnable(3, true); // 开启震动
M5.Axp.SetLDOEnable(3, false); // 关闭震动

语音提示

为了增加作品的趣味性,我们可以在收款成功后播放语音提示,ESP32 的 i2s 能够让我们直接播放简短音频,可以利用 M5Core2 的扬声器播放零钱到账的声音,程序如下:

#include <M5Core2.h>
#include <driver/i2s.h>
#include "play_WAV.h"extern const unsigned char previewR[90882];void setup() {M5.begin(true, true, true, true);M5.Axp.SetSpkEnable(true);InitI2SSpeakOrMic(1);
}void loop() {size_t bytes_written = 0;i2s_write(I2S_NUM_0, previewR, 90882, &bytes_written, portMAX_DELAY);delay(1000);
}

在上面的程序当中,M5Core2 将每隔一秒播放一次音频,这里我们省略了音频数据,获取音频数据方法如下,先准备一段 WAV 的简短音频,通过软件 HxDSetup 打开该音频,并按下图所示导出:

该程序及软件的完整例子请查看附录文件。

代码组合

最后,按照上述功能之间的逻辑关系,将代码组合在一起即可。由于篇幅限制,这里就不放完整的代码了,文中的所有案例以及完整程序大家可以通过下载附件进行查看。

小结

本项目中,我们以近乎零成本的方式实现了二维码支付的问题,你甚至可以仅通过 1 块 ESP8266 开发板而不需要屏幕来完成本项目。支付方式取决于你使用的第三方服务,在这里仅支持了主流的微信支付与支付宝,在最新一代的第三方服务当中你甚至可以不需要监听软件就可以实现本项目,但他们可能需要少许的费用。

本项目以体验为主,让大家以最低成本去实现属于你自己的共享经济项目。以本项目为基础可以扩展很多共享经济作品,比如自动贩卖机,或者你也可以制作一个笑话售卖机,一分钱看一则笑话。你的脑洞决定了你的作品,让我们一起脑洞大开吧!

DIY掌上POS机,或许是最小的收银POS机了!相关推荐

  1. 超便利!教你用ESP32开发板DIY掌上网页服务器!

    本文作者:默.默是铁熊的创客好友,经常与铁熊分享创意项目. 前段时间有个老师对我说:每到开学季,学校就要印刷学生的录取名单并进行张贴,为此学校每年都要耗费大量的人力物力.学校里面教学活动很多,传统的通 ...

  2. 菜鸟学开店—最简收银POS系统

    朋友开了家小服装店,场地不大,二十多平,想搞正规化,想整个收银系统,方便管理库存.让我给个最便宜,最不占地,最简单解决方案. 这个要求怎么可能把我难住,仔细琢磨了下,帮他整了个200块不到的解决方案, ...

  3. 【DIY】手把手教你爆改一台手机制作掌上游戏机

    开头声明: 此篇文章为本人原创,首发于酷安,转载请标明出处 这个游戏机的创意并非本人原创,创意来自B站UP主极客湾,在B站搜索"自制掌机"就可以看见相关视频视频转送门 这是一篇混合 ...

  4. 铁道部掌上12306手机客户端预计11月底推出-铁道部-掌上12306-手机客户端

    铁道部掌上12306手机客户端预计11月底推出|铁道部|掌上12306|手机客户端 中广网北京10月17日消息 据经济之声<天下财经>报道,铁路部门"掌上12306智能手机客户端 ...

  5. 如果有一天,掌上游戏机的屏幕可以卷起来……

    随着智能手机以及手机游戏的发展,已经把当年像PSP.PSV这样火爆一时的掌上游戏机彻底钉在了历史的长河底下,现在还剩下的掌机就都是那种把当年的小霸王游戏或者仙剑1代这种像素游戏装进去的掌上红白机,怀旧 ...

  6. 云端计算机可以玩游戏么,手机掌上云电脑是什么?为什么可以玩PC游戏?

    原标题:手机掌上云电脑是什么?为什么可以玩PC游戏? 经常会在一些短视频平台上看到别人用云电脑的应用在手机上玩PC游戏,那么这个掌上云电脑的应用到底是什么呢?为什么可以玩PC游戏呢? 按照以往的理解, ...

  7. “掌上运维” – 下一代网管的思考

    近年来,得益于手机等移动终端的性能的飞速提升,谷歌.苹果等这些IT巨头不遗余力的持续推动,基于手机的各种应用如雨后春笋般冒出头来.对许多人来说,手机已成为工作.生活.娱乐必不可少的组成部分,甚至超越了 ...

  8. 【ATF】庄卓然(南天):掌上精彩-连接过去与未来

    摘要 2016 ATF阿里技术论坛中,阿里巴巴资深总监,淘宝移动平台负责人 庄卓然(花名:南天)深入分享了淘宝移动技术平台及其在阿里无线的实践与创新经验.除此之外,也展望了移动技术未来的趋势,视频直播 ...

  9. 苹果推iOS游戏手柄,掌上游戏主机格局变天?

    发表于12小时前| 4893次阅读| 来源CSDN| 43 条评论| 作者唐小引 AppleWWDC移动游戏罗技任天堂iOSNvidiaAndroidMFi 摘要:在WWDC大会上,惊喜接踵而来,苹果 ...

最新文章

  1. OPPO Find X3通过网站推广正式官宣,打破常规探索高端旗舰新突破!
  2. Xcode 7中Static Cells自动计算高度失效的解决方法
  3. 计算机联锁仿真软件设计,一种基于LabVIEW的计算机联锁仿真系统的制作方法
  4. 笔记-项目质量管理-编制质量管理计划的工具与技术
  5. mysql snowflake_雪花算法-snowflake
  6. 码农心目中的高富帅甲骨文公司在走下坡路
  7. [转]ASP.NET MVC 入门3、Routing
  8. datatable 操作列根据权限动态展现_不会Excel透视表?教你一招轻松做出动态报表...
  9. 一名董事长给大学生的18条忠告(全)
  10. CentOS下常用配置文件和命令以及目录结构备注
  11. 如何将 EDI系统作为服务器开机自启动?
  12. ApacheCN 活动汇总 2019.7.19
  13. Linux系统扩容硬盘
  14. Envoy 架构、术语与基本配置解析
  15. 大厂调整考勤,996将成为过去式吗?
  16. 英语写作模板(适合高考、四六级英语和考研英语)
  17. 让dropout在图像超分辨领域大放异彩![2022 CVPR]
  18. 实现Windows下Qt扫描U盘的两种方式
  19. 【计算机网络:自顶向下方法】(一)计算机网络和英特网
  20. 川普的退休生活?不,是AI算法的宅舞演绎。

热门文章

  1. 解决Microsoft Store应用商店或UWP应用连不了网
  2. 【BZOJ4819】【SDOI2017】新生舞会(01分数规划,带权二分图匹配)
  3. java sha256 解密_如何解密SHA-256加密字符串?
  4. [附源码]计算机毕业设计葡萄酒销售管理系统论文Springboot程序
  5. Python3 中文与url格式编码的转换
  6. erp管理系统对服务器的要求,erp系统管理员职责跟要求.docx
  7. 普及环保知识应用—蜂窝分子筛
  8. Appium-System Bars(系统状态栏)
  9. 高速PCB设计指南(十四)
  10. 微软承认最新Win10更新会致电脑蓝屏死机