shadow-1 / yi-hack-v3

Alternative Firmware for Xiaomi Cameras based on Hi3518e Chipset
GNU General Public License v3.0
1.15k stars 155 forks source link

Trigger additional video capture without motion detection by manipulating IPC message queues? #207

Open tm-robinson opened 6 years ago

tm-robinson commented 6 years ago

I've managed to get yi-hack-v3 up and running on a YiHome 720p (the 47US variant) and it's all working nicely. I can access the camera via telnet/ftp and download the MP4 files that are recorded whenever motion is detected.

I also want to be able to "trigger" the recording of an MP4 file at an arbitrary time (maybe on a schedule, or from some other external event that I can wire up via telnet later).

I thought of doing this via the existing processes that are already running on the camera, to keep it as non-invasive as possible, as I still want to use the camera in the "normal" way, to look at live video and play recorded video via the iPhone/android/native apps.

Through examining the running processes via ps, lsof and /proc, and looking at the log file (/tmp/log.txt) I have noticed a few things:

The main processes that run that seem to be of interest are:

(If anyone has any more info on the above or can make any corrections please do!)

By looking at lsof I can see that these processes are communicating via a number of POSIX mqueue message queues:

/tmp/sd # lsof |grep ipc
747     /home/app/dispatch      /ipc_dispatch
747     /home/app/dispatch      /ipc_rmm
747     /home/app/dispatch      /ipc_cloud
747     /home/app/dispatch      /ipc_p2p
747     /home/app/dispatch      /ipc_rcd
747     /home/app/dispatch      /ipc_rtmp
747     /home/app/dispatch      /ipc_dispatch_worker
923     /home/app/rmm   /ipc_dispatch
923     /home/app/rmm   /ipc_rmm
981     /home/app/mp4record     /ipc_dispatch
981     /home/app/mp4record     /ipc_rcd
1126    /home/app/cloud /ipc_dispatch
1126    /home/app/cloud /ipc_cloud
1129    /home/app/p2p_tnp       /ipc_dispatch
1134    /home/app/arp_test      /ipc_dispatch

However if you check these in /dev/mqueue they seem to be always empty e.g.:

/tmp/sd # cat /dev/mqueue/ipc_*
QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0     

I wondered if these were indeed being used to communicate between processes, so as an experiment I killed the mp4record process (and then the watch_process process to stop it from restarting it 👍 ).

Sure enough, when I waved my hand in front of the camera, the ipc_dispatch queue seemed to start to fill with messages.

I thought it would be interesting to examine the contents of these messages and observe exactly when they were being sent. There are no command line tools to interact with the POSIX message queues, so I wrote a small C program to dump the messages from a particular queue:

dump_mq.c
=========

#include <fcntl.h>
#include <mqueue.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>

#define QUEUE_NAME  "/test_queue"
#define MAX_SIZE    1024
#define MSG_STOP    "exit"

#define REWRITE_MESSAGE 0

void writelog(FILE *f, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    vfprintf(f, fmt, args);
    fflush(f);
    fflush(stdout);
    va_end(args);
}

void * queue_receiver(void *queuename) {
        mqd_t mq;
        ssize_t bytes_read;
        time_t ltime;
    char buffer[MAX_SIZE + 1];
        FILE *f;

    f = fopen("dump_mq.log", "a+");

    writelog(f, "SERVER: Openening queue '%s'\n", (char*) queuename);
    /* open the message queue */
    mq = mq_open((char*) queuename, O_RDWR); // | O_NONBLOCK);

    if (f == NULL) {
        printf("Error opening logfile\n");
        exit(1);
    }
    while(1) {

                memset(buffer, 0x00, sizeof(buffer));
        bytes_read = mq_receive(mq, buffer, MAX_SIZE, NULL);
        if(bytes_read >= 0) {
                        ltime=time(NULL);
                        char * timestr = asctime(localtime(&ltime));
                        timestr[strlen(timestr)-1] = '\0';
                        writelog(f, "%s SERVER: Received message len %d: '%s' (in hex:'", timestr, bytes_read, buffer);

                        int i;
                        for (i = 0; i < bytes_read; i++)
                        {
                            if (i > 0) writelog(f, ":");
                            writelog(f, "%02X", buffer[i]);
                        }
                        writelog(f, "')\n");

#if REWRITE_MESSAGE

                mq_send(mq, buffer, bytes_read, 0);
                usleep(0.1 * 1e6);
#endif

                } else {
                        writelog(f, "SERVER: None \n");
                }

                //usleep(0.725 * 1e6);
                //sleep(1);
    }

    /* cleanup */
    mq_close(mq);
    fclose(f);

    return NULL;
}

int main(int argc, char** argv) {

        pthread_t client, server;

        printf("Start...\n");

        pthread_create(&server, NULL, &queue_receiver, argv[1]);

        pthread_join(server, NULL);

        printf("Done...\n");

        return (EXIT_SUCCESS);
}

This compiles with the Hi3518E V100R001C01SPC0B0 SDK using the command: arm-hisiv100nptl-linux-gcc dump_mq.c -o dump_mq -lrt -lpthread

And then when FTP'd onto the camera, it seems to run fine and dumps out messages from the queue (specified on the command line) onto the console and also into a log file called dump_mq.log. This screws up the camera behaviour as other processes that were expecting to receive messages now don't receive them as they have been grabbed by the dump_mq process. I did experiment (as can be seen in the code) with grabbing the messages and then immediately writing them back onto the same queue again after they've been examined, and then waiting 0.1 seconds to ensure the same message isn't written back straight away, but that has the possibility of "missing" messages as other processes could get there first (which could happen anyway, but sleeping makes it more likely I suppose).

So far I've observed /ipc_rmm seems to get a message whenever there is a motion detection event. This message (hex dumped) comes in at the same time (roughly) as the log entry below:

/tmp/sd # ./dump_mq /ipc_rmm
Start...
SERVER: Openening queue '/ipc_rmm'
Mon Jul 16 22:23:25 2018 SERVER: Received message len 31: '' (in hex:'02:00:00:00:04:00:00:00:07:10:01:00:0F:00:00:00:2F:74:6D:70:2F:6D:6F:74:69:6F:6E:2E:6A:70:67')
[./rmm][7/16/22:23:24:949]: got a new motion start

And then /ipc_dispatch seems to contain pretty regular messages which I think may be heartbeats from processes like mp4record (looking at the log entries that show up at similar times):

/tmp/sd # ./dump_mq /ipc_dispatch
Start...
SERVER: Openening queue '/ipc_dispatch'
Mon Jul 16 22:26:23 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:26:34 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:26:44 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:26:45 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:01:00:00:00:0E:40:0E:40:00:00:00:00:00:00:00:00')
Mon Jul 16 22:26:54 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:26:59 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:10:00:00:00:E7:00:E7:00:00:00:00:00')
Mon Jul 16 22:27:04 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:27:14 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:27:18 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:01:00:00:00:0E:40:0E:40:00:00:00:00:00:00:00:00')
Mon Jul 16 22:27:24 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:27:34 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:27:44 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Mon Jul 16 22:27:49 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:10:00:00:00:E7:00:E7:00:00:00:00:00')
./dispatch][7/16/22:23:42:6]: got rcd heartbeat
[./mp4record][7/16/22:23:42:7]: msg snd success[./dispatch][7/16/22:23:59:45]: got rcd heartbeat
[./mp4record][7/16/22:23:59:46]: msg snd success[./mp4record][7/16/22:24:0:59]: sub stream record finish[./mp4record][7/16/22:24:0:121]: main stream record finish[./dispatch][7/16/22:24:0:138]: rcd cost 0
[./p2p_tnp][7/16/22:24:3:657]: got event(54)[./mp4record][7/16/22:24:16:133]: msg snd success[./dispatch][7/16/22:24:16:133]: got rcd heartbeat
[./dispatch][7/16/22:24:32:753]: got rcd heartbeat

This is as far as I've got. I'd like to be able to find out more about what all the processes are doing that cause them to send the messages and confirm my assumptions about what is causing various messages to be sent. I assume I could use strace for this but I'm not sure how to cross-compile it (I have the source code in a buildroot project but need to compile it using the Hi3518 SDK, not sure how to do this yet).

It would also be great if I had a way to understand what the contents of the messages means. They don't print nicely using printf (hence the empty quotemarks in the pasted messages above) so I've hex dumped them. The /ipc_rmm messages do contain the text '/tmp/motion.jpg' which could be a clue (I converted them using https://www.rapidtables.com/convert/number/hex-to-ascii.html ) but the /ipc_dispatch ones don't seem to contain anything immediately useful.

If I'm going to create "fake" messages to put onto one of the ipc queues to trigger a detected motion, I expect I need another message to also trigger the "end" of the motion, otherwise the mp4 recording will continue forever. I haven't yet observed any message that looks like an example of a 'motion stopped' message.

Hopefully someone can build on the above or suggest some next steps. In the meantime I'll keep playing and post any further updates here.

Finally if anyone has any suggestions for better ways to tackle this, in a nice non-invasive manner, that would be good. I had considered trying to directly capture an image from the sensor (along the lines of this: https://mark4h.blogspot.com/2017/09/hi3518-camera-module-part-3-capturing.html or this https://github.com/mark4h/HI3518-SC1035 or potentially this https://github.com/samtap/fang-hacks/issues/87 ) but I am nervous that because the Yi apps are still running, they would still have the sensor "open" and these other tools would be unable to access it, so I haven't put any effort into trying to get these to compile etc yet.

tm-robinson commented 6 years ago

Ok I have managed to get strace cross compiled and running using the SDK. This was the method I used that worked:

  1. Install the SDK, so that /opt/hisi-linux-nptl/arm-hisiv100-linux/target/bin contains all the compiler binaries e.g. arm-hisiv100nptl-linux-gcc
  2. Set cross compiler environment variables:
    export PATH=/opt/hisi-linux-nptl/arm-hisiv100-linux/target/bin:$PATH
    export ARCH=arm
    export CROSS_COMPILE=arm-hisiv100nptl-linux-

    (Not sure if these are required or not)

  3. Download buildroot and run the compile process on the website, with strace turned on, which downloads the strace source code.
  4. Locate the strace source code directory within buildroot (for me this was ~/buildroot-2018.05/output/build/strace-4.21)
  5. Run the configure command specifying the target host type so the SDK cross compiler is used: ./configure --host=arm-hisiv100nptl-linux
  6. Run make
  7. Copy the resultant strace binary to the camera using FTP
  8. Attach strace to one of the existing processes e.g. to rmm:
    /tmp/sd # ./strace -p 923
    ./strace: Process 923 attached
    nanosleep(0xfffffffc, 0xbef0caa0)       = 0
    open("/dev/isp_dev", O_RDONLY)          = 28
    ioctl(28, _IOC(_IOC_WRITE, 0x49, 0, 0x4), 0xbef0c234) = 0
    ioctl(28, _IOC(_IOC_WRITE, 0x49, 0xa, 0x4), 0xbef0c238) = 0
    ioctl(28, _IOC(_IOC_READ, 0x49, 0x9, 0x4), 0xbef0c23c) = 0
    mmap2(NULL, 16384, PROT_READ|PROT_WRITE, MAP_SHARED, 10, 0x81621000) = 0xb5f70000
    munmap(0xb5f70000, 16384)               = 0
    ioctl(28, _IOC(_IOC_WRITE, 0x49, 0xa, 0x4), 0xbef0c238) = 0
    close(28)                               = 0
    rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
    nanosleep({tv_sec=1, tv_nsec=0}, 0xbef0caa0) = 0
    open("/dev/isp_dev", O_RDONLY)          = 28
    ioctl(28, _IOC(_IOC_WRITE, 0x49, 0, 0x4), 0xbef0c234) = 0
    ioctl(28, _IOC(_IOC_WRITE, 0x49, 0xa, 0x4), 0xbef0c238) = 0
    ioctl(28, _IOC(_IOC_READ, 0x49, 0x9, 0x4), 0xbef0c23c) = 0
    mmap2(NULL, 16384, PROT_READ|PROT_WRITE, MAP_SHARED, 10, 0x81621000) = 0xb5f70000
    munmap(0xb5f70000, 16384)               = 0
    ioctl(28, _IOC(_IOC_WRITE, 0x49, 0xa, 0x4), 0xbef0c238) = 0
    close(28)                               = 0
    rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
    nanosleep({tv_sec=1, tv_nsec=0}, ^C./strace: Process 923 detached
    <detached ...>

Initial guess is that rmm is opening the camera device (/dev/isp_dev?), reading a still image (the ioctl and mmap2 commands), closing the camera device and then sleeping for 1 second. I assume it compares the still image with the previous still image to detect motion.

I am not sure how the output of this gets fed into other processes though, as from the strace output, rmm doesn't seem to be writing to any queues. However the dispatch process does receive data from the /ipc_dispatch queue including some of the messages mentioned in my previous post.

xarnze commented 6 years ago

@tm-robinson did you get any further with this, I'd like to see if it's possible to trigger the PTZ motors using the same method?

tm-robinson commented 6 years ago

@xarnze I haven't got much further, apart from a few attempts to compile and grab still images directly, firstly using v4l2grab (which compiles, using this toolchain: https://github.com/mark4h/hi35xx-buildroot but isn't able to open the device (I'm not sure which device to use, the possible choices all seem to give "inappropriate ioctl" errors)) and then using vi_bayerdump (at https://github.com/mark4h/HI3518-SC1035/tree/master/vi_bayerdump_SC1035 ) which fails with i2c errors.

I assume you are referring to the PTZ motors in the Yi Dome? I was actually attempting all of the above on a Yi Home. I do also own a Yi Dome but haven't tried to flash it yet, so haven't got as far as examining what additional IPC messages are being sent around on there (if any). I never really finished fully investigating the IPC messages on the Yi Home either, and never attempted to send any fake ones to see what would happen (although the dump_mq.c code above has all the calls necessary to try this). So that's the next thing on the list if I get more time to play with this.

If anyone has time to write a direct frame grabber using the strace output I posted above (with the mmap calls) that would be great, as I think this would probably work to grab still images from the sensor, as the rmm process seems to be doing it fairly frequently but it always closes the device after each grab. So I can't see why a second process performing its own frame grab wouldn't work.

xarnze commented 6 years ago

I've decompiled the rmm binary and it if you search for zbar_scan_image that looks like the code that gets the image, theres also a lot in there to do with sending mq messages could be worth a look anyway. rmm.asm.zip

xarnze commented 6 years ago

I've tried killing everything but p2p_tnp and controlling the motors, they stopped moving after killing dispatch but the /ipc_dispatch started filling up every time you pressed the buttons, the capture is below

On /ipc_dispatch

// Up
Sat Jul 28 09:53:11 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Sat Jul 28 09:53:11 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Sat Jul 28 09:53:11 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:01:00:00:00:0E:40:0E:40:00:00:00:00:01:00:00:00')
Sat Jul 28 09:53:11 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Sat Jul 28 09:53:12 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:E2:00:01:00:00:00:00:00')
Sat Jul 28 09:53:12 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:08:00:00:00:E4:00:01:00:04:00:00:00:00:00:00:00')
Sat Jul 28 09:53:12 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:E1:00:01:00:00:00:00:00')
Sat Jul 28 09:53:12 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:08:00:00:00:7F:00:01:00:04:00:00:00:05:00:00:00')
Sat Jul 28 09:53:12 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:08:00:00:00:80:00:01:00:04:00:00:00:01:00:00:00')
Sat Jul 28 09:53:13 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:8F:00:01:00:00:00:00:00')
Sat Jul 28 09:53:13 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:08:00:00:00:E3:00:01:00:04:00:00:00:01:00:00:00')
Sat Jul 28 09:53:18 2018 SERVER: Received message len 24: '' (in hex:'01:00:00:00:08:00:00:00:06:40:06:40:18:00:00:00:01:00:00:00:00:00:00:00')
Sat Jul 28 09:53:18 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:07:40:01:00:00:00:00:00')
Sat Jul 28 09:53:20 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Sat Jul 28 09:53:21 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:01:00:00:00:0E:40:0E:40:00:00:00:00:01:00:00:00')
// Down
Sat Jul 28 09:53:33 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Sat Jul 28 09:53:37 2018 SERVER: Received message len 24: '' (in hex:'01:00:00:00:08:00:00:00:06:40:06:40:18:00:00:00:02:00:00:00:00:00:00:00')
Sat Jul 28 09:53:37 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:07:40:01:00:00:00:00:00')
Sat Jul 28 09:53:40 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
// Left
Sat Jul 28 09:54:04 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:10:00:00:00:E7:00:E7:00:00:00:00:00')
Sat Jul 28 09:54:04 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Sat Jul 28 09:54:07 2018 SERVER: Received message len 24: '' (in hex:'01:00:00:00:08:00:00:00:06:40:06:40:18:00:00:00:03:00:00:00:00:00:00:00')
Sat Jul 28 09:54:07 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:07:40:01:00:00:00:00:00')
// Right
Sat Jul 28 09:53:51 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:81:00:01:00:00:00:00:00')
Sat Jul 28 09:53:53 2018 SERVER: Received message len 20: '' (in hex:'01:00:00:00:01:00:00:00:0E:40:0E:40:00:00:00:00:01:00:00:00')
Sat Jul 28 09:53:53 2018 SERVER: Received message len 24: '' (in hex:'01:00:00:00:08:00:00:00:06:40:06:40:18:00:00:00:04:00:00:00:00:00:00:00')
Sat Jul 28 09:53:53 2018 SERVER: Received message len 16: '' (in hex:'01:00:00:00:08:00:00:00:07:40:01:00:00:00:00:00')
xarnze commented 6 years ago

Next thing to try will be adding one of these messages to the queue and seeing if the camera moves

xarnze commented 6 years ago

Left

Start moving left by sending:

unsigned char MsgA[] = "0100000010000000E700E70000000000";
unsigned char MsgB[] = "01000000080000008100010000000000";

Stop moving left by sending:

unsigned char MsgC[] = "010000000800000006400640180000000300000000000000";
unsigned char MsgD[] = "01000000080000000740010000000000";

Right

Start moving right by sending:

unsigned char MsgA[] = "01000000080000008100010000000000";
unsigned char MsgB[] = "01000000010000000E400E400000000001000000";

Stop moving right by sending:

unsigned char MsgC[] = "010000000800000006400640180000000400000000000000";
unsigned char MsgD[] = "01000000080000000740010000000000";

Up

Still looking at this one but looks to need more messages than all the other movements.

Down

Start moving down by sending:

unsigned char downMsgA[] = "01000000080000008100010000000000";
unsigned char downMsgB[] = "010000000800000006400640180000000200000000000000";

Stop moving by sending:

unsigned char downMsgC[] = "01000000080000000740010000000000";
unsigned char downMsgD[] = "01000000080000008100010000000000";

This code moves the camera down a bit by sending the start moving mq packets above, to the /icp_dispatch queue:

#include <fcntl.h>
#include <mqueue.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>

#define QUEUE_NAME  "/test_queue"
#define MAX_SIZE    1024
#define MSG_STOP    "exit"

unsigned char HexChar (char c)
{
    if ('0' <= c && c <= '9') return (unsigned char)(c - '0');
    if ('A' <= c && c <= 'F') return (unsigned char)(c - 'A' + 10);
    if ('a' <= c && c <= 'f') return (unsigned char)(c - 'a' + 10);
    return 0xFF;
}

int HexToBin (const char* s, unsigned char * buff, int length)
{
    int result;
    if (!s || !buff || length <= 0) return -1;

    for (result = 0; *s; ++result)
    {
        unsigned char msn = HexChar(*s++);
        if (msn == 0xFF) return -1;
        unsigned char lsn = HexChar(*s++);
        if (lsn == 0xFF) return -1;
        unsigned char bin = (msn << 4) + lsn;

        if (length-- <= 0) return -1;
        *buff++ = bin;
    }
    return result;
}

void BinToHex (const unsigned char * buff, int length, char * output, int outLength)
{
    char binHex[] = "0123456789ABCDEF";

    if (!output || outLength < 4) return;
    *output = '\0';

    if (!buff || length <= 0 || outLength <= 2 * length)
    {
        memcpy(output, "ERR", 4);
        return;
    }

    for (; length > 0; --length, outLength -= 2)
    {
        unsigned char byte = *buff++;

        *output++ = binHex[(byte >> 4) & 0x0F];
        *output++ = binHex[byte & 0x0F];
    }
    if (outLength-- <= 0) return;
    *output++ = '\0';
}

void writelog(FILE *f, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    vfprintf(f, fmt, args);
    fflush(f);
    fflush(stdout);
    va_end(args);
}

void * queue_receiver(void *queuename) {
        mqd_t mq;
        ssize_t bytes_read;
        time_t ltime;
    unsigned char buffer[MAX_SIZE + 1];
        FILE *f;

    f = fopen("dump_mq.log", "a+");

    writelog(f, "SERVER: Openening queue '%s'\n", (char*) queuename);
    /* open the message queue */
    mq = mq_open((char*) queuename, O_RDWR); // | O_NONBLOCK);

    if (f == NULL) {
        printf("Error opening logfile\n");
        exit(1);
    }
    memset(buffer, 0x00, sizeof(buffer));
    unsigned char downMsgA[] = "01000000080000008100010000000000";
    unsigned char downMsgB[] = "010000000800000006400640180000000200000000000000";
    unsigned char downMsgC[] = "01000000080000000740010000000000";
    unsigned char downMsgD[] = "01000000080000008100010000000000";
    writelog(f, "SERVER: Sending A \n");
    HexToBin(downMsgA, (unsigned char*)buffer, 32);
    mq_send(mq, buffer, 24, 0);
    usleep(0.1 * 1e6);
    writelog(f, "SERVER: Sending B \n");
    HexToBin(downMsgB, (unsigned char*)buffer, 48);
    mq_send(mq, buffer, 24, 0);
    usleep(0.3 * 1e6);
    writelog(f, "SERVER: Sent move \n");
    writelog(f, "SERVER: Sending C \n");
    HexToBin(downMsgC, (unsigned char*)buffer, 32);
    mq_send(mq, buffer, 24, 0);
    usleep(0.1 * 1e6);
    writelog(f, "SERVER: Sending D \n");
    HexToBin(downMsgD, (unsigned char*)buffer, 32);
    mq_send(mq, buffer, 24, 0);
    usleep(0.1 * 1e6);
    writelog(f, "SERVER: Sent stop move \n");

    /* cleanup */
    mq_close(mq);
    fclose(f);

    return NULL;
}

int main(int argc, char** argv) {

        pthread_t client, server;

        printf("Start...\n");

        pthread_create(&server, NULL, &queue_receiver, argv[1]);

        pthread_join(server, NULL);

        printf("Done...\n");

        return (EXIT_SUCCESS);
}
au-arms commented 6 years ago

@tm-robinson How did you snag the arm-hisiv100nptl-linux-gcc compiler? I've got the SDK and unpacked it as shadow-1 outlined in #23, but I'm having a hard time accessing that compiler and id love to be able to play around with motion triggering on the Yi dome.

au-arms commented 6 years ago

@tm-robinson Ive made some progress that you may be excited to hear about! to trigger video recording, you must send a message to the /ipc_dispatch while blocking messages through the /ipc_rmm queue (*this does not seem necessary). To stop recording, you must send another message to the /ipc_dispatch queue. How I achieved this was via running your listening script on /ipc_rmm queue, then running @xarnze 's injection version with the following replacement:

        unsigned char startMsg[] = "01000000020000007C007C0000000000";
    unsigned char endMsg[] = "01000000020000007D007D0000000000";
    writelog(f, "SERVER: start \n");
    HexToBin(startMsg, (unsigned char*)buffer, 32);
    mq_send(mq, buffer, 16, 0);
    usleep(100 * 1e6);
    writelog(f, "SERVER: stop \n");
    HexToBin(endMsg, (unsigned char*)buffer, 32);
    mq_send(mq, buffer, 16, 0);
    usleep(0.1 * 1e6);

in place of the movement messages. If you have an SD card that can save the motion video (for example, a SD card formatted by the YI, cards with the initial hack dont seem to record video) you will then find non-motion triggered mp4's sitting prime and pretty in the sd card. I'm working on a script to automate this and curl short videos from the camera on regular intervals, I'll throw it up here when it's done.

*i figured out the compiler also, accidentally skipped a few steps in the MAKE of the sdk.

dvv commented 6 years ago

I've built luajit.gz which may speed/ease the development. E.g. luajit ipc.lua:

--[[
    Copyright 2018 Vladimir Dronnikov
    GPL
]]

local ffi = require "ffi"

ffi.cdef[[
int printf(const char *fmt, ...);
void perror(const char *s);

void usleep(unsigned long usec);

int open(const char *pathname, int flags);
int read(int fd, const void *buf, unsigned count);
int write(int fd, const void *buf, unsigned count);
int close(int fd);
char *mmap (void *start, unsigned int length, int prot, int flags, int fd, unsigned int offset);
char *mmap2(void *start, unsigned int length, int prot, int flags, int fd, unsigned int pgoffset);
int munmap(void *start, unsigned int length);

char *memmem(const void *haystack, unsigned int haystacklen, const void *needle, unsigned int needlelen);
int memcmp(const void *s1, const void *s2, unsigned int n);

int mq_open(const char *name, int oflag);
int mq_send(int mqdes, const void *msg_ptr, unsigned int msg_len, unsigned msg_prio);
int mq_receive(int mqdes, const void *msg_ptr, unsigned int msg_len, unsigned *msg_prio);
]]

local MQ = ffi.load("/home/lib/librt-0.9.33.2.so")
local mq = MQ.mq_open(arg[1] or "/ipc_dispatch", 0x02)
print(mq, ffi.C.perror("mq_open"))
local buf = ("-"):rep(1024)

local startmsg = "\x01\x00\x00\x00\x02\x00\x00\x00\x7C\x00\x7C\x00\x00\x00\x00\x00"
local endmsg = "\x01\x00\x00\x00\x02\x00\x00\x00\x7D\x00\x7D\x00\x00\x00\x00\x00"

--[[
-- start
MQ.mq_send(mq, startmsg, #startmsg, 0)
-- wait
ffi.C.usleep(2000000)
-- stop
MQ.mq_send(mq, endmsg, #endmsg, 0)
]]

-- dump
while true do
    -- local len = ffi.C.read(mq, buf, #buf)
    local len = MQ.mq_receive(mq, buf, #buf, nil)
    if len == -1 then
        print(len, ffi.C.perror("read"))
        break
    end
    if len > 0 then
        io.stdout:write(("%5d "):format(len))
        for i = 1, len do
            io.stdout:write(("%02x"):format(buf:byte(i)))
        end
        io.stdout:write("\n")
    end
    ffi.C.usleep(200000)
end
dvv commented 6 years ago

On camera

$ tcpsvd -l 0 -v 0.0.0.0 4445 /tmp/sd/luajit /tmp/sd/ipc_dispatch.lua

--[[
    Copyright 2018 Vladimir Dronnikov
    GPL
]]

local ffi = require "ffi"

ffi.cdef[[
int printf(const char *fmt, ...);
void perror(const char *s);

void usleep(unsigned long usec);

int open(const char *pathname, int flags);
int read(int fd, const void *buf, unsigned count);
int write(int fd, const void *buf, unsigned count);
int close(int fd);
char *mmap (void *start, unsigned int length, int prot, int flags, int fd, unsigned int offset);
char *mmap2(void *start, unsigned int length, int prot, int flags, int fd, unsigned int pgoffset);
int munmap(void *start, unsigned int length);

char *memmem(const void *haystack, unsigned int haystacklen, const void *needle, unsigned int needlelen);
int memcmp(const void *s1, const void *s2, unsigned int n);

int mq_open(const char *name, int oflag);
int mq_send(int mqdes, const void *msg_ptr, unsigned int msg_len, unsigned msg_prio);
int mq_receive(int mqdes, const void *msg_ptr, unsigned int msg_len, unsigned *msg_prio);
]]

-- open queue
local MQ = ffi.load("/home/lib/librt-0.9.33.2.so")
local mq = MQ.mq_open("/ipc_dispatch", 0x02)
assert(mq ~= -1, ffi.C.perror("mq_open"))

-- helpers
function string.fromhex(str)
    return (str:gsub("..", function(cc)
        return string.char(tonumber(cc, 16))
    end))
end
function string.tohex(str)
    return (str:gsub(".", function (c)
        return string.format("%02X", string.byte(c))
    end))
end

-- read hex string input, write bin to queue
while true do
    local line = io.read("*line")
    if line == nil then break end
    local bin = line:fromhex()
    MQ.mq_send(mq, bin, #bin, 0)
end

On controller