Advertisement
iyogurt

32 Channel Relay Driver

Mar 24th, 2015
281
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.56 KB | None | 0 0
  1. #include <SPI.h>
  2. #include <Ethernet.h>
  3. #include <Wire.h>
  4. #include <I2cDiscreteIoExpander.h>
  5.  
  6. byte mac[] = {
  7.  0x90, 0xA2, 0xDA, 0x00, 0x72, 0x07 };
  8.  
  9. int timerData[32] = {
  10.    0, 0, 0, 0, 0, 0, 0, 0,        // port A
  11.    0, 0, 0, 0, 0, 0, 0, 0,        // port B
  12.    0, 0, 0, 0, 0, 0, 0, 0,        // port C
  13.    0, 0, 0, 0, 0, 0, 0, 0         // port D
  14. };
  15.  
  16. byte serverState = 0;             // holds what state main state machine is in
  17. int statePortsAB = 0;             // 16-bit value representing ports A, B, input states
  18. int statePortsCD = 0;             // 16-bit value representing ports C, D, input states
  19. int portABCmd = 0;                // 16-bit value representing ports A and B output states
  20. int portCDCmd = 0;                // 16-bit value representing ports C and D output states
  21. String cmd = "";
  22. bool changed = false;             // were any outputs changed in the interrupt
  23. bool allZero = true;              // are all the timers zero?
  24. byte opState = 0;                 // current operational state {0: idle, 1: running}
  25.  
  26. // States are:
  27. // 0 = Wait for client connection
  28. // 1 = Wait for client command
  29.  
  30.  
  31. EthernetServer server(23);
  32. EthernetClient client;
  33.  
  34. I2cDiscreteIoExpander IOPorts[5] = {0, 1, 2, 3, 4};  //Order is; 0, 1 -> PortsABCD Output: 2, 3 -> PortsABCD Input: 4 -> IPAddress Input
  35.  
  36. void setup() {
  37.   Wire.begin();
  38.  
  39.   IPAddress ip(192, 168, 0, getIPAddress());  // Get the IP address based on the top 8-bits of the IPAddrSetting I/O Expander
  40.   IPAddress gateway(192, 168, 0, 1);
  41.   IPAddress subnet(255, 255, 255, 0);
  42.   Ethernet.begin(mac, ip, gateway, subnet);
  43.  
  44.   noInterrupts();                       // disable all interrupts
  45.   TCCR1A = 0;
  46.   TCCR1B = 0;
  47.   TCNT1  = 0;
  48.  
  49.   OCR1A = 125;                          // compare match register 16MHz/64/2kHz
  50.   TCCR1B |= (1 << WGM12);               // CTC mode
  51.   TCCR1B |= (1 << CS11) | (1 << CS10);  // 64 prescaler
  52.   TIMSK1 |= (1 << OCIE1A);              // enable timer compare interrupt
  53.   interrupts();
  54.  
  55.   server.begin();
  56.   getStatus();  // make an initial read of the current status of the PCF8575's
  57. }
  58.  
  59. ISR(TIMER1_COMPA_vect) {  // the interrupt that's used once the command is executed to determine which outputs to shut off  
  60.   checkTimers();
  61. }
  62.  
  63. void loop() {
  64.   getStatus();
  65.  
  66.   switch (serverState) {
  67.     case 0:
  68.       checkConnection();
  69.       break;
  70.     case 1:
  71.       checkCommand();
  72.       break;
  73.   }
  74. }
  75.  
  76. void checkConnection() {        // check to see if a client has connected to us
  77.   client = server.available();
  78.  
  79.   if (client) {
  80.     client.flush();
  81.     serverState = 1;
  82.   }
  83. }
  84.  
  85. void checkCommand() {  // read the command from the client and send to parseCommand when complete
  86.  
  87.   if (client.available() > 0) {
  88.     char c = client.read();
  89.     cmd += c;
  90.  
  91.     if(cmd.substring(cmd.length()-3) == ">>>") {
  92.       cmd = cmd.substring(0, cmd.length() - 3);  // cut off the >>> before we parse it
  93.       parseCommand(cmd);
  94.       cmd = "";        // clear the command and go back to waiting for a client
  95.       serverState = 0;
  96.     }
  97.   }
  98. }
  99.  
  100. void parseCommand(String cmd) {  // parse the command portion of the incoming string and determine the function to call
  101.   char has[512];
  102.   cmd.toCharArray(has, 512);
  103.   rc4(has);
  104.   String cmdString = has;
  105.  
  106.   cmd = cmdString.substring(cmdString.indexOf("cmd:") + 4, (cmdString.indexOf(",")));     // parse out the specific command
  107.   String parameters = cmdString.substring(cmd.length()+7);      // remove the command from the command string so we can pass parameters if needed
  108.  
  109.   if (cmd == "abort"){  // state machine to handle command parsing
  110.     handleAbort();
  111.     server.print(F("{response:'Aborted'}"));
  112.    
  113.   } else if (cmd == "getStatus") {
  114.     sendStatus();
  115.    
  116.   } else if (cmd == "getUUID") {
  117.     getUUID();
  118.    
  119.   } else if (cmd == "getOp") {
  120.     server.print(opState);
  121.    
  122.   } else if (cmd == "getTimers") {
  123.     sendTimers();
  124.    
  125.   } else if (cmd == "setupTimer") {
  126.     setupTimer(parameters);
  127.    
  128.   } else if (cmd == "resetTimers") {
  129.     if (opState == 0){
  130.       for (int i = 0; i < 32; i++) { timerData[i] = 0; }  // reset the timer data array
  131.     }
  132.   } else if (cmd == "staticWrite") {
  133.     staticWrite(parameters);
  134.    
  135.   } else if (cmd == "setupOutputs") {
  136.     setupOutputs(parameters);
  137.    
  138.   } else if (cmd == "execute") {
  139.     executeCommand();
  140.     server.print(F("{response:'Executing command'}"));
  141.    
  142.   } else {
  143.     server.println(F("{response:'Invalid Command'}"));
  144.   }
  145. }
  146.  
  147. void getUUID() {   // return the UUID to the client
  148.   server.print(F("{UUID:0x"));  
  149.     for (int i = 0; i < 6; i++) {
  150.       server.print(mac[i], HEX);
  151.     }
  152.     server.println(F("}"));
  153. }
  154.  
  155. void rc4(char *data) {  // this is the RC4 encryption algorithm
  156.   unsigned char S[256];
  157.   int i,j,temp;
  158.   char key[] = "3CYUcW6mCd4ZdVuZ";  // PSK - TODO: This should be dynamic and created on connect
  159.  
  160.   for (i=0;i<256;i++) {
  161.     S[i] = i;
  162.   }
  163.   j = 0;
  164.   for (i=0;i<256;i++) {
  165.     j = (j+S[i]+key[i%strlen(key)]) %256;    
  166.     temp = S[i];
  167.     S[i] = S[j];
  168.     S[j] = temp;
  169.   }
  170.   i = j = 0;
  171.   for (int k=0;k<strlen(data);k++) {
  172.     i = (i+1) %256;
  173.     j = (j+S[i]) %256;
  174.     temp = S[i];
  175.     S[i] = S[j];
  176.     S[j] = temp;
  177.     data[k] = data[k]^S[(S[i]+S[j]) %256];
  178.   }  
  179.   data[strlen(data)+1] = '\0';
  180. }
  181.  
  182. void sendStatus() {  // send the current input port status to the client
  183.   server.print(F("{PortsAB:"));
  184.   server.print(statePortsAB);
  185.   server.print(F(", PortsCD:"));
  186.   server.print(statePortsCD);
  187.   server.println(F("}"));
  188. }
  189.  
  190. void sendTimers() { // send back the timing array to the client
  191.   server.print(F("{"));
  192.   for (int i = 0; i < 32; i++) {
  193.     server.print(F("timer"));
  194.     server.print(i);
  195.     server.print(F(":"));
  196.     server.print(timerData[i]/2);       // we divide by two because we multipled by two going in (see the updateTimers function)
  197.     server.print(F(", "));
  198.   }
  199.   server.print(F("}"));
  200. }
  201.  
  202. void getStatus() {  // check the current input port statuses and write them to the global variables
  203.   for (int i = 2; i < 4; i++) {
  204.    uint8_t status = IOPorts[i].digitalRead();
  205.    if (TWI_SUCCESS == status) {
  206.      statePortsAB = status;
  207.    } else {
  208.      statePortsAB = 0;
  209.    }
  210.   }
  211. }
  212.  
  213. int getIPAddress() {  // check the IPADDR ports and use them to construct the subnet portion of the IP address
  214.   uint8_t status = IOPorts[4].digitalRead();
  215.   if (TWI_SUCCESS == status) {
  216.    return status;
  217.   } else {
  218.    return 0;
  219.   }
  220. }
  221.  
  222. void setupOutputs(String cmdString) {  // parse the command string and setup the state to send to the port expanders
  223.   portABCmd = parseValueFromCmd(cmdString, "portAB:");
  224.  
  225.   cmdString = cmdString.substring(cmdString.indexOf("portAB:") + 12);  // trim out the AB command
  226.  
  227.   portCDCmd = parseValueFromCmd(cmdString, "portCD:");
  228. }
  229.  
  230. void setupTimer(String cmdString) {  // parse the command string and setup the timer array used in the timer interrupt function  
  231.   if (opState == 0) {      // only allow updates to the timer array if we're not currently running
  232.     //get the output number and value, put them in the array, delete them from the string
  233.     int outputNum = parseOutputFromCmd(cmdString);
  234.     int outputVal = parseValueFromCmd(cmdString, "output", 9) * 2;  // we multiply by two because timer is running at 2kHz and
  235.     timerData[outputNum] = outputVal;                               // timers are decremented by one each tick so our values need to be double
  236.   } else {
  237.     server.print(F("{response:'Command not allowed in current state'}"));
  238.   }
  239. }
  240.  
  241. void executeCommand() {  // send the output commands to the port expanders and begin the timer interrupt
  242.   opState = 1;
  243.   updateOutputs();
  244. }
  245.  
  246. void staticWrite(String cmdString) {  // writes a static value to a single output
  247.   int output = parseOutputFromCmd(cmdString);
  248.   int outputState = parseValueFromCmd(cmdString, "output", 7);
  249.  
  250.   if (output < 16) {
  251.     bitWrite(portABCmd, output, outputState);
  252.   } else {
  253.     bitWrite(portCDCmd, output - 16, outputState);
  254.   }
  255. }
  256.  
  257. void updateOutputs() {  // updates the register outputs based on the current global values
  258.   uint8_t status = IOPorts[0].digitalWrite(portABCmd);
  259.   status = IOPorts[1].digitalWrite(portCDCmd);
  260. }
  261.  
  262. void handleAbort() {  // stops the timers and resets all ports to false when an 'abort' command is received
  263.   portABCmd = 0;
  264.   portCDCmd = 0;
  265.   updateOutputs();
  266.   opState = 0;
  267. }
  268.  
  269. void checkTimers() {
  270.   if (opState == 1) {                      // this only runs if in the 'running' state
  271.     for (int i = 0; i < 32; i++) {         // iterate over all of the timing values...
  272.       if (timerData[i] != 0){              // if the timer is not at 0 decrement it...
  273.         allZero = false;                   // not all timers are at zero
  274.         timerData[i]--;                    // decrement the value, but we should replace this with a timing mechanism instead of assuming our timing is perfect, especially now since the interrupt is running at 2kHz not 1.
  275.        
  276.         if (timerData[i] == 0) {           // if the value is 0 now...
  277.           if (i < 16) {                    // if the value is in the first 16 its ports A and B...
  278.             bitWrite(portABCmd, i, 0);     // update the appropriate bit
  279.           } else {                         // if the value is in the second 16 its ports C and D...
  280.             bitWrite(portCDCmd, i-16, 0);  // update the appropriate bit
  281.           }
  282.           changed = true;                  // mark it as changed so we know we need to send a new command to the chip
  283.           allZero = true;                  // this one is at zero now, so reset the boolean
  284.         }
  285.       }
  286.     }
  287.   }
  288.  
  289.   if (allZero) {
  290.     handleAbort();                         // everyone is done, stop timing
  291.   } else if (changed) {
  292.     updateOutputs();                       // something changed, update the outputs
  293.   }                                        // short-circuit on abort, so we only send an update once
  294. }
  295.  
  296. int parseValueFromCmd(String cmdString, String value) {  // parse on integer value from in incoming command string
  297.   return cmdString.substring(cmdString.indexOf(value) + value.length(), (cmdString.indexOf(","))).toInt();
  298. }
  299.  
  300. int parseValueFromCmd(String cmdString, String value, int length) {  // parse on integer value from in incoming command string
  301.   return cmdString.substring(cmdString.indexOf(value) + length, (cmdString.indexOf(","))).toInt();
  302. }
  303.  
  304. int parseOutputFromCmd(String cmdString) {  // parse an integer value from a output command to determine which output is being commanded
  305.   return cmdString.substring(cmdString.indexOf("output") + 6, (cmdString.indexOf(":"))).toInt();
  306. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement