大数跨境
0
0

【小喵科技】插件开发指南-02:教程持续更新,活动福利继续

【小喵科技】插件开发指南-02:教程持续更新,活动福利继续 KittenBot小喵
2019-09-04
0
导读:查看手淘首页,了解造物福利详情;Robotbit套件新品即将上线!

喵家软件教程进阶系列,抓紧时间学习哦!


查看手淘首页,了解造物福利详情

KittenBot小喵科技】https://m.tb.cn/h.eP0YrQ7?sm=c5b490 点击链接,再选择浏览器咑閞;或椱ァ製这段描述¥craAYleUaTg¥后到◇綯℡寳



新的学习套件即将上线




点击查看详情


本章学习:
  • Kittenblock插件开发指南02




准备工作

为了和硬件通信我们首先需要准备一个硬件主板,目前kittenblock支持串口,tcp和udp还有蓝牙的硬件通信方式。只要您的硬件设备符合这三种其一就可以了。

其实要保证你的主板上已经烧录了通信用的固件,具体通信用的固件需要您执行开发,如果实在摸不着头脑可以参考一下kittenbot和minilfr的通信固件,源代码在kittenblock安装目录下的arduino\libraries\中kittenbot和minilfr中可以找到。我们推荐使用基于字符串的通信方式,这样可以节省大量的调试时间并且大部分CNC设备都是基于这种通信方式的。

插件改造

我们继续以上一节做的样板插件为基础,为了让插件可以选择硬件通信接口,我们首先需要在getInfo函数中使能插件状态按钮:

showStatusButton: true,

并且在类的构造函数中加上:

this.comm = runtime.ioDevices.comm;this.session = null;

其中comm是kittenblock的通信io实体,session是通信上下文,具体在打开端口后进行实例化。

另外我们需要在类中添加以下函数,这些函数是scratch3的虚拟机引擎所要求的的回调函数,如果没有这些函数实现会导致通信流程失败。

// method required by vm runtimestartDeviceScan (){
   this.comm.getDeviceList().then(result => {
       this.runtime.emit(this.runtime.constructor.PERIPHERAL_LIST_UPDATE, result);
   });}connectDevice (id){
   this.comm.connect(id).then(sess => {
       this.session = sess;
       this.session.onmessage = this.onmessage;
       this.session.onclose = this.onclose;
       // notify gui connected
       this.runtime.emit(this.runtime.constructor.PERIPHERAL_CONNECTED);
   }).catch(err => {
       log.warn('connect peripheral fail', err);
   });}disconnectSession (){
   this.session.close();}getPeripheralIsConnected (){
   return Boolean(this.session);}

我们来看看这时候的完整代码如下,重新加载插件后可以看到插件列表上方有个小的状态图标

const ArgumentType = Scratch.ArgumentType;const BlockType = Scratch.BlockType;const formatMessage = Scratch.formatMessage;const log = Scratch.log;class TestExt {
   constructor (runtime){
       this.runtime = runtime;
       this.comm = runtime.ioDevices.comm;
       this.session = null;
       
       this.runtime.registerExtensionDevice('TestExt', this);
   }
   
   // method required by vm runtime
   startDeviceScan (){
       this.comm.getDeviceList().then(result => {
           this.runtime.emit(this.runtime.constructor.PERIPHERAL_LIST_UPDATE, result);
       });
   }

   connectDevice (id){
       this.comm.connect(id).then(sess => {
           this.session = sess;
           this.session.onmessage = this.onmessage;
           this.session.onclose = this.onclose;
           // notify gui connected
           this.runtime.emit(this.runtime.constructor.PERIPHERAL_CONNECTED);
       }).catch(err => {
           log.warn('connect peripheral fail', err);
       });
   }

   disconnectSession (){
       this.session.close();
   }

   getPeripheralIsConnected (){
       return Boolean(this.session);
   }

   
   getInfo (){
       return {
           id: 'TestExt',
           name: 'TestExt',
           color1: '#DE5277',
           color2: '#AA3F5B',
           color3: '#AA3F5B',
           showStatusButton: true,

           blocks: [
               {
                   opcode: 'test1',
                   blockType: BlockType.COMMAND,
                   text: 'ABCDEF [VALUE]',
                   arguments: {
                       VALUE: {
                           type: ArgumentType.NUMBER,
                           defaultValue: 123
                       }
                   }
               },
               {
                   opcode: 'test2',
                   blockType: BlockType.REPORTER,

                   text: 'Random Value [MAX]',
                   arguments: {
                       MAX: {
                           type: ArgumentType.NUMBER,
                           defaultValue: 100
                       }
                   }
               },
           ]
       };
   }
   
   test1 (args){
       console.log('test1', args.VALUE);
   }
   
   test2 (args){
       return Math.random()
   }
   
   }module.exports = TestExt;

点击这个按钮将会打开通信选择的面板,不同硬件通信方式在图标上有不同的定义。

之后只要选择其中一个通信接口连接上去就行了。

当用户选择打开通信端口,kittenblock底层会自动调用插件的connectDevice方法,打开成功后会将通信实例(session)返回给插件。

插件需要对该session赋予onmessage和onclose两个方法,这两个方法针对每个插件都有可能不同。下面只是一个例子:

onmessage (data){
   const dataStr = new TextDecoder().decode(data);
   this.lineBuffer += dataStr;
   if (this.lineBuffer.indexOf('\n') !== -1){
       const lines = this.lineBuffer.split('\n');
       this.lineBuffer = lines.pop();
       for (const l of lines){
           if (this.reporter) this.reporter(l);
       }
   }}onclose (error){
   log.warn('on close', error);
   this.session = null;
   this.runtime.emit(this.runtime.constructor.PERIPHERAL_ERROR);}

当然我们还需要在构造函数中加上,保证这些异步函数具有同样的上下文

this.onmessage = this.onmessage.bind(this);this.onclose = this.onclose.bind(this);



测试用的通信固件

NEWS


为了让大家快速上手,这里我写了一个超级简单的arduino通信固件程序,当收到AXXX的时候会让4号脚上的舵机转动到对应的角度。当收到字母B的时候会返回A0口上的模拟值.

大家可以新建一个arduino项目并将下面代码烧录到主板上。

#include <Servo.h>Servo servo;void setup() {
Serial.begin(115200);
servo.attach(4);}char buf[32];int index;void loop() {
while(Serial.available()){
char c = Serial.read();
buf[index++] = c;
if (c == '\n'){
buf[index] = '\0';
if (buf[0] == 'A'){
int degree = atoi(buf+1);
servo.write(degree);
} else if (buf[0] == 'B'){
Serial.println(analogRead(A0));
}
index = 0;
}
}}

使用硬件通信接口

这里我们以电脑上的串口为例子讲解下如何与硬件进行通信。当然前提是你的硬件上有一套通信用的固件。

我们为插件的类再加上两个新方法:

write (data){
if (!data.endsWith('\n')) data += '\n';
if (this.session) this.session.write(data);
}

report (data){
return new Promise(resolve => {
this.write(data);
this.reporter = resolve;
});
}

之后将之前的COMMAND类型模块改造一下:

test1 (args){
this.write(`A${args.VALUE}\n`);
}

当执行这个方块的时候会在串口发送AXXX字符串,其中XXX就是方块中的变量值。

同理REPORTER模块也是这样的操作,不过我们需要让其返回一个promise并等待硬件的返回值。

test2 (args){
return this.report(`B\n`).then(ret => (ret));
}






PS:注意重新加载插件需要手动重新连接串口,目前kittenblock不知道插件重新加载是否应该端口通信接口。

PS2:如果你的插件有bug或者缺少回调函数导致通信底层进入不可知的专题,这时候需要重新启动kittenblock,后面我们会优化各种异常流程和bug捕捉。

PS3:请保证发给其他人用的插件都是调试ok的代码。

这节完整的代码

const ArgumentType = Scratch.ArgumentType;
const BlockType = Scratch.BlockType;
const formatMessage = Scratch.formatMessage;
const log = Scratch.log;

class TestExt {
constructor (runtime){
this.runtime = runtime;
this.comm = runtime.ioDevices.comm;
this.session = null;

this.runtime.registerExtensionDevice('TestExt', this);

// session callbacks
this.onmessage = this.onmessage.bind(this);
this.onclose = this.onclose.bind(this);
}

// method required by vm runtime
startDeviceScan (){
this.comm.getDeviceList().then(result => {
this.runtime.emit(this.runtime.constructor.PERIPHERAL_LIST_UPDATE, result);
});
}

connectDevice (id){
this.comm.connect(id).then(sess => {
this.session = sess;
this.session.onmessage = this.onmessage;
this.session.onclose = this.onclose;
// notify gui connected
this.runtime.emit(this.runtime.constructor.PERIPHERAL_CONNECTED);
}).catch(err => {
console.warn('connect peripheral fail', err);
});
}

disconnectSession (){
this.session.close();
}

getPeripheralIsConnected (){
return Boolean(this.session);
}

onmessage (data){
const dataStr = new TextDecoder().decode(data);
this.lineBuffer += dataStr;
if (this.lineBuffer.indexOf('\n') !== -1){
const lines = this.lineBuffer.split('\n');
this.lineBuffer = lines.pop();
for (const l of lines){
if (this.reporter) this.reporter(l);
}
}
}

onclose (error){
console.warn('on close', error);
this.session = null;
this.runtime.emit(this.runtime.constructor.PERIPHERAL_ERROR);
}

write (data){
console.log("write", data);
if (this.session) this.session.write(data);
}

report (data){
return new Promise(resolve => {
this.write(data);
this.reporter = resolve;
});
}


getInfo (){
return {
id: 'TestExt',
name: 'TestExt',
color1: '#DE5277',
color2: '#AA3F5B',
color3: '#AA3F5B',
showStatusButton: true,

blocks: [
{
opcode: 'test1',
blockType: BlockType.COMMAND,
text: 'ABCDEF [VALUE]',
arguments: {
VALUE: {
type: ArgumentType.NUMBER,
defaultValue: 123
}
}
},
{
opcode: 'test2',
blockType: BlockType.REPORTER,

text: 'Random Value [MAX]',
arguments: {
MAX: {
type: ArgumentType.NUMBER,
defaultValue: 100
}
}
},
]
};
}

test1 (args){
this.write(`A${args.VALUE}\n`);
}

test2 (args){
return this.report(`B\n`).then(ret => (ret));
}


}

module.exports = TestExt;

下一节我们将讲述如何将方块翻译成c++、python等等不同的代码并编译下载。

总结

和小喵一起学习吧!

1.QQ群

 444193538

2.喵家论坛

https://bbs.kittenbot.cn/forum.php

3.我们的Kittenblock软件http://learn.kittenbot.cn/zh_CN/latest/kittenblock/index.html

4.小喵科技店铺

https://kittenbot.taobao.com/shop/view_shop.htm?tracelog=twddp&user_number_id=2830157417

5.哔哩哔哩课程系列

https://space.bilibili.com/25299911




精彩教程作品

人工智能教程系列

系列一:中小学人工智能应该怎么学习 

系列二:文字朗读

系列三:文字翻译

系列四:语言识别

系列五:超级翻译官

系列六:专家系统

系列七:视觉识别01初探

系列八:视觉识别02人脸追踪

系列九:视觉识别03人脸检测

系列十:视觉识别04特定人脸辨别

系列十一:视觉识别05识别车牌

系列十二:视觉识别06识别印刷文字


机器学习教程系列

系列一:Tensorflow简介

系列二:TensorFlow快速开始(程序包集合)

系列三:鸢尾花(Iris)分类

系列四:MNIST 手写数字识别

系列五六:MNIST模型保存加载和识别

系列七:MobileNet模型任意物体识别

系列八:MachineLearning5 简介


机器学习5(轻量TensorFlow)教程系列

系列一:MachineLearning5 简介

系列二:图像分类器---看图识物

系列三:图像分类器---识别给定的图片

系列四:图像分类器---识别摄像头窗口物体

系列五:特征提取器入门

系列六:特征提取器---剪刀石头布为例

系列七:特征提取器---识别不同颜色

系列八:涂鸦KNN应用

系列九:骨架追踪


物联网教程系列

系列一:什么是物联网?

系列二:kittenblock本地IOT服务器--快速上手

系列三:kittenblock本地IOT服务器--温度推送

系列四:喵家外网IOT服务器

系列五:让microbit加入IOT

系列六:让rosbot加入IOT

系列七:中国移动Onenet使用--能量魔块

系列八:Thingspeak中应用--Microbit

物联网系列暂时完结


kittenblock软件教程更新

系列一:kittenblock学习指南教程

系列二:下载与安装

系列三:工具栏详解

系列四:编程积木分栏

系列五:舞台展示区

系列六:书包功能

系列七:列表导入导出功能

系列八:护眼模式切换,在线和离线模式

系列九:连接引擎使用简介

系列十:Thing Speak使用介绍

系列十一:自动化插件生成系统

系列十二:软件中界面翻译和插件翻译

系列十三:和风天气插件开发指南



每周二、四、六






【声明】内容源于网络
0
0
KittenBot小喵
专注于从入门到精通的移动机器人开源学习平台
内容 560
粉丝 0
KittenBot小喵 专注于从入门到精通的移动机器人开源学习平台
总阅读381
粉丝0
内容560