Skip to content

接口与指令

除了内置的控制台页面外,Node-Camera还可以利用接口调用的方式集成到目标系统。以下描述接口时,Node-Camera的IP地址或mDNS主机名统一使用${host}表示。

若系统设置启用了 Access Control,请参考访问控制

HTTP照片接口

使用HTTP GET获取Node-Camera实时照片。接口地址:

http://${host}/still

接口返回当前时点摄像头拍摄的JPEG格式照片。使用浏览器即可测试照片接口。

HTTP视频接口

使用HTTP GET访问Node-Camera摄像头视频流。与照片接口不同,视频流使用81端口。接口地址:

http://${host}:81/stream

接口以MIME类型multipart/x-mixed-replace返回实时MJPEG视频流。使用浏览器即可测试视频接口。

HTTP视频接口在同一时间只允许一个连接(独占),若已有客户端在访问视频,则后续的视频请求将被忽略。

WebSocket视频接口

系统设置启用 Stream Socket 后,Node-Camera将提供WebSocket视频接口,并停止HTTP视频接口。WebSocket接口地址:

ws://${host}:81/stream

客户端与Node-Camera建立WebSocket连接后,通过发送以下指令(字符串)控制视频流传输:

  • stream - 开始连续视频流传输。
  • stop - 停止视频流传输。
  • still - 传输一张照片后停止。

服务端发送的每一帧消息数据包(event.data)为一张完整的JPEG图片数据(Blob)。

WebSocket视频接口在同一时间只允许一个连接(独占),若已有客户端连接了WebSocket视频接口,则后续的连接请求将被忽略。当系统设置启用了 Stream Socket 后,Camera与Console页面会自动连接视频接口。因此,通过其它(自定义)方式访问WebSocket视频接口时,应该关闭Camera与Console页面。

客户端样例:

html
<!DOCTYPE html>
<html>

<head>
    <title>Stream-WebSocket</title>
</head>

<body>
    <div>
        <img id="image" src="">
    </div>
    <div>
        <button id="stream">Stream</button>
        <button id="stop">Stop</button>
        <button id="still">Still</button>
    </div>
</body>

<script>
    const image = document.getElementById('image');
    const stream = document.getElementById('stream');
    const stop = document.getElementById('stop');
    const still = document.getElementById('still');

    const socketURL = 'ws://192.168.1.100:81/stream'; // 替换为实际地址
    let socket = null;

    const connect = () => {
        socket = new WebSocket(socketURL);
        socket.addEventListener('open', e => {
            // Send the token here if 'Access Control' is enabled
        });
        socket.addEventListener('close', e => {
            setTimeout(connect, 1000);
        });
        socket.addEventListener('error', e => {
            console.warn('WebSocket Connection Failed');
        });
        socket.addEventListener('message', e => {
            if (e.data instanceof Blob) {
                image.src = URL.createObjectURL(e.data);
            }
        });
    }

    const send = arg => {
        if (socket?.readyState === WebSocket.OPEN) {
            socket.send(arg);
            console.log(arg);
        }
        else {
            console.warn('WebSocket Not Connected');
        }
    }

    stream.onclick = () => send('stream');
    stop.onclick = () => send('stop');
    still.onclick = () => send('still');

    connect();
</script>

</html>

指令

Node-Camera指令功能包括LED开关、云台控制、摇杆控制、抓拍触发、定时器开关、端口读/写、PWM控制、PCA9685驱动等。

指令接口

指令可以通过HTTP GET或WebSocket提交。若系统设置启用了 UART CMD,指令也可以由串口输入。

  • HTTP GET指令接口:

    http://${host}/cmd

    指令作为URL参数提交。例如http://${host}/cmd?flash=1(打开LED灯)

  • WebSocket指令接口:

    ws://${host}/socket/cmd

    指令通过已连接的WebSocket发送。例如socket.send("flash=1")(打开LED灯)

  • UART指令接口:

    若系统设置开启了 UART CMD,串口接收到的数据将作为指令解析并执行。例如RXD收到字符串flash=1,则打开LED灯。

指令定义

指令(command)由指令名称(command_name)与可选的指令参数(command_args)组成。指令格式:

command_name[=command_args]

对于有返回值(return_value)的指令,返回格式为:

command:return_value

其中command为接收到的完整指令。

例如,读取GPIO12端口指令get=12,返回get=12:1,表示GPIO12端口为高电平。

以下,按照指令名称分别说明:

  1. flash - LED开关控制。参数取值:0 - 关闭; 1 - 打开。无返回值。

  2. up - 云台舵机向上转,无参数,无返回值。舵机连续转动,直到上限或收到stop指令。

  3. down - 云台舵机向下转,无参数,无返回值。舵机连续转动,直到下限或收到stop指令。

  4. left - 云台舵机向左转,无参数,无返回值。舵机连续转动,直到下限或收到stop指令。

  5. right - 云台舵机向右转,无参数,无返回值。舵机连续转动,直到上限或收到stop指令。

  6. stop - 云台舵机停止转动,无参数,无返回值。

  7. joy - 摇杆控制,无返回值。

    摇杆由Console系统设置定义。参数为逗号分隔的两个整数,根据摇杆模式(joy_mode)分别定义。指令格式:

    joy=arg1,arg2

    • joy_mode = 1 - 两轮模式(Dual-Wheels)

      • arg1 - 左轮PWM负载,取值范围-1000~1000,负值表示反转。

      • arg2 - 右轮PWM负载,取值范围-1000~1000,负值表示反转。

    • joy_mode = 2 - 转向驱动模式(Steering & Drive)

      • arg1 - 转向舵机负载,取值范围-1000~1000-1000为转角下限,1000为转角上限,0值表示中间角。

      • arg2 - 驱动轮PWM负载,取值范围-1000~1000,负值表示反转。

  8. capture - 抓拍触发,无参数,无返回值。系统设置启用 Command Trigger 后有效。

  9. reset_seq - 重置抓拍照片文件名的循环递增序号为0,无参数,无返回值。

  10. timing - 抓拍定时器开关控制。参数取值:0 - 关闭; 1 - 打开。无返回值。

  11. get - 读取端口电平值,参数为GPIO端口号。

    该指令对Peripherals系统设置的 gpio.input 端口有效。如读取GPIO12端口电平:

    get=12

    该指令返回对应端口的电平值(0 - 低电平; 1 - 高电平)。若GPIO12端口为高电平,则返回:

    get=12:1

  12. set - 设置端口高电平,参数为GPIO端口号。

    该指令对Peripherals系统设置的 gpio.output 端口有效。如设置GPIO12为高电平:

    set=12

  13. clr - 设置端口低电平,参数为GPIO端口号。

    该指令对Peripherals系统设置的 gpio.output 端口有效。如设置GPIO12为低电平:

    clr=12

  14. raw - 读取模拟端口采样值,参数为ADC1 GPIO端口号。

    该指令对Peripherals系统设置的 gpio.analog 端口有效。如读取GPIO32 (ADC1_CH4)端口采样值:

    raw=32

    该指令返回对应端口的采样值,系统默认ADC为12bits位宽,返回值范围0~4095。如GPIO32端口采样返回:

    raw=32:2033

    模拟端口建议电压范围150mV~2450mV。

  15. vol - 读取模拟端口电压值,参数为ADC1 GPIO端口号。

    该指令对Peripherals系统设置的 gpio.analog 端口有效。与 raw 指令不同,该指令将采样值转换为实际的电压值(mV)返回。如读取GPIO32 (ADC1_CH4)端口电压值:

    vol=32

    指令返回对应端口的电压值,单位为mV。如GPIO32端口电压返回(1830mV):

    vol=32:1830

    模拟端口建议电压范围150mV~2450mV。

  16. bat - 读取电池电量,无参数。

    该指令需在Features系统设置中启用并配置电池监测(Battery Monitor)后有效,用于获取当前电池电量。电量以百分比数值表示(50表示50%),返回-1表示功能未启用。如剩余电量60%返回:

    bat:60

  17. pwm - 设置由Peripherals系统设置中 pwm 定义的PWM端口输出脉宽(Duty)。参数为逗号分隔的端口号(gpio_num)与负载值(duty)。无返回值。指令格式:

    pwm=gpio_num,duty

    • gpio_num - GPIO端口号。

    • duty - 脉宽负载,取值范围0~4095(12bits)。

  18. pca9685 - 设置由Peripherals系统设置中 pca9685 定义的PWM模块端口输出脉宽(Duty)。参数为逗号分隔的设备序号(id)、通道序号(channel),及负载值(duty)。无返回值。指令格式:

    pca9685=id,channel,duty

    • id - 指定一个Peripherals系统设置的PCA9685设备序号。

    • channel - 通道序号,取值范围0~15

    • duty - 脉宽负载,取值范围0~4095(12bits)。

串口透传

系统设置启用 UART Socket 后,Node-Camera串口(UART0)将映射到WebSocket地址:

ws://${host}/socket/uart

客户端通过该地址与Node-Camera建立WebSocket连接后,客户端向WebSocket发送的数据将透传到Node-Camera串口输出(TXD),Node-Camera串口接收到的数据(RXD)将由WebSocket发送给客户端。

串口透传接口在同一时间只允许一个连接(独占),若已有客户端连接了串口透传接口,则后续的连接请求将被忽略。当系统配置启用了 UART Socket 后,Settings页面会自动连接串口透传接口。因此,通过其它(自定义)方式访问串口透传接口时,应该关闭Settings页面。

客户端样例:

html
<!DOCTYPE html>
<html>

<head>
    <title>UART-WebSocket</title>
</head>

<body>
    <div>RXD</div>
    <div>
        <textarea id="rxd" rows="8" cols="32"></textarea>
    </div>
    <div>TXD</div>
    <div>
        <input id="txd" type="text"><button id="send">Send</button>
    </div>
</body>

<script>
    const rxd = document.getElementById('rxd');
    const txd = document.getElementById('txd');
    const send = document.getElementById('send');

    const socketURL = 'ws://192.168.1.100/socket/uart'; // 替换为实际地址
    let socket;

    const connect = () => {
        socket = new WebSocket(socketURL);
        socket.addEventListener('open', e => {
            // Send the token here if 'Access Control' is enabled
        });
        socket.addEventListener('close', e => {
            setTimeout(connect, 1000);
        });
        socket.addEventListener('error', e => {
            rxd.value = 'WebSocket Connection Failed';
        });
        socket.addEventListener('message', e => {
            rxd.value += e.data;
        });
    }

    send.onclick = () => {
        if (txd.value === '') return;

        if (socket?.readyState === WebSocket.OPEN) {
            socket.send(txd.value);
        }
        else {
            rxd.value = 'WebSocket Not Connected';
        }
    }

    connect();
</script>

</html>

SD卡接口

系统设置启用SD卡支持后,可通过HTTP GET访问SD卡接口。

  • 获取SD卡存储的照片列表:

    http://${host}/sdcard?list

    接口返回JSON格式照片列表:

    json
    [
        ["file_name", "yyyy-mm-dd hh:mi:ss"],
        ...
    ]

    返回样例:

    json
    [
        ["IMG_0000.JPG", "1980-01-01 00:00:30"],
        ["IMG_0001.JPG", "1980-01-01 00:00:40"],
        ["IMG_0002.JPG", "1980-01-01 00:00:50"]
    ]
  • 获取指定照片:

    http://${host}/sdcard?image=${file_name}

    调用样例:http://${host}/sdcard?image=IMG_0000.JPG

  • 删除指定照片:

    http://${host}/sdcard?delete=${file_name}

    调用样例:http://${host}/sdcard?delete=IMG_0000.JPG

访问控制

Node-Camera基于HTTP Cookie机制实现访问控制。

密码输入页面

系统配置启用 Access Control(访问控制)并设置密码后,对每个有效的HTTP请求,Node-Camera将检查其请求头Cookie中是否有名为"token"的键-值对(令牌)。若没有或token验证失败,则按以下规则处理:

  1. 访问控制台页面,将跳转到密码输入页面,验证通过后,继续返回请求的页面。

  2. 访问照片或视频接口,将返回Access Denied图片。

  3. 访问控制接口,将返回HTTP状态:401 Unauthorized。

对于WebSocket连接,当连接成功后,客户端需要先发送令牌token=xxxxxx,否则连接将被服务端关闭。

由密码输入页面提交访问密码,在验证通过的HTTP响应头中包含Cookie令牌:

Set-Cookie: token=xxxxxx

浏览器的后续请求会自带令牌Cookie:token=xxxxxx,实现正常访问。

Cookie机制被浏览器自动支持。若是用代码访问接口,可以参考以上机制,在HTTP请求头中添加Cookie令牌:

  1. 通过HTTP POST提交访问密码(Access Code)到地址:

    http://${host}/code

    从响应头中获取令牌token=xxxxxx

  2. 将令牌作为Cookie项添加到后续HTTP请求头中。

  3. 若使用WebSocket连接,则在连接建立后,首先发送由POST请求获取的令牌token=xxxxxx即可。

客户端样例(Node.js):

javascript
/*
 * 访问控制 - 获取摄像头实时照片并保存到本地
 */
const http = require('http');
const fs = require('fs');

const hostIP = '192.168.1.100'; // 替换为实际IP
const accessCode = 'nodenode';  // 访问密码
const localFile = 'still.jpg';  // 本地文件名

// step 3. 通过令牌获取摄像头照片并保存
function saveStill(token) {
    console.log(token);
    let file = fs.createWriteStream(localFile);
    http.get(
        {
            host: hostIP,
            path: '/still',
            headers: { 'Cookie': token } // 添加Cookie令牌
        },
        response => {
            response.pipe(file); // 保存
            console.log('Saved');
        }
    );
}

// step 2. 回调 - 从响应头提取令牌作为参数调用saveStill(token)
function callback(response) {
    console.log(response.statusCode);
    console.log(JSON.stringify(response.headers));
    let data = '';
    response.on('data', chunk => data += chunk);
    response.on('end', () => console.log(data));
    const cookies = response.headers['set-cookie'];
    if (cookies) {
        cookies.forEach(ck => {
            if (ck.indexOf('token=') == 0) saveStill(ck.split(';')[0]);
        });
    }
}

// step 1. 提交访问密码 (accessCode)
const req = http.request(
    {
        host: hostIP,
        path: '/code',
        method: 'POST',
        headers: {
            'Content-Type': 'text/plain',
            'Content-Length': accessCode.length
        }
    },
    callback);
req.write(accessCode); // POST
req.end();