├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── etc ├── nginx-jessie-site-default ├── nginx-wheezy-site-default └── pikrellcam.sudoers ├── install-pikrellcam.sh ├── libkrellm ├── glcd │ ├── Readme │ ├── colors.h │ ├── devices.h │ ├── font-12x24.c │ ├── font-9x15.c │ ├── font-shadow-bold.c │ ├── fonts-readable.h │ ├── fonts │ │ ├── 10x20.bdf │ │ ├── 12x24.bdf │ │ ├── 6x13.bdf │ │ ├── 7x14.bdf │ │ ├── 8x13.bdf │ │ ├── 9x15.bdf │ │ ├── 9x15.bdf.orig │ │ ├── Readme │ │ ├── bdf2c.c │ │ ├── radon-wide.bdf │ │ ├── shadow-bold.bdf │ │ └── shadow-bold.bdf.orig │ ├── glcd-events.c │ ├── glcd-fonts.h │ ├── glcd-touch-calibrate.c │ ├── glcd-touch.c │ ├── glcd-widgets.c │ ├── glcd-widgets.h │ ├── glcd.c │ ├── glcd.h │ ├── i420.c │ ├── ili9341.c │ └── ili9341.h └── utils │ ├── slist.c │ ├── spi-i2c.c │ ├── utils.c │ └── utils.h ├── pikrellcam ├── scripts-dist ├── Readme ├── _archive-still ├── _archive-video ├── _fix_thumbs ├── _init ├── _log-lines ├── _stills_thumbs_rescan ├── _thumb ├── _timelapse-convert ├── _upgrade ├── _upgrade-message ├── ds18b20.py ├── example-motion-detects-fifo ├── example-motion-events ├── example-motion-send-alarm1 ├── example-motion-send-alarm2 ├── motion-end ├── pkc-alarm ├── pkc-motion ├── pkc-reboot ├── pkc-recv ├── preview-save └── startup ├── src ├── Makefile ├── audio.c ├── config.c ├── display.c ├── event.c ├── mmal_status.h ├── mmalcam.c ├── motion.c ├── multicast.c ├── pikrellcam.c ├── pikrellcam.h ├── preset.c ├── servo.c ├── sunriset.c ├── sunriset.h ├── tcpserver.c └── tcpserver_mjpeg.c └── www ├── archive.php ├── audio_stream.php ├── config-defaults.php ├── config.php ├── control.php ├── download.php ├── fifo_command.php ├── help.php ├── images ├── architecture.jpg ├── arrow-down.png ├── arrow-left.png ├── arrow-right.png ├── arrow-up.png ├── arrow0-down.png ├── arrow0-left.png ├── arrow0-right.png ├── arrow0-up.png ├── arrow2-down.png ├── arrow2-left.png ├── arrow2-right.png ├── arrow2-up.png ├── audio-blank.png ├── audio-controls.jpg ├── audio-play.png ├── audio-stop.png ├── blank.png ├── camera-error.jpg ├── cpu-usage.jpg ├── electret.jpg ├── icon-close-open.png ├── loop.png ├── mic-down.png ├── mic-up.png ├── mic.png ├── motion-regions.jpg ├── nfs-mounting.jpg ├── paper1.png ├── passion.jpg ├── pause.png ├── preset-servos.jpg ├── record.png ├── setup.jpg ├── shadow.jpg ├── shutter.png ├── stop.png └── vector0.jpg ├── index.php ├── js-css ├── expandable-panels.css ├── expandable-panels.js ├── jquery.min.js ├── pikrellcam.css └── pikrellcam.js ├── live.php ├── log.php ├── media-archive.php ├── mjpeg_read.php ├── mjpeg_stream.php └── sys_command.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # http://EditorConfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | indent_size = 4 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | scripts 2 | www/.htpasswd 3 | www/FIFO 4 | www/motion_events_FIFO 5 | www/audio_FIFO 6 | www/media 7 | www/config-user* 8 | www/images/bg_* 9 | www/custom 10 | www/custom* 11 | www/custom.php 12 | www/user.php 13 | media 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PiKrellCam 2 | 3 | PiKrellCam is an audio/video recording motion detect program with an OSD web 4 | interface that detects motion using the Raspberry Pi camera MMAL motion vectors. 5 | 6 | Read about it and install instructions at: 7 | [PiKrellCam webpage](http://billw2.github.io/pikrellcam/pikrellcam.html) 8 | 9 | Git download with: 10 | $ git clone https://github.com/billw2/pikrellcam 11 | 12 | -------------------------------------------------------------------------------- /etc/nginx-jessie-site-default: -------------------------------------------------------------------------------- 1 | ## 2 | # You should look at the following URL's in order to grasp a solid understanding 3 | # of Nginx configuration files in order to fully unleash the power of Nginx. 4 | # http://wiki.nginx.org/Pitfalls 5 | # http://wiki.nginx.org/QuickStart 6 | # http://wiki.nginx.org/Configuration 7 | # 8 | # Generally, you will want to move this file somewhere, and start with a clean 9 | # file but keep this around for reference. Or just disable in sites-enabled. 10 | # 11 | # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. 12 | ## 13 | 14 | # Default server configuration 15 | # 16 | server { 17 | listen PORT default_server; 18 | listen [::]:PORT default_server; 19 | 20 | # SSL configuration 21 | # 22 | # listen 443 ssl default_server; 23 | # listen [::]:443 ssl default_server; 24 | # 25 | # Self signed certs generated by the ssl-cert package 26 | # Don't use them in a production server! 27 | # 28 | # include snippets/snakeoil.conf; 29 | 30 | root PIKRELLCAM_WWW; 31 | auth_basic "Restricted"; 32 | auth_basic_user_file PIKRELLCAM_WWW/.htpasswd; 33 | 34 | # Add index.php to the list if you are using PHP 35 | index index.php index.html index.htm index.nginx-debian.html; 36 | 37 | server_name _; 38 | 39 | location / { 40 | # First attempt to serve request as file, then 41 | # as directory, then fall back to displaying a 404. 42 | try_files $uri $uri/ =404; 43 | } 44 | 45 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 46 | # 47 | location ~ \.php$ { 48 | include snippets/fastcgi-php.conf; 49 | # 50 | # # With php5-cgi alone: 51 | # fastcgi_pass 127.0.0.1:9000; 52 | # # With php5-fpm: 53 | fastcgi_pass unix:/var/run/php5-fpm.sock; 54 | } 55 | 56 | # deny access to .htaccess files, if Apache's document root 57 | # concurs with nginx's one 58 | # 59 | #location ~ /\.ht { 60 | # deny all; 61 | #} 62 | } 63 | 64 | -------------------------------------------------------------------------------- /etc/nginx-wheezy-site-default: -------------------------------------------------------------------------------- 1 | # You may add here your 2 | # server { 3 | # ... 4 | # } 5 | # statements for each of your virtual hosts to this file 6 | 7 | ## 8 | # You should look at the following URL's in order to grasp a solid understanding 9 | # of Nginx configuration files in order to fully unleash the power of Nginx. 10 | # http://wiki.nginx.org/Pitfalls 11 | # http://wiki.nginx.org/QuickStart 12 | # http://wiki.nginx.org/Configuration 13 | # 14 | # Generally, you will want to move this file somewhere, and start with a clean 15 | # file but keep this around for reference. Or just disable in sites-enabled. 16 | # 17 | # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. 18 | ## 19 | 20 | server { 21 | listen PORT; ## listen for ipv4; this line is default and implied 22 | #listen [::]:PORT default_server ipv6only=on; ## listen for ipv6 23 | 24 | root PIKRELLCAM_WWW; 25 | index index.php index.html index.htm; 26 | 27 | auth_basic "Restricted"; 28 | auth_basic_user_file PIKRELLCAM_WWW/.htpasswd; 29 | 30 | # Make site accessible from http://localhost/ 31 | server_name localhost; 32 | 33 | location / { 34 | # First attempt to serve request as file, then 35 | # as directory, then fall back to displaying a 404. 36 | try_files $uri $uri/ /index.html; 37 | # Uncomment to enable naxsi on this location 38 | # include /etc/nginx/naxsi.rules 39 | } 40 | 41 | location /doc/ { 42 | alias /usr/share/doc/; 43 | autoindex on; 44 | allow 127.0.0.1; 45 | allow ::1; 46 | deny all; 47 | } 48 | 49 | 50 | #error_page 404 /404.html; 51 | 52 | # redirect server error pages to the static page /50x.html 53 | # 54 | #error_page 500 502 503 504 /50x.html; 55 | #location = /50x.html { 56 | # root /usr/share/nginx/www; 57 | #} 58 | 59 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 60 | # 61 | location ~ \.php$ { 62 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 63 | # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini 64 | # 65 | # # With php5-cgi alone: 66 | # fastcgi_pass 127.0.0.1:9000; 67 | # # With php5-fpm: 68 | fastcgi_pass unix:/var/run/php5-fpm.sock; 69 | fastcgi_index index.php; 70 | include fastcgi_params; 71 | } 72 | 73 | # deny access to .htaccess files, if Apache's document root 74 | # concurs with nginx's one 75 | # 76 | #location ~ /\.ht { 77 | # deny all; 78 | #} 79 | } 80 | 81 | -------------------------------------------------------------------------------- /etc/pikrellcam.sudoers: -------------------------------------------------------------------------------- 1 | # If started from the web page, real user needs to be pi. 2 | # so give www-data permission to run sudo -u pi pikrellcam 3 | 4 | www-data ALL=(USER) NOPASSWD: pikrellcam 5 | -------------------------------------------------------------------------------- /libkrellm/glcd/Readme: -------------------------------------------------------------------------------- 1 | glcd library 2 | ============ 3 | 4 | I originally wrote this glcd library to drive SPI LCDs connected to 5 | my Raspberry Pi adapter PCB and my STM32 project PCBs. For PiKrellCam, 6 | I added a simple I420 driver for drawing overlays on the camera mjpeg stream. 7 | 8 | The libkrellm directory organization here is structured for linking to 9 | my Raspberry Pi programs. For the purpose of distributing the PiKrellCam 10 | program, other programs and libkrellm libraries have been removed to 11 | avoid stuff not necessary for PiKrellCam. 12 | 13 | When compiling for an I420 only program such as PiKrellCam, wiringPi 14 | is not required and -D HAVE_WIRINGPI should not be defined. 15 | 16 | If compiled to use the ili9341 driver, the Makefile must have a 17 | -D HAVE_WIRINGPI define as a compiler argument and the final link 18 | must include -lwiringPi 19 | 20 | 21 | Features 22 | -------- 23 | Draw areas with clipping - tiled drawing areas can be created and graphics 24 | drawing primitives will clip to the draw areas. 25 | 26 | Widgets - Buttons, sliders and titled windows. 27 | 28 | Fonts - easy to add standard .bdf fonts. 29 | 30 | When using the ili9341 driver, colors can be selected using standard 31 | X11 color names. The I420 driver color uses only YUV intensity and 32 | does not touch the UV. 33 | 34 | TODO 35 | ---- 36 | Add other controllers. 37 | Auto detect LCD model if possible. 38 | I420 UV color. 39 | -------------------------------------------------------------------------------- /libkrellm/glcd/colors.h: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | /* 16 bit rgb colors (5/6/5) derived from: 20 | | http://en.wikipedia.org/wiki/X11_color_names 21 | */ 22 | 23 | #include 24 | 25 | /* Raspberry Pi is by default little endian */ 26 | 27 | #if __BYTE_ORDER == __LITTLE_ENDIAN 28 | 29 | #define AliceBlue 0xbfef 30 | #define AntiqueWhite 0x3af7 31 | #define Aqua 0xff07 32 | #define Aquamarine 0xf97f 33 | #define Azure 0xffef 34 | #define Beige 0x9aef 35 | #define Bisque 0x17ff 36 | #define Black 0x0000 37 | #define BlanchedAlmond 0x38ff 38 | #define Blue 0x1f00 39 | #define BlueViolet 0x5b81 40 | #define Brown 0x44a1 41 | #define Burlywood 0xb0d5 42 | #define CadetBlue 0xf35c 43 | #define Chartreuse 0xe07f 44 | #define Chocolate 0x23cb 45 | #define Coral 0xe9fb 46 | #define Cornflower 0x9c64 47 | #define Cornsilk 0xbaff 48 | #define Crimson 0xa7d0 49 | #define Cyan 0xff07 50 | #define DarkBlue 0x1100 51 | #define DarkCyan 0x5104 52 | #define DarkGoldenrod 0x21b4 53 | #define DarkGray 0x34a5 54 | #define DarkGreen 0x0003 55 | #define DarkKhaki 0xadb5 56 | #define DarkMagenta 0x1188 57 | #define DarkOliveGreen 0x4553 58 | #define DarkOrange 0x40fc 59 | #define DarkOrchid 0x9891 60 | #define DarkRed 0x0088 61 | #define DarkSalmon 0xaee4 62 | #define DarkSeaGreen 0xd18d 63 | #define DarkSlateBlue 0xf141 64 | #define DarkSlateGray 0x692a 65 | #define DarkTurquoise 0x7906 66 | #define DarkViolet 0x1988 67 | #define DeepPink 0xb1f8 68 | #define DeepSkyBlue 0xff05 69 | #define DimGray 0x2c63 70 | #define DodgerBlue 0x7f1c 71 | #define Firebrick 0x04a9 72 | #define FloralWhite 0xbdff 73 | #define ForestGreen 0x4424 74 | #define Fuchsia 0x1ff8 75 | #define Gainsboro 0xdad6 76 | #define GhostWhite 0xbff7 77 | #define Gold 0x80fe 78 | #define Goldenrod 0x04d5 79 | #define Gray 0xef7b 80 | #define Green 0xe003 81 | #define GreenYellow 0xe5af 82 | #define Honeydew 0xfdef 83 | #define HotPink 0x36fb 84 | #define IndianRed 0xcbc2 85 | #define Indigo 0x0f40 86 | #define Ivory 0xfdff 87 | #define Khaki 0x11ef 88 | #define Lavender 0x1edf 89 | #define LavenderBlush 0x7dff 90 | #define LawnGreen 0xc07f 91 | #define LemonChiffon 0xb8ff 92 | #define LightBlue 0xbbae 93 | #define LightCoral 0xefeb 94 | #define LightCyan 0xffdf 95 | #define LightGoldenrod 0xb9f7 96 | #define LightGray 0x99ce 97 | #define LightGreen 0x518f 98 | #define LightPink 0x97fd 99 | #define LightSalmon 0xeefc 100 | #define LightSeaGreen 0x9425 101 | #define LightSkyBlue 0x7e86 102 | #define LightSlateGray 0x3274 103 | #define LightSteelBlue 0x1aae 104 | #define LightYellow 0xfbff 105 | #define Lime 0xe007 106 | #define LimeGreen 0x4636 107 | #define Linen 0x7bf7 108 | #define Magenta 0x1ff8 109 | #define Maroon 0x0078 110 | #define MediumAquamarine 0x5466 111 | #define MediumBlue 0x1800 112 | #define MediumOrchid 0x99b2 113 | #define MediumPurple 0x7a8b 114 | #define MediumSeaGreen 0x8d3d 115 | #define MediumSlateBlue 0x3c73 116 | #define MediumSpringGreen 0xb207 117 | #define MediumTurquoise 0x7846 118 | #define MediumVioletRed 0xb0c0 119 | #define MidnightBlue 0xcd18 120 | #define MintCream 0xfeef 121 | #define MistyRose 0x1bff 122 | #define Moccasin 0x16ff 123 | #define NavajoWhite 0xd5fe 124 | #define Navy 0x0f00 125 | #define OldLace 0x9bf7 126 | #define Olive 0xe07b 127 | #define OliveDrab 0x646c 128 | #define Orange 0x00fd 129 | #define OrangeRed 0x20fa 130 | #define Orchid 0x7ad3 131 | #define PaleGoldenrod 0x34e7 132 | #define PaleGreen 0xb297 133 | #define PaleTurquoise 0x5caf 134 | #define PaleVioletRed 0x71d3 135 | #define PapayaWhip 0x7aff 136 | #define PeachPuff 0xb6fe 137 | #define Peru 0x07c4 138 | #define Pink 0xf8fd 139 | #define Plum 0xfad4 140 | #define PowderBlue 0xfbae 141 | #define Purple 0x0f78 142 | #define Red 0x00f8 143 | #define RosyBrown 0x71b4 144 | #define RoyalBlue 0x3b3b 145 | #define SaddleBrown 0x228a 146 | #define Salmon 0xedf3 147 | #define SandyBrown 0x0bed 148 | #define SeaGreen 0x4a2c 149 | #define Seashell 0x9cff 150 | #define Sienna 0x859a 151 | #define Silver 0xf7bd 152 | #define SkyBlue 0x7c86 153 | #define SlateBlue 0xd86a 154 | #define SlateGray 0xf16b 155 | #define Snow 0xbeff 156 | #define SpringGreen 0xef07 157 | #define SteelBlue 0x1644 158 | #define Tan 0x91cd 159 | #define Teal 0xef03 160 | #define Thistle 0xfad5 161 | #define Tomato 0x08fb 162 | #define Turquoise 0xf93e 163 | #define Violet 0x1ce4 164 | #define Wheat 0xd5ee 165 | #define White 0xffff 166 | #define WhiteSmoke 0x9def 167 | #define Yellow 0xe0ff 168 | #define YellowGreen 0x4696 169 | 170 | #else 171 | 172 | #define AliceBlue 0xefbf 173 | #define AntiqueWhite 0xf73a 174 | #define Aqua 0x07ff 175 | #define Aquamarine 0x7ff9 176 | #define Azure 0xefff 177 | #define Beige 0xef9a 178 | #define Bisque 0xff17 179 | #define Black 0x0000 180 | #define BlanchedAlmond 0xff38 181 | #define Blue 0x001f 182 | #define BlueViolet 0x815b 183 | #define Brown 0xa144 184 | #define Burlywood 0xd5b0 185 | #define CadetBlue 0x5cf3 186 | #define Chartreuse 0x7fe0 187 | #define Chocolate 0xcb23 188 | #define Coral 0xfbe9 189 | #define Cornflower 0x649c 190 | #define Cornsilk 0xffba 191 | #define Crimson 0xd0a7 192 | #define Cyan 0x07ff 193 | #define DarkBlue 0x0011 194 | #define DarkCyan 0x0451 195 | #define DarkGoldenrod 0xb421 196 | #define DarkGray 0xa534 197 | #define DarkGreen 0x0300 198 | #define DarkKhaki 0xb5ad 199 | #define DarkMagenta 0x8811 200 | #define DarkOliveGreen 0x5345 201 | #define DarkOrange 0xfc40 202 | #define DarkOrchid 0x9198 203 | #define DarkRed 0x8800 204 | #define DarkSalmon 0xe4ae 205 | #define DarkSeaGreen 0x8dd1 206 | #define DarkSlateBlue 0x41f1 207 | #define DarkSlateGray 0x2a69 208 | #define DarkTurquoise 0x0679 209 | #define DarkViolet 0x8819 210 | #define DeepPink 0xf8b1 211 | #define DeepSkyBlue 0x05ff 212 | #define DimGray 0x632c 213 | #define DodgerBlue 0x1c7f 214 | #define Firebrick 0xa904 215 | #define FloralWhite 0xffbd 216 | #define ForestGreen 0x2444 217 | #define Fuchsia 0xf81f 218 | #define Gainsboro 0xd6da 219 | #define GhostWhite 0xf7bf 220 | #define Gold 0xfe80 221 | #define Goldenrod 0xd504 222 | #define Gray 0x7bef 223 | #define Green 0x03e0 224 | #define GreenYellow 0xafe5 225 | #define Honeydew 0xeffd 226 | #define HotPink 0xfb36 227 | #define IndianRed 0xc2cb 228 | #define Indigo 0x400f 229 | #define Ivory 0xfffd 230 | #define Khaki 0xef11 231 | #define Lavender 0xdf1e 232 | #define LavenderBlush 0xff7d 233 | #define LawnGreen 0x7fc0 234 | #define LemonChiffon 0xffb8 235 | #define LightBlue 0xaebb 236 | #define LightCoral 0xebef 237 | #define LightCyan 0xdfff 238 | #define LightGoldenrod 0xf7b9 239 | #define LightGray 0xce99 240 | #define LightGreen 0x8f51 241 | #define LightPink 0xfd97 242 | #define LightSalmon 0xfcee 243 | #define LightSeaGreen 0x2594 244 | #define LightSkyBlue 0x867e 245 | #define LightSlateGray 0x7432 246 | #define LightSteelBlue 0xae1a 247 | #define LightYellow 0xfffb 248 | #define Lime 0x07e0 249 | #define LimeGreen 0x3646 250 | #define Linen 0xf77b 251 | #define Magenta 0xf81f 252 | #define Maroon 0x7800 253 | #define MediumAquamarine 0x6654 254 | #define MediumBlue 0x0018 255 | #define MediumOrchid 0xb299 256 | #define MediumPurple 0x8b7a 257 | #define MediumSeaGreen 0x3d8d 258 | #define MediumSlateBlue 0x733c 259 | #define MediumSpringGreen 0x07b2 260 | #define MediumTurquoise 0x4678 261 | #define MediumVioletRed 0xc0b0 262 | #define MidnightBlue 0x18cd 263 | #define MintCream 0xeffe 264 | #define MistyRose 0xff1b 265 | #define Moccasin 0xff16 266 | #define NavajoWhite 0xfed5 267 | #define Navy 0x000f 268 | #define OldLace 0xf79b 269 | #define Olive 0x7be0 270 | #define OliveDrab 0x6c64 271 | #define Orange 0xfd00 272 | #define OrangeRed 0xfa20 273 | #define Orchid 0xd37a 274 | #define PaleGoldenrod 0xe734 275 | #define PaleGreen 0x97b2 276 | #define PaleTurquoise 0xaf5c 277 | #define PaleVioletRed 0xd371 278 | #define PapayaWhip 0xff7a 279 | #define PeachPuff 0xfeb6 280 | #define Peru 0xc407 281 | #define Pink 0xfdf8 282 | #define Plum 0xd4fa 283 | #define PowderBlue 0xaefb 284 | #define Purple 0x780f 285 | #define Red 0xf800 286 | #define RosyBrown 0xb471 287 | #define RoyalBlue 0x3b3b 288 | #define SaddleBrown 0x8a22 289 | #define Salmon 0xf3ed 290 | #define SandyBrown 0xed0b 291 | #define SeaGreen 0x2c4a 292 | #define Seashell 0xff9c 293 | #define Sienna 0x9a85 294 | #define Silver 0xbdf7 295 | #define SkyBlue 0x867c 296 | #define SlateBlue 0x6ad8 297 | #define SlateGray 0x6bf1 298 | #define Snow 0xffbe 299 | #define SpringGreen 0x07ef 300 | #define SteelBlue 0x4416 301 | #define Tan 0xcd91 302 | #define Teal 0x03ef 303 | #define Thistle 0xd5fa 304 | #define Tomato 0xfb08 305 | #define Turquoise 0x3ef9 306 | #define Violet 0xe41c 307 | #define Wheat 0xeed5 308 | #define White 0xffff 309 | #define WhiteSmoke 0xef9d 310 | #define Yellow 0xffe0 311 | #define YellowGreen 0x9646 312 | 313 | #endif 314 | -------------------------------------------------------------------------------- /libkrellm/glcd/devices.h: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | #ifndef DEVICES_H 20 | #define DEVICES_H 21 | 22 | 23 | typedef struct CommandParams 24 | { 25 | uint8 command; 26 | uint8 n_params; 27 | uint16 *params; 28 | } 29 | CommandData; 30 | 31 | #define DEVICE_DELAY 0x7ffe 32 | 33 | #include "SSD1289.h" 34 | #include "ILI9327.h" 35 | #include "ILI9325.h" 36 | 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /libkrellm/glcd/fonts-readable.h: -------------------------------------------------------------------------------- 1 | #ifndef FONTS_READABLE_H 2 | #define FONTS_READABLE_H 3 | 4 | 5 | // (c) 2009, 2010 Lutz Sammer, License: AGPLv3 6 | 7 | /// bitmap font structure 8 | struct bitmap_font { 9 | unsigned char Width; ///< max. character width 10 | unsigned char Height; ///< character height 11 | unsigned short Chars; ///< number of characters in font 12 | const unsigned char *Widths; ///< width of each character 13 | const unsigned short *Index; ///< encoding to character index 14 | const unsigned char *Bitmap; ///< bitmap of all characters 15 | }; 16 | 17 | /// @{ defines to have human readable font files 18 | #define ________ 0x00 19 | #define _______X 0x01 20 | #define ______X_ 0x02 21 | #define ______XX 0x03 22 | #define _____X__ 0x04 23 | #define _____X_X 0x05 24 | #define _____XX_ 0x06 25 | #define _____XXX 0x07 26 | #define ____X___ 0x08 27 | #define ____X__X 0x09 28 | #define ____X_X_ 0x0A 29 | #define ____X_XX 0x0B 30 | #define ____XX__ 0x0C 31 | #define ____XX_X 0x0D 32 | #define ____XXX_ 0x0E 33 | #define ____XXXX 0x0F 34 | #define ___X____ 0x10 35 | #define ___X___X 0x11 36 | #define ___X__X_ 0x12 37 | #define ___X__XX 0x13 38 | #define ___X_X__ 0x14 39 | #define ___X_X_X 0x15 40 | #define ___X_XX_ 0x16 41 | #define ___X_XXX 0x17 42 | #define ___XX___ 0x18 43 | #define ___XX__X 0x19 44 | #define ___XX_X_ 0x1A 45 | #define ___XX_XX 0x1B 46 | #define ___XXX__ 0x1C 47 | #define ___XXX_X 0x1D 48 | #define ___XXXX_ 0x1E 49 | #define ___XXXXX 0x1F 50 | #define __X_____ 0x20 51 | #define __X____X 0x21 52 | #define __X___X_ 0x22 53 | #define __X___XX 0x23 54 | #define __X__X__ 0x24 55 | #define __X__X_X 0x25 56 | #define __X__XX_ 0x26 57 | #define __X__XXX 0x27 58 | #define __X_X___ 0x28 59 | #define __X_X__X 0x29 60 | #define __X_X_X_ 0x2A 61 | #define __X_X_XX 0x2B 62 | #define __X_XX__ 0x2C 63 | #define __X_XX_X 0x2D 64 | #define __X_XXX_ 0x2E 65 | #define __X_XXXX 0x2F 66 | #define __XX____ 0x30 67 | #define __XX___X 0x31 68 | #define __XX__X_ 0x32 69 | #define __XX__XX 0x33 70 | #define __XX_X__ 0x34 71 | #define __XX_X_X 0x35 72 | #define __XX_XX_ 0x36 73 | #define __XX_XXX 0x37 74 | #define __XXX___ 0x38 75 | #define __XXX__X 0x39 76 | #define __XXX_X_ 0x3A 77 | #define __XXX_XX 0x3B 78 | #define __XXXX__ 0x3C 79 | #define __XXXX_X 0x3D 80 | #define __XXXXX_ 0x3E 81 | #define __XXXXXX 0x3F 82 | #define _X______ 0x40 83 | #define _X_____X 0x41 84 | #define _X____X_ 0x42 85 | #define _X____XX 0x43 86 | #define _X___X__ 0x44 87 | #define _X___X_X 0x45 88 | #define _X___XX_ 0x46 89 | #define _X___XXX 0x47 90 | #define _X__X___ 0x48 91 | #define _X__X__X 0x49 92 | #define _X__X_X_ 0x4A 93 | #define _X__X_XX 0x4B 94 | #define _X__XX__ 0x4C 95 | #define _X__XX_X 0x4D 96 | #define _X__XXX_ 0x4E 97 | #define _X__XXXX 0x4F 98 | #define _X_X____ 0x50 99 | #define _X_X___X 0x51 100 | #define _X_X__X_ 0x52 101 | #define _X_X__XX 0x53 102 | #define _X_X_X__ 0x54 103 | #define _X_X_X_X 0x55 104 | #define _X_X_XX_ 0x56 105 | #define _X_X_XXX 0x57 106 | #define _X_XX___ 0x58 107 | #define _X_XX__X 0x59 108 | #define _X_XX_X_ 0x5A 109 | #define _X_XX_XX 0x5B 110 | #define _X_XXX__ 0x5C 111 | #define _X_XXX_X 0x5D 112 | #define _X_XXXX_ 0x5E 113 | #define _X_XXXXX 0x5F 114 | #define _XX_____ 0x60 115 | #define _XX____X 0x61 116 | #define _XX___X_ 0x62 117 | #define _XX___XX 0x63 118 | #define _XX__X__ 0x64 119 | #define _XX__X_X 0x65 120 | #define _XX__XX_ 0x66 121 | #define _XX__XXX 0x67 122 | #define _XX_X___ 0x68 123 | #define _XX_X__X 0x69 124 | #define _XX_X_X_ 0x6A 125 | #define _XX_X_XX 0x6B 126 | #define _XX_XX__ 0x6C 127 | #define _XX_XX_X 0x6D 128 | #define _XX_XXX_ 0x6E 129 | #define _XX_XXXX 0x6F 130 | #define _XXX____ 0x70 131 | #define _XXX___X 0x71 132 | #define _XXX__X_ 0x72 133 | #define _XXX__XX 0x73 134 | #define _XXX_X__ 0x74 135 | #define _XXX_X_X 0x75 136 | #define _XXX_XX_ 0x76 137 | #define _XXX_XXX 0x77 138 | #define _XXXX___ 0x78 139 | #define _XXXX__X 0x79 140 | #define _XXXX_X_ 0x7A 141 | #define _XXXX_XX 0x7B 142 | #define _XXXXX__ 0x7C 143 | #define _XXXXX_X 0x7D 144 | #define _XXXXXX_ 0x7E 145 | #define _XXXXXXX 0x7F 146 | #define X_______ 0x80 147 | #define X______X 0x81 148 | #define X_____X_ 0x82 149 | #define X_____XX 0x83 150 | #define X____X__ 0x84 151 | #define X____X_X 0x85 152 | #define X____XX_ 0x86 153 | #define X____XXX 0x87 154 | #define X___X___ 0x88 155 | #define X___X__X 0x89 156 | #define X___X_X_ 0x8A 157 | #define X___X_XX 0x8B 158 | #define X___XX__ 0x8C 159 | #define X___XX_X 0x8D 160 | #define X___XXX_ 0x8E 161 | #define X___XXXX 0x8F 162 | #define X__X____ 0x90 163 | #define X__X___X 0x91 164 | #define X__X__X_ 0x92 165 | #define X__X__XX 0x93 166 | #define X__X_X__ 0x94 167 | #define X__X_X_X 0x95 168 | #define X__X_XX_ 0x96 169 | #define X__X_XXX 0x97 170 | #define X__XX___ 0x98 171 | #define X__XX__X 0x99 172 | #define X__XX_X_ 0x9A 173 | #define X__XX_XX 0x9B 174 | #define X__XXX__ 0x9C 175 | #define X__XXX_X 0x9D 176 | #define X__XXXX_ 0x9E 177 | #define X__XXXXX 0x9F 178 | #define X_X_____ 0xA0 179 | #define X_X____X 0xA1 180 | #define X_X___X_ 0xA2 181 | #define X_X___XX 0xA3 182 | #define X_X__X__ 0xA4 183 | #define X_X__X_X 0xA5 184 | #define X_X__XX_ 0xA6 185 | #define X_X__XXX 0xA7 186 | #define X_X_X___ 0xA8 187 | #define X_X_X__X 0xA9 188 | #define X_X_X_X_ 0xAA 189 | #define X_X_X_XX 0xAB 190 | #define X_X_XX__ 0xAC 191 | #define X_X_XX_X 0xAD 192 | #define X_X_XXX_ 0xAE 193 | #define X_X_XXXX 0xAF 194 | #define X_XX____ 0xB0 195 | #define X_XX___X 0xB1 196 | #define X_XX__X_ 0xB2 197 | #define X_XX__XX 0xB3 198 | #define X_XX_X__ 0xB4 199 | #define X_XX_X_X 0xB5 200 | #define X_XX_XX_ 0xB6 201 | #define X_XX_XXX 0xB7 202 | #define X_XXX___ 0xB8 203 | #define X_XXX__X 0xB9 204 | #define X_XXX_X_ 0xBA 205 | #define X_XXX_XX 0xBB 206 | #define X_XXXX__ 0xBC 207 | #define X_XXXX_X 0xBD 208 | #define X_XXXXX_ 0xBE 209 | #define X_XXXXXX 0xBF 210 | #define XX______ 0xC0 211 | #define XX_____X 0xC1 212 | #define XX____X_ 0xC2 213 | #define XX____XX 0xC3 214 | #define XX___X__ 0xC4 215 | #define XX___X_X 0xC5 216 | #define XX___XX_ 0xC6 217 | #define XX___XXX 0xC7 218 | #define XX__X___ 0xC8 219 | #define XX__X__X 0xC9 220 | #define XX__X_X_ 0xCA 221 | #define XX__X_XX 0xCB 222 | #define XX__XX__ 0xCC 223 | #define XX__XX_X 0xCD 224 | #define XX__XXX_ 0xCE 225 | #define XX__XXXX 0xCF 226 | #define XX_X____ 0xD0 227 | #define XX_X___X 0xD1 228 | #define XX_X__X_ 0xD2 229 | #define XX_X__XX 0xD3 230 | #define XX_X_X__ 0xD4 231 | #define XX_X_X_X 0xD5 232 | #define XX_X_XX_ 0xD6 233 | #define XX_X_XXX 0xD7 234 | #define XX_XX___ 0xD8 235 | #define XX_XX__X 0xD9 236 | #define XX_XX_X_ 0xDA 237 | #define XX_XX_XX 0xDB 238 | #define XX_XXX__ 0xDC 239 | #define XX_XXX_X 0xDD 240 | #define XX_XXXX_ 0xDE 241 | #define XX_XXXXX 0xDF 242 | #define XXX_____ 0xE0 243 | #define XXX____X 0xE1 244 | #define XXX___X_ 0xE2 245 | #define XXX___XX 0xE3 246 | #define XXX__X__ 0xE4 247 | #define XXX__X_X 0xE5 248 | #define XXX__XX_ 0xE6 249 | #define XXX__XXX 0xE7 250 | #define XXX_X___ 0xE8 251 | #define XXX_X__X 0xE9 252 | #define XXX_X_X_ 0xEA 253 | #define XXX_X_XX 0xEB 254 | #define XXX_XX__ 0xEC 255 | #define XXX_XX_X 0xED 256 | #define XXX_XXX_ 0xEE 257 | #define XXX_XXXX 0xEF 258 | #define XXXX____ 0xF0 259 | #define XXXX___X 0xF1 260 | #define XXXX__X_ 0xF2 261 | #define XXXX__XX 0xF3 262 | #define XXXX_X__ 0xF4 263 | #define XXXX_X_X 0xF5 264 | #define XXXX_XX_ 0xF6 265 | #define XXXX_XXX 0xF7 266 | #define XXXXX___ 0xF8 267 | #define XXXXX__X 0xF9 268 | #define XXXXX_X_ 0xFA 269 | #define XXXXX_XX 0xFB 270 | #define XXXXXX__ 0xFC 271 | #define XXXXXX_X 0xFD 272 | #define XXXXXXX_ 0xFE 273 | #define XXXXXXXX 0xFF 274 | /// @} 275 | 276 | #endif 277 | -------------------------------------------------------------------------------- /libkrellm/glcd/fonts/Readme: -------------------------------------------------------------------------------- 1 | 2 | Steps to add a font to the ukrellm glcd library. 3 | ----------------------------------------------- 4 | 5 | 1) Get or generate a .bdf font file of the font you want, here use the 6 | 9x15.bdf font from X11 as an example. 7 | 8 | 2) If you want to reduce the font size to a minimum to conserve memory space, 9 | Edit the 9x15.bdf file to remove glyphs before the 'space' and after the 10 | 'tilde'. There should be 95 chars remaining in the font, so change the 11 | CHARS line in the 9x15.bdf file to be 95. Check this by running: 12 | 13 | $ grep STARTCHAR 9x15.bdf | wc 14 | 15 | 16 | 3) Run bdf2c on the .bdf file to generate the C file font.c. I have modified 17 | bdf2c (original from: http://sourceforge.net/projects/bdf2c/) to output a 18 | font.c file in a format suitable for the ukrellm glcd library. 19 | Give the font a C name "font_9x15" to be referenced in your code.. 20 | 21 | $ bdf2c -n font_9x15 -b < 9x15.bdf > 9x15.c 22 | 23 | If the 9x15.bdf file was not edited to make 'space' the first character, 24 | then you will need to edit the 9x15.c file and correctly initialize the 25 | "first_char" in the font_9x15 structure declaration at the bottom of the file. 26 | 27 | 4) Make the "font_9x15" name visible to code in the glcd library. 28 | Edit glcd-fonts.h and add the line: 29 | 30 | extern GlcdFont font_9x15; 31 | 32 | 5) Make the 9x15.c file visible to make so it will be compiled. 33 | Add a target to cSRCS_$(d) in rules.mk in the libglcd directory: 34 | 35 | cSRCS_$(d) := fonts/shadow-bold.c \ 36 | fonts/9x15.c 37 | 38 | 5) Now your application can access the font, E.g. 39 | 40 | GlcdFont *font = &font_9x15; 41 | -------------------------------------------------------------------------------- /libkrellm/glcd/glcd-events.c: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #ifdef HAVE_WIRINGPI 21 | 22 | #include 23 | #include 24 | 25 | #include "glcd-widgets.h" 26 | #include "utils.h" 27 | 28 | 29 | extern SList *glcd_widget_list; 30 | 31 | 32 | /* ====================================================== */ 33 | /* make static after debug */ 34 | GlcdEventTouch event_touch; 35 | 36 | void 37 | glcd_event_handler(Glcd *glcd) 38 | { 39 | TouchCalibratePoint display, raw; 40 | 41 | if (glcd_touch_data_available(glcd)) 42 | { 43 | glcd_touch_read_conversion(glcd, TRUE); 44 | 45 | event_touch.x_raw = raw.x = glcd_touch_get_raw_x(glcd); 46 | event_touch.y_raw = raw.y = glcd_touch_get_raw_y(glcd); 47 | touch_calibrate_point(&display, &raw, &glcd->touch.calibrate_matrix); 48 | event_touch.x = display.x; 49 | event_touch.y = display.y; 50 | 51 | // event_touch.x = glcd_touch_get_display_x(glcd); 52 | // event_touch.y = glcd_touch_get_display_y(glcd); 53 | // event_touch.x_raw = glcd_touch_get_raw_x(glcd); 54 | // event_touch.y_raw = glcd_touch_get_raw_y(glcd); 55 | // event_touch.time = millis(); 56 | event_touch.valid = TRUE; 57 | } 58 | 59 | } 60 | 61 | 62 | GlcdEventTouch * 63 | glcd_event_get_touch(Glcd *glcd) 64 | { 65 | GlcdEventTouch *te = NULL; 66 | 67 | glcd_event_handler(glcd); 68 | if (event_touch.valid) 69 | te = &event_touch; 70 | event_touch.valid = FALSE; 71 | return te; 72 | } 73 | 74 | void 75 | glcd_event_wait_for_touch_release(Glcd *glcd) 76 | { 77 | while (1) 78 | { 79 | usleep(10000); 80 | if (!glcd_touch_data_available(glcd)) 81 | break; 82 | glcd_touch_read_conversion(glcd, FALSE); 83 | } 84 | } 85 | 86 | 87 | GlcdButton * 88 | glcd_event_touch_in_button(GlcdEventTouch *te) 89 | { 90 | SList *list; 91 | GlcdWidget *pWid; 92 | int x, y; 93 | 94 | for (list = glcd_widget_list; list; list = list->next) 95 | { 96 | pWid = (GlcdWidget *) list->data; 97 | if (pWid->type != WIDGET_TYPE_BUTTON) 98 | continue; 99 | x = pWid->x + pWid->draw_area->x0; 100 | y = pWid->y + pWid->draw_area->y0; 101 | if ( te->x >= x && te->x <= x + pWid->dx 102 | && te->y >= y && te->y <= y + pWid->dy 103 | ) 104 | return (GlcdButton *) pWid; 105 | } 106 | return NULL; 107 | } 108 | 109 | GlcdSlider * 110 | glcd_event_touch_in_slider(GlcdEventTouch *te) 111 | { 112 | GlcdWidget *pWid; 113 | GlcdSlider *pSld; 114 | SList *list; 115 | int x, y; 116 | 117 | for (list = glcd_widget_list; list; list = list->next) 118 | { 119 | pWid = (GlcdWidget *) list->data; 120 | if (pWid->type != WIDGET_TYPE_SLIDER) 121 | continue; 122 | pSld = (GlcdSlider *) pWid; 123 | 124 | x = pWid->x + pWid->draw_area->x0; 125 | y = pWid->y + pWid->draw_area->y0; 126 | if ( te->x >= x && te->x < x + pWid->dx 127 | && te->y >= y && te->y < y + pWid->dy 128 | ) 129 | { 130 | te->x -= pWid->draw_area->x0; 131 | te->y -= pWid->draw_area->y0; 132 | return pSld; 133 | } 134 | } 135 | return NULL; 136 | } 137 | 138 | 139 | boolean 140 | glcd_event_check(Glcd *glcd) 141 | { 142 | GlcdEventTouch *pEv; 143 | GlcdWidget *pWid; 144 | GlcdButton *pBut; 145 | GlcdSlider *pSld; 146 | GlcdRegion *pBR; 147 | int dv, dt, value; 148 | 149 | if ((pEv = glcd_event_get_touch(glcd)) == NULL) 150 | return FALSE; 151 | if ((pBut = glcd_event_touch_in_button(pEv)) != NULL) 152 | { 153 | glcd_button_draw(pBut, BUTTON_DOWN); 154 | glcd->frame_buffer_update(glcd); 155 | glcd_event_wait_for_touch_release(glcd); 156 | glcd_button_draw(pBut, BUTTON_UP); 157 | glcd->frame_buffer_update(glcd); 158 | if (pBut->callback) 159 | (*pBut->callback)(pBut); 160 | } 161 | else if ((pSld = glcd_event_touch_in_slider(pEv)) != NULL) 162 | { 163 | pWid = (GlcdWidget *) pSld; 164 | pBR = &pSld->bar_region; 165 | 166 | /* A slider touch event region is the vertical bar region, but the 167 | | knob can only be adjusted by the slide_length (bar length minus 168 | | the knob length). So, scale touches within this region from 169 | | value_min to value_max. 170 | */ 171 | if (pWid->flags & WIDGET_FLAG_H_PACK) 172 | dv = pBR->y + (pSld->knob_length /2) + pSld->slide_length - pEv->y; 173 | else 174 | dv = pEv->x - (pBR->x + pSld->knob_length / 2); 175 | 176 | if (dv < 0) 177 | dv = 0; 178 | else if (dv > pSld->slide_length) 179 | dv = pSld->slide_length; 180 | 181 | value = pSld->value_min + (pSld->value_max - pSld->value_min) * dv 182 | / pSld->slide_length; 183 | 184 | /* Update low changes in slider value at a slower rate than 185 | | higher value changes to try to smooth the slider action. 186 | */ 187 | dv = abs(value - pSld->value); 188 | dt = pEv->time - pSld->time; 189 | if ( dv > 5 /* full speed slider */ 190 | || (dv > 3 && dt > 200) /* 5 updates/sec */ 191 | || (dv > 0 && dt > 333) /* 3 updates/sec */ 192 | ) 193 | { 194 | glcd_slider_draw(pSld); 195 | pSld->value = value; 196 | pSld->time = pEv->time; 197 | if (pSld->callback) 198 | (*pSld->callback)(pSld); 199 | } 200 | } 201 | return TRUE; 202 | } 203 | 204 | #endif /* WIRINGPI */ 205 | -------------------------------------------------------------------------------- /libkrellm/glcd/glcd-fonts.h: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | #ifndef GLCD_FONTS_H 20 | #define GLCD_FONTS_H 21 | 22 | /* XXX */ 23 | //#include "../../pikrellm.h" 24 | 25 | #include 26 | 27 | typedef struct GlcdFont 28 | { 29 | uint8_t char_width, 30 | char_height, 31 | first_char, 32 | n_chars; 33 | 34 | const unsigned char *bitmap; 35 | } 36 | GlcdFont; 37 | 38 | 39 | extern GlcdFont font_9x15; 40 | extern GlcdFont font_12x24; 41 | extern GlcdFont font_shadow_bold; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /libkrellm/glcd/glcd-touch-calibrate.c: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #ifdef HAVE_WIRINGPI 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include "glcd-widgets.h" 27 | 28 | 29 | /* Touch screen calibrate functions are based on the public domain code 30 | | written by by Carlos E. Vidales (copyright (c) 2001). See: 31 | | 32 | | http://www.embedded.com/design/system-integration/4023968/How-To-Calibrate-Touch-Screens 33 | | 34 | */ 35 | 36 | static void 37 | touch_calibrate_target(GlcdWindow *pW, 38 | TouchCalibratePoint *raw, TouchCalibratePoint *display, 39 | int radius, int text_rotation) 40 | { 41 | Glcd *glcd = pW->glcd; 42 | GlcdEventTouch *te; 43 | 44 | glcd_window_clear(pW, FALSE); 45 | glcd_draw_circle(pW->glcd, &pW->body_area, Red, 46 | display->x, display->y, radius); 47 | glcd->frame_buffer_update(glcd); 48 | usleep(500000); 49 | 50 | do 51 | { 52 | glcd_touch_read_conversion(glcd, FALSE); 53 | usleep(10000); 54 | te = glcd_event_get_touch(glcd); 55 | } 56 | while (!te); 57 | 58 | raw->x = te->x_raw; 59 | raw->y = te->y_raw; 60 | // printf("x_raw = %d y_raw = %d\n", raw->x, raw->y); 61 | } 62 | 63 | void 64 | touch_calibrate_screen(Glcd *glcd) 65 | { 66 | GlcdWindow *pW; 67 | DrawArea *screen_area; 68 | TouchCalibratePoint raw[3], 69 | display[3]; 70 | TouchCalibrateMatrix *matrix; 71 | int save_rotation; 72 | 73 | save_rotation = glcd_get_rotation(glcd); 74 | glcd_set_rotation(glcd, 0); 75 | pW = glcd_window_new(glcd, NULL, NULL, 0, 0, 0, 0, /* no title */ 76 | glcd_map_color_percent(18, 18, 28), /* body bg_color */ 77 | 0, 0, /* no body border */ 78 | 0, 0, /* window size */ 79 | glcd_get_display_width(glcd), glcd_get_display_height(glcd)); 80 | screen_area = &pW->body_area; 81 | 82 | display[0].x = screen_area->width / 10; 83 | display[0].y = screen_area->height / 10; 84 | touch_calibrate_target(pW, &raw[0], &display[0], 5, save_rotation); 85 | 86 | display[1].x = screen_area->width - screen_area->width / 10; 87 | display[1].y = screen_area->height / 2; 88 | touch_calibrate_target(pW, &raw[1], &display[1], 5, save_rotation); 89 | 90 | display[2].x = screen_area->width / 2; 91 | display[2].y = screen_area->height - screen_area->height / 10; 92 | touch_calibrate_target(pW, &raw[2], &display[2], 5, save_rotation); 93 | 94 | matrix = &glcd->touch.calibrate_matrix; 95 | touch_set_calibrate_matrix(&display[0], &raw[0], matrix); 96 | 97 | printf("Calibrate Matrix:\n"); 98 | printf( " An = %d\n" 99 | " Bn = %d\n" 100 | " Cn = %d\n" 101 | " Dn = %d\n" 102 | " En = %d\n" 103 | " Fn = %d\n" 104 | " dividor = %d\n", 105 | 106 | matrix->An, matrix->Bn, matrix->Cn, matrix->Dn, matrix->En, matrix->Fn, 107 | matrix->divider); 108 | 109 | glcd_set_rotation(glcd, save_rotation); 110 | } 111 | 112 | /* 113 | */ 114 | boolean 115 | touch_set_calibrate_matrix( 116 | TouchCalibratePoint *display, TouchCalibratePoint *raw, 117 | TouchCalibrateMatrix *matrix) 118 | { 119 | matrix->divider = ((raw[0].x - raw[2].x) * (raw[1].y - raw[2].y)) - 120 | ((raw[1].x - raw[2].x) * (raw[0].y - raw[2].y)); 121 | 122 | if (matrix->divider == 0) 123 | return FALSE; 124 | 125 | matrix->An = 126 | ((display[0].x - display[2].x) * (raw[1].y - raw[2].y)) - 127 | ((display[1].x - display[2].x) * (raw[0].y - raw[2].y)); 128 | 129 | matrix->Bn = 130 | ((raw[0].x - raw[2].x) * (display[1].x - display[2].x)) - 131 | ((display[0].x - display[2].x) * (raw[1].x - raw[2].x)); 132 | 133 | matrix->Cn = 134 | (raw[2].x * display[1].x - raw[1].x * display[2].x) * raw[0].y + 135 | (raw[0].x * display[2].x - raw[2].x * display[0].x) * raw[1].y + 136 | (raw[1].x * display[0].x - raw[0].x * display[1].x) * raw[2].y; 137 | 138 | matrix->Dn = 139 | ((display[0].y - display[2].y) * (raw[1].y - raw[2].y)) - 140 | ((display[1].y - display[2].y) * (raw[0].y - raw[2].y)); 141 | 142 | matrix->En = 143 | ((raw[0].x - raw[2].x) * (display[1].y - display[2].y)) - 144 | ((display[0].y - display[2].y) * (raw[1].x - raw[2].x)); 145 | 146 | matrix->Fn = 147 | (raw[2].x * display[1].y - raw[1].x * display[2].y) * raw[0].y + 148 | (raw[0].x * display[2].y - raw[2].x * display[0].y) * raw[1].y + 149 | (raw[1].x * display[0].y - raw[0].x * display[1].y) * raw[2].y; 150 | 151 | return TRUE; 152 | } 153 | 154 | 155 | /* Convert touch screen raw point coordinates into calibrated display 156 | | point coordinates using a calibrate matrix. 157 | */ 158 | int 159 | touch_calibrate_point( 160 | TouchCalibratePoint *display, TouchCalibratePoint *raw, 161 | TouchCalibrateMatrix *matrix) 162 | { 163 | if (matrix->divider == 0) 164 | return FALSE; 165 | 166 | display->x = ((matrix->An * raw->x) + (matrix->Bn * raw->y) + matrix->Cn) 167 | / matrix->divider; 168 | 169 | display->y = ((matrix->Dn * raw->x) + (matrix->En * raw->y) + matrix->Fn) 170 | / matrix->divider; 171 | 172 | return TRUE; 173 | } 174 | 175 | #endif /* HAVE_WIRINGPI */ 176 | -------------------------------------------------------------------------------- /libkrellm/glcd/glcd-widgets.h: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | 21 | #ifndef _GLCD_WIDGETS_H 22 | #define _GLCD_WIDGETS_H 23 | 24 | #include "glcd.h" 25 | 26 | #define BUTTON_UP 1 27 | #define BUTTON_DOWN 0 28 | 29 | #define SPINBUTTON_UP 0 30 | #define SPINBUTTON_INC_DOWN 1 31 | #define SPINBUTTON_DEC_DOWN 2 32 | 33 | 34 | #define SPLIT_1ST 0x1 35 | #define SPLIT_2ND 0x2 36 | #define SPLIT_PERCENT 0x4 37 | #define SPLIT_PIXELS 0x8 38 | 39 | 40 | typedef struct 41 | { 42 | Glcd *glcd; 43 | char *title; 44 | GlcdFont *title_font; 45 | uint16_t title_color; 46 | DrawArea title_area; 47 | DrawArea body_area; 48 | } 49 | GlcdWindow; 50 | 51 | 52 | 53 | #define BUTTON_EXPAND_NONE 0x1 54 | #define BUTTON_EXPAND_EQUAL 0x2 55 | #define BUTTON_EXPAND_FILL 0x4 56 | 57 | #define WIDGET_TYPE_BUTTON 1 58 | #define WIDGET_TYPE_SLIDER 2 59 | #define WIDGET_TYPE_SPINBUTTON 3 60 | 61 | #define WIDGET_FLAG_JUSTIFY_START 0x1 62 | #define WIDGET_FLAG_JUSTIFY_END 0x2 63 | #define WIDGET_FLAG_JUSTIFY_CENTER 0x4 64 | #define WIDGET_FLAG_PACK_START 0x8 65 | #define WIDGET_FLAG_PACK_END 0x10 66 | #define WIDGET_FLAG_H_PACK 0x20 67 | #define WIDGET_FLAG_V_PACK 0x40 68 | 69 | typedef struct 70 | { 71 | GlcdWindow *window; 72 | DrawArea *draw_area; 73 | uint8_t type, 74 | flags; 75 | int x, y, 76 | dx, dy; 77 | } 78 | GlcdWidget; 79 | 80 | typedef struct _GlcdButton 81 | { 82 | GlcdWidget widget; 83 | GlcdFont *font; 84 | char *text; 85 | uint16_t text_color, 86 | bg_color; 87 | uint8_t pad; 88 | 89 | void (*callback)(struct _GlcdButton *); 90 | int data; 91 | } 92 | GlcdButton; 93 | 94 | typedef struct 95 | { 96 | uint16_t x, y, 97 | dx, dy; 98 | } 99 | GlcdRegion; 100 | 101 | 102 | typedef struct _GlcdSpinButton 103 | { 104 | GlcdWidget widget; 105 | GlcdFont *font; 106 | char text[32]; 107 | uint16_t text_color, 108 | bg_color, 109 | arrow_color; 110 | 111 | int value, 112 | value_min, 113 | value_max, 114 | increment; 115 | 116 | int text_width, 117 | text_height; 118 | int arrow_width;; 119 | int pad; 120 | 121 | GlcdRegion value_region, 122 | arrow_region; 123 | 124 | void (*callback)(struct _GlcdSpinButton *); 125 | int data; 126 | } 127 | GlcdSpinButton; 128 | 129 | 130 | 131 | #define GLCDSLIDER_VALUE_PLACEMENT_MIN 1 132 | #define GLCDSLIDER_VALUE_PLACEMENT_MAX 2 133 | 134 | #define GLCDSLIDER_BAR_RADIUS 4 135 | #define GLCDSLIDER_KNOB_RADIUS 4 136 | 137 | typedef struct _GlcdSlider 138 | { 139 | GlcdWidget widget; 140 | GlcdFont *font; 141 | uint16_t text_color, 142 | body_color, 143 | knob_color; 144 | uint8_t pad, 145 | value_placement; /* Next to value_min or value_max */ 146 | int value, 147 | value_min, 148 | value_max; 149 | 150 | uint16_t knob_width, 151 | knob_length, 152 | bar_width, 153 | bar_length, 154 | slide_length; 155 | GlcdRegion value_region, 156 | knob_region, 157 | bar_region; 158 | 159 | unsigned int time; 160 | void (*callback)(struct _GlcdSlider *); 161 | int data; 162 | } 163 | GlcdSlider; 164 | 165 | 166 | typedef struct 167 | { 168 | TouchCalibratePoint display, 169 | raw; 170 | boolean valid; 171 | unsigned int time; 172 | 173 | int x, 174 | y, 175 | x_raw, 176 | y_raw; 177 | } 178 | GlcdEventTouch; 179 | 180 | 181 | GlcdWindow *glcd_window_new(Glcd *glcd, 182 | char *title, GlcdFont *title_font, 183 | uint16_t title_color, uint16_t title_bg_color, 184 | uint16_t title_border_color, uint8_t title_border, 185 | uint16_t body_bg_color, uint16_t body_border_color, 186 | uint8_t body_border, 187 | int x0, int y0, int width, int height); 188 | void glcd_window_clear(GlcdWindow *pW, boolean draw_title); 189 | void glcd_window_delete(GlcdWindow *pW); 190 | void glcd_widget_destroy_all(GlcdWindow *pW); 191 | void glcd_widget_draw_all(GlcdWindow *pW); 192 | void glcd_widget_h_pack(DrawArea *da, GlcdWidget *pWid, uint8_t flags); 193 | void glcd_widget_v_pack(DrawArea *da, GlcdWidget *pWid, uint8_t flags); 194 | void glcd_widget_extents(GlcdWindow *pW, DrawArea *da, 195 | int *dxmax, int *dymax); 196 | 197 | 198 | void glcd_area_clear(Glcd *glcd, DrawArea *da); 199 | 200 | void glcd_area_h_split(DrawArea *src, DrawArea *dst1, DrawArea *dst2, 201 | uint8_t amount, uint8_t how, uint8_t border); 202 | void glcd_area_v_split(DrawArea *src, DrawArea *dst1, DrawArea *dst2, 203 | uint8_t amount, uint8_t how, uint8_t border); 204 | 205 | 206 | GlcdButton *glcd_button_new(GlcdWindow *pW, 207 | GlcdFont *font, char *text, 208 | uint16_t text_color, uint16_t bg_color, uint8_t pad, 209 | void (*callback)(GlcdButton *), int data); 210 | void glcd_button_connect(GlcdButton *pBut, 211 | void (*callback)(GlcdButton *), int data); 212 | void glcd_button_draw(GlcdButton *but, boolean up); 213 | 214 | 215 | GlcdSpinButton 216 | *glcd_spin_button_new(GlcdWindow *pW, GlcdFont *font, 217 | uint16_t text_color, uint16_t bg_color, 218 | uint16_t arrow_color, uint8_t pad, 219 | int value, int value_min, int value_max, int increment, 220 | void (*callback)(GlcdSpinButton *), int data); 221 | void glcd_spin_button_connect(GlcdSpinButton *pBut, 222 | void (*callback)(GlcdSpinButton *), int data); 223 | void glcd_spin_button_draw(GlcdSpinButton *pBut, int how); 224 | 225 | 226 | GlcdSlider *glcd_slider_new(GlcdWindow *pW, GlcdFont *font, 227 | uint16_t text_color, uint16_t body_color, uint16_t knob_color, 228 | int value, int value_pos, int value_min, int value_max, 229 | int bar_width, int knob_width, int knob_length, int pad); 230 | void glcd_slider_connect(GlcdSlider *pSld, 231 | void (*callback)(GlcdSlider *), int data); 232 | void glcd_slider_draw(GlcdSlider *sld); 233 | 234 | 235 | 236 | void glcd_event_handler(Glcd *glcd); 237 | boolean glcd_event_check(Glcd *glcd); 238 | void glcd_event_wait_for_touch_release(Glcd *glcd); 239 | GlcdButton *glcd_event_touch_in_button(GlcdEventTouch *te); 240 | GlcdSlider *glcd_event_touch_in_slider(GlcdEventTouch *te); 241 | GlcdEventTouch *glcd_event_get_touch(Glcd *glcd); 242 | 243 | #endif /* _GLCD_WIDGETS_H */ 244 | -------------------------------------------------------------------------------- /libkrellm/glcd/glcd.h: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #ifndef GLCD_H 21 | #define GLCD_H 22 | 23 | #include "utils.h" 24 | #include "colors.h" 25 | #include "glcd-fonts.h" 26 | 27 | #define TRUE 1 28 | #define FALSE 0 29 | 30 | typedef int boolean; 31 | 32 | 33 | /* Known graphics LCD models 34 | */ 35 | #define GLCD_GENERIC 0 36 | #define GLCD_TFT01_32 1 /* Elec Freaks LCDs */ 37 | #define GLCD_TFT01_32W 2 38 | #define GLCD_TFT01_22S 3 39 | #define GLCD_ITDB02_32S 4 /* Iteadstudio LCDs */ 40 | #define GLCD_ITDB02_32WC 5 41 | 42 | /* And supported controllers 43 | */ 44 | #define ILI9341_SPI 1 45 | 46 | #define SSD1289 2 47 | #define ILI9327 3 48 | #define ILI9325 4 49 | 50 | 51 | #define GLCD_MODE_UNDEFINED 0 52 | #define GLCD_MODE_SPI_SOFT 1 53 | #define GLCD_MODE_SPI_HARD 2 54 | 55 | typedef struct 56 | { 57 | int32_t x, 58 | y; 59 | } 60 | TouchCalibratePoint; 61 | 62 | 63 | /* From Vidales calibrate.c 64 | */ 65 | 66 | typedef struct 67 | { 68 | int32_t An, 69 | Bn, 70 | Cn, 71 | Dn, 72 | En, 73 | Fn, 74 | divider; 75 | } 76 | TouchCalibrateMatrix; 77 | 78 | 79 | typedef struct 80 | { 81 | int DIN_pin, 82 | DOUT_pin, 83 | CS_pin, 84 | CLK_pin; 85 | } 86 | GlcdSpiSoft; 87 | 88 | typedef struct _glcdtouch 89 | { 90 | SpiDevice *spi_device; 91 | int spi_demux_BA; 92 | GlcdSpiSoft spi_soft; 93 | int IRQ_pin; 94 | 95 | uint16_t x_raw, /* 12 bits precision */ 96 | y_raw; 97 | int x_min, 98 | x_max, 99 | y_min, 100 | y_max; 101 | int convert_count; 102 | 103 | TouchCalibrateMatrix 104 | calibrate_matrix; 105 | 106 | void (*read_conversion)(struct _glcdtouch *glcd_touch, boolean save); 107 | boolean (*data_available)(struct _glcdtouch *glcd_touch); 108 | } 109 | GlcdTouch; 110 | 111 | typedef struct 112 | { 113 | uint8_t border; 114 | uint16_t border_color; 115 | uint16_t bg_color; 116 | int x0, y0, 117 | width, height; 118 | } 119 | DrawArea; 120 | 121 | typedef struct _glcd 122 | { 123 | SpiDevice *spi_device; 124 | int spi_demux_BA; 125 | int dc_pin, 126 | reset_pin, 127 | led_pin; 128 | int screen_width, 129 | screen_height; 130 | DrawArea display; 131 | int rotation; 132 | 133 | GlcdTouch touch; 134 | 135 | void (*led_control)(struct _glcd *glcd, int state); 136 | void (*set_address_window)(struct _glcd *glcd, 137 | int x0, int y0, int x1, int y1); 138 | void (*set_pixel)(struct _glcd *glcd, uint16_t color, int x, int y); 139 | void (*h_line)(struct _glcd *glcd, uint16_t color, 140 | int x, int y, int dx); 141 | void (*v_line)(struct _glcd *glcd, uint16_t color, 142 | int x, int y, int dy); 143 | void (*write_data)(uint16_t data); 144 | void (*set_rotation)(struct _glcd *glcd, int rotation); 145 | void (*set_frame_buffer)(struct _glcd *glcd, 146 | uint16_t * fb, int width, int height); 147 | void (*frame_buffer_update)(struct _glcd *glcd); 148 | 149 | uint16_t *frame_buffer; 150 | } 151 | Glcd; 152 | 153 | typedef struct 154 | { 155 | uint16_t width, 156 | height; 157 | uint16_t *data; 158 | } 159 | GlcdImage; 160 | 161 | 162 | #define DRAW_AREA(cp) ((DrawArea *) (cp)) 163 | 164 | 165 | //void glcd_dbi_init(uint8_t model, uint8_t CS, uint8_t DC, uint8_t RD, 166 | // uint8_t WR, uint8_t RESET, gpio_dev *DATA_dev); 167 | 168 | Glcd *glcd_i420_init(void); 169 | 170 | void glcd_led(Glcd *glcd, int state); 171 | 172 | int glcd_get_screen_height(Glcd *glcd); 173 | int glcd_get_screen_width(Glcd *glcd); 174 | int glcd_get_display_height(Glcd *glcd); 175 | int glcd_get_display_width(Glcd *glcd); 176 | DrawArea *glcd_get_display_area(Glcd *glcd); 177 | int glcd_get_rotation(Glcd *glcd); 178 | void glcd_set_rotation(Glcd *glcd, int rotation); 179 | void glcd_set_frame_buffer(Glcd *glcd, void *fb, int width, int height); 180 | 181 | uint16_t glcd_map_color(uint8_t r, uint8_t g, uint8_t b); 182 | uint16_t glcd_map_color_percent(int r_percent, int g_percent, int b_percent); 183 | 184 | void glcd_draw_pixel(Glcd *glcd, DrawArea *da, uint16_t color, 185 | int x0, int y0); 186 | void glcd_draw_line(Glcd *glcd, DrawArea *da, uint16_t color, 187 | int x0, int y0, int x1, int y1); 188 | void glcd_draw_h_line(Glcd *glcd, DrawArea *da, uint16_t color, 189 | int x0, int y0, int len); 190 | void glcd_draw_v_line(Glcd *glcd, DrawArea *da, uint16_t color, 191 | int x0, int y0, int len); 192 | 193 | 194 | void glcd_draw_rectangle(Glcd *glcd, DrawArea *da, uint16_t color, 195 | int x0, int y0, int width, int height); 196 | void glcd_fill_rectangle(Glcd *glcd, DrawArea *da, uint16_t color, 197 | int x0, int y0, int width, int height); 198 | void glcd_draw_rounded_rectangle(Glcd *glcd, DrawArea *da, uint16_t color, 199 | int x0, int y0, int width, int height, int radius); 200 | void glcd_fill_rounded_rectangle(Glcd *glcd, DrawArea *da, uint16_t color, 201 | int x0, int y0, int width, int height, int radius); 202 | void glcd_draw_circle(Glcd *glcd, DrawArea *da, uint16_t color, 203 | int x0, int y0, int radius); 204 | void glcd_fill_circle(Glcd *glcd, DrawArea *da, uint16_t color, 205 | int x0, int y0, int radius); 206 | void glcd_fill_screen(Glcd *glcd, uint16_t color); 207 | 208 | 209 | void glcd_print_string(Glcd *glcd, DrawArea *da, GlcdFont *font, 210 | uint16_t color, boolean clear, int row, char *string); 211 | int glcd_draw_string(Glcd *glcd, DrawArea *da, GlcdFont *font, 212 | uint16_t color, int x0, int y0, char *string); 213 | int glcd_draw_string_rotated(Glcd *glcd, DrawArea *pA, GlcdFont *font, 214 | uint16_t color, int degree, int x0, int y0, char *string); 215 | 216 | void glcd_draw_pixmap(Glcd *glcd, DrawArea *pA, uint16_t *pixmap, 217 | int x0, int y0, int width, int height); 218 | void glcd_draw_image(Glcd *glcd, DrawArea *pA, GlcdImage *im, 219 | int x0, int y0); 220 | 221 | 222 | void glcd_touch_spi_soft_init(Glcd *glcd, int DIN_pin, int DOUT_pin, 223 | int CS_pin, int CLK_pin, int IRQ_pin); 224 | void glcd_touch_spi_hard_init(Glcd *glcd, SpiDevice *spidev, uint32_t speed, 225 | int demux_BA, int IRQ_pin); 226 | void glcd_touch_irq_connect(Glcd *glcd, void (*handler)()); 227 | void glcd_touch_irq_disconnect(Glcd *glcd); 228 | void glcd_touch_set_calibrate(GlcdTouch *touch, 229 | int xmin, int xmax, int ymin, int ymax); 230 | void glcd_touch_read_conversion(Glcd *glcd, boolean save); 231 | boolean glcd_touch_data_available(Glcd *glcd); 232 | int glcd_touch_get_display_x(Glcd *glcd); 233 | int glcd_touch_get_display_y(Glcd *glcd); 234 | int glcd_touch_get_raw_x(Glcd *glcd); 235 | int glcd_touch_get_raw_y(Glcd *glcd); 236 | void glcd_touch_set_convert_count(GlcdTouch *touch, int count); 237 | 238 | void touch_calibrate_screen(Glcd *glcd); 239 | boolean touch_set_calibrate_matrix( 240 | TouchCalibratePoint *display, TouchCalibratePoint *raw, 241 | TouchCalibrateMatrix *matrix); 242 | int touch_calibrate_point( 243 | TouchCalibratePoint *display, TouchCalibratePoint *raw, 244 | TouchCalibrateMatrix *matrix); 245 | 246 | 247 | #endif 248 | -------------------------------------------------------------------------------- /libkrellm/glcd/i420.c: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #include 21 | #include "glcd.h" 22 | 23 | 24 | static void 25 | i420_set_pixel(Glcd *glcd, uint16_t color, int x, int y) 26 | { 27 | uint8_t *p = (uint8_t *) glcd->frame_buffer; 28 | int offset; 29 | 30 | offset = y * glcd->display.width + x; 31 | *(p + offset) = (uint8_t) color; 32 | } 33 | 34 | static void 35 | i420_h_line(Glcd *glcd, uint16_t color, int x, int y, int dx) 36 | { 37 | uint8_t *p = (uint8_t *) glcd->frame_buffer; 38 | int offset; 39 | 40 | offset = y * glcd->display.width + x; 41 | while (dx--) 42 | *(p + offset++) = (uint8_t) color; 43 | } 44 | 45 | static void 46 | i420_v_line(Glcd *glcd, uint16_t color, int x, int y, int dy) 47 | { 48 | uint8_t *p = (uint8_t *) glcd->frame_buffer; 49 | int offset; 50 | 51 | offset = y * glcd->display.width + x; 52 | while (dy--) 53 | { 54 | *(p + offset) = (uint8_t) color; 55 | offset += glcd->display.width; 56 | } 57 | } 58 | 59 | static void 60 | i420_set_frame_buffer(Glcd *glcd, uint16_t *fb, int width, int height) 61 | { 62 | glcd->frame_buffer = fb; 63 | glcd->screen_width = glcd->display.width = width; 64 | glcd->screen_height = glcd->display.height = height; 65 | } 66 | 67 | /* 68 | | 69 | */ 70 | Glcd * 71 | glcd_i420_init(void) 72 | { 73 | Glcd *glcd; 74 | 75 | glcd = calloc(sizeof(Glcd), 1); 76 | 77 | glcd->set_pixel = i420_set_pixel; 78 | glcd->h_line = i420_h_line; 79 | glcd->v_line = i420_v_line; 80 | glcd->set_frame_buffer = i420_set_frame_buffer; 81 | 82 | return glcd; 83 | } 84 | -------------------------------------------------------------------------------- /libkrellm/glcd/ili9341.c: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #ifdef HAVE_WIRINGPI 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "glcd.h" 29 | #include "utils.h" 30 | #include "ili9341.h" 31 | 32 | 33 | uint16_t frame_buffer[240 * 320]; 34 | 35 | 36 | 37 | /* Number of varargs passed, works only for int args. 38 | | See: http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html 39 | | http://smackerelofopinion.blogspot.com/2011/10/determining-number-of-arguments-in-c.html 40 | */ 41 | #define NUM_VARARGS(...) (int)(sizeof((int[]){0, ##__VA_ARGS__})/sizeof(int)-1) 42 | 43 | #define ili9341_command(glcd, command, ...) \ 44 | _ili9341_command(glcd, command, NUM_VARARGS(__VA_ARGS__), ##__VA_ARGS__) 45 | 46 | 47 | static void 48 | _ili9341_command(Glcd *glcd, int command, int n_params, ...) 49 | { 50 | va_list args; 51 | int i; 52 | uint8_t cbuf[2], buf[32]; 53 | 54 | // printf("0x%x: ", command); 55 | 56 | cbuf[0] = (uint8_t) command; 57 | 58 | va_start(args, n_params); 59 | for (i = 0; i < n_params; ++i) 60 | { 61 | buf[i] = (uint8_t) va_arg(args, unsigned int); 62 | // printf("%02x ", buf[i]); 63 | } 64 | va_end(args); 65 | // printf("\n"); 66 | 67 | digitalWrite(glcd->dc_pin, LOW); 68 | spi_write(glcd->spi_device, glcd->spi_demux_BA, cbuf, 1); 69 | 70 | digitalWrite(glcd->dc_pin, HIGH); 71 | if (n_params > 0) 72 | spi_write(glcd->spi_device, glcd->spi_demux_BA, buf, n_params); 73 | } 74 | 75 | static void 76 | ili9341_set_address_window(Glcd *glcd, int x0, int y0, int x1, int y1) 77 | { 78 | ili9341_command(glcd, 0x2A /* ILI9341_COLUMN_ADDRESS_SET */, 79 | (x0 >> 8) & 0xFF, x0 & 0xFF, (x1 >> 8) & 0xFF, x1 & 0xFF); 80 | ili9341_command(glcd, 0x2B /* ILI9341_PAGE_ADDRESS_SET */, 81 | (y0 >> 8) & 0xFF, y0 & 0xFF, (y1 >> 8) & 0xFF, y1 & 0xFF); 82 | 83 | ili9341_command(glcd, 0x2C /* ILI9341_MEMORY_WRITE */); 84 | } 85 | 86 | 87 | static void 88 | ili9341_set_pixel(Glcd *glcd, uint16_t color, int x, int y) 89 | { 90 | uint16_t *p = glcd->frame_buffer; 91 | int offset; 92 | 93 | offset = y * glcd->display.width + x; 94 | *(p + offset) = color; 95 | } 96 | 97 | static void 98 | ili9341_h_line(Glcd *glcd, uint16_t color, int x, int y, int dx) 99 | { 100 | uint16_t *p = glcd->frame_buffer; 101 | int offset; 102 | 103 | offset = y * glcd->display.width + x; 104 | while (dx--) 105 | *(p + offset++) = color; 106 | } 107 | 108 | static void 109 | ili9341_v_line(Glcd *glcd, uint16_t color, int x, int y, int dy) 110 | { 111 | uint16_t *p = glcd->frame_buffer; 112 | int offset; 113 | 114 | offset = y * glcd->display.width + x; 115 | while (dy--) 116 | { 117 | *(p + offset) = color; 118 | offset += glcd->display.width; 119 | } 120 | } 121 | 122 | static void 123 | ili9341_write_data(uint16_t color) 124 | { 125 | } 126 | 127 | static void 128 | ili9341_frame_buffer_update(Glcd *glcd) 129 | { 130 | uint8_t *fb8 = (uint8_t *) glcd->frame_buffer; 131 | uint8_t *p, *plast; 132 | int pixels = glcd->display.width * glcd->display.height; 133 | 134 | ili9341_set_address_window(glcd, 0, 0, 135 | glcd->display.width - 1, glcd->display.height - 1); 136 | 137 | /* SPI max IO transfer size defaults to one page size (4096) 138 | */ 139 | plast = fb8 + 2 * pixels; 140 | for (p = fb8; p < plast - 4096; p += 4096) 141 | spi_write(glcd->spi_device, glcd->spi_demux_BA, p, 4096); 142 | if (p < plast) 143 | spi_write(glcd->spi_device, glcd->spi_demux_BA, p, plast - p); 144 | } 145 | 146 | static void 147 | ili9341_set_rotation(Glcd *glcd, int rotation) 148 | { 149 | unsigned int param = ILI9341_MX; /* Default zero rotation */ 150 | 151 | if (rotation == 90) 152 | param = ILI9341_MY | ILI9341_MX | ILI9341_MV; 153 | else if (rotation == 180) 154 | param = ILI9341_MY; 155 | else if (rotation == 270) 156 | param = ILI9341_MV | ILI9341_ML; 157 | 158 | param |= ILI9341_BGR; 159 | 160 | ili9341_command(glcd, 0x36 /* ILI9341_MEMORY_ACCESS_CONTROL */, 161 | param); 162 | } 163 | 164 | static void 165 | ili9341_led(Glcd *glcd, int state) 166 | { 167 | if (state == LCD_LED_ON) 168 | digitalWrite(glcd->led_pin, LOW); 169 | else 170 | digitalWrite(glcd->led_pin, HIGH); 171 | } 172 | 173 | 174 | /* ILI9341 SCL read cycle min is 150 nsec or 6.66 MHz. SPI clock on the 175 | | RPI is settable in steps of 2, 4, 8, 16, and 32 MHz, so use 4 MHz. 176 | */ 177 | Glcd * 178 | glcd_ili9341_spi_init(SpiDevice *spidev, uint32_t speed, int demux_BA, 179 | int dc_pin, int reset_pin, int led_pin) 180 | { 181 | Glcd *glcd; 182 | int i, j, t0, t1; 183 | 184 | glcd = calloc(sizeof(Glcd), 1); 185 | 186 | if (!spidev) 187 | { 188 | printf("ili9341_spi_init() NULL spidev\n"); 189 | return NULL; 190 | } 191 | glcd->spi_device = spidev; 192 | glcd->spi_demux_BA = demux_BA; 193 | spi_device_speed_set(spidev, speed, demux_BA); 194 | 195 | glcd->screen_width = glcd->display.width = ILI9341_WIDTH; 196 | glcd->screen_height = glcd->display.height = ILI9341_HEIGHT; 197 | 198 | glcd->led_control = ili9341_led; 199 | glcd->set_rotation = ili9341_set_rotation; 200 | glcd->set_address_window = ili9341_set_address_window; 201 | glcd->set_pixel = ili9341_set_pixel; 202 | glcd->h_line = ili9341_h_line; 203 | glcd->v_line = ili9341_v_line; 204 | glcd->write_data = ili9341_write_data; 205 | glcd->frame_buffer_update = ili9341_frame_buffer_update; 206 | 207 | glcd->frame_buffer = 208 | calloc(ILI9341_WIDTH * ILI9341_HEIGHT, sizeof(uint16_t)); 209 | 210 | pinMode(dc_pin, OUTPUT); 211 | if (reset_pin >= 0) 212 | { 213 | pinMode(reset_pin, OUTPUT); 214 | digitalWrite(reset_pin, HIGH); 215 | } 216 | pinMode(led_pin, OUTPUT); 217 | 218 | glcd->dc_pin = dc_pin; 219 | glcd->reset_pin = reset_pin; 220 | glcd->led_pin = led_pin; 221 | 222 | 223 | ili9341_command(glcd, 0x01 /* ILI9341_SOFTWARE_RESET */); 224 | delay(5); 225 | 226 | ili9341_command(glcd, 0x28 /* ILI9341_DISPLAY_OFF */); 227 | 228 | ili9341_command(glcd, 0xCF /* ILI9341_POWER_CONTROL_B */, 229 | 0x00, 0x83, 0x30); 230 | 231 | ili9341_command(glcd, 0xED /* ILI9341_POWER_ON_SEQUENCE */, 232 | 0x64, 0x03, 0x12, 0x81); 233 | 234 | ili9341_command(glcd, 0xE8 /* ILI9341_DRIVER_TIMING_A */, 235 | 0x85, 0x01, 0x79); 236 | 237 | ili9341_command(glcd, 0xCB /* ILI9341_POWER_CONTROL_A */, 238 | 0x39, 0x2C, 0x00, 0x34, 0x02); 239 | 240 | ili9341_command(glcd, 0xF7 /* ILI9341_PUMP_RATIO_CONTROL */, 241 | 0x20); 242 | 243 | ili9341_command(glcd, 0xEA /* ILI9341_DRIVER_TIMING_B */, 244 | 0x00, 0x00); 245 | 246 | ili9341_command(glcd, 0xC0 /* ILI9341_POWER_CONTROL_1 */, 247 | 0x26); 248 | 249 | ili9341_command(glcd, 0xC1 /* ILI9341_POWER_CONTROL_2 */, 250 | 0x11); 251 | 252 | ili9341_command(glcd, 0xC5 /* ILI9341_VCOM_CONTROL_1 */, 253 | 0x35, 0x3E); 254 | 255 | ili9341_command(glcd, 0xC7 /* ILI9341_VCOM_CONTROL_2 */, 256 | 0xBE); 257 | 258 | ili9341_command(glcd, 0x3A /* ILI9341_PIXEL_FORMAT */, 259 | 0x55); /* 16bit pixel */ 260 | 261 | ili9341_command(glcd, 0xB1 /* ILI9341_FRAME_RATE_CONTROL */, 262 | 0x00, 0x1B); 263 | 264 | ili9341_command(glcd, 0x26 /* ILI9341_GAMMA_SET */, 265 | 0x01); /* gamma curve 1 */ 266 | 267 | ili9341_command(glcd, 0xE0 /* ILI9341_POSITIVE_GAMMA_CORRECTION */, 268 | // 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 269 | // 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00); 270 | 0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0x87, 271 | 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00); 272 | 273 | ili9341_command(glcd, 0xE1 /* ILI9341_NEGATIVE_GAMMA_CORRECTION */, 274 | // 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 275 | // 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F); 276 | 0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 277 | 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F); 278 | 279 | ili9341_command(glcd, 0xB7 /* ILI9341_ENTRY_MODE_SET */, 280 | 0x07); 281 | 282 | ili9341_command(glcd, 0xB6 /* ILI9341_DISPLAY_FUNCTION_CONTROL */, 283 | 0x0A, 0x82, 0x27, 0x00); 284 | 285 | ili9341_command(glcd, 0x11 /* ILI9341_SLEEP_OUT */); 286 | delay(100); 287 | 288 | ili9341_command(glcd, 0x29 /* ILI9341_DISPLAY_ON */); 289 | delay(20); 290 | 291 | 292 | 293 | /* ********** start temporary SPI speed test */ 294 | 295 | ili9341_set_address_window(glcd, 0, 0, 239, 319); 296 | t0 = millis(); 297 | 298 | /* SPI max IO transfer size defaults to one page size (4096) 299 | */ 300 | for (i = 0; i < 16; ++i) 301 | { 302 | for (j = 0; j < 240 * 320; ++j) 303 | glcd->frame_buffer[j] = glcd_map_color_percent(5, 5, 5); 304 | ili9341_frame_buffer_update(glcd); 305 | } 306 | t1 = millis(); 307 | printf("frame rate = %.2f\n", 1.0 / (float) (t1 - t0) * 16.0 * 1000); 308 | /* ********** end temporary SPI speed test */ 309 | 310 | 311 | return glcd; 312 | } 313 | 314 | #endif /* WIRINGPI */ 315 | -------------------------------------------------------------------------------- /libkrellm/glcd/ili9341.h: -------------------------------------------------------------------------------- 1 | /* libkrellm/glcd 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/glcd is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/glcd is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #ifndef _ILI9341_H 21 | #define _ILI9341_H 22 | 23 | #ifdef HAVE_WIRINGPI 24 | 25 | #define ILI9341_WIDTH 240 26 | #define ILI9341_HEIGHT 320 27 | 28 | 29 | /* ILI9341 commands 30 | */ 31 | 32 | #define ILI9341_SOFTWARE_RESET 0x01 33 | #define ILI9341_DISPLAY_OFF 0x28 34 | #define ILI9341_POWER_CONTROL_B 0xCF 35 | #define ILI9341_POWER_ON_SEQUENCE 0xED 36 | #define ILI9341_DRIVER_TIMING_A 0xE8 37 | #define ILI9341_POWER_CONTROL_A 0xCB 38 | #define ILI9341_PUMP_RATIO_CONTROL 0xF7 39 | #define ILI9341_DRIVER_TIMING_B 0xEA 40 | #define ILI9341_POWER_CONTROL_1 0xC0 41 | #define ILI9341_POWER_CONTROL_2 0xC1 42 | #define ILI9341_VCOM_CONTROL_1 0xC5 43 | #define ILI9341_VCOM_CONTROL_2 0xC7 44 | #define ILI9341_PIXEL_FORMAT 0x3A 45 | #define ILI9341_FRAME_RATE_CONTROL 0xB1 46 | #define ILI9341_GAMMA_SET 0x26 47 | #define ILI9341_ENTRY_MODE_SET 0xB7 48 | #define ILI9341_DISPLAY_FUNCTION_CONTROL 0xB6 49 | #define ILI9341_SLEEP_OUT 0x11 50 | #define ILI9341_DISPLAY_ON 0x29 51 | #define ILI9341_COLUMN_ADDRESS_SET 0x2A 52 | #define ILI9341_PAGE_ADDRESS_SET 0x2B 53 | #define ILI9341_MEMORY_WRITE 0x2C 54 | #define ILI9341_MEMORY_ACCESS_CONTROL 0x36 55 | #define ILI9341_POSITIVE_GAMMA_CORRECTION 0xE0 56 | #define ILI9341_NEGATIVE_GAMMA_CORRECTION 0xE1 57 | 58 | #define ILI9341_MY 0x80 59 | #define ILI9341_MX 0x40 60 | #define ILI9341_MV 0x20 61 | #define ILI9341_ML 0x10 62 | #define ILI9341_BGR 0x08 63 | #define ILI9341_MH 0x04 64 | 65 | #define LCD_LED_ON 1 66 | #define LCD_LED_OFF 0 67 | 68 | typedef struct 69 | { 70 | int speed, 71 | dc_pin, 72 | reset_pin, 73 | led_pin; 74 | } 75 | ili9341_LCD; 76 | 77 | 78 | Glcd *glcd_ili9341_spi_init(SpiDevice *spidev, uint32_t speed, int demux_BA, 79 | int dc_pin, int reset_pin, int led_pin); 80 | 81 | #endif /* HAVE_WIRINGPI */ 82 | #endif /* _ILI9341_H */ 83 | -------------------------------------------------------------------------------- /libkrellm/utils/slist.c: -------------------------------------------------------------------------------- 1 | /* 2 | | slist functions: 3 | | The slist functions are from glib-2.0.0 which are: 4 | | Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald 5 | | and are repackaged here for my libkrellm utils library. 6 | | 7 | | This Library is free software: you can redistribute it and/or modify 8 | | it under the terms of the GNU General Public License as published by 9 | | the Free Software Foundation, either version 3 of the License, or 10 | | (at your option) any later version. 11 | | 12 | | This Library is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | You should have received a copy of the GNU General Public License 18 | | along with the libukrellm. If not, see 19 | | . 20 | | 21 | */ 22 | 23 | #include 24 | 25 | #include "utils.h" 26 | 27 | /* ==== SList functions ==== */ 28 | 29 | void 30 | slist_free(SList *list) 31 | { 32 | SList *last; 33 | 34 | while (list) 35 | { 36 | last = list; 37 | list = list->next; 38 | free(last); 39 | } 40 | } 41 | 42 | void 43 | slist_and_data_free(SList *list) 44 | { 45 | SList *last; 46 | 47 | while (list) 48 | { 49 | if (list->data) 50 | free(list->data); 51 | last = list; 52 | list = list->next; 53 | free(last); 54 | } 55 | } 56 | 57 | 58 | SList * 59 | slist_last(SList *list) 60 | { 61 | if (list) 62 | { 63 | while (list->next) 64 | list = list->next; 65 | } 66 | return list; 67 | } 68 | 69 | SList * 70 | slist_append(SList *list, void *data) 71 | { 72 | SList *new_list; 73 | SList *last; 74 | 75 | if ((new_list = calloc(1, sizeof(SList))) != NULL) 76 | new_list->data = data; 77 | 78 | if (list) 79 | { 80 | last = slist_last(list); 81 | last->next = new_list; 82 | } 83 | else 84 | list = new_list; 85 | return list; 86 | } 87 | 88 | SList * 89 | slist_prepend(SList *list, void *data) 90 | { 91 | SList *new_list; 92 | 93 | if ((new_list = calloc(1, sizeof(SList))) != NULL) 94 | { 95 | new_list->data = data; 96 | new_list->next = list; 97 | } 98 | else 99 | new_list = list; /* If can't alloc, leave list unchanged */ 100 | return new_list; 101 | } 102 | 103 | SList * 104 | slist_insert(SList *list, void *data, int position) 105 | { 106 | SList *prev_list, 107 | *tmp_list, 108 | *new_list; 109 | 110 | if (position < 0) 111 | return slist_append(list, data); 112 | else if (position == 0) 113 | return slist_prepend(list, data); 114 | 115 | new_list = calloc(1, sizeof(SList)); 116 | new_list->data = data; 117 | 118 | if (!list) 119 | return new_list; 120 | 121 | prev_list = NULL; 122 | tmp_list = list; 123 | 124 | while ((position-- > 0) && tmp_list) 125 | { 126 | prev_list = tmp_list; 127 | tmp_list = tmp_list->next; 128 | } 129 | 130 | if (prev_list) 131 | { 132 | new_list->next = prev_list->next; 133 | prev_list->next = new_list; 134 | } 135 | else 136 | { 137 | new_list->next = list; 138 | list = new_list; 139 | } 140 | return list; 141 | } 142 | 143 | SList * 144 | slist_remove(SList *list, void *data) 145 | { 146 | SList *tmp, *prev = NULL; 147 | 148 | tmp = list; 149 | while (tmp) 150 | { 151 | if (tmp->data == data) 152 | { 153 | if (prev) 154 | prev->next = tmp->next; 155 | else 156 | list = tmp->next; 157 | 158 | free(tmp); 159 | break; 160 | } 161 | prev = tmp; 162 | tmp = prev->next; 163 | } 164 | return list; 165 | } 166 | 167 | SList * 168 | slist_nth(SList *list, int n) 169 | { 170 | while (n-- > 0 && list) 171 | list = list->next; 172 | return list; 173 | } 174 | 175 | void * 176 | slist_nth_data(SList *list, int n) 177 | { 178 | while (n-- > 0 && list) 179 | list = list->next; 180 | return list ? list->data : NULL; 181 | } 182 | 183 | SList * 184 | slist_find(SList *list, void *data) 185 | { 186 | while (list) 187 | { 188 | if (list->data == data) 189 | break; 190 | list = list->next; 191 | } 192 | return list; 193 | } 194 | 195 | int 196 | slist_length(SList *list) 197 | { 198 | int length; 199 | 200 | length = 0; 201 | while (list) 202 | { 203 | length++; 204 | list = list->next; 205 | } 206 | return length; 207 | } 208 | 209 | int 210 | slist_index(SList *list, void *data) 211 | { 212 | int i; 213 | 214 | i = 0; 215 | while (list) 216 | { 217 | if (list->data == data) 218 | return i; 219 | i++; 220 | list = list->next; 221 | } 222 | return -1; 223 | } 224 | 225 | SList * 226 | slist_remove_link(SList *list, SList *link) 227 | { 228 | SList *tmp, 229 | *prev = NULL; 230 | 231 | tmp = list; 232 | while (tmp) 233 | { 234 | if (tmp == link) 235 | { 236 | if (prev) 237 | prev->next = tmp->next; 238 | if (list == tmp) 239 | list = list->next; 240 | tmp->next = NULL; 241 | break; 242 | } 243 | prev = tmp; 244 | tmp = tmp->next; 245 | } 246 | return list; 247 | } 248 | 249 | SList * 250 | slist_insert_sorted(SList *list, void *data, 251 | int func(void *data1, void *data2)) 252 | { 253 | SList *tmp_list = list, 254 | *prev_list = NULL, 255 | *new_list; 256 | int cmp; 257 | 258 | if (!list) 259 | { 260 | new_list = calloc(1, sizeof(SList)); 261 | new_list->data = data; 262 | return new_list; 263 | } 264 | 265 | cmp = (*func)(data, tmp_list->data); 266 | 267 | while ((tmp_list->next) && (cmp > 0)) 268 | { 269 | prev_list = tmp_list; 270 | tmp_list = tmp_list->next; 271 | cmp = (*func)(data, tmp_list->data); 272 | } 273 | 274 | new_list = calloc(1, sizeof(SList)); 275 | new_list->data = data; 276 | 277 | if ((!tmp_list->next) && (cmp > 0)) 278 | { 279 | tmp_list->next = new_list; 280 | return list; 281 | } 282 | 283 | if (prev_list) 284 | { 285 | prev_list->next = new_list; 286 | new_list->next = tmp_list; 287 | return list; 288 | } 289 | else 290 | { 291 | new_list->next = list; 292 | return new_list; 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /libkrellm/utils/spi-i2c.c: -------------------------------------------------------------------------------- 1 | /* libkrellm/utils 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/utils is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/utils is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #ifdef HAVE_WIRINGPI 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "wiringPi.h" 31 | 32 | #include "utils.h" 33 | 34 | 35 | static uint8_t spi_mode = 0, 36 | spi_bpw = 8; 37 | static uint16_t spi_delay = 0; 38 | 39 | 40 | 41 | SpiDevice * 42 | spi_device_open(char *devname, uint32_t speed, int B_pin, int A_pin) 43 | { 44 | SpiDevice *spidev; 45 | int i; 46 | 47 | spidev = (SpiDevice *) calloc(1, sizeof(SpiDevice)); 48 | 49 | if ((spidev->fd = open(devname, O_RDWR)) < 0) 50 | { 51 | printf("%s open failed: %s\n", devname, strerror(errno)); 52 | free(spidev); 53 | return NULL; 54 | } 55 | for (i = 0; i < 4; ++i) 56 | spidev->speed[i] = speed; 57 | spidev->demux_B_pin = B_pin; 58 | spidev->demux_A_pin = A_pin; 59 | spidev->demux_enabled = FALSE; 60 | 61 | if (A_pin >= 0) 62 | { 63 | spidev->demux_enabled = TRUE; 64 | pinMode(A_pin, OUTPUT); 65 | digitalWrite(A_pin, LOW); 66 | } 67 | if (B_pin >= 0) 68 | { 69 | spidev->demux_enabled = TRUE; 70 | pinMode(B_pin, OUTPUT); 71 | digitalWrite(B_pin, LOW); 72 | } 73 | spidev->current_demux_BA = 0; 74 | 75 | if (ioctl(spidev->fd, SPI_IOC_WR_MODE, &spi_mode) < 0) 76 | printf("%s set mode failed: %s\n", devname, strerror(errno)); 77 | 78 | if (ioctl(spidev->fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bpw) < 0) 79 | printf("%s set bpw failed: %s\n", devname, strerror(errno)); 80 | 81 | if (ioctl(spidev->fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) 82 | printf("%s set speed failed: %s\n", devname, strerror(errno)); 83 | 84 | return spidev; 85 | } 86 | 87 | void 88 | spi_device_speed_set(SpiDevice *spidev, uint32_t speed, int demux_BA) 89 | { 90 | if (spidev && spidev->demux_enabled && demux_BA >= 0 && demux_BA < 4) 91 | { 92 | spidev->speed[demux_BA] = speed; 93 | } 94 | } 95 | 96 | static boolean 97 | spi_demux_select(SpiDevice *spidev, int demux_BA) 98 | { 99 | if (!spidev || demux_BA < 0 || demux_BA >= 4) 100 | return FALSE; 101 | if ( spidev->demux_enabled 102 | && spidev->current_demux_BA != demux_BA 103 | ) 104 | { 105 | if (spidev->demux_B_pin >= 0) 106 | digitalWrite(spidev->demux_B_pin, (demux_BA & 0x2) ? HIGH : LOW); 107 | if (spidev->demux_A_pin >= 0) 108 | digitalWrite(spidev->demux_A_pin, (demux_BA & 0x1) ? HIGH : LOW); 109 | spidev->current_demux_BA = demux_BA; 110 | } 111 | return TRUE; 112 | } 113 | 114 | boolean 115 | spi_read(SpiDevice *spidev, int demux_BA, uint8_t *data, int length) 116 | { 117 | struct spi_ioc_transfer spi; 118 | 119 | if (!spi_demux_select(spidev, demux_BA)) 120 | return FALSE; 121 | 122 | memset (&spi, 0, sizeof(spi)); /* clear padding bytes?? */ 123 | spi.tx_buf = 0; 124 | spi.rx_buf = (unsigned long) data; 125 | spi.len = length; 126 | spi.delay_usecs = spi_delay; 127 | spi.speed_hz = spidev->speed[demux_BA]; 128 | spi.bits_per_word = spi_bpw; 129 | 130 | if (ioctl(spidev->fd, SPI_IOC_MESSAGE(1), &spi) < 0) 131 | { 132 | printf("spi_read() ioctl() failed: %s\n", strerror(errno)); 133 | return FALSE; 134 | } 135 | return TRUE; 136 | } 137 | 138 | boolean 139 | spi_write(SpiDevice *spidev, int demux_BA, uint8_t *data, int length) 140 | { 141 | struct spi_ioc_transfer spi; 142 | 143 | if (!spi_demux_select(spidev, demux_BA)) 144 | return FALSE; 145 | 146 | memset (&spi, 0, sizeof(spi)); /* clear padding bytes?? */ 147 | spi.tx_buf = (unsigned long) data; 148 | spi.rx_buf = 0; 149 | spi.len = length; 150 | spi.delay_usecs = spi_delay; 151 | spi.speed_hz = spidev->speed[demux_BA]; 152 | spi.bits_per_word = spi_bpw; 153 | 154 | if (ioctl(spidev->fd, SPI_IOC_MESSAGE(1), &spi) < 0) 155 | { 156 | printf("spi_write() ioctl() failed: %s\n", strerror(errno)); 157 | return FALSE; 158 | } 159 | return TRUE; 160 | } 161 | 162 | boolean 163 | spi_rw(SpiDevice *spidev, int demux_BA, uint8_t *data, int length) 164 | { 165 | struct spi_ioc_transfer spi; 166 | 167 | if (!spi_demux_select(spidev, demux_BA)) 168 | return FALSE; 169 | 170 | memset (&spi, 0, sizeof(spi)); /* clear padding bytes?? */ 171 | spi.tx_buf = (unsigned long) data; 172 | spi.rx_buf = (unsigned long) data; 173 | spi.len = length; 174 | spi.delay_usecs = spi_delay; 175 | spi.speed_hz = spidev->speed[demux_BA]; 176 | spi.bits_per_word = spi_bpw; 177 | 178 | if (ioctl(spidev->fd, SPI_IOC_MESSAGE(1), &spi) < 0) 179 | { 180 | printf("spi_rw() ioctl() failed: %s\n", strerror(errno)); 181 | return FALSE; 182 | } 183 | return TRUE; 184 | } 185 | 186 | 187 | 188 | int 189 | i2c_open(int i2c_address) 190 | { 191 | int fd; 192 | char *device; 193 | 194 | device = (pi_board_rev() == 1) ? "/dev/i2c-0" : "/dev/i2c-1"; 195 | 196 | if ((fd = open(device, O_RDWR)) < 0) 197 | { 198 | printf("I2C device %s open failed: %s\n", device, strerror(errno)); 199 | exit(EXIT_FAILURE); 200 | } 201 | if (ioctl(fd, I2C_SLAVE, i2c_address) < 0) 202 | { 203 | printf("%s I2C slave address 0x%x failed: %s\n", 204 | device, i2c_address, strerror(errno)); 205 | exit(EXIT_FAILURE); 206 | } 207 | return fd; 208 | } 209 | 210 | 211 | #endif /* HAVE_WIRINGPI */ 212 | -------------------------------------------------------------------------------- /libkrellm/utils/utils.c: -------------------------------------------------------------------------------- 1 | /* libkrellm/utils 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/utils is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/utils is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "utils.h" 26 | 27 | 28 | /* Return elapsed time in usec since last call on this timer. 29 | | Return 0 if first call. 30 | */ 31 | int 32 | micro_elapsed_time(struct timeval *timer) 33 | { 34 | static struct timeval lap; 35 | struct timeval dif; 36 | 37 | gettimeofday(&lap, NULL); 38 | if (timer->tv_sec > 0 || timer->tv_usec > 0) 39 | timersub(&lap, timer, &dif); 40 | else 41 | dif.tv_sec = dif.tv_usec = 0; 42 | *timer = lap; 43 | return (dif.tv_sec * 1000000 + dif.tv_usec); 44 | } 45 | 46 | boolean 47 | dup_string(char **dst, char *src) 48 | { 49 | if (!dst) 50 | return FALSE; 51 | if (!src) 52 | src = ""; 53 | if (*dst) 54 | { 55 | if (!strcmp(*dst, src)) 56 | return FALSE; 57 | free(*dst); 58 | } 59 | *dst = strdup(src); 60 | return TRUE; 61 | } 62 | 63 | boolean 64 | isfifo(char *fifo) 65 | { 66 | struct stat st; 67 | boolean result = FALSE; 68 | 69 | if (stat(fifo, &st) == 0 && S_ISFIFO(st.st_mode)) 70 | result = TRUE; 71 | return result; 72 | } 73 | 74 | boolean 75 | isdir(char *dir) 76 | { 77 | struct stat st; 78 | boolean result = FALSE; 79 | 80 | if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode)) 81 | result = TRUE; 82 | return result; 83 | } 84 | 85 | boolean 86 | make_directory(char *dir) 87 | { 88 | boolean dir_exists; 89 | 90 | if ((dir_exists = isdir(dir)) == FALSE) 91 | { 92 | if (mkdir(dir, 0755) < 0) 93 | printf("Make directory %s failed: %s\n", dir, strerror(errno)); 94 | else 95 | dir_exists = TRUE; 96 | } 97 | return dir_exists; 98 | } 99 | 100 | -------------------------------------------------------------------------------- /libkrellm/utils/utils.h: -------------------------------------------------------------------------------- 1 | /* libkrellm/utils 2 | | 3 | | Copyright (C) 2013-2015 Bill Wilson billw@gkrellm.net 4 | | 5 | | libkrellm/utils is free software: you can redistribute it and/or modify 6 | | it under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | libkrellm/utils is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with the libkrellm. If not, see . 17 | | 18 | */ 19 | 20 | #ifndef _UTILS_H_ 21 | #define _UTILS_H_ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #ifndef TRUE 30 | #define TRUE 1 31 | #define FALSE 0 32 | #endif 33 | 34 | #ifndef MAX 35 | #define MAX(a,b) (((a) > (b)) ? (a) : (b)) 36 | #define MIN(a,b) (((a) < (b)) ? (a) : (b)) 37 | #endif 38 | 39 | 40 | typedef struct _SList SList; 41 | typedef int boolean; 42 | 43 | struct _SList 44 | { 45 | void *data; 46 | SList *next; 47 | }; 48 | 49 | 50 | typedef struct 51 | { 52 | int fd; 53 | uint32_t speed[4]; 54 | boolean demux_enabled; 55 | int current_demux_BA; 56 | int demux_B_pin, 57 | demux_A_pin; 58 | } 59 | SpiDevice; 60 | 61 | #ifdef __cplusplus 62 | extern "C" { 63 | #endif 64 | 65 | int micro_elapsed_time(struct timeval *timer); 66 | boolean dup_string(char **dst, char *src); 67 | 68 | boolean isfifo(char *fifo); 69 | boolean isdir(char *dir); 70 | boolean make_directory(char *dir); 71 | 72 | SpiDevice *spi_device_open(char *devname, uint32_t speed, 73 | int B_pin, int A_pin); 74 | void spi_device_speed_set(SpiDevice *spidev, uint32_t speed, 75 | int demux_BA); 76 | boolean spi_read(SpiDevice *spidev, int demux_BA, 77 | uint8_t *data, int length); 78 | boolean spi_write(SpiDevice *spidev, int demux_BA, 79 | uint8_t *data, int length); 80 | boolean spi_rw(SpiDevice *spidev, int demux_BA, 81 | uint8_t *data, int length); 82 | 83 | 84 | int i2c_open(int i2c_address); 85 | 86 | 87 | void slist_free(SList *list); 88 | void slist_and_data_free(SList *list); 89 | SList *slist_append(SList *list, void *data); 90 | SList *slist_prepend(SList *list, void *data); 91 | SList *slist_remove(SList *list, void *data); 92 | SList *slist_remove_link(SList *list, SList *link); 93 | SList *slist_nth(SList *list, int n); 94 | void *slist_nth_data(SList *list, int n); 95 | SList *slist_find(SList *list, void *data); 96 | int slist_index(SList *list, void *data); 97 | int slist_length(SList *list); 98 | SList *slist_insert(SList *list, void *data, int position); 99 | SList *slist_insert_sorted(SList *list, void *data, 100 | int func(void *data1, void *data2)); 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | 106 | #endif /* _UTILS_H_ */ 107 | -------------------------------------------------------------------------------- /pikrellcam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/pikrellcam -------------------------------------------------------------------------------- /scripts-dist/Readme: -------------------------------------------------------------------------------- 1 | Scripts in the scripts-dist direcotory are distribution scripts which, 2 | except for the scripts beginning with '_', are copied to the scripts dir when 3 | install-pikrellcam.sh is run. 4 | 5 | Scripts in the "scripts-dist" directory should not be edited since they 6 | are subject to being replaced when PiKrellCam is upgraded. 7 | 8 | Scripts in the "scripts" directory are user editable and are configured to 9 | be run from pikrellcam.conf or at-commands.conf. 10 | -------------------------------------------------------------------------------- /scripts-dist/_archive-still: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Move stills into an archive directory. 4 | # Calling format from pikrellcam or a at-commands script: 5 | # archive-still [day|still.jpg] yyyy-mm-dd $a $m $P $G 6 | # 7 | # Which pikrellcam expands into: 8 | # archive-still day yyyy-mm-dd archive-dir media-dir FIFO LOG 9 | # or 10 | # archive-still still.jpg yyyy-mm-dd archive-dir media-dir FIFO LOG 11 | 12 | # If $1 is the string "day", move all stills for the day yyyy-mm-dd into 13 | # the archive directory archive-dir/yyyy/mm/dd/stills 14 | # Otherwise, $1 should be a jpg still and it 15 | # is moved into the archive directory. 16 | 17 | 18 | DAY_FLAG=$1 19 | STILL=$1 20 | DATE=$2 21 | ARCHIVE_DIR=$3 22 | MEDIA_DIR=$4 23 | FIFO=$5 24 | LOG_FILE=$6 25 | 26 | if [ "$DATE" == "yesterday" ] 27 | then 28 | DATE=`date --date="yesterday" +'%F'` 29 | elif [ "$DATE" == "today" ] 30 | then 31 | DATE=`date --date="today" +'%F'` 32 | fi 33 | 34 | LEN=${#DATE} 35 | 36 | if [[ "$LEN" -ne "10" ]] 37 | then 38 | echo " archive-still bad date string: $DATE" >> $LOG_FILE 39 | exit 1 40 | fi 41 | 42 | # From DATE arg: yyyy-mm-dd, DATE_PATH is: yyyy/mm/dd 43 | YEAR=`expr substr $DATE 1 4` 44 | MONTH=`expr substr $DATE 6 2` 45 | DAY=`expr substr $DATE 9 2` 46 | 47 | DATE_PATH=$YEAR/$MONTH/$DAY 48 | 49 | ARCHIVE_STILLS_PATH=$ARCHIVE_DIR/$DATE_PATH/stills 50 | ARCHIVE_THUMBS_PATH=$ARCHIVE_STILLS_PATH/.thumbs 51 | 52 | YEAR=`expr substr $DATE 1 4` 53 | MONTH=`expr substr $DATE 6 2` 54 | DAY=`expr substr $DATE 9 2` 55 | 56 | create_dir() 57 | { 58 | DIR=$1 59 | if [ ! -d $DIR ] 60 | then 61 | mkdir $DIR 62 | sudo chown :www-data $DIR 63 | sudo chmod 775 $DIR 64 | fi 65 | } 66 | 67 | create_dir $ARCHIVE_DIR/$YEAR 68 | create_dir $ARCHIVE_DIR/$YEAR/$MONTH 69 | create_dir $ARCHIVE_DIR/$YEAR/$MONTH/$DAY 70 | 71 | create_dir $ARCHIVE_STILLS_PATH 72 | if [ ! -d $ARCHIVE_STILLS_PATH ] 73 | then 74 | echo " archive-still could not create archive directory: $ARCHIVE_STILLS_PATH" >> $LOG_FILE 75 | exit 1 76 | fi 77 | create_dir $ARCHIVE_THUMBS_PATH 78 | 79 | 80 | if [ "$DAY_FLAG" == "day" ] 81 | then 82 | echo " archive-still day $DATE to $ARCHIVE_DIR/$DATE_PATH" >> $LOG_FILE 83 | mv $MEDIA_DIR/stills/*${DATE}*.jpg $ARCHIVE_STILLS_PATH 84 | mv $MEDIA_DIR/stills/.thumbs/*${DATE}*.th.jpg $ARCHIVE_THUMBS_PATH 85 | else 86 | echo " archive-still $STILL to $ARCHIVE_DIR/$DATE_PATH" >> $LOG_FILE 87 | mv $MEDIA_DIR/stills/$STILL $ARCHIVE_STILLS_PATH 88 | THUMB_JPEG=${STILL%.jpg}.th.jpg 89 | mv $MEDIA_DIR/stills/.thumbs/$THUMB_JPEG $ARCHIVE_THUMBS_PATH 90 | fi 91 | 92 | # Cleanup in case no files were moved so archive page won't show dangling links 93 | # 94 | find $ARCHIVE_THUMBS_PATH -maxdepth 0 -empty -exec rmdir $ARCHIVE_THUMBS_PATH \; 95 | find $ARCHIVE_STILLS_PATH -maxdepth 0 -empty -exec rmdir $ARCHIVE_STILLS_PATH \; 96 | 97 | ARCHIVE_DATE_PATH=$ARCHIVE_DIR/$DATE_PATH 98 | find $ARCHIVE_DATE_PATH -maxdepth 0 -empty -exec rmdir $ARCHIVE_DATE_PATH \; 99 | -------------------------------------------------------------------------------- /scripts-dist/_archive-video: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Move videos and corresponding thumbs into an archive directory. 4 | # Calling format from pikrellcam or a at-commands script: 5 | # archive-video [day|video.mp4] yyyy-mm-dd $a $m $P $G 6 | # 7 | # Which pikrellcam expands into: 8 | # archive-video day yyyy-mm-dd archive-dir media-dir FIFO LOG 9 | # or 10 | # archive-video video.mp4 yyyy-mm-dd archive-dir media-dir FIFO LOG 11 | 12 | # If $1 is the string "day", move all videos for the day yyyy-mm-dd into 13 | # the archive directory archive-dir/yyyy/mm/dd/videos and all thumbs for 14 | # the day into archive-dir/yyyy/mm/dd/thumbs. 15 | # Otherwise, $1 should be a mp4 video and it and its corresponding thumb 16 | # are moved into the archive directory. 17 | 18 | 19 | DAY_FLAG=$1 20 | VIDEO=$1 21 | DATE=$2 22 | ARCHIVE_DIR=$3 23 | MEDIA_DIR=$4 24 | FIFO=$5 25 | LOG_FILE=$6 26 | 27 | if [ "$DATE" == "yesterday" ] 28 | then 29 | DATE=`date --date="yesterday" +'%F'` 30 | elif [ "$DATE" == "today" ] 31 | then 32 | DATE=`date --date="today" +'%F'` 33 | fi 34 | 35 | LEN=${#DATE} 36 | 37 | if [[ "$LEN" -ne "10" ]] 38 | then 39 | echo " archive-video bad date string: $DATE" >> $LOG_FILE 40 | exit 1 41 | fi 42 | 43 | # From DATE arg: yyyy-mm-dd, DATE_PATH is: yyyy/mm/dd 44 | YEAR=`expr substr $DATE 1 4` 45 | MONTH=`expr substr $DATE 6 2` 46 | DAY=`expr substr $DATE 9 2` 47 | 48 | DATE_PATH=$YEAR/$MONTH/$DAY 49 | 50 | ARCHIVE_VIDEOS_PATH=$ARCHIVE_DIR/$DATE_PATH/videos 51 | ARCHIVE_THUMBS_PATH=$ARCHIVE_DIR/$DATE_PATH/thumbs 52 | 53 | 54 | create_dir() 55 | { 56 | DIR=$1 57 | if [ ! -d $DIR ] 58 | then 59 | mkdir $DIR 60 | sudo chown :www-data $DIR 61 | sudo chmod 775 $DIR 62 | fi 63 | } 64 | 65 | create_dir $ARCHIVE_DIR/$YEAR 66 | create_dir $ARCHIVE_DIR/$YEAR/$MONTH 67 | create_dir $ARCHIVE_DIR/$YEAR/$MONTH/$DAY 68 | 69 | create_dir $ARCHIVE_VIDEOS_PATH 70 | if [ ! -d $ARCHIVE_VIDEOS_PATH ] 71 | then 72 | echo " archive-video could not create archive directory: $ARCHIVE_VIDEOS_PATH" >> $LOG_FILE 73 | exit 1 74 | fi 75 | 76 | create_dir $ARCHIVE_THUMBS_PATH 77 | if [ ! -d $ARCHIVE_THUMBS_PATH ] 78 | then 79 | echo " archive-video could not create archive directory: $ARCHIVE_THUMBS_PATH" >> $LOG_FILE 80 | exit 1 81 | fi 82 | 83 | 84 | if [ "$DAY_FLAG" == "day" ] || [ "$DAY_FLAG" == "day_loop" ] 85 | then 86 | echo " archive day $DATE to $ARCHIVE_DIR/$DATE_PATH" >> $LOG_FILE 87 | mv $MEDIA_DIR/videos/*${DATE}*.mp4 $ARCHIVE_VIDEOS_PATH 88 | mv $MEDIA_DIR/thumbs/*${DATE}*.th.jpg $ARCHIVE_THUMBS_PATH 89 | if [ "`echo $MEDIA_DIR/videos/*${DATE}*.csv`" != "$MEDIA_DIR/videos/*${DATE}*.csv" ] 90 | then 91 | echo " .csv $DATE to $ARCHIVE_DIR/$DATE_PATH" >> $LOG_FILE 92 | mv $MEDIA_DIR/videos/*${DATE}*.csv $ARCHIVE_VIDEOS_PATH 93 | fi 94 | else 95 | echo " archive $VIDEO to $ARCHIVE_DIR/$DATE_PATH" >> $LOG_FILE 96 | mv $MEDIA_DIR/videos/$VIDEO $ARCHIVE_VIDEOS_PATH 97 | THUMB_JPEG=${VIDEO%.mp4}.th.jpg 98 | mv $MEDIA_DIR/thumbs/$THUMB_JPEG $ARCHIVE_THUMBS_PATH 99 | CSV=${VIDEO%.mp4}.csv 100 | if [ -f $MEDIA_DIR/videos/$CSV ] 101 | then 102 | echo " $CSV to $ARCHIVE_DIR/$DATE_PATH" >> $LOG_FILE 103 | mv $MEDIA_DIR/videos/$CSV $ARCHIVE_VIDEOS_PATH 104 | fi 105 | fi 106 | 107 | # Cleanup in case no files were moved so archive page won't show dangling links 108 | # 109 | find $ARCHIVE_VIDEOS_PATH -maxdepth 0 -empty -exec rmdir $ARCHIVE_VIDEOS_PATH \; 110 | find $ARCHIVE_THUMBS_PATH -maxdepth 0 -empty -exec rmdir $ARCHIVE_THUMBS_PATH \; 111 | 112 | ARCHIVE_DATE_PATH=$ARCHIVE_DIR/$DATE_PATH 113 | find $ARCHIVE_DATE_PATH -maxdepth 0 -empty -exec rmdir $ARCHIVE_DATE_PATH \; 114 | -------------------------------------------------------------------------------- /scripts-dist/_fix_thumbs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generate missing thumbs for videos. Provides thumbs for manual or timelapse 4 | # videos recorded before pikrellcam version 2.1.9 5 | # Recovers thumbs (and labels "Recovered") for motion videos with no thumbs. 6 | # Deletes thumbs with no matching video. 7 | 8 | MEDIA_DIRECTORY=$1 9 | ARCHIVE_DIR=$2 10 | LOG=$3 11 | TEST=$4 12 | 13 | #MEDIA_DIRECTORY=/home/pi/pikrellcam/www/media 14 | #ARCHIVE_DIR=/home/pi/pikrellcam/www/media/archive 15 | 16 | BUSTER=10 17 | V=`cat /etc/debian_version` 18 | DEB_VERSION="${V%.*}" 19 | 20 | if ((DEB_VERSION >= BUSTER)) 21 | then 22 | AV_PGM=ffmpeg 23 | else 24 | AV_PGM=avconv 25 | fi 26 | 27 | function check_thumb 28 | { 29 | TMP_DIR=/var/run/pikrellcam 30 | BASE=`basename $1` 31 | PREFIX=${BASE:0:3} 32 | DIR=`dirname $1` 33 | if [[ $1 == *"/stills/"* ]] 34 | then 35 | STILLS=yes 36 | else 37 | STILLS=no 38 | fi 39 | 40 | if [[ $BASE == *"mp4" ]] 41 | then 42 | # Delete longest match of substring /videos from back of $DIR. 43 | MEDIA_DIR=${DIR%%/videos} 44 | THUMBS_DIR=$MEDIA_DIR/thumbs 45 | THUMB_JPEG=${BASE%.mp4}.th.jpg 46 | 47 | # echo "==> $1 $DIR $MEDIA_DIR $THUMBS_DIR" >> $LOG 48 | 49 | if [ ! -f $THUMBS_DIR/$THUMB_JPEG ] 50 | then 51 | THUMB_FAIL=false 52 | # echo " $AV_PGM -i $1 -ss 0 -vframes 1 $TMP_DIR/$THUMB_JPEG" >>$LOG 53 | if [ "$TEST" != "test" ] 54 | then 55 | if ! $AV_PGM -i $1 -ss 0 -vframes 1 $TMP_DIR/$THUMB_JPEG 56 | then 57 | echo " Failed to extract thumb from video: $1" >> $LOG 58 | THUMB_FAIL=true 59 | fi 60 | fi 61 | if [ "$THUMB_FAIL" == "false" ] 62 | then 63 | if [ "$PREFIX" == "mot" ] 64 | then 65 | echo " Recovering motion thumb: $THUMBS_DIR/$THUMB_JPEG" >> $LOG 66 | if [ "$TEST" != "test" ] 67 | then 68 | convert -crop 500x500+100+100 -resize 150x150 \ 69 | $TMP_DIR/$THUMB_JPEG $TMP_DIR/${THUMB_JPEG}.tmp 70 | convert -annotate +8+26 "Recovered" -pointsize 18 \ 71 | $TMP_DIR/${THUMB_JPEG}.tmp $THUMBS_DIR/$THUMB_JPEG 72 | rm $TMP_DIR/${THUMB_JPEG}.tmp 73 | fi 74 | else 75 | echo " Generating thumb type $PREFIX: $THUMBS_DIR/$THUMB_JPEG" >> $LOG 76 | if [ "$TEST" != "test" ] 77 | then 78 | convert -resize 150 $TMP_DIR/$THUMB_JPEG $THUMBS_DIR/$THUMB_JPEG 79 | fi 80 | fi 81 | 82 | if [ "$TEST" != "test" ] 83 | then 84 | rm $TMP_DIR/$THUMB_JPEG 85 | fi 86 | fi 87 | fi 88 | elif [[ $BASE == *"th.jpg" ]] && [ "$STILLS" == "no" ] 89 | then 90 | # Delete longest match of substring /thumbs from back of $DIR. 91 | MEDIA_DIR=${DIR%%/thumbs} 92 | VIDEOS_DIR=$MEDIA_DIR/videos 93 | VIDEO=${BASE%.th.jpg}.mp4 94 | if [ ! -f $VIDEOS_DIR/$VIDEO ] 95 | then 96 | echo " Deleted thumb with no video: $1" >> $LOG 97 | if [ "$TEST" != "test" ] 98 | then 99 | rm $1 100 | fi 101 | fi 102 | elif [[ $BASE == *"th.jpg" ]] && [ "$STILLS" == "yes" ] 103 | then 104 | # Delete longest match of substring /.thumbs from back of $DIR. 105 | STILLS_DIR=${DIR%%/.thumbs} 106 | IMAGE=${BASE%.th.jpg}.jpg 107 | if [ ! -f $STILLS_DIR/$IMAGE ] 108 | then 109 | echo " Deleted stills thumb with no image: $1" >> $LOG 110 | if [ "$TEST" != "test" ] 111 | then 112 | rm $1 113 | fi 114 | fi 115 | elif [[ $BASE == *".h264" ]] 116 | then 117 | if [[ $BASE == *".mp4.h264" ]] 118 | then 119 | VIDEO=${BASE%.mp4.h264}.mp4 120 | else 121 | VIDEO=${BASE%.h264}.mp4 122 | fi 123 | echo " Stray h264: $1 Will retry MP4Box and delete the h264." >> $LOG 124 | if [ "$TEST" != "test" ] 125 | then 126 | MEDIA_DIR=${DIR%%/videos} 127 | if MP4Box -add $1 $DIR/$VIDEO 128 | then 129 | echo " MP4Box run success. Rerun fix_thumbs to generate thumbnails." >> $LOG 130 | else 131 | echo " MP4Box failed, video is not recovered." >> $LOG 132 | fi 133 | rm $1 134 | fi 135 | fi 136 | } 137 | 138 | echo "fix_thumbs $TEST: checking $MEDIA_DIRECTORY ...(working)..." >> $LOG 139 | cd $MEDIA_DIRECTORY 140 | find . | while read file; do check_thumb "./$file"; done 141 | 142 | MEDIA_DIR_LEN=${#MEDIA_DIRECTORY} 143 | MATCH_LEN=`expr match "$ARCHIVE_DIR" "$MEDIA_DIRECTORY"` 144 | 145 | if [ $MATCH_LEN -ne $MEDIA_DIR_LEN ] 146 | then 147 | echo "fix_thumbs $TEST: checking $ARCHIVE_DIR" >> $LOG 148 | cd $ARCHIVE_DIR 149 | find . | while read file; do check_thumb "./$file"; done 150 | else 151 | echo "fix_thumbs $TEST: Archive directory is under media directory so archive is already checked." >> $LOG 152 | fi 153 | 154 | echo "fix_thumbs $TEST: DONE!" >> $LOG 155 | -------------------------------------------------------------------------------- /scripts-dist/_init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This init script is hardwired to be run when PiKrellCam starts. 4 | # It verifies that the www config matches the PiKrellCam config. 5 | # This is not a user script for the "scripts" directory. 6 | # 7 | # Argument substitution done by PiKrellCam before running this script: 8 | # $I - install directory full path 9 | # $a - the archive directory full path 10 | # $m - the media directory full path 11 | # $M - the mjpeg_file PiKrellCam writes and the web page reads. 12 | # $P - the fifo command file full path 13 | # $G - log file configured in ~/.pikrellcam/pikrellcam.conf. 14 | # 15 | 16 | INSTALL_DIR=$1 17 | ARCHIVE_DIR=$2 18 | MEDIA_DIR=$3 19 | MJPEG_FILE=$4 20 | FIFO_FILE=$5 21 | LOG_FILE=$6 22 | SERVOS_ENABLE=$7 23 | LOOP_DIR=$8 24 | 25 | STATE_FILE=/run/pikrellcam/state 26 | MJPEG_FILE=/run/pikrellcam/mjpeg.jpg 27 | 28 | if [ "$LOG_FILE" == "" ] 29 | then 30 | LOG_FILE = /dev/null 31 | fi 32 | 33 | cd $INSTALL_DIR 34 | 35 | # note there is a tab before 'define' in the CMD assignments. 36 | # 37 | WWW_CONFIG=$INSTALL_DIR/www/config.php 38 | PIKRELLCAM=$INSTALL_DIR/pikrellcam 39 | ARCHIVE_LINK=www/archive 40 | LOOP_LINK=www/loop 41 | MEDIA_LINK=www/media 42 | STATE_LINK=www/state 43 | MJPEG_LINK=www/mjpeg.jpg 44 | VERSION=`pikrellcam --version` 45 | 46 | if [ ! -h $STATE_LINK ] 47 | then 48 | echo " making $STATE_LINK link to $STATE_FILE" >> $LOG_FILE 49 | ln -s $STATE_FILE $STATE_LINK 50 | fi 51 | 52 | if [ ! -h $MJPEG_LINK ] 53 | then 54 | echo " making $MJPEG_LINK link to $MJPEG_FILE" >> $LOG_FILE 55 | ln -s $MJPEG_FILE $MJPEG_LINK 56 | fi 57 | 58 | if [ ! -h $MEDIA_LINK ] 59 | then 60 | echo " making $MEDIA_LINK link to $MEDIA_DIR" >> $LOG_FILE 61 | ln -s $MEDIA_DIR $MEDIA_LINK 62 | else 63 | CURRENT_MEDIA=`realpath $MEDIA_LINK` 64 | if [ "$CURRENT_MEDIA" != "$MEDIA_DIR" ] 65 | then 66 | echo " replacing $MEDIA_LINK link from $CURRENT_MEDIA to $MEDIA_DIR" >> $LOG_FILE 67 | rm $MEDIA_LINK 68 | ln -s $MEDIA_DIR $MEDIA_LINK 69 | else 70 | echo " $MEDIA_LINK link is already set to $MEDIA_DIR" >> $LOG_FILE 71 | fi 72 | fi 73 | 74 | if [ ! -h $ARCHIVE_LINK ] 75 | then 76 | echo " making $ARCHIVE_LINK link to $ARCHIVE_DIR" >> $LOG_FILE 77 | ln -s $ARCHIVE_DIR $ARCHIVE_LINK 78 | else 79 | CURRENT_ARCHIVE=`realpath $ARCHIVE_LINK` 80 | if [ "$CURRENT_ARCHIVE" != "$ARCHIVE_DIR" ] 81 | then 82 | echo " replacing $ARCHIVE_LINK link from $CURRENT_ARCHIVE to $ARCHIVE_DIR" >> $LOG_FILE 83 | rm $ARCHIVE_LINK 84 | ln -s $ARCHIVE_DIR $ARCHIVE_LINK 85 | else 86 | echo " $ARCHIVE_LINK link is already set to $ARCHIVE_DIR" >> $LOG_FILE 87 | fi 88 | fi 89 | 90 | if [ ! -h $LOOP_LINK ] 91 | then 92 | echo " making $LOOP_LINK link to $LOOP_DIR" >> $LOG_FILE 93 | ln -s $LOOP_DIR $LOOP_LINK 94 | else 95 | CURRENT_LOOP=`realpath $LOOP_LINK` 96 | if [ "$CURRENT_LOOP" != "$LOOP_DIR" ] 97 | then 98 | echo " replacing $LOOP_LINK link from $CURRENT_LOOP to $LOOP_DIR" >> $LOG_FILE 99 | rm $LOOP_LINK 100 | ln -s $LOOP_DIR $LOOP_LINK 101 | else 102 | echo " $LOOP_LINK link is already set to $LOOP_DIR" >> $LOG_FILE 103 | fi 104 | fi 105 | 106 | if ! grep -q $LOG_FILE $WWW_CONFIG 107 | then 108 | CMD="/LOG_FILE/c\ define\(\"LOG_FILE\", \"$LOG_FILE\"\);" 109 | sed -i "$CMD" $WWW_CONFIG 110 | echo " $WWW_CONFIG: LOG_FILE updated to: $LOG_FILE" >> $LOG_FILE 111 | else 112 | echo " $WWW_CONFIG: LOG_FILE not changed." >> $LOG_FILE 113 | fi 114 | 115 | if ! grep -q $MJPEG_FILE $WWW_CONFIG 116 | then 117 | CMD="/MJPEG_FILE/c\ define\(\"MJPEG_FILE\", \"$MJPEG_FILE\"\);" 118 | sed -i "$CMD" $WWW_CONFIG 119 | echo " $WWW_CONFIG: MJPEG_FILE updated to: $MJPEG_FILE" >> $LOG_FILE 120 | else 121 | echo " $WWW_CONFIG: MJPEG_FILE not changed." >> $LOG_FILE 122 | fi 123 | 124 | if ! grep -q $FIFO_FILE $WWW_CONFIG 125 | then 126 | CMD="/FIFO_FILE/c\ define\(\"FIFO_FILE\", \"$FIFO_FILE\"\);" 127 | sed -i "$CMD" $WWW_CONFIG 128 | echo " $WWW_CONFIG: FIFO_FILE updated to: $FIFO_FILE" >> $LOG_FILE 129 | else 130 | echo " $WWW_CONFIG: FIFO_FILE not changed." >> $LOG_FILE 131 | fi 132 | 133 | if ! grep -q $PIKRELLCAM $WWW_CONFIG 134 | then 135 | CMD="/PIKRELLCAM/c\ define\(\"PIKRELLCAM\", \"$PIKRELLCAM\"\);" 136 | sed -i "$CMD" $WWW_CONFIG 137 | echo " $WWW_CONFIG: PIKRELLCAM updated to: $PIKRELLCAM" >> $LOG_FILE 138 | else 139 | echo " $WWW_CONFIG: PIKRELLCAM not changed." >> $LOG_FILE 140 | fi 141 | 142 | if ! grep -q $SERVOS_ENABLE $WWW_CONFIG 143 | then 144 | CMD="/SERVOS_ENABLE/c\ define\(\"SERVOS_ENABLE\", \"$SERVOS_ENABLE\"\);" 145 | sed -i "$CMD" $WWW_CONFIG 146 | echo " $WWW_CONFIG: SERVOS_ENABLE updated to: $SERVOS_ENABLE" >> $LOG_FILE 147 | else 148 | echo " $WWW_CONFIG: SERVOS_ENABLE not changed." >> $LOG_FILE 149 | fi 150 | 151 | if ! fgrep -q $VERSION $WWW_CONFIG 152 | then 153 | CMD="/VERSION/c\ define\(\"VERSION\", \"$VERSION\"\);" 154 | sed -i "$CMD" $WWW_CONFIG 155 | echo " $WWW_CONFIG: VERSION updated to: $VERSION" >> $LOG_FILE 156 | else 157 | echo " $WWW_CONFIG: VERSION not changed." >> $LOG_FILE 158 | fi 159 | 160 | cd $INSTALL_DIR/scripts-dist 161 | for script in * 162 | do 163 | if [ ! -f ../scripts/$script ] && [ "${script:0:1}" != "_" ] \ 164 | && [ "${script:0:4}" != "pkc_" ] && [ "${script:0:8}" != "example_" ] 165 | then 166 | cp $script ../scripts 167 | fi 168 | done 169 | 170 | -------------------------------------------------------------------------------- /scripts-dist/_log-lines: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Limit the log file to a number of lines. This is run at pikrellcam startup 4 | # and at the start of each day. Run from log_lines() in event.c 5 | 6 | N_LINES=$1 7 | LOG_FILE=$2 8 | 9 | TMP_LOG_FILE=${LOG_FILE}.part 10 | 11 | if tail -$N_LINES $LOG_FILE 2>/dev/null > $TMP_LOG_FILE 12 | then 13 | mv $TMP_LOG_FILE $LOG_FILE 14 | else 15 | rm -f $TMP_LOG_FILE 16 | fi 17 | -------------------------------------------------------------------------------- /scripts-dist/_stills_thumbs_rescan: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Scan stills directory for thumbs to create. 4 | # Calling format from pikrellcam or a at-commands script: 5 | # archive-still stills_dir $I $P $G 6 | # 7 | # Which pikrellcam expands into: 8 | # archive-still stills_dir install-dir FIFO LOG 9 | 10 | # If $1 is the string "day", move all stills for the day yyyy-mm-dd into 11 | # the archive directory archive-dir/yyyy/mm/dd/stills 12 | # Otherwise, $1 should be a jpg still and it 13 | # is moved into the archive directory. 14 | 15 | 16 | STILLS_DIR=$1 17 | INSTALL_DIR=$2 18 | FIFO=$3 19 | LOG_FILE=$4 20 | 21 | STILLS_FULL_DIR=$INSTALL_DIR/www/$STILLS_DIR 22 | 23 | if [ ! -d $STILLS_FULL_DIR ] 24 | then 25 | echo "stills_thumbs_rescan - no stills directory: $STILLS_FULL_DIR" \ 26 | >> $LOG_FILE 27 | exit 1 28 | fi 29 | 30 | if [ ! -d $STILLS_FULL_DIR/.thumbs ] 31 | then 32 | mkdir $STILLS_FULL_DIR/.thumbs 33 | sudo chown :www-data $STILLS_FULL_DIR/.thumbs 34 | sudo chmod 775 $STILLS_FULL_DIR/.thumbs 35 | fi 36 | 37 | cd $STILLS_FULL_DIR 38 | 39 | LOCK_FILE=/tmp/pikrellcam-stills-rescan-lock 40 | 41 | if [ -f $LOCK_FILE ] 42 | then 43 | exit 0 44 | fi 45 | 46 | touch $LOCK_FILE 47 | 48 | cnt=0 49 | 50 | for still in *.jpg 51 | do 52 | thumb=".thumbs/${still%.jpg}.th.jpg" 53 | if [ ! -f $thumb ] 54 | then 55 | nice -n 12 convert -thumbnail 150 $still $thumb 56 | ((++cnt)) 57 | fi 58 | done 59 | 60 | rm -f $LOCK_FILE 61 | 62 | #echo stills thumbs rescan: $cnt 63 | -------------------------------------------------------------------------------- /scripts-dist/_thumb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generate a jpeg of the motion area in the motion preview jpeg 4 | # and store it in the media_dir/thumbs directory. 5 | # Substitute ".th.jpg" for ".jpg" in the PREVIEW_JPEG name. 6 | # 7 | # Argument substitution done by PiKrellCam before running this script: 8 | # $F - the preview jpeg filename full path (from MJPEG_DIR) 9 | # $G - log file configured in ~/.pikrellcam/pikrellcam.conf. 10 | # $A - the thumb pathname in media_dir or loop_dir 11 | # thumb_size 12 | # $i width of the motion detected area in the preview jpeg 13 | # $J height of the motion detected area in the preview jpeg 14 | # $K x coordinate of the motion detected area center in the preview jpeg 15 | # $Y y coordinate of the motion detected area center in the preview jpeg 16 | # 17 | 18 | preview_jpeg=$1 19 | log_file=$2 20 | thumb_pathname=$3 21 | thumb_size=$4 22 | w_motion=$5 23 | h_motion=$6 24 | xc=$7 25 | yc=$8 26 | 27 | w_thumb=$(echo $thumb_size | cut -d "x" -f 1) 28 | h_thumb=$(echo $thumb_size | cut -d "x" -f 2) 29 | 30 | if ((w_thumb <=10 || h_thumb <= 10)) 31 | then 32 | w_thumb=150 33 | h_thumb=150 34 | fi 35 | 36 | if ((w_motion * h_thumb / w_thumb < h_motion)) 37 | then 38 | w_motion=$((h_motion * w_thumb / h_thumb)) 39 | else 40 | h_motion=$((w_motion * h_thumb / w_thumb)) 41 | fi 42 | 43 | x0=$((xc - w_motion / 2)) 44 | y0=$((yc - h_motion / 2)) 45 | 46 | if ((x0 < 0)) 47 | then 48 | x0=0 49 | fi 50 | if ((y0 < 0)) 51 | then 52 | y0=0 53 | fi 54 | 55 | if ((w_motion > 0)) 56 | then 57 | echo " convert $preview_jpeg -crop ${w_motion}x${h_motion}+${x0}+${y0} \ 58 | -resize ${thumb_size}\! $thumb_pathname" >> $log_file 59 | convert $preview_jpeg -crop ${w_motion}x${h_motion}+${x0}+${y0} \ 60 | -resize ${thumb_size}\! $thumb_pathname 61 | else 62 | echo " convert $preview_jpeg -resize ${thumb_size}\! $thumb_pathname" >> $log_file 63 | convert $preview_jpeg -resize ${thumb_size}\! $thumb_pathname 64 | fi 65 | 66 | exit 0 67 | -------------------------------------------------------------------------------- /scripts-dist/_timelapse-convert: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Note: as of PiKellCam version 2.1.9, timelapse-end is obsolete. It was 4 | # renamed to timelapse-convert and modified to generate timelapse thumbnails. 5 | # 6 | # Script to run commands on timelapse files when a timelapse is ended. 7 | # This script is configured to run in ~/.pikrellcam/pikrellcam.conf 8 | # and the default is to run this in the scripts-dist directory: 9 | # timelapse_convert $c/_timelapse-convert $m $T $n $G $P $l 10 | # 11 | # To customize this script, do not edit it in the scripts-dist directory. 12 | # Copy it to timelapse-convert (or any name) in the scripts directory, edit 13 | # it, and change the configuration in pikrellcam.conf to: 14 | # timelapse_convert $C/timelapse-convert $m $T $n $G $P $l 15 | # 16 | # Argument substitution done by PiKrellCam before running this script: 17 | # $C - scripts directory so this script is found. 18 | # $m - the media directory full path 19 | # $T - the timelapse video filename full path output name. The path puts the 20 | # video in the video directory. 21 | # $n - the timelapse series just ended printed as %05d. 22 | # $G - log file configured in ~/.pikrellcam/pikrellcam.conf. 23 | # $P - the command FIFO full path. 24 | # $l - the timelapse temporary file name format suitable for use with avconv. 25 | # PiKrellCam uses a temporary file name format of tl_$n_$N.jpg. So if 26 | # for example the just ended timelapse series is 22, the timelapse temp 27 | # files in the timelapse subdir will be: tl_00022_xxxxx.jpg where xxxxx 28 | # will be the sequence numbers for the timelapse snapshots. 29 | # In this case the $l variable will be: tl_00022_%05d.jpg. 30 | # WARNING: Since a '%' is embeded in the string, the $l must be the 31 | # last variable given in the timelapse_convert string. 32 | # 33 | 34 | MEDIA_DIR=$1 35 | VIDEOFILE_MP4=$2 36 | SERIES=$3 37 | LOG_FILE=$4 38 | COMMAND_FIFO=$5 39 | FILENAME_FORMAT=$6 40 | 41 | BUSTER=10 42 | V=`cat /etc/debian_version` 43 | DEB_VERSION="${V%.*}" 44 | 45 | if ((DEB_VERSION >= BUSTER)) 46 | then 47 | AV_PGM=ffmpeg 48 | else 49 | AV_PGM=avconv 50 | fi 51 | 52 | # 53 | # Requires libav-tools 54 | # 55 | 56 | DATE=`date +"%F %T"` 57 | echo " $DATE timelapse-convert: converting $FILENAME_FORMAT to $VIDEOFILE_MP4" >> $LOG_FILE 58 | 59 | echo "tl_inform_convert start $SERIES" > $COMMAND_FIFO 60 | 61 | cd $MEDIA_DIR/timelapse 62 | 63 | FIRST=$(ls tl_${SERIES}_*.jpg | head -n1) 64 | NUM=${FIRST:9:5} 65 | 66 | nice -2 $AV_PGM -r 6 -start_number $NUM -i $FILENAME_FORMAT \ 67 | -b:v 6M -maxrate 6M -minrate 1M -bufsize 4M \ 68 | -vcodec libx264 -crf 20 -g 4 -movflags faststart \ 69 | $SERIES.mp4 70 | 71 | mv $SERIES.mp4 $VIDEOFILE_MP4 72 | 73 | BASE=`basename $VIDEOFILE_MP4` 74 | THUMB_JPEG=${BASE%.mp4}.th.jpg 75 | convert -resize 150 tl_${SERIES}_${NUM}.jpg $MEDIA_DIR/thumbs/$THUMB_JPEG 76 | 77 | echo "tl_inform_convert done $SERIES" > $COMMAND_FIFO 78 | 79 | rm tl_${SERIES}_*.jpg 80 | 81 | DATE=`date +"%F %T"` 82 | echo " $DATE timelapse-convert: $VIDEOFILE_MP4 done" >> $LOG_FILE 83 | -------------------------------------------------------------------------------- /scripts-dist/_upgrade: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Argument substitution done by PiKrellCam before running this script: 4 | # $I - install directory full path 5 | # $P - the fifo command file full path 6 | # $G - log file configured in ~/.pikrellcam/pikrellcam.conf. 7 | # $Z - pikrellcam version number. 8 | # 9 | 10 | INSTALL_DIR=$1 11 | FIFO=$2 12 | LOG_FILE=$3 13 | CURRENT_VERSION=$4 14 | 15 | WWW_CONFIG=$INSTALL_DIR/www/config.php 16 | 17 | cd $INSTALL_DIR 18 | 19 | CURRENT_COMMIT=`git log -1 --format=%cd` 20 | 21 | # echo "inform \"inform string\" row justify font xs ys" > $FIFO 22 | # row: 0 - 9 of the inform area. 10 large font rows may not fit. 23 | # justify: 4=left 3=center 2=left at x 1=center at x 0=right <0=right in field 24 | # font: 0=normal 1=large 25 | # xs, ys offsets in pixels 26 | # 27 | 28 | echo "inform \"PiKrellCam V$CURRENT_VERSION current commit:\" 1 3 1" > $FIFO 29 | echo "inform \"$CURRENT_COMMIT\" 2 3 1" > $FIFO 30 | echo "inform \"Checking for upgrade...\" 3 3 1" > $FIFO 31 | 32 | git fetch &> /dev/null 33 | git reset --hard origin/master 2>> $LOG_FILE 34 | 35 | NEW_VERSION=`./pikrellcam --version` 36 | NEW_COMMIT=`git log -1 --format=%cd` 37 | COMMIT_SUBJECT=`git log -1 --format=%s` 38 | 39 | echo "inform \" \" 3 3 1" > $FIFO 40 | if [ "$NEW_COMMIT" != "$CURRENT_COMMIT" ] 41 | then 42 | echo "inform \"New commit: $NEW_COMMIT\" 4 4 1" > $FIFO 43 | echo " Upgraded to commit $NEW_COMMIT Latest changes: $COMMIT_SUBJECT" >> $LOG_FILE 44 | if [ "$NEW_VERSION" != "$CURRENT_VERSION" ] 45 | then 46 | echo "inform \"New version: $NEW_VERSION - need restart/reload.\" 5 4 1" > $FIFO 47 | echo " Upgrade from $CURRENT_VERSION to $NEW_VERSION" >> $LOG_FILE 48 | 49 | if ! fgrep -q $NEW_VERSION $WWW_CONFIG 50 | then 51 | CMD="/VERSION/c\ define\(\"VERSION\", \"$NEW_VERSION\"\);" 52 | sed -i "$CMD" $WWW_CONFIG 53 | fi 54 | else 55 | echo "inform \"Version is unchanged - recommend reload.\" 5 4 1" > $FIFO 56 | echo " Version number is unchanged." >> $LOG_FILE 57 | fi 58 | echo "inform \"Latest changes:\" 6 4 1" > $FIFO 59 | echo "inform \"$COMMIT_SUBJECT\" 7 4 1" > $FIFO 60 | TIMEOUT=20 61 | else 62 | echo "inform \"No new upgrade is available.\" 4 4 1" > $FIFO 63 | echo " No upgrade is available." >> $LOG_FILE 64 | if [ "$NEW_VERSION" == "$CURRENT_VERSION" ] 65 | then 66 | echo "inform \"PiKrellCam is already up to date.\" 5 4 1" > $FIFO 67 | TIMEOUT=6 68 | else 69 | echo "inform \"But the latest version is not running.\" 5 4 1" > $FIFO 70 | echo "inform \"Did you restart after the last upgrade?\" 6 4 1" > $FIFO 71 | TIMEOUT=10 72 | fi 73 | fi 74 | 75 | MESSAGE=`$INSTALL_DIR/scripts-dist/_upgrade-message` 76 | len=${#MESSAGE} 77 | if (( len > 0 )) 78 | then 79 | echo "inform \"$MESSAGE\" 8 4 1" > $FIFO 80 | TIMEOUT=20 81 | fi 82 | 83 | echo "inform timeout $TIMEOUT" > $FIFO 84 | -------------------------------------------------------------------------------- /scripts-dist/_upgrade-message: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! dpkg -s libmp3lame0 2>/dev/null | grep Status | grep -q installed 4 | then 5 | echo "WARNING: install libmp3lme0 or restart will fail!" 6 | fi 7 | 8 | exit 0 9 | -------------------------------------------------------------------------------- /scripts-dist/ds18b20.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | 4 | output = "terminal" 5 | 6 | if len (sys.argv) < 2 : 7 | print "Usage: " + sys.argv[0] + " [C|F] {fifo}" 8 | print " C or F must be given" 9 | print " If \"fifo\" arg is present, write to PiKrellCam FIFO" 10 | sys.exit(1) 11 | 12 | mode = sys.argv[1] 13 | if (mode != "F") and (mode != "C"): 14 | print "Usage: " + sys.argv[0] + " [C|F] {fifo}" 15 | print " C or F must be given" 16 | print " If \"fifo\" arg is present, write to PiKrellCam FIFO" 17 | sys.exit(1) 18 | 19 | if len (sys.argv) == 3 : 20 | output = sys.argv[2] 21 | 22 | from os.path import expanduser 23 | home = expanduser("~") 24 | 25 | # Edit to get labels in the temperature output 26 | #label = ["T0=", "T1=", "T2=", "T3="] 27 | label = ["", "", "", ""] 28 | 29 | def read_temp(device): 30 | global mode 31 | for count in range(0, 3): 32 | path = "/sys/bus/w1/devices/" + device.rstrip() + "/w1_slave" 33 | file = open(path) 34 | lines = file.read() 35 | file.close() 36 | 37 | line1 = lines.split("\n")[0] 38 | line2 = lines.split("\n")[1] 39 | crc = line1.split(" ")[-1] 40 | temp = line2.split("=")[1] 41 | if crc == "YES": 42 | break 43 | 44 | if crc == "YES": 45 | if mode == "F" : 46 | t = float(temp) / 1000.0 * 9.0 / 5.0 + 32 47 | else: 48 | t = float(temp) / 1000.0 49 | else: 50 | t = 0.0 51 | return t 52 | 53 | try: 54 | id = 0 55 | if output == "fifo": 56 | sep = "_" 57 | else: 58 | sep = " " 59 | out_string = "" 60 | devices = open("/sys/bus/w1/devices/w1_bus_master1/w1_master_slaves") 61 | for device in devices.readlines(): 62 | t = read_temp(device) 63 | out_string = sep + out_string + label[id] + "{:.1f}".format(t) + mode 64 | id += 1 65 | devices.close() 66 | if output == "fifo": 67 | # fifo = open(home + "/pikrellcam/www/FIFO", "w") 68 | fifo = open("/home/pi/pikrellcam/www/FIFO", "w") 69 | fifo.write("annotate_string append ds18b20 " + out_string + "\n") 70 | fifo.close() 71 | else: 72 | print out_string 73 | except: 74 | print "no devices" 75 | -------------------------------------------------------------------------------- /scripts-dist/example-motion-detects-fifo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # tab-width: 4 3 | 4 | # This is an example of an external script that will read all motion detects 5 | # from PiKrellCam whether or not motion videos are enabled. 6 | # 7 | # Use it a starting point for a motion detect front end application that is 8 | # independent of motion video enabled state. 9 | # 10 | # Run this script from a terminal on a Pi running PiKrellCam and watch for 11 | # motion detects to be printed. 12 | # 13 | # This script can be terminated with a terminal ^C or by sending an "off" 14 | # command to PiKrellCam: 15 | # echo "motion_detects_fifo_enable off" > /home/pi/pikrellcam/www/FIFO 16 | # 17 | 18 | import os 19 | import sys 20 | import signal 21 | import fcntl 22 | import atexit 23 | import select 24 | import StringIO 25 | import time 26 | 27 | 28 | # Turn on pikrellcam motion detects writing to the motion_detects_FIFO 29 | # 30 | os.system('echo "motion_detects_fifo_enable on" > /home/pi/pikrellcam/www/FIFO') 31 | 32 | # If ^C interrupted, send "off" to the command FIFO 33 | # If external program sends "off", then this script will get an tag 34 | # read from the motion_detects_FIFO and exit. 35 | # 36 | def signal_handler(signum, frame): 37 | print ' Turning off motion_detects_fifo_enable' 38 | os.system('echo "motion_detects_fifo_enable off" > /home/pi/pikrellcam/www/FIFO') 39 | sys.exit(0) 40 | 41 | signal.signal(signal.SIGINT, signal_handler) 42 | 43 | 44 | motion_detects_FIFO = '/home/pi/pikrellcam/www/motion_detects_FIFO' 45 | detects_fifo = os.open(motion_detects_FIFO, os.O_RDONLY | os.O_NONBLOCK) 46 | 47 | bufferSize = 1024 48 | state = 'wait' 49 | 50 | while True: 51 | # Wait until there is data to read from the fifo. May be multiple lines. 52 | # 53 | select.select([detects_fifo],[],[detects_fifo]) 54 | data = os.read(detects_fifo, bufferSize) 55 | 56 | # Split the data into lines and process motion type lines between 57 | # ... tags 58 | # If a motion detect has external or audio, there may not be a frame vector. 59 | # A motion detect with actual motion always has an overall 'f' frame vector. 60 | # If there is an 'f' frame vector, there can also be one or more region 61 | # vectors only, or a burst vector only, or both burst and one or more 62 | # region vectors. If you care only about overall frame motion, look 63 | # for 'f' frame vectors. 64 | # 65 | lines = StringIO.StringIO(data) 66 | for line in lines.readlines(): 67 | line = line.strip('\n') 68 | print "FIFO read: " + line 69 | 70 | # If some program sends a "motion_detects_fifo_enable off" to the 71 | # command FIFO, an tag will be written to the motion_detects_FIFO. 72 | # 73 | if (line.find('') == 0): 74 | print " detected motion_detects_fifo_enable off - exiting." 75 | sys.exit(1) 76 | elif (line.find(' where t is system time with .1 second precision 79 | m, t, b = line.split() 80 | print " motion detected - system time " + t + " => " + time.asctime( time.localtime( int(float(t)) ) ) 81 | elif (line.find('') == 0): 82 | print "" 83 | state = 'wait' 84 | elif (state == 'motion'): 85 | if (line[0] == "f"): 86 | r, x, y, dx, dy, mag, count = line.split() 87 | print " motion vector - frame:" + " mag=" + mag + " count=" + count 88 | elif (line[0].isdigit()): 89 | r, x, y, dx, dy, mag, count = line.split() 90 | print " motion vector - region " + line[0] + ": mag=" + mag + " count=" + count 91 | elif (line[0] == "b"): 92 | b, count = line.split() 93 | print " burst detect - count=" + count 94 | elif (line[0] == "e"): 95 | e, code = line.split() 96 | print " external trigger - code: " + code 97 | elif (line[0] == "a"): 98 | a, level = line.split() 99 | print " audio trigger - level=" + level 100 | 101 | -------------------------------------------------------------------------------- /scripts-dist/example-motion-events: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # tab-width: 4 3 | 4 | # This is an example on_motion_begin script that reads the file 5 | # /run/pikrellcam/motion-events and prints motion event types as they occur. 6 | # Use it as a start for a custom script. 7 | # Copy this script to say ../scripts/motion_events 8 | # and then in ~/.pikrellcam/pikrellcam.conf, set on_motion_begin to: 9 | # 10 | # on_motion_begin $C/motion_events $e 11 | # 12 | # This script as is just prints output which can be seen only if pikrellcam 13 | # is run from a terminal. 14 | 15 | import sys 16 | import socket 17 | import time 18 | 19 | filename = '/run/pikrellcam/motion-events' 20 | file = open(filename,'r') 21 | 22 | state = 'wait' 23 | event = 1 24 | 25 | # sys.argv[1] is the $e value: motion, external or audio 26 | print "on_motion_begin triggered by: " + sys.argv[1] 27 | 28 | while 1: 29 | where = file.tell() 30 | line = file.readline() 31 | if not line: 32 | time.sleep(0.1) 33 | file.seek(where) 34 | continue 35 | line = line.strip('\n') 36 | if (line.find('') == 0): 41 | state = 'wait' 42 | elif (line.find('') == 0): 43 | sys.exit(1) 44 | elif (state == 'motion'): 45 | if (line[0].isdigit()): 46 | print " motion direction - region: " + line[0] 47 | elif (line[0] == "b"): 48 | b, count = line.split() 49 | print " burst detect - count: " + count 50 | elif (line[0] == "e"): 51 | e, code = line.split() 52 | print " external trigger - code: " + code 53 | elif (line[0] == "a"): 54 | a, level = line.split() 55 | print " audio trigger - level: " + level 56 | -------------------------------------------------------------------------------- /scripts-dist/example-motion-send-alarm1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # tab-width: 4 3 | 4 | # Edit this script to set the "to_hosts" line below. 5 | # 6 | # This is an example on_motion_begin script that will send an alarm message 7 | # to selected hosts if motion is detected. 8 | # 9 | # Set this script to be a PiKrellCam on_motion_begin script by copying to 10 | # scripts/motion-send-alarm and editing on_motion_begin in pikrellcam.conf: 11 | # on_motion_begin $C/motion-send-alarm 12 | # Also have pkc-alarm running on the to_hosts you configure here. 13 | # 14 | # With this script only one alarm is sent per motion detect video. 15 | # See example-motion-send-alarm2 for a different approach. 16 | 17 | import sys 18 | import socket 19 | import time 20 | 21 | # Edit to_hosts to select hosts you want to send alarms to. 22 | to_hosts = "gkrellm4,rpi0,rpi1" 23 | 24 | # UDP transmissions are unreliable. So repeat sending the alarm. 25 | # Receiving programs should check the message id and accept the alarm once 26 | # per time interval (about 2 seconds) 27 | # 28 | repeat = 4 29 | delay = .05 30 | 31 | 32 | from_host = socket.gethostname() 33 | 34 | # The PiKrellCam multicast group IP and port number is fixed: 35 | PKC_MULTICAST_GROUP = '225.0.0.55' 36 | PKC_MULTICAST_PORT = 22555 37 | send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 38 | send_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) 39 | 40 | msg_id = 1 41 | for x in range(0, repeat): 42 | msg = "%s:%d %s message alarm" % (from_host, msg_id, to_hosts) 43 | # print msg 44 | send_socket.sendto(msg, (PKC_MULTICAST_GROUP, PKC_MULTICAST_PORT)) 45 | time.sleep(delay) 46 | -------------------------------------------------------------------------------- /scripts-dist/example-motion-send-alarm2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # tab-width: 4 3 | 4 | # This file should be edited, read the configurable variables below. 5 | # 6 | # This is an example on_motion_begin script that will send an alarm message 7 | # to selected hosts if motion of a configurable magnitude and count 8 | # is detected in a particular motion region. 9 | # It reads /run/pikrellcam/motion-events to get motion events as they occur 10 | # while a motion video is being recorded. 11 | # 12 | # Set this script to be a PiKrellCam on_motion_begin script by copying to 13 | # scripts/motion-send-alarm and editing on_motion_begin in pikrellcam.conf: 14 | # on_motion_begin $C/motion-send-alarm 15 | # Also have pkc-alarm running on the to_hosts you configure here. 16 | # 17 | # With this motion detection settings can be low to detect small animals, 18 | # but an alarm sent only if larger magnitude and count motion is detected 19 | # in a region placed around some object of critical interest. 20 | # 21 | # This is a preliminary version of this script and can be improved. 22 | # Check future PiKrellCam releases for any changes that might appear here. 23 | # 24 | import sys 25 | import socket 26 | import time 27 | 28 | # Configurable variables 29 | # 30 | # Edit to_hosts to select who to send the alarm to. On each of these hosts, 31 | # pkc-alarm should be running in order to receive the alarm. 32 | # Edit motion_region to be the region you want monitored for detection. 33 | # This can be a region around a door, car, or any region of interest. 34 | # Edit magnitude_limit and count_limit to be the motion vector parameters 35 | # you want exceeded for an alarm to be sent. These should >= the values 36 | # the pikrellcam program has configured for motion detection. 37 | # Edit holdoff to be the minimum seconds between sending alarm messages. 38 | # It should be greater than the holdoff in pkc-alarm. 39 | # 40 | to_hosts = "gkrellm4,rpi0,rpi1" 41 | motion_region = 3 42 | magnitude_limit = 12 43 | count_limit = 20 44 | holdoff = 6 45 | 46 | # UDP transmissions are unreliable. So repeat sending the alarm. 47 | # Receiving programs should check the message id and accept the alarm once 48 | # per time interval (about 2 seconds) 49 | # 50 | repeat = 4 51 | delay = .05 52 | 53 | from_host = socket.gethostname() 54 | 55 | 56 | # The PiKrellCam multicast group IP and port number is fixed: 57 | PKC_MULTICAST_GROUP = '225.0.0.55' 58 | PKC_MULTICAST_PORT = 22555 59 | send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 60 | send_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) 61 | 62 | filename = '/run/pikrellcam/motion-events' 63 | file = open(filename,'r') 64 | 65 | msg_id = 1 66 | 67 | state = 'wait' 68 | tsent = 0 69 | 70 | while 1: 71 | where = file.tell() 72 | line = file.readline() 73 | if not line: 74 | time.sleep(0.1) 75 | file.seek(where) 76 | continue 77 | line = line.strip('\n') 78 | # print '==>' + line 79 | if (line.find('') == 0): 82 | state = 'wait' 83 | elif (line.find('') == 0): 84 | sys.exit(1) 85 | elif (state == 'motion'): 86 | if (line.startswith(str(motion_region))): 87 | # print line 88 | tnow = time.time() 89 | if (tnow >= tsent + holdoff): 90 | r, x, y, dx, dy, mag, count = line.split() 91 | if (int(mag) >= magnitude_limit and int(count) >= count_limit): 92 | for x in range(0, repeat): 93 | msg = "%s:%d %s message alarm" % (from_host, msg_id, to_hosts) 94 | # print msg 95 | send_socket.sendto(msg, (PKC_MULTICAST_GROUP, PKC_MULTICAST_PORT)) 96 | time.sleep(delay) 97 | tsent = tnow 98 | msg_id = msg_id + 1 99 | 100 | -------------------------------------------------------------------------------- /scripts-dist/motion-end: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to ssh copy a motion video file from the pi to an archive site. 4 | # If this script is configured to be run as the on_motion_end command, than 5 | # motion videos can be immediately saved to a backup site as long as the 6 | # backup site is online. 7 | # 8 | # This script should be configured in ~/.pikrellcam/pikrellcam.conf and not 9 | # in the at-command.conf file. Eg in pikrellcam.conf: 10 | # on_motion_end $C/motion-end $v $P $G 11 | # 12 | # Argument substitution done by PiKrellCam before running this script: 13 | # $C - scripts directory so this script is found. 14 | # $v - the full path name of the motion video just saved. 15 | # $P - the command FIFO so we can write an inform notice to display on the 16 | # PiKrellCam web page (not used yet). 17 | # $G - log file configured in ~/.pikrellcam/pikrellcam.conf. 18 | # 19 | 20 | # ====== WARNING ====== 21 | # This script will fail on hosts that are not authenticated, so you may need 22 | # to initially do a manual ssh or scp to the host and answer yes when asked if 23 | # you want to continue connecting so the host will be added to the list of 24 | # known hosts. 25 | # 26 | 27 | VIDEO_FILE=$1 28 | FIFO=$2 29 | LOG_FILE=$3 30 | 31 | if [ "$LOG_FILE" == "" ] 32 | then 33 | LOG_FILE = /dev/null 34 | fi 35 | 36 | 37 | # Add a line for all the hosts to copy the video file to with this info: 38 | # dest_name=(user host password ssh_port video_directory) 39 | # and then add a scp_video line below for the "dest_name". 40 | # scp_video $VIDEO_FILE ${dest_name[@]} 41 | # 42 | machine1=(user machine1.com password 22 /home/pi/motion_videos) 43 | rpi2=(pi rpi2 raspberry 20002 /home/pi/videos) 44 | 45 | 46 | scp_video() 47 | { 48 | dest=($@) 49 | video=${dest[0]} 50 | user=${dest[1]} 51 | host=${dest[2]} 52 | password=${dest[3]} 53 | port=${dest[4]} 54 | dir=${dest[5]} 55 | 56 | echo $video copy to $host 57 | 58 | # Do the scp if the port is open. 59 | if nc -zv -w 3 $host $port 2> /dev/null 60 | then 61 | echo " sshpass -p $password scp -P $port $video $user@$host:$dir" >> $LOG_FILE 62 | sshpass -p $password scp -P $port $video $user@$host:$dir 2>> $LOG_FILE 63 | else 64 | echo " Cannot connect to $host:$port. scp of $video failed." >> $LOG_FILE 65 | fi 66 | } 67 | 68 | scp_video $VIDEO_FILE ${machine1[@]} 69 | scp_video $VIDEO_FILE ${rpi2[@]} 70 | 71 | # A delete of the video could be here. Or write a scheduled videos 72 | # directory cleanup and run it from the at-command list. Or manage 73 | # it by hand. 74 | -------------------------------------------------------------------------------- /scripts-dist/pkc-alarm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # tab-width: 4 3 | 4 | # Have this script running to receive alarm messages sent from PiKrellCams 5 | # on your LAN and edit the sound playing command for your setup. 6 | # TODO: play different sounds depending on from_host, or put the from_host 7 | # in an espeak commmand. The audio_command would then need to be set after 8 | # from_host is known. 9 | # Extend the alarm message to have something like alarm level args. 10 | # FIXME: repeat receive detection looks only at message id and not at 11 | # the from_host & message id combination. Also what about sound playing 12 | # collisions if alarms are received from different hosts? 13 | # 14 | import socket 15 | import struct 16 | import os 17 | import time 18 | 19 | # Edit and uncomment one audio_command. Depending on the audio command, you 20 | # may need to apt-get install additional packages such as espeak or mpg123 21 | # 22 | audio_command = "aplay /home/pi/audio/danger-will-robinson.wav &" 23 | #audio_command = "mpg123 path-to-audiofile.mp3 &" 24 | #'espeak' + " '" + ", motion detected' &" 25 | 26 | # Edit holdoff to be the minimum seconds between accepting alarm messages. 27 | # It should be long enough for a previous audio alarm to complete and 28 | # probably less than the holdoff in the sending motion-send-alarm script. 29 | # I've found that sound playing can hang for a while if aplay's step on 30 | # each other when pkc-alarm is run on a Pi. 31 | # 32 | holdoff = 5 33 | 34 | 35 | 36 | PKC_MULTICAST_GROUP_IP = '225.0.0.55' 37 | PKC_MULTICAST_GROUP_PORT = 22555 38 | 39 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 40 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 41 | sock.bind((PKC_MULTICAST_GROUP_IP, PKC_MULTICAST_GROUP_PORT)) 42 | mreq = struct.pack("4sl", socket.inet_aton(PKC_MULTICAST_GROUP_IP), socket.INADDR_ANY) 43 | sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) 44 | 45 | id = 0 46 | idprev = 0 47 | tprev = 0 48 | tplay = 0 49 | 50 | this_host = socket.gethostname() 51 | 52 | def host_match(from_host, to_hosts): 53 | if ('all' in to_hosts.lower()): 54 | if ('!' in to_hosts): 55 | return from_host != this_host 56 | else: 57 | return True 58 | host_list = to_hosts.split(',') 59 | if (this_host in host_list): 60 | return True 61 | return False 62 | 63 | 64 | while True: 65 | line = sock.recv(256) 66 | tnow = time.time() 67 | # print line 68 | 69 | from_host, to_hosts, msg_type, msg_body = line.split(' ', 3) 70 | if (msg_type != 'message' or msg_body != 'alarm'): 71 | continue; 72 | 73 | idx = from_host.find(':') 74 | if (idx >= 0): 75 | from_host, ids = from_host.split(':') 76 | id = int(ids) 77 | 78 | if (host_match(from_host, to_hosts) == False): 79 | continue 80 | 81 | # reject repeat messages of same msg_id. 82 | if (id == idprev and tnow - tprev <= 2): 83 | tprev = tnow 84 | continue 85 | 86 | if (id != idprev or tnow - tplay >= holdoff): 87 | print audio_command 88 | os.system(audio_command) 89 | tplay = tnow 90 | 91 | idprev = id 92 | 93 | -------------------------------------------------------------------------------- /scripts-dist/pkc-motion: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # tab-width: 4 3 | 4 | # pkc-motion multicasts a motion_enable command to all PiKrellCams on a LAN 5 | # to turn on or off motion detection. 6 | 7 | import sys 8 | import socket 9 | import time 10 | 11 | # The PiKrellCam multicast group IP and port number is fixed: 12 | PKC_MULTICAST_GROUP = '225.0.0.55' 13 | PKC_MULTICAST_PORT = 22555 14 | 15 | # Open UDP multicast socket to write to. 16 | send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 17 | send_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) 18 | 19 | # UDP transmissions are unreliable. So repeat sending the motion_enable 20 | # command multiple times to ensure all PiKrellCams will receive it. 21 | # Set a fraction of a second delay between repeat sends. 22 | # The send will use a fixed message id number > 0 so that each receiving 23 | # PiKrellCam will execute the first motion_enable it receives and then 24 | # ignore any following repeated commands it receives. 25 | 26 | repeat = 4 27 | delay = .05 28 | 29 | def usage(): 30 | print "usage: pkc-motion {host|host-list|all} " 31 | print " Multicast a motion_enable command to PiKrellCams on a LAN." 32 | print " If host, host-list or all is not specified, all is assumed." 33 | print " host-list is a comma separated list of hosts." 34 | print " on or off must be given." 35 | sys.exit() 36 | 37 | argc = len(sys.argv) 38 | if argc == 1: 39 | usage() 40 | if argc == 2: 41 | hosts = "all" 42 | onoff = sys.argv[1] 43 | if argc == 3: 44 | hosts = sys.argv[1] 45 | onoff = sys.argv[2] 46 | if onoff == "on": 47 | msg_id = 1 48 | elif onoff == "off": 49 | msg_id = 2 50 | else: 51 | print 'Bad arg: ' + onoff 52 | usage() 53 | 54 | hostname = socket.gethostname() 55 | 56 | for x in range(1, repeat + 1): 57 | msg = "%s:%d %s command @motion_enable %s" % (hostname, msg_id, hosts, onoff) 58 | # print msg 59 | send_socket.sendto(msg, (PKC_MULTICAST_GROUP, PKC_MULTICAST_PORT)) 60 | time.sleep(delay) 61 | 62 | -------------------------------------------------------------------------------- /scripts-dist/pkc-reboot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # tab-width: 4 3 | 4 | # pkc-reboot multicasts a reboot command to selected hosts running 5 | # PiKrellCam on a LAN 6 | # Uses: 7 | # Reboot Pis running PiKrellCam from a desktop. 8 | # If the internet connection is lost to a Pi running PiKrellCam, it is 9 | # possible a multicast can still get through. It has worked for me. 10 | 11 | import sys 12 | import socket 13 | import time 14 | 15 | # The PiKrellCam multicast group IP and port number is fixed: 16 | PKC_MULTICAST_GROUP = '225.0.0.55' 17 | PKC_MULTICAST_PORT = 22555 18 | 19 | # Open UDP multicast socket to write to. 20 | send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 21 | send_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) 22 | 23 | # UDP transmissions are unreliable. So repeat sending the reboot 24 | # command multiple times to ensure all PiKrellCams will receive it. 25 | # Set a fraction of a second delay between repeat sends. 26 | # The sends will use a fixed message id number > 0 so that each receiving 27 | # PiKrellCam will accept the first reboot command it receives per id 28 | # number and then ignore any following repeated commands it receives for 29 | # that id number. 30 | 31 | repeat = 4 32 | delay = .05 33 | 34 | def usage(): 35 | print "usage: pkc-reboot " 36 | print "Multicast a reboot command to PiKrellCams on a LAN." 37 | print " host, host-list or all must be given." 38 | print " host-list is a comma separated list of hosts." 39 | sys.exit() 40 | 41 | argc = len(sys.argv) 42 | if argc == 1: 43 | usage() 44 | elif argc == 2: 45 | hosts = sys.argv[1] 46 | else: 47 | print 'Bad number of args' 48 | usage() 49 | 50 | hostname = socket.gethostname() 51 | 52 | msg_id = 1 53 | for x in range(0, repeat): 54 | msg = "%s:%d %s command @reboot" % (hostname, msg_id, hosts) 55 | # print msg 56 | send_socket.sendto(msg, (PKC_MULTICAST_GROUP, PKC_MULTICAST_PORT)) 57 | time.sleep(delay) 58 | 59 | 60 | # PiKrellCam needs a confirming reboot command within 10 seconds. 61 | # Change the id number so the second reboot command will be accepted. 62 | 63 | time.sleep(1) 64 | msg_id = 2 65 | for x in range(0, repeat): 66 | msg = "%s:%d %s command @reboot" % (hostname, msg_id, hosts) 67 | # print msg 68 | send_socket.sendto(msg, (PKC_MULTICAST_GROUP, PKC_MULTICAST_PORT)) 69 | time.sleep(delay) 70 | -------------------------------------------------------------------------------- /scripts-dist/pkc-recv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Listen for traffic on the PiKrellCam multicast group and print all lines. 4 | # Run this in a terminal for debugging multicast traffic. 5 | 6 | import socket 7 | import struct 8 | 9 | PKC_MULTICAST_GROUP_IP = '225.0.0.55' 10 | PKC_MULTICAST_GROUP_PORT = 22555 11 | 12 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 13 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 14 | sock.bind((PKC_MULTICAST_GROUP_IP, PKC_MULTICAST_GROUP_PORT)) 15 | mreq = struct.pack("4sl", socket.inet_aton(PKC_MULTICAST_GROUP_IP), socket.INADDR_ANY) 16 | 17 | sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) 18 | 19 | while True: 20 | print sock.recv(1024) 21 | -------------------------------------------------------------------------------- /scripts-dist/preview-save: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is a motion event on_motion_preview_save script. 4 | # Uncomment lines to enable emailing the preview jpeg, copying the preview 5 | # jpeg, or taking a still 6 | # 7 | # uncomment any echo lines to the LOG_FILE if you want logging. 8 | # 9 | # Argument substitution done by PiKrellCam before running this script: 10 | # $C - scripts directory so this script is found. 11 | # $F - the preview jpeg filename full path (from MJPEG_DIR) 12 | # $m - the media directory full path 13 | # $P - the command FIFO 14 | # $G - log file configured in ~/.pikrellcam/pikrellcam.conf. 15 | # $A - the filename full path of the thumb jpeg of the motion area 16 | # Note: if motion_preview_save_mode is "first" and the detect 17 | # that triggers this script is audio or external, then there is no 18 | # motion area and the thumb jpeg will be from the full preview jpeg. 19 | # Additionally, if there is subsequent motion, the thumb jpeg will 20 | # be renamed after the motion video ends. 21 | # 22 | # In ~/.pikrellcam.conf, for this script as is, make on_motion_preview_save: 23 | # 24 | # on_motion_preview_save $C/preview-save $F $m $P $G 25 | 26 | PREVIEW_JPEG=$1 27 | MEDIA_DIR=$2 28 | FIFO=$3 29 | LOG_FILE=$4 30 | THUMB_JPEG=$5 31 | 32 | # To configure emailing a jpeg for a motion event, uncomment one EMAIL_JPG 33 | # line, edit the MY_EMAIL line, and uncomment the mpack line: 34 | 35 | # If you want to email the full mjpeg_width preview jpg, uncomment this line: 36 | # EMAIL_JPEG=$PREVIEW_JPEG 37 | # OR, to email the smaller motion area thumb jpg, uncomment this line: 38 | # EMAIL_JPEG=$THUMB_JPEG 39 | 40 | # Edit MY_EMAIL to your email address to email the preview jpeg and uncomment 41 | # the mpack line. Run mpack in the background because if it is slow in 42 | # sending the email, pikrellcam is waiting for this script to end and the 43 | # preview display can pause. 44 | # 45 | MY_EMAIL=myuser@gmail.com 46 | #mpack -s pikrellcam@$HOSTNAME $EMAIL_JPEG $MY_EMAIL & 47 | 48 | #echo "mpack -s pikrellcam@$HOSTNAME $PREVIEW_JPEG $MY_EMAIL &" >> $LOG_FILE 49 | 50 | 51 | 52 | # uncomment to copy the low resolution preview jpeg to the stills directory. 53 | # Edit PREVIEW_JPEG_DIR to copy it anywhere you want. 54 | # 55 | PREVIEW_JPEG_DIR=$MEDIA_DIR/stills 56 | #cp $PREVIEW_JPEG $PREVIEW_JPEG_DIR 57 | #echo "cp $PREVIEW_JPEG $PREVIEW_JPEG_DIR" >> $LOG_FILE 58 | 59 | 60 | 61 | # NOTE: taking a full resolution still can put a glitch into the motion 62 | # video being recorded because the camera has to switch modes, however, 63 | # uncomment if you do want to take a still. This is probably only useful if 64 | # preview_save_mode is "first" because if the mode is "best" the still will 65 | # be taken after the action has ended. So use the on_motion_begin command 66 | # to take a still if preview_save_mode is "best". The on_motion_begin 67 | # command can be a script like this or the internal command "@still" 68 | # 69 | #echo "still" > $FIFO 70 | #echo "preview save script: still" >> $LOG_FILE 71 | -------------------------------------------------------------------------------- /scripts-dist/startup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Startup script to mount usb drives on media_dir or NFS mount on archive_dir 4 | # 5 | # This script should be configured in ~/.pikrellcam/pikrellcam.conf: 6 | # 7 | # on_startup $C/startup $I $m $a $G 8 | # 9 | # NOTE: the above on_startup command is not compatible with the default 10 | # ~/pikrellcam/scripts/startup script in pikrellcam versions prior to 11 | # V 4.1.5. If you have copied this script over an existing startup script, 12 | # be sure to check how on_startup is configured in pikrellcam.conf. 13 | # 14 | # Argument substitution done by PiKrellCam before running this script: 15 | # $C - scripts directory so this script is found. 16 | # $I - install directory path 17 | # Variables configured in ~/.pikrellcam/pikrellcam.conf 18 | # $m - the media_dir directory path 19 | # $a - the archive_dir directory path 20 | # $G - the log_file path 21 | # 22 | 23 | 24 | # Set MOUNT_DISK to a disk name to mount a disk on media_dir 25 | # If the media_dir directory is on a tmpfs, it will not be mounted. 26 | # For example, to mount /dev/sda1 on media_dir: 27 | # 28 | # MOUNT_DISK=sda1 29 | # 30 | 31 | MOUNT_DISK="" 32 | 33 | 34 | # Set NFS_ARCHIVE to nfs mount a disk on archive_dir. See the help page. 35 | # NFS_ARCHIVE should match the nfs mount line in /etc/fstab 36 | # For example, the examples in the web help page would set: 37 | # 38 | # NFS_ARCHIVE=gkrellm6:/home/bill/media-archive 39 | # or 40 | # NFS_ARCHIVE=rpi0:/mnt/archive/media-archive 41 | # 42 | 43 | NFS_ARCHIVE="" 44 | 45 | 46 | 47 | install_dir=$1 48 | media_dir=$2 49 | archive_dir=$3 50 | log_file=$4 51 | 52 | if [ "$log_file" == "" ] 53 | then 54 | log_file=/dev/null 55 | fi 56 | 57 | if [ "$MOUNT_DISK" == "" ] 58 | then 59 | echo " MOUNT_DISK is not set." >> $log_file 60 | else 61 | if ! grep -q $MOUNT_DISK /proc/partitions 62 | then 63 | echo " Cannot find $MOUNT_DISK to mount on $media_dir" >> $log_file 64 | MOUNT_DISK="" 65 | fi 66 | fi 67 | 68 | if [ "$MOUNT_DISK" != "" ] 69 | then 70 | disk_device=/dev/$MOUNT_DISK 71 | cd $install_dir 72 | 73 | curmount_dir=`fgrep "$disk_device " /etc/mtab | cut -f 2 -d ' ' ` 74 | if [ "$curmount_dir" != "" ] 75 | then 76 | if [ "$curmount_dir" != "$media_dir" ] 77 | then 78 | echo " umount $disk_device from $curmount_dir" >> $log_file 79 | sudo umount $disk_device 80 | fi 81 | fi 82 | 83 | if ! mountpoint -q $media_dir 84 | then 85 | FS_TYPE=`stat -f -c '%T' $media_dir` 86 | if [ "$FS_TYPE" != "tmpfs" ] 87 | then 88 | if sudo mount $disk_device $media_dir 89 | then 90 | echo " mounted $disk_device on $media_dir" >> $log_file 91 | else 92 | echo " mount of $disk_device on $media_dir failed" >> $log_file 93 | fi 94 | # 95 | # PiKrellCam will make subdirectories and fixup permissions. 96 | else 97 | echo " not mounting $disk_device because $media_dir is a tmpfs" >> $log_file 98 | fi 99 | else 100 | echo " $media_dir is already mounted." >> $log_file 101 | fi 102 | fi 103 | 104 | 105 | 106 | if [ "$NFS_ARCHIVE" == "" ] 107 | then 108 | echo " NFS_ARCHIVE is not set." >> $log_file 109 | fi 110 | 111 | if [ "$NFS_ARCHIVE" != "" ] 112 | then 113 | fs_type=$(stat -f -L -c %T $archive_dir) 114 | 115 | stale=$(echo "$fs_type" | grep "Stale") 116 | 117 | if [ "$stale" != "" ] 118 | then 119 | echo " NFS archive directory $archive_dir is stale." >> $log_file 120 | echo " Remote NFS server may be down?" >> log_file 121 | echo " To force unmount, run: sudo umount -f $archive_dir" >> log_file 122 | elif [ "$fs_type" == "nfs" ] 123 | then 124 | echo " $archive_dir is already NFS mounted." >> $log_file 125 | else 126 | cp $install_dir/www/images/nfs-mounting.jpg /run/pikrellcam/mjpeg.jpg 127 | sudo mount $NFS_ARCHIVE & 128 | for ((p = 0; p < 10; p++)) 129 | do 130 | sleep 1 131 | fs_type=$(stat -f -L -c %T $archive_dir) 132 | if [ "$fs_type" == "nfs" ] 133 | then 134 | echo " NFS mounted $NFS_ARCHIVE" >> $log_file 135 | break; 136 | else 137 | echo " NFS mount of $NFS_ARCHIVE slow or failed ..." >> $log_file 138 | fi 139 | done 140 | fi 141 | fi 142 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | EXECUTABLE = ../pikrellcam 4 | LIBKRELLM_ROOT := ../libkrellm 5 | BUILDDIR = /tmp/build-pikrellcam 6 | 7 | MODULES := glcd utils 8 | 9 | LIBKRELLM_DIRS=$(addprefix $(LIBKRELLM_ROOT)/,$(MODULES)) 10 | #$(info $(LIBKRELLM_DIRS)) 11 | 12 | INCLUDES := $(addprefix -I,$(LIBKRELLM_DIRS)) 13 | #$(info $(INCLUDES)) 14 | 15 | MMAL_INCLUDE ?= -I/opt/vc/include \ 16 | -I/opt/vc/include/interface/vcos/pthreads \ 17 | -I/opt/vc/include/interface/vmcs_host/linux 18 | 19 | MMAL_LIB ?= -L/opt/vc/lib -lbcm_host -lvcos -lmmal -lmmal_core -lmmal_util \ 20 | -lmmal_vc_client 21 | 22 | 23 | FLAGS = -O2 -Wall $(MMAL_INCLUDE) $(INCLUDES) 24 | LIBS = $(MMAL_LIB) -lm -lpthread -lasound -lmp3lame 25 | 26 | LOCAL_SRC = pikrellcam.c mmalcam.c motion.c event.c display.c config.c \ 27 | servo.c preset.c sunriset.c multicast.c audio.c \ 28 | tcpserver.c tcpserver.c tcpserver_mjpeg.c 29 | 30 | KRELLMLIB_SRC = $(wildcard $(addsuffix /*.c,$(LIBKRELLM_DIRS))) 31 | SOURCES = $(LOCAL_SRC) $(KRELLMLIB_SRC) 32 | #$(info $(SOURCES)) 33 | 34 | KRELLMLIB_DEPS = $(wildcard $(addsuffix /*.h,$(LIBKRELLM_DIRS))) 35 | DEPS = pikrellcam.h $(KRELLMLIB_DEPS) 36 | #$(info $(DEPS)) 37 | 38 | OBJECTS = $(addprefix $(BUILDDIR)/, $(notdir $(SOURCES:%.c=%.o))) 39 | #$(info $(OBJECTS)) 40 | 41 | 42 | VPATH = $(LIBKRELLM_DIRS) 43 | $(BUILDDIR)/%.o: %.c $(DEPS) 44 | $(CC) -c $(FLAGS) $< -o $@ 45 | 46 | 47 | all: dir $(EXECUTABLE) 48 | 49 | dir: 50 | mkdir -p $(BUILDDIR) 51 | 52 | $(EXECUTABLE): $(OBJECTS) 53 | $(CC) $^ -o $(EXECUTABLE) $(LIBS) 54 | 55 | clean: 56 | rm -f $(BUILDDIR)/*o $(EXECUTABLE) 57 | -------------------------------------------------------------------------------- /src/mmal_status.h: -------------------------------------------------------------------------------- 1 | 2 | /* These status messages are from comments in 3 | | /opt/vc/include/interface/mmalmmal_types.h 4 | | which is Copyright (c) 2012, Broadcom Europe Ltd 5 | */ 6 | 7 | char *mmal_status[] = 8 | { 9 | "Success", 10 | "Out of memory", 11 | "Out of resources", 12 | "Invalid argument", 13 | "Unimplemented function", 14 | "No such file or directory", 15 | "No such device or address", 16 | "I/O error", 17 | "Illegal seek", 18 | "Corrupt data", 19 | "Component not ready", 20 | "Component not configured", 21 | "Port is already connected", 22 | "Port is disconnected", 23 | "Resource temporarily unavailable", 24 | "Bad address" 25 | }; 26 | -------------------------------------------------------------------------------- /src/multicast.c: -------------------------------------------------------------------------------- 1 | /* PiKrellCam 2 | | 3 | | Copyright (C) 2015-2020 Bill Wilson billw@gkrellm.net 4 | | 5 | | PiKrellCam is free software: you can redistribute it and/or modify it 6 | | under the terms of the GNU General Public License as published by 7 | | the Free Software Foundation, either version 3 of the License, or 8 | | (at your option) any later version. 9 | | 10 | | PiKrellCam is distributed in the hope that it will be useful, but WITHOUT 11 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 | | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 13 | | License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with this program. If not, see http://www.gnu.org/licenses/ 17 | | 18 | | This file is part of PiKrellCam. 19 | */ 20 | 21 | #include "pikrellcam.h" 22 | #include 23 | #include 24 | #include 25 | 26 | static int fd_recv = -1, 27 | fd_send = -1; 28 | static struct sockaddr_in 29 | addr_send, 30 | addr_recv; 31 | 32 | typedef struct 33 | { 34 | char *name; 35 | int message_id; 36 | int time; 37 | } 38 | MulticastHost; 39 | 40 | static SList *multicast_host_list; 41 | 42 | void 43 | multicast_send(char *seq, char *message) 44 | { 45 | char buf[256]; 46 | 47 | if (fd_send < 0 || !pikrellcam.multicast_enable) 48 | return; 49 | if (!seq) 50 | seq = ""; 51 | if (!message || *message == '\0') 52 | return; 53 | snprintf(buf, sizeof(buf), "%s%s %s\n", pikrellcam.hostname, seq, message); 54 | sendto(fd_send, buf, strlen(buf), 0, 55 | (struct sockaddr *) &addr_send, sizeof(addr_send)); 56 | } 57 | 58 | static boolean 59 | hostname_match(char *from, char *to) 60 | { 61 | char *next; 62 | 63 | if (!strcasecmp(to, "all")) 64 | return TRUE; 65 | if (!strcasecmp(to, "all!") || !strcasecmp(to, "!all")) 66 | { 67 | if (!strcmp(from, pikrellcam.hostname)) 68 | return FALSE; 69 | else 70 | return TRUE; 71 | } 72 | 73 | while (*to) 74 | { 75 | next = strchr(to, ','); 76 | if (next) 77 | *next++ = '\0'; 78 | if (!strcmp(to, pikrellcam.hostname)) 79 | return TRUE; 80 | to = next ? next : ""; 81 | } 82 | return FALSE; 83 | } 84 | 85 | static MulticastHost * 86 | multicast_host_find(char *hostname) 87 | { 88 | MulticastHost *host; 89 | SList *list; 90 | 91 | for (list = multicast_host_list; list; list = list->next) 92 | { 93 | host = (MulticastHost *) list->data; 94 | if (!strcmp(host->name, hostname)) 95 | return host; 96 | } 97 | return NULL; 98 | 99 | } 100 | 101 | /* If there is a record of a multicast message from hostname within a 102 | | threshold time with this message_id number, then consider that we are 103 | | getting a repeat transmission of an already received message. 104 | */ 105 | static boolean 106 | multicast_message_id_repeat(char *hostname, int message_id) 107 | { 108 | MulticastHost *host; 109 | boolean result = FALSE; 110 | 111 | host = multicast_host_find(hostname); 112 | if (!host) 113 | { 114 | host = (MulticastHost *) calloc(1, sizeof(MulticastHost)); 115 | host->name = strdup(hostname); 116 | multicast_host_list = slist_append(multicast_host_list, host); 117 | } 118 | else if ( message_id > 0 119 | && host->message_id == message_id 120 | && pikrellcam.t_now - host->time < 3 121 | ) 122 | result = TRUE; 123 | 124 | host->message_id = message_id; 125 | host->time = pikrellcam.t_now; 126 | return result; 127 | } 128 | 129 | static void 130 | multicast_ack(char *to_host, int message_id) 131 | { 132 | char message[200]; 133 | 134 | snprintf(message, sizeof(message), "%s ack %d", to_host, message_id); 135 | multicast_send("", message); 136 | } 137 | 138 | void 139 | multicast_recv(void) 140 | { 141 | socklen_t addrlen; 142 | int msg_id, n, nbytes = 0; 143 | char *line, *eol, *s, recv_buf[1025]; 144 | char from[128], to[256], message[256]; 145 | char msg_type[32], action[256]; 146 | boolean repeat, match; 147 | 148 | if (fd_recv < 0 || !pikrellcam.multicast_enable) 149 | return; 150 | ioctl(fd_recv, FIONREAD, &nbytes); 151 | if (nbytes <= 0) 152 | return; 153 | addrlen = sizeof(addr_recv); 154 | if ((nbytes = recvfrom(fd_recv, recv_buf, sizeof(recv_buf) - 1, 0, 155 | (struct sockaddr *) &addr_recv, &addrlen)) <= 0) 156 | return; 157 | if (recv_buf[nbytes - 1] != '\n') 158 | recv_buf[nbytes++] = '\n'; 159 | recv_buf[nbytes] = '\0'; 160 | line = recv_buf; 161 | while (*line) 162 | { 163 | eol = strchr(line, '\n'); 164 | if (!eol) 165 | break; 166 | *eol++ = '\0'; 167 | msg_id = 0; 168 | repeat = FALSE; 169 | from[0] = to[0] = message[0] = '\0'; 170 | n = sscanf(line, "%127s %255s %255[^\n]", from, to, message); 171 | if (pikrellcam.verbose_multicast) 172 | printf("multicast recv: <%s> <%s> <%s>\n", from, to, message); 173 | if ((s = strchr(from, ':')) != NULL) 174 | { 175 | *s++ = '\0'; 176 | msg_id = atoi(s); 177 | repeat = multicast_message_id_repeat(from, msg_id); 178 | } 179 | if (n == 3) 180 | { 181 | if ((match = hostname_match(from, to)) && !repeat) 182 | { 183 | if (pikrellcam.verbose_multicast) 184 | printf(" message accepted for %s\n", pikrellcam.hostname); 185 | if (sscanf(message, "%31s %255[^\n]", msg_type, action) == 2) 186 | { 187 | dup_string(&pikrellcam.multicast_from_hostname, from); 188 | if (!strcmp(msg_type, "command")) 189 | exec_no_wait(action, NULL, TRUE); 190 | else if (!strcmp(msg_type, "pkc-message")) /* user defined */ 191 | exec_no_wait(pikrellcam.on_multicast_message_cmd, action, TRUE); 192 | /* ack and other message types are ignored */ 193 | } 194 | else 195 | if (pikrellcam.verbose_multicast) 196 | printf(" msg_type action parse fail.\n"); 197 | } 198 | else 199 | if (pikrellcam.verbose_multicast) 200 | printf(" message rejected: %s\n", 201 | !match ? "hostname match failed" : "repeated message"); 202 | if (match && msg_id > 0) 203 | multicast_ack(from, msg_id); 204 | } 205 | line = eol; 206 | } 207 | } 208 | 209 | void 210 | multicast_init(void) 211 | { 212 | struct ip_mreq mreq; 213 | int opt = TRUE; 214 | 215 | if (!pikrellcam.multicast_enable) 216 | return; 217 | 218 | /* Receiving multicast needs a UDP socket which must be reusable. 219 | */ 220 | if ( (fd_recv = socket(AF_INET, SOCK_DGRAM, 0)) >= 0 221 | && setsockopt(fd_recv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) >= 0 222 | ) 223 | { 224 | /* To receive multicast, bind to the multicast group port and 225 | | join the multicast group by adding membership to the group IP. 226 | | (Membership selects the group IP so the bind addr should be ANY). 227 | */ 228 | addr_recv.sin_family = AF_INET; 229 | addr_recv.sin_addr.s_addr = htonl(INADDR_ANY); 230 | addr_recv.sin_port = htons(pikrellcam.multicast_group_port); 231 | 232 | mreq.imr_multiaddr.s_addr = inet_addr(pikrellcam.multicast_group_IP); 233 | mreq.imr_interface.s_addr = htonl(INADDR_ANY); 234 | 235 | if ( bind(fd_recv, (struct sockaddr *) &addr_recv, 236 | sizeof(addr_recv)) < 0 237 | || setsockopt(fd_recv, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, 238 | sizeof(mreq)) < 0 239 | ) 240 | { 241 | close(fd_recv); 242 | fd_recv = -1; 243 | } 244 | } 245 | 246 | /* Sending multicast needs a UDP socket and needs a send address set up 247 | | to send to the multicast group IP and group port number. 248 | */ 249 | fd_send = socket(AF_INET, SOCK_DGRAM, 0); 250 | addr_send.sin_family = AF_INET; 251 | addr_send.sin_addr.s_addr = inet_addr(pikrellcam.multicast_group_IP); 252 | addr_send.sin_port = htons(pikrellcam.multicast_group_port); 253 | 254 | 255 | } 256 | 257 | 258 | -------------------------------------------------------------------------------- /src/sunriset.h: -------------------------------------------------------------------------------- 1 | extern const char* timezone_name; 2 | extern long int timezone_offset; 3 | 4 | #define TMOD(x) ((x)<0?(x)+24:((x)>=24?(x)-24:(x))) 5 | #define DAYSOFF(x) ((x)<0?"(-1) ":((x)>=24?"(+1) ":"")) 6 | 7 | #define HOURS(h) ((int)(floor(h))) 8 | #define MINUTES(h) ((int)(60*(h-floor(h)))) 9 | 10 | #define ABS(x) ((x)<0?-(x):(x)) 11 | 12 | /* A macro to compute the number of days elapsed since 2000 Jan 0.0 */ 13 | /* (which is equal to 1999 Dec 31, 0h UT) */ 14 | /* Dan R sez: This is some pretty fucking high magic. */ 15 | #define days_since_2000_Jan_0(y,m,d) \ 16 | (367L*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530L) 17 | 18 | /* Some conversion factors between radians and degrees */ 19 | 20 | #ifndef PI 21 | #define PI 3.1415926535897932384 22 | #endif 23 | 24 | #define RADEG ( 180.0 / PI ) 25 | #define DEGRAD ( PI / 180.0 ) 26 | 27 | /* The trigonometric functions in degrees */ 28 | 29 | #define sind(x) sin((x)*DEGRAD) 30 | #define cosd(x) cos((x)*DEGRAD) 31 | #define tand(x) tan((x)*DEGRAD) 32 | 33 | #define atand(x) (RADEG*atan(x)) 34 | #define asind(x) (RADEG*asin(x)) 35 | #define acosd(x) (RADEG*acos(x)) 36 | #define atan2d(y,x) (RADEG*atan2(y,x)) 37 | 38 | /* Following are some macros around the "workhorse" function __daylen__ */ 39 | /* They mainly fill in the desired values for the reference altitude */ 40 | /* below the horizon, and also selects whether this altitude should */ 41 | /* refer to the Sun's center or its upper limb. */ 42 | 43 | 44 | /* This macro computes the length of the day, from sunrise to sunset. */ 45 | /* Sunrise/set is considered to occur when the Sun's upper limb is */ 46 | /* 50 arc minutes below the horizon (this accounts for the refraction */ 47 | /* of the Earth's atmosphere). */ 48 | /* The original version of the program used the value of 35 arc mins, */ 49 | /* which is the accepted value in Sweden. */ 50 | #define day_length(year,month,day,lon,lat) \ 51 | __daylen__( year, month, day, lon, lat, -50.0/60.0, 1 ) 52 | 53 | /* This macro computes the length of the day, including civil twilight. */ 54 | /* Civil twilight starts/ends when the Sun's center is 6 degrees below */ 55 | /* the horizon. */ 56 | #define day_civil_twilight_length(year,month,day,lon,lat) \ 57 | __daylen__( year, month, day, lon, lat, -6.0, 0 ) 58 | 59 | /* This macro computes the length of the day, incl. nautical twilight. */ 60 | /* Nautical twilight starts/ends when the Sun's center is 12 degrees */ 61 | /* below the horizon. */ 62 | #define day_nautical_twilight_length(year,month,day,lon,lat) \ 63 | __daylen__( year, month, day, lon, lat, -12.0, 0 ) 64 | 65 | /* This macro computes the length of the day, incl. astronomical twilight. */ 66 | /* Astronomical twilight starts/ends when the Sun's center is 18 degrees */ 67 | /* below the horizon. */ 68 | #define day_astronomical_twilight_length(year,month,day,lon,lat) \ 69 | __daylen__( year, month, day, lon, lat, -18.0, 0 ) 70 | 71 | 72 | /* This macro computes times for sunrise/sunset. */ 73 | /* Sunrise/set is considered to occur when the Sun's upper limb is */ 74 | /* 35 arc minutes below the horizon (this accounts for the refraction */ 75 | /* of the Earth's atmosphere). */ 76 | #define sun_rise_set(year,month,day,lon,lat,rise,set) \ 77 | __sunriset__( year, month, day, lon, lat, -35.0/60.0, 1, rise, set ) 78 | 79 | /* This macro computes the start and end times of civil twilight. */ 80 | /* Civil twilight starts/ends when the Sun's center is 6 degrees below */ 81 | /* the horizon. */ 82 | #define civil_twilight(year,month,day,lon,lat,start,end) \ 83 | __sunriset__( year, month, day, lon, lat, -6.0, 0, start, end ) 84 | 85 | /* This macro computes the start and end times of nautical twilight. */ 86 | /* Nautical twilight starts/ends when the Sun's center is 12 degrees */ 87 | /* below the horizon. */ 88 | #define nautical_twilight(year,month,day,lon,lat,start,end) \ 89 | __sunriset__( year, month, day, lon, lat, -12.0, 0, start, end ) 90 | 91 | /* This macro computes the start and end times of astronomical twilight. */ 92 | /* Astronomical twilight starts/ends when the Sun's center is 18 degrees */ 93 | /* below the horizon. */ 94 | #define astronomical_twilight(year,month,day,lon,lat,start,end) \ 95 | __sunriset__( year, month, day, lon, lat, -18.0, 0, start, end ) 96 | 97 | 98 | /* Function prototypes */ 99 | 100 | double __daylen__( int year, int month, int day, double lon, double lat, 101 | double altit, int upper_limb ); 102 | 103 | int __sunriset__( int year, int month, int day, double lon, double lat, 104 | double altit, int upper_limb, double *rise, double *set ); 105 | 106 | void sunpos( double d, double *lon, double *r ); 107 | 108 | void sun_RA_dec( double d, double *RA, double *dec, double *r ); 109 | 110 | double revolution( double x ); 111 | 112 | double rev180( double x ); 113 | 114 | double GMST0( double d ); 115 | 116 | -------------------------------------------------------------------------------- /src/tcpserver.c: -------------------------------------------------------------------------------- 1 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 | // TCP Stream Server for Pikrellcam 3 | // V1.0 2015-12-07 4 | // Thomas Götz 5 | // 6 | // Goal: 7 | // Live preview of the h264 Full HD Camera Stream 8 | // 9 | // Idea: 10 | // Grab the data being written to circular buffer and send them via tcp server to a listener. 11 | // Maybe there is a better solution, like a further Pipeline which gets the h264 live stream and sends it. 12 | // Maybe also multiple streams possible with different resolution (full, medium, mobile, ...) 13 | // 14 | // use with gst-rtsp-server-1.4.4 on Raspbian Jessie (on Wheezy I didn't get gst-rtsp-server built) or 15 | // gst-variable-rtsp-server (look for gst-gateworks-apps-master) 16 | // and the following pipeline (!! do-timestamp=true is important !!, blocksize=262144 optional, can be lower) 17 | // 18 | // ./gst-variable-rtsp-server -d 99 -p 8555 -m /stream 19 | // -u "(tcpclientsrc port=3000 do-timestamp=true blocksize=262144 20 | // ! video/x-h264,stream-format=byte-stream,profile=high 21 | // ! h264parse ! rtph264pay name=pay0 pt=96 )" 22 | // 23 | // then open the stream, e.g. vlc rtsp://your_pi_addr:8555/stream 24 | // 25 | // delay between live and stream is ~ 1 sec. 26 | // CPU load with 1 active full hd stream is ~10% on a PI2 (4 Cores, 100% load is 400% displayed with 'top') 27 | // 28 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 29 | 30 | 31 | #include "pikrellcam.h" 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #define SERV_PORT 3000 //port 47 | #define LISTENQ 1 //maximum number of client connections 48 | 49 | extern PiKrellCam pikrellcam; 50 | 51 | int listenfd, connfd, num_sent=0; 52 | socklen_t clilen; 53 | struct sockaddr_in cliaddr, servaddr; 54 | long save_fd; 55 | int h264_conn_status=H264_TCP_WAIT_FOR_CONNECT; 56 | 57 | void setup_h264_tcp_server(void) 58 | { 59 | //creation of the socket 60 | listenfd = socket (AF_INET, SOCK_STREAM, 0); 61 | 62 | //preparation of the socket address 63 | servaddr.sin_family = AF_INET; 64 | //servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 65 | servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 66 | servaddr.sin_port = htons(SERV_PORT); 67 | 68 | int reuse = 1; 69 | if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) 70 | { 71 | perror("Server: setsockopt(SO_REUSEADDR) failed\n"); 72 | log_printf("Server: setsockopt(SO_REUSEADDR) failed\n"); 73 | return; 74 | } 75 | 76 | #ifdef SO_REUSEPORT 77 | if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) 78 | { 79 | perror("Server: setsockopt(SO_REUSEPORT) failed\n"); 80 | log_printf("Server: setsockopt(SO_REUSEPORT) failed\n"); 81 | return; 82 | } 83 | #endif 84 | 85 | if(bind (listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))<0) 86 | { 87 | // perror("Server: error binding\n"); 88 | log_printf("Server: error binding\n"); 89 | //return; 90 | } 91 | 92 | listen (listenfd, LISTENQ); 93 | 94 | save_fd = fcntl( listenfd, F_GETFL ); 95 | save_fd |= O_NONBLOCK; 96 | fcntl( listenfd, F_SETFL, save_fd ); 97 | 98 | 99 | // fprintf(stderr,"%s\n","Server running...waiting for connections."); 100 | log_printf("Server running...waiting for connections.\n"); 101 | } 102 | 103 | 104 | void tcp_poll_connect(void) 105 | { 106 | if((h264_conn_status == H264_TCP_WAIT_FOR_CONNECT)) 107 | { 108 | clilen = sizeof(cliaddr); 109 | connfd = accept (listenfd, (struct sockaddr *) &cliaddr, &clilen); 110 | 111 | if (connfd <=0) 112 | { 113 | if (pikrellcam.debug) 114 | printf("Poll: Kein Client am Socket ...\n"); 115 | } 116 | else 117 | { 118 | h264_conn_status=H264_TCP_SEND_HEADER; //must send header 119 | num_sent=0; 120 | // fprintf (stderr, "Server: connect from host %s, port %u.\n", 121 | // inet_ntoa (cliaddr.sin_addr), 122 | // ntohs (cliaddr.sin_port)); 123 | log_printf("Server: connect from host %s, port %u.\n", 124 | inet_ntoa (cliaddr.sin_addr), 125 | ntohs (cliaddr.sin_port)); 126 | } 127 | } 128 | } 129 | 130 | 131 | void tcp_send_h264_header(void *data, int len) 132 | { 133 | if(h264_conn_status != H264_TCP_WAIT_FOR_CONNECT) 134 | { 135 | if(h264_conn_status == H264_TCP_SEND_HEADER) 136 | { 137 | num_sent=send(connfd, data, len, MSG_NOSIGNAL); 138 | h264_conn_status=H264_TCP_SEND_DATA; 139 | if (pikrellcam.debug) 140 | printf("write h264 header:%d \n",len); 141 | 142 | if (num_sent < 0 || num_sent !=len) 143 | { 144 | perror("Server: Client connection closed\n"); 145 | log_printf("Server: Client connection closed\n"); 146 | shutdown(connfd, SHUT_RDWR); 147 | close(connfd); 148 | h264_conn_status=H264_TCP_WAIT_FOR_CONNECT; 149 | } 150 | } 151 | } 152 | } 153 | 154 | void tcp_send_h264_data(char * what, void *data, int len) 155 | { 156 | if(h264_conn_status != H264_TCP_WAIT_FOR_CONNECT) 157 | { 158 | if(h264_conn_status == H264_TCP_SEND_DATA) 159 | { 160 | num_sent=send(connfd, data,len, MSG_NOSIGNAL); 161 | if (pikrellcam.debug) 162 | printf("write tcp %s:%d \n",what, len); 163 | if (num_sent < 0 || num_sent !=len) 164 | { 165 | perror("Server: Client connection closed\n"); 166 | log_printf("Server: Client connection closed\n"); 167 | close(connfd); 168 | h264_conn_status=H264_TCP_WAIT_FOR_CONNECT; 169 | return; 170 | } 171 | } 172 | 173 | } 174 | } 175 | 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/tcpserver_mjpeg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MJPEG TCP server 3 | * Brijesh Singh 4 | * 5 | * Live preview of the MJPEG Camera Stream. The stream can be viewed directly on browser 6 | * by accessing http://:8081/mjpeg_live.php or Andriod MJPEG viewer apps 7 | * (tinycam monitor etc). 8 | * 9 | * Eventhough the server is designed to work with single producer and consumer mind but we do 10 | * not refuse the connection from more than one clients. 11 | */ 12 | 13 | #include "pikrellcam.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define MAX_WIDTH 1920 25 | #define MAX_HEIGHT 1080 26 | #define MAX_IMAGE_SIZE (MAX_WIDTH * MAX_HEIGHT * 1.5) /* worst case compression */ 27 | #define SERVER_PORT 9999 28 | #define MAX_BUF_SIZE 1024 29 | #define NUM_CIRC_BUFS 2 30 | 31 | struct buffer { 32 | int len; 33 | char *data; 34 | }; 35 | 36 | struct client_info { 37 | int fd; 38 | struct sockaddr_in sockaddr; 39 | }; 40 | 41 | static int server_fd; 42 | static int head, tail; 43 | static pthread_t handler_tid; 44 | static struct buffer buffers[NUM_CIRC_BUFS]; 45 | static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 46 | static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 47 | 48 | static int new_connection_log_count; 49 | 50 | /* returned a new buffer, after use the buffer must be free'd with image_buffer_free() */ 51 | static struct buffer* client_queue_get() 52 | { 53 | struct buffer *buf; 54 | 55 | buf = calloc(1, sizeof(*buf)); 56 | if (!buf) 57 | return NULL; 58 | 59 | buf->data = malloc(MAX_IMAGE_SIZE); 60 | if (!buf->data) { 61 | free(buf); 62 | return NULL; 63 | } 64 | 65 | pthread_mutex_lock(&mutex); 66 | if (head == tail) /* queue is empty */ 67 | pthread_cond_wait(&cond, &mutex); 68 | buf->len = buffers[head].len; 69 | memcpy(buf->data, buffers[head].data, buf->len); 70 | head = (head + 1) % NUM_CIRC_BUFS; 71 | pthread_mutex_unlock(&mutex); 72 | 73 | return buf; 74 | } 75 | 76 | static void image_buffer_free(struct buffer *buf) 77 | { 78 | if (buf) { 79 | free(buf->data); 80 | free(buf); 81 | } 82 | } 83 | 84 | static void* handle_client(void *args) 85 | { 86 | char *data = NULL; 87 | int i, fd; 88 | struct client_info *client = args; 89 | char header[MAX_BUF_SIZE]; 90 | struct buffer *buf = NULL; 91 | 92 | if (++new_connection_log_count < 30) /* punt - FIXME */ 93 | log_printf("new connection from host '%s' on port '%d'\n", 94 | inet_ntoa(client->sockaddr.sin_addr), 95 | ntohs(client->sockaddr.sin_port)); 96 | 97 | /* We had delayed the circular buffer allocation until atleast one client is 98 | * connected to the socket. let allocate the buffer now */ 99 | for (i = 0; i < NUM_CIRC_BUFS; i++) { 100 | /* if the buffer is already allocated by some other client then share it */ 101 | if (!buffers[i].data) { 102 | buffers[i].data = malloc(MAX_IMAGE_SIZE); 103 | if (!buffers[i].data) { 104 | log_printf("failed to allocate memory %s\n", strerror(errno)); 105 | goto failed; 106 | } 107 | } 108 | } 109 | 110 | fd = client->fd; 111 | 112 | while(1) { 113 | buf = client_queue_get(); 114 | if (!buf) 115 | goto failed; 116 | 117 | /* send JPEG boundary start header */ 118 | memset(header, '\0', MAX_BUF_SIZE); 119 | snprintf(header, MAX_BUF_SIZE, 120 | "\r\n--fooboundary\r\n" 121 | "Content-type: image/jpeg\r\n\r\n"); 122 | if (send(fd, header, strlen(header), MSG_NOSIGNAL) < 0) 123 | goto failed; 124 | 125 | /* send image contents */ 126 | if (send(fd, buf->data, buf->len, MSG_NOSIGNAL) < 0) 127 | goto failed; 128 | 129 | /* we are done with the image buffer */ 130 | image_buffer_free(buf); 131 | } 132 | failed: 133 | if (new_connection_log_count < 30) /* punt - FIXME */ 134 | log_printf("closing connection from host '%s' on port '%d'\n", 135 | inet_ntoa(client->sockaddr.sin_addr), 136 | ntohs(client->sockaddr.sin_port)); 137 | 138 | image_buffer_free(buf); 139 | 140 | if (data) 141 | free(data); 142 | close(client->fd); 143 | free(client); 144 | pthread_detach(pthread_self()); 145 | return NULL; 146 | } 147 | 148 | static void add_new_connection (int server_fd) 149 | { 150 | int fd; 151 | pthread_t tid; 152 | socklen_t size; 153 | struct sockaddr_in client; 154 | struct client_info *c; 155 | 156 | size = sizeof(client); 157 | fd = accept(server_fd, (struct sockaddr*)&client, &size); 158 | if (fd < 0) { 159 | log_printf("failed to accept"); 160 | return; 161 | } 162 | 163 | c = calloc(1, sizeof(*c)); 164 | if (!c) 165 | return; 166 | 167 | memcpy(&c->sockaddr, &client, sizeof(struct sockaddr_in)); 168 | c->fd = fd; 169 | pthread_create(&tid, NULL, handle_client, (void*)c); 170 | 171 | return; 172 | } 173 | 174 | static int create_sock (int port) 175 | { 176 | struct sockaddr_in server; 177 | 178 | server_fd = socket(AF_INET, SOCK_STREAM, 0); 179 | if (server_fd < 0) { 180 | log_printf("socket create failed: %s\n", strerror(errno)); 181 | return 1; 182 | } 183 | 184 | server.sin_family = AF_INET; 185 | server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 186 | server.sin_port = htons(port); 187 | 188 | if (bind(server_fd, (struct sockaddr*) &server, sizeof(server)) < 0) { 189 | log_printf("socket bind failed: %s\n", strerror(errno)); 190 | return 1; 191 | } 192 | 193 | if (listen(server_fd, 3) < 0) { 194 | log_printf("socket listen failed: %s\n", strerror(errno)); 195 | return 1; 196 | } 197 | 198 | log_printf("MJPEG server is listening on port '%d'\n", port); 199 | return 0; 200 | } 201 | 202 | static void* handler(void *unused) 203 | { 204 | int i; 205 | fd_set active_fd_set, read_fd_set; 206 | 207 | FD_ZERO(&active_fd_set); 208 | FD_SET(server_fd, &active_fd_set); 209 | 210 | while(1) { 211 | /* block until input arrives on one or more active sockets */ 212 | read_fd_set = active_fd_set; 213 | if (select(FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) { 214 | log_printf("select failure: %s\n", strerror(errno) ); 215 | goto failed; 216 | } 217 | 218 | /* service all the socket with input pending */ 219 | for(i=0; i < FD_SETSIZE; ++i) { 220 | if (FD_ISSET(i, &read_fd_set)) { 221 | if (i == server_fd) 222 | add_new_connection(i); 223 | } 224 | } 225 | } 226 | 227 | failed: 228 | pthread_detach(pthread_self()); 229 | return NULL; 230 | } 231 | 232 | char* mjpeg_server_queue_get() 233 | { 234 | return buffers[tail].data; 235 | } 236 | 237 | void mjpeg_server_queue_put(char *data, int len) 238 | { 239 | pthread_mutex_lock(&mutex); 240 | buffers[tail].len = len; 241 | tail = (tail + 1) % NUM_CIRC_BUFS; 242 | if (tail == head) /* queue is full */ 243 | head = head + 1; 244 | if (head >= NUM_CIRC_BUFS) /* if head reach to end of queue then reset it */ 245 | head = 0; 246 | pthread_cond_broadcast(&cond); 247 | pthread_mutex_unlock(&mutex); 248 | } 249 | 250 | int setup_mjpeg_tcp_server() 251 | { 252 | /* create a server socket */ 253 | if (create_sock(SERVER_PORT)) 254 | return 1; 255 | 256 | /* spwan a thread to accept connections */ 257 | pthread_create(&handler_tid, NULL, handler, NULL); 258 | 259 | return 0; 260 | } 261 | 262 | -------------------------------------------------------------------------------- /www/archive.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | "; 57 | 58 | $month_dir = str_pad($month, 2, "0", STR_PAD_LEFT); 59 | if (count(glob("$archive_root/$year/$month_dir/*")) == 0) 60 | $month_html .= "$month_name"; 61 | else 62 | $month_html .= " 63 | 65 | $month_name"; 66 | 67 | foreach ($weekday_names as $weekday) 68 | $month_html .= "
$weekday
"; 69 | $month_html .= "
  
"; 70 | 71 | if ($day_of_week > 0) // 1st week initial blank days 72 | $month_html .= " "; 73 | 74 | $n_week_links = 0; 75 | for ($day = 1; $day <= $month_days; $day++) 76 | { 77 | $day_dir = str_pad($day, 2, "0", STR_PAD_LEFT); 78 | $archive_path = "$archive_root/$year/$month_dir/$day_dir"; 79 | 80 | if (count(glob("$archive_path/videos/*")) == 0 && count(glob("$archive_path/stills/*")) == 0) 81 | $day_displayed = $day; 82 | else 83 | { 84 | $label = "$month_label $day"; 85 | $day_displayed = "$day"; 87 | $n_week_links++; 88 | } 89 | if ($day == $today_day && $month == $today_month && $year == $today_year) 90 | $month_html .= "$day_displayed"; 91 | else 92 | $month_html .= "$day_displayed"; 93 | 94 | if ($day_of_week++ == 6) 95 | { 96 | $label = "Week $week_number"; 97 | if ($n_week_links > 0) 98 | $month_html .= " 99 | 101 |
$week_number
"; 102 | else 103 | $month_html .= "
$week_number
"; 104 | if ($day < $month_days) 105 | { 106 | $week_d0 = $day + 1; 107 | $week_m0 = $month; 108 | $month_html .= ""; 109 | } 110 | else 111 | { 112 | $week_d0 = 1; 113 | $week_m0 = $month + 1; 114 | } 115 | $week_number += 1; 116 | $n_week_links = 0; 117 | } 118 | $day_of_week %= 7; 119 | } 120 | 121 | if ($day_of_week > 0) 122 | { 123 | $blank_days = 7 - $day_of_week; 124 | $month_html .= " "; 125 | if ($n_week_links > 0) 126 | { 127 | if ($month < 12) 128 | { 129 | $d1 = $blank_days; 130 | $m1 = $month + 1; 131 | } 132 | else 133 | { 134 | $d1 = 31; 135 | $m1 = 12; 136 | } 137 | $month_html .= " 138 | 140 |
$week_number
"; 141 | } 142 | else 143 | $month_html .= "
$week_number
"; 144 | } 145 | $month_html .= ""; 146 | return $month_html; 147 | } 148 | 149 | // ini_set('display_errors',1); 150 | // ini_set('display_startup_errors',1); 151 | // error_reporting(-1); 152 | 153 | $title = TITLE_STRING; 154 | $header = ""; 155 | $header .= ""; 156 | $header .= "$title Calendar"; 157 | $header .= ""; 158 | $header .= ""; 159 | $header .= ""; 160 | $header .= "
"; 161 | $header .= "
"; 163 | echo $header; 164 | 165 | if (isset($_GET["year"])) 166 | $year = $_GET["year"]; 167 | else 168 | $year = date('Y'); 169 | 170 | if (isset($_GET["view"])) 171 | { 172 | $archive_initial_view = $_GET["view"]; 173 | config_user_save(); 174 | // echo ""; 175 | // exit(0); 176 | } 177 | 178 | if (isset($_GET["next"])) 179 | $year += 1; 180 | if (isset($_GET["prev"])) 181 | $year -= 1; 182 | if (isset($_GET["today"])) 183 | $year = date('Y'); 184 | 185 | echo "
"; 186 | echo ""; 189 | // echo " 190 | // $year"; 191 | echo " 193 | $year"; 194 | 195 | echo ""; 198 | echo "
"; 199 | 200 | echo "
"; 201 | echo ""; 202 | for ($i = 0; $i < 12; $i++) 203 | { 204 | if (($i % 3) == 0) 205 | echo ""; 206 | echo ""; 209 | if (($i % 3) == 2) 210 | echo ""; 211 | } 212 | echo "
"; 207 | echo build_month_html($year, $i + 1, $archive_initial_view); 208 | echo "
"; 213 | echo "
"; 214 | 215 | echo "
"; 216 | $title = TITLE_STRING; 217 | echo " 219 | $title
"; 220 | 221 | $archive_root = ARCHIVE_DIR; 222 | $fs_type = exec("stat -f -L -c %T $archive_root"); 223 | 224 | if ("$fs_type" == "nfs") 225 | $arch_type = "NFS"; 226 | else if (strpos($fs_type, 'Stale') !== false) 227 | $arch_type = "Stale"; 228 | else 229 | $arch_type = ""; 230 | 231 | $dir = exec("readlink -f $archive_root"); 232 | 233 | if ("$arch_type" != "") 234 | { 235 | $nfs_dir = exec("(df | grep $dir | cut -d \" \" -f 1)"); 236 | $arch_label = "$nfs_dir   $arch_type mounted->   $dir"; 237 | } 238 | else 239 | $arch_label = $dir; 240 | 241 | if ("$archive_initial_view" == "videos") 242 | $next_view = "thumbs"; 243 | else if ("$archive_initial_view" == "thumbs") 244 | $next_view = "stills"; 245 | else if ("$archive_initial_view" == "stills") 246 | $next_view = "videos"; 247 | else 248 | $next_view = "videos"; 249 | 250 | echo " 251 | Initial view: 252 | $archive_initial_view"; 253 | 254 | echo " 257 | $arch_label"; 258 | 259 | 260 | echo "
"; 261 | echo ""; 262 | ?> 263 | -------------------------------------------------------------------------------- /www/audio_stream.php: -------------------------------------------------------------------------------- 1 | 34 | 35 | -------------------------------------------------------------------------------- /www/config-defaults.php: -------------------------------------------------------------------------------- 1 | \n"); 110 | fclose($file); 111 | chmod("config-user.php", 0666); 112 | } 113 | 114 | if (file_exists("config-media.php")) 115 | { 116 | include_once(dirname(__FILE__) . '/config-media.php'); 117 | unlink("config-media.php"); 118 | } 119 | 120 | if (defined('N_COLUMNS')) 121 | $n_columns = N_COLUMNS; 122 | if (defined('NAME_STYLE')) 123 | $name_style = NAME_STYLE; 124 | 125 | //echo ""; 126 | 127 | if (defined('N_VIDEO_SCROLL_PIXELS')) 128 | $n_video_scroll_pixels = N_VIDEO_SCROLL_PIXELS; 129 | if (defined('N_STILL_SCROLL_PIXELS')) 130 | $n_still_scroll_pixels = N_STILL_SCROLL_PIXELS; 131 | 132 | if (defined('DEFAULT_TEXT_COLOR')) 133 | $default_text_color = DEFAULT_TEXT_COLOR; 134 | if (defined('SELECTED_TEXT_COLOR')) 135 | $selected_text_color = SELECTED_TEXT_COLOR; 136 | if (defined('MEDIA_TEXT_COLOR')) 137 | $media_text_color = MEDIA_TEXT_COLOR; 138 | if (defined('MANUAL_VIDEO_TEXT_COLOR')) 139 | $manual_video_text_color = MANUAL_VIDEO_TEXT_COLOR; 140 | 141 | if (defined('VIDEOS_MODE')) 142 | $videos_mode = VIDEOS_MODE; 143 | if (defined('STILLS_MODE')) 144 | $stills_mode = STILLS_MODE; 145 | if (defined('LOOP_MODE')) 146 | $loop_mode = LOOP_MODE; 147 | if (defined('ARCHIVE_INITIAL_VIEW')) 148 | $archive_initial_view = ARCHIVE_INITIAL_VIEW; 149 | if (defined('ARCHIVE_THUMBS_SCROLLED')) 150 | $archive_thumbs_scrolled = ARCHIVE_THUMBS_SCROLLED; 151 | if (defined('MEDIA_THUMBS_SCROLLED')) 152 | $media_thumbs_scrolled = MEDIA_THUMBS_SCROLLED; 153 | if (defined('LOOP_THUMBS_SCROLLED')) 154 | $loop_thumbs_scrolled = LOOP_THUMBS_SCROLLED; 155 | if (defined('N_THUMB_SCROLL_PIXELS')) 156 | $n_thumb_scroll_pixels = N_THUMB_SCROLL_PIXELS; 157 | 158 | if (defined('N_LOG_SCROLL_PIXELS')) 159 | $n_log_scroll_pixels = N_LOG_SCROLL_PIXELS; 160 | if (defined('LOG_TEXT_COLOR')) 161 | $log_text_color = LOG_TEXT_COLOR; 162 | 163 | if (defined('BACKGROUND_IMAGE')) 164 | $background_image = BACKGROUND_IMAGE; 165 | 166 | if (defined('VIDEO_URL')) 167 | $video_url = VIDEO_URL; 168 | 169 | if (defined('SHOW_AUDIO_CONTROLS')) 170 | $show_audio_controls = SHOW_AUDIO_CONTROLS; 171 | 172 | if (defined('INCLUDE_CONTROL')) 173 | $include_control = INCLUDE_CONTROL; 174 | 175 | if (defined('CONFIG_EVENT_COUNT')) 176 | $old_event_count = CONFIG_EVENT_COUNT; 177 | else 178 | $old_event_count = $config_event_count; 179 | 180 | if (!file_exists("config-user.php") || $old_event_count != $config_event_count) 181 | config_user_save(); 182 | 183 | ?> 184 | -------------------------------------------------------------------------------- /www/config.php: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /www/control.php: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | "; 16 | 17 | ?> 18 | -------------------------------------------------------------------------------- /www/download.php: -------------------------------------------------------------------------------- 1 | 4 | 34 | -------------------------------------------------------------------------------- /www/fifo_command.php: -------------------------------------------------------------------------------- 1 | 4 | 9 | -------------------------------------------------------------------------------- /www/images/architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/architecture.jpg -------------------------------------------------------------------------------- /www/images/arrow-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow-down.png -------------------------------------------------------------------------------- /www/images/arrow-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow-left.png -------------------------------------------------------------------------------- /www/images/arrow-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow-right.png -------------------------------------------------------------------------------- /www/images/arrow-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow-up.png -------------------------------------------------------------------------------- /www/images/arrow0-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow0-down.png -------------------------------------------------------------------------------- /www/images/arrow0-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow0-left.png -------------------------------------------------------------------------------- /www/images/arrow0-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow0-right.png -------------------------------------------------------------------------------- /www/images/arrow0-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow0-up.png -------------------------------------------------------------------------------- /www/images/arrow2-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow2-down.png -------------------------------------------------------------------------------- /www/images/arrow2-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow2-left.png -------------------------------------------------------------------------------- /www/images/arrow2-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow2-right.png -------------------------------------------------------------------------------- /www/images/arrow2-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/arrow2-up.png -------------------------------------------------------------------------------- /www/images/audio-blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/audio-blank.png -------------------------------------------------------------------------------- /www/images/audio-controls.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/audio-controls.jpg -------------------------------------------------------------------------------- /www/images/audio-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/audio-play.png -------------------------------------------------------------------------------- /www/images/audio-stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/audio-stop.png -------------------------------------------------------------------------------- /www/images/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/blank.png -------------------------------------------------------------------------------- /www/images/camera-error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/camera-error.jpg -------------------------------------------------------------------------------- /www/images/cpu-usage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/cpu-usage.jpg -------------------------------------------------------------------------------- /www/images/electret.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/electret.jpg -------------------------------------------------------------------------------- /www/images/icon-close-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/icon-close-open.png -------------------------------------------------------------------------------- /www/images/loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/loop.png -------------------------------------------------------------------------------- /www/images/mic-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/mic-down.png -------------------------------------------------------------------------------- /www/images/mic-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/mic-up.png -------------------------------------------------------------------------------- /www/images/mic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/mic.png -------------------------------------------------------------------------------- /www/images/motion-regions.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/motion-regions.jpg -------------------------------------------------------------------------------- /www/images/nfs-mounting.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/nfs-mounting.jpg -------------------------------------------------------------------------------- /www/images/paper1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/paper1.png -------------------------------------------------------------------------------- /www/images/passion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/passion.jpg -------------------------------------------------------------------------------- /www/images/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/pause.png -------------------------------------------------------------------------------- /www/images/preset-servos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/preset-servos.jpg -------------------------------------------------------------------------------- /www/images/record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/record.png -------------------------------------------------------------------------------- /www/images/setup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/setup.jpg -------------------------------------------------------------------------------- /www/images/shadow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/shadow.jpg -------------------------------------------------------------------------------- /www/images/shutter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/shutter.png -------------------------------------------------------------------------------- /www/images/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/stop.png -------------------------------------------------------------------------------- /www/images/vector0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billw2/pikrellcam/994384dae9d01d6210518ce6515ecd011b32d38f/www/images/vector0.jpg -------------------------------------------------------------------------------- /www/js-css/expandable-panels.css: -------------------------------------------------------------------------------- 1 | 2 | /* --------- COLLAPSIBLE PANELS ----------*/ 3 | 4 | h3, p, ol, ul, li { 5 | margin:0px; 6 | padding:0px; 7 | font-size:0.85em; 8 | // font-family:Arial, Helvetica, sans-serif; 9 | font-family:Serif, Helvetica, sans-serif; 10 | } 11 | ol, ul { 12 | padding:3px 0 10px 22px; 13 | } 14 | li { 15 | padding:0 0 4px 0; 16 | } 17 | hr { 18 | border:none; 19 | height:1px; 20 | border-top:1px dashed #999; 21 | } 22 | #container { 23 | width:95%; 24 | margin:auto; 25 | margin-top:10px; 26 | } 27 | 28 | .expandable-panel { 29 | width:100%; 30 | position:relative; 31 | min-height:34px; 32 | overflow:auto; 33 | margin-bottom: 3px; 34 | border:1px solid #999; 35 | } 36 | .expandable-panel-heading { 37 | width:100%; 38 | cursor:pointer; 39 | min-height:34px; 40 | clear:both; 41 | background-color:#c8c8c8; 42 | position:relative; 43 | } 44 | .expandable-panel-heading:hover { 45 | color:#666; 46 | } 47 | .expandable-panel-heading h3 { 48 | padding:8px 10px 6px 15px; 49 | font-size:1.2em; 50 | line-height:20px; 51 | } 52 | .expandable-panel-content { 53 | padding:8px 15px 0 15px; 54 | margin-top:-999px; 55 | } 56 | .expandable-panel-content p { 57 | padding:4px 0 6px 0; 58 | } 59 | .expandable-panel-content p:first-child { 60 | padding-top:10px; 61 | } 62 | .expandable-panel-content p:last-child { 63 | padding-bottom:15px; 64 | } 65 | .icon-close-open { 66 | width:20px; 67 | height:20px; 68 | position:absolute; 69 | background-image:url(../images/icon-close-open.png); 70 | right:15px; 71 | } 72 | 73 | .expandable-panel-content img { 74 | float:right; 75 | padding-left:12px; 76 | /* clear:both; */ 77 | } 78 | .header-active { 79 | background-color:#c4c8c8; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /www/js-css/expandable-panels.js: -------------------------------------------------------------------------------- 1 | 2 | (function($) { 3 | $(document).ready(function () { 4 | /* ----------------------------- EXPANDABLE PANELS ------------------------------------ */ 5 | /* From: http://www.webdevdoor.com/jquery/expandable-collapsible-panels-jquery/ */ 6 | /* jquery.min.js from: http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js */ 7 | /* ------------------------------------------------------------------------------------ */ 8 | 9 | var panelspeed = 500; //panel animate speed in milliseconds 10 | var totalpanels = 3; //total number of collapsible panels 11 | var defaultopenpanel = 0; //leave 0 for no panel open 12 | var accordian = true; //set panels to behave like an accordian, with one panel only ever open at once 13 | 14 | var panelheight = new Array(); 15 | var currentpanel = defaultopenpanel; 16 | var iconheight = parseInt($('.icon-close-open').css('height')); 17 | var highlightopen = true; 18 | 19 | //Initialise collapsible panels 20 | function panelinit() { 21 | for (var i=1; i<=totalpanels; i++) { 22 | panelheight[i] = parseInt($('#cp-'+i).find('.expandable-panel-content').css('height')); 23 | $('#cp-'+i).find('.expandable-panel-content').css('margin-top', -panelheight[i]); 24 | if (defaultopenpanel == i) { 25 | $('#cp-'+i).find('.icon-close-open').css('background-position', '0px -'+iconheight+'px'); 26 | $('#cp-'+i).find('.expandable-panel-content').css('margin-top', 0); 27 | } 28 | } 29 | } 30 | 31 | $('.expandable-panel-heading').click(function() { 32 | var obj = $(this).next(); 33 | var objid = parseInt($(this).parent().attr('ID').substr(3,2)); 34 | currentpanel = objid; 35 | if (accordian == true) { 36 | resetpanels(); 37 | } 38 | 39 | if (parseInt(obj.css('margin-top')) <= (panelheight[objid]*-1)) { 40 | obj.clearQueue(); 41 | obj.stop(); 42 | obj.prev().find('.icon-close-open').css('background-position', '0px -'+iconheight+'px'); 43 | obj.animate({'margin-top':0}, panelspeed); 44 | if (highlightopen == true) { 45 | $('#cp-'+currentpanel + ' .expandable-panel-heading').addClass('header-active'); 46 | } 47 | } else { 48 | obj.clearQueue(); 49 | obj.stop(); 50 | obj.prev().find('.icon-close-open').css('background-position', '0px 0px'); 51 | obj.animate({'margin-top':(panelheight[objid]*-1)}, panelspeed); 52 | if (highlightopen == true) { 53 | $('#cp-'+currentpanel + ' .expandable-panel-heading').removeClass('header-active'); 54 | } 55 | } 56 | }); 57 | 58 | function resetpanels() { 59 | for (var i=1; i<=totalpanels; i++) { 60 | if (currentpanel != i) { 61 | $('#cp-'+i).find('.icon-close-open').css('background-position', '0px 0px'); 62 | $('#cp-'+i).find('.expandable-panel-content').animate({'margin-top':-panelheight[i]}, panelspeed); 63 | if (highlightopen == true) { 64 | $('#cp-'+i + ' .expandable-panel-heading').removeClass('header-active'); 65 | } 66 | } 67 | } 68 | } 69 | 70 | 71 | $(window).load(function() { 72 | panelinit(); 73 | }); //END LOAD 74 | }); //END READY 75 | })(jQuery); 76 | 77 | -------------------------------------------------------------------------------- /www/js-css/pikrellcam.css: -------------------------------------------------------------------------------- 1 | 2 | * { 3 | box-sizing: border-box; 4 | } 5 | 6 | table 7 | { 8 | border-collapse: collapse; 9 | border-spacing: 0; 10 | } 11 | 12 | td, 13 | th { 14 | padding: 0; 15 | } 16 | 17 | body 18 | { 19 | font-size: 0.9em; 20 | } 21 | 22 | .text-center 23 | { 24 | text-align: center; 25 | } 26 | 27 | .top-margin 28 | { 29 | margin-top: 6px; 30 | } 31 | 32 | selected 33 | { 34 | text-shadow: -1px -1px 0px #fff, 1px 1px 0px #000; 35 | color: #706468; 36 | opacity: 0.85; 37 | font-size: 1.2em; 38 | // font-style: bold; 39 | // color:#690300; 40 | } 41 | 42 | .text-shadow 43 | { 44 | text-shadow: -1px -1px 0px #fff, 1px 1px 0px #000; 45 | // color: #fce4c8; 46 | // color: #8c7478; 47 | color: #706468; 48 | opacity: 0.85; 49 | } 50 | 51 | 52 | .text-shadow-large 53 | { 54 | text-shadow: -1px -1px 0px #fff, 1px 1px 0px #000; 55 | // color: #fce4c8; 56 | // color: #8c7478; 57 | color: #706468; 58 | opacity: 0.85; 59 | font-size: 1.5em; 60 | margin-bottom: 6px; 61 | } 62 | 63 | .text-shadow-large:hover 64 | { 65 | text-shadow: -1px -1px 0px #fff, 1px 1px 0px #000; 66 | // color: #ccb498; 67 | // color: #8c7458; 68 | color: #908080; 69 | opacity: 0.85; 70 | font-size: 1.5em; 71 | margin-bottom: 6px; 72 | } 73 | 74 | 75 | .btn-menu 76 | { 77 | -webkit-border-radius: 5; 78 | -moz-border-radius: 5; 79 | border-radius: 5px; 80 | // font-family: Arial; 81 | font-family: Serif; 82 | color: #000000; 83 | font-size: 0.9em; 84 | // background: #c1c6d1; 85 | background: #c7c7c7; 86 | padding: 3px 6px 3px 6px; 87 | border: solid #1f628d 1px; 88 | text-decoration: none; 89 | } 90 | 91 | .btn-menu:hover 92 | { 93 | background: #d4d4d4; 94 | text-decoration: none; 95 | } 96 | 97 | 98 | .btn-control 99 | { 100 | -webkit-border-radius: 5; 101 | -moz-border-radius: 5; 102 | border-radius: 7px; 103 | // font-family: Arial; 104 | font-family: Serif; 105 | color: #000000; 106 | font-size: 0.9em; 107 | // background: #9daaad; 108 | background: #bac1c2; 109 | padding: 3px 6px 3px 6px; 110 | border: solid #1f628d 1px; 111 | text-decoration: none; 112 | } 113 | 114 | .btn-control:hover 115 | { 116 | // background: #aab7be; 117 | background: #c4cacb; 118 | text-decoration: none; 119 | } 120 | 121 | .motion-control 122 | { 123 | color: #315b71; 124 | } 125 | 126 | .snap-control 127 | { 128 | color: #31715b; 129 | } 130 | 131 | .alert-control 132 | { 133 | background: #b8a5a5; 134 | } 135 | 136 | .alert-control:hover 137 | { 138 | background: #cfc0c3; 139 | } 140 | 141 | /* centers table */ 142 | .table-container 143 | { 144 | margin: 0 auto; 145 | } 146 | 147 | .table-container td 148 | { 149 | border: 1px solid black; 150 | padding: 4px; 151 | } 152 | 153 | .expanded-image 154 | { 155 | width: 100%; 156 | z-index: 100; 157 | } 158 | 159 | .small-font 160 | { 161 | font-size: 0.88em; 162 | } 163 | 164 | .small-checkbox 165 | { 166 | zoom: 0.75; 167 | transform: scale(0.75); 168 | -ms-transform: scale(0.75); 169 | -webkit-transform: scale(0.75); 170 | -o-transform: scale(0.75); 171 | -moz-transform: scale(0.75); 172 | transform-origin: 0 0; 173 | -ms-transform-origin: 0 0; 174 | -webkit-transform-origin: 0 0; 175 | -o-transform-origin: 0 0; 176 | -moz-transform-origin: 0 0; 177 | } 178 | -------------------------------------------------------------------------------- /www/js-css/pikrellcam.js: -------------------------------------------------------------------------------- 1 | var move_mode = "move_coarse"; 2 | 3 | function move_region_mode($cb) 4 | { 5 | if ($cb.checked) 6 | move_mode = "move_coarse"; 7 | else 8 | move_mode = "move_fine"; 9 | } 10 | 11 | function move_region(where) 12 | { 13 | // alert("motion " + move_mode + " " + where); 14 | fifo_command("motion " + move_mode + " " + where); 15 | } 16 | 17 | function tl_start() 18 | { 19 | var period; 20 | 21 | period = document.getElementById('tl_period').value; 22 | fifo_command("tl_start " + period); 23 | } 24 | 25 | 26 | function image_expand_toggle() 27 | { 28 | var mjpeg_img = document.getElementById("mjpeg_image"); 29 | 30 | mjpeg_img.classList.toggle("expanded-image"); 31 | } 32 | 33 | function new_region() 34 | { 35 | fifo_command("motion new_region 0.3 0.3 0.3 0.3"); 36 | // alert("Two consecutive fifo_command() not working."); 37 | // fifo_command("motion select_region last\n"); 38 | } 39 | 40 | function list_regions() 41 | { 42 | fifo_command("motion list_regions"); 43 | } 44 | 45 | function load_regions() 46 | { 47 | fifo_command('motion load_regions_show ' + document.getElementById('load_regions').value); 48 | document.getElementById('load_regions').value = ""; 49 | } 50 | 51 | function save_regions() 52 | { 53 | fifo_command('motion save_regions ' + document.getElementById('save_regions').value); 54 | document.getElementById('save_regions').value = ""; 55 | } 56 | 57 | 58 | var mjpeg; 59 | 60 | function mjpeg_read() 61 | { 62 | setTimeout("mjpeg.src = 'mjpeg_read.php?time=' + new Date().getTime();", 150); 63 | } 64 | 65 | function mjpeg_start() 66 | { 67 | mjpeg = document.getElementById("mjpeg_image"); 68 | mjpeg.onload = mjpeg_read; 69 | mjpeg.onerror = mjpeg_read; 70 | mjpeg_read(); 71 | } 72 | 73 | 74 | function audio_play() 75 | { 76 | var audio_file = document.getElementById("audio_fifo"); 77 | 78 | audio_file.src = document.getElementById("audio_fifo").src; 79 | audio_file.play(); 80 | fifo_command("audio stream_open"); 81 | } 82 | 83 | function audio_stop() 84 | { 85 | var audio_file = document.getElementById("audio_fifo"); 86 | 87 | fifo_command("audio stream_close"); 88 | audio_fifo.pause(); 89 | audio_fifo.currentTime = 0; 90 | } 91 | 92 | 93 | 94 | function create_XMLHttpRequest() 95 | { 96 | if (window.XMLHttpRequest) 97 | return new XMLHttpRequest(); // IE7+, Firefox, Chrome, Opera, Safari 98 | else 99 | return new ActiveXObject("Microsoft.XMLHTTP"); // IE6, IE5 100 | } 101 | 102 | 103 | var sys_cmd = create_XMLHttpRequest(); 104 | 105 | function pikrellcam(start_stop) 106 | { 107 | sys_cmd.open("PUT", "sys_command.php?cmd=pikrellcam_" + start_stop, true); 108 | sys_cmd.send(); 109 | } 110 | 111 | 112 | var fifo_cmd = create_XMLHttpRequest(); 113 | 114 | function fifo_command (cmd) 115 | { 116 | fifo_cmd.open("PUT", "fifo_command.php?cmd=" + cmd, true); 117 | fifo_cmd.send(); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /www/live.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | <?php echo TITLE_STRING; ?> 11 | 12 | 13 | 14 | 15 | 16 | 35 | 36 | 37 | 40 | 41 | 42 | "; 44 | ?> 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /www/log.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | 15 | 16 | "; 32 | while (($line = fgets($file, 1024)) !== false) 33 | echo "$line"; 34 | fclose($file); 35 | echo ""; 36 | } 37 | } 38 | ?> 39 | 40 | 41 | 42 | 43 | 44 | PiKrellCam Log 45 | 46 | 47 | 48 | "; 50 | echo "
"; 51 | //ini_set('display_errors',1); 52 | //ini_set('display_startup_errors',1); 53 | //error_reporting(-1); 54 | 55 | echo "
"; 56 | $title = TITLE_STRING; 57 | 58 | echo "
"; 59 | echo ""; 62 | echo "$title";; 63 | echo ""; 64 | echo "
"; 65 | echo "
"; 66 | 67 | if (isset($_GET["delete_log"])) 68 | { 69 | $fifo = fopen(FIFO_FILE,"w"); 70 | fwrite($fifo, "delete_log"); 71 | fclose($fifo); 72 | sleep(1); 73 | // www-data cannot delete a pi /tmp file even if group write permission! 74 | // unlink("$log_file"); 75 | } 76 | 77 | $div_style = "overflow-y: scroll; height:${n_log_scroll_pixels}px; overflow-x: auto; border:4px groove silver"; 78 | echo "
"; 79 | echo "
"; 80 | // echo "
"; 81 | // echo '
'; 82 | 83 | dump_log(); 84 | echo ""; 85 | echo "
"; 86 | echo "
"; 87 | 88 | echo "
"; 89 | echo " 91 | $title"; 92 | 93 | echo ""; 98 | 99 | echo "
"; 100 | 101 | ?> 102 |
103 | 104 | 105 | -------------------------------------------------------------------------------- /www/mjpeg_read.php: -------------------------------------------------------------------------------- 1 | 4 | 10 | -------------------------------------------------------------------------------- /www/mjpeg_stream.php: -------------------------------------------------------------------------------- 1 | 26 | 27 | -------------------------------------------------------------------------------- /www/sys_command.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | /dev/null 2>&1 &"; 20 | $res = exec($SUDO_CMD); 21 | } 22 | else if ($cmd === "pikrellcam_stop") 23 | { 24 | $fifo = fopen(FIFO_FILE,"w"); 25 | fwrite($fifo, "quit"); 26 | fclose($fifo); 27 | usleep(500000); 28 | exec("pgrep pikrellcam", $output, $return); 29 | if ($return == 0) 30 | exec('killall pikrellcam'); 31 | } 32 | } 33 | ?> 34 | --------------------------------------------------------------------------------