/* * Ademco library * captainwong * 2022-6-13 rewrited this C version */ #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #endif #include "ademco.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <assert.h> #ifdef DEBUG #define dprintf printf #else #define dprintf(...) #endif #define dline dprintf("%d\n", __LINE__); #define dmsg dline; dprintf void ademcoPrint(const uint8_t* buff, int len) { for (int i = 0; i < len; i++) { if (isprint(buff[i])) { printf("%c", buff[i]); } else { printf("\\x%02X", buff[i]); } } printf("\n"); } int ademcoIsMachineStatusEvent(AdemcoEvent ademcoEvent) { return ademcoEvent == EVENT_ARM || ademcoEvent == EVENT_HALFARM || ademcoEvent == EVENT_HALFARM_1456 || ademcoEvent == EVENT_DISARM; } int ademcoIsMachineTypeEvent(AdemcoEvent ademcoEvent) { return ademcoEvent == EVENT_I_AM_NET_MODULE || ademcoEvent == EVENT_I_AM_GPRS || ademcoEvent == EVENT_I_AM_LCD_MACHINE || ademcoEvent == EVENT_I_AM_WIRE_MACHINE || ademcoEvent == EVENT_I_AM_WIFI_MACHINE || ademcoEvent == EVENT_I_AM_3_SECTION_MACHINE || ademcoEvent == EVENT_I_AM_IOT_MACHINE || ademcoEvent == EVENT_I_AM_TRUE_COLOR || ademcoEvent == EVENT_I_AM_GPRS_IOT || ademcoEvent == EVENT_I_AM_GPRS_PHONE || ademcoEvent == EVENT_I_AM_NB_MACHINE; } int ademcoIsEventNeedControlSource(AdemcoEvent ademcoEvent) { return ademcoEvent == EVENT_ARM || ademcoEvent == EVENT_HALFARM || ademcoEvent == EVENT_HALFARM_1456 || ademcoEvent == EVENT_DISARM || ademcoEvent == EVENT_EMERGENCY || ademcoEvent == EVENT_DURESS || ademcoEvent == EVENT_PHONE_USER_CANCLE_ALARM; } AdemcoEvent ademcoGetExceptionEventByResumeEvent(AdemcoEvent resumeEvent) { switch (resumeEvent) { case EVENT_RECONNECT: return EVENT_DISCONNECT; case EVENT_LOST_RECOVER: return EVENT_LOST; case EVENT_SERIAL485CONN: return EVENT_SERIAL485DIS; case EVENT_SUB_MACHINE_SENSOR_RESUME: return EVENT_SUB_MACHINE_SENSOR_EXCEPTION; case EVENT_SUB_MACHINE_POWER_RESUME: return EVENT_SUB_MACHINE_POWER_EXCEPTION; case EVENT_BATTERY_RECOVER: return EVENT_LOWBATTERY; case EVENT_BATTERY_EXCEPTION_RECOVER: return EVENT_BATTERY_EXCEPTION; case EVENT_OTHER_EXCEPTION_RECOVER: return EVENT_OTHER_EXCEPTION; case EVENT_AC_RECOVER: return EVENT_AC_BROKE; case EVENT_SOLARDISTURB_RECOVER: return EVENT_SOLARDISTURB; case EVENT_BADBATTERY_RECOVER: return EVENT_BADBATTERY; default: return EVENT_INVALID_EVENT; } } AdemcoEventLevel ademcoGetEventLevel(AdemcoEvent ademcoEvent) { switch (ademcoEvent) { case EVENT_ARM: case EVENT_DISARM: case EVENT_HALFARM: case EVENT_HALFARM_1456: return EVENT_LEVEL_STATUS; case EVENT_BADBATTERY_RECOVER: case EVENT_AC_RECOVER: case EVENT_RECONNECT: case EVENT_LOST_RECOVER: case EVENT_SERIAL485CONN: case EVENT_SUB_MACHINE_SENSOR_RESUME: case EVENT_SUB_MACHINE_POWER_RESUME: case EVENT_BATTERY_RECOVER: case EVENT_BATTERY_EXCEPTION_RECOVER: case EVENT_OTHER_EXCEPTION_RECOVER: case EVENT_SOLARDISTURB_RECOVER: return EVENT_LEVEL_EXCEPTION_RESUME; case EVENT_AC_BROKE: case EVENT_LOWBATTERY: case EVENT_BADBATTERY: case EVENT_SOLARDISTURB: case EVENT_DISCONNECT: case EVENT_LOST: //case EVENT_DOORRINGING: //case EVENT_CONN_HANGUP: //case EVENT_CONN_RESUME: case EVENT_SUB_MACHINE_SENSOR_EXCEPTION: case EVENT_SUB_MACHINE_POWER_EXCEPTION: case EVENT_BATTERY_EXCEPTION: case EVENT_OTHER_EXCEPTION: return EVENT_LEVEL_EXCEPTION; case EVENT_SERIAL485DIS: case EVENT_BURGLAR: case EVENT_DURESS: case EVENT_EMERGENCY: case EVENT_FIRE: case EVENT_GAS: case EVENT_TAMPER: case EVENT_ZONE_TAMPER: case EVENT_WATER: return EVENT_LEVEL_ALARM; default: return EVENT_LEVEL_NULL; } } const char* ademcoEventToString(AdemcoEvent ademcoEvent) { switch (ademcoEvent) { case EVENT_ARM: return "ARM"; case EVENT_BURGLAR: return "BURGLAR"; case EVENT_DISARM: return "DISARM"; case EVENT_DURESS: return "DURESS"; case EVENT_EMERGENCY: return "EMERGENCY"; case EVENT_FIRE: return "FIRE"; case EVENT_GAS: return "GAS"; case EVENT_HALFARM: case EVENT_HALFARM_1456: return "HALFARM"; case EVENT_TAMPER: return "TAMPER"; case EVENT_ZONE_TAMPER: return "ZONE_TAMPER"; case EVENT_BYPASS: return "BYPASS"; case EVENT_BYPASS_RESUME: return "BYPASS_RESUME"; case EVENT_WATER: return "WATER"; case EVENT_AC_BROKE: return "AC BROKE"; case EVENT_AC_RECOVER: return "AC RECOVER"; case EVENT_LOWBATTERY: return "LOWBATTERY"; case EVENT_BATTERY_RECOVER: return "BATATTERY_RECOVER"; case EVENT_BADBATTERY: return "BADBATTERY"; case EVENT_BADBATTERY_RECOVER: return "BADBATTERY_RECOVER"; case EVENT_SOLARDISTURB: return "SOLARDISTURB"; case EVENT_SOLARDISTURB_RECOVER: return "SOLARDISTURB_RECOVER"; case EVENT_DISCONNECT: return "DISCONNECT"; case EVENT_RECONNECT: return "RECONNECT"; case EVENT_LOST: return "LOST"; case EVENT_LOST_RECOVER: return "LOST_RECOVER"; case EVENT_3100: return "LCD CLEAR EXCEPTION"; case EVENT_BATTERY_EXCEPTION: return "BATTERY_EXCEPTION"; case EVENT_BATTERY_EXCEPTION_RECOVER: return "BATTERY_EXCEPTION_RECOVER"; case EVENT_OTHER_EXCEPTION: return "OTHER_EXCEPTION"; case EVENT_OTHER_EXCEPTION_RECOVER: return "OTHER_EXCEPTION_RECOVER"; case EVENT_SERIAL485DIS: return "485DIS"; case EVENT_SERIAL485CONN: return "485CONN"; case EVENT_DOORRINGING: return "DOORRINGING"; case EVENT_CONN_HANGUP: return "CONN_HANGUP"; case EVENT_CONN_RESUME: return "CONN_RESUME"; case EVENT_DISARM_PWD_ERR: return "DISARM_PWD_ERR"; case EVENT_SUB_MACHINE_SENSOR_EXCEPTION: return "SUB_SENSOR_EXCEPTION"; case EVENT_SUB_MACHINE_SENSOR_RESUME: return "SUB_SENSOR_RESUME"; case EVENT_SUB_MACHINE_POWER_EXCEPTION: return "SUB_POWER_EXCEPTION"; case EVENT_SUB_MACHINE_POWER_RESUME: return "SUB_POWER_RESUME"; case EVENT_COM_PASSTHROUGH: return "COM_PASSTHROUGH"; case EVENT_ENTER_SET_MODE: return "ENTER_SET_MODE"; case EVENT_EXIT_SET_MODE: return "EXIT_SET_MODE"; case EVENT_QUERY_SUB_MACHINE: return "QUERY"; case EVENT_WRITE_TO_MACHINE: return "WRITE_TO_MACHINE"; case EVENT_I_AM_NET_MODULE: return "I_AM_NET_MODULE"; case EVENT_PHONE_USER_SOS: return "SOS"; case EVENT_PHONE_USER_CANCLE_ALARM: return "PHONE_USER_CANCLE_ALARM"; case EVENT_I_AM_GPRS: return "I_AM_GPRS_MACHINE"; case EVENT_I_AM_LCD_MACHINE: return "I_AM_LCD_MACHINE"; case EVENT_I_AM_WIRE_MACHINE: return "I_AM_WIRE_MACHINE"; case EVENT_I_AM_WIFI_MACHINE: return "I_AM_WIFI_MACHINE"; case EVENT_I_AM_3_SECTION_MACHINE: return "I_AM_3_SECTION_MACHINE"; case EVENT_I_AM_IOT_MACHINE: return "I_AM_IOT_MACHINE"; case EVENT_I_AM_TRUE_COLOR: return "I_AM_TRUE_COLOR"; case EVENT_I_AM_GPRS_IOT: return "I_AM_GPRS_IOT"; case EVENT_I_AM_GPRS_PHONE: return "I_AM_GPRS_PHONE"; case EVENT_I_AM_NB_MACHINE: return "I_AM_NB_MACHINE"; case EVENT_SIM_IS_IOT_CARD: return "SIM card is IOT"; case EVENT_SIM_IS_IOT_PLATFORM_CARD: return "SIM card is Platform IOT"; case EVENT_SIM_IS_NOT_IOT_CARD: return "SIM card is not IOT"; case EVENT_ENTER_SETTING_MODE: return "ENTER_SETTING_MODE"; case EVENT_EXIT_SETTING_MODE: return "EXIT_SETTING_MODE"; case EVENT_RESTORE_FACTORY_SETTINGS_710: case EVENT_RESTORE_FACTORY_SETTINGS: return "RESTORE_FACTORY_SETTINGS"; case EVENT_WHAT_IS_YOUR_TYPE: return "WHAT_IS_YOUR_TYPE"; case EVENT_SIGNAL_STRENGTH_CHANGED: return "SIGNAL_STRENGTH_CHANGED"; case EVENT_OFFLINE: return "EVENT_OFFLINE"; case EVENT_ONLINE: return "EVENT_ONLINE"; default: return "Undefined AdemcoEvent"; } } const char* ademcoEventToStringChinese(AdemcoEvent ademcoEvent) { switch (ademcoEvent) { case EVENT_ARM: return "布防"; case EVENT_BURGLAR: return "盗警"; case EVENT_DISARM: return "撤防"; case EVENT_DURESS: return "胁迫"; case EVENT_EMERGENCY: return "紧急报警"; case EVENT_FIRE: return "火警"; case EVENT_GAS: return "煤气"; case EVENT_HALFARM: case EVENT_HALFARM_1456: return "半布防"; case EVENT_TAMPER: return "防拆"; case EVENT_ZONE_TAMPER: return "防区防拆"; case EVENT_WATER: return "水警"; case EVENT_BYPASS: return "旁路"; case EVENT_BYPASS_RESUME: return "解除旁路"; case EVENT_AC_BROKE: return "主机AC掉电"; case EVENT_AC_RECOVER: return "主机AC恢复"; case EVENT_LOWBATTERY: return "低电"; case EVENT_BATTERY_RECOVER: return "复电"; case EVENT_BADBATTERY: return "坏电"; case EVENT_BADBATTERY_RECOVER: return "坏电恢复"; case EVENT_SOLARDISTURB: return "光扰"; case EVENT_SOLARDISTURB_RECOVER: return "光扰恢复"; case EVENT_DISCONNECT: return "失效"; case EVENT_RECONNECT: return "恢复"; case EVENT_LOST: return "失联"; case EVENT_LOST_RECOVER: return "恢复"; case EVENT_3100: return "液晶主机清除异常指示"; case EVENT_BATTERY_EXCEPTION: return "防区电源故障"; case EVENT_BATTERY_EXCEPTION_RECOVER: return "防区电源故障恢复"; case EVENT_OTHER_EXCEPTION: return "防区其他故障"; case EVENT_OTHER_EXCEPTION_RECOVER: return "防区其他故障恢复"; case EVENT_SERIAL485DIS: return "485断开"; case EVENT_SERIAL485CONN: return "485恢复"; case EVENT_DOORRINGING: return "门铃"; case EVENT_CONN_HANGUP: return "链路挂起"; case EVENT_CONN_RESUME: return "链路恢复"; case EVENT_DISARM_PWD_ERR: return "撤防密码错误"; case EVENT_SUB_MACHINE_SENSOR_EXCEPTION: return "分防区异常"; case EVENT_SUB_MACHINE_SENSOR_RESUME: return "分防区恢复"; case EVENT_SUB_MACHINE_POWER_EXCEPTION: return "分防区电源异常"; case EVENT_SUB_MACHINE_POWER_RESUME: return "分防区电源恢复"; case EVENT_COM_PASSTHROUGH: return "串口透传"; case EVENT_ENTER_SET_MODE: return "进入设置状态"; case EVENT_EXIT_SET_MODE: return "拒绝/停止设置"; case EVENT_QUERY_SUB_MACHINE: return "查询"; case EVENT_WRITE_TO_MACHINE: return "写入主机信息"; case EVENT_I_AM_NET_MODULE: return "我是网络模块"; case EVENT_PHONE_USER_SOS: return "手机用户SOS"; case EVENT_PHONE_USER_CANCLE_ALARM: return "手机用户消警"; case EVENT_I_AM_GPRS: return "我是GPRS主机"; case EVENT_I_AM_LCD_MACHINE: return "我是液晶主机"; case EVENT_I_AM_WIRE_MACHINE: return "我是网线主机"; case EVENT_I_AM_WIFI_MACHINE: return "我是WiFi主机"; case EVENT_I_AM_3_SECTION_MACHINE: return "我是三区段主机"; case EVENT_I_AM_IOT_MACHINE: return "我是物联卡主机"; case EVENT_I_AM_TRUE_COLOR: return "我是真彩主机"; case EVENT_I_AM_GPRS_IOT: return "我是简化版物联卡主机"; case EVENT_I_AM_GPRS_PHONE: return "我是GPRS主机能打电话"; case EVENT_I_AM_NB_MACHINE: return "我是NB报警接收主机"; case EVENT_SIM_IS_IOT_CARD: return "SIM卡为物联卡"; case EVENT_SIM_IS_IOT_PLATFORM_CARD: return "SIM卡为平台物联卡"; case EVENT_SIM_IS_NOT_IOT_CARD: return "SIM卡为非物联卡"; case EVENT_ENTER_SETTING_MODE: return "主机进入设置状态"; case EVENT_EXIT_SETTING_MODE: return "主机退出设置状态"; case EVENT_RESTORE_FACTORY_SETTINGS_710: case EVENT_RESTORE_FACTORY_SETTINGS: return "主机恢复出厂设置"; case EVENT_WHAT_IS_YOUR_TYPE: return "索要主机类型"; case EVENT_SIGNAL_STRENGTH_CHANGED: return "信号强度变化"; case EVENT_OFFLINE: return "上线"; case EVENT_ONLINE: return "离线"; default: return "未定义的安定宝事件"; } } static const uint16_t crc16Table[256] = { /* DEFINE THE FIRST ORDER POLYINOMIAL TABLE */ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, }; uint16_t ademcoCRC16(const uint8_t* buff, int len) { uint16_t crc = 0; while (len--) { crc = (crc >> 8) ^ crc16Table[(crc ^ *buff++) & 0xFF]; } return crc; } int ademcoAppendDataSegment(uint8_t* buff, AdemcoId ademcoId, AdemcoEvent ademcoEvent, AdemcoGG gg, AdemcoZone zone) { char* p = (char*)buff; if (ademcoEvent == EVENT_INVALID_EVENT) { *p++ = '['; *p++ = ']'; return ADEMCO_PACKET_DATA_SEGMENT_EMPTY_LEN; } else { *p++ = '['; *p++ = '#'; snprintf(p, 6 + 1, "%06X", ademcoId % ADEMCO_ID_SENTINEL); p += 6; *p++ = '|'; snprintf(p, 4 + 1, "%04d", ademcoEvent % ADEMCO_EVENT_SENTINEL); p += 4; *p++ = ' '; if (gg == 0xEE) { *p++ = 'E'; *p++ = 'E'; } else if (gg == 0xCC) { *p++ = 'C'; *p++ = 'C'; } else { *p++ = ((gg / 10) & 0x0F) + '0'; *p++ = ((gg % 10) & 0x0F) + '0'; } *p++ = ' '; snprintf(p, 3 + 1, "%03d", zone % ADEMCO_ZONE_SENTINEL); p += 3; *p++ = ']'; assert(p - (char*)buff == ADEMCO_PACKET_DATA_SEGMENT_FULL_LEN); *p++ = '\0'; // for debug convenience return ADEMCO_PACKET_DATA_SEGMENT_FULL_LEN; } } int ademcoAppendDataSegment2(AdemcoDataSegment* dataSegment, AdemcoId ademcoId, AdemcoEvent ademcoEvent, AdemcoGG gg, AdemcoZone zone) { dataSegment->ademcoId = ademcoId; dataSegment->ademcoEvent = ademcoEvent; dataSegment->gg = gg; dataSegment->zone = zone; return dataSegment->raw_len = ademcoAppendDataSegment(dataSegment->raw, ademcoId, ademcoEvent, gg, zone); } AdemcoParseResult ademcoParseDataSegment(const uint8_t* packet, int packet_len, AdemcoDataSegment* dataSegment) { if (packet_len == ADEMCO_PACKET_DATA_SEGMENT_EMPTY_LEN && packet[0] == '[' && packet[1] == ']') { memset(dataSegment, 0, sizeof(*dataSegment)); strcpy((char*)dataSegment->raw, "[]"); dataSegment->raw_len = 2; return RESULT_OK; } else if (packet_len >= ADEMCO_PACKET_DATA_SEGMENT_FULL_LEN && packet[0] == '[' && packet[packet_len - 1] == ']') { // [#000000|1400 00 000] do { const uint8_t* p = packet + 2; int acct_len = packet_len - 15; if (acct_len < 6) { break; } char temp[ADEMCO_PACKET_DATA_SEGMENT_FULL_LEN_MAX] = {0}; strncpy(temp, (const char*)p, acct_len); temp[acct_len] = '\0'; dataSegment->ademcoId = (AdemcoId)strtoul(temp, NULL, 16); p += acct_len; if (*p++ != '|') { break; } strncpy(temp, (const char*)p, 4); temp[4] = '\0'; dataSegment->ademcoEvent = (AdemcoEvent)(atoi(temp)); p += 4; if (*p++ != ' ') { break; } if (*p == 'E' && *(p + 1) == 'E') { dataSegment->gg = 0xEE; } else if (*p == 'C' && *(p + 1) == 'C') { dataSegment->gg = 0xCC; } else { dataSegment->gg = (AdemcoGG)((*p - '0') * 10 + (*(p + 1) - '0')); } p += 2; if (*p++ != ' ') { break; } strncpy(temp, (const char*)p, 3); temp[3] = '\0'; dataSegment->zone = atoi(temp); if (dataSegment->raw != packet) { memcpy(dataSegment->raw, packet, packet_len); } dataSegment->raw_len = packet_len; return RESULT_OK; } while (0); } return RESULT_ERROR; } int ademcoDataSegmentToCongwinFe100(uint8_t* fe100, const AdemcoDataSegment* dataSegment) { if (dataSegment->raw_len == ADEMCO_PACKET_DATA_SEGMENT_EMPTY_LEN) { return 0; } else if (dataSegment->raw_len >= ADEMCO_PACKET_DATA_SEGMENT_FULL_LEN) { int ndx = 0; char* p = (char*)fe100; *p++ = '\n'; *p++ = ' '; *p++ = '0'; // RG *p++ = '0'; *p++ = ' '; sprintf(p, "%08d", dataSegment->ademcoId); p += 8; *p++ = ' '; // 18 *p++ = '1'; *p++ = '8'; *p++ = ' '; // E for open, R for close if (dataSegment->ademcoEvent / 1000 == 1 || dataSegment->ademcoEvent == EVENT_HALFARM) { *p++ = 'E'; } else { *p++ = 'R'; } if (dataSegment->ademcoEvent == EVENT_OFFLINE) { *p++ = '3'; *p++ = 'B'; *p++ = '2'; } else if (dataSegment->ademcoEvent == EVENT_ONLINE) { *p++ = '3'; *p++ = 'B'; *p++ = '0'; } else { sprintf(p, "%03d", dataSegment->ademcoEvent % 1000); p += 3; } *p++ = ' '; // FE100, gg === 0 *p++ = '0'; *p++ = '0'; *p++ = ' '; if (ademcoIsMachineStatusEvent(dataSegment->ademcoEvent)) { *p++ = 'U'; // user event } else { *p++ = 'C'; // zone event } sprintf(p, "%03d", dataSegment->zone); p += 3; *p++ = ' '; *p++ = '\r'; return p - (char*)fe100; } return 0; } #ifdef SWIG static #endif const uint8_t* ademcoXDataGetValidContentAddr(const AdemcoXDataSegment* xdata) { if (xdata->lenghFormat == TWO_HEX && xdata->raw_len > 4) { return xdata->raw + 3; } else if (xdata->lenghFormat == FOUR_DECIMAL && xdata->raw_len > 6) { return xdata->raw + 5; } else { return NULL; } } int ademcoXDataMemcmp(const AdemcoXDataSegment* xdata, const void* buf, int buf_len) { if (ademcoXDataGetValidContentLen(xdata) != buf_len) return 0; const uint8_t* p = ademcoXDataGetValidContentAddr(xdata); if (!p) return 0; return memcmp(p, buf, buf_len); } int ademcoXDataGetValidContentLen(const AdemcoXDataSegment* xdata) { if (xdata->lenghFormat == TWO_HEX && xdata->raw_len > 4) { return xdata->raw_len - 4; } else if (xdata->lenghFormat == FOUR_DECIMAL && xdata->raw_len > 6) { return xdata->raw_len - 6; } else { return 0; } } int ademcoMakeXData(AdemcoXDataSegment* xdata, AdemcoXDataLengthFormat xlf, AdemcoXDataTransform xtr, const uint8_t* content, int len) { uint8_t transformed[ADEMCO_PACKET_XDATA_MAX_LEN]; uint16_t translen = len; if (xtr == AdemcoXDataTransform_as_is) { memcpy(transformed, content, len); } else { if (len * 2 < ADEMCO_PACKET_XDATA_MAX_LEN) { translen = ademcoHexArrayToStr((char*)transformed, content, len); } else { return ADEMCO_ERR; } } xdata->lenghFormat = xlf; if (xlf == TWO_HEX && translen + 4 < ADEMCO_PACKET_XDATA_MAX_LEN) { uint8_t* p = xdata->raw; *p++ = '['; *p++ = (translen & 0xFFFF) >> 8; *p++ = (translen & 0xFFFF) & 0xFF; memcpy(p, transformed, translen); p += translen; *p++ = ']'; xdata->raw_len = p - xdata->raw; return ADEMCO_OK; } else if (xlf == FOUR_DECIMAL && translen + 6 < ADEMCO_PACKET_XDATA_MAX_LEN) { uint8_t* p = xdata->raw; *p++ = '['; char temp[5]; snprintf(temp, sizeof(temp), "%04d", translen & 0xFFFF); memcpy(p, temp, 4); p += 4; memcpy(p, transformed, translen); p += translen; *p++ = ']'; xdata->raw_len = p - xdata->raw; return ADEMCO_OK; } else { return ADEMCO_ERR; } } int isAdemcoPacketId(const char* standard, const char* id, int len) { return strncmp(standard, id, strlen(standard)) == 0; } static const char* const ids[AID_COUNT] = { ADEMCO_PACKET_ID_NULL, ADEMCO_PACKET_ID_ACK, ADEMCO_PACKET_ID_NAK, ADEMCO_PACKET_ID_DUH, ADEMCO_PACKET_ID_HB, ADEMCO_PACKET_ID_ADM_CID, ADEMCO_PACKET_ID_MOD_REG, ADEMCO_PACKET_ID_REG_RSP, }; AdemcoPacketId getAdemcoPacketId(const char* id, int len) { for (int i = 0; i < AID_COUNT; i++) { if (isAdemcoPacketId(ids[i], id, len)) { return (AdemcoPacketId)(i); } } return AID_INVALID; } const char* admecoPacketIdToString(AdemcoPacketId id) { if (0 <= id && id < AID_COUNT) { return ids[id]; } else { return "Unkown AdemcoPacketId"; } } static void getNowTimestamp(char* buff) { time_t now = time(NULL); struct tm* tm = localtime(&now); snprintf(buff, ADEMCO_PACKET_TIMESTAMP_LEN + 1, "_%02d:%02d:%02d,%02d-%02d-%04d", tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900); } int ademcoMakeEmptyDataPacket(uint8_t* dst_buff, int len, const char* id, uint16_t seq, const char* acct, AdemcoId ademcoId) { uint8_t buff[ADEMCO_PACKET_MAX_LEN]; uint8_t* p = buff; uint8_t* pcrc = buff + 1; uint8_t* plen = buff + 5; uint8_t* pid = buff + 9; buff[0] = ADEMCO_PACKET_PREFIX; memcpy(pid, id, strlen(id)); p = pid + strlen(id); snprintf((char*)p, 5, "%04d", seq); p += 4; memcpy(p, ADEMCO_RRCVR_DEFAULT, strlen(ADEMCO_RRCVR_DEFAULT)); p += strlen(ADEMCO_RRCVR_DEFAULT); memcpy(p, ADEMCO_LPREF_DEFAULT, strlen(ADEMCO_LPREF_DEFAULT)); p += strlen(ADEMCO_LPREF_DEFAULT); *p++ = '#'; if (acct != NULL && strlen(acct) > 0) { memcpy(p, acct, strlen(acct)); p += strlen(acct); } else { snprintf((char*)p, 7, "%06X", ademcoId); p += 6; } *p++ = '['; *p++ = ']'; getNowTimestamp((char*)p); p += ADEMCO_PACKET_TIMESTAMP_LEN; *p++ = ADEMCO_PACKET_SUFIX; char temp[5]; int packet_len = p - buff; // \n crc len \r int ademco_len = packet_len - 1 - 4 - 4 - 1; snprintf(temp, 5, "%04X", ademco_len); memcpy(plen, temp, 4); uint16_t crc = ademcoCRC16(pid, ademco_len); snprintf(temp, 5, "%04X", crc); memcpy(pcrc, temp, 4); if (dst_buff != NULL && len > packet_len) { memcpy(dst_buff, buff, packet_len); dst_buff[packet_len] = '\0'; // for debug convenience return packet_len; } else { return 0; } } int ademcoMakeNullPacket(uint8_t* buff, int len, uint16_t seq, const char* acct, AdemcoId ademcoId) { return ademcoMakeEmptyDataPacket(buff, len, ADEMCO_PACKET_ID_NULL, seq, acct, ademcoId); } int ademcoMakeAckPacket(uint8_t* buff, int len, uint16_t seq, const char* acct, AdemcoId ademcoId) { return ademcoMakeEmptyDataPacket(buff, len, ADEMCO_PACKET_ID_ACK, seq, acct, ademcoId); } int ademcoMakeNakPacket(uint8_t* buff, int len, uint16_t seq, const char* acct, AdemcoId ademcoId) { return ademcoMakeEmptyDataPacket(buff, len, ADEMCO_PACKET_ID_NAK, seq, acct, ademcoId); } int ademcoMakeHbPacket(uint8_t* dst_buff, int len, uint16_t seq, const char* acct, AdemcoId ademcoId, AdemcoEvent ademcoEvent, AdemcoGG gg, AdemcoZone zone, AdemcoXDataSegment* xdata) { char buff[ADEMCO_PACKET_MAX_LEN]; char* p = buff; char* pcrc = buff + 1; char* plen = buff + 5; char* pid = buff + 9; buff[0] = ADEMCO_PACKET_PREFIX; memcpy(pid, ADEMCO_PACKET_ID_HB, strlen(ADEMCO_PACKET_ID_HB)); p = pid + strlen(ADEMCO_PACKET_ID_HB); snprintf((char*)p, 5, "%04d", seq); p += 4; memcpy(p, ADEMCO_RRCVR_DEFAULT, strlen(ADEMCO_RRCVR_DEFAULT)); p += strlen(ADEMCO_RRCVR_DEFAULT); memcpy(p, ADEMCO_LPREF_DEFAULT, strlen(ADEMCO_LPREF_DEFAULT)); p += strlen(ADEMCO_LPREF_DEFAULT); *p++ = '#'; if (acct != NULL) { memcpy(p, acct, strlen(acct)); p += strlen(acct); } else { snprintf(p, 7, "%06X", ademcoId); p += 6; } p += ademcoAppendDataSegment((uint8_t*)p, ademcoId, ademcoEvent, gg, zone); if (xdata && xdata->raw_len > 0) { memcpy(p, xdata->raw, xdata->raw_len); p += xdata->raw_len; } getNowTimestamp(p); p += ADEMCO_PACKET_TIMESTAMP_LEN; *p++ = ADEMCO_PACKET_SUFIX; char temp[5]; int packet_len = p - buff; // \n crc len \r int ademco_len = packet_len - 1 - 4 - 4 - 1; snprintf(temp, 5, "%04X", ademco_len); memcpy(plen, temp, 4); uint16_t crc = ademcoCRC16((const uint8_t*)pid, ademco_len); snprintf(temp, 5, "%04X", crc); memcpy(pcrc, temp, 4); if (dst_buff != NULL && len > packet_len) { memcpy(dst_buff, buff, packet_len); dst_buff[packet_len] = '\0'; // for debug convenience return packet_len; } return 0; } static void copyAcct2AdemcoPacket(AdemcoPacket* pkt, const char* acct) { int len = strlen(acct); if (len > ADEMCO_PACKET_ACCT_MAX_LEN) { len = ADEMCO_PACKET_ACCT_MAX_LEN; } memcpy(pkt->acct, acct, len); pkt->acct[len] = '\0'; } int ademcoMakeNullPacket2(AdemcoPacket* pkt, uint16_t seq, const char* acct, AdemcoId ademcoId) { pkt->seq = seq; copyAcct2AdemcoPacket(pkt, acct); pkt->data.ademcoId = ademcoId; return pkt->raw_len = ademcoMakeNullPacket(pkt->raw, sizeof(pkt->raw), seq, acct, ademcoId); } int ademcoMakeAckPacket2(AdemcoPacket* pkt, uint16_t seq, const char* acct, AdemcoId ademcoId) { pkt->seq = seq; copyAcct2AdemcoPacket(pkt, acct); pkt->data.ademcoId = ademcoId; return pkt->raw_len = ademcoMakeAckPacket(pkt->raw, sizeof(pkt->raw), seq, acct, ademcoId); } int ademcoMakeNakPacket2(AdemcoPacket* pkt, uint16_t seq, const char* acct, AdemcoId ademcoId) { pkt->seq = seq; copyAcct2AdemcoPacket(pkt, acct); pkt->data.ademcoId = ademcoId; return pkt->raw_len = ademcoMakeNakPacket(pkt->raw, sizeof(pkt->raw), seq, acct, ademcoId); } int ademcoMakeHbPacket2(AdemcoPacket* pkt, uint16_t seq, const char* acct, AdemcoId ademcoId, AdemcoEvent ademcoEvent, AdemcoGG gg, AdemcoZone zone, AdemcoXDataSegment* xdata) { pkt->seq = seq; copyAcct2AdemcoPacket(pkt, acct); ademcoAppendDataSegment2(&pkt->data, ademcoId, ademcoEvent, gg, zone); if (xdata && xdata->raw_len > 0) { pkt->xdata.lenghFormat = xdata->lenghFormat; memcpy(pkt->xdata.raw, xdata->raw, xdata->raw_len); pkt->xdata.raw_len = xdata->raw_len; } else { memset(&pkt->xdata, 0, sizeof(*xdata)); } return pkt->raw_len = ademcoMakeHbPacket(pkt->raw, sizeof(pkt->raw), seq, acct, ademcoId, ademcoEvent, gg, zone, xdata); } AdemcoParseResult ademcoPacketParse(const uint8_t* buff, int len, AdemcoPacket* pkt, int* cb_commited) { if (len < 9) { return RESULT_NOT_ENOUGH; } do{ const uint8_t* p = buff; if (*p++ != ADEMCO_PACKET_PREFIX) { dline; break; } char temp[5]; // crc memcpy(temp, p, 4); temp[4] = '\0'; uint16_t crc = strtoul(temp, NULL, 16) & 0xFFFF; p += 4; // len memcpy(temp, p, 4); temp[4] = '\0'; pkt->len = strtoul(temp, NULL, 16); p += 4; int len_needed = 9 + pkt->len + 1; if (len < len_needed) { return RESULT_NOT_ENOUGH; } if (len_needed >= ADEMCO_PACKET_MAX_LEN) { dline; return RESULT_ERROR; } const uint8_t* pid = p; const uint8_t* pcr = pid + pkt->len; if (pcr - buff > len || *pcr != ADEMCO_PACKET_SUFIX) { dline; break; } pkt->crc = ademcoCRC16(pid, pkt->len); if (pkt->crc != crc) { dline; break; } // id if (*pid != '\"') { dline; break; } p = pid + 1; while (p < pcr && *p != '\"') { p++; } if (p >= pcr || *p != '\"') { dline; break; } pkt->id = getAdemcoPacketId((const char*)pid, ++p - pid); if (pkt->id == AID_INVALID) { dline; break; } // seq const uint8_t* pseq = p; while (p < pcr && *p != 'R' && *p != 'L') { p++; } if (p - pseq != 4 || (*p != 'R' && *p != 'L')) { dline; break; } memcpy(temp, pseq, 4); temp[4] = '\0'; pkt->seq = strtoul(temp, NULL, 10) & 0xFFFF; if (pkt->seq >= 10000) { pkt->seq = 0; } // *rrcvr if (*p == 'R') { // rrcvr exists const uint8_t* prcvr = p; while (p < pcr && *p != 'L' && *p != '#') { p++; } if (p >= pcr || (*p != 'L' && *p != '#')) { dline; break; } // only check if format is correct, ignore it's content } else if (*p == 'L') { // rrcvr not exits // pass } else { dline; break; } // lpref if (*p != 'L') { dline; break; } while (p < pcr && *p != '#') { p++; } if (p >= pcr || *p != '#') { break; } // only check if format is correct, ignore it's content // pass // #acct const uint8_t* pacct = ++p; while (p < pcr && *p != '[') { if (!isalnum(*p)) { p = NULL; break; } p++; } if (p == NULL || p >= pcr || *p != '[' || p - pacct >= ADEMCO_PACKET_ACCT_MAX_LEN) { dline; break; } strncpy(pkt->acct, (const char*)pacct, p - pacct); pkt->acct[p - pacct] = '\0'; // data const uint8_t* pdata = p; while (p < pcr && *p != ']') { p++; } if (p >= pcr || *p != ']') { dline; break; } if (ademcoParseDataSegment(pdata, ++p - pdata, &pkt->data) != RESULT_OK) { dline; break; } // *xdata if (*p == '[') { // xdata exists const uint8_t* pxdata = p++; AdemcoXDataLengthFormat xlf = FOUR_DECIMAL; int valid_len; for (int i = 0; i < 4; i++) { if (!isxdigit(*(p + i))) { xlf = TWO_HEX; } } if (xlf == FOUR_DECIMAL) { memcpy(temp, pxdata + 1, 4); temp[4] = '\0'; valid_len = strtoul(temp, NULL, 16); p += 4 + valid_len; } else { valid_len = pxdata[1] * 256 + pxdata[2]; p += 2 + valid_len; } if (p >= pcr || *p != ']' || *(p + 1) != '_') { dline; break; } pkt->xdata.lenghFormat = xlf; pkt->xdata.raw_len = ++p - pxdata; memcpy(pkt->xdata.raw, pxdata, pkt->xdata.raw_len); } else { pkt->xdata.raw_len = 0; } // timestamp if (pcr - p != ADEMCO_PACKET_TIMESTAMP_LEN) { dline; break; } { struct tm tm; int ret = sscanf((const char*)p, "_%02d:%02d:%02d,%02d-%02d-%04d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tm.tm_mon, &tm.tm_mday, &tm.tm_year); if (ret != 6) { // wont break } tm.tm_year -= 1900; tm.tm_mon--; tm.tm_isdst = -1; pkt->timestamp = mktime(&tm); if (pkt->timestamp < 0) { // use local time instead pkt->timestamp = time(NULL); } p += ADEMCO_PACKET_TIMESTAMP_LEN; } if (p++ != pcr) { dline; break; } pkt->raw_len = *cb_commited = p - buff; if (pkt->raw != buff) { memcpy(pkt->raw, buff, pkt->raw_len); } return RESULT_OK; } while (0); return RESULT_ERROR; } int ademcoHiLoArrayToDecStr(char* str, const uint8_t* arr, int len) { char* p = str; for (int i = 0; i < len; i++) { char c = (arr[i] >> 4) & 0x0F; if (c > 9) { return p - str; } *p++ = c + '0'; c = (arr[i] & 0x0F); if (c > 9) { return p - str; } *p++ = c + '0'; } return p - str; } int ademcoDecStrToHiLoArray(uint8_t* arr, int len, const char* str) { uint8_t* p = arr; int slen = strlen(str); if (slen > len * 2) { slen = len * 2; } for (int i = 0; i < slen; i += 2) { char hi = str[i]; if ('0' <= hi && hi <= '9') { if (i + 1 < slen) { char lo = str[i + 1]; if ('0' <= lo && lo <= '9') { *p++ = (hi << 4) | (lo & 0x0F); } else { *p++ = (hi << 4) | 0x0F; break; } } else { *p++ = (hi << 4) | 0x0F; break; } } else { break; } } while (p - arr < len) { *p++ = 0xFF; } return len; } static uint8_t hex2char(uint8_t h) { h &= 0x0F; if (h > 9) { return 'A' + h - 10; } else { return '0' + h; } } int ademcoHexArrayToStr(char* str, const uint8_t* arr, int len) { uint8_t c = 0; char* p = str; for (int i = 0; i < len; i++) { *p++ = hex2char((arr[i] >> 4) & 0x0F); *p++ = hex2char(arr[i] & 0x0F); } return p - str; } static uint8_t char2hex(uint8_t c) { if ('0' <= c && c <= '9') { return c - '0'; } else if ('A' <= c && c <= 'F') { return c - 'A' + 10; } else if ('a' <= c && c <= 'f') { return c - 'a' + 10; } else { return 0xFF; } } int ademcoHexStrToArray(uint8_t* arr, const char* str, uint8_t padding) { uint8_t* p = arr; uint8_t hi = 0, lo = 0; int slen = strlen(str); padding &= 0x0F; for (int i = 0; i < slen / 2; i++) { char c = str[i * 2]; if ((hi = char2hex(c)) == 0xFF) { return 0; } if (i * 2 + 1 < slen) { c = str[i * 2 + 1]; if ((lo = char2hex(c)) == 0xFF) { return 0; } } else { lo = padding; } *p++ = ((hi << 4) & 0xF0) | (lo & 0x0F); } return p - arr; } int ademcoHexStrToArrayN(uint8_t* arr, const char* str, int len, uint8_t padding) { uint8_t* p = arr; uint8_t hi = 0, lo = 0; padding &= 0x0F; for (int i = 0; i < len / 2; i++) { char c = str[i * 2]; if ((hi = char2hex(c)) == 0xFF) { return 0; } if (i * 2 + 1 < len) { c = str[i * 2 + 1]; if ((lo = char2hex(c)) == 0xFF) { return 0; } } else { lo = padding; } *p++ = ((hi << 4) & 0xF0) | (lo & 0x0F); } return p - arr; } int ademcoHexStrToArrayN_allow_non_hex_str(uint8_t* arr, const char* str, int len, uint8_t padding) { uint8_t* p = arr; uint8_t hi = 0, lo = 0; padding &= 0x0F; for (int i = 0; i < len / 2; i++) { char c = str[i * 2]; if ((hi = char2hex(c)) == 0xFF) { hi = padding; } if (i * 2 + 1 < len) { c = str[i * 2 + 1]; if ((lo = char2hex(c)) == 0xFF) { lo = padding; } } else { lo = padding; } *p++ = ((hi << 4) & 0xF0) | (lo & 0x0F); } return p - arr; }