modbus-fronius.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <modbus.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <stdlib.h>
  7. #include <math.h>
  8. #include <sys/select.h>
  9. #include <sys/socket.h>
  10. #include <arpa/inet.h>
  11. #include <netinet/in.h>
  12. #define NB_REGS 120
  13. #define NB_CONNECTION 5
  14. #define IEM3155_ADDRESS 1 // Modbus RTU slave address for iEM3155
  15. #define METER_OR_GATEWAY_IP_ADDRESS "10.0.1.100"
  16. #define METER_OR_GATEWAY_PORT "502"
  17. #define TRANSLATOR_IP_ADDRESS "10.0.1.2"
  18. #define MODBUS_SET_INT32_TO_INT16_FRONIUS(tab_int16, index, value) \
  19. do { \
  20. ((int16_t *) (tab_int16))[(index) + 1] = (int16_t) ((value) >> 16); \
  21. ((int16_t *) (tab_int16))[(index)] = (int16_t) (value); \
  22. } while (0)
  23. int stringModbusCopy(char * destination,char * source, int n){ // Custom method to write string in the right format for Modbus
  24. // Copy even characters
  25. for(int i = 0; i < n; i+=2){
  26. destination[i + 1] = source[i];
  27. }
  28. // Copy odd characters
  29. for(int i = 1; i < n; i+=2){
  30. destination[i - 1] = source[i];
  31. }
  32. }
  33. static int server_socket = -1;
  34. static modbus_mapping_t *mb_mapping_sunspec;
  35. static modbus_t *ctx2; // Context for requests from GEN24
  36. static void close_sigint(int dummy)
  37. {
  38. if (server_socket != -1) {
  39. close(server_socket);
  40. }
  41. modbus_free(ctx2);
  42. modbus_mapping_free(mb_mapping_sunspec);
  43. exit(dummy);
  44. }
  45. int main()
  46. {
  47. printf("Begin\n");
  48. int ret;
  49. int rc;
  50. int header_length;
  51. uint8_t *query;
  52. uint8_t nb_retry = 0;
  53. modbus_t *ctx; // Context for requests to iEM3155
  54. uint16_t dest [NB_REGS];
  55. uint16_t reg_address;
  56. uint16_t nb_registers;
  57. uint16_t tempTab [2];
  58. float current_ph1, current_ph2, current_ph3, current_total, apower_ph1, apower_ph2, apower_ph3, apower_total, apower_max, apPower_total, rpower_total, pf;
  59. int64_t activeEnergyImport, activeEnergyExport;
  60. fd_set refset;
  61. fd_set rdset;
  62. /* Maximum file descriptor number */
  63. int fdmax;
  64. int master_socket;
  65. //Reader preparation
  66. ctx = modbus_new_tcp_pi(METER_OR_GATEWAY_IP_ADDRESS, METER_OR_GATEWAY_PORT);
  67. if (ctx == NULL) {
  68. perror("Unable to create the libmodbus context\n");
  69. return -1;
  70. }
  71. modbus_set_response_timeout(ctx, 1, 0);
  72. ret = modbus_set_slave(ctx, IEM3155_ADDRESS);
  73. if(ret < 0){
  74. perror("modbus_set_slave error\n");
  75. return -1;
  76. }
  77. ret = modbus_connect(ctx);
  78. if(ret < 0){
  79. perror("modbus_connect error\n");
  80. return -1;
  81. }
  82. // Receiver preparation
  83. ctx2 = modbus_new_tcp_pi(TRANSLATOR_IP_ADDRESS, "502"); // You have to use port TCP/502 here, because the GEN24 doesn't appreciate otherwise
  84. if (ctx2 == NULL) {
  85. perror("Unable to create the libmodbus context\n");
  86. return -1;
  87. }
  88. query = malloc(MODBUS_TCP_MAX_ADU_LENGTH);
  89. ret = modbus_set_slave(ctx2, 1);
  90. if(ret < 0){
  91. perror("modbus_set_slave error\n");
  92. return -1;
  93. }
  94. server_socket = modbus_tcp_pi_listen(ctx2, NB_CONNECTION);
  95. if (server_socket == -1) {
  96. perror("Unable to listen\n");
  97. return -1;
  98. }
  99. signal(SIGINT, close_sigint);
  100. /* Clear the reference set of socket */
  101. FD_ZERO(&refset);
  102. /* Add the server socket */
  103. FD_SET(server_socket, &refset);
  104. /* Keep track of the max file descriptor */
  105. fdmax = server_socket;
  106. //mb_mapping = modbus_mapping_new_start_address(0,0,0,0,0,50002,0,0); //
  107. mb_mapping_sunspec = modbus_mapping_new_start_address(0,0,0,0,40000,178,0,0);
  108. // Static Sunspec Values
  109. mb_mapping_sunspec->tab_registers[0] = 0x5375; // Sunspec ID
  110. mb_mapping_sunspec->tab_registers[1] = 0x6e53; // Sunspec ID
  111. mb_mapping_sunspec->tab_registers[2] = 1; // Sunspec ID
  112. mb_mapping_sunspec->tab_registers[3] = 65; // Length of Sunspec model meter common
  113. stringModbusCopy((char *) &mb_mapping_sunspec->tab_registers[4], "Schneider Electric", 18); // Manufacturer name
  114. stringModbusCopy((char *) &mb_mapping_sunspec->tab_registers[20], "iEM3155", 7); // Meter product name
  115. stringModbusCopy((char *) &mb_mapping_sunspec->tab_registers[36], "Utility", 7); // Meter name (test)
  116. stringModbusCopy((char *) &mb_mapping_sunspec->tab_registers[44], "1.3.007", 7); // SW version
  117. stringModbusCopy((char *) &mb_mapping_sunspec->tab_registers[52], "0123456789", 10); // Serial number
  118. mb_mapping_sunspec->tab_registers[68] = 1; // Modbus address
  119. mb_mapping_sunspec->tab_registers[69] = 203; // Meter type (203 = Three phases + Neutral)
  120. mb_mapping_sunspec->tab_registers[70] = 105; // Length of Sunspec model ac_meter
  121. // Static power factor
  122. mb_mapping_sunspec->tab_registers[75] = -3; // Current scale factor (dynamic)
  123. mb_mapping_sunspec->tab_registers[84] = -1; // Voltage scale factor
  124. mb_mapping_sunspec->tab_registers[86] = -2; // Frequency scale factor
  125. mb_mapping_sunspec->tab_registers[91] = 0; // Active power scale factor (dynamic)
  126. mb_mapping_sunspec->tab_registers[96] = 0; // Apparent power scale factor (dynamic)
  127. mb_mapping_sunspec->tab_registers[101] = 0; // Reactive power scale factor (dynamic)
  128. mb_mapping_sunspec->tab_registers[106] = -1; // Power factor scale factor
  129. mb_mapping_sunspec->tab_registers[123] = 0; // Active energy scale factor
  130. mb_mapping_sunspec->tab_registers[70] = 105; // Length of Sunspec model ac_meter
  131. mb_mapping_sunspec->tab_registers[176] = 0xFFFF; // End of model ac_meter
  132. mb_mapping_sunspec->tab_registers[177] = 0; // End of model ac_meter
  133. // Not implemented registers
  134. mb_mapping_sunspec->tab_registers[93] = 0xFFFF; // Apparent power phase 1
  135. mb_mapping_sunspec->tab_registers[94] = 0xFFFF; // Apparent power phase 2
  136. mb_mapping_sunspec->tab_registers[95] = 0xFFFF; // Apparent power phase 3
  137. mb_mapping_sunspec->tab_registers[98] = 0xFFFF; // Reactive power phase 1
  138. mb_mapping_sunspec->tab_registers[99] = 0xFFFF; // Reactive power phase 2
  139. mb_mapping_sunspec->tab_registers[100] = 0xFFFF; // Reactive power phase 3
  140. mb_mapping_sunspec->tab_registers[103] = 0xFFFF; // Power factor phase 1
  141. mb_mapping_sunspec->tab_registers[104] = 0xFFFF; // Power factor phase 2
  142. mb_mapping_sunspec->tab_registers[105] = 0xFFFF; // Power factor phase 3
  143. mb_mapping_sunspec->tab_registers[140] = 0x8000; // Apparent energy scale factor (not implemented)
  144. mb_mapping_sunspec->tab_registers[173] = 0x8000; // Reactive energy scale factor (not implemented)
  145. header_length = modbus_get_header_length(ctx2);
  146. for (;;) {
  147. rdset = refset;
  148. if (select(fdmax + 1, &rdset, NULL, NULL, NULL) == -1) {
  149. perror("Server select() failure.");
  150. close_sigint(1);
  151. }
  152. /* Run through the existing connections looking for data to be
  153. * read */
  154. for (master_socket = 0; master_socket <= fdmax; master_socket++) {
  155. if (!FD_ISSET(master_socket, &rdset)) {
  156. continue;
  157. }
  158. if (master_socket == server_socket) {
  159. /* A client is asking a new connection */
  160. socklen_t addrlen;
  161. struct sockaddr_in clientaddr;
  162. int newfd;
  163. /* Handle new connections */
  164. addrlen = sizeof(clientaddr);
  165. memset(&clientaddr, 0, sizeof(clientaddr));
  166. newfd = accept(server_socket, (struct sockaddr *) &clientaddr, &addrlen);
  167. if (newfd == -1) {
  168. perror("Server accept() error");
  169. } else {
  170. FD_SET(newfd, &refset);
  171. if (newfd > fdmax) {
  172. /* Keep track of the maximum */
  173. fdmax = newfd;
  174. }
  175. printf("New connection from %s:%d on socket %d\n",
  176. inet_ntoa(clientaddr.sin_addr),
  177. clientaddr.sin_port,
  178. newfd);
  179. }
  180. } else {
  181. modbus_set_socket(ctx2, master_socket);
  182. rc = modbus_receive(ctx2, query);
  183. if (rc > 0) {
  184. if (query[header_length] == 0x03) {
  185. //Read holding registers
  186. reg_address = (uint16_t) MODBUS_GET_INT16_FROM_INT8(query, header_length + 1);
  187. nb_registers = (uint16_t) MODBUS_GET_INT16_FROM_INT8(query, header_length + 3);
  188. //printf("Request received. Address : %d, nb registers : %d\n", reg_address, nb_registers);
  189. if((reg_address >= 40071 && reg_address < 40107) || ((reg_address + nb_registers) >= 40071 && (reg_address + nb_registers) < 40108) || (reg_address < 40071 && (reg_address + nb_registers) >= 40108)) { // Request dynamic registers (realtime data (V, I, PF, W...)), so we have to update registers
  190. //if(reg_address >= 40071 && (reg_address + nb_registers) < 40179) { // Request dynamic registers, so we have to update registers
  191. //printf("Realtime data update\n");
  192. ret = modbus_read_registers(ctx, 2999, 112, dest); // Request realtime data (V, I, PF, W...)
  193. if(ret < 0){
  194. if(nb_retry < 10){
  195. printf("Error number : %d\n", errno);
  196. perror("modbus_read_regs error (Realtime registers)\n");
  197. nb_retry++;
  198. } else {
  199. printf("Error number : %d\n", errno);
  200. perror("modbus_read_regs error (Realtime registers)\n");
  201. return -1;
  202. }
  203. } else {
  204. nb_retry = 0;
  205. }
  206. // Current
  207. current_ph1 = modbus_get_float_abcd(&dest[0]);
  208. current_ph2 = modbus_get_float_abcd(&dest[2]);
  209. current_ph3 = modbus_get_float_abcd(&dest[4]);
  210. current_total = current_ph1 + current_ph2 + current_ph3;
  211. //printf("Phase 1 current : %f\n", current_ph1);
  212. if(current_ph1 > 32.5 || current_ph1 < -32.5 || current_ph2 > 32.5 || current_ph2 < -32.5 || current_ph3 > 32.5 || current_ph3 < -32.5 || current_total > 32.5 || current_total < -32.5){
  213. mb_mapping_sunspec->tab_registers[75] = -2; // Current scale factor (dynamic)
  214. mb_mapping_sunspec->tab_registers[71] = (int16_t) (100 * current_total);
  215. mb_mapping_sunspec->tab_registers[72] = (int16_t) (100 * current_ph1);
  216. mb_mapping_sunspec->tab_registers[73] = (int16_t) (100 * current_ph2);
  217. mb_mapping_sunspec->tab_registers[74] = (int16_t) (100 * current_ph3);
  218. } else {
  219. mb_mapping_sunspec->tab_registers[75] = -3; // Current scale factor (dynamic)
  220. mb_mapping_sunspec->tab_registers[71] = (int16_t) (1000 * current_total);
  221. mb_mapping_sunspec->tab_registers[72] = (int16_t) (1000 * current_ph1);
  222. mb_mapping_sunspec->tab_registers[73] = (int16_t) (1000 * current_ph2);
  223. mb_mapping_sunspec->tab_registers[74] = (int16_t) (1000 * current_ph3);
  224. }
  225. // L-N Voltage
  226. mb_mapping_sunspec->tab_registers[76] = (int16_t) (10 * modbus_get_float_abcd(&dest[36])); // L-N Voltage (avg)
  227. //printf("L-N Voltage : %d\n", (int16_t) (10 * modbus_get_float_abcd(&dest[36])));
  228. mb_mapping_sunspec->tab_registers[77] = (int16_t) (10 * modbus_get_float_abcd(&dest[28])); // L-N Voltage ph1
  229. mb_mapping_sunspec->tab_registers[78] = (int16_t) (10 * modbus_get_float_abcd(&dest[30])); // L-N Voltage ph2
  230. mb_mapping_sunspec->tab_registers[79] = (int16_t) (10 * modbus_get_float_abcd(&dest[32])); // L-N Voltage ph3
  231. // L-L Voltage
  232. mb_mapping_sunspec->tab_registers[80] = (int16_t) (10 * modbus_get_float_abcd(&dest[26])); // L-L Voltage (avg)
  233. //printf("L-L Voltage : %d\n", (int16_t) (10 * modbus_get_float_abcd(&dest[26])));
  234. mb_mapping_sunspec->tab_registers[81] = (int16_t) (10 * modbus_get_float_abcd(&dest[20])); // L1-L2 Voltage
  235. mb_mapping_sunspec->tab_registers[82] = (int16_t) (10 * modbus_get_float_abcd(&dest[22])); // L2-L3 Voltage
  236. mb_mapping_sunspec->tab_registers[83] = (int16_t) (10 * modbus_get_float_abcd(&dest[24])); // L3-L1 Voltage
  237. // Frequency
  238. mb_mapping_sunspec->tab_registers[85] = (int16_t) (100 * modbus_get_float_abcd(&dest[110]));
  239. //printf("Frequency : %d\n", (int16_t) (100 * modbus_get_float_abcd(&dest[110])));
  240. // Active power
  241. apower_ph1 = modbus_get_float_abcd(&dest[54]);
  242. apower_ph2 = modbus_get_float_abcd(&dest[56]);
  243. apower_ph3 = modbus_get_float_abcd(&dest[58]);
  244. apower_total = modbus_get_float_abcd(&dest[60]);
  245. apower_max = fmaxf(fabsf(apower_ph1),fmaxf(fabsf(apower_ph2),fmaxf(fabsf(apower_ph3),fabsf(apower_total))));
  246. //printf("Active Power : %f\n", apower_total);
  247. if(apower_max > 32.5){
  248. mb_mapping_sunspec->tab_registers[91] = 1; // Active power scale factor (dynamic)
  249. mb_mapping_sunspec->tab_registers[87] = (int16_t) (100 * apower_total);
  250. mb_mapping_sunspec->tab_registers[88] = (int16_t) (100 * apower_ph1);
  251. mb_mapping_sunspec->tab_registers[89] = (int16_t) (100 * apower_ph2);
  252. mb_mapping_sunspec->tab_registers[90] = (int16_t) (100 * apower_ph3);
  253. } else {
  254. mb_mapping_sunspec->tab_registers[91] = 0; // Active power scale factor (dynamic)
  255. mb_mapping_sunspec->tab_registers[87] = (int16_t) (1000 * apower_total);
  256. mb_mapping_sunspec->tab_registers[88] = (int16_t) (1000 * apower_ph1);
  257. mb_mapping_sunspec->tab_registers[89] = (int16_t) (1000 * apower_ph2);
  258. mb_mapping_sunspec->tab_registers[90] = (int16_t) (1000 * apower_ph3);
  259. }
  260. // Apparent power (sum)
  261. apPower_total = modbus_get_float_abcd(&dest[76]);
  262. //printf("Apparent Power : %f\n", modbus_get_float_abcd(&dest[76]));
  263. if(fabsf(apPower_total) > 32.6){
  264. mb_mapping_sunspec->tab_registers[96] = 1; // Apparent power scale factor (dynamic)
  265. mb_mapping_sunspec->tab_registers[92] = (int16_t) (100 * apPower_total);
  266. } else {
  267. mb_mapping_sunspec->tab_registers[96] = 0; // Apparent power scale factor (dynamic)
  268. mb_mapping_sunspec->tab_registers[92] = (int16_t) (1000 * apPower_total);
  269. }
  270. // Reactive power (sum)
  271. rpower_total = modbus_get_float_abcd(&dest[68]);
  272. //printf("Reactive Power : %f\n", modbus_get_float_abcd(&dest[68]));
  273. if(fabsf(rpower_total) > 32.6){
  274. mb_mapping_sunspec->tab_registers[101] = 1; // Reactive power scale factor (dynamic)
  275. mb_mapping_sunspec->tab_registers[97] = (int16_t) (100 * rpower_total);
  276. } else {
  277. mb_mapping_sunspec->tab_registers[101] = 0; // Reactive power scale factor (dynamic)
  278. mb_mapping_sunspec->tab_registers[97] = (int16_t) (1000 * rpower_total);
  279. }
  280. // Power factor (sum)
  281. pf = modbus_get_float_abcd(&dest[84]);
  282. if(pf<-1){ // Calculation according to iEM3155 manual and Sunspec specification. It might be erronous.
  283. pf = - 2 - pf;
  284. } else if(pf < 0){
  285. pf = -pf;
  286. } else if(pf < 1){
  287. pf = -pf;
  288. } else {
  289. pf = 2 - pf;
  290. }
  291. mb_mapping_sunspec->tab_registers[102] = (int16_t) (1000 * pf);
  292. //printf("Power factor : %d\n", (int16_t) (1000 * pf));
  293. //printf("Power factor raw : %f\n",pf);
  294. }
  295. if((reg_address >= 40107 && reg_address < 40142) || ((reg_address + nb_registers) > 40107 && (reg_address + nb_registers) < 40142) || (reg_address < 40107 && (reg_address + nb_registers) >= 40142)) { // Request dynamic registers (energy registers (Wh)), so we have to update registers
  296. //printf("Energy data update\n");
  297. ret = modbus_read_registers(ctx, 3203, 8, dest); // Request energy registers (Wh)
  298. if(ret < 0){
  299. if(nb_retry < 10){
  300. printf("Error number : %d\n", errno);
  301. perror("modbus_read_regs error (Energy registers)\n");
  302. nb_retry++;
  303. } else {
  304. printf("Error number : %d\n", errno);
  305. perror("modbus_read_regs error (Energy registers)\n");
  306. return -1;
  307. }
  308. } else {
  309. nb_retry = 0;
  310. }
  311. // Active energy exported
  312. activeEnergyExport = MODBUS_GET_INT64_FROM_INT16(dest, 4);
  313. //printf("Active Energy Export : %d\n", activeEnergyExport);
  314. // Active energy imported
  315. activeEnergyImport = MODBUS_GET_INT64_FROM_INT16(dest, 0);
  316. //printf("Active Energy Import : %d\n", activeEnergyImport);
  317. if(fmax((double) (activeEnergyExport),(double) (activeEnergyImport)) > 4294967295) {
  318. mb_mapping_sunspec->tab_registers[123] = 1; // Active energy scale factor
  319. MODBUS_SET_INT32_TO_INT16(mb_mapping_sunspec->tab_registers, 107, (uint32_t) (activeEnergyExport / 10));
  320. MODBUS_SET_INT32_TO_INT16(mb_mapping_sunspec->tab_registers, 115, (uint32_t) (activeEnergyImport / 10));
  321. } else {
  322. mb_mapping_sunspec->tab_registers[123] = 0; // Active energy scale factor
  323. MODBUS_SET_INT32_TO_INT16(mb_mapping_sunspec->tab_registers, 107, (uint32_t) (activeEnergyExport));
  324. MODBUS_SET_INT32_TO_INT16(mb_mapping_sunspec->tab_registers, 115, (uint32_t) (activeEnergyImport));
  325. }
  326. }
  327. rc = modbus_reply(ctx2, query, rc, mb_mapping_sunspec);
  328. if (rc == -1) {
  329. break;
  330. }
  331. }
  332. } else if (rc == -1) {
  333. /* This example server in ended on connection closing or
  334. * any errors. */
  335. printf("Connection closed on socket %d\n", master_socket);
  336. close(master_socket);
  337. /* Remove from reference set */
  338. FD_CLR(master_socket, &refset);
  339. if (master_socket == fdmax) {
  340. fdmax--;
  341. }
  342. }
  343. }
  344. }
  345. }
  346. //modbus_close(ctx);
  347. //modbus_free(ctx);
  348. free(query);
  349. modbus_close(ctx2);
  350. modbus_free(ctx2);
  351. }