V3.0 with servo control and presets
Control servos using hardware PWM GPIOs or other GPIOs using ServoBlaster. Presets group motion detect counts/limits and motion regions into settings presets and servo positions into position presets. New files preset.c and servo.c www/state link to /run/pikrellcam/state Single videos link on index.php Fix timelapse stills to not create zero sized jpegs. Motion regions save/load by name usage changed to backup roll for presets.
|
@ -100,6 +100,46 @@ slist_prepend(SList *list, void *data)
|
|||
return new_list;
|
||||
}
|
||||
|
||||
SList *
|
||||
slist_insert(SList *list, void *data, int position)
|
||||
{
|
||||
SList *prev_list,
|
||||
*tmp_list,
|
||||
*new_list;
|
||||
|
||||
if (position < 0)
|
||||
return slist_append(list, data);
|
||||
else if (position == 0)
|
||||
return slist_prepend(list, data);
|
||||
|
||||
new_list = calloc(1, sizeof(SList));
|
||||
new_list->data = data;
|
||||
|
||||
if (!list)
|
||||
return new_list;
|
||||
|
||||
prev_list = NULL;
|
||||
tmp_list = list;
|
||||
|
||||
while ((position-- > 0) && tmp_list)
|
||||
{
|
||||
prev_list = tmp_list;
|
||||
tmp_list = tmp_list->next;
|
||||
}
|
||||
|
||||
if (prev_list)
|
||||
{
|
||||
new_list->next = prev_list->next;
|
||||
prev_list->next = new_list;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_list->next = list;
|
||||
list = new_list;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
SList *
|
||||
slist_remove(SList *list, void *data)
|
||||
{
|
||||
|
@ -166,6 +206,22 @@ slist_length(SList *list)
|
|||
return length;
|
||||
}
|
||||
|
||||
int
|
||||
slist_index(SList *list, void *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while (list)
|
||||
{
|
||||
if (list->data == data)
|
||||
return i;
|
||||
i++;
|
||||
list = list->next;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
SList *
|
||||
slist_remove_link(SList *list, SList *link)
|
||||
{
|
||||
|
@ -189,3 +245,50 @@ slist_remove_link(SList *list, SList *link)
|
|||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
SList *
|
||||
slist_insert_sorted(SList *list, void *data,
|
||||
int func(void *data1, void *data2))
|
||||
{
|
||||
SList *tmp_list = list,
|
||||
*prev_list = NULL,
|
||||
*new_list;
|
||||
int cmp;
|
||||
|
||||
if (!list)
|
||||
{
|
||||
new_list = calloc(1, sizeof(SList));
|
||||
new_list->data = data;
|
||||
return new_list;
|
||||
}
|
||||
|
||||
cmp = (*func)(data, tmp_list->data);
|
||||
|
||||
while ((tmp_list->next) && (cmp > 0))
|
||||
{
|
||||
prev_list = tmp_list;
|
||||
tmp_list = tmp_list->next;
|
||||
cmp = (*func)(data, tmp_list->data);
|
||||
}
|
||||
|
||||
new_list = calloc(1, sizeof(SList));
|
||||
new_list->data = data;
|
||||
|
||||
if ((!tmp_list->next) && (cmp > 0))
|
||||
{
|
||||
tmp_list->next = new_list;
|
||||
return list;
|
||||
}
|
||||
|
||||
if (prev_list)
|
||||
{
|
||||
prev_list->next = new_list;
|
||||
new_list->next = tmp_list;
|
||||
return list;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_list->next = list;
|
||||
return new_list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,11 @@ SList *slist_remove_link(SList *list, SList *link);
|
|||
SList *slist_nth(SList *list, int n);
|
||||
void *slist_nth_data(SList *list, int n);
|
||||
SList *slist_find(SList *list, void *data);
|
||||
int slist_index(SList *list, void *data);
|
||||
int slist_length(SList *list);
|
||||
SList *slist_insert(SList *list, void *data, int position);
|
||||
SList *slist_insert_sorted(SList *list, void *data,
|
||||
int func(void *data1, void *data2));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
BIN
pikrellcam
|
@ -19,6 +19,9 @@ MEDIA_DIR=$3
|
|||
MJPEG_FILE=$4
|
||||
FIFO_FILE=$5
|
||||
LOG_FILE=$6
|
||||
SERVOS_ENABLE=$7
|
||||
|
||||
STATE_FILE=/run/pikrellcam/state
|
||||
|
||||
if [ "$LOG_FILE" == "" ]
|
||||
then
|
||||
|
@ -33,8 +36,15 @@ WWW_CONFIG=$INSTALL_DIR/www/config.php
|
|||
PIKRELLCAM=$INSTALL_DIR/pikrellcam
|
||||
ARCHIVE_LINK=www/archive
|
||||
MEDIA_LINK=www/media
|
||||
STATE_LINK=www/state
|
||||
VERSION=`pikrellcam --version`
|
||||
|
||||
if [ ! -h $STATE_LINK ]
|
||||
then
|
||||
echo " making $STATE_LINK link to $STATE_FILE" >> $LOG_FILE
|
||||
ln -s $STATE_FILE $STATE_LINK
|
||||
fi
|
||||
|
||||
if [ ! -h $MEDIA_LINK ]
|
||||
then
|
||||
echo " making $MEDIA_LINK link to $MEDIA_DIR" >> $LOG_FILE
|
||||
|
@ -103,6 +113,15 @@ else
|
|||
echo " $WWW_CONFIG: PIKRELLCAM not changed." >> $LOG_FILE
|
||||
fi
|
||||
|
||||
if ! grep -q $SERVOS_ENABLE $WWW_CONFIG
|
||||
then
|
||||
CMD="/SERVOS_ENABLE/c\ define\(\"SERVOS_ENABLE\", \"$SERVOS_ENABLE\"\);"
|
||||
sed -i "$CMD" $WWW_CONFIG
|
||||
echo " $WWW_CONFIG: SERVOS_ENABLE updated to: $SERVOS_ENABLE" >> $LOG_FILE
|
||||
else
|
||||
echo " $WWW_CONFIG: SERVOS_ENABLE not changed." >> $LOG_FILE
|
||||
fi
|
||||
|
||||
if ! fgrep -q $VERSION $WWW_CONFIG
|
||||
then
|
||||
CMD="/VERSION/c\ define\(\"VERSION\", \"$VERSION\"\);"
|
||||
|
|
|
@ -23,7 +23,8 @@ MMAL_LIB ?= -L/opt/vc/lib -lbcm_host -lvcos -lmmal -lmmal_core -lmmal_util \
|
|||
FLAGS = -O2 -Wall $(MMAL_INCLUDE) $(INCLUDES)
|
||||
LIBS = $(MMAL_LIB) -lm -lpthread
|
||||
|
||||
LOCAL_SRC = pikrellcam.c mmalcam.c motion.c event.c display.c config.c sunriset.c tcpserver.c tcpserver.c tcpserver_mjpeg.c
|
||||
LOCAL_SRC = pikrellcam.c mmalcam.c motion.c event.c display.c config.c servo.c \
|
||||
preset.c sunriset.c tcpserver.c tcpserver.c tcpserver_mjpeg.c
|
||||
|
||||
KRELLMLIB_SRC = $(wildcard $(addsuffix /*.c,$(LIBKRELLM_DIRS)))
|
||||
SOURCES = $(LOCAL_SRC) $(KRELLMLIB_SRC)
|
||||
|
|
163
src/config.c
|
@ -1,6 +1,6 @@
|
|||
/* PiKrellCam
|
||||
|
|
||||
| Copyright (C) 2015 Bill Wilson billw@gkrellm.net
|
||||
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
|
||||
|
|
||||
| PiKrellCam is free software: you can redistribute it and/or modify it
|
||||
| under the terms of the GNU General Public License as published by
|
||||
|
@ -512,7 +512,7 @@ config_value_int_set(char *arg, ConfigResult *result)
|
|||
{
|
||||
int valid = TRUE;
|
||||
|
||||
if (isdigit(*arg))
|
||||
if (isdigit(*arg) || (*arg == '-' && isdigit((*(arg + 1)))))
|
||||
*result->value = atoi(arg);
|
||||
else
|
||||
{
|
||||
|
@ -533,15 +533,6 @@ static Config config[] =
|
|||
"#",
|
||||
"install_dir", "/home/pi/pikrellcam", TRUE, { .string = &pikrellcam.install_dir }, config_string_set },
|
||||
|
||||
{ "# Directory for the stream mjpeg file and info file. These files\n"
|
||||
"# are frequently updated so this directory should be in a tmpfs.\n"
|
||||
"# This could be a directory in /tmp if your /tmp is a tmpfs.\n"
|
||||
"# Avoid putting this directory under /run/shm or /dev/shm because these\n"
|
||||
"# directories are subject to automatic cleanups which could delete the\n"
|
||||
"# tmpfs_dir out from under a running pikrellcam if running headless.\n"
|
||||
"#",
|
||||
"tmpfs_dir", "/run/pikrellcam", TRUE, { .string = &pikrellcam.tmpfs_dir }, config_string_set },
|
||||
|
||||
{ "# If media_dir has no leading '/' it will be a sub directory in install_dir.\n"
|
||||
"# Otherwise it is a full pathname to the media directory.\n"
|
||||
"# So the default media dir is /home/pi/pikrellcam/media.\n"
|
||||
|
@ -568,7 +559,7 @@ static Config config[] =
|
|||
{ "# At startup and at each new day, trim the log file number of lines\n"
|
||||
"# to log_lines. If log_lines is 0 the log file is not trimmed.\n"
|
||||
"#",
|
||||
"log_lines", "1000", FALSE, {.value = &pikrellcam.log_lines}, config_value_int_set},
|
||||
"log_lines", "500", FALSE, {.value = &pikrellcam.log_lines}, config_value_int_set},
|
||||
|
||||
{ "# Command to run at PiKrellCam startup. This is run after the config\n"
|
||||
"# files are loaded but before the camera is started or directories\n"
|
||||
|
@ -582,28 +573,24 @@ static Config config[] =
|
|||
|
||||
|
||||
{ "\n# -------------------- Motion Detect Options -----------------------\n"
|
||||
"# PiKrellCam V3.0 stores some motion detect settings in preset-xxx.conf\n"
|
||||
"# Vector and burst limits/counts are no longer saved in pikrellcam.conf.\n"
|
||||
"#\n"
|
||||
"# Enable pikrellcam motion detection at startup\n"
|
||||
"#",
|
||||
"motion_enable", "off", FALSE, {.value = &pikrellcam.motion_enable}, config_value_bool_set},
|
||||
|
||||
{ "# Motion vectors must be at least this magnitude for a motion detect.\n"
|
||||
"# Minimum is 3 for detecting the slowest moving objects possible.\n"
|
||||
{ "# If off, do not detect motion when servos are off a preset.\n"
|
||||
"#",
|
||||
"motion_off_preset", "off", FALSE, {.value = &pikrellcam.motion_off_preset}, config_value_bool_set},
|
||||
|
||||
{ "#OBSOLETE",
|
||||
"motion_magnitude_limit", "5", FALSE, {.value = &pikrellcam.motion_magnitude_limit}, config_value_int_set},
|
||||
|
||||
{ "# The count of vectors required for a motion detect.\n"
|
||||
"# Minimum is 2 for detecting the smallest objects possible.\n"
|
||||
"#",
|
||||
{ "#OBSOLETE",
|
||||
"motion_magnitude_limit_count", "4", FALSE, {.value = &pikrellcam.motion_magnitude_limit_count}, config_value_int_set},
|
||||
|
||||
{ "# Motion vector count minimum for a burst motion detect.\n"
|
||||
"# For large/close object detection.\n"
|
||||
"#",
|
||||
{ "#OBSOLETE",
|
||||
"motion_burst_count", "400", FALSE, {.value = &pikrellcam.motion_burst_count}, config_value_int_set},
|
||||
|
||||
{ "# The number of sustained frames for a burst motion detect.\n"
|
||||
"#",
|
||||
{ "#OBSOLETE",
|
||||
"motion_burst_frames", "3", FALSE, {.value = &pikrellcam.motion_burst_frames}, config_value_int_set},
|
||||
|
||||
{ "# Time length limit of motion video record excluding pre_capture time.\n"
|
||||
|
@ -686,12 +673,16 @@ static Config config[] =
|
|||
"#",
|
||||
"motion_preview_clean", "on", FALSE, {.value = &pikrellcam.motion_preview_clean}, config_value_bool_set },
|
||||
|
||||
{ "# If on, show extra vector count data on the OSD when presets are shown.\n"
|
||||
"#",
|
||||
"motion_show_counts", "off", FALSE, {.value = &pikrellcam.motion_show_counts}, config_value_bool_set },
|
||||
|
||||
{ "# Minimum width and height in pixels for the substitution width and height\n"
|
||||
"# variables for motion detect areas in the preview jpeg.\n"
|
||||
"# This minimum helps with possible frame skew for smaller relatively\n"
|
||||
"# faster moving objects.\n"
|
||||
"#",
|
||||
"motion_area_min_side", "60", FALSE, {.value = &pikrellcam.motion_area_min_side}, config_value_int_set },
|
||||
"motion_area_min_side", "80", FALSE, {.value = &pikrellcam.motion_area_min_side}, config_value_int_set },
|
||||
|
||||
{ "# Enable writing a motion statistics .csv file for each motion video.\n"
|
||||
"# For users who have a need for advanced video post processing.\n"
|
||||
|
@ -774,10 +765,11 @@ static Config config[] =
|
|||
"video_fps", "24", FALSE, {.value = &pikrellcam.camera_adjust.video_fps}, config_value_int_set },
|
||||
|
||||
{ "# MP4Box output frames per second if video filename is a .mp4\n"
|
||||
"# If this is different from video_fps, the final mp4 will be a\n"
|
||||
"# slow or fast motion video.\n"
|
||||
"# If this is non zero and different from video_fps, the final mp4 will\n"
|
||||
"# be a slow or fast motion video.\n"
|
||||
"# Normally leave this set to zero so it will track video_fps.\n"
|
||||
"#",
|
||||
"video_mp4box_fps", "24", FALSE, {.value = &pikrellcam.camera_adjust.video_mp4box_fps}, config_value_int_set },
|
||||
"video_mp4box_fps", "0", FALSE, {.value = &pikrellcam.camera_adjust.video_mp4box_fps}, config_value_int_set },
|
||||
|
||||
{ "# Video bitrate affects the quality and size of a video recording.\n"
|
||||
"# Along with pre_capture and event_gap times, it also determines the\n"
|
||||
|
@ -790,13 +782,18 @@ static Config config[] =
|
|||
"video_bitrate", "6000000", TRUE, {.value = &pikrellcam.camera_adjust.video_bitrate}, config_value_int_set },
|
||||
|
||||
{ "# Pixel width of the stream jpeg file. Aspect ratio is determined by the video.\n"
|
||||
"# This value will be rounded off to be a multiple of 16. If you want\n"
|
||||
"# a larger image but not increase bandwith usage, try a mjpeg_width of\n"
|
||||
"# 1024 with a mjpeg_quality 10.\n"
|
||||
"#",
|
||||
"mjpeg_width", "640", TRUE, {.value = &pikrellcam.mjpeg_width}, config_value_int_set },
|
||||
"mjpeg_width", "800", TRUE, {.value = &pikrellcam.mjpeg_width}, config_value_int_set },
|
||||
|
||||
{ "# Quality factor (up to 100) affects the quality and size of the stream jpeg.\n"
|
||||
"# Set this lower if you need to reduce the stream bandwidth.\n"
|
||||
"# Set this lower if you need to reduce the stream bandwidth. The value\n"
|
||||
"# is not the same as quality factors in other jpeg programs and should\n"
|
||||
"# be set lower than those programs.\n"
|
||||
"#",
|
||||
"mjpeg_quality", "14", TRUE, {.value = &pikrellcam.mjpeg_quality}, config_value_int_set },
|
||||
"mjpeg_quality", "10", TRUE, {.value = &pikrellcam.mjpeg_quality}, config_value_int_set },
|
||||
|
||||
{ "# Divide the video_fps by this to get the stream jpeg file update rate.\n"
|
||||
"# This will also be the motion frame check rate for motion detection.\n"
|
||||
|
@ -835,7 +832,7 @@ static Config config[] =
|
|||
|
||||
{ "# This quality factor affects the size and quality of still captures.\n"
|
||||
"#",
|
||||
"still_quality", "30", TRUE, {.value = &pikrellcam.camera_adjust.still_quality}, config_value_int_set },
|
||||
"still_quality", "14", TRUE, {.value = &pikrellcam.camera_adjust.still_quality}, config_value_int_set },
|
||||
|
||||
{ "# Command to run after a still capture.\n"
|
||||
"# email the still somewhere with:\n"
|
||||
|
@ -874,6 +871,74 @@ static Config config[] =
|
|||
{.string = &pikrellcam.timelapse_convert_cmd}, config_string_set },
|
||||
|
||||
|
||||
{ "\n# ------------------- Servo/Preset Options -----------------------\n"
|
||||
"#\n"
|
||||
"# PiKrellCam can use internal hardware PWM code to drive servos and for\n"
|
||||
"# this there is no extra install required.\n"
|
||||
"# For hardware PWM, the pan/tilt or tilt/pan gpio pairs must be one of\n"
|
||||
"# 12,13 12,19 18,13 18,19\n"
|
||||
"# and these are Pi hardware gpio header pin numbers.\n"
|
||||
"# \n"
|
||||
"# Or, PiKrellCam can use ServoBlaster and will then send servo commands\n"
|
||||
"# to /dev/servoblaster. But for this, a separate ServoBlaster install\n"
|
||||
"# and configuration to run is required.\n"
|
||||
"# For ServoBlaster, the PiKrellCam servo pan/tilt gpio values should not\n"
|
||||
"# be Pi header gpio numbers but instead should be one of the ServoBlaster\n"
|
||||
"# documented servo numbers 0 - 7. See ServoBlaster documentation.\n"
|
||||
"# \n"
|
||||
"# Leave the gpios at -1 if not using servos.\n"
|
||||
"#",
|
||||
"servo_pan_gpio", "-1", FALSE, {.value = &pikrellcam.servo_pan_gpio}, config_value_int_set },
|
||||
|
||||
{ "#",
|
||||
"servo_tilt_gpio", "-1", FALSE, {.value = &pikrellcam.servo_tilt_gpio}, config_value_int_set },
|
||||
|
||||
{ "# Set to true to use ServoBlaster for servos. A separate install of\n"
|
||||
"# ServoBlaster will be required.\n"
|
||||
"#",
|
||||
"servo_use_servoblaster", "false", TRUE, {.value = &pikrellcam.servo_use_servoblaster }, config_value_bool_set },
|
||||
|
||||
{ "# pan/tilt min/max values are best set using the web OSD.\n"
|
||||
"# The value units are 0.01 msec, so for example, a servo_pan_min of\n"
|
||||
"# 120 would limit the pan servo control pulse to a 1.2 msec minimum.\n"
|
||||
"#",
|
||||
"servo_pan_min", "120", FALSE, {.value = &pikrellcam.servo_pan_min}, config_value_int_set },
|
||||
|
||||
{ "#",
|
||||
"servo_pan_max", "180", FALSE, {.value = &pikrellcam.servo_pan_max}, config_value_int_set },
|
||||
|
||||
{ "#",
|
||||
"servo_tilt_min", "130", FALSE, {.value = &pikrellcam.servo_tilt_min}, config_value_int_set },
|
||||
|
||||
{ "#",
|
||||
"servo_tilt_max", "170", FALSE, {.value = &pikrellcam.servo_tilt_max}, config_value_int_set },
|
||||
|
||||
{ "# Set invert values to on if the servo turns the wrong way.\n"
|
||||
"#",
|
||||
"servo_pan_invert", "off", FALSE, {.value = &pikrellcam.servo_pan_invert}, config_value_bool_set },
|
||||
|
||||
{ "#",
|
||||
"servo_tilt_invert", "off", FALSE, {.value = &pikrellcam.servo_tilt_invert}, config_value_bool_set },
|
||||
|
||||
{ "# Servo moves have three modes: move by one step, by servo_move_steps,\n"
|
||||
"# or move continuous until stopped or a min/max limit is reached.\n"
|
||||
"# Set here the number of steps wanted for the second mode.\n"
|
||||
"#",
|
||||
"servo_move_steps", "10", FALSE, {.value = &pikrellcam.servo_move_steps }, config_value_int_set },
|
||||
|
||||
{ "# Delay in msec between servo pulse width steps for servo move commands.\n"
|
||||
"#",
|
||||
"servo_move_step_msec", "40", FALSE, {.value = &pikrellcam.servo_move_step_msec }, config_value_int_set },
|
||||
|
||||
{ "# Delay in msec between servo pulse width steps when going to a preset.\n"
|
||||
"#",
|
||||
"servo_preset_step_msec", "20", FALSE, {.value = &pikrellcam.servo_preset_step_msec }, config_value_int_set },
|
||||
|
||||
{ "# Delay in msec after servo stops moving before enabling motion detection.\n"
|
||||
"#",
|
||||
"servo_settle_msec", "600", FALSE, {.value = &pikrellcam.servo_settle_msec }, config_value_int_set },
|
||||
|
||||
|
||||
{ "\n# ------------------- Miscellaneous Options -----------------------\n"
|
||||
"#\n"
|
||||
"# How long in seconds a notify string should stay on the stream jpeg file.\n"
|
||||
|
@ -926,7 +991,7 @@ static Config config[] =
|
|||
|
||||
{ "# Annotate text size. Range: integer from 6 - 160\n"
|
||||
"#",
|
||||
"annotate_text_size", "32", FALSE, {.value = &pikrellcam.annotate_text_size }, config_value_int_set },
|
||||
"annotate_text_size", "40", FALSE, {.value = &pikrellcam.annotate_text_size }, config_value_int_set },
|
||||
};
|
||||
|
||||
#define CONFIG_SIZE (sizeof(config) / sizeof(Config))
|
||||
|
@ -959,11 +1024,10 @@ config_set_option(char *opt, char *arg, boolean set_safe)
|
|||
}
|
||||
|
||||
void
|
||||
config_set_defaults(void)
|
||||
config_set_defaults(char *home_dir)
|
||||
{
|
||||
CameraParameter *param;
|
||||
Config *cfg;
|
||||
char *home_dir;
|
||||
boolean valid;
|
||||
|
||||
/* Camera parameter table and pikrellcam config table have initial value pointers
|
||||
|
@ -992,8 +1056,10 @@ config_set_defaults(void)
|
|||
/* If pikrellcam started by rc.local or web page, need to get correct
|
||||
| home directory. Makefile does a setuid/setgid on executable.
|
||||
*/
|
||||
if (!home_dir)
|
||||
home_dir = getpwuid(geteuid())->pw_dir;
|
||||
asprintf(&pikrellcam.config_dir, "%s/%s", home_dir, PIKRELLCAM_CONFIG_DIR);
|
||||
pikrellcam.tmpfs_dir = strdup("/run/pikrellcam");
|
||||
|
||||
if (make_directory(pikrellcam.config_dir))
|
||||
{
|
||||
|
@ -1014,7 +1080,7 @@ config_set_defaults(void)
|
|||
motion_command("add_region 0.266 0.159 0.233 0.756");
|
||||
motion_command("add_region 0.500 0.150 0.233 0.750");
|
||||
motion_command("add_region 0.734 0.156 0.224 0.753");
|
||||
motion_frame.show_regions = FALSE;
|
||||
motion_frame.show_preset = FALSE;
|
||||
}
|
||||
|
||||
boolean
|
||||
|
@ -1028,7 +1094,7 @@ config_load(char *config_file)
|
|||
if ((f = fopen(config_file, "r")) == NULL)
|
||||
return FALSE;
|
||||
|
||||
pikrellcam.config_sequence_new = 15;
|
||||
pikrellcam.config_sequence_new = 31;
|
||||
|
||||
while (fgets(linebuf, sizeof(linebuf), f))
|
||||
{
|
||||
|
@ -1048,6 +1114,9 @@ config_load(char *config_file)
|
|||
}
|
||||
fclose(f);
|
||||
|
||||
/* Round off mjpeg_width to multiple of 16 */
|
||||
pikrellcam.mjpeg_width = (pikrellcam.mjpeg_width + 8) & ~0xf;
|
||||
|
||||
if (pikrellcam.motion_magnitude_limit < 3)
|
||||
pikrellcam.motion_magnitude_limit = 3;
|
||||
if (pikrellcam.motion_magnitude_limit_count < 2)
|
||||
|
@ -1085,6 +1154,22 @@ config_load(char *config_file)
|
|||
camera_adjust_temp = pikrellcam.camera_adjust;
|
||||
motion_times_temp = pikrellcam.motion_times;
|
||||
|
||||
if ( (pikrellcam.servo_use_servoblaster && pikrellcam.servo_pan_gpio >= 0)
|
||||
|| ( !pikrellcam.servo_use_servoblaster
|
||||
&& ( pikrellcam.servo_pan_gpio == 12 || pikrellcam.servo_pan_gpio == 13
|
||||
|| pikrellcam.servo_pan_gpio == 18 || pikrellcam.servo_pan_gpio == 19
|
||||
)
|
||||
)
|
||||
)
|
||||
pikrellcam.have_servos = TRUE;
|
||||
|
||||
asprintf(&pikrellcam.preset_config_file, "%s/preset-%s.conf",
|
||||
pikrellcam.config_dir,
|
||||
pikrellcam.have_servos ? "servos" : "no-servos");
|
||||
asprintf(&pikrellcam.preset_state_file, "%s/preset-%s.state",
|
||||
pikrellcam.config_dir,
|
||||
pikrellcam.have_servos ? "servos" : "no-servos");
|
||||
|
||||
if (pikrellcam.config_sequence_new != pikrellcam.config_sequence)
|
||||
{
|
||||
pikrellcam.config_sequence = pikrellcam.config_sequence_new;
|
||||
|
@ -1128,6 +1213,8 @@ config_save(char *config_file)
|
|||
);
|
||||
for (cfg = &config[0]; cfg < &config[CONFIG_SIZE]; ++cfg)
|
||||
{
|
||||
if (!strncmp("#OBSOLETE", cfg->description, 9)) /* do not save */
|
||||
continue;
|
||||
fprintf(f, "%s\n", cfg->description);
|
||||
if (cfg->config_func == config_value_int_set)
|
||||
fprintf(f, "%s %d\n\n", cfg->option, *(cfg->result.value));
|
||||
|
|
723
src/display.c
78
src/event.c
|
@ -1,6 +1,6 @@
|
|||
/* PiKrellCam
|
||||
|
|
||||
| Copyright (C) 2015 Bill Wilson billw@gkrellm.net
|
||||
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
|
||||
|
|
||||
| PiKrellCam is free software: you can redistribute it and/or modify it
|
||||
| under the terms of the GNU General Public License as published by
|
||||
|
@ -31,6 +31,8 @@ static pthread_mutex_t event_mutex;
|
|||
static SList *event_list,
|
||||
*at_command_list;
|
||||
|
||||
static boolean exec_with_session = TRUE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
boolean initialized;
|
||||
|
@ -59,6 +61,13 @@ typedef struct
|
|||
|
||||
static Sun sun;
|
||||
|
||||
|
||||
void
|
||||
set_exec_with_session(boolean set)
|
||||
{
|
||||
exec_with_session = set;
|
||||
}
|
||||
|
||||
/* exec a command with the given arg. Any strftime() % replacements should
|
||||
| have been done before calling this so there will remain only pikrellcam
|
||||
| specific $X conversions. Change all '$X' to '%s' and printf in what we
|
||||
|
@ -68,6 +77,7 @@ static int
|
|||
exec_command(char *command, char *arg, boolean wait, pid_t *pid)
|
||||
{
|
||||
struct tm *tm_now;
|
||||
PresetPosition *pos;
|
||||
CompositeVector *frame_vec = &motion_frame.final_preview_vector;
|
||||
char specifier, *fmt, *fmt_arg, *copy, *cmd_line, *name, buf[BUFSIZ];
|
||||
int t, i, status = 0;
|
||||
|
@ -147,6 +157,14 @@ exec_command(char *command, char *arg, boolean wait, pid_t *pid)
|
|||
case 'P':
|
||||
fmt_arg = pikrellcam.command_fifo;
|
||||
break;
|
||||
case 'p':
|
||||
pos = (PresetPosition *) slist_nth_data(pikrellcam.preset_position_list,
|
||||
pikrellcam.preset_position_index);
|
||||
snprintf(buf, sizeof(buf), "%d %d",
|
||||
pikrellcam.preset_position_index + 1,
|
||||
pos ? pos->settings_index + 1 : 1);
|
||||
fmt_arg = buf;
|
||||
break;
|
||||
case 'c':
|
||||
fmt_arg = pikrellcam.scripts_dist_dir;
|
||||
break;
|
||||
|
@ -212,6 +230,7 @@ exec_command(char *command, char *arg, boolean wait, pid_t *pid)
|
|||
{ /* child - execute command in background */
|
||||
for (i = getdtablesize(); i > 2; --i)
|
||||
close(i);
|
||||
if (exec_with_session)
|
||||
setsid(); /* new session group - ie detach */
|
||||
execl("/bin/sh", "sh", "-c", cmd_line, " &", NULL);
|
||||
_exit (EXIT_FAILURE);
|
||||
|
@ -429,15 +448,49 @@ state_file_write(void)
|
|||
{
|
||||
static char *fname_part;
|
||||
FILE *f;
|
||||
MotionFrame *mf = &motion_frame;
|
||||
VideoCircularBuffer *vcb = &video_circular_buffer;
|
||||
PresetPosition *pos;
|
||||
PresetSettings *settings = NULL;
|
||||
char *state;
|
||||
int pan, tilt;
|
||||
|
||||
if (!fname_part)
|
||||
asprintf(&fname_part, "%s.part", pikrellcam.state_filename);
|
||||
f = fopen(fname_part, "w");
|
||||
|
||||
fprintf(f, "motion_enable %s\n",
|
||||
motion_frame.motion_enable ? "on" : "off");
|
||||
fprintf(f, "motion_enable %s\n", mf->motion_enable ? "on" : "off");
|
||||
fprintf(f, "show_preset %s\n", mf->show_preset ? "on" : "off");
|
||||
fprintf(f, "show_vectors %s\n", mf->show_vectors ? "on" : "off");
|
||||
|
||||
servo_get_position(&pan, &tilt);
|
||||
pos = preset_find_at_position(pan, tilt);
|
||||
if (pos)
|
||||
{
|
||||
fprintf(f, "preset %d %d\n", pikrellcam.preset_position_index + 1,
|
||||
pos ? pos->settings_index + 1 : 1);
|
||||
settings = (PresetSettings *) slist_nth_data(pos->settings_list, pos->settings_index);
|
||||
if (settings)
|
||||
{
|
||||
fprintf(f, "magnitude_limit %d\n", settings->mag_limit);
|
||||
fprintf(f, "magnitude_count %d\n", settings->mag_limit_count);
|
||||
fprintf(f, "burst_count %d\n", settings->burst_count);
|
||||
fprintf(f, "burst_frames %d\n", settings->burst_frames);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(f, "preset 0 0\n");
|
||||
fprintf(f, "magnitude_limit 0\n");
|
||||
fprintf(f, "magnitude_count 0\n");
|
||||
fprintf(f, "burst_count 0\n");
|
||||
fprintf(f, "burst_frames 0\n");
|
||||
}
|
||||
if (pikrellcam.have_servos)
|
||||
{
|
||||
fprintf(f, "pan %d\n", pan);
|
||||
fprintf(f, "tilt %d\n", tilt);
|
||||
}
|
||||
|
||||
if (vcb->state & VCB_STATE_MOTION)
|
||||
state = "motion";
|
||||
|
@ -452,6 +505,7 @@ state_file_write(void)
|
|||
fprintf(f, "still_last %s\n",
|
||||
pikrellcam.still_last ? pikrellcam.still_last : "none");
|
||||
|
||||
fprintf(f, "show_timelapse %s\n", time_lapse.show_status ? "on" : "off");
|
||||
fprintf(f, "timelapse_period %d\n", time_lapse.period);
|
||||
fprintf(f, "timelapse_active %s\n",
|
||||
time_lapse.activated ? "on" : "off");
|
||||
|
@ -490,6 +544,7 @@ event_count_down_add(char *name, int count,
|
|||
event = calloc(1, sizeof(Event));
|
||||
event->name = name;
|
||||
event->count = count;
|
||||
event->period = 0; /* one time event */
|
||||
event->func = func;
|
||||
event->data = data;
|
||||
|
||||
|
@ -498,8 +553,8 @@ event_count_down_add(char *name, int count,
|
|||
pthread_mutex_unlock(&event_mutex);
|
||||
|
||||
if (pikrellcam.verbose)
|
||||
printf("Event add [%s] period=%d\n",
|
||||
event->name, (int) event->period);
|
||||
printf("Event count down add [%s] count=%d\n",
|
||||
event->name, (int) event->count);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
@ -529,6 +584,17 @@ event_add(char *name, time_t time, time_t period,
|
|||
return event;
|
||||
}
|
||||
|
||||
void
|
||||
event_list_lock(void)
|
||||
{
|
||||
pthread_mutex_lock(&event_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
event_list_unlock(void)
|
||||
{
|
||||
pthread_mutex_unlock(&event_mutex);
|
||||
}
|
||||
|
||||
Event *
|
||||
event_find(char *name)
|
||||
|
@ -846,6 +912,8 @@ event_process(void)
|
|||
start = FALSE;
|
||||
if (pikrellcam.config_modified)
|
||||
config_save(pikrellcam.config_file);
|
||||
if (pikrellcam.preset_modified)
|
||||
preset_config_save();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* PiKrellCam
|
||||
|
|
||||
| Copyright (C) 2015 Bill Wilson billw@gkrellm.net
|
||||
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
|
||||
|
|
||||
| PiKrellCam is free software: you can redistribute it and/or modify it
|
||||
| under the terms of the GNU General Public License as published by
|
||||
|
@ -200,7 +200,8 @@ mjpeg_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|||
{
|
||||
mmal_buffer_header_mem_lock(buffer);
|
||||
n = fwrite(buffer->data, 1, buffer->length, file);
|
||||
if (tcp_buf) {
|
||||
if (tcp_buf)
|
||||
{
|
||||
memcpy(tcp_buf + tcp_buf_offset, buffer->data, buffer->length);
|
||||
tcp_buf_offset += buffer->length;
|
||||
}
|
||||
|
@ -213,7 +214,8 @@ mjpeg_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|||
}
|
||||
if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)
|
||||
{
|
||||
if (tcp_buf) {
|
||||
if (tcp_buf)
|
||||
{
|
||||
mjpeg_server_queue_put(tcp_buf, tcp_buf_offset);
|
||||
tcp_buf = NULL;
|
||||
tcp_buf_offset = 0;
|
||||
|
@ -280,11 +282,13 @@ still_jpeg_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|||
{
|
||||
CameraObject *data = (CameraObject *) port->userdata;
|
||||
int n;
|
||||
static int bytes_written;
|
||||
|
||||
if (buffer->length && still_jpeg_encoder.file)
|
||||
{
|
||||
mmal_buffer_header_mem_lock(buffer);
|
||||
n = fwrite(buffer->data, 1, buffer->length, still_jpeg_encoder.file);
|
||||
bytes_written += n;
|
||||
mmal_buffer_header_mem_unlock(buffer);
|
||||
if (n != buffer->length)
|
||||
{
|
||||
|
@ -295,12 +299,25 @@ still_jpeg_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|||
if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)
|
||||
{
|
||||
fclose(still_jpeg_encoder.file);
|
||||
still_jpeg_encoder.file = NULL;
|
||||
if (pikrellcam.still_capture_event)
|
||||
event_add("still capture command", pikrellcam.t_now, 0,
|
||||
event_still_capture_cmd,
|
||||
pikrellcam.on_still_capture_cmd);
|
||||
else if (pikrellcam.timelapse_capture_event)
|
||||
{
|
||||
if (bytes_written > 0)
|
||||
time_lapse.sequence += 1;
|
||||
else if (pikrellcam.timelapse_jpeg_last)
|
||||
{
|
||||
unlink(pikrellcam.timelapse_jpeg_last);
|
||||
dup_string(&pikrellcam.timelapse_jpeg_last, "failed");
|
||||
}
|
||||
}
|
||||
pikrellcam.still_capture_event = FALSE;
|
||||
pikrellcam.timelapse_capture_event = FALSE;
|
||||
bytes_written = 0;
|
||||
pikrellcam.state_modified = TRUE;
|
||||
still_jpeg_encoder.file = NULL;
|
||||
}
|
||||
return_buffer_to_port(port, buffer);
|
||||
}
|
||||
|
|
96
src/motion.c
|
@ -1,6 +1,6 @@
|
|||
/* PiKrellCam
|
||||
|
|
||||
| Copyright (C) 2015 Bill Wilson billw@gkrellm.net
|
||||
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
|
||||
|
|
||||
| PiKrellCam is free software: you can redistribute it and/or modify it
|
||||
| under the terms of the GNU General Public License as published by
|
||||
|
@ -578,8 +578,12 @@ motion_frame_process(VideoCircularBuffer *vcb, MotionFrame *mf)
|
|||
if (motion_burst_frame == pikrellcam.motion_burst_frames)
|
||||
motion_burst_frame = 0;
|
||||
|
||||
motion_enabled = mf->motion_enable
|
||||
|| (mf->external_trigger_mode & EXT_TRIG_MODE_ENABLE);
|
||||
motion_enabled = ( ( mf->motion_enable
|
||||
|| (mf->external_trigger_mode & EXT_TRIG_MODE_ENABLE)
|
||||
)
|
||||
&& !pikrellcam.servo_moving
|
||||
&& (pikrellcam.on_preset || pikrellcam.motion_off_preset)
|
||||
);
|
||||
|
||||
if ( ( (mf->motion_status & MOTION_DETECTED)
|
||||
&& motion_enabled
|
||||
|
@ -782,22 +786,23 @@ atof_range(float *result, char *value, double low, double high)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
#define ADD_REGION 0
|
||||
#define MOVE_REGION 1
|
||||
#define MOVE_COARSE 2
|
||||
#define MOVE_FINE 3
|
||||
#define ASSIGN_REGION 4
|
||||
#define SAVE_REGIONS 5
|
||||
#define LOAD_REGIONS 6
|
||||
#define LOAD_REGIONS_SHOW 7
|
||||
#define LIST_REGIONS 8
|
||||
#define DELETE_REGIONS 9
|
||||
#define SET_LIMITS 10
|
||||
#define SET_BURST 11
|
||||
#define SELECT_REGION 12
|
||||
#define SHOW_REGIONS 13
|
||||
#define SHOW_VECTORS 14
|
||||
#define TRIGGER 15
|
||||
#define NEW_REGION 0
|
||||
#define ADD_REGION 1
|
||||
#define MOVE_REGION 2
|
||||
#define MOVE_COARSE 3
|
||||
#define MOVE_FINE 4
|
||||
#define ASSIGN_REGION 5
|
||||
#define SAVE_REGIONS 6
|
||||
#define LOAD_REGIONS 7
|
||||
#define LOAD_REGIONS_SHOW 8
|
||||
#define LIST_REGIONS 9
|
||||
#define DELETE_REGIONS 10
|
||||
#define SET_LIMITS 11
|
||||
#define SET_BURST 12
|
||||
#define SELECT_REGION 13
|
||||
#define SHOW_PRESET 14
|
||||
#define SHOW_VECTORS 15
|
||||
#define TRIGGER 16
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -809,9 +814,11 @@ typedef struct
|
|||
|
||||
static MotionCommand motion_commands[] =
|
||||
{
|
||||
{ "show_regions", SHOW_REGIONS, 1 },
|
||||
{ "show_preset", SHOW_PRESET, 1 },
|
||||
{ "show_regions", SHOW_PRESET, 1 },
|
||||
{ "show_vectors", SHOW_VECTORS, 1 },
|
||||
{ "add_region", ADD_REGION, 4 },
|
||||
{ "new_region", NEW_REGION, 4 },
|
||||
{ "add_region", ADD_REGION, 4 }, // Not a regions modify
|
||||
{ "move_region", MOVE_REGION, 5 },
|
||||
{ "move_coarse", MOVE_COARSE, 2 },
|
||||
{ "move_fine", MOVE_FINE, 2 },
|
||||
|
@ -820,7 +827,7 @@ static MotionCommand motion_commands[] =
|
|||
{ "load_regions", LOAD_REGIONS, 1 },
|
||||
{ "load_regions_show", LOAD_REGIONS_SHOW, 1 },
|
||||
{ "list_regions", LIST_REGIONS, 0 },
|
||||
{ "delete_regions", DELETE_REGIONS, 1 },
|
||||
{ "delete_regions", DELETE_REGIONS, 1 }, // All not a regions modify
|
||||
{ "select_region", SELECT_REGION, 1 },
|
||||
{ "limits", SET_LIMITS, 2 },
|
||||
{ "burst", SET_BURST, 2 },
|
||||
|
@ -907,6 +914,7 @@ motion_command(char *cmd_line)
|
|||
float delta = 0.0;
|
||||
struct dirent *dp;
|
||||
DIR *dfd;
|
||||
boolean new_region = FALSE;
|
||||
|
||||
arg1[0] = '\0';
|
||||
arg2[0] = '\0';
|
||||
|
@ -934,13 +942,20 @@ motion_command(char *cmd_line)
|
|||
|
||||
switch (id)
|
||||
{
|
||||
case SHOW_REGIONS:
|
||||
config_set_boolean(&mf->show_regions, arg1);
|
||||
case SHOW_PRESET:
|
||||
config_set_boolean(&mf->show_preset, arg1);
|
||||
pikrellcam.state_modified = TRUE;
|
||||
break;
|
||||
case SHOW_VECTORS:
|
||||
config_set_boolean(&mf->show_vectors, arg1);
|
||||
pikrellcam.state_modified = TRUE;
|
||||
break;
|
||||
case ADD_REGION: /* add_region x y dx dy */
|
||||
/* A new_region is an edit modify from the web page.
|
||||
| An add_region is a load from a config file and is not a modify.
|
||||
*/
|
||||
case NEW_REGION: /* new_region x y dx dy */
|
||||
new_region = TRUE;
|
||||
case ADD_REGION: /* load_region x y dx dy */
|
||||
memset((char *) &mrtmp, 0, sizeof(MotionRegion));
|
||||
if (get_motion_args(&mrtmp, arg1, arg2, arg3, arg4, 0.00, 1.0))
|
||||
{
|
||||
|
@ -953,7 +968,10 @@ motion_command(char *cmd_line)
|
|||
mf->n_regions = slist_length(mf->motion_region_list);
|
||||
mreg->region_number = mf->n_regions - 1;
|
||||
mf->selected_region = mreg->region_number;
|
||||
mf->show_regions = TRUE;
|
||||
mf->show_preset = TRUE;
|
||||
pikrellcam.state_modified = TRUE;
|
||||
if (new_region)
|
||||
preset_regions_set_modified();
|
||||
pthread_mutex_unlock(&mf->region_list_mutex);
|
||||
}
|
||||
else
|
||||
|
@ -976,7 +994,8 @@ motion_command(char *cmd_line)
|
|||
mf->selected_region = 0;
|
||||
}
|
||||
mf->prev_selected_region = mf->selected_region;
|
||||
mf->show_regions = TRUE;
|
||||
mf->show_preset = TRUE;
|
||||
pikrellcam.state_modified = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1007,6 +1026,7 @@ motion_command(char *cmd_line)
|
|||
mreg->dyf += delta;
|
||||
motion_region_fixup(mreg);
|
||||
}
|
||||
preset_regions_set_modified();
|
||||
pthread_mutex_unlock(&mf->region_list_mutex);
|
||||
}
|
||||
else
|
||||
|
@ -1029,6 +1049,7 @@ motion_command(char *cmd_line)
|
|||
mreg->dyf += mrtmp.dyf;
|
||||
motion_region_fixup(mreg);
|
||||
}
|
||||
preset_regions_set_modified();
|
||||
pthread_mutex_unlock(&mf->region_list_mutex);
|
||||
break;
|
||||
|
||||
|
@ -1045,6 +1066,7 @@ motion_command(char *cmd_line)
|
|||
mreg->dyf = mrtmp.dyf;
|
||||
motion_region_fixup(mreg);
|
||||
}
|
||||
preset_regions_set_modified();
|
||||
pthread_mutex_unlock(&mf->region_list_mutex);
|
||||
break;
|
||||
|
||||
|
@ -1055,10 +1077,12 @@ motion_command(char *cmd_line)
|
|||
break;
|
||||
|
||||
case LOAD_REGIONS_SHOW: /* load_regions and show */
|
||||
mf->show_regions = TRUE;
|
||||
mf->show_preset = TRUE;
|
||||
pikrellcam.state_modified = TRUE;
|
||||
case LOAD_REGIONS: /* load_regions config-name */
|
||||
path = regions_custom_config(arg1);
|
||||
motion_regions_config_load(path, TRUE);
|
||||
if (motion_regions_config_load(path, TRUE))
|
||||
preset_regions_set_modified();
|
||||
free(path);
|
||||
break;
|
||||
|
||||
|
@ -1086,7 +1110,10 @@ motion_command(char *cmd_line)
|
|||
closedir(dfd);
|
||||
}
|
||||
break;
|
||||
|
||||
/* An all delete is a clear prior to a load from a config file and
|
||||
| is not a modify. A delete of a region number is a delete from the
|
||||
| from the web page and is an edit modify of the regions.
|
||||
*/
|
||||
case DELETE_REGIONS: /* delete_regions all / delete r */
|
||||
pthread_mutex_lock(&mf->region_list_mutex);
|
||||
if (!strcmp(arg1, "all"))
|
||||
|
@ -1108,6 +1135,7 @@ motion_command(char *cmd_line)
|
|||
mf->motion_region_list =
|
||||
slist_remove(mf->motion_region_list, mreg);
|
||||
free(mreg);
|
||||
preset_regions_set_modified();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1144,6 +1172,8 @@ motion_command(char *cmd_line)
|
|||
if (n > mf->width * mf->height / 2)
|
||||
n = n > mf->width * mf->height / 2;
|
||||
pikrellcam.motion_magnitude_limit_count = n;
|
||||
|
||||
preset_settings_set_modified();
|
||||
break;
|
||||
|
||||
case SET_BURST: /* burst count frames */
|
||||
|
@ -1160,6 +1190,8 @@ motion_command(char *cmd_line)
|
|||
if (n > 20)
|
||||
n = 20;
|
||||
pikrellcam.motion_burst_frames = n;
|
||||
|
||||
preset_settings_set_modified();
|
||||
break;
|
||||
|
||||
case TRIGGER:
|
||||
|
@ -1278,7 +1310,7 @@ motion_regions_config_load(char *config_file, boolean inform)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
save_show = motion_frame.show_regions;
|
||||
save_show = motion_frame.show_preset;
|
||||
|
||||
motion_command("delete_regions all");
|
||||
while (fgets(buf, sizeof(buf), f) != NULL)
|
||||
|
@ -1292,6 +1324,6 @@ motion_regions_config_load(char *config_file, boolean inform)
|
|||
display_inform(dbuf);
|
||||
display_inform("timeout 1");
|
||||
}
|
||||
motion_frame.show_regions = save_show; /* can't double send_cmd()??*/
|
||||
motion_frame.show_preset = save_show; /* can't double send_cmd()??*/
|
||||
return TRUE;
|
||||
}
|
||||
|
|
170
src/pikrellcam.c
|
@ -1,6 +1,6 @@
|
|||
/* PiKrellCam
|
||||
|
|
||||
| Copyright (C) 2015 Bill Wilson billw@gkrellm.net
|
||||
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
|
||||
|
|
||||
| PiKrellCam is free software: you can redistribute it and/or modify it
|
||||
| under the terms of the GNU General Public License as published by
|
||||
|
@ -32,6 +32,10 @@ extern int setup_mjpeg_tcp_server(void);
|
|||
static char *pgm_name;
|
||||
static boolean quit_flag;
|
||||
|
||||
static uid_t user_uid;
|
||||
static gid_t user_gid;
|
||||
static char *homedir;
|
||||
|
||||
/* Substitute fmt_arg into str to replace a "$V" substitution variable.
|
||||
| "str" argument must be allocated memory.
|
||||
*/
|
||||
|
@ -296,7 +300,7 @@ still_capture(char *fname)
|
|||
|
||||
/* timelapse_shapshot() also uses the still_jpeg_encoder, so wait if busy.
|
||||
*/
|
||||
for (n = 0; n < 5; ++n)
|
||||
for (n = 0; n < 10; ++n)
|
||||
{
|
||||
if (still_jpeg_encoder.file == NULL)
|
||||
break;
|
||||
|
@ -327,12 +331,13 @@ still_capture(char *fname)
|
|||
result = TRUE;
|
||||
log_printf("Still: %s\n", fname);
|
||||
dup_string(&pikrellcam.still_last, fname);
|
||||
pikrellcam.state_modified = TRUE;
|
||||
n = pikrellcam.notify_duration * EVENT_LOOP_FREQUENCY;
|
||||
|
||||
event_list_lock();
|
||||
if ((event = event_find("still saved")) != NULL)
|
||||
event->count = n; /* rapid stills, extend the time */
|
||||
else
|
||||
event_list_unlock();
|
||||
if (!event)
|
||||
event_count_down_add("still saved", n,
|
||||
event_notify_expire, &pikrellcam.still_notify);
|
||||
pikrellcam.still_capture_event = TRUE;
|
||||
|
@ -354,7 +359,7 @@ timelapse_capture(void)
|
|||
|
||||
/* still_capture() also uses the still_jpeg_encoder, so wait if busy.
|
||||
*/
|
||||
for (n = 0; n < 5; ++n)
|
||||
for (n = 0; n < 10; ++n)
|
||||
{
|
||||
if (still_jpeg_encoder.file == NULL)
|
||||
break;
|
||||
|
@ -372,26 +377,28 @@ timelapse_capture(void)
|
|||
pikrellcam.timelapse_format,
|
||||
'N', seq_buf,
|
||||
'n', series_buf);
|
||||
time_lapse.sequence += 1;
|
||||
|
||||
if ((still_jpeg_encoder.file = fopen(path, "w")) == NULL)
|
||||
log_printf("Could not create timelapse file %s. %m\n", path);
|
||||
else
|
||||
{
|
||||
dup_string(&pikrellcam.timelapse_jpeg_last, path);
|
||||
pikrellcam.timelapse_capture_event = TRUE;
|
||||
if ((status = mmal_port_parameter_set_boolean(
|
||||
camera.component->output[CAMERA_CAPTURE_PORT],
|
||||
MMAL_PARAMETER_CAPTURE, 1)) != MMAL_SUCCESS)
|
||||
{
|
||||
fclose(still_jpeg_encoder.file);
|
||||
unlink(path);
|
||||
still_jpeg_encoder.file = NULL;
|
||||
dup_string(&pikrellcam.timelapse_jpeg_last, "failed");
|
||||
pikrellcam.timelapse_capture_event = FALSE;
|
||||
log_printf("Timelapse capture startup failed. Status %s\n",
|
||||
mmal_status[status]);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_printf("Timelapse still: %s\n", path);
|
||||
dup_string(&pikrellcam.timelapse_jpeg_last, path);
|
||||
pikrellcam.state_modified = TRUE;
|
||||
|
||||
/* timelapse_capture() is an event call (inside the event loop)
|
||||
| and we here add an event to the list.
|
||||
|
@ -574,7 +581,9 @@ video_record_stop(VideoCircularBuffer *vcb)
|
|||
asprintf(&cmd, "(MP4Box %s -tmp %s -fps %d -add %s %s %s && rm %s %s)",
|
||||
pikrellcam.verbose ? "" : "-quiet",
|
||||
tmp_dir,
|
||||
pikrellcam.camera_adjust.video_mp4box_fps,
|
||||
(pikrellcam.camera_adjust.video_mp4box_fps > 0) ?
|
||||
pikrellcam.camera_adjust.video_mp4box_fps :
|
||||
pikrellcam.camera_adjust.video_fps,
|
||||
pikrellcam.video_h264, pikrellcam.video_pathname,
|
||||
pikrellcam.verbose ? "" : "2> /dev/null",
|
||||
pikrellcam.video_h264,
|
||||
|
@ -643,7 +652,7 @@ get_arg_pass1(char *arg)
|
|||
|
||||
if (!strcmp(arg, "-V") || !strcmp(arg, "--version"))
|
||||
{
|
||||
printf("%s\n", pikrellcam.version);
|
||||
printf("%s\n", PIKRELLCAM_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
|
||||
|
@ -651,25 +660,18 @@ get_arg_pass1(char *arg)
|
|||
/* XXX */
|
||||
exit(0);
|
||||
}
|
||||
#if 0
|
||||
else if (!strcmp(arg, "-d") || !strcmp(arg, "--detach"))
|
||||
{
|
||||
if (getppid() != 1) /* if not already a daemon */
|
||||
{
|
||||
if (daemon(0, 0)) /* Detach from terminal, reparent to pid 1 */
|
||||
{
|
||||
printf("Detach failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (!strcmp(arg, "-v"))
|
||||
pikrellcam.verbose = TRUE;
|
||||
else if (!strcmp(arg, "-vm"))
|
||||
pikrellcam.verbose_motion = TRUE;
|
||||
else if (!strcmp(arg, "-debug"))
|
||||
pikrellcam.debug = TRUE;
|
||||
else if (!strncmp(arg, "-user", 5))
|
||||
user_uid = atoi(arg + 5);
|
||||
else if (!strncmp(arg, "-group", 6))
|
||||
user_gid = atoi(arg + 6);
|
||||
else if (!strncmp(arg, "-home", 5))
|
||||
homedir = strdup(arg + 5);
|
||||
else
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
|
@ -704,6 +706,8 @@ typedef enum
|
|||
annotate_string,
|
||||
delete_log,
|
||||
fix_thumbs,
|
||||
servo_cmd,
|
||||
preset_cmd,
|
||||
upgrade,
|
||||
quit
|
||||
}
|
||||
|
@ -730,7 +734,7 @@ static Command commands[] =
|
|||
{ "tl_hold", tl_hold, 1, TRUE },
|
||||
{ "tl_show_status", tl_show_status, 1, FALSE },
|
||||
|
||||
{ "motion", motion_cmd, 1, TRUE },
|
||||
{ "motion", motion_cmd, 1, FALSE },
|
||||
{ "motion_enable", motion_enable, 1, TRUE },
|
||||
|
||||
/* Above commands are redirected to abort a menu or adjustment display
|
||||
|
@ -751,6 +755,8 @@ static Command commands[] =
|
|||
{ "delete_log", delete_log, 0, TRUE },
|
||||
{ "fix_thumbs", fix_thumbs, 1, TRUE },
|
||||
{ "annotate_string", annotate_string, 1, FALSE },
|
||||
{ "preset", preset_cmd, 1, FALSE },
|
||||
{ "servo", servo_cmd, 1, FALSE },
|
||||
{ "upgrade", upgrade, 0, TRUE },
|
||||
{ "quit", quit, 0, TRUE },
|
||||
};
|
||||
|
@ -901,6 +907,7 @@ command_process(char *command_line)
|
|||
time_lapse.period = n;
|
||||
config_timelapse_save_status();
|
||||
config_set_boolean(&time_lapse.show_status, "on");
|
||||
pikrellcam.state_modified = TRUE;
|
||||
break;
|
||||
|
||||
case tl_hold:
|
||||
|
@ -935,6 +942,7 @@ command_process(char *command_line)
|
|||
exec_no_wait(pikrellcam.timelapse_convert_cmd, NULL);
|
||||
|
||||
config_set_boolean(&time_lapse.show_status, "on");
|
||||
pikrellcam.state_modified = TRUE;
|
||||
display_inform("\"Timelapse ended.\" 3 3 1");
|
||||
display_inform("\"Starting convert...\" 4 3 1");
|
||||
display_inform("timeout 2");
|
||||
|
@ -971,6 +979,7 @@ command_process(char *command_line)
|
|||
|
||||
case tl_show_status:
|
||||
config_set_boolean(&time_lapse.show_status, args);
|
||||
pikrellcam.state_modified = TRUE;
|
||||
break;
|
||||
|
||||
case display_cmd:
|
||||
|
@ -1095,6 +1104,14 @@ command_process(char *command_line)
|
|||
log_printf("Wrong number of args for command: %s\n", command);
|
||||
break;
|
||||
|
||||
case preset_cmd:
|
||||
preset_command(args);
|
||||
break;
|
||||
|
||||
case servo_cmd:
|
||||
servo_command(args);
|
||||
break;
|
||||
|
||||
case upgrade:
|
||||
snprintf(buf, sizeof(buf), "%s/scripts-dist/_upgrade $I $P $G $Z",
|
||||
pikrellcam.install_dir);
|
||||
|
@ -1103,8 +1120,11 @@ command_process(char *command_line)
|
|||
|
||||
case quit:
|
||||
config_timelapse_save_status();
|
||||
preset_state_save();
|
||||
if (pikrellcam.config_modified)
|
||||
config_save(pikrellcam.config_file);
|
||||
if (pikrellcam.preset_modified)
|
||||
preset_config_save();
|
||||
display_quit();
|
||||
exit(0);
|
||||
break;
|
||||
|
@ -1185,49 +1205,112 @@ static void
|
|||
signal_quit(int sig)
|
||||
{
|
||||
config_timelapse_save_status();
|
||||
preset_state_save();
|
||||
if (pikrellcam.config_modified)
|
||||
config_save(pikrellcam.config_file);
|
||||
if (pikrellcam.preset_modified)
|
||||
preset_config_save();
|
||||
display_quit();
|
||||
log_printf("quit signal received - exiting!\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
log_start(boolean start_sep, boolean time, boolean end_sep)
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
if (start_sep)
|
||||
log_printf_no_timestamp("\n========================================================\n");
|
||||
if (time)
|
||||
{
|
||||
strftime(buf, sizeof(buf), "%F %T", localtime(&pikrellcam.t_now));
|
||||
log_printf_no_timestamp("======= PiKrellCam %s started at %s\n",
|
||||
pikrellcam.version, buf);
|
||||
}
|
||||
if (end_sep)
|
||||
log_printf_no_timestamp("========================================================\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fifo;
|
||||
int i, n;
|
||||
char *opt, *arg, *equal_arg, *homedir, *user;
|
||||
char *opt, *arg, *equal_arg, *user;
|
||||
char *line, *eol, buf[4096];
|
||||
|
||||
pgm_name = argv[0];
|
||||
bcm_host_init();
|
||||
setlocale(LC_TIME, "");
|
||||
|
||||
time(&pikrellcam.t_now);
|
||||
|
||||
config_set_defaults();
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
get_arg_pass1(argv[i]);
|
||||
|
||||
config_set_defaults(homedir);
|
||||
|
||||
if (!config_load(pikrellcam.config_file))
|
||||
config_save(pikrellcam.config_file);
|
||||
if (!motion_regions_config_load(pikrellcam.motion_regions_config_file, FALSE))
|
||||
motion_regions_config_save(pikrellcam.motion_regions_config_file, FALSE);
|
||||
if (quit_flag) /* Just making sure initial config file is written */
|
||||
exit(0);
|
||||
|
||||
if (*pikrellcam.log_file != '/')
|
||||
if (!pikrellcam.log_file || !*pikrellcam.log_file)
|
||||
pikrellcam.log_file = strdup("/dev/null");
|
||||
else if (*pikrellcam.log_file != '/')
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s/%s", pikrellcam.install_dir, pikrellcam.log_file);
|
||||
dup_string(&pikrellcam.log_file, buf);
|
||||
}
|
||||
if (!quit_flag)
|
||||
|
||||
/* If need to mmap() gpios for servos, restart a sudo pikrellcam which can
|
||||
| mmap() /dev/mem and then drop priviledes back to orig user/group
|
||||
*/
|
||||
if (getuid() == 0) /* root, so mmap(), drop privileges and continue */
|
||||
{
|
||||
log_printf_no_timestamp("\n========================================================\n");
|
||||
strftime(buf, sizeof(buf), "%F %T", localtime(&pikrellcam.t_now));
|
||||
log_printf_no_timestamp("%s ===== PiKrellCam %s started =====\n", buf, pikrellcam.version);
|
||||
log_printf_no_timestamp("========================================================\n");
|
||||
log_start(FALSE, TRUE, FALSE);
|
||||
servo_init();
|
||||
if (user_gid > 0)
|
||||
setgid(user_gid);
|
||||
setuid(user_uid);
|
||||
log_printf_no_timestamp("== Dropped root priviledges-continuing as normal user ==\n");
|
||||
log_start(FALSE, FALSE, TRUE);
|
||||
}
|
||||
else if (pikrellcam.have_servos && !pikrellcam.servo_use_servoblaster)
|
||||
{
|
||||
/* Need to restart pikrellcam as root so can mmap() PWMs for servos.
|
||||
*/
|
||||
log_start(TRUE, FALSE, FALSE);
|
||||
log_printf_no_timestamp("========= Restarting as root to mmap() servos ==========\n");
|
||||
homedir = getpwuid(geteuid())->pw_dir;
|
||||
i = snprintf(buf, sizeof(buf), "sudo %s -user%d -group%d -home%s ",
|
||||
*argv++, (int) getuid(), (int) getgid(), homedir);
|
||||
while (--argc && i < sizeof(buf) - 64 - strlen(*argv))
|
||||
i = sprintf(buf + i, "%s ", *argv++);
|
||||
|
||||
set_exec_with_session(FALSE);
|
||||
exec_wait(buf, NULL); /* restart as root so can mmap() gpios*/
|
||||
exit(0);
|
||||
}
|
||||
else if (pikrellcam.servo_use_servoblaster)
|
||||
{
|
||||
log_start(TRUE, TRUE,FALSE);
|
||||
servo_init();
|
||||
log_start(FALSE, FALSE, TRUE);
|
||||
}
|
||||
else
|
||||
log_start(TRUE, TRUE, TRUE);
|
||||
|
||||
bcm_host_init();
|
||||
if (!homedir)
|
||||
homedir = getpwuid(geteuid())->pw_dir;
|
||||
|
||||
user = strrchr(homedir, '/');
|
||||
pikrellcam.effective_user = strdup(user ? user + 1 : "pi");
|
||||
|
||||
if (!motion_regions_config_load(pikrellcam.motion_regions_config_file, FALSE))
|
||||
motion_regions_config_save(pikrellcam.motion_regions_config_file, FALSE);
|
||||
preset_config_load();
|
||||
|
||||
if (!at_commands_config_load(pikrellcam.at_commands_config_file))
|
||||
at_commands_config_save(pikrellcam.at_commands_config_file);
|
||||
|
@ -1238,11 +1321,6 @@ main(int argc, char *argv[])
|
|||
continue;
|
||||
opt = argv[i];
|
||||
|
||||
/* Just for initial install-pikrellcam.sh run to create config files.
|
||||
*/
|
||||
if (!strcmp(opt, "-quit"))
|
||||
exit(0);
|
||||
|
||||
/* Accept: --opt arg -opt arg opt=arg --opt=arg -opt=arg
|
||||
*/
|
||||
for (i = 0; i < 2; ++i)
|
||||
|
@ -1272,10 +1350,6 @@ main(int argc, char *argv[])
|
|||
if (pikrellcam.debug)
|
||||
printf("debugging...\n");
|
||||
|
||||
homedir = getpwuid(geteuid())->pw_dir;
|
||||
user = strrchr(homedir, '/');
|
||||
pikrellcam.effective_user = strdup(user ? user + 1 : "pi");
|
||||
|
||||
if (*pikrellcam.media_dir != '/')
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s/%s", pikrellcam.install_dir, pikrellcam.media_dir);
|
||||
|
@ -1313,8 +1387,9 @@ main(int argc, char *argv[])
|
|||
)
|
||||
exit(1);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/scripts-dist/_init $I $a $m $M $P $G",
|
||||
pikrellcam.install_dir);
|
||||
snprintf(buf, sizeof(buf), "%s/scripts-dist/_init $I $a $m $M $P $G %s",
|
||||
pikrellcam.install_dir,
|
||||
(pikrellcam.have_servos) ? "servos_on" : "servos_off");
|
||||
exec_wait(buf, NULL);
|
||||
|
||||
/* User may have enabled a mount disk on media_dir
|
||||
|
@ -1344,6 +1419,7 @@ main(int argc, char *argv[])
|
|||
|
||||
camera_start();
|
||||
config_timelapse_load_status();
|
||||
preset_state_load();
|
||||
pikrellcam.state_modified = TRUE;
|
||||
|
||||
signal(SIGINT, signal_quit);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* PiKrellCam
|
||||
|
|
||||
| Copyright (C) 2015 Bill Wilson billw@gkrellm.net
|
||||
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
|
||||
|
|
||||
| PiKrellCam is free software: you can redistribute it and/or modify it
|
||||
| under the terms of the GNU General Public License as published by
|
||||
|
@ -50,7 +50,7 @@
|
|||
|
||||
#include "utils.h"
|
||||
|
||||
#define PIKRELLCAM_VERSION "2.1.13"
|
||||
#define PIKRELLCAM_VERSION "3.0.0"
|
||||
|
||||
|
||||
//TCP Stream Server
|
||||
|
@ -89,6 +89,9 @@ extern int h264_conn_status;
|
|||
#define PIKRELLCAM_STILL_SUBDIR "stills"
|
||||
#define PIKRELLCAM_TIMELAPSE_SUBDIR "timelapse"
|
||||
|
||||
#define SERVO_MIN_WIDTH 50
|
||||
#define SERVO_MAX_WIDTH 250
|
||||
|
||||
|
||||
/* ------------------ MMAL Camera ---------------
|
||||
*/
|
||||
|
@ -275,7 +278,7 @@ typedef struct
|
|||
int motion_status;
|
||||
boolean motion_enable,
|
||||
show_vectors,
|
||||
show_regions;
|
||||
show_preset;
|
||||
|
||||
boolean do_preview_save,
|
||||
do_preview_save_cmd;
|
||||
|
@ -427,6 +430,26 @@ typedef struct
|
|||
|
||||
extern CameraAdjust camera_adjust_temp;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int pan,
|
||||
tilt,
|
||||
settings_index,
|
||||
n_settings;
|
||||
SList *settings_list;
|
||||
}
|
||||
PresetPosition;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int mag_limit,
|
||||
mag_limit_count,
|
||||
burst_count,
|
||||
burst_frames;
|
||||
SList *region_list; /* string "xf0 yf0 dxf dyf" */
|
||||
}
|
||||
PresetSettings;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -491,7 +514,8 @@ typedef struct
|
|||
*on_motion_preview_save_cmd;
|
||||
boolean motion_preview_clean,
|
||||
motion_vertical_filter,
|
||||
motion_stats;
|
||||
motion_stats,
|
||||
motion_show_counts;
|
||||
int motion_area_min_side;
|
||||
|
||||
CameraConfig
|
||||
|
@ -533,8 +557,38 @@ typedef struct
|
|||
*timelapse_format,
|
||||
*timelapse_jpeg_last,
|
||||
*timelapse_status_file;
|
||||
boolean timelapse_capture_event;
|
||||
char *timelapse_convert_cmd;
|
||||
|
||||
int servo_pan_gpio,
|
||||
servo_tilt_gpio,
|
||||
servo_pan_min,
|
||||
servo_pan_max,
|
||||
servo_tilt_min,
|
||||
servo_tilt_max,
|
||||
servo_preset_step_msec,
|
||||
servo_move_step_msec,
|
||||
servo_move_steps,
|
||||
servo_settle_msec;
|
||||
boolean servo_pan_invert,
|
||||
servo_tilt_invert,
|
||||
servo_moving,
|
||||
servo_use_servoblaster,
|
||||
have_servos;
|
||||
|
||||
SList *preset_position_list;
|
||||
int preset_position_index,
|
||||
n_preset_positions;
|
||||
char *preset_config_file,
|
||||
*preset_state_file;
|
||||
PresetPosition
|
||||
*preset_last_on;
|
||||
boolean preset_modified,
|
||||
preset_modified_warning,
|
||||
preset_notify,
|
||||
motion_off_preset,
|
||||
on_preset; /* modify in servo_control.mutex */
|
||||
|
||||
char *annotate_format_string,
|
||||
annotate_string_space_char;
|
||||
SList *annotate_list;
|
||||
|
@ -686,7 +740,7 @@ CameraParameter
|
|||
|
||||
extern boolean config_load(char *config_file);
|
||||
extern void config_save(char *config_file);
|
||||
extern void config_set_defaults(void);
|
||||
extern void config_set_defaults(char *homedir);
|
||||
extern boolean config_set_option(char *opt, char *arg, boolean set_safe);
|
||||
boolean config_boolean_value(char *value);
|
||||
void config_set_boolean(boolean *result, char *arg);
|
||||
|
@ -735,6 +789,8 @@ Event *event_count_down_add(char *name, int count,
|
|||
void (*func)(), void *data);
|
||||
|
||||
Event *event_find(char *name);
|
||||
void event_list_lock(void);
|
||||
void event_list_unlock(void);
|
||||
void event_remove(Event *event);
|
||||
void event_process(void);
|
||||
void event_preview_save(void);
|
||||
|
@ -750,6 +806,32 @@ int exec_wait(char *command, char *arg);
|
|||
void exec_no_wait(char *command, char *arg);
|
||||
Event *exec_child_event(char *event_name, char *command, char *arg);
|
||||
|
||||
void preset_command(char *args);
|
||||
void preset_config_load(void);
|
||||
void preset_config_save(void);
|
||||
void preset_state_save(void);
|
||||
void preset_state_load(void);
|
||||
void preset_load_values(boolean do_pan);
|
||||
void preset_on_check(int pan, int tilt);
|
||||
PresetPosition *preset_find_at_position(int pan, int tilt);
|
||||
void preset_settings_set_modified(void);
|
||||
void preset_regions_set_modified(void);
|
||||
void preset_pan_range(int *max, int *min);
|
||||
void preset_tilt_range(int *max, int *min);
|
||||
|
||||
void gpio_alt_function(int pin, int altfn);
|
||||
void gpio_set_mode(int pin, int mode);
|
||||
void gpio_set_pud(int pin, int pud);
|
||||
void gpio_to_channel(int gpio, int *channel, int *altfn);
|
||||
int gpio_read(int pin);
|
||||
void gpio_write(int pin, int level);
|
||||
void gpio_hardware_pwm(int pin);
|
||||
void servo_get_position(int *pan, int *tilt);
|
||||
void servo_move(int pan, int tilt, int delay);
|
||||
void servo_command(char *args);
|
||||
void servo_init(void);
|
||||
|
||||
void set_exec_with_session(boolean set);
|
||||
void sun_times_init(void);
|
||||
void at_commands_config_save(char *config_file);
|
||||
boolean at_commands_config_load(char *config_file);
|
||||
|
|
|
@ -0,0 +1,900 @@
|
|||
|
||||
/* PiKrellCam
|
||||
|
|
||||
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
|
||||
|
|
||||
| PiKrellCam is free software: you can redistribute it and/or modify it
|
||||
| under the terms of the GNU General Public License as published by
|
||||
| the Free Software Foundation, either version 3 of the License, or
|
||||
| (at your option) any later version.
|
||||
|
|
||||
| PiKrellCam is distributed in the hope that it will be useful, but WITHOUT
|
||||
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
| License for more details.
|
||||
|
|
||||
| You should have received a copy of the GNU General Public License
|
||||
| along with this program. If not, see http://www.gnu.org/licenses/
|
||||
|
|
||||
| This file is part of PiKrellCam.
|
||||
*/
|
||||
|
||||
#include "pikrellcam.h"
|
||||
|
||||
|
||||
|
||||
#define PREV_POSITION 0
|
||||
#define NEXT_POSITION 1
|
||||
#define PREV_SETTINGS 2
|
||||
#define NEXT_SETTINGS 3
|
||||
#define GOTO 4
|
||||
#define PRESET_NEW 5
|
||||
#define PRESET_COPY 6
|
||||
#define PRESET_DELETE 7
|
||||
#define MOVE_ONE 8
|
||||
#define MOVE_ALL 9
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int id,
|
||||
n_args;
|
||||
}
|
||||
PresetCommand;
|
||||
|
||||
static PresetCommand preset_commands[] =
|
||||
{
|
||||
{ "prev_position", PREV_POSITION, 0 },
|
||||
{ "next_position", NEXT_POSITION, 0 },
|
||||
{ "prev_settings", PREV_SETTINGS, 0 },
|
||||
{ "next_settings", NEXT_SETTINGS, 0 },
|
||||
{ "goto", GOTO, 1 },
|
||||
{ "move_one", MOVE_ONE, 0 },
|
||||
{ "move_all", MOVE_ALL, 0 },
|
||||
{ "new", PRESET_NEW, 0 },
|
||||
{ "copy", PRESET_COPY, 0 },
|
||||
{ "delete", PRESET_DELETE, 0 }
|
||||
};
|
||||
|
||||
#define N_PRESET_COMMANDS (sizeof(preset_commands) / sizeof(PresetCommand))
|
||||
|
||||
|
||||
static void
|
||||
preset_notify(int count)
|
||||
{
|
||||
Event *event;
|
||||
|
||||
event_list_lock();
|
||||
if ((event = event_find("preset notify")) != NULL)
|
||||
event->count = count; /* extend time of existing notify */
|
||||
event_list_unlock();
|
||||
if (!event)
|
||||
event_count_down_add("preset notify", count,
|
||||
event_notify_expire, &pikrellcam.preset_notify);
|
||||
pikrellcam.preset_notify = TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
pan_position_cmp(void *data1, void *data2)
|
||||
{
|
||||
PresetPosition *pos1 = (PresetPosition *) data1,
|
||||
*pos2 = (PresetPosition *) data2;
|
||||
|
||||
if (pos1->pan > pos2->pan)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PresetPosition *
|
||||
preset_find_at_position(int pan, int tilt)
|
||||
{
|
||||
PresetPosition *pos = NULL;
|
||||
SList *list;
|
||||
int index = 0;
|
||||
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
if (pos->pan == pan && (tilt == 0 || tilt == pos->tilt))
|
||||
{
|
||||
pikrellcam.preset_position_index = index;
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
pos = NULL;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
/* Called from locked servo_control.mutex in servo_thread()
|
||||
*/
|
||||
void
|
||||
preset_on_check(int pan, int tilt)
|
||||
{
|
||||
PresetPosition *pos;
|
||||
|
||||
if ((pos = preset_find_at_position(pan, tilt)) != NULL)
|
||||
{
|
||||
pikrellcam.on_preset = TRUE;
|
||||
pikrellcam.preset_last_on = pos;
|
||||
preset_load_values(FALSE);
|
||||
preset_notify(22);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
preset_pan_range(int *max, int *min)
|
||||
{
|
||||
PresetPosition *pos = NULL;
|
||||
SList *list;
|
||||
|
||||
*max = SERVO_MIN_WIDTH;
|
||||
*min = SERVO_MAX_WIDTH;
|
||||
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
if (pos->pan > *max)
|
||||
*max = pos->pan;
|
||||
if (pos->pan < *min)
|
||||
*min = pos->pan;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
preset_tilt_range(int *max, int *min)
|
||||
{
|
||||
PresetPosition *pos = NULL;
|
||||
SList *list;
|
||||
|
||||
*max = SERVO_MIN_WIDTH;
|
||||
*min = SERVO_MAX_WIDTH;
|
||||
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
if (pos->tilt > *max)
|
||||
*max = pos->tilt;
|
||||
if (pos->tilt < *min)
|
||||
*min = pos->tilt;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
preset_position_prev(void)
|
||||
{
|
||||
PresetPosition *pos;
|
||||
SList *list;
|
||||
int idx, pan, tilt;
|
||||
|
||||
servo_get_position(&pan, &tilt);
|
||||
idx = -1;
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
if (pos->pan >= pan)
|
||||
{
|
||||
if (pos->pan == pan && pos->tilt != tilt)
|
||||
++idx; /* stay on same pan position */
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
if (idx >= pikrellcam.n_preset_positions)
|
||||
idx = pikrellcam.n_preset_positions - 1;
|
||||
if (idx == -1)
|
||||
idx = 0;
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int
|
||||
preset_position_next(void)
|
||||
{
|
||||
PresetPosition *pos;
|
||||
SList *list;
|
||||
int idx, pan, tilt;
|
||||
|
||||
servo_get_position(&pan, &tilt);
|
||||
idx = 0;
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
if ( (pos->pan == pan && pos->tilt != tilt)
|
||||
|| pos->pan > pan
|
||||
)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
if (idx >= pikrellcam.n_preset_positions)
|
||||
idx = pikrellcam.n_preset_positions - 1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
void
|
||||
preset_load_values(boolean do_pan)
|
||||
{
|
||||
PresetPosition *pos;
|
||||
PresetSettings *settings;
|
||||
Event *event;
|
||||
MotionFrame *mf = &motion_frame;
|
||||
SList *rlist;
|
||||
char *region, buf[100];
|
||||
boolean save_show;
|
||||
|
||||
pos = (PresetPosition *) slist_nth_data(pikrellcam.preset_position_list,
|
||||
pikrellcam.preset_position_index);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
//printf("preset_load %d %d (%d %d) servo:%d %d\n",
|
||||
//pikrellcam.have_servos, do_pan,
|
||||
//pikrellcam.preset_position_index, pos->settings_index, pos->pan, pos->tilt);
|
||||
|
||||
if (pikrellcam.have_servos && do_pan && pos->pan > 0)
|
||||
{
|
||||
event_list_lock();
|
||||
if ((event = event_find("preset notify")) != NULL)
|
||||
if (event->count > 0)
|
||||
event->count = 1;
|
||||
event_list_unlock();
|
||||
servo_move(pos->pan, pos->tilt, pikrellcam.servo_preset_step_msec);
|
||||
|
||||
/* preset_load_values(FALSE) will be called again after move
|
||||
| if the move ends up on a preset
|
||||
*/
|
||||
return;
|
||||
}
|
||||
if (!pikrellcam.have_servos)
|
||||
{
|
||||
pikrellcam.on_preset = TRUE;
|
||||
pikrellcam.preset_last_on = pos;
|
||||
}
|
||||
|
||||
settings = (PresetSettings *) slist_nth_data(pos->settings_list, pos->settings_index);
|
||||
if (settings)
|
||||
{
|
||||
pikrellcam.motion_magnitude_limit = settings->mag_limit;
|
||||
pikrellcam.motion_magnitude_limit_count = settings->mag_limit_count;
|
||||
pikrellcam.motion_burst_count = settings->burst_count;
|
||||
pikrellcam.motion_burst_frames = settings->burst_frames;
|
||||
}
|
||||
save_show = mf->show_preset;
|
||||
motion_command("delete_regions all");
|
||||
for (rlist = settings->region_list; rlist; rlist = rlist->next)
|
||||
{
|
||||
region = (char *) rlist->data;
|
||||
snprintf(buf, sizeof(buf), "%s", region);
|
||||
motion_command(buf);
|
||||
}
|
||||
mf->show_preset = save_show;
|
||||
pikrellcam.preset_modified_warning = FALSE;
|
||||
pikrellcam.state_modified = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
preset_settings_regions_set(PresetSettings *settings)
|
||||
{
|
||||
MotionFrame *mf = &motion_frame;
|
||||
MotionRegion *mreg;
|
||||
SList *list;
|
||||
char *region;
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
slist_and_data_free(settings->region_list);
|
||||
settings->region_list = NULL;
|
||||
for (list = mf->motion_region_list; list; list = list->next)
|
||||
{
|
||||
mreg = (MotionRegion *) list->data;
|
||||
asprintf(®ion, "add_region %.3f %.3f %.3f %.3f\n",
|
||||
mreg->xf0, mreg->yf0, mreg->dxf, mreg->dyf);
|
||||
settings->region_list = slist_append(settings->region_list, region);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
preset_settings_set_modified(void)
|
||||
{
|
||||
PresetPosition *pos = NULL;
|
||||
PresetSettings *settings = NULL;
|
||||
int pan, tilt;
|
||||
|
||||
servo_get_position(&pan, &tilt);
|
||||
if ((pos = preset_find_at_position(pan, tilt)) != NULL)
|
||||
{
|
||||
settings = (PresetSettings *) slist_nth_data(pos->settings_list,
|
||||
pos->settings_index);
|
||||
if (settings)
|
||||
{
|
||||
settings->mag_limit = pikrellcam.motion_magnitude_limit;
|
||||
settings->mag_limit_count = pikrellcam.motion_magnitude_limit_count;
|
||||
settings->burst_count = pikrellcam.motion_burst_count;
|
||||
settings->burst_frames = pikrellcam.motion_burst_frames;
|
||||
pikrellcam.preset_modified = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
pikrellcam.preset_modified_warning = TRUE;
|
||||
}
|
||||
|
||||
/* Called from locked mf->region_list_mutex when a region is modified.
|
||||
*/
|
||||
void
|
||||
preset_regions_set_modified(void)
|
||||
{
|
||||
PresetPosition *pos = NULL;
|
||||
PresetSettings *settings = NULL;
|
||||
int pan, tilt;
|
||||
|
||||
servo_get_position(&pan, &tilt);
|
||||
if ((pos = preset_find_at_position(pan, tilt)) != NULL)
|
||||
{
|
||||
settings = (PresetSettings *) slist_nth_data(pos->settings_list,
|
||||
pos->settings_index);
|
||||
preset_settings_regions_set(settings);
|
||||
pikrellcam.preset_modified = TRUE;
|
||||
}
|
||||
else
|
||||
pikrellcam.preset_modified_warning = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
preset_new(PresetPosition *pos_src)
|
||||
{
|
||||
PresetPosition *pos = NULL;
|
||||
PresetSettings *settings = NULL, *settings_src;
|
||||
SList *slist, *rlist;
|
||||
char *region;
|
||||
int pan, tilt;
|
||||
|
||||
servo_get_position(&pan, &tilt);
|
||||
if (pikrellcam.preset_position_list)
|
||||
{
|
||||
pos = (PresetPosition *) slist_nth_data(pikrellcam.preset_position_list,
|
||||
pikrellcam.preset_position_index);
|
||||
if ( pikrellcam.have_servos
|
||||
&& (pos->pan != pan || pos->tilt != tilt)
|
||||
)
|
||||
pos = NULL;
|
||||
}
|
||||
if (!pos)
|
||||
{
|
||||
pos = calloc(1, sizeof(PresetPosition));
|
||||
pos->pan = pan;
|
||||
pos->tilt = tilt;
|
||||
pos->n_settings = 0;
|
||||
pos->settings_index = -1;
|
||||
pikrellcam.preset_position_list =
|
||||
slist_insert_sorted(pikrellcam.preset_position_list, pos,
|
||||
pan_position_cmp);
|
||||
pikrellcam.n_preset_positions += 1;
|
||||
pikrellcam.preset_position_index =
|
||||
slist_index(pikrellcam.preset_position_list, pos);
|
||||
if (pikrellcam.preset_position_index == -1)
|
||||
pikrellcam.preset_position_index = 0;
|
||||
}
|
||||
else
|
||||
pos_src = NULL;
|
||||
|
||||
if (pos_src)
|
||||
{
|
||||
for (slist = pos_src->settings_list; slist; slist = slist->next)
|
||||
{
|
||||
settings_src = (PresetSettings *) slist->data;
|
||||
settings = calloc(1, sizeof(PresetSettings));
|
||||
settings->mag_limit = settings_src->mag_limit;
|
||||
settings->mag_limit_count = settings_src->mag_limit_count;
|
||||
settings->burst_count = settings_src->burst_count;
|
||||
settings->burst_frames = settings_src->burst_frames;
|
||||
for (rlist = settings_src->region_list; rlist; rlist = rlist->next)
|
||||
{
|
||||
region = strdup((char *) rlist->data);
|
||||
settings->region_list = slist_append(settings->region_list, region);
|
||||
}
|
||||
pos->settings_list = slist_append(pos->settings_list, settings);
|
||||
pos->n_settings += 1;
|
||||
}
|
||||
pos->settings_index = pos_src->settings_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
settings = calloc(1, sizeof(PresetSettings));
|
||||
pos->settings_index += 1;
|
||||
pos->settings_list = slist_insert(pos->settings_list,
|
||||
settings, pos->settings_index);
|
||||
pos->n_settings += 1;
|
||||
settings->mag_limit = pikrellcam.motion_magnitude_limit;
|
||||
settings->mag_limit_count = pikrellcam.motion_magnitude_limit_count;
|
||||
settings->burst_count = pikrellcam.motion_burst_count;
|
||||
settings->burst_frames = pikrellcam.motion_burst_frames;
|
||||
preset_settings_regions_set(settings);
|
||||
preset_notify(18);
|
||||
}
|
||||
pikrellcam.preset_modified = TRUE;
|
||||
pikrellcam.preset_modified_warning = FALSE;
|
||||
pikrellcam.on_preset = TRUE;
|
||||
pikrellcam.preset_last_on = pos;
|
||||
}
|
||||
|
||||
static void
|
||||
preset_delete(void)
|
||||
{
|
||||
PresetPosition *pos = NULL;
|
||||
PresetSettings *settings = NULL;
|
||||
int pan, tilt, idx;
|
||||
char buf[100];
|
||||
|
||||
if (!pikrellcam.preset_position_list)
|
||||
return;
|
||||
if (pikrellcam.have_servos)
|
||||
{
|
||||
servo_get_position(&pan, &tilt);
|
||||
if ((pos = preset_find_at_position(pan, tilt)) == NULL)
|
||||
{
|
||||
display_inform("\"Position is not on a preset.\" 3 3 1");
|
||||
display_inform("\"Cannot delete.\" 4 3 1");
|
||||
display_inform("timeout 3");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
pos = (PresetPosition *) slist_nth_data(pikrellcam.preset_position_list,
|
||||
pikrellcam.preset_position_index);
|
||||
|
||||
if (pikrellcam.n_preset_positions == 1 && pos->n_settings == 1)
|
||||
{
|
||||
display_inform("\"You must have at least one preset.\" 3 3 1");
|
||||
display_inform("\"Cannot delete your only preset.\" 4 3 1");
|
||||
display_inform("timeout 3");
|
||||
return;
|
||||
}
|
||||
settings = (PresetSettings *) slist_nth_data(pos->settings_list, pos->settings_index);
|
||||
slist_and_data_free(settings->region_list);
|
||||
pos->settings_list = slist_remove(pos->settings_list, settings);
|
||||
free(settings);
|
||||
|
||||
pos->n_settings -= 1;
|
||||
if ((idx = pos->settings_index) >= pos->n_settings)
|
||||
pos->settings_index -= 1;
|
||||
if (pos->n_settings <= 0)
|
||||
{
|
||||
display_inform("\"Deleted preset at current position.\" 3 3 1");
|
||||
display_inform("timeout 2");
|
||||
pikrellcam.preset_position_list = slist_remove(pikrellcam.preset_position_list, pos);
|
||||
pikrellcam.n_preset_positions -= 1;
|
||||
pikrellcam.on_preset = FALSE;
|
||||
pikrellcam.preset_last_on = NULL;
|
||||
if (pikrellcam.preset_position_index > 0)
|
||||
pikrellcam.preset_position_index -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "\"Deleted preset settings %d.\" 3 3 1", idx + 1);
|
||||
display_inform(buf);
|
||||
display_inform("timeout 2");
|
||||
}
|
||||
preset_load_values(FALSE);
|
||||
preset_notify(18);
|
||||
pikrellcam.preset_modified = TRUE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
preset_command(char *cmd_line)
|
||||
{
|
||||
PresetCommand *pcmd;
|
||||
PresetPosition *pos;
|
||||
SList *list;
|
||||
int i, n, id = -1;
|
||||
int pan, tilt, dpan, dtilt;
|
||||
char buf[64], arg1[32];
|
||||
boolean move_all = FALSE;
|
||||
|
||||
arg1[0] = '\0';
|
||||
n = sscanf(cmd_line, "%63s %[^\n]", buf, arg1);
|
||||
if (n < 1)
|
||||
return;
|
||||
|
||||
for (i = 0; i < N_PRESET_COMMANDS; ++i)
|
||||
{
|
||||
pcmd = &preset_commands[i];
|
||||
if (!strcmp(pcmd->name, buf))
|
||||
{
|
||||
if (pcmd->n_args <= n - 1)
|
||||
id = pcmd->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id == -1)
|
||||
{
|
||||
// inform_message("Bad preset command.");
|
||||
return;
|
||||
}
|
||||
|
||||
servo_get_position(&pan, &tilt);
|
||||
switch (id)
|
||||
{
|
||||
case PREV_POSITION:
|
||||
pikrellcam.preset_position_index = preset_position_prev();
|
||||
preset_load_values(TRUE);
|
||||
break;
|
||||
|
||||
case NEXT_POSITION:
|
||||
pikrellcam.preset_position_index = preset_position_next();
|
||||
preset_load_values(TRUE);
|
||||
break;
|
||||
|
||||
case PREV_SETTINGS:
|
||||
pos = (PresetPosition *) slist_nth_data(pikrellcam.preset_position_list, pikrellcam.preset_position_index);
|
||||
if (pos)
|
||||
{
|
||||
if (pos->tilt == tilt && pos->pan == pan)
|
||||
{
|
||||
if (pos->settings_index > 0)
|
||||
{
|
||||
--pos->settings_index;
|
||||
preset_load_values(FALSE);
|
||||
preset_notify(18);
|
||||
}
|
||||
else
|
||||
preset_notify(10);
|
||||
}
|
||||
else
|
||||
preset_load_values(TRUE);
|
||||
}
|
||||
break;
|
||||
|
||||
case NEXT_SETTINGS:
|
||||
pos = (PresetPosition *) slist_nth_data(pikrellcam.preset_position_list, pikrellcam.preset_position_index);
|
||||
if (pos)
|
||||
{
|
||||
if (pos->tilt == tilt && pos->pan == pan)
|
||||
{
|
||||
n = slist_length(pos->settings_list);
|
||||
if (pos->settings_index < n - 1)
|
||||
{
|
||||
++pos->settings_index;
|
||||
preset_load_values(FALSE);
|
||||
preset_notify(18);
|
||||
}
|
||||
else
|
||||
preset_notify(10);
|
||||
}
|
||||
else
|
||||
preset_load_values(TRUE);
|
||||
}
|
||||
break;
|
||||
|
||||
case MOVE_ALL:
|
||||
move_all = TRUE;
|
||||
case MOVE_ONE:
|
||||
if (pikrellcam.on_preset || !pikrellcam.preset_last_on)
|
||||
{
|
||||
if (pikrellcam.on_preset)
|
||||
display_inform("\"Servo position is on a preset.\" 2 3 1");
|
||||
else
|
||||
display_inform("\"Move to a preset first, then off.\" 2 3 1");
|
||||
display_inform("\"Servos must be moved off a preset\" 3 3 1");
|
||||
display_inform("\"before the preset can be moved.\" 4 3 1");
|
||||
display_inform("timeout 3");
|
||||
}
|
||||
else if (pikrellcam.preset_last_on)
|
||||
{
|
||||
dpan = pan - pikrellcam.preset_last_on->pan;
|
||||
dtilt = tilt - pikrellcam.preset_last_on->tilt;
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
if (move_all || pos == pikrellcam.preset_last_on)
|
||||
{
|
||||
if ( pos->pan + dpan > pikrellcam.servo_pan_max
|
||||
|| pos->pan + dpan < pikrellcam.servo_pan_min
|
||||
|| pos->tilt + dtilt > pikrellcam.servo_tilt_max
|
||||
|| pos->tilt + dtilt < pikrellcam.servo_tilt_min
|
||||
)
|
||||
{
|
||||
display_inform("\"Error:\" 2 3 1");
|
||||
display_inform("\"Can't move a preset past\" 3 3 1");
|
||||
display_inform("\"servo pan/tilt limits.\" 4 3 1");
|
||||
display_inform("timeout 3");
|
||||
dpan = dtilt = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (move_all)
|
||||
{
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
pos->pan += dpan;
|
||||
pos->tilt += dtilt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = pikrellcam.preset_last_on;
|
||||
pos->pan += dpan;
|
||||
pos->tilt += dtilt;
|
||||
|
||||
/* Re insert sorted in case moved past another preset.
|
||||
*/
|
||||
pikrellcam.preset_position_list =
|
||||
slist_remove(pikrellcam.preset_position_list, pos);
|
||||
pikrellcam.preset_position_list =
|
||||
slist_insert_sorted(pikrellcam.preset_position_list,
|
||||
pos, pan_position_cmp);
|
||||
}
|
||||
if (dpan != 0 || dtilt != 0)
|
||||
{
|
||||
preset_on_check(pan, tilt);
|
||||
pikrellcam.preset_modified = TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GOTO:
|
||||
if (sscanf(arg1, "%d %d", &n, &i) == 2)
|
||||
{
|
||||
if (--n < 0) /* n was 1 based position number */
|
||||
n = pikrellcam.preset_position_index;
|
||||
pos = (PresetPosition *) slist_nth_data(pikrellcam.preset_position_list, n);
|
||||
if (pos)
|
||||
{
|
||||
pikrellcam.preset_position_index = n;
|
||||
--i; /* settings number was 1 based */
|
||||
if (i < 0 || i >= slist_length(pos->settings_list))
|
||||
i = 0;
|
||||
pos->settings_index = i;
|
||||
preset_load_values(TRUE);
|
||||
preset_notify(18);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PRESET_NEW:
|
||||
pos = preset_find_at_position(pan, 0);
|
||||
if (pos)
|
||||
{
|
||||
if (pos->tilt == tilt)
|
||||
{
|
||||
preset_new(NULL);
|
||||
display_inform("\"Created new settings at current preset\" 3 3 1");
|
||||
display_inform("timeout 2");
|
||||
}
|
||||
else
|
||||
{
|
||||
display_inform("\"Pan position is on a preset but tilt\" 2 3 1");
|
||||
display_inform("\"is not. Cannot create new settings\" 3 3 1");
|
||||
display_inform("\"for this preset.\" 4 3 1");
|
||||
display_inform("timeout 3");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
preset_new(NULL);
|
||||
display_inform("\"Created preset at new position.\" 3 3 1");
|
||||
display_inform("timeout 2");
|
||||
}
|
||||
break;
|
||||
|
||||
case PRESET_COPY:
|
||||
pos = preset_find_at_position(pan, 0);
|
||||
if (pos || !pikrellcam.preset_last_on)
|
||||
{
|
||||
if (pos)
|
||||
display_inform("\"Pan position is on a preset.\" 2 3 1");
|
||||
else
|
||||
display_inform("\"Move to a preset first, then off.\" 2 3 1");
|
||||
display_inform("\"Pan servo must be moved off a\" 3 3 1");
|
||||
display_inform("\"preset before you can copy it.\" 4 3 1");
|
||||
display_inform("timeout 3");
|
||||
}
|
||||
else
|
||||
{
|
||||
preset_new(pikrellcam.preset_last_on);
|
||||
display_inform("\"Copied preset into new position.\" 3 3 1");
|
||||
display_inform("timeout 2");
|
||||
}
|
||||
break;
|
||||
|
||||
case PRESET_DELETE:
|
||||
preset_delete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
preset_config_load(void)
|
||||
{
|
||||
FILE *f;
|
||||
PresetPosition *pos = NULL;
|
||||
PresetSettings *settings = NULL;
|
||||
char *region;
|
||||
int pan, tilt;
|
||||
char buf[100];
|
||||
|
||||
if ((f = fopen(pikrellcam.preset_config_file, "r")) == NULL)
|
||||
{
|
||||
preset_new(NULL);
|
||||
preset_config_save();
|
||||
return;
|
||||
}
|
||||
while (fgets(buf, sizeof(buf), f) != NULL)
|
||||
{
|
||||
if (buf[0] == '#' || buf[0] == '\n')
|
||||
continue;
|
||||
if (sscanf(buf, "<position %d %d>", &pan, &tilt) == 2)
|
||||
{
|
||||
pos = calloc(1, sizeof(PresetPosition));
|
||||
pos->pan = pan;
|
||||
pos->tilt = tilt;
|
||||
pikrellcam.preset_position_list = slist_append(pikrellcam.preset_position_list, pos);
|
||||
pikrellcam.n_preset_positions += 1;
|
||||
continue;
|
||||
}
|
||||
if (!pos)
|
||||
continue;
|
||||
if (!strncmp(buf, "<settings>", 10))
|
||||
{
|
||||
settings = calloc(1, sizeof(PresetSettings));
|
||||
pos->settings_list = slist_append(pos->settings_list, settings);
|
||||
pos->n_settings += 1;
|
||||
continue;
|
||||
}
|
||||
if (!settings)
|
||||
continue;
|
||||
|
||||
if (!strncmp(buf, "motion", 6))
|
||||
sscanf(buf + 7, "%d %d %d %d",
|
||||
&settings->mag_limit, &settings->mag_limit_count,
|
||||
&settings->burst_count, &settings->burst_frames);
|
||||
|
||||
if (!strncmp(buf, "magnitude_limit", 15))
|
||||
sscanf(buf + 16, "%d", &settings->mag_limit);
|
||||
else if (!strncmp(buf, "magnitude_count", 15))
|
||||
sscanf(buf + 16, "%d", &settings->mag_limit_count);
|
||||
else if (!strncmp(buf, "burst_count", 11))
|
||||
sscanf(buf + 12, "%d", &settings->burst_count);
|
||||
else if (!strncmp(buf, "burst_frames", 12))
|
||||
sscanf(buf + 13, "%d", &settings->burst_frames);
|
||||
else if (!strncmp(buf, "add_region", 10))
|
||||
{
|
||||
region = strdup(buf); /* string "add_region xf0 yf0 dxf dyf" */
|
||||
settings->region_list = slist_append(settings->region_list, region);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
/* read preset state file to load position_index, settings_index */
|
||||
}
|
||||
|
||||
void
|
||||
preset_config_save(void)
|
||||
{
|
||||
FILE *f;
|
||||
SList *list, *slist, *rlist;
|
||||
PresetPosition *pos;
|
||||
PresetSettings *settings;
|
||||
char *region;
|
||||
|
||||
if ((f = fopen(pikrellcam.preset_config_file, "w")) == NULL)
|
||||
{
|
||||
log_printf("Failed to save preset config file %s. %m\n",
|
||||
pikrellcam.preset_config_file);
|
||||
return;
|
||||
}
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
fprintf(f, "<position %d %d>\n", pos->pan, pos->tilt);
|
||||
for (slist = pos->settings_list; slist; slist = slist->next)
|
||||
{
|
||||
settings = (PresetSettings *) slist->data;
|
||||
fprintf(f, "<settings>\n");
|
||||
fprintf(f, "magnitude_limit %d\n", settings->mag_limit);
|
||||
fprintf(f, "magnitude_count %d\n", settings->mag_limit_count);
|
||||
fprintf(f, "burst_count %d\n", settings->burst_count);
|
||||
fprintf(f, "burst_frames %d\n", settings->burst_frames);
|
||||
for (rlist = settings->region_list; rlist; rlist = rlist->next)
|
||||
{
|
||||
region = (char *) rlist->data; /* string "add_region xf0 yf0 dxf dyf" */
|
||||
fprintf(f, "%s", region);
|
||||
}
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
fclose(f);
|
||||
pikrellcam.preset_modified = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
preset_state_save(void)
|
||||
{
|
||||
FILE *f;
|
||||
PresetPosition *pos;
|
||||
SList *list;
|
||||
int pan, tilt;
|
||||
|
||||
f = fopen(pikrellcam.preset_state_file, "w");
|
||||
if (f)
|
||||
{
|
||||
servo_get_position(&pan, &tilt);
|
||||
fprintf(f, "pan %d\n", pan);
|
||||
fprintf(f, "tilt %d\n", tilt);
|
||||
fprintf(f, "position_index %d\n", pikrellcam.preset_position_index);
|
||||
for (list = pikrellcam.preset_position_list; list; list = list->next)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
fprintf(f, "settings_index %d\n", pos->settings_index);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
preset_state_load(void)
|
||||
{
|
||||
FILE *f;
|
||||
SList *list = pikrellcam.preset_position_list;
|
||||
PresetPosition *pos;
|
||||
int pan = 150, tilt = 150, i;
|
||||
char buf[100];
|
||||
|
||||
if ((f = fopen(pikrellcam.preset_state_file, "r")) == NULL)
|
||||
{
|
||||
preset_load_values(TRUE);
|
||||
return;
|
||||
}
|
||||
while (fgets(buf, sizeof(buf), f) != NULL)
|
||||
{
|
||||
if (buf[0] == '#' || buf[0] == '\n')
|
||||
continue;
|
||||
/* XXX */
|
||||
if (!strncmp(buf, "position_index settings_index pan tilt", 38))
|
||||
{
|
||||
fgets(buf, sizeof(buf), f);
|
||||
sscanf(buf, "%d %d %d %d\n", &pikrellcam.preset_position_index,
|
||||
&i, &pan, &tilt);
|
||||
break;
|
||||
}
|
||||
/* XXX */
|
||||
|
||||
if (!strncmp(buf, "pan", 3))
|
||||
sscanf(buf + 4, "%d", &pan);
|
||||
else if (!strncmp(buf, "tilt", 4))
|
||||
sscanf(buf + 5, "%d", &tilt);
|
||||
else if (!strncmp(buf, "position_index", 14))
|
||||
sscanf(buf + 15, "%d", &pikrellcam.preset_position_index);
|
||||
else if (!strncmp(buf, "settings_index", 14) && list)
|
||||
{
|
||||
pos = (PresetPosition *) list->data;
|
||||
sscanf(buf + 15, "%d", &pos->settings_index);
|
||||
if ( pos->settings_index < 0
|
||||
|| pos->settings_index > slist_length(pos->settings_list) - 1
|
||||
)
|
||||
pos->settings_index = 0;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
if ( pikrellcam.preset_position_index < 0
|
||||
|| pikrellcam.preset_position_index > pikrellcam.n_preset_positions - 1
|
||||
)
|
||||
pikrellcam.preset_position_index = 0;
|
||||
if (pan < pikrellcam.servo_pan_min || pan > pikrellcam.servo_pan_max)
|
||||
pan = 150;
|
||||
if (tilt < pikrellcam.servo_tilt_min || tilt > pikrellcam.servo_tilt_max)
|
||||
tilt = 150;
|
||||
|
||||
/* Don't move to the preset position because we may not have been on
|
||||
| a preset when stopped. Just move to saved pan/tilt.
|
||||
*/
|
||||
preset_load_values(FALSE);
|
||||
if (pikrellcam.have_servos)
|
||||
servo_move(pan, tilt, pikrellcam.servo_move_step_msec);
|
||||
else
|
||||
preset_notify(22);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,637 @@
|
|||
|
||||
/* PiKrellCam
|
||||
|
|
||||
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
|
||||
|
|
||||
| PiKrellCam is free software: you can redistribute it and/or modify it
|
||||
| under the terms of the GNU General Public License as published by
|
||||
| the Free Software Foundation, either version 3 of the License, or
|
||||
| (at your option) any later version.
|
||||
|
|
||||
| PiKrellCam is distributed in the hope that it will be useful, but WITHOUT
|
||||
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
| License for more details.
|
||||
|
|
||||
| You should have received a copy of the GNU General Public License
|
||||
| along with this program. If not, see http://www.gnu.org/licenses/
|
||||
|
|
||||
| This file is part of PiKrellCam.
|
||||
*/
|
||||
|
||||
/* BCM2835-ARM-Peripherals.pdf document:
|
||||
| https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf
|
||||
| Addendum covers clock manager:
|
||||
| http://www.scribd.com/doc/127599939/BCM2835-Audio-clocks
|
||||
*/
|
||||
|
||||
#include "pikrellcam.h"
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define PI_1_PERIPHERAL_BASE 0x20000000
|
||||
#define PI_2_PERIPHERAL_BASE 0x3F000000
|
||||
|
||||
#define GPIO_BASE 0x200000
|
||||
#define PWM_BASE 0x20C000
|
||||
#define CLOCK_BASE 0x101000
|
||||
|
||||
|
||||
/* xxx_REG defines are uint32_t pointer offsets to registers in the gpio,
|
||||
| pwm or clock address space and are BCM2835-ARM-Peripherals.pdf or scribd
|
||||
| register byte addresses / 4
|
||||
*/
|
||||
#define PWM_CTL_REG (0x0 / 4)
|
||||
#define CTL_REG_RESET_STATE 0
|
||||
#define CTL_REG_PWM1_ENABLE 1
|
||||
#define CTL_REG_MSEN1 0x80
|
||||
#define CTL_REG_PWM1_MS_MODE (CTL_REG_PWM1_ENABLE | CTL_REG_MSEN1)
|
||||
#define CTL_REG_PWM2_ENABLE 0x100
|
||||
#define CTL_REG_MSEN2 0x8000
|
||||
#define CTL_REG_PWM2_MS_MODE (CTL_REG_PWM2_ENABLE | CTL_REG_MSEN2)
|
||||
|
||||
#define PWM_RNG1_REG (0x10 / 4)
|
||||
#define PWM_DAT1_REG (0x14 / 4)
|
||||
#define PWM_RNG2_REG (0x20 / 4)
|
||||
#define PWM_DAT2_REG (0x24 / 4)
|
||||
|
||||
#define GPSET_REG (0x1c / 4)
|
||||
#define GPCLR_REG (0x28 / 4)
|
||||
#define GPLEV_REG (0x34 / 4)
|
||||
#define GPPUD_REG (0x94 / 4)
|
||||
#define PUD_DOWN 1
|
||||
#define PUD_UP 2
|
||||
#define GPPUDCLK_REG (0x98 / 4)
|
||||
|
||||
|
||||
/* PWM clock manager registers CM_PWMDIV & CM_PWMCTL from the scribd addendum:
|
||||
*/
|
||||
#define CM_PASSWORD 0x5A000000
|
||||
#define CM_PWMCTL_REG (0xa0 / 4)
|
||||
#define PWMCTL_BUSY 0x80 // Read only
|
||||
#define PWMCTL_KILL 0x20
|
||||
#define PWMCTL_ENABLE 0x10
|
||||
#define PWMCTL_SRC_OSC 0x1
|
||||
#define CM_PWMDIV_REG (0xa4 / 4)
|
||||
#define PWMDIV_DIVI(divi) (divi << 12) // bits 23-12, max 4095
|
||||
|
||||
|
||||
#define PWM_CLOCK_HZ 19200000.0
|
||||
#define PWM_RESOLUTION 0.000005 // 5 usec resolution
|
||||
#define PWM_MSEC_TO_COUNT(ms) ((ms) / PWM_RESOLUTION / 1000.0)
|
||||
#define PULSE_WIDTH_RESOLUTION .00001 // .01 msec resolution
|
||||
|
||||
#define SERVO_MODE_MOVE_ONE 0
|
||||
#define SERVO_MODE_MOVE_STEPS 1
|
||||
#define SERVO_MODE_MOVE_LIMIT 2
|
||||
|
||||
#define SERVO_IDLE 0
|
||||
#define SERVO_NEW_MOVE 1
|
||||
#define SERVO_MOVING 2
|
||||
#define SERVO_STOP 3
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
int status;
|
||||
int pan,
|
||||
tilt,
|
||||
delay;
|
||||
}
|
||||
ServoControl;
|
||||
|
||||
static ServoControl servo_control;
|
||||
|
||||
/* Pointers to mapped peripheral registers.
|
||||
*/
|
||||
static volatile uint32_t *gpio_mmap;
|
||||
static volatile uint32_t *pwm_mmap;
|
||||
static volatile uint32_t *clock_mmap;
|
||||
|
||||
|
||||
static pthread_t servo_thread_ref;
|
||||
|
||||
static void (*pwm_width_func)(int channel, int width, boolean invert);
|
||||
|
||||
static FILE *fblaster;
|
||||
|
||||
static float pan_cur = 150.0,
|
||||
tilt_cur = 150.0;
|
||||
|
||||
static int pan_channel,
|
||||
tilt_channel;
|
||||
|
||||
/* Servo pulse width units are .01 msec (so width = 150 is 1.5 msec)
|
||||
*/
|
||||
void
|
||||
pwm_width_hardware(int channel, int width, boolean invert)
|
||||
{
|
||||
uint32_t count;
|
||||
int reg;
|
||||
|
||||
if (channel == 1)
|
||||
reg = PWM_DAT1_REG;
|
||||
else if (channel == 2)
|
||||
reg = PWM_DAT2_REG;
|
||||
else
|
||||
return;
|
||||
|
||||
if (invert)
|
||||
width = 300 - width; // 150 msec is center
|
||||
if (width < SERVO_MIN_WIDTH)
|
||||
width = SERVO_MIN_WIDTH;
|
||||
if (width > SERVO_MAX_WIDTH)
|
||||
width = SERVO_MAX_WIDTH;
|
||||
count = (uint32_t) (PULSE_WIDTH_RESOLUTION / PWM_RESOLUTION) * width;
|
||||
*(pwm_mmap + reg) = count;
|
||||
}
|
||||
|
||||
void
|
||||
pwm_width_servoblaster(int channel, int width, boolean invert)
|
||||
{
|
||||
char buf[64];
|
||||
static boolean logged = FALSE;
|
||||
|
||||
if (channel < 0)
|
||||
return;
|
||||
if (!fblaster)
|
||||
fblaster = fopen("/dev/servoblaster", "w");
|
||||
if (!fblaster)
|
||||
{
|
||||
if (!logged)
|
||||
log_printf_no_timestamp("Failed to open /dev/servoblaster: %m\n");
|
||||
logged = TRUE;
|
||||
return;
|
||||
}
|
||||
if (invert)
|
||||
width = 300 - width;
|
||||
if (width < SERVO_MIN_WIDTH)
|
||||
width = SERVO_MIN_WIDTH;
|
||||
if (width > SERVO_MAX_WIDTH)
|
||||
width = SERVO_MAX_WIDTH;
|
||||
snprintf(buf, sizeof(buf), "%d=%d\n", channel, width);
|
||||
fwrite(buf, strlen(buf), 1, fblaster);
|
||||
fflush(fblaster);
|
||||
}
|
||||
|
||||
void
|
||||
gpio_to_channel(int gpio, int *channel, int *altfn)
|
||||
{
|
||||
int chan = -1, alt = -1;
|
||||
|
||||
if (gpio == 12 || gpio == 18)
|
||||
{
|
||||
chan = 1;
|
||||
alt = (gpio == 18) ? 5 : 0;
|
||||
}
|
||||
if (gpio == 13 || gpio == 19)
|
||||
{
|
||||
chan = 2;
|
||||
alt = (gpio == 19) ? 5 : 0;
|
||||
}
|
||||
if (channel)
|
||||
*channel = chan;
|
||||
if (altfn)
|
||||
*altfn = alt;
|
||||
}
|
||||
|
||||
void
|
||||
servo_get_position(int *pan, int *tilt)
|
||||
{
|
||||
if (pan)
|
||||
*pan = (int) pan_cur;
|
||||
if (tilt)
|
||||
*tilt = (int) tilt_cur;
|
||||
}
|
||||
|
||||
/* GPFSELn registers start at offset zero from gpio_mmap.
|
||||
| BCM2835-ARM-Peripherals.pdf pg 91, 10 gpios per GPFSELn with mode bits:
|
||||
*/
|
||||
static unsigned int gpfsel_mode_table[] =
|
||||
{
|
||||
/* in out alt0 alt1 alt2 alt3 alt4 alt5 */
|
||||
0b000, 0b001, 0b100, 0b101, 0b110, 0b111, 0b011, 0b010
|
||||
};
|
||||
|
||||
void
|
||||
gpio_alt_function(int pin, int altfn)
|
||||
{
|
||||
int reg = pin / 10,
|
||||
shift = (pin % 10) * 3;
|
||||
|
||||
if (altfn >= 0 && altfn <= 5)
|
||||
*(gpio_mmap + reg) = (*(gpio_mmap + reg) & ~(0x7 << shift))
|
||||
| (gpfsel_mode_table[altfn + 2] << shift);
|
||||
}
|
||||
|
||||
void
|
||||
gpio_set_mode(int pin, int mode) /* mode 0:input 1:output */
|
||||
{
|
||||
int reg = pin / 10,
|
||||
shift = (pin % 10) * 3;
|
||||
|
||||
if (mode == 0 || mode == 1)
|
||||
*(gpio_mmap + reg) = (*(gpio_mmap + reg) & ~(0x7 << shift))
|
||||
| (gpfsel_mode_table[mode] << shift);
|
||||
}
|
||||
|
||||
|
||||
/* BCM2835-ARM-Peripherals.pdf pg 101 - GPIO Pull-up/down sequence
|
||||
*/
|
||||
void
|
||||
gpio_set_pud(int pin, int pud)
|
||||
{
|
||||
int reg = GPPUDCLK_REG + ((pin > 31) ? 1 : 0);
|
||||
|
||||
if (pud != PUD_DOWN && pud != PUD_UP)
|
||||
return;
|
||||
*(gpio_mmap + GPPUD_REG) = pud;
|
||||
usleep(2); // min wait of 150 cycles
|
||||
*(gpio_mmap + reg) = 1 << (pin & 0x1f);
|
||||
usleep(2);
|
||||
*(gpio_mmap + GPPUD_REG) = 0;
|
||||
*(gpio_mmap + reg) = 0;
|
||||
}
|
||||
|
||||
int
|
||||
gpio_read(int pin)
|
||||
{
|
||||
int reg = GPLEV_REG + ((pin > 31) ? 1 : 0);
|
||||
|
||||
return (*(gpio_mmap + reg) & (1 << (pin & 0x1f)) ? 1 : 0 );
|
||||
}
|
||||
|
||||
void
|
||||
gpio_write(int pin, int level)
|
||||
{
|
||||
int reg = ((level == 0) ? GPCLR_REG : GPSET_REG) + ((pin > 31) ? 1 : 0);
|
||||
|
||||
*(gpio_mmap + reg) = 1 << (pin & 0x1f);
|
||||
}
|
||||
|
||||
int
|
||||
pi_model(void)
|
||||
{
|
||||
FILE *f;
|
||||
static int model;
|
||||
char buf[200], arm[32];
|
||||
|
||||
if (model == 0)
|
||||
{
|
||||
if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
|
||||
{
|
||||
while (fgets(buf, sizeof(buf), f) != NULL)
|
||||
{
|
||||
if (sscanf(buf, "model name %*s %31s", arm) > 0)
|
||||
{
|
||||
if (!strcmp(arm, "ARMv7"))
|
||||
model = 2;
|
||||
else
|
||||
model = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
static void
|
||||
_servo_move(int pan, int tilt, int delay)
|
||||
{
|
||||
float pan_inc, tilt_inc;
|
||||
int pan_delta, tilt_delta, max_delta, i;
|
||||
|
||||
if (pan_channel < 0 && tilt_channel < 0)
|
||||
return;
|
||||
|
||||
servo_control.status = SERVO_MOVING;
|
||||
pikrellcam.servo_moving = TRUE;
|
||||
|
||||
if (pan_cur == 0)
|
||||
pan_cur = (float) pan;
|
||||
if (tilt_cur == 0)
|
||||
tilt_cur = (float) tilt;
|
||||
|
||||
pan_delta = pan - pan_cur;
|
||||
tilt_delta = tilt - tilt_cur;
|
||||
max_delta = MAX(abs(pan_delta), abs(tilt_delta));
|
||||
|
||||
pan_inc = (abs(pan_delta) > 1.0)
|
||||
? (float) pan_delta / (float) max_delta : 0;
|
||||
tilt_inc = (abs(tilt_delta) > 1.0)
|
||||
? (float) tilt_delta / (float) max_delta : 0;
|
||||
|
||||
//printf("pan: %d pan_cur:%.0f pan_delta:%d pan_inc:%.2f\n",
|
||||
// pan, pan_cur, pan_delta, pan_inc);
|
||||
//printf("tilt:%d tilt_cur:%.0f tilt_delta:%d tilt_inc:%.2f\n",
|
||||
// tilt, tilt_cur, tilt_delta, tilt_inc);
|
||||
|
||||
for (i = 1; i < max_delta && delay > 0; ++i)
|
||||
{
|
||||
pan_cur += pan_inc;
|
||||
tilt_cur += tilt_inc;
|
||||
pwm_width_func(pan_channel, (int) pan_cur, pikrellcam.servo_pan_invert);
|
||||
pwm_width_func(tilt_channel, (int) tilt_cur, pikrellcam.servo_tilt_invert);
|
||||
usleep(delay * 1000);
|
||||
pthread_mutex_lock(&servo_control.mutex);
|
||||
if (servo_control.status != SERVO_MOVING)
|
||||
{
|
||||
pan_cur = floorf(pan_cur);
|
||||
tilt_cur = floorf(tilt_cur);
|
||||
if (servo_control.status != SERVO_NEW_MOVE)
|
||||
{
|
||||
preset_on_check((int) pan_cur, (int) tilt_cur);
|
||||
pthread_mutex_unlock(&servo_control.mutex);
|
||||
usleep(pikrellcam.servo_settle_msec * 1000);
|
||||
pikrellcam.servo_moving = FALSE;
|
||||
pikrellcam.state_modified = TRUE;
|
||||
}
|
||||
else
|
||||
pthread_mutex_unlock(&servo_control.mutex);
|
||||
return;
|
||||
}
|
||||
pthread_mutex_unlock(&servo_control.mutex);
|
||||
}
|
||||
pan_cur = (float) pan;
|
||||
tilt_cur = (float) tilt;
|
||||
pwm_width_func(pan_channel, pan, pikrellcam.servo_pan_invert);
|
||||
pwm_width_func(tilt_channel, tilt, pikrellcam.servo_tilt_invert);
|
||||
pthread_mutex_lock(&servo_control.mutex);
|
||||
if (servo_control.status != SERVO_NEW_MOVE)
|
||||
{
|
||||
servo_control.status = SERVO_IDLE;
|
||||
preset_on_check(pan, tilt);
|
||||
pthread_mutex_unlock(&servo_control.mutex);
|
||||
usleep(pikrellcam.servo_settle_msec * 1000);
|
||||
pikrellcam.servo_moving = FALSE;
|
||||
pikrellcam.state_modified = TRUE;
|
||||
}
|
||||
else
|
||||
pthread_mutex_unlock(&servo_control.mutex);
|
||||
}
|
||||
|
||||
static void *
|
||||
servo_thread(void *ptr)
|
||||
{
|
||||
static boolean first_move_done;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (servo_control.status != SERVO_NEW_MOVE)
|
||||
{
|
||||
usleep(50000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!first_move_done) /* Minimize possible current spike */
|
||||
{
|
||||
pikrellcam.servo_moving = TRUE;
|
||||
pthread_mutex_lock(&servo_control.mutex);
|
||||
pwm_width_func(pan_channel, servo_control.pan, pikrellcam.servo_pan_invert);
|
||||
usleep(300000);
|
||||
pwm_width_func(tilt_channel, servo_control.tilt, pikrellcam.servo_tilt_invert);
|
||||
usleep(300000);
|
||||
pan_cur = servo_control.pan;
|
||||
tilt_cur = servo_control.tilt;
|
||||
servo_control.status = SERVO_IDLE;
|
||||
preset_on_check((int) pan_cur, (int) tilt_cur);
|
||||
pthread_mutex_unlock(&servo_control.mutex);
|
||||
pikrellcam.servo_moving = FALSE;
|
||||
pikrellcam.state_modified = TRUE;
|
||||
}
|
||||
else
|
||||
_servo_move(servo_control.pan, servo_control.tilt, servo_control.delay);
|
||||
first_move_done = TRUE;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
servo_move(int pan, int tilt, int delay)
|
||||
{
|
||||
if (!pikrellcam.have_servos)
|
||||
return;
|
||||
pthread_mutex_lock(&servo_control.mutex);
|
||||
servo_control.pan = pan;
|
||||
servo_control.tilt = tilt;
|
||||
servo_control.delay = delay;
|
||||
servo_control.status = SERVO_NEW_MOVE;
|
||||
pikrellcam.on_preset = FALSE;
|
||||
pthread_mutex_unlock(&servo_control.mutex);
|
||||
}
|
||||
|
||||
void
|
||||
servo_init(void)
|
||||
{
|
||||
int fd, peripheral_base, pan_alt, tilt_alt;
|
||||
uint32_t divi, t1, t2;
|
||||
|
||||
pan_cur = tilt_cur = 150.0;
|
||||
if (!pikrellcam.have_servos)
|
||||
return;
|
||||
if (pikrellcam.servo_use_servoblaster)
|
||||
{
|
||||
pwm_width_func = pwm_width_servoblaster;
|
||||
pan_channel = pikrellcam.servo_pan_gpio;
|
||||
tilt_channel = pikrellcam.servo_tilt_gpio;
|
||||
pthread_create (&servo_thread_ref, NULL, servo_thread, NULL);
|
||||
log_printf_no_timestamp("======= Servo using ServoBlaster (%d %d)\n",
|
||||
pan_channel, tilt_channel);
|
||||
return;
|
||||
}
|
||||
pwm_width_func = pwm_width_hardware;
|
||||
gpio_to_channel(pikrellcam.servo_pan_gpio, &pan_channel, &pan_alt);
|
||||
gpio_to_channel(pikrellcam.servo_tilt_gpio, &tilt_channel, &tilt_alt);
|
||||
if ( (pan_channel < 0 && tilt_channel < 0)
|
||||
|| (pan_channel == tilt_channel)
|
||||
)
|
||||
{
|
||||
log_printf_no_timestamp("======= Servo init failed, bad gpio numbers: %s\n",
|
||||
(pan_channel == tilt_channel && pan_channel != -1) ?
|
||||
"PWM channel collision" : "gpio number not a hardware PWM");
|
||||
pan_channel = tilt_channel = -1;
|
||||
return;
|
||||
}
|
||||
peripheral_base = (pi_model() == 2) ? PI_2_PERIPHERAL_BASE : PI_1_PERIPHERAL_BASE;
|
||||
|
||||
if ((fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)
|
||||
{
|
||||
pan_channel = tilt_channel = -1;
|
||||
log_printf_no_timestamp("======= Servo init failed: /dev/mem open failed.");
|
||||
return;
|
||||
}
|
||||
gpio_mmap = (uint32_t *) mmap(NULL, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED,
|
||||
fd, peripheral_base + GPIO_BASE);
|
||||
pwm_mmap = (uint32_t *) mmap(NULL, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED,
|
||||
fd, peripheral_base + PWM_BASE);
|
||||
clock_mmap = (uint32_t *) mmap(NULL, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED,
|
||||
fd, peripheral_base + CLOCK_BASE);
|
||||
close(fd);
|
||||
|
||||
if (pwm_mmap == MAP_FAILED || clock_mmap == MAP_FAILED)
|
||||
{
|
||||
pan_channel = tilt_channel = -1;
|
||||
log_printf_no_timestamp("======= Servo init failed: PWM gpio mmap() failed");
|
||||
return;
|
||||
}
|
||||
if (pan_channel > 0)
|
||||
gpio_alt_function(pikrellcam.servo_pan_gpio, pan_alt);
|
||||
if (tilt_channel > 0)
|
||||
gpio_alt_function(pikrellcam.servo_tilt_gpio, tilt_alt);
|
||||
|
||||
*(clock_mmap + CM_PWMCTL_REG) = CM_PASSWORD | PWMCTL_KILL;
|
||||
usleep(10);
|
||||
|
||||
divi = (uint32_t) (PWM_CLOCK_HZ * PWM_RESOLUTION);
|
||||
*(clock_mmap + CM_PWMDIV_REG) = CM_PASSWORD | PWMDIV_DIVI(divi);
|
||||
*(clock_mmap + CM_PWMCTL_REG) = CM_PASSWORD | PWMCTL_ENABLE | PWMCTL_SRC_OSC;
|
||||
|
||||
/* Turn off PWM, set range registers and enable clocks so PWM channels run
|
||||
| in M/S mode where data count gives pulse width range count is period.
|
||||
*/
|
||||
*(pwm_mmap + PWM_CTL_REG) = CTL_REG_RESET_STATE;
|
||||
usleep(50);
|
||||
*(pwm_mmap + PWM_RNG1_REG) = (uint32_t) PWM_MSEC_TO_COUNT(20);
|
||||
*(pwm_mmap + PWM_RNG2_REG) = (uint32_t) PWM_MSEC_TO_COUNT(20);
|
||||
*(pwm_mmap + PWM_CTL_REG) = CTL_REG_PWM1_MS_MODE | CTL_REG_PWM2_MS_MODE;
|
||||
|
||||
/* t1 & t2 is pulse width time in .01 msec units.
|
||||
*/
|
||||
t1 = *(pwm_mmap + PWM_DAT1_REG);
|
||||
t1 = (uint32_t) ((float) t1 * PWM_RESOLUTION / PULSE_WIDTH_RESOLUTION);
|
||||
|
||||
t2 = *(pwm_mmap + PWM_DAT2_REG);
|
||||
t2 = (uint32_t) ((float) t2 * PWM_RESOLUTION / PULSE_WIDTH_RESOLUTION);
|
||||
|
||||
pan_cur = (float) ((pan_channel == 1) ? t1 : t2);
|
||||
if (pikrellcam.servo_pan_invert)
|
||||
pan_cur = 300.0 - pan_cur;
|
||||
if (pan_cur < pikrellcam.servo_pan_min || pan_cur > pikrellcam.servo_pan_max)
|
||||
pan_cur = 150;
|
||||
|
||||
tilt_cur = (float) ((tilt_channel == 1) ? t1 : t2);
|
||||
if (pikrellcam.servo_tilt_invert)
|
||||
tilt_cur = 300.0 - tilt_cur;
|
||||
if (tilt_cur < pikrellcam.servo_tilt_min || tilt_cur > pikrellcam.servo_tilt_max)
|
||||
tilt_cur = 150;
|
||||
log_printf_no_timestamp("======= Servo using hardware PWM (%d %d)\n",
|
||||
pikrellcam.servo_pan_gpio, pikrellcam.servo_tilt_gpio);
|
||||
|
||||
pthread_create (&servo_thread_ref, NULL, servo_thread, NULL);
|
||||
}
|
||||
|
||||
|
||||
#define PAN_LEFT 0
|
||||
#define PAN_RIGHT 1
|
||||
#define TILT_UP 2
|
||||
#define TILT_DOWN 3
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int id,
|
||||
n_args;
|
||||
}
|
||||
ServoCommand;
|
||||
|
||||
static ServoCommand servo_commands[] =
|
||||
{
|
||||
{ "pan_left", PAN_LEFT, 1 },
|
||||
{ "pan_right", PAN_RIGHT, 1 },
|
||||
{ "tilt_up", TILT_UP, 1 },
|
||||
{ "tilt_down", TILT_DOWN, 1 },
|
||||
};
|
||||
|
||||
#define N_SERVO_COMMANDS (sizeof(servo_commands) / sizeof(ServoCommand))
|
||||
|
||||
static int
|
||||
servo_move_position(int cur, int dir, int mode, int limit)
|
||||
{
|
||||
if (mode == SERVO_MODE_MOVE_ONE)
|
||||
cur += dir;
|
||||
else if (mode == SERVO_MODE_MOVE_STEPS)
|
||||
cur += dir * pikrellcam.servo_move_steps;
|
||||
else if (mode == SERVO_MODE_MOVE_LIMIT)
|
||||
cur = limit;
|
||||
return cur;
|
||||
}
|
||||
|
||||
void
|
||||
servo_command(char *cmd_line)
|
||||
{
|
||||
ServoCommand *scmd;
|
||||
int i, n, id = -1;
|
||||
int pan, tilt, mode;
|
||||
char buf[64], arg1[32];
|
||||
static int prev_id;
|
||||
|
||||
if (!pikrellcam.have_servos)
|
||||
return;
|
||||
|
||||
arg1[0] = '\0';
|
||||
n = sscanf(cmd_line, "%63s %31s", buf, arg1);
|
||||
if (n < 1)
|
||||
return;
|
||||
|
||||
for (i = 0; i < N_SERVO_COMMANDS; ++i)
|
||||
{
|
||||
scmd = &servo_commands[i];
|
||||
if (!strcmp(scmd->name, buf))
|
||||
{
|
||||
if (scmd->n_args <= n - 1)
|
||||
id = scmd->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id == -1)
|
||||
{
|
||||
// inform_message("Bad motion command.");
|
||||
return;
|
||||
}
|
||||
pan = (int) pan_cur;
|
||||
tilt = (int) tilt_cur;
|
||||
mode = atoi(arg1);
|
||||
|
||||
if ( mode == SERVO_MODE_MOVE_LIMIT
|
||||
&& servo_control.status == SERVO_MOVING
|
||||
&& id == prev_id
|
||||
)
|
||||
{
|
||||
servo_control.status = SERVO_IDLE; /* Force to idle */
|
||||
return;
|
||||
}
|
||||
prev_id = id;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case PAN_LEFT:
|
||||
pan = servo_move_position(pan, -1, mode, pikrellcam.servo_pan_min);
|
||||
if (pan < pikrellcam.servo_pan_min)
|
||||
pan = pikrellcam.servo_pan_min;
|
||||
servo_move(pan, tilt, pikrellcam.servo_move_step_msec);
|
||||
break;
|
||||
|
||||
case PAN_RIGHT:
|
||||
pan = servo_move_position(pan, 1, mode, pikrellcam.servo_pan_max);
|
||||
if (pan > pikrellcam.servo_pan_max)
|
||||
pan = pikrellcam.servo_pan_max;
|
||||
servo_move(pan, tilt, pikrellcam.servo_move_step_msec);
|
||||
break;
|
||||
|
||||
case TILT_UP:
|
||||
tilt = servo_move_position(tilt, 1, mode, pikrellcam.servo_tilt_max);
|
||||
if (tilt > pikrellcam.servo_tilt_max)
|
||||
tilt = pikrellcam.servo_tilt_max;
|
||||
servo_move(pan, tilt, pikrellcam.servo_move_step_msec);
|
||||
break;
|
||||
|
||||
case TILT_DOWN:
|
||||
tilt = servo_move_position(tilt, -1, mode, pikrellcam.servo_tilt_min);
|
||||
if (tilt < pikrellcam.servo_tilt_min)
|
||||
tilt = pikrellcam.servo_tilt_min;
|
||||
servo_move(pan, tilt, pikrellcam.servo_move_step_msec);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -84,7 +84,7 @@ void setup_h264_tcp_server(void)
|
|||
|
||||
if(bind (listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))<0);
|
||||
{
|
||||
perror("Server: error binding\n");
|
||||
// perror("Server: error binding\n");
|
||||
log_printf("Server: error binding\n");
|
||||
//return;
|
||||
}
|
||||
|
@ -95,7 +95,8 @@ void setup_h264_tcp_server(void)
|
|||
save_fd |= O_NONBLOCK;
|
||||
fcntl( listenfd, F_SETFL, save_fd );
|
||||
|
||||
fprintf(stderr,"%s\n","Server running...waiting for connections.");
|
||||
|
||||
// fprintf(stderr,"%s\n","Server running...waiting for connections.");
|
||||
log_printf("Server running...waiting for connections.\n");
|
||||
}
|
||||
|
||||
|
@ -116,9 +117,9 @@ void tcp_poll_connect(void)
|
|||
{
|
||||
h264_conn_status=H264_TCP_SEND_HEADER; //must send header
|
||||
num_sent=0;
|
||||
fprintf (stderr, "Server: connect from host %s, port %u.\n",
|
||||
inet_ntoa (cliaddr.sin_addr),
|
||||
ntohs (cliaddr.sin_port));
|
||||
// fprintf (stderr, "Server: connect from host %s, port %u.\n",
|
||||
// inet_ntoa (cliaddr.sin_addr),
|
||||
// ntohs (cliaddr.sin_port));
|
||||
log_printf("Server: connect from host %s, port %u.\n",
|
||||
inet_ntoa (cliaddr.sin_addr),
|
||||
ntohs (cliaddr.sin_port));
|
||||
|
|
|
@ -45,6 +45,8 @@ static struct buffer buffers[NUM_CIRC_BUFS];
|
|||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
static int new_connection_log_count;
|
||||
|
||||
/* returned a new buffer, after use the buffer must be free'd with image_buffer_free() */
|
||||
static struct buffer* client_queue_get()
|
||||
{
|
||||
|
@ -87,6 +89,7 @@ static void* handle_client(void *args)
|
|||
char header[MAX_BUF_SIZE];
|
||||
struct buffer *buf = NULL;
|
||||
|
||||
if (++new_connection_log_count < 30) /* punt - FIXME */
|
||||
log_printf("new connection from host '%s' on port '%d'\n",
|
||||
inet_ntoa(client->sockaddr.sin_addr),
|
||||
ntohs(client->sockaddr.sin_port));
|
||||
|
@ -127,6 +130,7 @@ static void* handle_client(void *args)
|
|||
image_buffer_free(buf);
|
||||
}
|
||||
failed:
|
||||
if (new_connection_log_count < 30) /* punt - FIXME */
|
||||
log_printf("closing connection from host '%s' on port '%d'\n",
|
||||
inet_ntoa(client->sockaddr.sin_addr),
|
||||
ntohs(client->sockaddr.sin_port));
|
||||
|
@ -135,6 +139,7 @@ failed:
|
|||
|
||||
if (data)
|
||||
free(data);
|
||||
close(client->fd);
|
||||
free(client);
|
||||
pthread_detach(pthread_self());
|
||||
return NULL;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
// Do not edit this file. Edit config-user.php instead.
|
||||
//
|
||||
$config_event_count = 17;
|
||||
$config_event_count = 18;
|
||||
|
||||
$n_columns = 4;
|
||||
$name_style = "short";
|
||||
|
@ -23,6 +23,7 @@ $background_image = "images/paper1.png";
|
|||
$archive_initial_view = "thumbs";
|
||||
$archive_thumbs_scrolled = "yes";
|
||||
$media_thumbs_scrolled = "yes";
|
||||
$videos_mode = "thumbs";
|
||||
|
||||
$video_url = "";
|
||||
$include_control = "no";
|
||||
|
@ -33,7 +34,7 @@ function config_user_save()
|
|||
global $default_text_color, $selected_text_color, $media_text_color, $manual_video_text_color;
|
||||
global $n_log_scroll_pixels, $log_text_color, $n_thumb_scroll_pixels, $background_image;
|
||||
global $config_event_count, $include_control;
|
||||
global $archive_initial_view, $archive_thumbs_scrolled, $media_thumbs_scrolled;
|
||||
global $archive_initial_view, $archive_thumbs_scrolled, $media_thumbs_scrolled, $videos_mode;
|
||||
global $video_url;
|
||||
|
||||
$file = fopen("config-user.php", "w");
|
||||
|
@ -89,6 +90,7 @@ function config_user_save()
|
|||
."//\n");
|
||||
fwrite($file, "define(\"NAME_STYLE\", \"$name_style\");\n");
|
||||
fwrite($file, "define(\"N_COLUMNS\", \"$n_columns\");\n");
|
||||
fwrite($file, "define(\"VIDEOS_MODE\", \"$videos_mode\");\n");
|
||||
fwrite($file, "define(\"ARCHIVE_INITIAL_VIEW\", \"$archive_initial_view\");\n");
|
||||
fwrite($file, "define(\"ARCHIVE_THUMBS_SCROLLED\", \"$archive_thumbs_scrolled\");\n");
|
||||
fwrite($file, "define(\"MEDIA_THUMBS_SCROLLED\", \"$media_thumbs_scrolled\");\n\n");
|
||||
|
@ -127,6 +129,8 @@ if (defined('MEDIA_TEXT_COLOR'))
|
|||
if (defined('MANUAL_VIDEO_TEXT_COLOR'))
|
||||
$manual_video_text_color = MANUAL_VIDEO_TEXT_COLOR;
|
||||
|
||||
if (defined('VIDEOS_MODE'))
|
||||
$videos_mode = VIDEOS_MODE;
|
||||
if (defined('ARCHIVE_INITIAL_VIEW'))
|
||||
$archive_initial_view = ARCHIVE_INITIAL_VIEW;
|
||||
if (defined('ARCHIVE_THUMBS_SCROLLED'))
|
||||
|
|
|
@ -15,14 +15,15 @@
|
|||
|
||||
define("ARCHIVE_DIR", "archive");
|
||||
|
||||
// The mjpeg file can be changed by editing ~/.pikrellcam/pikrellcam.conf
|
||||
// The others are fixed by the install and enforced by the startup script.
|
||||
// It is no use to change these here.
|
||||
// These are set up by the install or pikrellcam.conf and enforced by
|
||||
// the startup script. It is no use to change these here.
|
||||
//
|
||||
define("LOG_FILE", "/tmp/pikrellcam.log");
|
||||
define("MJPEG_FILE", "/run/pikrellcam/mjpeg.jpg");
|
||||
define("PIKRELLCAM", "/home/pi/pikrellcam/pikrellcam");
|
||||
define("FIFO_FILE", "/home/pi/pikrellcam/www/FIFO");
|
||||
|
||||
define("VERSION", "2.1.12");
|
||||
define("SERVOS_ENABLE", "servos_off");
|
||||
|
||||
define("VERSION", "3.0.0");
|
||||
?>
|
||||
|
|
445
www/help.php
|
@ -64,6 +64,41 @@ And there is a Raspberry Pi
|
|||
Under construction...
|
||||
</div>
|
||||
|
||||
<span style='font-size: 1.5em; font-weight: 650;'>Version 3.0 Upgrade Notice for Version 2.x Users</span><hr>
|
||||
<div class='indent0'>
|
||||
The upgrade from PiKrellcam V2.x to PiKrellCam V3.0 adds presets and servo control.<br>
|
||||
This is documented below on this page, but there are changes in how
|
||||
motion regions usage is handled that is important to be aware of up front:
|
||||
<ul>
|
||||
<li> The use of saving and loading of motion regions by name is no longer the primary
|
||||
way to change the motion regions in effect. Saving and loading regions by name now
|
||||
has a new role of maintaining a set of motion regions as temporaries for backup or
|
||||
using as an initial condition to be loaded when creating a new preset.
|
||||
</li>
|
||||
<li> If motion regions are edited or a new set loaded by name, the changes are
|
||||
automatically stored into the current preset (unless you have servos and are off
|
||||
a preset - see below). So if you edit motion regions the current preset is
|
||||
changed and there is no need to save by name unless you want the backup.
|
||||
</li>
|
||||
<li> When pikrellcam is restarted, it loads the preset you were on when pikrellcam
|
||||
stopped. If for example, you have a preset 1 set up for default use and a preset 2
|
||||
for windy conditions, then if you want to be sure that at program start preset 1 is
|
||||
selected, you should have in at-commands.conf:
|
||||
<pre>
|
||||
daily start "@preset goto 1 1"
|
||||
</pre>
|
||||
If you have servos, you might want to have a position number other than "1".<br>
|
||||
This would replace the use of an at command to load regions and if you have such
|
||||
a startup motion load_regions command, you probably want to take that out. If you
|
||||
don't take it out and don't have a startup preset goto, the regions will be loaded
|
||||
into whatever preset you restart with which can be not what you want.
|
||||
If you do use a startup
|
||||
preset goto command, also having a startup motion load_regions is likely redundant
|
||||
because the preset remembers its motion regions.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<span style='font-size: 1.5em; font-weight: 650;'>Install</span><hr>
|
||||
<div class='indent0'>
|
||||
|
@ -105,7 +140,8 @@ Go to the PiKrellCam web page in your browser (omit the port number if it was le
|
|||
<span style='font-weight:700'>System</span> panel and start the PiKrellCam prgram
|
||||
by clicking the button
|
||||
<span class='btn-control'>Start</span>
|
||||
<br>After two or three seconds, the preview image from the camera should appear.
|
||||
<br>After two or three seconds (startup can be slower on a Pi 1),
|
||||
the preview image from the camera should appear.
|
||||
If it does not you should get in its place an error image indicating that the
|
||||
camera could not be started. This can happen if the camera is busy (another program
|
||||
is using it) or if there is a problem with the ribbon cable camera connection.
|
||||
|
@ -119,17 +155,17 @@ Go to the PiKrellCam web page in your browser (omit the port number if it was le
|
|||
and PiKrellCam is now operating with its default settings.
|
||||
</li>
|
||||
<li>Wait for motion to be detected and watch the OSD for the video record progress.<br>
|
||||
After the video ends, view it by going to the Thumbs (or Videos) page by clicking:
|
||||
<span style='font-weight:700'>Media:</span> <span class='btn-control'>Thumbs</span>
|
||||
After the video ends, view it by going to the Videos page by clicking:
|
||||
<span style='font-weight:700'>Media:</span> <span class='btn-control'>Videos</span>
|
||||
</li>
|
||||
<li>On the button bar, click the buttons
|
||||
<span style='font-weight:700'>Show:</span>
|
||||
<span class='btn-control'>Preset</span>
|
||||
<span class='btn-control'>Timelapse</span>
|
||||
<span class='btn-control'>Regions</span>
|
||||
<span class='btn-control'>Vectors</span>
|
||||
<br>to toggle showing information PiKrellCam can display on the OSD.
|
||||
This information shows real time motion detection information and gives a feel
|
||||
for motion magnitudes and counts which can be configured to tune motion detection.
|
||||
When you show Preset, you see the currently configured motion detection vector
|
||||
and burst values and the motion detect regions in effect. See below.
|
||||
</li>
|
||||
<li>A basic first configuration to consider is enabling motion detection to be turned on
|
||||
each time PiKrellCam is started. To do this, use the OSD menu system:<br>
|
||||
|
@ -138,7 +174,7 @@ Go to the PiKrellCam web page in your browser (omit the port number if it was le
|
|||
Expand the <span style='font-weight:700'>Setup</span> panel.
|
||||
</li>
|
||||
<li>In the
|
||||
<span style='font-weight:700'>Motion</span> group,
|
||||
<span style='font-weight:700'>Config</span> group,
|
||||
click the button <span class='btn-control'>Settings</span>
|
||||
</li>
|
||||
<li>The OSD will show a horizontal menu with
|
||||
|
@ -287,7 +323,7 @@ motion:
|
|||
</div>
|
||||
<p>
|
||||
You can see the results of PiKrellCam's vector processing on the OSD by turning on
|
||||
the showing of Regions and Vectors. Watching this display will allow you to tune
|
||||
the showing of Preset and Vectors. Watching this display will allow you to tune
|
||||
your configured vector limit values to your camera environment. To
|
||||
get a better look at the vectors, you can temporarily raise the mjpeg_divider
|
||||
value so the OSD will update more slowly.
|
||||
|
@ -311,7 +347,10 @@ value so the OSD will update more slowly.
|
|||
<li>Sparkles are camera motion vectors that have no neighbors and PiKrellCam
|
||||
considers them noise and excludes them from the composite vectors.
|
||||
</li>
|
||||
<li>Interpreting the two vector count status lines:
|
||||
<li>The vector count status line will be shown if you set
|
||||
Setup->Config->Settings->Vector_Counts on and Show: Preset.
|
||||
<br>
|
||||
Interpreting the vector count status line:
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>any:47 (17.1)</span> This shows there was
|
||||
a frame total of 47 vectors excluding sparkles passing the magnitude limit test.
|
||||
|
@ -352,20 +391,145 @@ value so the OSD will update more slowly.
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<span style='font-size: 1.5em; font-weight: 650;'>Servos</span><hr>
|
||||
<div class='indent0'>
|
||||
PiKrellCam has built in servo control for cameras mounted on servos.
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>Hardware PWM</span>: If servos are connected to
|
||||
the hardware PWM GPIO pins, PiKrellCam can directly control the PWM signals and the
|
||||
only configuration needed is to set in pikrellcam.conf
|
||||
<span style='font-weight:700'>servo_pan_gpio</span> and
|
||||
<span style='font-weight:700'>servo_tilt_gpio</span>
|
||||
to the PWM GPIO pin numbers. So the pan/tilt or tilt/pan gpio pairs must be one of<br>
|
||||
<span style='font-weight:700'>12,13 12,19 18,13 18,19</span><br>
|
||||
Stop pikrellcam before editing ~/.pikrellcam/pikrellcam.conf to set the GPIO values.
|
||||
Then restart pikrellcam, reload the web page and you will have new buttons to control
|
||||
position presets and moving the servos.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>ServoBlaster</span>: If your servos are connected to
|
||||
GPIOs that are not the hardware PWM pins you can use ServoBlaster.
|
||||
Set
|
||||
<span style='font-weight:700'>servo_pan_gpio</span> and
|
||||
<span style='font-weight:700'>servo_tilt_gpio</span>
|
||||
in pikrellcam.conf to the ServoBlaster servo numbers you are using and set
|
||||
<span style='font-weight:700'>servo_use_servoblaster</span> to
|
||||
<span style='font-weight:700'>on</span>.<br>
|
||||
For this a separate install of ServoBlaster is required according to ServoBlaster
|
||||
documentation.<br>
|
||||
Stop pikrellcam before editing ~/.pikrellcam/pikrellcam.conf to set the use
|
||||
servoblaster option and gpio values to ServoBlaster servo numbers.
|
||||
Then restart pikrellcam and reload the web page.
|
||||
</li>
|
||||
</ul>
|
||||
After configuring for servos, the first thing to do is to check if the servos move
|
||||
in the right direction when the servo arrow buttons are clicked. If they do not,
|
||||
then the directions can be inverted in
|
||||
<nobr><span style='font-weight:700'>Setup->Config->Servo</span></nobr>
|
||||
</div>
|
||||
|
||||
<span style='font-size: 1.5em; font-weight: 650;'>Presets</span><hr>
|
||||
<div class='indent0'>
|
||||
<img src="images/preset-servos.jpg" alt="preset-servos.jpg">
|
||||
<p>
|
||||
A preset is a camera position with a group of motion detect settings
|
||||
(vector magnitude / count and burst count / frames) and a set of motion regions.
|
||||
Clicking the preset up/down
|
||||
arrows moves to a new settings preset which single click loads a completely new set of
|
||||
motion detect settings and motion regions. So presets can be configured with
|
||||
motion detect sensitivities and motion regions appropriate for different weather
|
||||
or other conditions and quickly selected with single clicks.
|
||||
<p>
|
||||
Preset left/right arrow buttons are shown only if servos are configured and
|
||||
move the servos to configured position presets.
|
||||
<p>
|
||||
The Servo button and arrows are shown only if servos are enabled. Click the
|
||||
<span class='btn-control'>Servo</span>
|
||||
button to cycle the servo arrow direction buttons through three modes: step by one, step by
|
||||
<span style='font-weight:700'>Move_Steps</span>, and scan. When arrow buttons are in scan
|
||||
mode, clicking an arrow will step the servo continuously at
|
||||
<span style='font-weight:700'>Move_Step_msec</span>
|
||||
rate until the arrow button is clicked
|
||||
again or the servo reaches a pan/tilt limit.
|
||||
<p>
|
||||
<span style='font-weight:700'>Preset behavior without servos:</span>
|
||||
<ul>
|
||||
<li> PiKrellCam considers the camera at a single fixed position and will never be
|
||||
off a preset. There will be only preset up/down arrows and
|
||||
no preset left/right or servo arrows. The pan/tilt graphics in the above image
|
||||
will not be shown.
|
||||
</li>
|
||||
<li> Any motion settings or regions edits will immediately apply to the currently
|
||||
selected settings preset (Preset up/down arrows).
|
||||
</li>
|
||||
<li> To create a new settings preset, click
|
||||
<nobr><span style='font-weight:700'>Setup->Preset->New</span></nobr>
|
||||
and a new settings preset will be created with the existing
|
||||
motion settings and regions which can then be edited.
|
||||
</li>
|
||||
</ul>
|
||||
<span style='font-weight:700'>Preset behavior with servos:</span>
|
||||
<ul>
|
||||
<li> If <nobr><span style='font-weight:700'>Setup->Config->Servo->Motion_Off_Preset</span></nobr> is
|
||||
<span style='font-weight:700'>OFF</span>,
|
||||
motion detection applies only if the servos are on a preset and if the servos are
|
||||
moved off a position preset with the servo arrow buttons
|
||||
then motion detection is put on hold. Set this option to
|
||||
<span style='font-weight:700'>ON</span>
|
||||
if you want to have motion detected even if a servo position is off a preset.
|
||||
</li>
|
||||
<li> Presets cannot be created with different tilt positions at the same pan
|
||||
position.
|
||||
</li>
|
||||
<li> When the servos are on a position preset, a new settings for the position
|
||||
is created with
|
||||
<nobr><span style='font-weight:700'>Setup->Preset->New</span></nobr>
|
||||
</li>
|
||||
<li> To create a preset at a new position:
|
||||
<ul>
|
||||
<li>Move the servos to the desired position and click <br>
|
||||
<span style='font-weight:700'>Setup->Preset->New</span><br>
|
||||
and you can then edit the settings and motion regions for the preset.
|
||||
<br>
|
||||
Or you may move the servos off a preset and edit
|
||||
the settings or regions before creating the new preset and the OSD will warn you
|
||||
that you will need to create a new preset or else your edits will not be saved.
|
||||
If you move the servos back to an existing preset before creating a new one,
|
||||
your edits will be replaced with the preset settings.
|
||||
</li>
|
||||
<li>Copy an existing set of settings from a position preset to a new preset at
|
||||
a new position.<br>
|
||||
To do this, first move the servos to the existing preset you want to
|
||||
copy the settings from.
|
||||
Then, use the Servo arrows to move the camera to the new position (don't let
|
||||
the servo position fall on any other preset), and click<br>
|
||||
<span style='font-weight:700'>Setup->Preset->Copy</span><br>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<span style='font-size: 1.5em; font-weight: 650;'>Motion Regions Panel</span><hr>
|
||||
<img src="images/motion-regions.jpg" alt="motion-regions.jpg">
|
||||
<div class='indent0'>
|
||||
As motion regions are edited, they are saved to the current preset unless servos are
|
||||
configured and the servo position is not on a preset. If the servo position is not on
|
||||
a preset, motion region edits will be lost unless you create a new preset or save the
|
||||
motion regions by name.
|
||||
<p>
|
||||
Motion regions outline areas of the camera view that will be
|
||||
sensitive to motion and provides for excluding from motion detection areas such
|
||||
as wind blown vegetation.
|
||||
Motion regions may be added, deleted, resized or moved. After the
|
||||
motion regions have been edited, they may be saved and loaded by name. Typically, a region
|
||||
will be configured for a particular camera setup and then saved by name. Then that motion region
|
||||
setup can be automatically loaded when PiKrellCam starts - see the example in the at commands
|
||||
section. If you save an edited motion regions to the special name
|
||||
<span style='font-weight:700'>default</span>, then your modified regions will load
|
||||
at startup without having to configure an at command motion regions load.
|
||||
Motion regions may be added, deleted, resized or moved at each preset.
|
||||
|
||||
Motion regions may also be saved by name and this provides a way to maintain a set of
|
||||
motion regions as a backup or a temporary. For example, a backup motion region by
|
||||
name can be loaded as an initial condition after creating a new preset. Or temporary
|
||||
motion regions by name can be loaded if you have a set of different motion regions you
|
||||
want to load to a preset on demand for evaluation.
|
||||
If a motion region is loaded by name it is automatically saved to the
|
||||
current preset unless you have servos and are off a preset.
|
||||
<p>
|
||||
The increment of a region resize or move can be coarse or fine by selecting/deselecting the
|
||||
<span style='font-weight:700'>Coarse Move</span> select button. When the increment is fine,
|
||||
|
@ -403,51 +567,16 @@ After a menu is opened and an option or value from it is highlighted
|
|||
by clicking the arrow buttons, the option or value must be finalized by clicking the
|
||||
<span class='btn-control' >Sel</span>
|
||||
button. This is required for the change to be saved in the configuration file.
|
||||
|
||||
<p>
|
||||
<span style='font-size: 1.2em; font-weight: 680;'>Camera Config</span>
|
||||
If servos are not configured, there will be no Move or Copy buttons in the
|
||||
Preset group and there will be no Servo button in the Config group.
|
||||
<p>
|
||||
<span style='font-size: 1.2em; font-weight: 650;'>Preset</span>
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>Video Presets</span> - selects the video resolution for
|
||||
motion and manual videos. Different resolutions may have different fields of view. So
|
||||
one reason for selecting
|
||||
<span style='font-weight:700'>720p</span> over
|
||||
<span style='font-weight:700'>1080p</span> would be to get a wider field of view. Resolutions
|
||||
will have either 16:9 or 4:3 aspect ratio.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Still Presets</span> - selecting different resolutions
|
||||
gives different fields of view and aspect ratios.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Adjustments</span>
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>video_bitrate</span> - determines the size of a video
|
||||
and its quality. Adjust up if it improves video quality. Adjust down if you want
|
||||
to reduce the size of the videos.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>video_fps</span> - typically this should be no higher
|
||||
than 24 or the motion detecting preview jpeg camera stream may start dropping frames.
|
||||
(I have no data on the effect GPU overclocking might have on this limitation).
|
||||
</li>
|
||||
<li><span style='font-weight:700'>video_mp4box_fps</span> - keep this value the same
|
||||
as video_fps unless you want to create fast or slow motion videos.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>mjpeg_divider</span> - this value is divided into
|
||||
the video_fps value to get the preview jpeg rate. The preview is updated at this rate
|
||||
and it is the rate that motion vector frames are checked for motion.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>still_quality</span> - adjust up if it improves
|
||||
still jpeg quality. Adjust down if you want to reduce the size of still jpegs.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<span style='font-size: 1.2em; font-weight: 650;'>Camera Params</span>
|
||||
<div class='indent1'>
|
||||
The camera parameters menus can set the hardware parameters of the Pi camera.
|
||||
</div>
|
||||
<p>
|
||||
<span style='font-size: 1.2em; font-weight: 650;'>Motion</span>
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>Vector Limits</span>
|
||||
<li><span style='font-weight:700'>Settings</span><br>
|
||||
These values are part of a preset and editing them applies to the currently
|
||||
selected preset. If you have servos and are off a preset, editing these values
|
||||
can be done in anticipation of creating a new preset.
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>Vector_Magnitude</span> - sets the minimum magnitude
|
||||
of a motion vector. Individual motion vector and region composite vector
|
||||
|
@ -489,6 +618,121 @@ button. This is required for the change to be saved in the configuration file.
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Move: One</span><br>
|
||||
You will have this button only if servos are configured.<br>
|
||||
If the servos are moved off a preset, click this if you want to move
|
||||
the preset you were on to the current servo position.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Move: All</span><br>
|
||||
You will have this button only if servos are configured.<br>
|
||||
If the servos are moved off a preset, click this if you want to move
|
||||
the preset you were on to the current servo position and move all the other preset
|
||||
positions by the same amount. If the camera installation is disturbed or serviced,
|
||||
this allows a quick adjustment for restoring position presets. The other presets
|
||||
may still need small adjustments if servo positioning is non linear. All presets
|
||||
cannot be moved if the move would move any preset past a servo position limit.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Del</span><br>
|
||||
If servos are not configured or if the servo position is on an existing preset, delete
|
||||
the current Settings. If servos are configured and the servo position is on a preset
|
||||
and the Settings are the last Settings for the preset, then delete the position preset
|
||||
unless it is the only existing position preset. There must always be at least one
|
||||
preset and you cannot delete down to zero presets.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Copy</span><br>
|
||||
You will have this button only if servos are configured.<br>
|
||||
If the pan servo is moved off a preset, click this to create a
|
||||
new preset at the servo position which is initiallized by copying all of
|
||||
the preset settings (motion detect limits and regions) from the preset you
|
||||
were on into the new preset.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>New</span><br>
|
||||
Creates a new preset. If servos are not configured or if the servo position is on an
|
||||
existing position preset, this will create a new Settings preset which can then
|
||||
be edited. If servos are configured and the servo position is not on an existing preset,
|
||||
then a new position preset is created with an initial single Settings.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<span style='font-size: 1.2em; font-weight: 650;'>Time Lapse</span>
|
||||
<div class='indent1'>
|
||||
Enter a time period in the text box and click <span style='font-weight:700'>Start</span>
|
||||
to begin a time lapse run. Entering a new period and clicking
|
||||
<span style='font-weight:700'>Start</span> will change the period for the current time
|
||||
lapse run and does not start a new time lapse run. Click the
|
||||
<span style='font-weight:700'>Timelapse</span> button on the button bar to show the time
|
||||
lapse status on the preview OSD. When the
|
||||
<span style='font-weight:700'>End</span> button is clicked PiKrellCam will run a time lapse
|
||||
end script which will convert the time lapse images into a video and store the final video
|
||||
in the media <span style='font-weight:700'>videos</span> directory and the video name
|
||||
will have a
|
||||
<span style='font-weight:700'>tl_</span> prefix.
|
||||
The progress of this
|
||||
conversion will be shown on the time lapse OSD display. To better control start, end and
|
||||
overnight hold times, a time lapse can be controlled with at commands. See that section for
|
||||
an example.
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<span style='font-size: 1.2em; font-weight: 680;'>Config</span>
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>Video Res</span> - selects the video resolution for
|
||||
motion and manual videos. Different resolutions may have different fields of view. So
|
||||
one reason for selecting
|
||||
<span style='font-weight:700'>720p</span> over
|
||||
<span style='font-weight:700'>1080p</span> would be to get a wider field of view. Resolutions
|
||||
will have either 16:9 or 4:3 aspect ratio.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Still Res</span> - selecting different resolutions
|
||||
gives different fields of view and aspect ratios.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Settings</span>
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>Startup_Motion</span> - set to
|
||||
<span style='font-weight:700'>ON</span> for motion detection to be enabled each time
|
||||
PiKrellCam starts. If set to
|
||||
<span style='font-weight:700'>OFF</span>, motion detection will need to be manually
|
||||
enabled from the web page or a script.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>video_bitrate</span> - determines the size of a video
|
||||
and its quality. Adjust up if it improves video quality. Adjust down if you want
|
||||
to reduce the size of the videos.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>video_fps</span> - typically this should be no higher
|
||||
than 24 or the motion detecting preview jpeg camera stream may start dropping frames.
|
||||
(I have no data on the effect GPU overclocking might have on this limitation).
|
||||
</li>
|
||||
<li><span style='font-weight:700'>video_mp4box_fps</span> - keep this value set to zero
|
||||
unless you want to create fast or slow motion videos. When zero, mp4 boxing fps will be
|
||||
the same as video_fps which is normally what you want. But this value can be set to a
|
||||
non zero value different from video_fps if you want fast or slow motion videos.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>mjpeg_divider</span> - this value is divided into
|
||||
the video_fps value to get the preview jpeg rate. The preview is updated at this rate
|
||||
and it is the rate that motion vector frames are checked for motion.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>still_quality</span> - adjust up if it improves
|
||||
still jpeg quality. Adjust down if you want to reduce the size of still jpegs.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Vector_Counts</span> - enable showing of vector count
|
||||
statistics when showing a Preset. This information may help when setting motion detect
|
||||
limits.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Vector_Dimming</span> - sets a percentage dimming
|
||||
of the preview jpeg image when the
|
||||
<span style='font-weight:700'>Vectors</span> display is enabled. This is to improve
|
||||
the contrast of the drawn motion vectors.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Preview_Clean</span> - if set to
|
||||
<span style='font-weight:700'>OFF</span>, whatever text or graphics that happen to be
|
||||
drawn on the preview jpeg at the time a motion preview save or thumb save occurs will
|
||||
also appear on the saved preview or thumb. This might help with some debugging, but
|
||||
is normally not desirable, so the option should be set
|
||||
<span style='font-weight:700'>ON</span>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Times</span>
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>Confirm_Gap</span> - for motion direction detects,
|
||||
|
@ -539,46 +783,49 @@ button. This is required for the change to be saved in the configuration file.
|
|||
is configured.
|
||||
</div>
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Settings</span>
|
||||
<li><span style='font-weight:700'>Servo</span><br>
|
||||
The Servo menu is shown only if servos have been configured.
|
||||
<ul>
|
||||
<li><span style='font-weight:700'>Startup_Motion</span> - set to
|
||||
<span style='font-weight:700'>ON</span> for motion detection to be enabled each time
|
||||
PiKrellCam starts. If set to
|
||||
<span style='font-weight:700'>OFF</span>, motion detection will need to be manually
|
||||
enabled from the web page or a script.
|
||||
<li><span style='font-weight:700'>Motion_Off_Preset</span> - if
|
||||
<span style='font-weight:700'>OFF</span>, do not detect motion when the servo postion
|
||||
is off a preset. If configured motion regions are suitable for any servo position,
|
||||
this can be set
|
||||
<span style='font-weight:700'>ON</span> if you do not want motion detection to be
|
||||
put on hold when a servo is manually moved off a preset.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Vector_Dimming</span> - sets a percentage dimming
|
||||
of the preview jpeg image when the
|
||||
<span style='font-weight:700'>Vectors</span> display is enabled. This is to improve
|
||||
the contrast of the drawn motion vectors.
|
||||
<li><span style='font-weight:700'>Move_Step_msec</span> - delay in milliseconds between
|
||||
servo steps when a servo is moved using the
|
||||
<span style='font-weight:700'>Servo</span> arrow buttons. A servo step changes the
|
||||
pulse width of the servo control line by 1/100 of a millisecond (10 usec).
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Preview_Clean</span> - if set to
|
||||
<span style='font-weight:700'>OFF</span>, whatever text or graphics that happen to be
|
||||
drawn on the preview jpeg at the time a motion preview save or thumb save occurs will
|
||||
also appear on the saved preview or thumb. This might help with some debugging, but
|
||||
is normally not desirable, so the option should be set
|
||||
<span style='font-weight:700'>ON</span>.
|
||||
<li><span style='font-weight:700'>Preset_Step_msec</span> - delay
|
||||
in milliseconds between servo steps when a servo is moved using the
|
||||
<span style='font-weight:700'>Preset</span>
|
||||
left/right arrow buttons.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Servo_Settle_msec</span> - delay in milliseconds before
|
||||
motion detection is taken out of its hold state after servos stop moving.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Move_Steps</span> - number of steps to move when the
|
||||
<span style='font-weight:700'>Servo</span> arrow buttons are cycled to the second step
|
||||
mode. The other step modes are single step and scan and are selected by clicking the
|
||||
<span style='font-weight:700'>Servo</span> button. When in scan mode, the scan can be
|
||||
stopped by clicking the double arrow button again.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Pan_Left_Limit</span><br>
|
||||
<span style='font-weight:700'>Pan_Right_Limit</span><br>
|
||||
<span style='font-weight:700'>Tilt_Up_Limit</span><br>
|
||||
<span style='font-weight:700'>Tilt_Down_Limit</span> - Sets the servo limits.
|
||||
</li>
|
||||
<li><span style='font-weight:700'>Servo_Pan_Invert</span><br>
|
||||
<span style='font-weight:700'>Servo_Tilt_Invert</span> - Set these to ON or OFF
|
||||
to change the direction servos move when the direction arrows are clicked.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<span style='font-size: 1.2em; font-weight: 650;'>Time Lapse</span>
|
||||
<span style='font-size: 1.2em; font-weight: 650;'>Camera Params</span>
|
||||
<div class='indent1'>
|
||||
Enter a time period in the text box and click <span style='font-weight:700'>Start</span>
|
||||
to begin a time lapse run. Entering a new period and clicking
|
||||
<span style='font-weight:700'>Start</span> will change the period for the current time
|
||||
lapse run and does not start a new time lapse run. Click the
|
||||
<span style='font-weight:700'>Timelapse</span> button on the button bar to show the time
|
||||
lapse status on the preview OSD. When the
|
||||
<span style='font-weight:700'>End</span> button is clicked PiKrellCam will run a time lapse
|
||||
end script which will convert the time lapse images into a video and store the final video
|
||||
in the media <span style='font-weight:700'>videos</span> directory and the video name
|
||||
will have a
|
||||
<span style='font-weight:700'>tl_</span> prefix.
|
||||
The progress of this
|
||||
conversion will be shown on the time lapse OSD display. To better control start, end and
|
||||
overnight hold times, a time lapse can be controlled with at commands. See that section for
|
||||
an example.
|
||||
The camera parameters menus can set the hardware parameters of the Pi camera.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -864,6 +1111,12 @@ motion show_vectors [on|off|toggle]
|
|||
motion [command] - other commands sent by the web page to edit motion regions not
|
||||
intented for script or command line use.
|
||||
|
||||
preset prev_position
|
||||
preset next_position
|
||||
preset prev_settings
|
||||
preset next_settings
|
||||
preset goto position settings
|
||||
|
||||
display [command] - commands sent by the web page to display OSD menus. Not intended for
|
||||
script or command line use.
|
||||
|
||||
|
@ -1126,8 +1379,7 @@ recognized.
|
|||
<li>
|
||||
At each PiKrellCam startup
|
||||
<ol>
|
||||
<li>Load a motion detect regions file named
|
||||
<span style='font-weight:700'>driveway</span>.
|
||||
<li>Goto a preset.
|
||||
</li>
|
||||
<li> Prepend the hostname to the annotated text date string drawn on each video
|
||||
<br>
|
||||
|
@ -1136,7 +1388,10 @@ At each PiKrellCam startup
|
|||
</li>
|
||||
</ol>
|
||||
<pre>
|
||||
daily start "@motion load_regions driveway"
|
||||
# If no servos, goto position 1 (only 1 position possible with no servos) settings 1:
|
||||
daily start "@preset goto 1 1"
|
||||
# If servos, goto position 3 settings 1
|
||||
daily start "@preset goto 3 1"
|
||||
daily start "@annotate_string prepend start1 $H_"
|
||||
</pre>
|
||||
</li>
|
||||
|
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 14 KiB |
499
www/index.php
|
@ -1,15 +1,64 @@
|
|||
<script>
|
||||
var servo_mode = 0;
|
||||
|
||||
var servo_left_array =
|
||||
[
|
||||
"images/arrow0-left.png",
|
||||
"images/arrow-left.png",
|
||||
"images/arrow2-left.png"
|
||||
];
|
||||
|
||||
var servo_right_array =
|
||||
[
|
||||
"images/arrow0-right.png",
|
||||
"images/arrow-right.png",
|
||||
"images/arrow2-right.png"
|
||||
];
|
||||
|
||||
var servo_up_array =
|
||||
[
|
||||
"images/arrow0-up.png",
|
||||
"images/arrow-up.png",
|
||||
"images/arrow2-up.png"
|
||||
];
|
||||
|
||||
var servo_down_array =
|
||||
[
|
||||
"images/arrow0-down.png",
|
||||
"images/arrow-down.png",
|
||||
"images/arrow2-down.png"
|
||||
];
|
||||
|
||||
function servo_move_mode()
|
||||
{
|
||||
|
||||
servo_mode += 1;
|
||||
if (servo_mode > servo_left_array.length - 1)
|
||||
servo_mode = 0;
|
||||
|
||||
document.getElementById("servo_left").src = servo_left_array[servo_mode];
|
||||
document.getElementById("servo_right").src = servo_right_array[servo_mode];
|
||||
document.getElementById("servo_up").src = servo_up_array[servo_mode];
|
||||
document.getElementById("servo_down").src = servo_down_array[servo_mode];
|
||||
}
|
||||
|
||||
function servo_move_command(pan_tilt)
|
||||
{
|
||||
// alert("motion " + move_mode + " " + where);
|
||||
fifo_command("servo " + pan_tilt + " " + servo_mode);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<?php
|
||||
//ini_set('display_errors',1);
|
||||
//ini_set('display_startup_errors',1);
|
||||
//error_reporting(-1);
|
||||
|
||||
|
||||
require_once(dirname(__FILE__) . '/config.php');
|
||||
|
||||
if (file_exists("config-user.php"))
|
||||
require_once(dirname(__FILE__) . '/config.php');
|
||||
include_once(dirname(__FILE__) . '/config-user.php');
|
||||
|
||||
include_once(dirname(__FILE__) . '/config-defaults.php');
|
||||
include_once(dirname(__FILE__) . '/config-defaults.php');
|
||||
|
||||
function time_lapse_period()
|
||||
{
|
||||
|
@ -50,7 +99,7 @@ echo "<body background=\"$background_image\" onload=\"mjpeg_start();\">";
|
|||
echo "<div class=\"text-center\" style=\"color: $default_text_color; font-size: 1.4em;\">";
|
||||
echo "<img id=\"mjpeg_image\"
|
||||
alt=\"No preview jpeg. Is pikrellcam running? Click: System->Start\"
|
||||
style=\"border:6px groove silver;\"
|
||||
style=\"border:4px groove silver;\"
|
||||
onclick=\"image_expand_toggle();\"
|
||||
></div>";
|
||||
?>
|
||||
|
@ -74,10 +123,59 @@ echo "<body background=\"$background_image\" onload=\"mjpeg_start();\">";
|
|||
<input type="image" src="images/shutter.png"
|
||||
width="30" height="30"
|
||||
onclick="fifo_command('still')"
|
||||
style="margin-left:20px; vertical-align: bottom;"
|
||||
style="margin-left:16px; vertical-align: bottom;"
|
||||
>
|
||||
|
||||
<?php
|
||||
|
||||
if (defined('SERVOS_ENABLE'))
|
||||
$servos_enable = SERVOS_ENABLE;
|
||||
else
|
||||
$servos_enable = "servos_off";
|
||||
|
||||
|
||||
echo "<span style=\"margin-left:20px; color: $default_text_color\">Preset:</span>";
|
||||
|
||||
echo "<input type='image' id='preset_up' src='images/arrow-up.png'
|
||||
style='margin-left:2px; vertical-align: bottom;'
|
||||
onclick=\"fifo_command('preset next_settings')\">";
|
||||
echo "<input type='image' id='preset_down' src='images/arrow-down.png'
|
||||
style='margin-left:2px; vertical-align: bottom;'
|
||||
onclick=\"fifo_command('preset prev_settings')\">";
|
||||
if ($servos_enable == "servos_on")
|
||||
{
|
||||
echo "<input type='image' id='preset_left' src='images/arrow-left.png'
|
||||
style='margin-left:2px; vertical-align: bottom;'
|
||||
onclick=\"fifo_command('preset prev_position')\">";
|
||||
echo "<input type='image' id='preset_right' src='images/arrow-right.png'
|
||||
style='margin-left:2px; vertical-align: bottom;'
|
||||
onclick=\"fifo_command('preset next_position')\">";
|
||||
}
|
||||
|
||||
if ($servos_enable == "servos_on")
|
||||
{
|
||||
// echo "<span style=\"margin-left:20px; color: $default_text_color\">Servo:</span>";
|
||||
// background: rgba(255, 255, 255, 0.16);
|
||||
echo "<input id='servo_move_mode' type='button' value=\"Servo:\"
|
||||
class=\"btn-control\"
|
||||
style=\"cursor: pointer;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
color: $default_text_color; margin-left:20px; padding-left:2px; padding-right:0px;\"
|
||||
onclick='servo_move_mode();'>";
|
||||
echo "<input type='image' id='servo_left' src='images/arrow0-left.png'
|
||||
style='margin-left:2px; vertical-align: bottom;'
|
||||
onclick=\"servo_move_command('pan_left')\">";
|
||||
echo "<input type='image' id='servo_right' src='images/arrow0-right.png'
|
||||
style='margin-left:2px; vertical-align: bottom;'
|
||||
onclick=\"servo_move_command('pan_right')\">";
|
||||
echo "<input type='image' id='servo_up' src='images/arrow0-up.png'
|
||||
style='margin-left:2px; vertical-align: bottom;'
|
||||
onclick=\"servo_move_command('tilt_up')\">";
|
||||
echo "<input type='image' id='servo_down' src='images/arrow0-down.png'
|
||||
style='margin-left:2px; vertical-align: bottom;'
|
||||
onclick=\"servo_move_command('tilt_down')\">";
|
||||
}
|
||||
|
||||
if (defined('INCLUDE_CONTROL'))
|
||||
{
|
||||
if ($include_control == "yes")
|
||||
|
@ -98,30 +196,30 @@ if (file_exists("custom-control.php"))
|
|||
class="btn-control"
|
||||
style="margin-right:20px;"
|
||||
>Archive Calendar</a>
|
||||
<?php echo "<span style=\"color: $default_text_color\"> Media:</span>"; ?>
|
||||
<a href="media-archive.php?mode=media&type=videos"
|
||||
class="btn-control"
|
||||
>Videos</a>
|
||||
<a href="media-archive.php?mode=media&type=thumbs"
|
||||
class="btn-control"
|
||||
>Thumbs</a>
|
||||
<a href="media-archive.php?mode=media&type=stills"
|
||||
class="btn-control"
|
||||
style="margin-right:30px;"
|
||||
>Stills</a>
|
||||
<?php
|
||||
echo "<span style=\"color: $default_text_color\"> Media:</span>";
|
||||
echo "<a href='media-archive.php?mode=media&type=videos'
|
||||
style='margin-left:2px;'
|
||||
class='btn-control'
|
||||
>Videos</a>";
|
||||
echo "<a href='media-archive.php?mode=media&type=stills'
|
||||
class='btn-control'
|
||||
style='margin-left:2px; margin-right:30px;'
|
||||
>Stills</a>";
|
||||
echo "<span style=\"color: $default_text_color\"> Enable:</span>";
|
||||
?>
|
||||
|
||||
<?php echo "<span style=\"color: $default_text_color\"> Enable:</span>"; ?>
|
||||
<input type="button" id="motion_button" value="Motion"
|
||||
onclick="fifo_command('motion_enable toggle')"
|
||||
class="btn-control motion-control"
|
||||
>
|
||||
<?php echo "<span style=\"float: right; color: $default_text_color\"> Show:"; ?>
|
||||
<input id="timelapse_button" type="button" value="Timelapse"
|
||||
onclick="fifo_command('tl_show_status toggle')"
|
||||
<input type="button" id="regions_button" value="Preset"
|
||||
onclick="fifo_command('motion show_regions toggle')"
|
||||
class="btn-control motion-control"
|
||||
>
|
||||
<input type="button" id="regions_button" value="Regions"
|
||||
onclick="fifo_command('motion show_regions toggle')"
|
||||
<input id="timelapse_button" type="button" value="Timelapse"
|
||||
onclick="fifo_command('tl_show_status toggle')"
|
||||
class="btn-control motion-control"
|
||||
>
|
||||
<input type="button" id="vectors_button" value="Vectors"
|
||||
|
@ -132,7 +230,197 @@ if (file_exists("custom-control.php"))
|
|||
</div>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div class="expandable-panel" id="cp-1">
|
||||
<div class="expandable-panel-heading">
|
||||
<h3>Setup<span class="icon-close-open"></span></h3>
|
||||
</div>
|
||||
<div class="expandable-panel-content">
|
||||
<table class="table-container">
|
||||
<tr>
|
||||
<td style="border: 0;" align="right">
|
||||
<input type="image" src="images/arrow2-left.png"
|
||||
style="padding:0px 0px 0px 0px; margin:0;"
|
||||
onclick="fifo_command('display <<');"
|
||||
>
|
||||
<input type="image" src="images/arrow-left.png"
|
||||
style="padding:0px 0px 0px 0px; margin:0;"
|
||||
onclick="fifo_command('display <');"
|
||||
>
|
||||
</td>
|
||||
<td style="border: 0;" align="center">
|
||||
<input type="button" value="SEL"
|
||||
class="btn-control"
|
||||
onclick="fifo_command('display sel');"
|
||||
>
|
||||
</td>
|
||||
<td style="border: 0;" align="left">
|
||||
<input type="image" src="images/arrow-right.png"
|
||||
style="padding:0px 0px 0px 0px; margin:0;"
|
||||
onclick="fifo_command('display >');"
|
||||
>
|
||||
<input type="image" src="images/arrow2-right.png"
|
||||
style="padding:0px 0px 0px 0px; margin:0;"
|
||||
onclick="fifo_command('display >>');"
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="border: 0;" align="right" >
|
||||
</td>
|
||||
<td style="border: 0;" align="center">
|
||||
<input type="button" value="Back"
|
||||
onclick="fifo_command('display back');"
|
||||
class="btn-control"
|
||||
>
|
||||
</td>
|
||||
<td style="border: 0;" align="left" >
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<table class="table-container">
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo "<span style=\"font-weight:600; color: $default_text_color\">Preset</span>"; ?>
|
||||
<div>
|
||||
<input type="button" value="Settings"
|
||||
class="btn-menu"
|
||||
style="margin-left:40px"
|
||||
onclick="fifo_command('display motion_limit');"
|
||||
>
|
||||
|
||||
<?php
|
||||
if ($servos_enable == "servos_on")
|
||||
{
|
||||
echo "<span style=\"margin-left:20px; margin-right:0px; color: $default_text_color\">Move:";
|
||||
echo "<input type='button' value='One'
|
||||
class='btn-menu'
|
||||
style='margin-left:2px; margin-right:0px;'
|
||||
onclick=\"fifo_command('preset move_one')\">";
|
||||
echo "<input type='button' value='All'
|
||||
class='btn-menu'
|
||||
style='margin-left:4px;'
|
||||
onclick=\"fifo_command('preset move_all')\">";
|
||||
}
|
||||
?>
|
||||
|
||||
<input type="button" value="New"
|
||||
class="btn-menu"
|
||||
style="float: right; margin-left:6px"
|
||||
onclick="fifo_command('preset new');"
|
||||
>
|
||||
<?php
|
||||
if ($servos_enable == "servos_on")
|
||||
{
|
||||
echo "<input type='button' value='Copy'
|
||||
class='btn-menu'
|
||||
style='float: right; margin-left:6px'
|
||||
onclick=\"fifo_command('preset copy')\">";
|
||||
}
|
||||
?>
|
||||
<input type="button" value="Del"
|
||||
class="btn-menu alert-control"
|
||||
style="float: right;margin-left:20px"
|
||||
onclick="fifo_command('preset delete');"
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo "<span style=\"font-weight:600; color: $default_text_color\"> Time Lapse </span>"; ?>
|
||||
<div>
|
||||
<?php echo "<span style=\"margin-left:40px; font-weight:600; color: $default_text_color\"> Period </span>"; ?>
|
||||
<input type="text" id="tl_period" value="<?php echo time_lapse_period(); ?>" size="3"
|
||||
>
|
||||
<?php echo "<span style=\"margin-left:4px; color: $default_text_color\"> sec </span>"; ?>
|
||||
<input type="button" value="Start"
|
||||
class="btn-menu"
|
||||
onclick="tl_start();"
|
||||
style="float: right; margin-left:10px;"
|
||||
>
|
||||
<input type="button" value="Hold"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('tl_hold toggle');"
|
||||
style="float: right; margin-left:10px;"
|
||||
>
|
||||
<input type="button" value="End"
|
||||
class="btn-menu alert-control"
|
||||
onclick="fifo_command('tl_end');"
|
||||
style="float: right;"
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo "<span style=\"font-weight:600; color: $default_text_color\">Config</span>"; ?>
|
||||
<div>
|
||||
<input type="button" value="Video Res"
|
||||
class="btn-menu"
|
||||
style="margin-left:40px"
|
||||
onclick="fifo_command('display video_presets');"
|
||||
>
|
||||
<input type="button" value="Still Res"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display still_presets');"
|
||||
>
|
||||
<input type="button" value="Settings"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display settings');"
|
||||
>
|
||||
<input type="button" value="Times"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display motion_time');"
|
||||
>
|
||||
<?php
|
||||
if ($servos_enable == "servos_on")
|
||||
{
|
||||
echo "<input type='button' value='Servo'
|
||||
class='btn-menu'
|
||||
onclick=\"fifo_command('display servo_settings')\">";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo "<span style=\"font-weight:600; color: $default_text_color\"> Camera Params </span>"; ?>
|
||||
<div>
|
||||
<input type="button" value="Picture"
|
||||
class="btn-menu"
|
||||
style= "margin-left:40px"
|
||||
onclick="fifo_command('display picture');"
|
||||
>
|
||||
<input type="button" value="Meter"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display metering');"
|
||||
>
|
||||
<input type="button" value="Exposure"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display exposure');"
|
||||
>
|
||||
<input type="button" value="White Bal"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display white_balance');"
|
||||
>
|
||||
<input type="button" value="Image Effect"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display image_effect');"
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="expandable-panel" id="cp-2">
|
||||
<div class="expandable-panel-heading">
|
||||
<h3>Motion Regions<span class="icon-close-open"></span></h3>
|
||||
</div>
|
||||
|
@ -144,7 +432,7 @@ if (file_exists("custom-control.php"))
|
|||
<table cellpadding="0" cellspacing="0" border="0" table-layout="fixed">
|
||||
<tr>
|
||||
<td style="border: 0;" >
|
||||
<input type="button" value="List" style="margin-right: 12px;"
|
||||
<input type="button" value="List" style="margin-right: 20px;"
|
||||
onclick="list_regions();"
|
||||
class="btn-control"
|
||||
>
|
||||
|
@ -154,7 +442,7 @@ if (file_exists("custom-control.php"))
|
|||
class="btn-menu"
|
||||
>
|
||||
</td>
|
||||
<td style="border: 0;" align="right">
|
||||
<td style="border: 0;" align="left">
|
||||
<input type="text" id="save_regions" size=6 >
|
||||
<input type="button" value="Save"
|
||||
onclick="save_regions();"
|
||||
|
@ -167,7 +455,7 @@ if (file_exists("custom-control.php"))
|
|||
<td style="border: 0;" align="left">
|
||||
</td>
|
||||
<td style="border: 0;" align="right">
|
||||
<?php echo "<span style=\"color: $default_text_color\">
|
||||
<?php echo "<span style=\"color: $default_text_color; margin-left: 12px;\">
|
||||
Coarse Move</span>"; ?>
|
||||
<input type="checkbox" name="move_mode"
|
||||
onclick='move_region_mode(this);' checked>
|
||||
|
@ -175,25 +463,25 @@ if (file_exists("custom-control.php"))
|
|||
</tr>
|
||||
|
||||
<tr align="right">
|
||||
<td style="border: 0;" align="right">
|
||||
<td style="border: 0;" align="left">
|
||||
<input type="button" value="New"
|
||||
onclick="new_region();"
|
||||
class="btn-control"
|
||||
>
|
||||
<input type="button" value="Delete" style="margin-right: 8px;"
|
||||
<input type="button" value="Del" style="margin-left: 8px;"
|
||||
onclick="fifo_command('motion delete_regions selected');"
|
||||
class="btn-control alert-control"
|
||||
>
|
||||
</td>
|
||||
<td style="border: 0;" align="right">
|
||||
<?php echo "<span style=\"color: $default_text_color\">Select</span>"; ?>
|
||||
<input type="button" value="<"
|
||||
<?php echo "<span style=\"color: $default_text_color;\">Select</span>"; ?>
|
||||
<input type='image' src='images/arrow0-left.png'
|
||||
style="vertical-align: bottom;"
|
||||
onclick="fifo_command('motion select_region <');"
|
||||
class="btn-control"
|
||||
>
|
||||
<input type="button" value=">"
|
||||
<input type='image' src='images/arrow0-right.png'
|
||||
style="vertical-align: bottom;"
|
||||
onclick="fifo_command('motion select_region >');"
|
||||
class="btn-control"
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -276,152 +564,7 @@ if (file_exists("custom-control.php"))
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="expandable-panel" id="cp-2">
|
||||
<div class="expandable-panel-heading">
|
||||
<h3>Setup<span class="icon-close-open"></span></h3>
|
||||
</div>
|
||||
<div class="expandable-panel-content">
|
||||
<table class="table-container">
|
||||
<tr>
|
||||
<td style="border: 0;" align="right">
|
||||
<input type="image" src="images/arrow2-left.png"
|
||||
style="padding:0px 0px 0px 0px; margin:0;"
|
||||
onclick="fifo_command('display <<');"
|
||||
>
|
||||
<input type="image" src="images/arrow-left.png"
|
||||
style="padding:0px 0px 0px 0px; margin:0;"
|
||||
onclick="fifo_command('display <');"
|
||||
>
|
||||
</td>
|
||||
<td style="border: 0;" align="center">
|
||||
<input type="button" value="SEL"
|
||||
class="btn-control"
|
||||
onclick="fifo_command('display sel');"
|
||||
>
|
||||
</td>
|
||||
<td style="border: 0;" align="left">
|
||||
<input type="image" src="images/arrow-right.png"
|
||||
style="padding:0px 0px 0px 0px; margin:0;"
|
||||
onclick="fifo_command('display >');"
|
||||
class="btn-control"
|
||||
>
|
||||
<input type="image" src="images/arrow2-right.png"
|
||||
style="padding:0px 0px 0px 0px; margin:0;"
|
||||
onclick="fifo_command('display >>');"
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="border: 0;" align="right" >
|
||||
</td>
|
||||
<td style="border: 0;" align="center">
|
||||
<input type="button" value="Back"
|
||||
onclick="fifo_command('display back');"
|
||||
class="btn-control"
|
||||
>
|
||||
</td>
|
||||
<td style="border: 0;" align="left" >
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<table class="table-container">
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo "<span style=\"font-weight:600; color: $default_text_color\"> Camera Config </span>"; ?>
|
||||
<div class="text-center">
|
||||
<input type="button" value="Video Presets"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display video_presets');"
|
||||
>
|
||||
<input type="button" value="Still Presets"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display still_presets');"
|
||||
>
|
||||
<input type="button" value="Adjustments"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display camera_adjustments');"
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo "<span style=\"font-weight:600; color: $default_text_color\"> Camera Params </span>"; ?>
|
||||
<div class="text-center">
|
||||
<input type="button" value="Picture"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display picture');"
|
||||
>
|
||||
<input type="button" value="Metering"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display metering');"
|
||||
>
|
||||
<input type="button" value="Exposure"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display exposure');"
|
||||
>
|
||||
<input type="button" value="White Balance"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display white_balance');"
|
||||
>
|
||||
<input type="button" value="Image Effect"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display image_effect');"
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo "<span style=\"font-weight:600; color: $default_text_color\"> Motion </span>"; ?>
|
||||
<div class="text-center" >
|
||||
<input type="button" value="Vector Limits"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display motion_limit');"
|
||||
>
|
||||
<input type="button" value="Times"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display motion_time');"
|
||||
>
|
||||
<input type="button" value="Settings"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('display motion_setting');"
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo "<span style=\"font-weight:600; color: $default_text_color\"> Time Lapse </span>"; ?>
|
||||
<div>
|
||||
<?php echo "<span style=\"margin-left:40px; font-weight:600; color: $default_text_color\"> Period </span>"; ?>
|
||||
<input type="text" id="tl_period" value="<?php echo time_lapse_period(); ?>" size="3"
|
||||
>
|
||||
<?php echo "<span style=\"margin-left:4px; color: $default_text_color\"> sec </span>"; ?>
|
||||
<input type="button" value="Start"
|
||||
class="btn-menu"
|
||||
onclick="tl_start();"
|
||||
style="float: right; margin-left:10px;"
|
||||
>
|
||||
<input type="button" value="Hold"
|
||||
class="btn-menu"
|
||||
onclick="fifo_command('tl_hold toggle');"
|
||||
style="float: right; margin-left:10px;"
|
||||
>
|
||||
<input type="button" value="End"
|
||||
class="btn-menu alert-control"
|
||||
onclick="fifo_command('tl_end');"
|
||||
style="float: right;"
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="expandable-panel" id="cp-3">
|
||||
<div class="expandable-panel-heading">
|
||||
|
|
|
@ -5,7 +5,8 @@ h3, p, ol, ul, li {
|
|||
margin:0px;
|
||||
padding:0px;
|
||||
font-size:0.85em;
|
||||
font-family:Arial, Helvetica, sans-serif;
|
||||
// font-family:Arial, Helvetica, sans-serif;
|
||||
font-family:Serif, Helvetica, sans-serif;
|
||||
}
|
||||
ol, ul {
|
||||
padding:3px 0 10px 22px;
|
||||
|
|
|
@ -77,9 +77,10 @@ selected
|
|||
-webkit-border-radius: 5;
|
||||
-moz-border-radius: 5;
|
||||
border-radius: 5px;
|
||||
font-family: Arial;
|
||||
// font-family: Arial;
|
||||
font-family: Serif;
|
||||
color: #000000;
|
||||
font-size: 1.0em;
|
||||
font-size: 0.9em;
|
||||
// background: #c1c6d1;
|
||||
background: #c7c7c7;
|
||||
padding: 3px 6px 3px 6px;
|
||||
|
@ -99,9 +100,10 @@ selected
|
|||
-webkit-border-radius: 5;
|
||||
-moz-border-radius: 5;
|
||||
border-radius: 7px;
|
||||
font-family: Arial;
|
||||
// font-family: Arial;
|
||||
font-family: Serif;
|
||||
color: #000000;
|
||||
font-size: 1.0em;
|
||||
font-size: 0.9em;
|
||||
// background: #9daaad;
|
||||
background: #bac1c2;
|
||||
padding: 3px 6px 3px 6px;
|
||||
|
|
|
@ -32,7 +32,7 @@ function image_expand_toggle()
|
|||
|
||||
function new_region()
|
||||
{
|
||||
fifo_command("motion add_region 0.3 0.3 0.3 0.3");
|
||||
fifo_command("motion new_region 0.3 0.3 0.3 0.3");
|
||||
// alert("Two consecutive fifo_command() not working.");
|
||||
// fifo_command("motion select_region last\n");
|
||||
}
|
||||
|
|
|
@ -60,10 +60,10 @@ function eng_filesize($bytes, $decimals = 1)
|
|||
|
||||
function media_dir_array_create($media_dir)
|
||||
{
|
||||
global $archive_root, $media_mode, $media_type;
|
||||
global $archive_root, $media_mode, $media_type, $media_subdir;
|
||||
|
||||
$media_array = array();
|
||||
$file_dir = "$media_dir/$media_type"; // videos, thumbs, or stills
|
||||
$file_dir = "$media_dir/$media_subdir"; // videos, thumbs, or stills
|
||||
|
||||
if (is_dir($file_dir))
|
||||
{
|
||||
|
@ -98,9 +98,9 @@ function media_dir_array_create($media_dir)
|
|||
if ($mtime == 0)
|
||||
$mtime = filemtime("$file_dir" . "/" . "$file_name");
|
||||
$ymd = date("Y-m-d", $mtime);
|
||||
if ("$media_type" == "videos" || "$media_type" == "thumbs")
|
||||
{
|
||||
if ("$media_type" == "videos")
|
||||
{
|
||||
if ("$media_subdir" == "videos")
|
||||
{
|
||||
$thumb_name = str_replace(".mp4", ".th.jpg", $file_name);
|
||||
$short_name = date('H:i:s', $mtime) . "$extension";
|
||||
|
@ -195,12 +195,12 @@ function media_array_index($name)
|
|||
|
||||
function delete_file($media_dir, $fname)
|
||||
{
|
||||
global $media_mode, $media_type;
|
||||
global $media_mode, $media_subdir;
|
||||
|
||||
if (!is_dir($media_dir))
|
||||
return;
|
||||
|
||||
if ("$media_type" == "stills")
|
||||
if ("$media_subdir" == "stills")
|
||||
unlink("$media_dir/stills/$fname");
|
||||
else
|
||||
{
|
||||
|
@ -229,7 +229,7 @@ function delete_day($media_dir, $ymd)
|
|||
if (!is_dir($media_dir))
|
||||
return;
|
||||
|
||||
if ("$media_type" == "videos" || "$media_type" == "thumbs")
|
||||
if ("$media_type" == "videos")
|
||||
{
|
||||
array_map('unlink', glob("$media_dir/videos/*$ymd*.mp4"));
|
||||
array_map('unlink', glob("$media_dir/videos/*$ymd*.csv"));
|
||||
|
@ -248,7 +248,7 @@ function delete_all_files($media_dir)
|
|||
|
||||
if (!is_dir($media_dir))
|
||||
return;
|
||||
if ("$media_type" == "videos" || "$media_type" == "thumbs")
|
||||
if ("$media_type" == "videos")
|
||||
{
|
||||
array_map('unlink', glob("$media_dir/videos/*.mp4"));
|
||||
array_map('unlink', glob("$media_dir/videos/*.csv"));
|
||||
|
@ -329,7 +329,7 @@ function wait_files_gone($key, $pat)
|
|||
break;
|
||||
else if ("$key" == "day")
|
||||
{
|
||||
if ("$media_type" == "videos" || "$media_type" == "thumbs")
|
||||
if ("$media_type" == "videos")
|
||||
{
|
||||
if ( count(glob("$media_dir/videos/*$pat*")) == 0
|
||||
&& count(glob("$media_dir/thumbs/*$pat*")) == 0
|
||||
|
@ -397,9 +397,11 @@ function restart_page($selected)
|
|||
$year = "";
|
||||
|
||||
if (isset($_GET["newtype"]))
|
||||
$media_type = $_GET["newtype"]; // "videos", "stills", or "thumbs"
|
||||
$media_type = $_GET["newtype"]; // "videos" or "stills"
|
||||
else if (isset($_GET["type"]))
|
||||
$media_type = $_GET["type"]; // "videos", "stills", or "thumbs"
|
||||
$media_type = $_GET["type"]; // "videos" or "stills"
|
||||
if ("$media_type" == "thumbs")
|
||||
$media_type = "videos"; // transition from old version
|
||||
|
||||
if (isset($_GET["label"])) // Descriptive label of media_mode
|
||||
$label = $_GET["label"];
|
||||
|
@ -425,6 +427,27 @@ function restart_page($selected)
|
|||
$env = "mode=$media_mode&type=$media_type";
|
||||
}
|
||||
|
||||
if (isset($_GET["videos_mode_list"]))
|
||||
{
|
||||
$videos_mode = "list";
|
||||
config_user_save();
|
||||
}
|
||||
if (isset($_GET["videos_mode_thumbs"]))
|
||||
{
|
||||
$videos_mode = "thumbs";
|
||||
config_user_save();
|
||||
}
|
||||
if ("$media_type" == "stills")
|
||||
$media_subdir = "stills";
|
||||
else if ("$videos_mode" == "thumbs")
|
||||
$media_subdir = "thumbs";
|
||||
else
|
||||
$media_subdir = "videos";
|
||||
|
||||
//echo "<script type='text/javascript'>alert('$media_dir $media_type $media_subdir $videos_mode');</script>";
|
||||
//echo "<script type='text/javascript'>alert('$env');</script>";
|
||||
|
||||
|
||||
if (isset($_GET["toggle_scroll"]))
|
||||
{
|
||||
if ("$media_mode" == "archive")
|
||||
|
@ -500,7 +523,7 @@ function restart_page($selected)
|
|||
$fname = $_GET["archive"]; // mp4 passed for videos or thumbs type
|
||||
$ymd = $_GET["date"];
|
||||
$fifo = fopen(FIFO_FILE,"w");
|
||||
if ("$media_type" == "videos" || "$media_type" == "thumbs")
|
||||
if ("$media_type" == "videos")
|
||||
{
|
||||
fwrite($fifo, "archive_video $fname $ymd");
|
||||
$subdir = "videos";
|
||||
|
@ -519,7 +542,7 @@ function restart_page($selected)
|
|||
{
|
||||
$ymd = $_GET["archive_date"];
|
||||
$fifo = fopen(FIFO_FILE,"w");
|
||||
if ("$media_type" == "videos" || "$media_type" == "thumbs")
|
||||
if ("$media_type" == "videos")
|
||||
fwrite($fifo, "archive_video day $ymd");
|
||||
else
|
||||
fwrite($fifo, "archive_still day $ymd");
|
||||
|
@ -554,7 +577,7 @@ function restart_page($selected)
|
|||
media_array_create();
|
||||
$index = media_array_index("$selected");
|
||||
|
||||
if ( "$media_type" == "thumbs" &&
|
||||
if ( "$media_subdir" == "thumbs" &&
|
||||
( ("$media_mode" == "archive" && "$archive_thumbs_scrolled" == "no")
|
||||
|| ("$media_mode" == "media" && "$media_thumbs_scrolled" == "no")
|
||||
)
|
||||
|
@ -572,20 +595,20 @@ function restart_page($selected)
|
|||
echo "<a href=$file_path target='_blank'>
|
||||
<img src=\"$file_path\"
|
||||
style='max-width:100%;'
|
||||
style='border:6px groove silver;'>
|
||||
style='border:4px groove silver;'>
|
||||
</a>";
|
||||
else if ("$scrolled" == "yes")
|
||||
{
|
||||
$thumb_path = $media_array[$index]['thumb_path'];
|
||||
echo "<video controls width='640' style='border:6px groove silver;'>
|
||||
echo "<video controls width='640' style='border:4px groove silver;'>
|
||||
<source src=\"$file_path\" type='video/mp4'>
|
||||
Your browser does not support the video tag.
|
||||
</video>";
|
||||
if (is_file($thumb_path))
|
||||
echo "<img src=\"$thumb_path\" style='border:6px groove silver;'>";
|
||||
echo "<img src=\"$thumb_path\" style='border:4px groove silver;'>";
|
||||
else
|
||||
echo "<img src=\"$background_image\"
|
||||
style='width:150px; height:150px; border:6px groove silver;'>";
|
||||
style='width:150px; height:150px; border:4px groove silver;'>";
|
||||
}
|
||||
if ("$scrolled" == "yes")
|
||||
{
|
||||
|
@ -657,44 +680,35 @@ function restart_page($selected)
|
|||
|
||||
echo "<span style=\"font-size: 1.2em; font-weight: 500;\">
|
||||
$media_label</span>";
|
||||
$uctype = ucfirst($media_type);
|
||||
if ("$media_type" == "videos")
|
||||
{
|
||||
echo "<span style=\"margin-left: 16px; font-size: 1.2em; font-weight: 500;\">
|
||||
$uctype</span>";
|
||||
echo "<a href=\"media-archive.php?newtype=thumbs&$env\"
|
||||
class='btn-control' style='margin-left:8px;'>Thumbs</a>";
|
||||
echo "<a href=\"media-archive.php?newtype=stills&$env\"
|
||||
class='btn-control' style='margin-left:8px;'>Stills</a>";
|
||||
}
|
||||
else if ("$media_type" == "thumbs")
|
||||
{
|
||||
echo "<a href=\"media-archive.php?newtype=videos&$env\"
|
||||
class='btn-control' style='margin-left:16px;'>Videos</a>";
|
||||
echo "<span style=\"margin-left: 4px; font-size: 1.2em; font-weight: 500;\">
|
||||
$uctype</span>";
|
||||
echo "<span style=\"margin-left: 4px; font-size: 1.2em; font-weight: 500;\">Videos</span>";
|
||||
echo "<a href=\"media-archive.php?newtype=stills&$env\"
|
||||
class='btn-control' style='margin-left:8px;'>Stills</a>";
|
||||
}
|
||||
else if ("$media_type" == "stills")
|
||||
{
|
||||
echo "<a href=\"media-archive.php?newtype=videos&$env\"
|
||||
class='btn-control' style='margin-left:16px;'>Videos</a>";
|
||||
echo "<a href=\"media-archive.php?newtype=thumbs&$env\"
|
||||
class='btn-control' style='margin-left:8px;'>Thumbs</a>";
|
||||
echo "<span style=\"margin-left: 4px; font-size: 1.2em; font-weight: 500;\">
|
||||
$uctype</span>";
|
||||
class='btn-control' style='margin-left:8px;'>Videos</a>";
|
||||
echo "<span style=\"margin-left: 4px; font-size: 1.2em; font-weight: 500;\">Stills</span>";
|
||||
}
|
||||
|
||||
$disk_total = disk_total_space($archive_root);
|
||||
$disk_free = disk_free_space($archive_root);
|
||||
$used_percent = sprintf('%.1f',(($disk_total - $disk_free) / $disk_total) * 100);
|
||||
$free_percent = sprintf('%.1f',($disk_free / $disk_total) * 100);
|
||||
|
||||
$total = eng_filesize($disk_total);
|
||||
$free = eng_filesize($disk_free);
|
||||
$used = eng_filesize($disk_total - $disk_free);
|
||||
|
||||
echo "<span style=\"float: top; margin-left:30px; font-size: 0.96em; font-weight:550; color: $default_text_color\">
|
||||
Disk: ${total}B   Free: ${free}B   Used: ${used}B ($used_percent %)</span>";
|
||||
Disk: ${total}B   Free: ${free}B ($free_percent %)</span>";
|
||||
|
||||
echo "<span style='float:right;'>";
|
||||
if ("$videos_mode" == "thumbs")
|
||||
echo "<a href='media-archive.php?$env&videos_mode_list'>List View</a>";
|
||||
else
|
||||
echo "<a href='media-archive.php?$env&videos_mode_thumbs'>Thumbs View</a>";
|
||||
echo "</span>";
|
||||
echo "</div>";
|
||||
|
||||
if ($media_array_size == 0)
|
||||
|
@ -705,7 +719,7 @@ function restart_page($selected)
|
|||
|
||||
if ("$scrolled" == "yes")
|
||||
{
|
||||
if ("$media_type" == "videos" || "$media_type" == "thumbs")
|
||||
if ("$media_type" == "videos")
|
||||
$div_style = "overflow-y: scroll; height:${n_video_scroll_pixels}px; overflow-x: auto; border:4px groove silver";
|
||||
else
|
||||
$div_style = "overflow-y: scroll; height:${n_still_scroll_pixels}px; overflow-x: auto; border:4px groove silver";
|
||||
|
@ -713,7 +727,7 @@ function restart_page($selected)
|
|||
else
|
||||
$div_style = "margin: 20px; border: 4px";
|
||||
|
||||
if ("$media_type" == "thumbs")
|
||||
if ("$media_subdir" == "thumbs")
|
||||
echo "<form method=\"POST\" action=\"media-archive.php?$env\">";
|
||||
echo "<div style=\"$div_style\">";
|
||||
if ("$scrolled" == "yes")
|
||||
|
@ -734,13 +748,13 @@ function restart_page($selected)
|
|||
$date_string</span>";
|
||||
$ymd_header = $ymd;
|
||||
$dir = $media_array[$k]['media_dir'];
|
||||
if ($n_columns > 2 && "$media_type" != "thumbs")
|
||||
if ($n_columns > 2 && "$media_subdir" != "thumbs")
|
||||
echo "</td><td>";
|
||||
if ("$next_select" != "")
|
||||
$next_file = "&file=$next_select";
|
||||
else
|
||||
$next_file = "";
|
||||
if ("$media_type" == "thumbs")
|
||||
if ("$media_subdir" == "thumbs")
|
||||
echo "<input style='margin-left: 16px' type='checkbox' name='checkbox_list[]'
|
||||
onClick=\"select_day(this, '$ymd')\"/>";
|
||||
else
|
||||
|
@ -752,7 +766,7 @@ function restart_page($selected)
|
|||
style='margin-left: 32px; margin-bottom:4px; margin-top:24px; font-size: 0.82em; text-align: left;'
|
||||
onclick='if (confirm(\"Archive day $ymd?\"))
|
||||
{window.location=\"media-archive.php?$env&dir=$dir&archive_date=$ymd$next_file\";}'>";
|
||||
if ($n_columns > 2 && "$media_type" != "thumbs")
|
||||
if ($n_columns > 2 && "$media_subdir" != "thumbs")
|
||||
echo "</td><td>";
|
||||
}
|
||||
echo "<input type='button' value='Delete Day'
|
||||
|
@ -768,7 +782,7 @@ function restart_page($selected)
|
|||
$n_rows = ceil(($last - $k) / $n_columns);
|
||||
}
|
||||
|
||||
if ("$media_type" == "thumbs")
|
||||
if ("$media_subdir" == "thumbs")
|
||||
{
|
||||
echo "<tr><td>";
|
||||
for ($idx = $k; $idx < $last; ++$idx)
|
||||
|
@ -881,7 +895,7 @@ function restart_page($selected)
|
|||
Archive Calendar</a>";
|
||||
|
||||
echo "<span style=\"color: $default_text_color;\">";
|
||||
if ("$media_type" == "thumbs")
|
||||
if ("$media_subdir" == "thumbs")
|
||||
{
|
||||
echo "<span style='margin-left: 50px;'>Selections:</span>";
|
||||
if ("$media_mode" != "archive")
|
||||
|
@ -928,7 +942,7 @@ function restart_page($selected)
|
|||
echo "</span>";
|
||||
|
||||
echo "</div>";
|
||||
if ("$media_type" == "thumbs")
|
||||
if ("$media_subdir" == "thumbs")
|
||||
echo "</form>";
|
||||
|
||||
echo "</div></body></html>";
|
||||
|
|