├── APCI.cpp ├── APCI.h ├── APDU.cpp ├── APDU.h ├── ASDU.cpp ├── ASDU.h ├── iec104.cpp └── iec104.h /APCI.cpp: -------------------------------------------------------------------------------- 1 | #include "APCI.h" 2 | #include 3 | 4 | <<<<<<< HEAD 5 | using namespace std; 6 | 7 | ======= 8 | >>>>>>> 0b7e00f4a8489eb33fad1bf1649051befee613c1 9 | APCI::APCI() 10 | { 11 | clear(); 12 | } 13 | 14 | APCI::~APCI() 15 | { 16 | } 17 | 18 | void APCI::clear() 19 | { 20 | start = STARTHEAD; 21 | length = 4; 22 | format = U_FORMAT; 23 | func = 0; 24 | ssn = 0; 25 | rsn = 0; 26 | } 27 | 28 | unsigned char APCI::get(unsigned char* data) //moving inf. from object to data[] 29 | { 30 | if (start != 0x68) 31 | return 0; 32 | memset(data, 0, 6); 33 | data[0] = start; 34 | data[1] = length; 35 | 36 | switch (format) 37 | { 38 | case I_FORMAT: 39 | data[2] = (ssn & 0x7f) << 1; 40 | data[2] &= 0xfe; 41 | data[3] = (ssn >> 7); 42 | data[4] = (rsn & 0x7f) << 1; 43 | data[4] &= 0xfe; 44 | data[5] = (rsn >> 7); 45 | break; 46 | case S_FORMAT: 47 | data[2] = 1; 48 | data[4] = (rsn & 0x7f) << 1; 49 | data[4] &= 0xfe; 50 | data[5] = (rsn >> 7); 51 | break; 52 | case U_FORMAT: 53 | data[2] = func | 0x03; 54 | break; 55 | default: 56 | return 0; 57 | } 58 | return 6; 59 | } 60 | 61 | <<<<<<< HEAD 62 | void APCI::set(unsigned char* data) //fill the object with inf. from data[] 63 | ======= 64 | void APCI::set(const unsigned char* data) //fill the object with inf. from data[] 65 | >>>>>>> 0b7e00f4a8489eb33fad1bf1649051befee613c1 66 | { 67 | start = data[0]; 68 | length = data[1]; 69 | if (data[2] & 0x01) 70 | format = data[2] & 0x03; 71 | else 72 | format = I_FORMAT; 73 | switch (format) 74 | { 75 | case I_FORMAT: 76 | func = 0; 77 | ssn = 0x80 * data[3] + (data[2] >> 1); 78 | rsn = 0x80 * data[5] + (data[4] >> 1); 79 | break; 80 | case S_FORMAT: 81 | func = 0; 82 | ssn = 0; 83 | rsn = 0x80 * data[5] + (data[4] >> 1); 84 | break; 85 | case U_FORMAT: 86 | func = data[2] & 0xfc; 87 | break; 88 | } 89 | } 90 | 91 | bool APCI::valid() 92 | { 93 | if (start != 0x68) 94 | return false; 95 | switch (format) 96 | { 97 | case I_FORMAT: 98 | if ((length<6) || (length>253)) 99 | return false; 100 | break; 101 | case S_FORMAT: 102 | if (length != 4) 103 | return false; 104 | if (ssn != 0) 105 | return false; 106 | break; 107 | case U_FORMAT: 108 | if (length != 4) 109 | return false; 110 | break; 111 | default: 112 | return false; 113 | } 114 | return true; 115 | } 116 | <<<<<<< HEAD 117 | ======= 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | >>>>>>> 0b7e00f4a8489eb33fad1bf1649051befee613c1 126 | -------------------------------------------------------------------------------- /APCI.h: -------------------------------------------------------------------------------- 1 | #define STARTHEAD 0x68; 2 | 3 | #define I_FORMAT 0x00 4 | #define S_FORMAT 0x01 5 | #define U_FORMAT 0x03 6 | #define STARTDTACT 0x04 7 | #define STARTDTCON 0x08 8 | #define STOPDTACT 0x10 9 | #define STOPDTCON 0x20 10 | #define TESTFRACT 0x40 11 | #define TESTFRCON 0x80 12 | 13 | class APCI 14 | { 15 | public: 16 | unsigned char start; 17 | unsigned char length; 18 | unsigned char format; 19 | unsigned char func; 20 | unsigned char ssn; 21 | unsigned char rsn; 22 | APCI(); 23 | virtual ~APCI(); 24 | void clear(); 25 | unsigned char get(unsigned char* data); //returns only err code 26 | <<<<<<< HEAD 27 | void set(unsigned char* data); 28 | ======= 29 | void set(const unsigned char* data); 30 | >>>>>>> 0b7e00f4a8489eb33fad1bf1649051befee613c1 31 | bool valid(); 32 | }; 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | /* yuriy: code from example 41 | #define I_FORMAT 0x00 42 | #define S_FORMAT 0x01 43 | #define U_FORMAT 0x03 44 | #define STARTDTACT 0x04 45 | #define STARTDTCON 0x08 46 | #define STOPDTACT 0x10 47 | #define STOPDTCON 0x20 48 | #define TESTFRACT 0x40 49 | #define TESTFRCON 0x80 50 | <<<<<<< HEAD 51 | ======= 52 | 53 | >>>>>>> 0b7e00f4a8489eb33fad1bf1649051befee613c1 54 | class APCI{ 55 | public: 56 | BYTE start; 57 | int length; 58 | int format; 59 | int func; 60 | int ssn; 61 | int rsn; 62 | APCI(); 63 | virtual ~APCI(); 64 | void clear(); 65 | int get(BYTE* data); 66 | void set(const BYTE* data); 67 | bool valid(); 68 | }; 69 | */ 70 | <<<<<<< HEAD 71 | 72 | ======= 73 | >>>>>>> 0b7e00f4a8489eb33fad1bf1649051befee613c1 74 | -------------------------------------------------------------------------------- /APDU.cpp: -------------------------------------------------------------------------------- 1 | #include "APDU.h" 2 | 3 | APDU::APDU() 4 | { 5 | apci = new APCI(); 6 | asdu = new ASDU(); 7 | clear(); 8 | } 9 | 10 | APDU::~APDU() 11 | { 12 | delete apci; 13 | delete asdu; 14 | } 15 | 16 | void APDU::clear() 17 | { 18 | apci->clear(); 19 | asdu->clear(); 20 | } 21 | 22 | int APDU::get(unsigned char* data) 23 | { 24 | if (!apci->valid()) 25 | return 0; 26 | apci->get(data); 27 | if (apci->format==I_FORMAT) 28 | asdu->get(&data[6]); 29 | return apci->length+2; 30 | } 31 | 32 | bool APDU::set(unsigned char* data) 33 | { 34 | apci->set(data); 35 | if (!apci->valid()) 36 | return false; 37 | if (apci->format==I_FORMAT) 38 | asdu->set(&data[6]); 39 | return true; 40 | } 41 | 42 | bool APDU::valid() 43 | { 44 | if (!apci->valid()) 45 | return false; 46 | return asdu->valid(); 47 | } 48 | 49 | void APDU::addIO(int address) 50 | { 51 | InformationObject io; 52 | io.objectAdr = address; 53 | int len=asdu->addIO(io); 54 | apci->length+=len; 55 | } 56 | 57 | void APDU::addIO(InformationObject& inf) 58 | { 59 | int len=asdu->addIO(inf); 60 | apci->length+=len; 61 | } 62 | 63 | void APDU::setAPCI(int format) 64 | { 65 | apci->format=format; 66 | } 67 | 68 | void APDU::setDUI(int common, int ident, int cause) 69 | { 70 | asdu->DUI.typeIdent=ident; 71 | asdu->DUI.cause.cause=cause; 72 | asdu->DUI.commonAdrASDU=common; 73 | apci->length+=6; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /APDU.h: -------------------------------------------------------------------------------- 1 | #include "APCI.h" 2 | #include "ASDU.h" 3 | 4 | class APDU 5 | { 6 | public: 7 | APCI *apci; 8 | ASDU *asdu; 9 | APDU(); 10 | virtual ~APDU(); 11 | void clear(); 12 | int get(unsigned char* data); 13 | bool set(unsigned char* data); 14 | bool valid(); 15 | void addIO(int address); 16 | void addIO(InformationObject& inf); 17 | void setDUI(int common, int ident, int cause); 18 | void setAPCI(int format); 19 | }; 20 | 21 | -------------------------------------------------------------------------------- /ASDU.cpp: -------------------------------------------------------------------------------- 1 | #include "ASDU.h" 2 | 3 | using namespace std; 4 | 5 | ASDU::ASDU() 6 | { 7 | clear(); 8 | } 9 | 10 | ASDU::~ASDU() 11 | { 12 | } 13 | 14 | bool ASDU::valid(){ 15 | return true; 16 | } 17 | 18 | void ASDU::clear() 19 | { 20 | DUI.clear(); 21 | IO.clear(); 22 | } 23 | 24 | int ASDU::get(unsigned char* data) 25 | { 26 | int index = 0; 27 | 28 | 29 | // DATA UNIT IDENTIFIER 30 | data[index++] = DUI.typeIdent; 31 | data[index++] = (DUI.qualifier.SQ<<7)|(DUI.qualifier.Number); 32 | data[index++] = (DUI.cause.test<<7)|(DUI.cause.confim<<6)|(DUI.cause.cause); 33 | data[index++] = DUI.cause.initiator; 34 | data[index++] = DUI.commonAdrASDU&0xff; 35 | data[index++] = (DUI.commonAdrASDU>>8); 36 | 37 | // INFORMATION OBJECT 38 | 39 | vector::iterator it; 40 | it = IO.begin(); 41 | 42 | while (it!=IO.end()) 43 | { 44 | if ((it==IO.begin()) || !DUI.qualifier.SQ) 45 | { 46 | memcpy(&data[index], &it->objectAdr, 3); 47 | index+=3; 48 | } 49 | switch (DUI.typeIdent) 50 | { 51 | case M_SP_NA_1: 52 | memcpy(&data[index++],&it->siq,1); 53 | break; 54 | case M_DP_NA_1: 55 | memcpy(&data[index++],&it->diq,1); 56 | break; 57 | case M_ME_NA_1: 58 | memcpy(&data[index],&it->nva,2); 59 | index+=2; 60 | memcpy(&data[index++],&it->qds,1); 61 | break; 62 | case M_ME_NC_1: 63 | memcpy(&data[index],&it->value,4); 64 | index+=4; 65 | memcpy(&data[index++],&it->qds,1); 66 | break; 67 | // case M_SP_TB_1: 68 | // memcpy(&data[index++],&it->siq,1); 69 | // memcpy(&data[index],&it->cp56time,7); 70 | // index+=7; 71 | // break; 72 | // case M_DP_TB_1: 73 | // memcpy(&data[index++],&it->diq,1); 74 | // memcpy(&data[index],&it->cp56time,7); 75 | // index+=7; 76 | // break; 77 | // case M_ME_TD_1: 78 | // memcpy(&data[index],&it->nva,2); 79 | // index+=2; 80 | // memcpy(&data[index++],&it->qds,1); 81 | // memcpy(&data[index],&it->cp56time,7); 82 | // index+=7; 83 | // break; 84 | // case M_ME_TF_1: 85 | // memcpy(&data[index],&it->value,4); 86 | // index+=4; 87 | // memcpy(&data[index++],&it->qds,1); 88 | // memcpy(&data[index],&it->cp56time,7); 89 | // index+=7; 90 | // break; 91 | case C_SC_NA_1: 92 | memcpy(&data[index++],&it->sco,1); 93 | break; 94 | case C_DC_NA_1: 95 | memcpy(&data[index++],&it->dco,1); 96 | break; 97 | // case C_SC_TA_1: 98 | // memcpy(&data[index++],&it->sco,1); 99 | // memcpy(&data[index],&it->cp56time,7); 100 | // index+=7; 101 | // break; 102 | // case C_DC_TA_1: 103 | // memcpy(&data[index++],&it->dco,1); 104 | // memcpy(&data[index],&it->cp56time,7); 105 | // index+=7; 106 | // break; 107 | case M_EI_NA_1: 108 | memcpy(&data[index++],&it->coi,1); 109 | break; 110 | case C_IC_NA_1: 111 | data[index++]=it->qoi; 112 | break; 113 | case C_CI_NA_1: 114 | data[index++]=it->qcc; 115 | break; 116 | // case C_CS_NA_1: 117 | // memcpy(&data[index],&it->cp56time,7); 118 | // index+=7; 119 | // break; 120 | }; 121 | ++it; 122 | } 123 | return index; 124 | } 125 | 126 | void ASDU::set (unsigned char * data) 127 | { 128 | DUI.typeIdent = data[0]; 129 | DUI.qualifier.Number = data[1]&0x7f; 130 | DUI.qualifier.SQ = data[1]>>7; 131 | DUI.cause.cause = data[2]&0x3F; 132 | DUI.cause.confim = (data[2]>>6)&0x01; 133 | DUI.cause.test = data[2]>>7; 134 | DUI.cause.initiator = data[3]; 135 | DUI.commonAdrASDU = data[4] + data[5]*256; 136 | 137 | int i, index = 6; 138 | InformationObject inf; 139 | 140 | for (i=0; i1)) 228 | len-=3; 229 | return len; 230 | } 231 | 232 | 233 | 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /ASDU.h: -------------------------------------------------------------------------------- 1 | /* This file is a definition of all structures os ASDU part 2 | of IEC-60870-5-104 protocol 3 | */ 4 | 5 | 6 | #include "string.h" //for memset() work 7 | #include 8 | 9 | 10 | 11 | /* Definition of "Identification Data Block". "Identification Data Block" is consist of: 12 | TYPE IDENTIFICATION (1 byte) 13 | QUALIFIER OF VARIABLE STRUCTURE (1 byte) 14 | CAUSE OF TRANSMISSION (1 or 2 byte) 15 | GENERAL ADDRESS OF ASDU (1 or 2 byte) 16 | */ 17 | 18 | //TYPE IDENTIFICATION 19 | 20 | #define M_SP_NA_1 1 // Single-point information 21 | #define M_DP_NA_1 3 // Double-point information 22 | #define M_ST_NA_1 5 // Step position information 23 | #define M_BO_NA_1 7 // Bitstring of 32 bit 24 | #define M_ME_NA_1 9 // Measured value, normalized value 25 | #define M_ME_NC_1 13 // Measured value, short floating point value 26 | #define M_SP_TB_1 30 // Single-point information with time tag CP56Time2a 27 | #define M_DP_TB_1 31 // Double-point information with time tag CP56Time2a 28 | #define M_ST_TB_1 32 // Step position information with time tag CP56Time2a 29 | #define M_ME_TD_1 34 // Measured value, normalized value with time tag CP56Time2a 30 | #define M_ME_TF_1 36 // Measured value, short floating point value with time tag CP56Time2a 31 | #define M_IT_TB_1 37 // Integrated totals value with time tag CP56Time2a 32 | #define C_SC_NA_1 45 // Single command 33 | #define C_DC_NA_1 46 // Double command 34 | #define C_RC_NA_1 47 // Regulating step command 35 | #define C_SE_NA_1 48 // Set point command, normalised value 36 | #define C_BO_NA_1 51 // Bitstring of 32 bit 37 | #define C_SC_TA_1 58 // Single command with time tag CP56Time2a 38 | #define C_DC_TA_1 59 // Double command with time tag CP56Time2a 39 | #define M_EI_NA_1 70 // End of initialization 40 | #define C_IC_NA_1 100 // Interrrogation command 41 | #define C_CI_NA_1 101 // Counter interrrogation command 42 | #define C_CS_NA_1 103 // Clock syncronization command 43 | #define C_TS_NB_1 104 // Test command 44 | #define C_RP_NC_1 105 // Reset process command 45 | 46 | // QUALIFIER OF VARIABLE STRUCTURE 47 | 48 | struct QualifierVariableStructute { //VariableStructureQualifier 49 | 50 | unsigned char Number; // number of Informational Objects 51 | unsigned char SQ; // Single <0> or Series <1> of Objects 52 | 53 | }; 54 | 55 | // CAUSE OF TRANSMISSION: define causes 56 | 57 | #define COT_PERIODIC 1 58 | #define COT_BACKGROUND 2 59 | #define COT_SPONTANEOUS 3 60 | #define COT_INITIALISED 4 61 | #define COT_REQUEST 5 62 | #define COT_ACTIVATION 6 63 | #define COT_ACTIVATIONCONFIRM 7 64 | #define COT_DEACTIVATION 8 65 | #define COT_DEACTIVATIONCONFIRM 9 66 | #define COT_ACTIVATIONTERMINATION 10 67 | #define COT_REMOTECOMMAND 11 68 | #define COT_LOCALCOMMAND 12 69 | #define COT_FILETRANSFER 13 70 | #define COT_INTERROGATION 20 71 | 72 | // CAUSE OF TRANSMISSION: define structure 73 | 74 | struct CauseOfTransmission { 75 | 76 | unsigned char cause; // <0..63> cause number 77 | unsigned char confim; // <0> - positive , <1> - negative 78 | unsigned char test; // <0> - not a test, <1> - test 79 | unsigned char initiator; // number of initiating address 80 | 81 | }; 82 | 83 | // GENERAL ADDRESS OF ASDU 84 | 85 | /* Definition of "OBJECT OF INFORMATION". "OBJECT OF INFORMATION" is consist of: 86 | ADRRESS OF THE OBJECT OF INFORMATION (1,2 OR 3 BYTES) INFORMATION OBJECT ID 87 | FRAMEWORK OF ELEMENT OF INFORMATION 88 | TIMESTAMP 89 | 90 | */ 91 | 92 | // FRAMEWORK OF ELEMENT OF INFORMATION: define of element of information 93 | 94 | // Single-Point Information 95 | struct SIQ { 96 | 97 | unsigned char SPI :1; // <0> - OFF, <1> - ON 98 | unsigned char RES :3; // RESERV 99 | unsigned char BL :1; // <0> - no block, <1> - block 100 | unsigned char SB :1; // <0> - no substitution (replacement), <1> - substitution 101 | unsigned char NT :1; // <0> - relevance value, <1> - irrelevance value 102 | unsigned char IV :1; // <0> - valid value, <1> - invalid value 103 | 104 | }; 105 | 106 | // Double-point Information 107 | struct DIQ 108 | { 109 | unsigned char DPI :2; // <0> - Indefined state/Intermidiate state, <1> - OFF, <2> - ON, <3> - Indefined State 110 | unsigned char RES :2; // RESERV 111 | unsigned char BL :1; // <0> - no block, <1> - block 112 | unsigned char SB :1; // <0> - no substitution (replacement), <1> - substitution 113 | unsigned char NT :1; // <0> - relevance value, <1> - irrelevance value 114 | unsigned char IV :1; // <0> - valid value, <1> - invalid value 115 | }; 116 | 117 | // Quality control 118 | struct QDS 119 | { 120 | unsigned char OV : 1; // Overflow 121 | unsigned char RES : 3; 122 | unsigned char BL : 1; // Blocked 123 | unsigned char SB : 1; // Substituted 124 | unsigned char NT : 1; // Not topical 125 | unsigned char IV : 1; // Invalid 126 | }; 127 | 128 | // Value with transient state indication 129 | struct VTI 130 | { 131 | unsigned char Value : 7; 132 | unsigned char Transient : 1; 133 | 134 | }; 135 | 136 | // Indicators of binary counter 137 | 138 | struct BCR 139 | { 140 | 141 | int counter : 32; 142 | unsigned char SQ : 5; // Sequence number 143 | unsigned char CY : 1; // Counter overflow 144 | unsigned char CA : 1; // Counter adjusted 145 | unsigned char IV : 1; // Invalid 146 | 147 | }; 148 | 149 | // Single-point command 150 | 151 | struct SCO 152 | { 153 | 154 | unsigned char SCS : 1; // 0=OFF, 1=ON 155 | unsigned char res : 1; // Fast 0 156 | unsigned char QU : 5; 157 | unsigned char ES : 1; // 0=Execute, 1=Select 158 | 159 | }; 160 | 161 | //Double-point command 162 | 163 | struct DCO 164 | { 165 | 166 | unsigned char DCS : 2; // 0= Not permitted, 1=OFF, 2=ON, 3=Not permitted 167 | unsigned char QU : 5; 168 | unsigned char ES : 1; // 0=Execute, 1=Select 169 | 170 | }; 171 | 172 | // Stepwise regulation command 173 | 174 | struct RCO 175 | { 176 | unsigned char RCS : 2; // 0= Not permitted, 1=Next LOWER, 2=Next HIGHER, 3=Not permitted 177 | unsigned char QU : 5; 178 | unsigned char ES : 1; // 0=Execute, 1=Select 179 | }; 180 | 181 | // Initialization cause 182 | 183 | struct COI 184 | { 185 | unsigned char cause : 7; // 186 | unsigned char change : 1; // Initialisation after change of local parameters 187 | }; 188 | 189 | // Quantities pointer measured 190 | 191 | struct QPM 192 | { 193 | unsigned char KPA : 6; 194 | unsigned char LPC : 1; 195 | unsigned char POP : 1; 196 | }; 197 | 198 | // TIMESTAMP: define structure 199 | 200 | /*struct CP56Time2a 201 | { 202 | unsigned int ms : 16; // 0..59 999 ms 203 | unsigned int min : 6; // 0..59 min. 204 | unsigned int res1 : 1; 205 | unsigned int IV : 1; // Invalid time 206 | unsigned int h : 5; // 0..23 h 207 | unsigned int res2 : 2; 208 | unsigned int SU : 1; 209 | unsigned int d : 5; // 0..31 Dag i m?neden 210 | unsigned int wd : 3; // 1..7 Dag i uka 211 | unsigned int m : 4; // 1..12 M?ned 212 | unsigned int res3 : 4; 213 | unsigned int y : 7; // 0..99 ?r 214 | unsigned int res4 : 1; 215 | };*/ 216 | 217 | class DataUnitIdentifier 218 | { 219 | public: 220 | unsigned char typeIdent; 221 | QualifierVariableStructute qualifier; 222 | CauseOfTransmission cause; 223 | unsigned short commonAdrASDU; 224 | 225 | DataUnitIdentifier() // constructor 226 | { 227 | clear(); 228 | }; 229 | 230 | void clear(){ 231 | typeIdent = 0; 232 | memset(&qualifier,0,1); 233 | memset(&cause,0,2); 234 | commonAdrASDU = 0; 235 | }; 236 | }; 237 | 238 | class InformationObject 239 | { 240 | public: 241 | unsigned int objectAdr; 242 | union // 1 byte 243 | { 244 | SIQ siq; // 1, 30 245 | DIQ diq; // 3, 31 246 | QDS qds; // 5, 7, 9, 13, 32, 34, 36 247 | SCO sco; // 45, 58 248 | DCO dco; // 46, 59 249 | RCO rco; // 47, 60 250 | // QOS qos; // 48, 61 251 | COI coi; // 70 252 | unsigned char qoi; // 100 253 | unsigned char qcc; // 101 254 | unsigned char qrp; // 105 255 | QPM qpm; // 110, 112 256 | }; 257 | union // 5 byte 258 | { 259 | VTI vti; // 5, 32 260 | unsigned int bsi; // 7, 51 261 | unsigned short nva; // 9, 34, 48, 61 262 | float value; // 13, 36, 112 263 | BCR bcr; // 37 264 | unsigned short fbp; 265 | }; 266 | // CP56Time2a cp56time; // 30, 31, 32, 34, 36, 103 267 | 268 | InformationObject() 269 | { 270 | clear(); 271 | }; 272 | void clear() 273 | { 274 | objectAdr = 0; 275 | qoi = 0; 276 | memset(&bcr,0,5); 277 | // memset(&cp56time,0,7); 278 | }; 279 | }; 280 | 281 | class ASDU 282 | { 283 | public: 284 | DataUnitIdentifier DUI; 285 | std::vector IO; // not sure that vector will work 286 | ASDU(); 287 | virtual ~ASDU(); // not sure that virtual function is allowed 288 | virtual void clear(); 289 | int get (unsigned char* data); 290 | void set(unsigned char* data); 291 | int addIO(InformationObject& i); 292 | bool valid(); 293 | }; 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | -------------------------------------------------------------------------------- /iec104.cpp: -------------------------------------------------------------------------------- 1 | #include "iec104.h" 2 | #include "stdio.h" 3 | 4 | void print(const char* str, unsigned char* data, int size){ 5 | printf("%s",str); 6 | for (int i=0; iset(data); 19 | 20 | apdu1->apci->func <<= 1;// | 0b00001000 & 0b11111011; 21 | 22 | apdu1->get(data); 23 | 24 | print("OUTcoming data: ",data, data[1]); 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /iec104.h: -------------------------------------------------------------------------------- 1 | #include "APDU.h" 2 | 3 | void testfunc(unsigned char* data); 4 | 5 | --------------------------------------------------------------------------------