modbus-fronius-symo.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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. #include <time.h>
  13. #define NB_REGS 120
  14. #define NB_CONNECTION 5
  15. #define IEM3155_ADDRESS 1 // Modbus RTU slave address for iEM3155
  16. #define METER_OR_GATEWAY_PORT "502"
  17. #define TRANSLATOR_IP_ADDRESS "10.0.1.2"
  18. #define SYMO_IP_ADDRESS "10.0.1.3"
  19. #define MODBUS_SET_INT32_TO_INT16_FRONIUS(tab_int16, index, value) \
  20. do { \
  21. ((int16_t *) (tab_int16))[(index) + 1] = (int16_t) ((value) >> 16); \
  22. ((int16_t *) (tab_int16))[(index)] = (int16_t) (value); \
  23. } while (0)
  24. int stringModbusCopy(char * destination,char * source, int n){ // Custom method to write string in the right format for Modbus
  25. // Copy even characters
  26. for(int i = 0; i < n; i+=2){
  27. destination[i + 1] = source[i];
  28. }
  29. // Copy odd characters
  30. for(int i = 1; i < n; i+=2){
  31. destination[i - 1] = source[i];
  32. }
  33. }
  34. static int server_socket = -1;
  35. static modbus_mapping_t *mb_mapping_sunspec_snap;
  36. static modbus_t *ctx; // Context for requests to Symo
  37. static modbus_t *ctx2; // Context for requests from GEN24
  38. static void close_sigint(int dummy)
  39. {
  40. if (server_socket != -1) {
  41. close(server_socket);
  42. }
  43. modbus_free(ctx);
  44. modbus_free(ctx2);
  45. //modbus_free(ctx4);
  46. modbus_mapping_free(mb_mapping_sunspec_snap);
  47. exit(dummy);
  48. }
  49. int snapUpdate(modbus_mapping_t *mapping, modbus_t *ctxbis){
  50. int ret;
  51. uint16_t dest [NB_REGS];
  52. struct timespec ts;
  53. float v1, v2, v3;
  54. ts.tv_sec = 0;
  55. ts.tv_nsec = 500000000;
  56. //printf("Debut update symo\n");
  57. ret = modbus_read_registers(ctxbis, 40069, 40, dest); // Request sunspec model
  58. if(ret < 0){
  59. perror("Unable to update\n");
  60. return -1;
  61. }
  62. //memcpy(&mapping->tab_registers[71], &dest[2], 2); // AC Total Current
  63. /*printf("Test float abcd : %f\n",modbus_get_float_abcd(&dest[2]));
  64. printf("Test float badc : %f\n",modbus_get_float_badc(&dest[2]));
  65. printf("Test float cdab : %f\n",modbus_get_float_cdab(&dest[2]));
  66. printf("Test float dcba : %f\n",modbus_get_float_dcba(&dest[2]));*/
  67. modbus_set_float_badc(-1.0 * modbus_get_float_abcd(&dest[2]), &mapping->tab_registers[71]); // AC Total Current
  68. modbus_set_float_badc(-1.0 * modbus_get_float_abcd(&dest[4]), &mapping->tab_registers[73]); // AC Ph1 Current
  69. modbus_set_float_badc(-1.0 * modbus_get_float_abcd(&dest[6]), &mapping->tab_registers[75]); // AC Ph2 Current
  70. modbus_set_float_badc(-1.0 * modbus_get_float_abcd(&dest[8]), &mapping->tab_registers[77]); // AC Ph3 Current
  71. v1 = modbus_get_float_abcd(&dest[16]); // L1-N Voltage
  72. modbus_set_float_badc(v1, &mapping->tab_registers[81]);
  73. v2 = modbus_get_float_abcd(&dest[18]); // L2-N Voltage
  74. modbus_set_float_badc(v2, &mapping->tab_registers[83]);
  75. v3 = modbus_get_float_abcd(&dest[20]); // L3-N Voltage
  76. modbus_set_float_badc(v3, &mapping->tab_registers[85]);
  77. modbus_set_float_badc((v1 + v2 + v3)/3.0, &mapping->tab_registers[79]); // L-N Voltage avg
  78. v1 = modbus_get_float_abcd(&dest[10]); // L1-L2 Voltage
  79. modbus_set_float_badc(v1, &mapping->tab_registers[89]);
  80. v2 = modbus_get_float_abcd(&dest[12]); // L2-L3 Voltage
  81. modbus_set_float_badc(v2, &mapping->tab_registers[91]);
  82. v3 = modbus_get_float_abcd(&dest[14]); // L3-L1 Voltage
  83. modbus_set_float_badc(v3, &mapping->tab_registers[93]);
  84. modbus_set_float_badc((v1 + v2 + v3)/3.0, &mapping->tab_registers[87]); // L-L Voltage avg
  85. memcpy(&mapping->tab_registers[95], &dest[24], 2); // Frequency
  86. modbus_set_float_badc(-1.0 * modbus_get_float_abcd(&dest[22]), &mapping->tab_registers[97]); // AC Real power
  87. modbus_set_float_badc(-1.0 * modbus_get_float_abcd(&dest[26]), &mapping->tab_registers[105]); // AC Apparent power
  88. modbus_set_float_badc(-1.0 * modbus_get_float_abcd(&dest[28]), &mapping->tab_registers[113]); // AC Reactive power
  89. modbus_set_float_badc(-1.0 * modbus_get_float_abcd(&dest[30]), &mapping->tab_registers[121]); // Power factor
  90. memcpy(&mapping->tab_registers[129], &dest[32], 2); // Energy exported
  91. return 0;
  92. }
  93. int main()
  94. {
  95. printf("Begin\n");
  96. int ret;
  97. int rc;
  98. int header_length;
  99. uint8_t *query;
  100. uint8_t nb_retry = 0;
  101. uint8_t slaveAddress;
  102. uint16_t dest [NB_REGS];
  103. uint16_t reg_address;
  104. uint16_t nb_registers;
  105. uint16_t tempTab [2];
  106. fd_set refset;
  107. fd_set rdset;
  108. /* Maximum file descriptor number */
  109. int fdmax;
  110. int master_socket;
  111. time_t update_snap;
  112. //Reader preparation - Symo
  113. ctx = modbus_new_tcp_pi(SYMO_IP_ADDRESS, "502");
  114. if (ctx == NULL) {
  115. perror("Unable to create the libmodbus context\n");
  116. return -1;
  117. }
  118. ret = modbus_set_slave(ctx, 1);
  119. if(ret < 0){
  120. perror("modbus_set_slave error\n");
  121. return -1;
  122. }
  123. modbus_set_response_timeout(ctx, 10, 0);
  124. modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_LINK);
  125. ret = modbus_connect(ctx);
  126. if(ret < 0){
  127. perror("modbus_connect error\n");
  128. //return -1;
  129. }
  130. // Receiver preparation
  131. ctx2 = modbus_new_tcp_pi(TRANSLATOR_IP_ADDRESS, "502"); // You have to use port TCP/502 here, because the GEN24 doesn't appreciate otherwise
  132. if (ctx2 == NULL) {
  133. perror("Unable to create the libmodbus context\n");
  134. return -1;
  135. }
  136. query = malloc(MODBUS_TCP_MAX_ADU_LENGTH);
  137. server_socket = modbus_tcp_pi_listen(ctx2, NB_CONNECTION);
  138. if (server_socket == -1) {
  139. perror("Unable to listen\n");
  140. return -1;
  141. }
  142. signal(SIGINT, close_sigint);
  143. /* Clear the reference set of socket */
  144. FD_ZERO(&refset);
  145. /* Add the server socket */
  146. FD_SET(server_socket, &refset);
  147. /* Keep track of the max file descriptor */
  148. fdmax = server_socket;
  149. mb_mapping_sunspec_snap = modbus_mapping_new_start_address(0,0,0,0,40000,197,0,0);
  150. // Static Sunspec Values - Symo
  151. mb_mapping_sunspec_snap->tab_registers[0] = 0x5375; // Sunspec ID
  152. mb_mapping_sunspec_snap->tab_registers[1] = 0x6e53; // Sunspec ID
  153. mb_mapping_sunspec_snap->tab_registers[2] = 1; // Sunspec ID
  154. mb_mapping_sunspec_snap->tab_registers[3] = 65; // Length of Sunspec model meter common
  155. stringModbusCopy((char *) &mb_mapping_sunspec_snap->tab_registers[4], "Fronius", 7); // Manufacturer name
  156. stringModbusCopy((char *) &mb_mapping_sunspec_snap->tab_registers[20], "Symo 6", 6); // Meter product name
  157. stringModbusCopy((char *) &mb_mapping_sunspec_snap->tab_registers[36], "custom", 6); // Meter name (test)
  158. stringModbusCopy((char *) &mb_mapping_sunspec_snap->tab_registers[44], "1.0", 3); // SW version
  159. stringModbusCopy((char *) &mb_mapping_sunspec_snap->tab_registers[52], "0123456789", 10); // Serial number
  160. mb_mapping_sunspec_snap->tab_registers[68] = 5; // Modbus address
  161. mb_mapping_sunspec_snap->tab_registers[69] = 213; // Meter type (213 = Three phases + Neutral float)
  162. mb_mapping_sunspec_snap->tab_registers[70] = 124; // Length of Sunspec model ac_meter
  163. mb_mapping_sunspec_snap->tab_registers[195] = 0xFFFF; // End of model ac_meter
  164. mb_mapping_sunspec_snap->tab_registers[196] = 0; // End of model ac_meter
  165. // Not implemented registers - Symo
  166. mb_mapping_sunspec_snap->tab_registers[99] = 0x7FC0; // Active power phase 1
  167. mb_mapping_sunspec_snap->tab_registers[101] = 0x7FC0; // Active power phase 2
  168. mb_mapping_sunspec_snap->tab_registers[103] = 0x7FC0; // Active power phase 3
  169. mb_mapping_sunspec_snap->tab_registers[107] = 0x7FC0; // Apparent power phase 1
  170. mb_mapping_sunspec_snap->tab_registers[109] = 0x7FC0; // Apparent power phase 2
  171. mb_mapping_sunspec_snap->tab_registers[111] = 0x7FC0; // Apparent power phase 3
  172. mb_mapping_sunspec_snap->tab_registers[115] = 0x7FC0; // Reactive power phase 1
  173. mb_mapping_sunspec_snap->tab_registers[117] = 0x7FC0; // Reactive power phase 2
  174. mb_mapping_sunspec_snap->tab_registers[119] = 0x7FC0; // Reactive power phase 3
  175. mb_mapping_sunspec_snap->tab_registers[123] = 0x7FC0; // Power factor phase 1
  176. mb_mapping_sunspec_snap->tab_registers[125] = 0x7FC0; // Power factor phase 2
  177. mb_mapping_sunspec_snap->tab_registers[127] = 0x7FC0; // Power factor phase 3
  178. mb_mapping_sunspec_snap->tab_registers[131] = 0x7FC0; // Active energy exported phase 1
  179. mb_mapping_sunspec_snap->tab_registers[133] = 0x7FC0; // Active energy exported phase 2
  180. mb_mapping_sunspec_snap->tab_registers[135] = 0x7FC0; // Active energy exported phase 3
  181. mb_mapping_sunspec_snap->tab_registers[139] = 0x7FC0; // Active energy imported phase 1
  182. mb_mapping_sunspec_snap->tab_registers[141] = 0x7FC0; // Active energy imported phase 2
  183. mb_mapping_sunspec_snap->tab_registers[143] = 0x7FC0; // Active energy imported phase 3
  184. mb_mapping_sunspec_snap->tab_registers[145] = 0x7FC0; // Apparent energy exported total
  185. mb_mapping_sunspec_snap->tab_registers[147] = 0x7FC0; // Apparent energy exported phase 1
  186. mb_mapping_sunspec_snap->tab_registers[149] = 0x7FC0; // Apparent energy exported phase 2
  187. mb_mapping_sunspec_snap->tab_registers[151] = 0x7FC0; // Apparent energy exported phase 3
  188. mb_mapping_sunspec_snap->tab_registers[153] = 0x7FC0; // Apparent energy imported total
  189. mb_mapping_sunspec_snap->tab_registers[155] = 0x7FC0; // Apparent energy imported phase 1
  190. mb_mapping_sunspec_snap->tab_registers[157] = 0x7FC0; // Apparent energy imported phase 2
  191. mb_mapping_sunspec_snap->tab_registers[159] = 0x7FC0; // Apparent energy imported phase 3
  192. mb_mapping_sunspec_snap->tab_registers[161] = 0x7FC0; // Reactive energy imported Q1 total
  193. mb_mapping_sunspec_snap->tab_registers[163] = 0x7FC0; // Reactive energy imported Q1 phase 1
  194. mb_mapping_sunspec_snap->tab_registers[165] = 0x7FC0; // Reactive energy imported Q1 phase 2
  195. mb_mapping_sunspec_snap->tab_registers[167] = 0x7FC0; // Reactive energy imported Q1 phase 3
  196. mb_mapping_sunspec_snap->tab_registers[169] = 0x7FC0; // Reactive energy imported Q2 total
  197. mb_mapping_sunspec_snap->tab_registers[171] = 0x7FC0; // Reactive energy imported Q2 phase 1
  198. mb_mapping_sunspec_snap->tab_registers[173] = 0x7FC0; // Reactive energy imported Q2 phase 2
  199. mb_mapping_sunspec_snap->tab_registers[175] = 0x7FC0; // Reactive energy imported Q2 phase 3
  200. mb_mapping_sunspec_snap->tab_registers[177] = 0x7FC0; // Reactive energy exported Q3 total
  201. mb_mapping_sunspec_snap->tab_registers[179] = 0x7FC0; // Reactive energy exported Q3 phase 1
  202. mb_mapping_sunspec_snap->tab_registers[181] = 0x7FC0; // Reactive energy exported Q3 phase 2
  203. mb_mapping_sunspec_snap->tab_registers[183] = 0x7FC0; // Reactive energy exported Q3 phase 3
  204. mb_mapping_sunspec_snap->tab_registers[185] = 0x7FC0; // Reactive energy exported Q4 total
  205. mb_mapping_sunspec_snap->tab_registers[187] = 0x7FC0; // Reactive energy exported Q4 phase 1
  206. mb_mapping_sunspec_snap->tab_registers[189] = 0x7FC0; // Reactive energy exported Q4 phase 2
  207. mb_mapping_sunspec_snap->tab_registers[191] = 0x7FC0; // Reactive energy exported Q4 phase 3
  208. // Test data for Symo
  209. modbus_set_float_badc(0.0, &mb_mapping_sunspec_snap->tab_registers[71]); // AC Total Current
  210. modbus_set_float_badc(0.0, &mb_mapping_sunspec_snap->tab_registers[73]); // AC Ph1 Current
  211. modbus_set_float_badc(0.0, &mb_mapping_sunspec_snap->tab_registers[75]); // AC Ph2 Current
  212. modbus_set_float_badc(0.0, &mb_mapping_sunspec_snap->tab_registers[77]); // AC Ph3 Current
  213. modbus_set_float_badc(230.0, &mb_mapping_sunspec_snap->tab_registers[81]);
  214. modbus_set_float_badc(230.0, &mb_mapping_sunspec_snap->tab_registers[83]);
  215. modbus_set_float_badc(230.0, &mb_mapping_sunspec_snap->tab_registers[85]);
  216. modbus_set_float_badc(230.0, &mb_mapping_sunspec_snap->tab_registers[79]); // L-N Voltage avg
  217. modbus_set_float_badc(400.0, &mb_mapping_sunspec_snap->tab_registers[89]);
  218. modbus_set_float_badc(400.0, &mb_mapping_sunspec_snap->tab_registers[91]);
  219. modbus_set_float_badc(400.0, &mb_mapping_sunspec_snap->tab_registers[93]);
  220. modbus_set_float_badc(400.0, &mb_mapping_sunspec_snap->tab_registers[87]); // L-L Voltage avg
  221. modbus_set_float_badc(50.0, &mb_mapping_sunspec_snap->tab_registers[95]); // Frequency
  222. modbus_set_float_badc(0.0, &mb_mapping_sunspec_snap->tab_registers[97]); // AC Real power
  223. modbus_set_float_badc(0.0, &mb_mapping_sunspec_snap->tab_registers[105]); // AC Apparent power
  224. modbus_set_float_badc(0.0, &mb_mapping_sunspec_snap->tab_registers[113]); // AC Reactive power
  225. modbus_set_float_badc(100.0, &mb_mapping_sunspec_snap->tab_registers[121]); // Power factor
  226. modbus_set_float_badc(0.0, &mb_mapping_sunspec_snap->tab_registers[129]); // Energy exported
  227. if(snapUpdate(mb_mapping_sunspec_snap, ctx) == 0){
  228. update_snap = time( NULL );
  229. } else {
  230. update_snap = 0;
  231. }
  232. header_length = modbus_get_header_length(ctx2);
  233. for (;;) {
  234. rdset = refset;
  235. if (select(fdmax + 1, &rdset, NULL, NULL, NULL) == -1) {
  236. perror("Server select() failure.");
  237. close_sigint(1);
  238. }
  239. /* Run through the existing connections looking for data to be
  240. * read */
  241. for (master_socket = 0; master_socket <= fdmax; master_socket++) {
  242. if (!FD_ISSET(master_socket, &rdset)) {
  243. continue;
  244. }
  245. if (master_socket == server_socket) {
  246. /* A client is asking a new connection */
  247. socklen_t addrlen;
  248. struct sockaddr_in clientaddr;
  249. int newfd;
  250. /* Handle new connections */
  251. addrlen = sizeof(clientaddr);
  252. memset(&clientaddr, 0, sizeof(clientaddr));
  253. newfd = accept(server_socket, (struct sockaddr *) &clientaddr, &addrlen);
  254. if (newfd == -1) {
  255. perror("Server accept() error");
  256. } else {
  257. FD_SET(newfd, &refset);
  258. if (newfd > fdmax) {
  259. /* Keep track of the maximum */
  260. fdmax = newfd;
  261. }
  262. printf("New connection from %s:%d on socket %d\n",
  263. inet_ntoa(clientaddr.sin_addr),
  264. clientaddr.sin_port,
  265. newfd);
  266. }
  267. } else {
  268. modbus_set_socket(ctx2, master_socket);
  269. rc = modbus_receive(ctx2, query);
  270. if (rc > 0) {
  271. if (query[header_length] == 0x03) {
  272. //Read holding registers
  273. reg_address = (uint16_t) MODBUS_GET_INT16_FROM_INT8(query, header_length + 1);
  274. nb_registers = (uint16_t) MODBUS_GET_INT16_FROM_INT8(query, header_length + 3);
  275. slaveAddress = query[header_length - 1];
  276. //printf("Request received. Slave address : %d, register address : %d, nb registers : %d\n", slaveAddress, reg_address, nb_registers);
  277. if(slaveAddress == 5) { // Symo snap meter
  278. if(difftime(time(NULL), update_snap) > 9){
  279. if(snapUpdate(mb_mapping_sunspec_snap, ctx) == 0){
  280. update_snap = time( NULL);
  281. //printf("Update symo\n");
  282. }
  283. }
  284. //printf("Reply Symo\n");
  285. rc = modbus_reply(ctx2, query, rc, mb_mapping_sunspec_snap);
  286. if (rc == -1) {
  287. perror("Error during reply");
  288. }
  289. } else {
  290. printf("Slave not found : %d\n", slaveAddress);
  291. }
  292. }
  293. } else if (rc == -1) {
  294. /* This example server in ended on connection closing or
  295. * any errors. */
  296. printf("Connection closed on socket %d\n", master_socket);
  297. close(master_socket);
  298. /* Remove from reference set */
  299. FD_CLR(master_socket, &refset);
  300. if (master_socket == fdmax) {
  301. fdmax--;
  302. }
  303. }
  304. }
  305. }
  306. }
  307. //modbus_close(ctx);
  308. //modbus_free(ctx);
  309. free(query);
  310. modbus_close(ctx2);
  311. modbus_free(ctx2);