WCPU path) define("MCUERR_LOG", 0x0020); // µALFAT or SD-card failure define("MCUERR_GSM", 0x0040); // WCPU unreachable define("MCUERR_SMS", 0x0080); // not used define("MCUERR_ANY", 0x00F0); // battery status define("MCUSTAT_BATTERY_OK", 0); define("MCUSTAT_BATTERY_LOW", 1); define("MCUSTAT_BATTERY_CRITICAL", 2); define("MCUSTAT_BATTERY_ABSENT", 3); define("MCUSTAT_BATTERY1_LOW", (1 << (MCUSTAT_BATTERY_LOW + 7))); define("MCUSTAT_BATTERY1_CRITICAL", (1 << (MCUSTAT_BATTERY_CRITICAL + 7))); define("MCUSTAT_BATTERY1_ABSENT", (1 << (MCUSTAT_BATTERY_ABSENT + 7))); define("MCUSTAT_BATTERY2_LOW", (1 << (MCUSTAT_BATTERY_LOW + 11))); define("MCUSTAT_BATTERY2_CRITICAL", (1 << (MCUSTAT_BATTERY_CRITICAL + 11))); define("MCUSTAT_BATTERY2_ABSENT", (1 << (MCUSTAT_BATTERY_ABSENT + 11))); define("MCUSTAT_BATTERY", 0xFF00); // combination of all battery status flags define("MCUSTAT_BATTERY1", 0x0F00); define("MCUSTAT_BATTERY2", 0xF000); // // Fields for the status array ['mcu']['local state'] // define("MCULOC_IN_SWEEP", 0x0003); // sweep busy define("MCULOC_IN_FREQ_SWEEP", 0x0001); // frequency sweep define("MCULOC_IN_AMPL_SWEEP", 0x0002); // amplitude sweep define("MCULOC_GPSPOWER", 0x0008); // GPS power enabled (for trace & trace) define("MCULOC_GPS_RECEIVED", 0x0010); // GPS co-ordinates received define("MCULOC_RTC_RECEIVED", 0x0020); // clock data received define("MCULOC_LED", 0x0040); // LED intensity is "high" / deprecated 20091111 define("MCULOC_LANTERN", 0x0080); // lantern is "on" / deprecated 20091111 define("MCULOC_AUTOCAL", 0x0100); // auto-calibration enabled define("MCULOC_AUTOCAL_FAIL", 0x0200); // auto-calibration failed (internal state) define("MCULOC_EXT_CONTACT", 0x0400); // external contact enabled define("MCULOC_RELAY_DRIVER", 0x0800); // relay driver enabled define("MCULOC_GSM_ENABLED", 0x2000); // GSM is ready define("MCULOC_TCP_CONNECTED", 0x4000); // logged in with TCP server define("MCULOC_INIT_DONE", 0x8000); // MCU is initialized // // Fields for the status array ['mcu']['persistent'] // define("MCUFLG_AUTOCAL_DONE", 0x0001); // device is calibrated define("MCUFLG_SW3000_AVAILABLE", 0x0004); // device is equiped with a Switch 3000 module define("MCUFLG_PM3000_AVAILABLE", 0x0008); // device is equiped with a Power Management 3000 module define("MCUFLG_MEASUREMENT", 0x0010); // measurement busy define("MCUFLG_MEASUREMENT_RC", 0x0020); // measurements turned on remotely define("MCUFLG_MEASUREMENT_KEY", 0x0040); // measurements turned on with the key define("MCUFLG_SW3000_ENABLED", 0x0080); // Switch 3000 is enabled define("MCUFLG_BATTERY_MASK", 0xFF00); // Battery type (t) and voltage (v) for battery #1 (b=0) or #2 (b=1) // MCUFLG_BATTERY_TYPE(b,t,v) ((((t) << 2) | (v)) << (8 + (4*b))) define("MCUFLG_BATT_NiMH", 0); // default: NiMH battery define("MCUFLG_BATT_NiCd", 1); // option: NiCd battery define("MCUFLG_BATT_LiIon", 2); // option: LiIon or LiPolymer battery define("MCUFLG_BATT_Alkaline", 3); // option: alkaline battery (1.5V per cell instead of 1.2V) // (when MCUFLG_BATT_Li is _not_ set) define("MCUFLG_BATT_12V", 0); // default: 12V battery define("MCUFLG_BATT_6V", 1); // option: 6V battery define("MCUFLG_BATT_24V", 3); // option: 24V battery // (when MCUFLG_BATT_Li is set - currently only MCUTYPE_ZKL3000_RC) define("MCUFLG_BATT_7_2V", 0); // default: 7.2V battery define("MCUFLG_BATT_10_8V", 1); // option: 10.8V define("MCUFLG_BATT_14_4V", 2); // option: 14.4V define("MCUFLG_BATT_3_6V", 3); // (not for a ZKL, but other devices with a 3.6/3.7 V LiIon battery) // // Fields for the status array ['mcu']['remote control status'] // define("MCURC_MEASUREMENT_ON", 0x0001); // transient state: measurements are turned on from MTinfo define("MCURC_DETECTION_OK", 0x0002); // detection status (short circuit between the rails detected) // when the Switch3000 was turned on or off define("MCURC_KEY_SWITCH_ON", 0x0004); // key for Switch3000 is "on" define("MCURC_KEY_SWITCH_OFF", 0x0008); // key for Switch3000 is "off" define("MCURCERR_SWI2C", 0x0010); // software I2C error define("MCURCERR_SW1W", 0x0020); // software 1-Wire error define("MCURCERR_SW3000", 0x0020); // (alias) define("MCURCERR_PM3000", 0x0040); // PM3000 failure define("MCURCERR_ANY", 0x0070); // define("MCURC_PM_SW3000_ON", 0x0080); // Switch 3000 power always on define("MCURC_PM_SWITCH1", 0x0100); // power management: switch 1 is "on" (Switch 3000) define("MCURC_PM_SWITCH2", 0x0200); // power management: switch 2 is "on" (undefined) define("MCURC_PM_CHARGE", 0x0400); // power management: charging LiIon battery define("MCURC_PM_BOOST", 0x0800); // transient state: 9V boost for the analogue circuit enabled define("MCURC_GPS_POWER_ON", 0x1000); // transient state: GPS (5V circuit) powered up define("MCURC_PM_CHARGE_MANUAL", 0x2000); // battery charger controlled manually define("MCURC_SW3000_ONESHOT", 0x4000); // Switch 3000: one shot measurement define("MCURC_STATUS_VALID", 0x8000); // transient state: status is retrieved for Switch 3000 // and it ready to be read (the flag will disappear // immediately after that) // // Fields for the status array ['mcu']['switch3000 status'] // define("MCUSW_SEC1_ON", 0x0001); // section #1 is enabled define("MCUSW_SEC2_ON", 0x0002); // section #2 is enabled define("MCUSW_SEC3_ON", 0x0004); // section #3 is enabled define("MCUSW_SEC4_ON", 0x0008); // section #4 is enabled define("MCUSW_SEC_NONE", 0x0000); // (placeholder) define("MCUSW_SEC_ALL", 0x000F); // (all sections) define("MCUSWERR_TIMEOUT", 0x0010); // time-out error define("MCUSWERR_ERROR", 0x0020); // generic error, most likely in the 1-Wire signaling define("MCUSW_SEC1_BATT_OK", 0x0100); // battery for section #1 is okay define("MCUSW_SEC2_BATT_OK", 0x0200); // battery for section #2 is okay define("MCUSW_SEC3_BATT_OK", 0x0400); // battery for section #3 is okay define("MCUSW_SEC4_BATT_OK", 0x0800); // battery for section #4 is okay define("MCUSW_SECE_BATT_OK", 0x8000); // battery for emergency section is okay define("MCUSW_SEC_ALL_BATT", 0x8F00); // (all batteries) // // Wireless CPU status (used internally in the WCPU) // define("WCPUERR_NONE", 0x00000000); define("WCPUERR_INTERNAL", 0x00000001); // WCPU internal datastructure failed to initialize define("WCPUERR_GPRS", 0x00000002); // GPRS or TCP failure define("WCPUERR_SMS", 0x00000004); // SMS service couldn't be started define("WCPUERR_SIM", 0x00000008); // SIM or SIM service failed define("WCPUERR_UART1", 0x00000010); // UART1 failure (MCU->WCPU path) define("WCPUERR_UART2", 0x00000020); // UART2 failure (WCPU<->GPS path) define("WCPUERR_I2C", 0x00000040); // I2C error (WCPU->MCU path) define("WCPUERR_MCU", 0x00000080); // MCU does not respond on I2C bus define("WPCUERR_ANY", 0x000000FF); // Actived modules define("GSMACTIVE_GSM", 0x00000100); define("GSMACTIVE_SIM", 0x00000200); define("GSMACTIVE_GPRS", 0x00000400); define("GSMACTIVE_TCP", 0x00000800); define("GSMACTIVE_GPS", 0x00001000); define("GSMACTIVE_SMS", 0x00002000); define("GSMACTIVE_SSL", 0x00004000); define("GSMACTIVE_RTOS", 0x00008000); // _never_ in wcpu_flags (feature only) // Others define("WCPUERR_TEMP_ONBOARD", 0x00100000); // on-board temperature over threshold define("WCPUERR_TEMP_EXT1", 0x00200000); // NTC temperature over threshold define("WCPUERR_TEMP_EXT2", 0x00400000); // (reserved) define("WCPUERR_TEMP_SW3000", 0x00800000); // Switch 3000 temperature over threshold define("WCPUSTAT_GPS_FIX", 0x00008000); // GPS fix quality indicator set define("WCPUSTAT_GEOFENCE_INSIDE", 0x00080000); // inside a geofence // Log replay state (for debugging purposes) define("WPCULOG_ENABLED", 0x00010000); // connected to the server for streaming data define("WCPULOG_STALLED", 0x00020000); // stalled because data cannot be sent immediately // WCPULOG_REQ(what) ((u32)(what) << (16 + 8)) define("WCPULOG_REQ_NEW", 0x01); // pending request for new file, no reply define("WCPULOG_REQ_CONT", 0x02); // pending request, same file, not yet replied define("WCPULOG_REQ_ABORT", 0x03); // log reply is aborted define("WCPULOG_REQ_ERROR", 0x08); // error detected define("WCPULOG_REQ_ACK", 0x10); // request was acknowledge, orred with REQ_NEW or REQ_CONT define("WCPULOG_REQ_READING", 0x20); // reading data from MCU, orred with REQ_NEW or REQ_CONT define("WCPULOG_REQ_PROCESSING", 0x30); // processing log data define("WCPULOG_REQ_NEXT", 0x40); // requesting next file from TCP-server define("WCPULOG_REQ_LISTING", 0x80); // sending directory listing to TCP-server define("WCPULOG_REQ_DONE", 0xF0); // current log file completed define("WCPULOG_REQ_IDLE", 0x00); // log replay not yet started define("WCPULOG_REQ_MASK", 0xF0); // mask request state (inverted) and overall state /* ** Globals */ // status of the last transfer or command global $zkl_status, $zkl_error; $zkl_status = 0; $zkl_error = ""; /* ** Private functions */ // // Calculate the checksum of a message // function calc_checksum($buffer) { $checksum = 0; $ptr = 0; // skip initial $ or ! if( $buffer[0] == '$' || $buffer[0] == '!' ) $buffer = substr($buffer, 1); while( strlen($buffer) != 0 && $buffer[0] != "*" ) { // read a quoted string up to the end quote if( $buffer[0] == '"' ) { do { $checksum ^= ord($buffer); if( $buffer[0] == '\\' ) { // escaped character $buffer = substr($buffer, 1); $checksum ^= ord($buffer); $buffer = substr($buffer, 1); } else { $buffer = substr($buffer, 1); } // return immediately when there is no ending quote if( strlen($buffer) == 0 ) return -1; } while( $buffer[0] != '"' ); } $checksum ^= ord($buffer); $buffer = substr($buffer, 1); } return $checksum & 255; } // append the second array to the first - PHP doesn't have this function... function array_append(&$array1, $array2) { foreach( $array2 as $item ) $array1[] = $item; } // wait for one or more sockets, with a timeout // $sockets, an array with sockets to wait for, will be modified function wait_for_sockets(&$sockets, &$timeout) { return cp3000_wait_data($sockets, $timeout); } // wait for a single socket, with a timeout function wait_for_socket($socket, &$timeout) { return cp3000_wait_data($socket, $timeout); } // // Send a command, contained in '$fmt', over the socket. The command '$fmt' // can contains format specifiers for 'sprintf()', the arguments for which // are provided in the array '$args'. // The checksum is calculated for resulting command, after expansion of // '$args' and this is sent to the TCP server. // function zkl_send_command($socket, $fmt, $args = NULL) { global $zkl_status, $zkl_error; if( !$fmt ) { // library crashes on an empty string ($ forgotten?) $zkl_status = TCPERR_SOCKET; $zkl_error = "cp3000_send_data(): invalid NULL command"; return FALSE; } // send command, with checksum $cmd = cp3000_tokens2cmd(vsprintf($fmt, $args)); if( @cp3000_send_data($socket, $cmd) === FALSE ) { $zkl_status = TCPERR_SOCKET; $zkl_error = "cp3000_send_data(): " . cp3000_error_string($socket, $zkl_status); return FALSE; } // DEBUGGING: else echo "--[" . microtime() . "]-> " . $cmd . "\n"; return TRUE; } // // Queue retrieval of the value of a key. // The value must read using 'zkl_read_result()' // function zkl_queue_retrieval($socket, $key) { // send the command, its arguments and the checksum if( zkl_send_command($socket, "\$RETR,\"%s\"", $key) === FALSE ) return FALSE; } // // Read the result of the previous command from the socket and tokenize it. // Also used for asynchronous replies. // function zkl_read_result($socket, &$result, $timeout = NULL) { global $zkl_status, $zkl_error; // read result $result = ""; do { $reply = @cp3000_read_data($socket, $timeout); if( $reply === FALSE ) { if( $timeout == 0.0 ) $zkl_status = TCPERR_TIMEOUT; else $zkl_status = TCPERR_SOCKET; $zkl_error = "cp3000_read_data(): " . cp3000_error_string($socket, $zkl_status); return $zkl_status; } // DEBUGGING: else echo "<-[" . microtime() . "]-- " . strlen($reply) . ": " . $reply . "\n"; if( $reply[0] == '!' ) { $tokens = cp3000_cmd2tokens($reply); $zkl_status = $tokens['status']; // save error in '$zkl_error' if( $zkl_status != TCPSTAT_OK && $zkl_status != TCPSTAT_INTERMEDIATE && $zkl_status != TCPSTAT_WARNING && $zkl_status != TCPSTAT_BADSTATE ) { $zkl_error = $tokens['args'][0]; } // save result and/or error string if( is_array($tokens['args']) ) { if( $result ) $result .= ","; $result .= implode(",", $tokens['args']); } } } while( $zkl_status == TCPSTAT_INTERMEDIATE || $reply[0] == '$' ); return $zkl_status; } /* ** Low level functions */ // // Send a command to the TCP-server and wait for its reply // When a time-out is not needed, pass NULL as the second argument to this function // function zkl_command($socket, $timeout, $fmt) { // send the command, its arguments and the checksum $args = func_get_args(); $args = array_slice($args, 3); if( zkl_send_command($socket, $fmt, $args) === FALSE ) return FALSE; if( zkl_read_result($socket, $result, $timeout) != TCPSTAT_OK ) return FALSE; else return $result; } /* ** Global functions */ // // Security (SSL): set private key // // Inputs: // $key - private key (name of a PEM encoded file) // $passphrase - passphrase for the key, a filename of a raw (!) file // containing the actual passphrase; this file must only // the passphrase, i.e. it must not have line endings // or comments or whatever (or rather: the _entire_ file // contents is used as the passphrase) // function zkl_set_private_key($key, $passphrase) { global $_SECURITY; if( !isset($_SECURITY) ) $_SECURITY = array(); $_SECURITY['private']['key'] = $key; $_SECURITY['private']['passphrase'] = $passphrase; } // // Security (SSL): set public key // // Inputs: // $cert - "my" certificate (name of a PEM encoded file) // $signer - certificate of the CA that signed "my" certificate // (name of a PEM encoded file) // function zkl_set_public_key($cert, $signer) { global $_SECURITY; if( !isset($_SECURITY) ) $_SECURITY = array(); $_SECURITY['public']['cert'] = $cert; $_SECURITY['public']['trust'] = $signer; } // // Login with the TCP-server // // Inputs: // $zkl - database ID of the ZKL // $server - hostname:port with which to connect // $priv_key - NOT USED, use "zkl_set_private_key()" instead // $pub_key - NOI USED, use "zkl_set_public_key()" instead // $alarmmsg - this channel is for alarm messages ($alarmmsg = TRUE) // or retrieve or storing keys on the ZKL (the default, // $alarmmsg = FALSE) // $timeout - Max timeout of the $DB command // $shmem_fallback - When shmem failed, login? // // Returns: // CP3000 channel descriptor for reading and writing (historically // called a socket) // function zkl_tcplogin($zkl, $server, $priv_key = NULL, $pub_key = NULL, $alarmmsg = ZKL_NORMAL, $timeout = 3, $shmem_fallback = TRUE) { global $zkl_status, $zkl_error; global $_SECURITY, $_HOSTNAME; // Setup connection to shared memory object or TCP-server // Isolate the host name to test if it is an alias for the local host $host = $server; $host_pos = strpos($host, ":"); if( $host_pos !== FALSE ) $host = substr($host, 0, $host_pos); // try shared memory object and semaphore - fallback to TCP if the shared memory object // or the semaphore can't be opened // NB: string parameter to 'cp3000_sysv_key()' _must_ be correctly // formatted, i.e. don't change this if( $alarmmsg == ZKL_SHMEM && isset($_HOSTNAME) && (is_array($_HOSTNAME) ? in_array($host, $_HOSTNAME) : $host == $_HOSTNAME) && ( $shm_id = @shmop_open( cp3000_sysv_key($server . "-" . sprintf("%u", $zkl) . "-shm"), "a", 0, 0 ) ) !== FALSE && ( $semaphore = @sem_get( cp3000_sysv_key($server . "-" . sprintf("%u", $zkl) . "-sem") ) ) !== FALSE ) { // shared memory object and semaphore are available return array('shm' => $shm_id, 'sem' => $semaphore); } else { if (($alarmmsg != ZKL_SHMEM) || ($shmem_fallback)) { // TCP-server or TCP-server Secure if( isset($_SECURITY) ) { $socket = @cp3000_connect( $server, $_SECURITY['public']['cert'], $_SECURITY['public']['trust'], $_SECURITY['private']['key'], $_SECURITY['private']['passphrase'] ); } else $socket = @cp3000_connect($server); // connected? if( !$socket ) { return FALSE; } // login if( zkl_command($socket, $timeout, "\$DB,%u,%d", $zkl, $alarmmsg ? 1 : 0) === FALSE ) { @cp3000_close($socket); return FALSE; } else return $socket; } else { return FALSE; } } } // // Set up connection with server // // Inputs: // $server - hostname:port with which to connect // function zkl_connect($server) { return @cp3000_connect($server); } // // Disconnect connection // function zkl_disconnect($socket) { @cp3000_close($socket); return TRUE; } // // Logout from TCP-server (trivial...) // function zkl_tcplogout($socket) { if( is_cp3000($socket) ) { zkl_disconnect($socket); } else { @shmop_close($socket['shm']); // nothing special needed for the semaphore } return TRUE; } // // Is the connection a TCP connection? // function zkl_via_tcp($socket) { return is_cp3000($socket); } // // Get peer name // // Returns: // - SSL common name of the peer, as determined by its certificate; // - hostname (reverse DNS look-up, or IP addres if not found) for // unsecure connections // function zkl_get_peername($socket) { if( is_cp3000($socket) ) return @cp3000_get_peername($socket); else return ""; } // // Retrieve the value of a key // function zkl_retrieve($socket, $key, $timeout = NULL) { return zkl_command($socket, $timeout, "\$RETR,\"%s\"", $key); } // // Store the value of a key // function zkl_store($socket, $key, $value) { return zkl_send_command($socket, "\$STOR,\"%s\",\"%s\"", array($key, $value)) !== FALSE; } /* ** High level functions */ // // Get real-time status // Return an associative array // function zkl_get_status($socket, $timeout = NULL) { if( !is_cp3000($socket) ) return FALSE; return zkl_interpret_status(zkl_retrieve($socket, "STAT", $timeout)); } function zkl_get_cached_status($socket, $timeout = NULL) { if( is_cp3000($socket) ) { $status = zkl_command($socket, $timeout, "\$STAT"); } else { // 256 bytes _should_ be long enough to contain the status string // (at the moment, 20101027, it is about 140 bytes or less) @sem_acquire($socket['sem']); $status = @shmop_read($socket['shm'], 0, 256); @sem_release($socket['sem']); // trim the string - PHP strings are a bit awkward as it is a strange // mix between C type strings and byte arrays with a length field; // 'strrpos()' in one of those functions that treats it as a byte // array so we trim the data read from shared memory to the length // of the C string it contains $pos_eos = strpos($status, "\0"); if( $pos_eos !== FALSE ) $status = substr($status, 0, $pos_eos); } return zkl_interpret_status($status); } function zkl_interpret_status($statbuf) { if( !$statbuf ) return FALSE; $seqnr_pos = strrpos($statbuf, "@"); if( $seqnr_pos !== FALSE ) { $seqnr_time = substr($statbuf, $seqnr_pos + 1); $statbuf = substr($statbuf, 0, $seqnr_pos); $time_pos = strpos($seqnr_time, "."); if( $time_pos) { $status['time'] = intval(substr($seqnr_time, $time_pos + 1)); $seqnr_time = substr($seqnr_time, 0, $time_pos); } else $status['time'] = 0; $status['seqnr'] = intval($seqnr_time); } else $status['seqnr'] = -1; $statbuf = explode(",", $statbuf); // gather status bitfields; will be expanded $mcu_state = array(); $has_switch_status = preg_match("|([0-9A-F]{4})/([0-9A-F]{4})/([0-9A-F]{4})|", $statbuf[0], $mcu_state) > 0; if( $has_switch_status ) { $statbuf[0] = $mcu_state[1]; } $status['mcu']['status'] = intval($statbuf[0], 16); $status['mcu']['local state'] = intval($statbuf[1], 16); $status['mcu']['persistent'] = intval($statbuf[2], 16); $wcpu_status = trim($statbuf[3]); if( strlen($wcpu_status) == 8 && intval("80000000", 16) != 0x80000000 ) { // argh! the conversion on 32-bit, which the most significant bit set, goes gaga // the log status is stripped because it corrupts the conversion on 32-bit system // (and it is not interesting anyway) $status['wcpu']['status'] = (intval(substr($wcpu_status, 2, 2), 16) << 16) + (intval(substr($wcpu_status, 4, 4), 16) << 0); } else { // on 64-bit systems... $status['wcpu']['status'] = intval($statbuf[3], 16); } if( $has_switch_status ) { // remote control and switch 3000 status $status['mcu']['remote control status'] = intval($mcu_state[2], 16); $status['mcu']['switch3000 status'] = intval($mcu_state[3], 16); } // measurement info $status['detection']['error'] = ($status['mcu']['status'] & 0x0004) != 0; $status['detection']['calibrated'] = ($status['mcu']['persistent'] & 0x0001) != 0; $status['detection']['b/a limit'] = floatval($statbuf[4]); $status['detection']['freq'] = floatval($statbuf[5]); if( $status['mcu']['status'] & 0x0001 ) { $status['detection']['active'] = TRUE; $status['detection']['rms'] = intval($statbuf[6], 10); $status['detection']['b/a'] = floatval($statbuf[7]); $status['detection']['quality'] = zkl_measurement_quality($status['detection']['b/a'], $status['detection']['b/a limit']); $status['detection']['ok'] = ($status['mcu']['status'] & 0x0002) != 0; } else { $status['detection']['active'] = FALSE; $status['detection']['rms'] = 0; $status['detection']['b/a'] = 0; $status['detection']['quality'] = 0; $status['detection']['ok'] = FALSE; } // special, transient status flags $status['detection']['sweep']['enabled'] = ($status['mcu']['local state'] & 0x0003) != 0; $status['detection']['auto-cal']['enabled'] = ($status['mcu']['local state'] & 0x0100) != 0; $status['detection']['auto-cal']['failure'] = ($status['mcu']['local state'] & 0x0200) != 0; // power management and switch 3000 if( $has_switch_status ) { if( $status['mcu']['persistent'] & MCUFLG_PM3000_AVAILABLE ) { $status['pm3000']['switch3000']['on'] = ($status['mcu']['remote control status'] & 0x0100) != 0; $status['pm3000']['charger']['on'] = ($status['mcu']['remote control status'] & 0x0400) != 0; $status['pm3000']['switch #2']['on'] = ($status['mcu']['remote control status'] & 0x0200) != 0; } if( $status['mcu']['persistent'] & MCUFLG_SW3000_AVAILABLE ) { $status['switch3000']['on'] = ($status['mcu']['switch3000 status'] & 0x0009) == 0x0009 || ($status['mcu']['switch3000 status'] & 0x0006) == 0x0006; $status['switch3000']['key']['on'] = ($status['mcu']['remote control status'] & 0x0004) != 0; $status['switch3000']['key']['off'] = ($status['mcu']['remote control status'] & 0x0008) != 0; $status['switch3000']['detection']['ok'] = ($status['mcu']['remote control status'] & 0x0002) != 0; $status['switch3000']['enabled'] = ($status['mcu']['persistent'] & MCUFLG_SW3000_ENABLED) != 0; // switch 3000 batteries $status['batt']['switch3000']['sec1']['status'] = ($status['mcu']['switch3000 status'] & 0x0100) != 0 ? "ok" : "alarm"; $status['batt']['switch3000']['sec2']['status'] = ($status['mcu']['switch3000 status'] & 0x0200) != 0 ? "ok" : "alarm"; $status['batt']['switch3000']['sec3']['status'] = ($status['mcu']['switch3000 status'] & 0x0400) != 0 ? "ok" : "alarm"; $status['batt']['switch3000']['sec4']['status'] = ($status['mcu']['switch3000 status'] & 0x0800) != 0 ? "ok" : "alarm"; $status['batt']['switch3000']['emer']['status'] = ($status['mcu']['switch3000 status'] & 0x8000) != 0 ? "ok" : "alarm"; } } // relay $status['relay']['open'] = ($status['mcu']['status'] & 0x0008) != 0; $status['relay']['enabled'] = ($status['mcu']['local state'] & 0x0800) != 0; // external contact, led intensity and lantern $status['external contact']['enabled'] = ($status['mcu']['local state'] & 0x0400) != 0; $status['led']['high'] = ($status['mcu']['local state'] & 0x0040) != 0; $status['led']['intensity'] = ($status['mcu']['local state'] & 0x0040) != 0 ? "high" : "low"; $status['lantern']['enabled'] = ($status['mcu']['local state'] & 0x0080) != 0; // batteries; battery number is zero-based $status['batt']['select'] = intval($statbuf[8], 10); if( $status['batt']['select'] > 0 ) $status['batt']['select'] -= 1; $status['batt'][0]['V'] = floatval($statbuf[9]); $status['batt'][1]['V'] = floatval($statbuf[10]); if( $status['mcu']['status'] & 0x0400 ) $status['batt'][0]['status'] = "removed"; else if( $status['mcu']['status'] & 0x0200 ) $status['batt'][0]['status'] = "empty"; else if( $status['mcu']['status'] & 0x0100 ) $status['batt'][0]['status'] = "alarm"; else $status['batt'][0]['status'] = "ok"; if( $status['mcu']['status'] & 0x4000 ) $status['batt'][1]['status'] = "removed"; else if( $status['mcu']['status'] & 0x2000 ) $status['batt'][1]['status'] = "empty"; else if( $status['mcu']['status'] & 0x1000 ) $status['batt'][1]['status'] = "alarm"; else $status['batt'][1]['status'] = "ok"; // temperature $status['temp']['on-board']['C'] = (strlen($statbuf[11]) > 0) ? floatval($statbuf[11]) : NULL; $status['temp']['ext1']['C'] = (strlen($statbuf[12]) > 0) ? floatval($statbuf[12]) : NULL; $status['temp']['ext2']['C'] = FALSE; $status['temp']['switch3000']['C'] = FALSE; $status['temp']['on-board']['ok'] = (($status['wcpu']['status'] & WCPUERR_TEMP_ONBOARD) == 0); $status['temp']['ext1']['ok'] = (($status['wcpu']['status'] & WCPUERR_TEMP_EXT1) == 0); $status['temp']['ext2']['ok'] = (($status['wcpu']['status'] & WCPUERR_TEMP_EXT2) == 0); $status['temp']['switch3000']['ok'] = (($status['wcpu']['status'] & WCPUERR_TEMP_SW3000) == 0); // motion $status['motion']['active'] = ($status['mcu']['status'] & 0x00800000) != 0; $status['motion']['start'] = ($status['mcu']['status'] & 0x00C00000) == 0x00C00000; $status['motion']['stop'] = ($status['mcu']['status'] & 0x00C00000) == 0x00400000; // GSM reception $status['gsm']['enabled'] = ($status['mcu']['local state'] & 0x2000) != 0; $status['gsm']['connected'] = ($status['wcpu']['status'] & 0x0100) != 0; $status['gsm']['gprs connected'] = ($status['wcpu']['status'] & 0x0400) != 0; $status['gsm']['tcp/ip connected'] = ($status['wcpu']['status'] & 0x0800) != 0; $status['gsm']['sim initialized'] = ($status['wcpu']['status'] & 0x0200) != 0; $status['gsm']['received signal strength'] = floatval($statbuf[13]); $status['gsm']['bit error rate'] = intval($statbuf[14], 10); if( $status['gsm']['bit error rate'] != -1 ) { if( $status['gsm']['bit error rate'] == 0 ) $status['gsm']['min error pct'] = 0; else $status['gsm']['min error pct'] = pow(2.0, $status['gsm']['bit error rate']) / 10; $status['gsm']['max error pct'] = pow(2.0, $status['gsm']['bit error rate'] + 1) / 10; } // GPS reception $status['gps']['enabled'] = TRUE; // obsolete $status['gps']['latitude'] = floatval($statbuf[15]); $status['gps']['longitude'] = floatval($statbuf[16]); $status['gps']['altitude'] = floatval($statbuf[17]); $status['gps']['hdop'] = floatval($statbuf[18]); $status['gps']['speed'] = floatval($statbuf[19]); $status['gps']['heading'] = floatval($statbuf[20]); $status['gps']['tijd'] = intval($statbuf[21]); $status['gps']['fix'] = ($status['wcpu']['status'] & 0x8000) != 0; switch( substr($statbuf[18], -1) ) { case 'A': // fix: autonomous case 'D': // fix: differential (DGPS) case 'E': // fix: estimated (dead reckoning) case 'I': // no fix: initialized from MCU case 'N': // no fix case 'U': // no fix, device is initializing $status['gps']['mode'] = substr($statbuf[18], -1); break; default: unset($status['gps']['mode']); break; } $status['gps']['track&trace'] = ($status['mcu']['local state'] & 0x0008) != 0; $status['geofence']['inside'] = ($status['wcpu']['status'] & 0x80000) != 0; // subsystem errors $status['system']['error']['i2c'] = ($status['mcu']['status'] & 0x0010) != 0 || ($status['wcpu']['status'] & 0x0040) != 0; $status['system']['error']['log'] = ($status['mcu']['status'] & 0x0020) != 0; $status['system']['error']['gsm'] = ($status['mcu']['status'] & 0x0040) != 0; $status['system']['error']['sms'] = ($status['mcu']['status'] & 0x0080) != 0; $status['system']['error']['wcpu'] = ($status['wcpu']['status'] & 0x0001) != 0; $status['system']['error']['gprs'] = ($status['wcpu']['status'] & 0x0002) != 0; $status['system']['error']['tcp'] = ($status['wcpu']['status'] & 0x0002) != 0; $status['system']['error']['uart'] = ($status['wcpu']['status'] & 0x0010) != 0; $status['system']['error']['gps'] = ($status['wcpu']['status'] & 0x0020) != 0; // UART2 actually $status['system']['error']['mcu'] = ($status['wcpu']['status'] & 0x0080) != 0; if( $has_switch_status ) { $status['system']['error']['swi2c'] = ($status['mcu']['remote control status'] & 0x0010) != 0; $status['system']['error']['sw1w'] = ($status['mcu']['remote control status'] & 0x0020) != 0; } $status['system']['ok'] = ($status['mcu']['status'] & 0x00F0) == 0 && ($status['wcpu']['status'] & 0x00FF) == 0 && (!$has_switch_status || ($status['mcu']['remote control status'] & 0x00F0) == 0); // in initialisation phase? $status['system']['init'] = ($status['mcu']['local state'] & 0x8000) == 0; return $status; } // // Quality of the measurement (rather arbitrary calculation) // function zkl_measurement_quality($b_a_measurement, $b_a_autocal) { require_once("utilities.php"); // the ZKL is calibrated at 150 mOhm, with a margin of 1.5% (1/64) // in the calculation below we'll use a margin of 3%; the closer // b/a diverges to the calibrated value, the less the quality will // be; above the calibrate value the quality is 0.0, below the // mentioned 3% is 1.0 (100%) // measuring with a 150 mOhm resistor will therefore show a quality // of 50% // the 3% margin is chosen rather arbitrarily and it indicates to // the user that the measurment is near the detection margin and that // he/she should try to place the ZKL anew. // the percentage at with quality is 100% can be adjusted below by // setting the correct value for $q_good. return CalcShortCircuitQuality($b_a_autocal, $b_a_measurement); } // // Get a (well, the last) measurement // function zkl_get_measurement($socket, $timeout = NULL) { $buffer = zkl_retrieve($socket, "DEBUG[MEASUREMENT]", $timeout); if( $buffer === FALSE ) return FALSE; $buffer = explode(",", $buffer); $samples = array(); $samples['count'] = intval($buffer[0]); $samples['b/a'] = floatval($buffer[1]) / 65536.0; $samples['t_lat'] = floatval($buffer[4]); $samples['t_seq'] = floatval($buffer[5]); $samples['v_ref']['rms'] = intval($buffer[2]); $samples['v_meas']['rms'] = intval($buffer[3]); $buffer_ptr = 6; for( $array = 0; $array < 4; $array++ ) { // determine in which array to put the samples switch( $array ) { case 0: $signal = 'v_ref'; $type = 'raw'; break; case 1: $signal = 'v_meas'; $type = 'raw'; break; case 2: $signal = 'v_ref'; $type = 'filtered'; break; case 3: $signal = 'v_meas'; $type = 'filtered'; break; } // collect the samples $sample_array = array(); for( $sample = 0; $sample < $samples['count']; $sample++ ) { $val = intval($buffer[$buffer_ptr++], 16); if( $val & 0x8000 ) { // 2's complement negative number $val -= 0x10000; } array_push($sample_array, $val); } // save the samples in the main array $samples[$signal][$type] = $sample_array; } // done return $samples; } /* ** Functions for real-time status updates */ // // Wait until one of the ZKLs has a new status // Input: // $sockets - array of sockets, opened with a function call like // 'zkl_tcplogin($id, $tcp_server, $priv_key, $pub_key, TRUE)' // $timeout - time-out in seconds (floating point number) // Output: // - array with one or more items as below; each ZKL that has a // status update will be in this array; ZKLs that didn't present // a new status will _not_ be in the array // items in entry of the array // - ['zkl'] - database ID of the ZKL // - ['flags'] - which data is updated; bit field with one or more // 'STATUPDATE_xxx' bits set. // - ['stat'] - status array; see 'zkl_interpret_status()' // - empty array when the time-out expired // - FALSE on error // function zkl_wait($sockets, $timeout = NULL) { $n = wait_for_sockets($sockets, $timeout); // check the result if( !$n ) // error return FALSE; else { $result = array(); if( $n ) foreach( $sockets as $socket ) { $reply = @cp3000_read_data($socket, $timeout); if( $reply === FALSE ) { $zkl_status = TCPERR_SOCKET; $zkl_error = "socket_read(): " . cp3000_error_string($socket, $zkl_status); continue; } // DEBUGGING: else echo "--[" . microtime() . "]-- " . $reply . "\n"; $tokens = cp3000_cmd2tokens($reply); array_push($result, array( 'zkl' => $tokens['client'], 'flags' => intval($tokens['args'][0], 10), 'stat' => zkl_interpret_status($tokens['args'][1]) ) ); } return $result; } } /* ** Send an SMS */ // // Send an SMS // Input: // $server - the SMS server // $da - destination address (the phone number) _or_ an array with // (at least) 'telefoonnr' and 'imsi' to send an SMS to a // device (M2M API aware) // $msg - the message to send // $wait - wait for reply if TRUE // Output: // FALSE if an error occured, reply from the SMS server it $wait is TRUE // of simply TRUE when $wait is FALSE. // // The globals $zkl_status and $zkl_error will be set when an error occurs // function zkl_send_sms($server, $da, $msg, $wait = TRUE) { global $zkl_status, $zkl_error; if( strlen($msg) > 160 ) { $zkl_status = TCPERR_INVAL; return FALSE; } require_once("m2mapi.php"); $success = TRUE; if( !is_array($da) || !m2mapi_enabled_for_device($da) ) { // open the socket to the server $socket = cp3000_connect($server); if( !$socket ) { $zkl_status = TCPERR_CONNECT; return FALSE; } if( !zkl_send_command($socket, "\$SENDSMS,\"%s\",\"%s\"", array($da, $msg)) ) { // $zkl_status set by 'zkl_send_command()' $success = FALSE; } else if( $wait ) { $success = ((zkl_read_result($socket, $zkl_status, 15.0) == TCPSTAT_OK) && ($zkl_status == TCPSTAT_OK)); } // done cp3000_close($socket); } else { // send to the device using the M2M API (Vodafone Smart Services) $success = m2mapi_send_sms($da['imsi'], $msg); } return $success; } // // Send SMS messages to multiple recipients on the same server // Input: // $server - the SMS server // $msgs - array with destiantion address (key 'da') and messages (key 'msg') // as: // $msgs = array( // array('da' => '+31612345678', 'msg' => 'Text for the first recipient'), // array('da' => '+31687654321', 'msg' => 'Text for the second recipient'), // // etc // ); // $wait - wait for reply if TRUE // Output: // FALSE if an error occured, TRUE otherwise (_not_ a message index on success) // function zkl_send_multi_sms($server, $msgs, $wait = FALSE) { global $zkl_status, $zkl_error; $success = TRUE; // open the socket to the server $socket = cp3000_connect($server); if( !$socket ) { $zkl_status = TCPERR_CONNECT; return FALSE; } foreach( $msgs as $msg ) { if( !$msg['da'] || !$msg['msg'] || strlen($msg['msg']) > 160 ) { $zkl_status = TCPERR_INVAL; $success = FALSE; } else if( !zkl_send_command($socket, "\$SENDSMS,\"%s\",\"%s\"", array($msg['da'], $msg['msg'])) ) { $success = FALSE; } else if( $wait && !zkl_read_result($socket, $reply, 15.0) ) { $success = FALSE; } } // done cp3000_close($socket); return $success; } // // Build an array with version information from '$str' (which is retrieved from the database) // function zkl_get_fw_version($str) { $p = 0; $ver = array(); $ver['major'] = 0; while( ctype_digit(substr($str, $p, 1)) ) $ver['major'] = $ver['major'] * 10 + (ord(substr($str, $p++, 1)) - ord("0")); $ver['minor'] = 0; if( ord(substr($str, $p, 1)) == ord(".") ) { $p++; while( ctype_digit(substr($str, $p, 1)) ) $ver['minor'] = $ver['minor'] * 10 + (ord(substr($str, $p++, 1)) - ord("0")); } $ver['release'] = 0; if( ord(substr($str, $p, 1)) == ord(".") ) { $p++; while( ctype_digit(substr($str, $p, 1)) ) $ver['release'] = $ver['release'] * 10 + (ord(substr($str, $p++, 1)) - ord("0")); } $ver['update'] = 0; if( ord(substr($str, $p, 1)) == ord(".") ) { $p++; while( ctype_digit(substr($str, $p, 1)) ) $ver['update'] = $ver['update'] * 10 + (ord(substr($str, $p++, 1)) - ord("0")); } $ver['datecode'] = 0; if( ord(substr($str, $p, 1)) == ord("-") ) { $p++; while( ctype_digit(substr($str, $p, 1)) ) $ver['datecode'] = $ver['datecode'] * 16 + (ord(substr($str, $p++, 1)) - ord("0")); } $ver['devtype'] == ""; if( ord(substr($str, $p, 1)) == ord("-") ) { $p++; $ver['devtype'] = substr($str, $p); } return $ver; } ?>