PHP in Elphel cameras

From ElphelWiki

Jump to: navigation, search


Why PHP?

One of the main objectives of Elphel products is to make them developer-friendly. Just opening of the camera internals (including source code, FPGA code, hardware schematics) is not enough without providing means to modify the cameras easily, and here PHP comes to the rescue. It is powerful and efficient and is well known by many developers including those who may have problems to modify FPGA code or kernel drivers. It is much easier to try your custom applications written in PHP - just transfer the files to the camera (i.e. with ftp) and open them in the web browser.

PHP supports custom extensions written in C and we'll work (and I hope not just we at Elphel) to provide more custom function to combine flexibility and ease of use of the PHP with efficiency of native compiled code.

PHP in Elphel cameras (models 353/363)

Current version of PHP in Elphel cameras is 5.2.1, we will try to keep it (almost) current. PHP in the camera is working in Fast CGI mode through Lighttpd. Fast CGI mode does not restart applications (like traditional CGI does) for each HTTP request but rather keeps them alive to serve multiple requests. In the case of large application like PHP itself it makes a big performance difference and allows usage of the PHP scripts in the camera without the penalty of slow responses - something really undesirable for AJAX applications.

How to use PHP in the camera

It is very simple. It is the same as to try your HTML/JavaScript pages. You just need to ftp you script to one of the two locations (or their subdirectories) in the camera. Examples below assume you have the default camera IP=

You may also edit small text files with web-based editor. It does not allow to open non-existent file, but has an option to "Save As" - So you can try opening and then (modify and) save it to /var/html/test.php . When done - just open the following URL and watch the results of the script processing by in-camera PHP:  - run - edit

It is also possible to execute PHP scripts without the browser - camera startup scripts already use it for various initialization of the hardware. You can (silently) execute the same program in telnet session:

php /var/html/test.php >/dev/null

Elphel PHP extension in the camera

Since Elphel software version 7.1.6 there is a custom extension module for PHP installed in the camera, you may also create your own extension as described below. The existent extension exports several functions and defines constants.

Constants, defined in elphel PHP extension

see: Elphel_PHP_constants

elphel extension functions

Note: Currently arguments (and array values) are supposed to be numbers, not strings - you may want to use $value+0 (instead of just $value) if $value may be a string (i.e. passed from _GET[])--Andrey.filippov 19:55, 14 December 2007 (CST)


long elphel_get_P_value (long $address)

This function returns value of one of internal camera parameters, as defined in c313a.h and documented in Elphel_PHP_constants. It is strongly recommended to use symbolic names as the numeric values may change in the future (as they already did multiple times). The values are maintained by the camera driver(s), usually they are updated when the driver programs the acquisition process.


echo elphel_get_P_value(ELPHEL_FP1000S)/1000; // will return current camera sensor frame rate in (frames per second);


void elphel_set_P_value (long $address, long $data)

Set a single camera parameter.


elphel_set_P_value(ELPHEL_QUALITY,100); // set JPEG compression quality to 100% - virtually lossless

The elphel_get_P_value and elphel_set_P_value deal with different variables. Those that are read are validated to match the allowed values and may differ from those written (and they are only updated when sensor/compressor are programmed). You may read the parameters values that were written to the camera by adding ELPHEL_NUMBER to the parameter address, i.e

echo elphel_get_P_value(ELPHEL_QUALITY+ELPHEL_NUMBER); // will output the compression quality as written


array elphel_get_P_arr (array $template)

Read multiple camera parameters as an associative array, according to $template. $template should have elements with the key names matching the camera parameters names, same as defined Elphel_PHP_constants, but without "ELPHEL_" prefix (all the elements with the other keys will be ignored). elphel_get_P_arr returns array, where element keys are the same as in $template (after removing of any unknown ones), and values are long values read from the camera.


$arr=array("foo"=>"", "WOI_LEFT" => "",  "WOI_TOP" => "", "1", 3, "WOI_WIDTH" => 0, "WOI_HEIGHT" => 0);
print_r ($arr);

will return (5MPix sensor, full window):

   [WOI_LEFT] => 0
   [WOI_TOP] => 0
   [WOI_WIDTH] => 2592
   [WOI_HEIGHT] => 1936


long elphel_set_P_arr (array $input, long $frame, long $flag) )

Write multiple camera parameters, provided as an associative array, where keys are parameter names same as defined Elphel_PHP_constants, but without "ELPHEL_" prefix. Values should be numeric and integer, all other types are ignored.

optional arguments:

 frame - frame number to write these parameters to - if not specified default framedelay (current frame + 3) is applied)
 flag - possible flags? 

Return value - frame number to which parameters were written


echo elphel_set_P_arr(array("WOI_LEFT" => 896,  "WOI_TOP" => 672, "WOI_WIDTH" => 800, "WOI_HEIGHT" => 600));

Will set 800x600 window in the center of the 5Mpix sensor and print frame number these changes where applied to


long elphel_get_state (void) There are multiple states defined in the camera, some are obsolete, the following are most common (no symbolic constants in PHP yet)


long elphel_fpga_read (long $address)

Reads data from a certain fpga register address.


echo elphel_fpga_read(4); // outputs the data at address 4


void elphel_fpga_write (long $address, long $data)

Writes data to a certain fpga register address.

for Elphel 353 cameras these values can be the following (quoted from x353.h):

 187 #define X313_WA_WCTL	    0 // write control register - 32
 188 #define X313_WA_DMACR	    1 // DMA control register:
 189                               // 17:    DMA enable
 190                               // 16:    0- raw, 1 - JPEG
 191                               // 15-10: not used
 192                               // 0-9:   line length in long words
 193 #define X313_WA_SENSFPN	    2 // Sensor/FPN control register:
 194                                        // [10] - testmode - if 1 generates gradient data (as Zoran chips) where pixel value = horizontal position
 195                                        // [9:7] - submode - subtract background (8 bits) mode (use di[7:0]):
 196                                        // 000 - no subtraction;
 197                                        // 001 - (finest) subtract 8 bit bkgnd from 12 bits pixels
 198                                        // 010 - shift 8bit bkgnd 1 bit  left before applying
 199                                        // 011 - shift 8bit bkgnd 2 bits left before applying
 200                                        // 100 - shift 8bit bkgnd 2 bits left before applying
 201                                        // 101 - shift 8bit bkgnd 2 bits left before applying
 202                                        // fpn data to subtract should be a little less to add a "fat zero"
 203                                        // [6:4] - mpymode sensitivity correction mode (use di[15:8]):
 204                                        // 000 - no correction
 205                                        // 001 - fine correction (+/-3.125%)
 206                                        // 010 - fine correction (+/-6.25%)
 207                                        // 011 - fine correction (+/-12.5%)
 208                                        // 100 - +/- 25%
 209                                        // 101 - +/- 50%
 210                                        // [3] - wdth - word width: 0 - 8 bit, 1 - 16 bit (5 MSB == 0)
 211                                        // [2:0] scaling of 11 bit FPN result to fit in 8bit output:
 212                                        // 00 - default - use [9:1]
 213                                        // 01 - use [10:2] - to protect from saturation after applying mpymode
 214                                        //      nominal range - 0..127
 215                                        // 10 - use [7:0] before saturation, "digital gain" == 4 (maximal)
 216                                        // 11 - use [8:1] before saturation, "digital gain" == 2
 218                               // 31-8: not used
 219                               //    7: 0 - normal, 1 - test mode (as Zoran)
 220                               //  6-5: subtract FPN mode:
 221                               //       0 - no subtraction
 222                               //       1 (fine) - subtract 8-bit FPN from 10-bit pixel
 223                               //       2 - multiply FPN by 2 before subtracting
 224                               //       3 - multiply FPN by 4 before subtracting (full scfpcfale)
 225                               // negative result is replaced by 0, decrease FPN data before applying for "fat 0"
 226                               // 4-3: muliply by inverse sensitivity (sensitivity correction) mode:
 227                               //       0 - no correction
 228                               //       1 - fine (+/- 12.5%)
 229                               //       2 - medium (+/- 25%)
 230                               //       3 - maximal (+/- 50%)
 231                               //   2: pixel depth:
 232                               //       0 - 8 bits/pixel (needed for JPEG encoding)
 233                               //       1 - 16 bits/pixel (only 11 LSBs are non-zero)
 234                               // 1-0: scale (8 bit mode only):
 235                               //       0 - full scale corresponds to sensor full scale if no correction was applied
 236                               //       1 - divide by 2. This will guarantee against saturation after sensitivity correction was
 237                               //           applied. Nominal output range - 0..127
 238                               //       2 - multiply by 4 (use 8 lower bits from sensor data)
 239                               //       3 - multiply by 2
 240                               // Result data is saturated by 255
 241 #define X313_WA_VIRTTRIG   3  // Virtual trigger threshold
 242                               //   31-22  not used
 243                               //   21-0  Trigger will fire if sum of pixels in a line
 244                               //         (after FPN processig) is more than this value.
 245                               //         Disabled if 0 - selected real (electrical) external trigger
 246 #define X313_WA_TRIG       4  // Sensor triggering
 247                               // 31-3: not used
 248                               //    2: Enable sensor (0 - abort at once)
 249                               //    1: External Trigger (0 - internal)
 250                               //    0: continuous acquisition (0 - single "frame" - actually number of lines specified)
 251 #define X313_WA_NLINES     5  //  number of lines to acquire (afer trigger)
 252                               // 31-11: not used
 253                               // 10-0 : number of lines to acquire (in a frame or after the external trigger)
 254 //#define X313_WA_IRQM       6  // Interrupt mask (1 - enable, 0 - reset all but done)
 255                               // 31-04: not used
 256                               //     3: Done. Reset by writing to X313_WA_TRIG
 257                               //     2: frame acquisition over
 258                               //     1: external trigger
 259                               //     0: frame sync (vacts)
 260 #define X313_WA_DCDC       7  // sensor DC-DC converter frequency settings
 261                               // 31-05: not used
 262                               //   4-0  0 - use internal clock (not sync)
 263                               //        1..31 use divided pixel clock
 264                               //        for 20MHz use 5'h10,
 265                               //        else - N= (Fpix[MHz]/1.2)-1, if Fpix=20MHz,
 266                               //        N= 15.7->16=5'h10


elphel_fpga_write(4, 4); // Sets sensor triggering to an unused number and therefore disabling it -> acquire single frame to video buffer


void elphel_reset_sensor (void)

Reset the sensor settings, stop compressor (if it was running) and force sensor re-initialization.




void elphel_program_sensor (long $nonstop)

Applies written earlier (see elphel_set_P_value, elphel_set_P_arr, elphel_white_balance) parameters to the cameras sensor and FPGA compressor. $nonstop determines if the acquisition should be restarted ($nonstop==0) or updated without stopping ($nonstop==1).

Note: As of version 8.x, parameters are applied as they are set. Therefore, this function is no longer necessary.




void elphel_wait_frame (void)

Waits for the next frame to be compressed to the circular buffer - it is also the time when some other parameters (accessible with elphel_get_P_value, elphel_get_P_arr) are updated.




long elphel_wait_frame (long frame)

wait for absolute frame number (includes those that are not compressed)

returns: frame number




long elphel_skip_frames (long frames)

skip some frames (includes those that are not compressed) - will work even if no frames are compressed

returns: frame number




void elphel_compressor_run (void)

Turn the FPGA compressor on. Sensor should be already programmed ([[PHP_in_Elphel_cameras#|elphel_get_state] should return 7 (or higher) if tested prior to issuing elphel_compressor_run() command.




void elphel_compressor_stop (void)

Stop camera compressor (will stop transferring more frames to the circular buffer. This command does not to be synchronized - compressor will finish current frame before stopping.




void elphel_compressor_frame (void)

Acquire one frame to the buffer (will apply written JPEG quality). Sensor should be already programmed - elphel_get_state should return 7 (or higher) if tested prior to issuing elphel_compressor_frame() command.




double elphel_set_fpga_time (double seconds)

Set FPGA internal timer that is used for time-stamping images and video frames. Internal counter has a 32-bit seconds counter and 12 bit microsecond one, so you may specify epoch - seconds since 01/01/1970.

Returns time set as a double - it should match the input parameter after rounding to microseconds (resolution of the FPGA timer).




double elphel_get_fpga_time (void)

Copies current value of the FPGA timer registers to the global array (maybe read with several functions described above) and returns the the time as a double value, combining second as microseconds. The readout process guaranties that seconds and microseconds are sample simultaneously.


echo elphel_get_fpga_time();


int elphel_white_balance ([double thrsh [, double minfrac [, double rscale [, double bscale]]]])

This function is designed to perform white balancing of the camera images. It relies on histogram calculation, "gamma" tables (curves) (both implemented in the FPGA) and the auto-exposure software in the camera (not yet included in this PHP extension, it is currently controlled through the separate CGI program). White balance uses the same window as auto-exposure, only data inside the specified rectangular window is processed.

The goal of this function is to make the brightest areas in the image white (equal values of red, green and blue), it assumes that auto-exposure algorithm (operating after each frame acquired) already adjusted the brightness of the images so it uses all the 8-bit range and some pixel values are close to the maximal 255.

The balancing is achieved by modification of the lookup tables ("gamma" tables, "curves") that map 12-bit (in 5MPix sensor, may be 10 or 14 bits with other devices) pixel data to the 8-bit pixel values used by the image/video compressor, the same values can be manually entered with elphel_get_P_value, elphel_get_P_arr functions applied to RSCALE and BSCALE parameters. The balancing is sensor-independent and it does not change sensor analog gains (where available), but if the required correction is significant, it is recommended to adjust those gains also, leaving only fine adjustment to the white balance algorithm - that will increase the dynamic range of the images.

All function parameters are optional, but you can not skip the first one(s) if you would like to specify the next one(s), in that case default values should be explicitly entered.

First parameter thrsh defines how bright should be pixels to be counted as white. Default value of 0.98 corresponds to 250 counts on 255 scale.

Second parameter minfrac (default 0.01) tells how many pixels (at least) should be counted, as a fraction of the total number of the pixels in the specified window. If there are too few pixels with the brightness above the thrsh, the thrsh will be lowered, to let more pixels to be counted.

Next two parameters - rscale and bscale (both with default values of 1.0) can be used to apply manual bias to the automatically calculated values of red and blue scales ( green)

When this function is executed successfully it returns 0 (negative numbers indicate failure) in adjusts parameters (similar to manual elphel_get_P_value, elphel_get_P_arr), but that will propagate to the FPGA only after elphel_program_sensor is executed. If you do not need to change any other parameters, you may use elphel_program_sensor(1) - in this case the image/video acquisition process will not be interrupted, frame timing will be preserved.

As this function uses the histograms calculated from the previously acquired images, you actually need to acquire some. It is recommended you wait till the auto-exposure will adjust the brightness before executing elphel_white_balance. It also is not needed to run it as frequently as auto-exposure (for each frame), usually it is enough to call it only when the lighting conditions are changed.


if (elphel_white_balance()>=0) elphel_program_sensor(1);


elphel_white_balance(0.98,0.01,1.0,1.2); // set bluish tint

whitebalance.php is an example script that uses this function. It is installed in the camera and can be called as (provided camera has the default IP of



void elphel_autoexposure_set (void)

This function controls auto-exposure in the camera. It is a quick modification that preserves the existent functionality (including interface) and just adds control from the PHP. This function itself does not take any parameters, it also does not return any data, It applies parameters that have to be already written by either of elphel_set_P_value, elphel_set_P_arr, the current state of the auto-exposure algorithm can be read back using elphel_get_P_value, elphel_get_P_arr functions.

auto-exposure software in the camera
kernel space (driver)

When the image from the sensor is acquired in the camera (even the compressor is off), software interrupt is generated and among others two things related to the auto-exposure control happen in the driver.

user space (daemon)

User space application autoexp.c (running as a daemon) processes the histograms and calculates the corrected value of the exposure, the result value is sent to the driver that applies the required correction.

In addition the histograms (four of them - individually for each of the 4 Bayer components, including 2 sets of green pixels), driver provides applications with the lookup ("gamma") tables that are used by the FPGA to convert sensor pixel value (12 bit for the 5MPix sensor) into the 8-bit range, usually - non-linearly, controlled by "gamma" parameter. This tables are needed when processing the histograms, as FPGA calculates them after the LUT are applied to the pixel values.

The auto-exposure configuration (input) parameters and the current state (output) can still be accessed using the current CGI script autoexpos.cgi.c (as used by camvc user interface), alternatively they are directly accessible through the elphel_set_P_value, elphel_set_P_arr, elphel_get_P_value, elphel_get_P_arr functions.

auto-exposure algorithm in the camera

The goal of the auto-exposure algorithm is to use as much as possible of the sensor dynamic range in the area of interest. It tries to adjust the exposure of the sensor so the whole range of the image pixel values (up to 255) are used (small number of pixels are usually still allowed to overexpose and be return the 255 limit value). It is possible to maintain the brightest pixels (disregarding just few "super-bright") near 255, but the it has 2 disadvantages:

For these reasons a slightly different algorithm is used - the software tries to maintain specified part of the pixels in the window of interest to be below the specified level (usually high, but less than the full scale). In this case if the brightness of the scene suddenly increases (up to the ratio of the full scale pixel value to the specified threshold), the exposure correction can be calculated and applied in a single step. You may experiment with this algorithm using GUI in camvc camera web interface.

parameters that control auto-exposure in the camera

The following are parameters (it is possible to both their names as array keys with functions elphel_set_P_arr, elphel_get_P_arr, elphel_get_P_warr. There are also constants defined that use the same names with "ELPHEL_"prefix (i.e. ELPHEL_AUTOEXP_ON), these constants are useful with the functions elphel_set_P_value, elphel_get_P_value. All the parameter values are long (32-bit) integers, you may read them back (as written) with elphel_get_P_warr function call. Te value of ELPHEL_HIST_NOT_CHANGE (65536) for any of these parameters mean that values corresponding to these parameters will not be updated.

Creating custom PHP extensions for Elphel cameras

The followings steps are needed to create a custom PHP extension to run in Elphel product:

You may use ext_skel script to automate this process or follow a nice tutorial by Sara Goleman Extension Writing Part I: Introduction to PHP and Zend . You may also like to read her book, "Extending and Embedding PHP".


PHP_ARG_ENABLE(hello, whether to enable Hello World support, [ --enable-hello   Enable Hello World support])
if test "$PHP_HELLO" = "yes"; then
 AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
 PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)


#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1
extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry


#include "config.h"
#include "php.h"
#include "php_hello.h"
static function_entry hello_functions[] = {
   PHP_FE(hello_world, NULL)
zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
#if ZEND_MODULE_API_NO >= 20010901
   RETURN_STRING("Hello World", 1);

With the 3 files created you need to cd to the extension directory and run


That will generate support files (running phpize script) so you'll just need to run

make install

to have the new extension (as a dynamic library) installed in the target directory tree and included in the camera flash image.

You will need to add "extension=<your_extension_name>" to the php.ini file to be activated.

When troubleshooting extensions you may also want to try your scripts from the command line in the camera - in that case the stdout will be visible and PHP+your module will be able to complain about Segmentation faults (when invoked from the browser it you will get Error 500 at best if that happens). And you'll have to manually restart web server + PHP after module changes (remember - Fast CGI does not kill PHP after end of HTTP request?)

Known problems

Function fread in PHP always tries to read 8192 bytes even if the length parameter is <8192, that causes problems when read from devices (i.e. i2c) being too slow or causing side effects. It is considered feature, not a bug.

A quick fix will be to change the driver to always read minimal (1/2/3/4 bytes depending on the nature of the data) if the count passed to device read function is 8192.

Personal tools