Skip to content

I2C助手

基于Web的I2C调试助手,无需上位机,打开浏览器便可操作。视频展示如下:


操作流程:

  1. 配置i2c参数(设置波特率、使用的GPIO引脚)等
  2. 扫描总线
  3. 添加设备的展示插件,使数据更易读

数据可以多种格式展示

  • 10 进制
  • 16 进制
  • BCD 码

可二次开发的的Web展示插件,结合 JS社区的生态数据的展示更加丰富。

操作指令

界面的底部输入框可以直接输入命令来控制 I2C设备,语法如下:

  1. 多条命令以;隔开
  2. 读操作 r 长度(字节)r 10
  • r 10
  • read 10
  1. 写操作
  • 直接输入以,分隔的字节序列;以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;
 -->