I2C助手
基于Web的I2C调试助手,无需上位机,打开浏览器便可操作。视频展示如下:
操作流程:
- 配置i2c参数(设置波特率、使用的GPIO引脚)等
- 扫描总线
- 添加设备的展示插件,使数据更易读
数据可以多种格式展示
- 10 进制
- 16 进制
- BCD 码
可二次开发的的Web展示插件,结合 JS社区的生态数据的展示更加丰富。
操作指令
界面的底部输入框可以直接输入命令来控制 I2C设备,语法如下:
- 多条命令以
;
隔开 - 读操作
r 长度(字节)
如r 10
- r 10
- read 10
- 写操作
- 直接输入以
,
分隔的字节序列;以0x
开始会识别为16进制。 "string"
双引号内的信息被认为字符串。
示例
对RTC模块从地址0处开始读取7个寄存器的操作。0;read 7
。
WEB插件
通过WEB插件可以发挥你的想象力自定义数据展示方式了:表格、图表等多种型态。通过JS+CSS 的加持可以为所欲为,试一试!
- i2c_active_dev 当前操作的设备地址
- last_resp_msg 最后一次收到的数据 :
last_resp_msg[i2c_active_dev]
cmd_table 组件的使用
自定义命令列表
参数:
- debugger 日志输出开关
- title I2C 设备名称
- hisSize 图表展示时保留的历史数据数量
- devAddr 设备的i2c地址
- Send 向设备发送数据的方法
- commands
不可修改的参数
- devAddr
- Send
命令配置列表
[{name,command,every,parse}]
- name :命令的标识
- command :发送的命令如SHT3X的读温湿度命令:
0x2c,0x06;r 6
- parse :数据解析函数如SHT3X的解析. 读取的数据是以字节数组响应的
javascript
(buff)=>{
let temp= -45.0 + 175.0 * (buff[0 ] * 256 + buff[1 ])/65535.0 ;
let hum = 100 * (buff[3 ] * 256 + buff[4 ])/ 65535.0 ;
return `温度:${temp.toFixed(2)} 湿度:${hum.toFixed(2)}`
}
命令的历史数据存储于上下文Row的.history数组中
可以使用折线图展示历史数据,最大的点数由hisSize
指定
折线图line_chart 组件的使用
折线图展示数据
参数
- class CSS类名(可以使用tailwind定义的类名)
- height 组件高度
- option 图表数据定义,请重点关注‘series’字段的使用(字段详情可以参考 Apache ECharts)
html
<line_chart class="w-full p-3 h-6 " height="350px" :option="
{
'animation': false,
'legend' : { 'data': ['温度', '湿度'] },
'title' : { 'text': '温度', 'left': 'left' },
'xAxis' : { 'type': 'category' }, 'yAxis': { 'type': 'value',},
'series' : [
{ 'name':'温度','type': 'line', 'smooth': true,'data':!Row.history?[]:Row.history.map(buff=> -45.0 + 175.0 * (buff[0 ] * 256 + buff[1 ])/65535.0 )},
{ 'name':'湿度','type': 'line', 'smooth': true,'data':!Row.history?[]:Row.history.map(buff=> 100 * (buff[3 ] * 256 + buff[4 ])/ 65535.0 )},
]
}
"></line_chart>
WEB插件完整示例
SHT3X温/湿度传感器
html
<cmd_table :debugger="true" title="SHT3X温/湿度传感器" :hisSize="250" :devAddr ="i2c_active_dev" :Send="Send" :commands="[
{'name':'Read','command':'0x2c,0x06;r 6','every':[1,0.1,0.01],parse:(buff)=>{
let temp= -45.0 + 175.0 * (buff[0 ] * 256 + buff[1 ])/65535.0 ;
let hum = 100 * (buff[3 ] * 256 + buff[4 ])/ 65535.0 ;
return `温度:${temp.toFixed(2)} 湿度:${hum.toFixed(2)}`},
}
]">
<template #Read="Row" >
<tr ><td colspan="5" >
<line_chart class="w-full p-3 h-6 " height="350px" :option="
{
'animation': false,
'legend' : { 'data': ['温度', '湿度'] },
'title' : { 'text': '温度', 'left': 'left' },
'xAxis' : { 'type': 'category' }, 'yAxis': { 'type': 'value',},
'series' : [
{ 'name':'温度','type': 'line', 'smooth': true,'data':!Row.history?[]:Row.history.map(buff=> -45.0 + 175.0 * (buff[0 ] * 256 + buff[1 ])/65535.0 )},
{ 'name':'湿度','type': 'line', 'smooth': true,'data':!Row.history?[]:Row.history.map(buff=> 100 * (buff[3 ] * 256 + buff[4 ])/ 65535.0 )},
]
}
"> </line_chart>
</td></tr>
</template>
<pluginname @plugin="i2c_name[i2c_active_dev]='SHT3X'"/>
</cmd_table>
RTC(DS3231) Plugin
html
<div class="flex-3 flex content-center items-center text-center p-4 pr-0">
<div id="clock" @click="Send('0;r 10')" style="font-size: 30px; text-align: center; padding-top:40px;margin:auto;" class="blue-400">
<p class="text-blue-600">
{{DS3231(last_resp_msg[i2c_active_dev])}}
</p>
</div>
<button @click="Send('0;r 10')" type="button" class="ml-2 inline-flex justify-center items-center gap-1 rounded px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm bg-blue-600 hover:bg-blue-500 disabled:bg-blue-400 ">
<svg_refresh class="w-6 h-6"/>
</button>
</div>
RTC(DS3231) 进阶版本
本示例copy 于ECharts gauge-clock 网站有大量精美图表示例供参考。
html
<div class="flex-3 flex content-center items-center text-center p-4 pr-0">
<div id="clock" @click="Send('0;r 10')" style="font-size: 30px; text-align: center; padding-top:40px;margin:auto;" class="blue-400">
<p class="text-blue-600">
<line_chart class="w-full p-3 h-12" style="height: 480px;width: 480px;" :option="
{
series: [
{
name: 'hour', type: 'gauge', startAngle: 90, endAngle: -270, min: 0, max: 12, splitNumber: 12, clockwise: true,
axisLine: {
lineStyle: {width: 15, color: [[1, 'rgba(0,0,0,0.7)']], shadowColor: 'rgba(0, 0, 0, 0.5)', shadowBlur: 15 }
},
splitLine: {
lineStyle: {shadowColor: 'rgba(0, 0, 0, 0.3)', shadowBlur: 3, shadowOffsetX: 1, shadowOffsetY: 2 }
},
axisLabel: {
fontSize: 50, distance: 25, formatter: function (value) {if (value === 0) {return ''; } return value + ''; }
},
anchor: {
show: true,
icon: 'path://M532.8,70.8C532.8,70.8,532.8,70.8,532.8,70.8L532.8,70.8C532.7,70.8,532.8,70.8,532.8,70.8z M456.1,49.6c-2.2-6.2-8.1-10.6-15-10.6h-37.5v10.6h37.5l0,0c2.9,0,5.3,2.4,5.3,5.3c0,2.9-2.4,5.3-5.3,5.3v0h-22.5c-1.5,0.1-3,0.4-4.3,0.9c-4.5,1.6-8.1,5.2-9.7,9.8c-0.6,1.7-0.9,3.4-0.9,5.3v16h10.6v-16l0,0l0,0c0-2.7,2.1-5,4.7-5.3h10.3l10.4,21.2h11.8l-10.4-21.2h0c6.9,0,12.8-4.4,15-10.6c0.6-1.7,0.9-3.5,0.9-5.3C457,53,456.7,51.2,456.1,49.6z M388.9,92.1h11.3L381,39h-3.6h-11.3L346.8,92v0h11.3l3.9-10.7h7.3h7.7l3.9-10.6h-7.7h-7.3l7.7-21.2v0L388.9,92.1z M301,38.9h-10.6v53.1H301V70.8h28.4l3.7-10.6H301V38.9zM333.2,38.9v10.6v10.7v31.9h10.6V38.9H333.2z M249.5,81.4L249.5,81.4L249.5,81.4c-2.9,0-5.3-2.4-5.3-5.3h0V54.9h0l0,0c0-2.9,2.4-5.3,5.3-5.3l0,0l0,0h33.6l3.9-10.6h-37.5c-1.9,0-3.6,0.3-5.3,0.9c-4.5,1.6-8.1,5.2-9.7,9.7c-0.6,1.7-0.9,3.5-0.9,5.3l0,0v21.3c0,1.9,0.3,3.6,0.9,5.3c1.6,4.5,5.2,8.1,9.7,9.7c1.7,0.6,3.5,0.9,5.3,0.9h33.6l3.9-10.6H249.5z M176.8,38.9v10.6h49.6l3.9-10.6H176.8z M192.7,81.4L192.7,81.4L192.7,81.4c-2.9,0-5.3-2.4-5.3-5.3l0,0v-5.3h38.9l3.9-10.6h-53.4v10.6v5.3l0,0c0,1.9,0.3,3.6,0.9,5.3c1.6,4.5,5.2,8.1,9.7,9.7c1.7,0.6,3.4,0.9,5.3,0.9h23.4h10.2l3.9-10.6l0,0H192.7z M460.1,38.9v10.6h21.4v42.5h10.6V49.6h17.5l3.8-10.6H460.1z M541.6,68.2c-0.2,0.1-0.4,0.3-0.7,0.4C541.1,68.4,541.4,68.3,541.6,68.2L541.6,68.2z M554.3,60.2h-21.6v0l0,0c-2.9,0-5.3-2.4-5.3-5.3c0-2.9,2.4-5.3,5.3-5.3l0,0l0,0h33.6l3.8-10.6h-37.5l0,0c-6.9,0-12.8,4.4-15,10.6c-0.6,1.7-0.9,3.5-0.9,5.3c0,1.9,0.3,3.7,0.9,5.3c2.2,6.2,8.1,10.6,15,10.6h21.6l0,0c2.9,0,5.3,2.4,5.3,5.3c0,2.9-2.4,5.3-5.3,5.3l0,0h-37.5v10.6h37.5c6.9,0,12.8-4.4,15-10.6c0.6-1.7,0.9-3.5,0.9-5.3c0-1.9-0.3-3.7-0.9-5.3C567.2,64.6,561.3,60.2,554.3,60.2z',
showAbove: false, offsetCenter: [0, '-35%'], size: 120, keepAspect: true, itemStyle: {color: '#707177' }
},
pointer: {
icon: 'path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z',
width: 12, length: '55%', offsetCenter: [0, '8%'], itemStyle: {color: '#C0911F', shadowColor: 'rgba(0, 0, 0, 0.3)', shadowBlur: 8, shadowOffsetX: 2, shadowOffsetY: 4 }
},
detail: {show: false }, title: {offsetCenter: [0, '30%'] },
data: [{value: plugin_obj.Hour } ]
},
{
name: 'minute', type: 'gauge', startAngle: 90, endAngle: -270, min: 0, max: 60, clockwise: true, axisLine: {show: false }, splitLine: {show: false }, axisTick: {show: false }, axisLabel: {show: false },
pointer: {
icon: 'path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z',
width: 8, length: '70%', offsetCenter: [0, '8%'], itemStyle: {color: '#C0911F', shadowColor: 'rgba(0, 0, 0, 0.3)', shadowBlur: 8, shadowOffsetX: 2, shadowOffsetY: 4 }
},
anchor: {
show: true, size: 20, showAbove: false, itemStyle: {borderWidth: 15, borderColor: '#C0911F', shadowColor: 'rgba(0, 0, 0, 0.3)', shadowBlur: 8, shadowOffsetX: 2, shadowOffsetY: 4 }
},
detail: {show: false }, title: {offsetCenter: ['0%', '-40%'] },
data: [{value: plugin_obj.Minute } ]
},
{
name: 'second', type: 'gauge', startAngle: 90, endAngle: -270, min: 0, max: 60, animationEasingUpdate: 'bounceOut', clockwise: true, axisLine: {show: false }, splitLine: {show: false }, axisTick: {show: false },
axisLabel: {show: false },
pointer: {
icon: 'path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z',
width: 4, length: '85%', offsetCenter: [0, '8%'], itemStyle: {color: '#C0911F', shadowColor: 'rgba(0, 0, 0, 0.3)', shadowBlur: 8, shadowOffsetX: 2, shadowOffsetY: 4 }
},
anchor: {show: true, size: 15, showAbove: true, itemStyle: {color: '#C0911F', shadowColor: 'rgba(0, 0, 0, 0.3)', shadowBlur: 8, shadowOffsetX: 2, shadowOffsetY: 4 } },
detail: {show: false },
title: {offsetCenter: ['0%', '-40%'] },
data: [{value: plugin_obj.Second }]
}]
}
"></line_chart>
</p>
</div>
<span @click="Send('0;r 10',(buff)=>{
[seconds,minutes,hour,day,date,month,year] = DS3231(buff,false);
plugin_obj.Second = seconds;
plugin_obj.Minute = minutes;
plugin_obj.Hour = hour;
})" class="ml-2 inline-block font-medium rounded-md px-2 py-1 text-xs ring-1 ring-inset bg-green-100 text-green-900 ring-green-300">
Read
</span>
<span @click="stop('every'),every(1000,addr=>Send('0;r 10',(buff)=>{
if(buff.byteLength <=6) return 0;//buff
[seconds,minutes,hour,day,date,month,year] = DS3231(buff,false);
plugin_obj.Second = seconds;
plugin_obj.Minute = minutes;
plugin_obj.Hour = hour;
},1000,i2c_active_dev),'every')" class="ml-2 inline-block font-medium rounded-md px-2 py-1 text-xs ring-1 ring-inset bg-yellow-100 text-yellow-900 ring-yellow-300">
every 1s
</span>
<span v-if="plugin_obj['timer']['every']" @click="stop('every')" class="ml-2 inline-block font-medium rounded-md px-2 py-1 text-xs ring-1 ring-inset bg-red-100 text-red-900 ring-red-300"> Stop </span>
<pluginname @plugin="i2c_name[i2c_active_dev]='DS3231'"/>
</div>
AT24C32 EEPROM编辑器
I2C 调试助手可以直接读写EEPROM做为EEPROM编辑器使用
html
<hexeditor :dataArray="plugin_obj.dataArray" :Read="(addr,len=64)=>{
let read_addr = `${(addr&0xff00)>>8},${(addr&0xff)};r ${len}`;
console.log('Read E2PROM=====>@',addr,len)
Send(read_addr,(buff)=>{
if(!(buff && buff.byteLength >6)) return;
data = buff.slice(6);
if(!plugin_obj.dataArray || plugin_obj.dataArray.byteLength == 0){
let tem_Array = new WIN.ArrayBuffer(data.byteLength);
let view = new WIN.DataView(tem_Array);
for(let i = 0;i< data.byteLength;i++){
view.setUint8(i,data[i]);
}
plugin_obj.dataArray = tem_Array;
plugin_obj.dataView = view;
}
else{
let new_array = new WIN.ArrayBuffer(data.byteLength + plugin_obj.dataArray.byteLength);
let view = new WIN.DataView(new_array);
let oldView = new WIN.DataView(plugin_obj.dataArray);
for(let i = 0;i< plugin_obj.dataArray.byteLength;i++){
view.setUint8(i,oldView.getUint8(i));
}
for(let i = 0;i< data.byteLength;i++){
view.setUint8(plugin_obj.dataArray.byteLength+i,data[i]);
}
plugin_obj.dataArray = new_array;
plugin_obj.dataView = view;
}
})
}" :Write="(addr,buffer,callback)=>{
let cmd_str = `${(addr&0xff00)>>8},${(addr&0xff)}`;
for(let i = 0;i<buffer.byteLength;i++){
cmd_str += `,${buffer[i]}`
}
console.log(`Write E2PROM=====>@${addr} CDM:${cmd_str}`);
Send(cmd_str,(resp)=>{
console.log('RESP :',resp);
if(typeof(callback) == 'function'){
callback(resp);
}
});
}"
@clear="(t,v)=>{
console.log('Recv clear');
plugin_obj.dataArray = new WIN.ArrayBuffer(0);
}" >
<pluginname @plugin="i2c_name[i2c_active_dev]='AT24C32'"/>
</hexeditor>
MPU6050 3轴加速度传感器
借助于 three.js 的 3d渲染功能。I2C助手可以直观展示 3D状态了
html
<div class="h-full flex flex-col px-4 sm:px-2 lg:px-4 lg:py-4 ">
<div class="grid sm:grid-cols-3 lg:grid-cols-3 gap-4 sm:gap-6 place-items-stretch place-content-center">
<div class="flex flex-col bg-white border shadow-sm rounded-xl ">
<div class="overflow-auto rounded-lg bg-white px-4 py-2 ">
<div class="flex items-center gap-x-2">
<p class="text-sm truncate text-gray-500 font-medium"> Temperature °C </p>
</div>
<div class="mt-1 flex items-center gap-x-2">
<h3 class="text-xl truncate font-semibold tracking-tight text-yellow-800 ">{{plugin_obj.temp?plugin_obj.temp:'?'}} °C </h3>
<span class="flex items-center good">
<span class="inline-flex items-center gap-1.5 py-0.5">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</span>
</span>
</div>
</div>
</div>
<!-- -->
<div class="flex flex-col bg-white border shadow-sm rounded-xl ">
<div class="overflow-auto rounded-lg bg-white px-4 py-2 ">
<div class="flex items-center gap-x-2">
<p class="text-sm truncate text-gray-500 font-medium"> 加速度 </p>
</div>
<div class="mt-1 flex items-center gap-x-2">
<h3 class="text-xl truncate font-semibold tracking-tight text-yellow-800 ">{{plugin_obj.acc}}</h3>
<span class="flex items-center good">
<span class="inline-flex items-center gap-1.5 py-0.5">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</span>
</span>
</div>
</div>
</div>
<!-- -->
<div class="flex flex-col bg-white border shadow-sm rounded-xl ">
<div class="overflow-auto rounded-lg bg-white px-4 py-2 ">
<div class="flex items-center gap-x-2">
<p class="text-sm truncate text-gray-500 font-medium"> 陀螺仪 </p>
</div>
<div class="mt-1 flex items-center gap-x-2">
<h3 class="text-xl truncate font-semibold tracking-tight text-yellow-800 ">{{plugin_obj.gyro}}</h3>
<span class="flex items-center good">
<span class="inline-flex items-center gap-1.5 py-0.5">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
<!-- 指令表 -->
<div class=" flex flex-col h-full ">
<div class=" h-2/4 ">
<line_chart class="w-full p-3 h-6" :option="
{
'animation': false,
'legend': {'data': ['Temperature', 'yaw','pitch','roll'/*'acc_x', 'acc_y', 'acc_z', 'gyro_x','gyro_y','gyro_z'*/] },
'title': { 'text': 'MPU6050', 'left': 'left' }, 'xAxis': { 'type': 'category' }, 'yAxis': { 'type': 'value','max':plugin_obj.Ymax}, 'series':
[
{ 'name':'Temperature','type': 'line', 'smooth': true,'data': plugin_obj.temp_his? plugin_obj.temp_his:[]},
{ 'name':'yaw','type': 'line', 'smooth': true,'data': plugin_obj.yaw_his? plugin_obj.yaw_his:[]},
{ 'name':'pitch','type': 'line','smooth': true, 'data': plugin_obj.pitch_his? plugin_obj.pitch_his:[]},
{ 'name':'roll','type': 'line', 'smooth': true,'data': plugin_obj.roll_his? plugin_obj.roll_his:[]},
/*
{ 'name':'acc_x','type': 'line', 'data': plugin_obj.acc_his_x? plugin_obj.acc_his_x:[]},
{ 'name':'acc_y','type': 'line', 'data': plugin_obj.acc_his_y? plugin_obj.acc_his_y:[]},
{ 'name':'acc_z','type': 'line', 'data': plugin_obj.acc_his_z? plugin_obj.acc_his_z:[]},
{ 'name':'gyro_x','type': 'line', 'data': plugin_obj.gyro_his_x? plugin_obj.gyro_his_x:[]},
{ 'name':'gyro_y','type': 'line', 'data': plugin_obj.gyro_his_y? plugin_obj.gyro_his_y:[]},
{ 'name':'gyro_z','type': 'line', 'data': plugin_obj.gyro_his_z? plugin_obj.gyro_his_z:[]},
*/
/*
https://www.youtube.com/watch?v=zkdMM4jqtF0
https://github.com/Hykudoru/MPU6050-Gyro-Motion-Tracking
*/
]
}
" @mounted=" WIN._key = 0,
WIN._frq = 1,
WIN._type = 1,
WIN._clear_history = (key)=>{
if(key=='temp'){
plugin_obj.acc_his_x = plugin_obj.acc_his_y = plugin_obj.acc_his_z= null;
plugin_obj.gyro_his_x = plugin_obj.gyro_his_y = plugin_obj.gyro_his_z = null;
}else if(key=='acc'){
plugin_obj.gyro_his_x = plugin_obj.gyro_his_y = plugin_obj.gyro_his_z = null;
plugin_obj.temp_his = null;
}else{
plugin_obj.acc_his_x = plugin_obj.acc_his_y = plugin_obj.acc_his_z = null;
plugin_obj.temp_his = null;
}
},
WIN._mpu_MahonyAHRS=(_)=>{
let A_cal = [265.0, -80.0, -700.0, 0.994, 1.000, 1.014];
const [MPU6050_AXOFFSET,MPU6050_AYOFFSET,MPU6050_AZOFFSET] = [-208,417,93];
const [MPU6050_GXOFFSET,MPU6050_GYOFFSET,MPU6050_GZOFFSET] = [0,2,3];//[-499.5, -17.7, -82.0]; //raw offsets, determined for gyro at rest
const [MPU6050_GXGAIN,MPU6050_GYGAIN,MPU6050_GZGAIN] = [16.384,16.384,16.384];
const [MPU6050_AXGAIN,MPU6050_AYGAIN,MPU6050_AZGAIN] = [4096.0,4096.0,4096.0];// AFS_SEL = 2, +/-8g, MPU6050_ACCEL_FS_8
let [twoKp,twoKi] = [2.0 * 0.5,2.0 * 0.0];// 2 * proportional gain (Kp) ;2 * integral gain (Ki)
let [q0,q1,q2,q3] = [1.0,0.0,0.0,0.0];
let [integralFBx,integralFBy,integralFBz] = [0.0,0.0,0.0];
function mpu6050_updateQuaternion() {
if(WIN._type ==0){
let axg = (plugin_obj['acc'][0] - MPU6050_AXOFFSET) / MPU6050_AXGAIN;
let ayg = (plugin_obj['acc'][1] - MPU6050_AYOFFSET) / MPU6050_AYGAIN;
let azg = (plugin_obj['acc'][2] - MPU6050_AZOFFSET) / MPU6050_AZGAIN;
let gxrs = (plugin_obj['gyro'][0] - MPU6050_GXOFFSET) / MPU6050_GXGAIN * 0.01745329; //degree to radians
let gyrs = (plugin_obj['gyro'][1] - MPU6050_GYOFFSET) / MPU6050_GYGAIN * 0.01745329; //degree to radians
let gzrs = (plugin_obj['gyro'][2] - MPU6050_GZOFFSET) / MPU6050_GZGAIN * 0.01745329; //degree to radians
// Degree to Radians Pi / 180 = 0.01745329 0.01745329251994329576923690768489
return [gxrs,gyrs,gzrs,axg,ayg,azg];
}else{
let axg = plugin_obj['acc'][0] ;
let ayg = plugin_obj['acc'][1] ;
let azg = plugin_obj['acc'][2] ;
let gxrs = plugin_obj['gyro'][0] * 0.01745329;//degree to radians
let gyrs = plugin_obj['gyro'][1] * 0.01745329;//degree to radians
let gzrs = plugin_obj['gyro'][2] * 0.01745329;//degree to radians
return [gxrs,gyrs,gzrs,axg,ayg,azg];
}
}
function MahonyAHRSupdateIMU() {
let sampleFreq = (1 * (WIN._frq)); // set integration time by time elapsed since last filter update
// let sampleFreq = 512.0; // set integration time by time elapsed since last filter update
plugin_obj.sampleFreq = sampleFreq;
let [ gx, gy, gz, ax, ay, az] = mpu6050_updateQuaternion();
let norm;
let halfvx, halfvy, halfvz;
let halfex, halfey, halfez;
let qa, qb, qc;
// Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
if(!((ax == 0.0) && (ay == 0.0) && (az == 0.0))) {
// Normalise accelerometer measurement
norm = Math.sqrt(ax * ax + ay * ay + az * az);
ax /= norm;
ay /= norm;
az /= norm;
// Estimated direction of gravity and vector perpendicular to magnetic flux
halfvx = q1 * q3 - q0 * q2;
halfvy = q0 * q1 + q2 * q3;
halfvz = q0 * q0 - 0.5 + q3 * q3;
// Error is sum of cross product between estimated and measured direction of gravity
halfex = (ay * halfvz - az * halfvy);
halfey = (az * halfvx - ax * halfvz);
halfez = (ax * halfvy - ay * halfvx);
// Compute and apply integral feedback if enabled
if(twoKi > 0.0) {
integralFBx += twoKi * halfex * (1.0 / sampleFreq); // integral error scaled by Ki
integralFBy += twoKi * halfey * (1.0 / sampleFreq);
integralFBz += twoKi * halfez * (1.0 / sampleFreq);
gx += integralFBx; // apply integral feedback
gy += integralFBy;
gz += integralFBz;
}
else {
integralFBx = 0.0; // prevent integral windup
integralFBy = 0.0;
integralFBz = 0.0;
}
// Apply proportional feedback
gx += twoKp * halfex;
gy += twoKp * halfey;
gz += twoKp * halfez;
}
// Integrate rate of change of quaternion
gx *= (0.5 * (1.0 / sampleFreq)); // pre-multiply common factors
gy *= (0.5 * (1.0 / sampleFreq));
gz *= (0.5 * (1.0 / sampleFreq));
qa = q0;
qb = q1;
qc = q2;
q0 += (-qb * gx - qc * gy - q3 * gz);
q1 += (qa * gx + qc * gz - q3 * gy);
q2 += (qa * gy - qb * gz + q3 * gx);
q3 += (qa * gz + qb * gy - qc * gx);
// Normalise quaternion
norm = Math.sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 /= norm;
q1 /= norm;
q2 /= norm;
q3 /= norm;
}
function mpu6050_getRollPitchYaw() {
if(1){
let roll = Math.atan2(2.0 * (q0 * q1 + q2 * q3), q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3) * 57.29577951;
let yaw = -Math.atan2(2.0 * (q1 * q2 + q0 * q3), q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3) * 57.29577951;
let pitch = Math.asin(2.0 * (q1 * q3 - q0 * q2)) * 57.29577951;
return [yaw,pitch,roll];
}
let roll = Math.atan2(q0*q1 + q2*q3, 0.5 - q1*q1 - q2*q2);
let pitch = Math.asin(-2.0 * (q1*q3 - q0*q2));
let yaw = Math.atan2(q1*q2 + q0*q3, 0.5 - q2*q2 - q3*q3);
return [yaw,pitch,roll];
}
function doIt(accX,accY,accZ,gyroX,gyro_Y,gyro_Z){
mpu6050_updateQuaternion();
MahonyAHRSupdateIMU();
return mpu6050_getRollPitchYaw();
}
return doIt;
},
WIN._mpu6050_do = (buff,key)=>{
if(buff){
function gen_key(tm) {
return WIN._key;
return timeFormat(tm,true);
}
let accel_scale = 16384;//MPU6050_RANGE_2_G.
let gyro_scale = 65.5; //MPU6050_RANGE_500_DEG
if(WIN._type ==0){
accel_scale = gyro_scale = 1;
}
plugin_obj['acc']=[
((buff[0 ] * 256 + buff[1 ])<<16) >>16,
((buff[2 ] * 256 + buff[3 ])<<16) >>16,
((buff[4 ] * 256 + buff[5 ])<<16) >>16,
].map(it=>(it/accel_scale).toFixed(2)*1);
let reg_v = (((buff[6+0 ] << 8) | buff[6+1]) << 16) >> 16;
plugin_obj.temp= reg_v /340.0 +36.53;
plugin_obj.temp= plugin_obj.temp.toFixed(2);
plugin_obj['gyro']=[
((buff[8+0 ] * 256 + buff[8+1 ])<<16) >>16,
((buff[8+2 ] * 256 + buff[8+3 ])<<16) >>16,
((buff[8+4 ] * 256 + buff[8+5 ])<<16) >>16,
].map(it=>(it/gyro_scale).toFixed(2)*1)
let [yaw,pitch,roll] = WIN._mpu_MahonyAHRS()();
// conso=le.log('[yaw,pitch,roll]=',[yaw,pitch,roll]);
plugin_obj.last_yaw = yaw;
plugin_obj.last_pitch = pitch;
plugin_obj.last_roll = roll;
let maxV = Math.max(plugin_obj.Ymax?plugin_obj.Ymax:0,yaw,pitch,roll);
// plugin_obj.Ymax = plugin_obj.Ymax.toFixed(2)+'';
if(plugin_obj.Ymax != maxV){
plugin_obj.Ymax = maxV*1.1;
}
WIN._key += 1;
if(plugin_obj.acc_his_x){
plugin_obj.acc_his_x.push([gen_key(+new Date(),true),plugin_obj.acc[0].toFixed(2)*1]);
plugin_obj.acc_his_y.push([gen_key(+new Date(),true),plugin_obj.acc[1].toFixed(2)*1]);
plugin_obj.acc_his_z.push([gen_key(+new Date(),true),plugin_obj.acc[2].toFixed(2)*1]);
plugin_obj.gyro_his_x.push([gen_key(+new Date(),true),plugin_obj.gyro[0].toFixed(2)*1]);
plugin_obj.gyro_his_y.push([gen_key(+new Date(),true),plugin_obj.gyro[1].toFixed(2)*1]);
plugin_obj.gyro_his_z.push([gen_key(+new Date(),true),plugin_obj.gyro[2].toFixed(2)*1]);
plugin_obj.yaw_his.push([gen_key(+new Date(),true),yaw.toFixed(2)*1]);
plugin_obj.pitch_his.push([gen_key(+new Date(),true),pitch.toFixed(2)*1]);
plugin_obj.roll_his.push([gen_key(+new Date(),true),roll.toFixed(2)*1]);
}
else{
plugin_obj.acc_his_x = [[gen_key(+new Date(),true),plugin_obj.acc[0].toFixed(2)*1]];
plugin_obj.acc_his_y = [[gen_key(+new Date(),true),plugin_obj.acc[1].toFixed(2)*1]];
plugin_obj.acc_his_z = [[gen_key(+new Date(),true),plugin_obj.acc[2].toFixed(2)*1]];
plugin_obj.gyro_his_x = [[gen_key(+new Date(),true),plugin_obj.gyro[0].toFixed(2)*1]];
plugin_obj.gyro_his_y = [[gen_key(+new Date(),true),plugin_obj.gyro[1].toFixed(2)*1]];
plugin_obj.gyro_his_z = [[gen_key(+new Date(),true),plugin_obj.gyro[2].toFixed(2)*1]];
plugin_obj.yaw_his = [[gen_key(+new Date(),true),yaw.toFixed(2)*1]];
plugin_obj.pitch_his= [[gen_key(+new Date(),true),pitch.toFixed(2)*1]];
plugin_obj.roll_his = [[gen_key(+new Date(),true),roll.toFixed(2)*1]];
}
if(plugin_obj.acc_his_x.length >60){
plugin_obj.acc_his_x.shift() ;
plugin_obj.acc_his_y.shift() ;
plugin_obj.acc_his_z.shift() ;
plugin_obj.gyro_his_x.shift();
plugin_obj.gyro_his_y.shift();
plugin_obj.gyro_his_z.shift();
plugin_obj.yaw_his.shift();
plugin_obj.pitch_his.shift();
plugin_obj.roll_his.shift();
}
let avg_n = WIN._frq/2;
if(avg_n > plugin_obj.yaw_his.length) avg_n = plugin_obj.yaw_his.length;
if(arg_n <= 1) arg_n=1;
plugin_obj.avg_n = avg_n
plugin_obj.avg_yaw = plugin_obj.yaw_his.slice(avg_n*-1).reduce((sum,c)=>sum+c[1],0) / avg_n;
plugin_obj.avg_pitch = plugin_obj.pitch_his.slice(avg_n*-1).reduce((sum,c)=>sum+c[1],0) / avg_n;
plugin_obj.avg_roll = plugin_obj.roll_his.slice(avg_n*-1).reduce((sum,c)=>sum+c[1],0) / avg_n;
}
}
"></line_chart>
</div>
<cmd_table :debugger="true" :noresult= "true" title="Title" :hisSize="60" :devAddr ="i2c_active_dev" :Send="Send" :commands="[
{'name':'Init','command':'0x6B,0x80;0x19,0x00;0x1A,0x00;0x1b,0x8;0x1c,0x00;0x6B,0x01'},
{'name':'Read','command':'0x3b;r 14','every':[1,0.1,0.01],parse:(buff)=>{
if(WIN._mpu6050_do) WIN._mpu6050_do(buff)
}
}
]">
</cmd_table>
</div>
plugin_obj.avg_n:{{plugin_obj.avg_n}}
<pluginname @plugin="i2c_name[i2c_active_dev]='MPU6050'"/>
<tree :data="{
gyroX:plugin_obj.avg_roll,
gyroY:plugin_obj.avg_pitch,
gyroZ:plugin_obj.avg_yaw,//green
}"/>
</div>
<!--
<tree :data="{
gyroX:plugin_obj.last_roll ,
gyroY:plugin_obj.last_pitch,
gyroZ:plugin_obj.last_yaw,//green
}"/>
cube.rotation.x = obj.gyroY;
cube.rotation.z = obj.gyroX;
cube.rotation.y = obj.gyroZ;
gyroY:plugin_obj.last_yaw ,
gyroX:plugin_obj.last_pitch,
gyroZ:plugin_obj.last_roll
tem = ((buff[0]<<8) | buff[1]);//温度拼接
hum = ((buff[3]<<8) | buff[4]);//湿度拼接
Temperature= (175.0*(float)tem/65535.0-45.0) ; // T = -45 + 175 * tem / (2^16-1)
Humidity= (100.0*(float)hum/65535.0); // RH = hum*100 / (2^16-1)
int temp = (data[0] * 256 + data[1]);
float cTemp = -45 + (175 * (buff[0] * 256 + buff[1]) / 65535.0);
float fTemp = -49 + (315 * temp / 65535.0);
float humidity = 100 * (buff[3] * 256 + buff[4]) / 65535.0;
-->