0 ) { while (true) { $b = self::read_exact($fp, 1); if ($b === chr(0x3E)) { // radio -> app break; } } $lenBytes = self::read_exact($fp, 2); $len = unpack('v', $lenBytes)[1]; $result = self::read_exact($fp, $len); if( Environment::getDebug() ) echo "Reading frame:\n" . \Menking\Meshcore\Util\Debug::hexDump($result) . "\n"; return $result; } else { return chr(0x01) . chr(0xff); } } /** * * @param mixed $fp * @param int $n * @return string */ private static function read_exact($fp, int $n): string { $buf = ''; while (strlen($buf) < $n) { $chunk = fread($fp, $n - strlen($buf)); if ($chunk === false || $chunk === '') { usleep(10000); continue; } $buf .= $chunk; } return $buf; } /** * Changes the serial port * * @param string $device * @return bool * @throws RuntimeException */ public static function configureTty(string $device): bool { if( PHP_OS_FAMILY == 'Linux' ) { $cmd = sprintf( 'stty -F %s %d raw -echo -icanon min 0 time 10', escapeshellarg($device), 115200 ); exec($cmd . ' 2>&1', $output, $code); if ($code !== 0) { throw new \RuntimeException( "stty failed: " . implode("\n", $output) ); } else { return true; } } else { throw new \RuntimeException("This library hasn't been tested on Windows or MacOS"); } } public static function getErrorText(int $error_code) { switch($error_code) { case self::ERR_CODE_UNSUPPORTED_CMD: return "Unsupported command"; case self::ERR_CODE_NOT_FOUND: return "Not found"; case self::ERR_CODE_TABLE_FULL: return "Table full"; case self::ERR_CODE_BAD_STATE: return "Bad state"; case self::ERR_CODE_FILE_IO_ERROR: return "File I/O error"; case self::ERR_CODE_ILLEGAL_ARG: return "Illegal argument"; case 0xff: return "I/O timeout"; default: return "Unknown error: 0x" . dechex($error_code); } } public static function getLppType(int $type) { switch($type) { case self::LPP_TEMP: return 'temperature'; case self::LPP_VOLTAGE: return 'voltage'; case self::LPP_ILLUMINANCE: return 'illuminance'; case self::LPP_PRESENCE: return 'presence'; case self::LPP_HUMIDITY: return 'humidity'; case self::LPP_ACCEL: return 'accelerometer'; case self::LPP_BAROMETER: return 'barometer'; case self::LPP_GYRO: return 'gyrometer'; case self::LPP_GPS: return 'gps'; default: return 'unknown'; } } const PUB_KEY_SIZE = 32; const MAX_PATH_SIZE = 64; const CMD_APP_START = 1; const CMD_SEND_TXT_MSG = 2; const CMD_SEND_CHANNEL_TXT_MSG = 3; const CMD_GET_CONTACTS = 4; const CMD_GET_DEVICE_TIME = 5; const CMD_SET_DEVICE_TIME = 6; const CMD_SEND_SELF_ADVERT = 7; const CMD_SET_ADVERT_NAME = 8; const CMD_ADD_UPDATE_CONTACT = 9; const CMD_SYNC_NEXT_MESSAGE = 10; const CMD_SET_RADIO_PARAMS = 11; const CMD_SET_RADIO_TX_POWER = 12; const CMD_RESET_PATH = 13; const CMD_SET_ADVERT_LATLON = 14; const CMD_REMOVE_CONTACT = 15; const CMD_SHARE_CONTACT = 16; const CMD_EXPORT_CONTACT = 17; const CMD_IMPORT_CONTACT = 18; const CMD_REBOOT = 19; const CMD_GET_BATT_AND_STORAGE = 20; // was CMD_GET_BATTERY_VOLTAGE const CMD_SET_TUNING_PARAMS = 21; const CMD_DEVICE_QUERY = 22; const CMD_EXPORT_PRIVATE_KEY = 23; const CMD_IMPORT_PRIVATE_KEY = 24; const CMD_SEND_RAW_DATA = 25; const CMD_SEND_LOGIN = 26; const CMD_SEND_STATUS_REQ = 27; const CMD_HAS_CONNECTION = 28; const CMD_LOGOUT = 29; // 'Disconnect' const CMD_GET_CONTACT_BY_KEY = 30; const CMD_GET_CHANNEL = 31; const CMD_SET_CHANNEL = 32; const CMD_SIGN_START = 33; const CMD_SIGN_DATA = 34; const CMD_SIGN_FINISH = 35; const CMD_SEND_TRACE_PATH = 36; const CMD_SET_DEVICE_PIN = 37; const CMD_SET_OTHER_PARAMS = 38; const CMD_SEND_TELEMETRY_REQ = 39; // can deprecate this const CMD_GET_CUSTOM_VARS = 40; const CMD_SET_CUSTOM_VAR = 41; const CMD_GET_ADVERT_PATH = 42; const CMD_GET_TUNING_PARAMS = 43; const CMD_SEND_BINARY_REQ = 50; const CMD_FACTORY_RESET = 51; const CMD_SEND_PATH_DISCOVERY_REQ = 52; const CMD_SET_FLOOD_SCOPE_KEY = 54; // v8+ const CMD_SEND_CONTROL_DATA = 55; // v8+ const CMD_GET_STATS = 56; // v8+, second byte is stats type const CMD_SEND_ANON_REQ = 57; const CMD_SET_AUTOADD_CONFIG = 58; const CMD_GET_AUTOADD_CONFIG = 59; const CMD_GET_ALLOWED_REPEAT_FREQ = 60; const CMD_SET_PATH_HASH_MODE = 61; const CMD_SEND_CHANNEL_DATA = 62; const CMD_SET_DEFAULT_FLOOD_SCOPE = 63; const CMD_GET_DEFAULT_FLOOD_SCOPE = 64; const RESP_CODE_OK = 0; const RESP_CODE_ERR = 1; const RESP_CODE_CONTACTS_START = 2; // first reply to CMD_GET_CONTACTS const RESP_CODE_CONTACT = 3; // multiple of these (after CMD_GET_CONTACTS) const RESP_CODE_END_OF_CONTACTS = 4; // last reply to CMD_GET_CONTACTS const RESP_CODE_SELF_INFO = 5; // reply to CMD_APP_START const RESP_CODE_SENT = 6; // reply to CMD_SEND_TXT_MSG const RESP_CODE_CONTACT_MSG_RECV = 7; // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) const RESP_CODE_CHANNEL_MSG_RECV = 8; // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3) const RESP_CODE_CURR_TIME = 9; // a reply to CMD_GET_DEVICE_TIME const RESP_CODE_NO_MORE_MESSAGES = 10; // a reply to CMD_SYNC_NEXT_MESSAGE const RESP_CODE_EXPORT_CONTACT = 11; const RESP_CODE_BATT_AND_STORAGE = 12; // a reply to a CMD_GET_BATT_AND_STORAGE const RESP_CODE_DEVICE_INFO = 13; // a reply to CMD_DEVICE_QEURY const RESP_CODE_PRIVATE_KEY = 14; // a reply to CMD_EXPORT_PRIVATE_KEY const RESP_CODE_DISABLED = 15; const RESP_CODE_CONTACT_MSG_RECV_V3 = 16; // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) const RESP_CODE_CHANNEL_MSG_RECV_V3 = 17; // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) const RESP_CODE_CHANNEL_INFO = 18; // a reply to CMD_GET_CHANNEL const RESP_CODE_SIGN_START = 19; const RESP_CODE_SIGNATURE = 20; const RESP_CODE_CUSTOM_VARS = 21; const RESP_CODE_ADVERT_PATH = 22; const RESP_CODE_TUNING_PARAMS = 23; const RESP_CODE_STATS = 24; // v8+, second byte is stats type const RESP_CODE_AUTOADD_CONFIG = 25; const RESP_ALLOWED_REPEAT_FREQ = 26; const RESP_CODE_CHANNEL_DATA_RECV = 27; const RESP_CODE_DEFAULT_FLOOD_SCOPE = 28; const ERR_CODE_UNSUPPORTED_CMD = 1; const ERR_CODE_NOT_FOUND = 2; const ERR_CODE_TABLE_FULL = 3; const ERR_CODE_BAD_STATE = 4; const ERR_CODE_FILE_IO_ERROR = 5; const ERR_CODE_ILLEGAL_ARG = 6; // these are _pushed_ to client app at any time const PUSH_CODE_ADVERT = 0x80; const PUSH_CODE_PATH_UPDATED = 0x81; const PUSH_CODE_SEND_CONFIRMED = 0x82; const PUSH_CODE_MSG_WAITING = 0x83; const PUSH_CODE_RAW_DATA = 0x84; const PUSH_CODE_LOGIN_SUCCESS = 0x85; const PUSH_CODE_LOGIN_FAIL = 0x86; const PUSH_CODE_STATUS_RESPONSE = 0x87; const PUSH_CODE_LOG_RX_DATA = 0x88; const PUSH_CODE_TRACE_DATA = 0x89; const PUSH_CODE_NEW_ADVERT = 0x8A; const PUSH_CODE_TELEMETRY_RESPONSE = 0x8B; const PUSH_CODE_BINARY_RESPONSE = 0x8C; const PUSH_CODE_PATH_DISCOVERY_RESPONSE = 0x8D; const PUSH_CODE_CONTROL_DATA = 0x8E; // v8+ const PUSH_CODE_CONTACT_DELETED = 0x8F; // used to notify client app of deleted contact when overwriting oldest const PUSH_CODE_CONTACTS_FULL = 0x90; // used to notify client app that contacts storage is full const BINREQ_STATUS = 0x01; const BINREQ_KEEP_ALIVE = 0x02; const BINREQ_TELEMETRY = 0x03; const BINREQ_MMA = 0x04; const BINREQ_ACL = 0x05; const BINREQ_NEIGHBORS = 0x06; const ANONREQ_REGIONS = 0x01; const ANONREQ_OWNER = 0x02; const ANONREQ_BASIC = 0x03; const LPP_VOLTAGE = 0x74; const LPP_TEMP = 0x67; const LPP_ILLUMINANCE = 0x65; const LPP_PRESENCE = 0x66; const LPP_HUMIDITY = 0x68; const LPP_ACCEL = 0x71; const LPP_BAROMETER = 0x73; const LPP_GYRO = 0x86; const LPP_GPS = 0x88; const STATS_TYPE_CORE = 0; const STATS_TYPE_RADIO = 1; const STATS_TYPE_PACKETS = 2; }