再来就是实际建立透过 select 选择的脚位,并建立相关 Firmata 功能。
规划一下预期 UI 内容。
电子助教:「规划个毛,这明明就是类比输入控制组件换个字而已」
鳕鱼:「被发现惹 (´,,•ω•,,)」
建立 window-pwm-output-item.vue
组件,用来控制 PWM 输出。
具体实现功能:
旋钮
使用 Quasar Knob。
拉条
使用 Quasar Slider。
删除按钮(脚位编号)
使用 Quasar Button。
程序的部份为:
window-digital-io-item.vue
pin
之 resolution
计算 PWM 输出最大数值src\components\window-pwm-output-item.vue <template lang="pug">
.c-row.q-0px.items-center.w-full
.pin-number
.text-20px
| {{ pin.number }}
q-btn.bg-white(
@click='handleDelete',
icon='r_delete',
dense,
flat,
rounded,
color='grey-5'
)
q-slider.mx-20px(
v-model='pinValue',
color='light-green-4',
:min='0',
:max='valueMax'
)
q-knob(
v-model='pinValue',
size='60px',
show-value,
readonly,
color='light-green-4',
track-color='grey-3',
font-size='12px',
:min='0',
:max='valueMax'
)
src\components\window-pwm-output-item.vue <style scoped lang="sass">
@import '@/styles/quasar.variables.sass'
.pin-number
width: 36px
padding: 10px 0px
margin-right: 10px
font-family: 'Orbitron'
color: $grey
text-align: center
position: relative
&:hover
.q-btn
pointer-events: auto
opacity: 1
.q-btn
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
pointer-events: none
transition-duration: 0.4s
opacity: 0
src\components\window-pwm-output-item.vue <script>
/**
* @typedef {import('@/script/modules/port-transceiver').default} PortTransceiver
*
* @typedef {import('@/types/type').PinInfo} PinInfo
* @typedef {import('@/types/type').PinCapability} PinCapability
*/
import { mapState } from 'vuex';
import { PinMode } from '@/script/utils/firmata.utils';
const { PWM } = PinMode;
export default {
name: 'WindowPwmOutputItem',
components: {},
props: {
/** @type {PinInfo} */
pin: {
type: Object,
required: true,
},
},
data() {
return {
/** 脚位数值 */
pinValue: 0,
};
},
computed: {
...mapState({
/** @type {PortTransceiver} */
portTransceiver: (state) => state.core.transceiver,
}),
/** 数值最大值 */
valueMax() {
/** @type {PinInfo} */
const pin = this.pin;
const target = pin.capabilities.find(
(capability) => capability.mode === PWM
);
return 2 ** target.resolution - 1;
},
},
watch: {},
created() {
},
mounted() {},
beforeDestroy() {},
methods: {
handleDelete() {
this.$emit('delete', this.pin);
},
},
};
接着在 window-pwm-output.vue
引入 window-pwm-output-item.vue
src\components\window-pwm-output.vue <script>
// ...
import BaseWindow from '@/components/base-window.vue';
import BaseSelectPin from '@/components/base-select-pin.vue';
import WindowPwmOutputItem from '@/components/window-pwm-output-item.vue';
// ...
export default {
name: 'WindowPwmOutput',
components: {
'base-window': BaseWindow,
'base-select-pin': BaseSelectPin,
'window-pwm-output-item': WindowPwmOutputItem,
},
// ...
};
src\components\window-pwm-output.vue <template lang="pug">
base-window.window-pwm-output(
:pos='pos',
header-icon-color='light-green-4',
body-class='c-col p-20px pt-20px',
title='PWM 输出功能'
)
base-select-pin(
:pins='supportPins',
color='light-green-4',
@selected='addPin',
@err='handleErr'
)
q-scroll-area.pt-10px.h-300px.flex
transition-group(name='list-complete', tag='div')
window-pwm-output-item.py-10px(
v-for='pin in existPins',
:pin='pin',
:key='pin.number',
@delete='deletePin'
)
建立脚位时间!
接下来就是实作类比输入功能了!
在控制脚位数值之前,一样先设定脚位模式。
在 window-pwm-output-item.vue
新增以下程序:
methods
新增 init()
,初始化脚位相关功能。created()
呼叫 init()
src\components\window-pwm-output-item.vue <script>
/**
* @typedef {import('@/script/modules/port-transceiver').default} PortTransceiver
*
* @typedef {import('@/types/type').PinInfo} PinInfo
* @typedef {import('@/types/type').PinCapability} PinCapability
*/
import { mapState } from 'vuex';
import { PinMode } from '@/script/utils/firmata.utils';
const { PWM } = PinMode;
export default {
name: 'WindowPwmOutputItem',
// ...
created() {
this.init();
},
// ...
methods: {
// ...
init() {
/** @type {PinInfo} */
const pin = this.pin;
/** @type {PortTransceiver} */
const portTransceiver= this.portTransceiver;
portTransceiver.addCmd('setMode', {
pin: pin.number,
mode: PWM,
});
},
},
};
到设定 PWM 数值的命令与 Analog 14-bit data format
格式相同:
0 analog pin, 0xE0-0xEF, (MIDI Pitch Wheel)
1 analog least significant 7 bits
2 analog most significant 7 bits
Uno 之 PWM 最大数值为 255,根据协定需将 255 以 7 bit 为一组拆分成 2 byte 传输。
忘记为甚麽的朋友们可以回去复习「D04 - 从零开始的 Firmata 通讯」)
在 utils.js
建立一个专门拆分数值的功能 numberToSignificantBytes()
。
src\script\utils\utils.js
// ...
/** 将数值转为 Bytes
* @param {number} number 数值
* @param {number} [length] bytes 数量
* @param {number} [bitsNum] 每 byte 有效位元数
*/
export function numberToSignificantBytes(number, length = 2, bitsNum = 7) {
const bytes = [];
const mesh = 2 ** bitsNum - 1;
let remainingValue = number;
for (let i = 0; i < length; i++) {
const byte = remainingValue & mesh;
bytes.push(byte);
remainingValue = remainingValue >> bitsNum;
}
return bytes;
}
cmd-define.js
新增设定 PWM 数值命令。
src\script\firmata\cmd-define.js
// ...
export default [
// ...
// setPwmPinValue: 设定 PWM 数值
{
key: 'setPwmValue',
getValue({ pin, value }) {
const cmd = 0xE0 + pin;
const [byte01, byte02] = numberToSignificantBytes(value);
return [cmd, byte01, byte02];
},
},
]
最後实作发送命令部分:
pinValue
变化并发送数值。methods
新增 writeValue()
发送数值。src\components\window-pwm-output-item.vue <script>
// ...
export default {
name: 'WindowPwmOutputItem',
// ...
watch: {
pinValue(value) {
this.writeValue(value);
},
},
// ...
methods: {
// ...
/** 发送数值 */
writeValue(value) {
/** @type {PinInfo} */
const pin = this.pin;
/** @type {PortTransceiver} */
const portTransceiver = this.portTransceiver;
portTransceiver.addCmd('setPwmValue', {
pin: pin.number,
value,
});
},
},
};
怎麽好像有点延迟呢?因为 watch
事件触发太频繁,导致命令发送有点塞车。
聪明的读者们一定想到怎麽解决了吧。没错,就是老朋友 throttle
。
watch
的部份改成呼叫 throttle
。
src\components\window-pwm-output-item.vue <script>
// ...
import { throttle } from 'lodash-es';
// ...
export default {
name: 'WindowPwmOutputItem',
// ...
data() {
return {
/** 脚位数值 */
pinValue: 0,
throttle: {
writeValue: null,
},
};
},
// ...
watch: {
pinValue(value) {
this.throttle.writeValue(value);
},
},
created() {
this.init();
this.throttle.writeValue = throttle(this.writeValue, 60);
},
// ...
};
看看效果如何。
感觉很棒!(≧∀≦)
大家可以加上更多个 LED 看看效果
成功完成所有基础功能,接下来要进入实际应用的部分喽!─=≡Σ((( つ•̀ω•́)つ
以上程序码已同步至 GitLab,大家可以前往下载:
>>: 【从实作学习ASP.NET Core】Day20 | 前台 | 建立前台页面
学习进度 资料结构 泛型 通配字元 Android Studio RecyclerView Recy...
延续昨日 今天我们来实现 管理者帐号跟团队介绍 其实这两点应该可以同时执行 因为只有管理者可以新增跟...
Web API Open data是一种Web API,使用HTTP请求来执行其他系统提供功能来存取...
今天我们不教程序,而是想带大家读一篇关於模型的论文,那为什麽会有这一Part呢?以前小编也不喜欢读P...
启航罗~~~ 此篇开始,会介绍AWS上使用完全托管的Kubernetes服务之EKS系列。一般自己托...