j0h

ESP32_to_IDEC_PLC

j0h
Mar 19th, 2026
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.57 KB | None | 0 0
  1. /*
  2.   ESP32 Arduino sketch: toggle IDEC PLC Y0..Y7 over Maintenance Protocol (TCP/2101)
  3.  
  4.   Translates Python MiSmSerial Protocol:
  5.     - Build msg: ENQ + device(2 ASCII) + '0' + 'W' + 'y' + addr(4 ASCII) + ('0'/'1')
  6.     - BCC: XOR of all bytes in msg, appended as 2 ASCII hex chars
  7.     - Append CR
  8.  
  9.   Notes:
  10.     - PLC replies typically end with CR, so we read until '\r' or timeout.
  11.     - This demo opens a new TCP connection per write (matches your Python).
  12. */
  13.  
  14. #include <WiFi.h>
  15.  
  16. const char* SSID     = "Bangor Makerspace";
  17. const char* PASS     = "UWishL0L";
  18.  
  19. const char* PLC_IP   = "10.1.0.175";
  20. const uint16_t PLC_PORT = 2101;
  21.  
  22. // Device code is 2 ASCII hex chars in your frames (commonly "FF" or "00")
  23. const char* DEVICE = "FF";
  24.  
  25. // ---------- Utility: XOR BCC, returned as 2 ASCII hex chars ----------
  26. static void bcc_xor_ascii_hex(const uint8_t* data, size_t len, char out2[2]) {
  27.   uint8_t b = 0;
  28.   for (size_t i = 0; i < len; i++) b ^= data[i];
  29.  
  30.   const char* hex = "0123456789ABCDEF";
  31.   out2[0] = hex[(b >> 4) & 0x0F];
  32.   out2[1] = hex[b & 0x0F];
  33. }
  34.  
  35. // ---------- Read until CR or timeout ----------
  36. static size_t read_until_cr(WiFiClient& client, uint8_t* out, size_t out_cap, uint32_t timeout_ms) {
  37.   size_t n = 0;
  38.   uint32_t start = millis();
  39.  
  40.   while ((millis() - start) < timeout_ms && n < out_cap) {
  41.     while (client.available() && n < out_cap) {
  42.       int c = client.read();
  43.       if (c < 0) break;
  44.       out[n++] = (uint8_t)c;
  45.       if ((uint8_t)c == '\r') return n;
  46.     }
  47.     delay(2);
  48.   }
  49.   return n;
  50. }
  51.  
  52. // ---------- Transmit framed payload ----------
  53. static bool xmit(const char* ip, const uint8_t* payload, size_t payload_len,
  54.                  uint8_t* rx_buf, size_t rx_cap, size_t* rx_len,
  55.                  uint32_t timeout_ms = 2000) {
  56.  
  57.   WiFiClient client;
  58.   client.setTimeout(timeout_ms / 1000.0f);
  59.  
  60.   if (!client.connect(ip, PLC_PORT)) {
  61.     return false;
  62.   }
  63.  
  64.   // Send
  65.   size_t sent = client.write(payload, payload_len);
  66.   client.flush();
  67.   if (sent != payload_len) {
  68.     client.stop();
  69.     return false;
  70.   }
  71.  
  72.   // Receive (until CR)
  73.   *rx_len = read_until_cr(client, rx_buf, rx_cap, timeout_ms);
  74.   client.stop();
  75.   return true;
  76. }
  77.  
  78. // ---------- Build + send "write y-bit" request ----------
  79. static bool write_y_bit(uint8_t bit_index, uint8_t value,
  80.                         uint8_t* rx_buf, size_t rx_cap, size_t* rx_len) {
  81.   if (bit_index > 7) return false;
  82.   if (value != 0 && value != 1) return false;
  83.  
  84.   // Message (without BCC/CR): ENQ + device(2) + '0' + 'W' + 'y' + addr(4) + val(1)
  85.   // Total = 1 + 2 + 1 + 1 + 1 + 4 + 1 = 11 bytes
  86.   uint8_t msg[11];
  87.   size_t p = 0;
  88.  
  89.   msg[p++] = 0x05;                 // ENQ
  90.   msg[p++] = (uint8_t)DEVICE[0];   // device 2 ASCII
  91.   msg[p++] = (uint8_t)DEVICE[1];
  92.   msg[p++] = (uint8_t)'0';         // continuation
  93.   msg[p++] = (uint8_t)'W';         // command
  94.   msg[p++] = (uint8_t)'y';         // datatype
  95.  
  96.   // addr as 4 ASCII digits, like Python f"{bit_index:04d}"
  97.   char addr[5];
  98.   snprintf(addr, sizeof(addr), "%04u", (unsigned)bit_index);
  99.   msg[p++] = (uint8_t)addr[0];
  100.   msg[p++] = (uint8_t)addr[1];
  101.   msg[p++] = (uint8_t)addr[2];
  102.   msg[p++] = (uint8_t)addr[3];
  103.  
  104.   msg[p++] = value ? (uint8_t)'1' : (uint8_t)'0';
  105.  
  106.   // Frame = msg + bcc(2 ascii) + CR
  107.   uint8_t frame_buf[11 + 2 + 1];
  108.   memcpy(frame_buf, msg, sizeof(msg));
  109.  
  110.   char bcc2[2];
  111.   bcc_xor_ascii_hex(msg, sizeof(msg), bcc2);
  112.   frame_buf[11] = (uint8_t)bcc2[0];
  113.   frame_buf[12] = (uint8_t)bcc2[1];
  114.   frame_buf[13] = (uint8_t)'\r';
  115.  
  116.   return xmit(PLC_IP, frame_buf, sizeof(frame_buf), rx_buf, rx_cap, rx_len);
  117. }
  118.  
  119. // ---------- Pretty-print bytes ----------
  120. static void print_bytes(const uint8_t* b, size_t n) {
  121.   Serial.print("b'");
  122.   for (size_t i = 0; i < n; i++) {
  123.     uint8_t c = b[i];
  124.     if (c >= 32 && c <= 126 && c != '\\' && c != '\'') {
  125.       Serial.write(c);
  126.     } else {
  127.       Serial.print("\\x");
  128.       const char* hex = "0123456789abcdef";
  129.       Serial.print(hex[(c >> 4) & 0x0F]);
  130.       Serial.print(hex[c & 0x0F]);
  131.     }
  132.   }
  133.   Serial.println("'");
  134. }
  135.  
  136. void setup() {
  137.   Serial.begin(115200);
  138.   delay(200);
  139.  
  140.   WiFi.mode(WIFI_STA);
  141.   WiFi.begin(SSID, PASS);
  142.  
  143.   Serial.print("WiFi connecting");
  144.   while (WiFi.status() != WL_CONNECTED) {
  145.     delay(300);
  146.     Serial.print(".");
  147.   }
  148.   Serial.println();
  149.   Serial.print("WiFi OK, IP: ");
  150.   Serial.println(WiFi.localIP());
  151.  
  152.   // Optional: basic TCP connectivity check
  153.   WiFiClient probe;
  154.   if (probe.connect(PLC_IP, PLC_PORT)) {
  155.     Serial.println("PLC port 2101 open (TCP connect OK).");
  156.     probe.stop();
  157.   } else {
  158.     Serial.println("PLC port 2101 connect failed.");
  159.   }
  160. }
  161.  
  162. void loop() {
  163.   static uint32_t last_run = 0;
  164.   if (millis() - last_run < 1000) return;
  165.   last_run = millis();
  166.  
  167.   uint8_t rx[256];
  168.   size_t rx_len = 0;
  169.  
  170.   // Turn outputs ON 0..7
  171.   for (uint8_t i = 0; i < 8; i++) {
  172.     bool ok = write_y_bit(i, 1, rx, sizeof(rx), &rx_len);
  173.     Serial.print("TX: set Y"); Serial.print(i); Serial.print("=1 -> ");
  174.     if (!ok) {
  175.       Serial.println("RX: (send/connect failed)");
  176.     } else {
  177.       Serial.print("RX: ");
  178.       print_bytes(rx, rx_len);
  179.     }
  180.     delay(1000);
  181.   }
  182.  
  183.   delay(1000);
  184.  
  185.   // Turn outputs OFF 7..0
  186.   for (int i = 7; i >= 0; i--) {
  187.     bool ok = write_y_bit((uint8_t)i, 0, rx, sizeof(rx), &rx_len);
  188.     Serial.print("TX: set Y"); Serial.print(i); Serial.print("=0 -> ");
  189.     if (!ok) {
  190.       Serial.println("RX: (send/connect failed)");
  191.     } else {
  192.       Serial.print("RX: ");
  193.       print_bytes(rx, rx_len);
  194.     }
  195.     delay(1000);
  196.   }
  197.  
  198.   // Pause before repeating
  199.   delay(2000);
  200. }
  201.  
Advertisement
Add Comment
Please, Sign In to add comment