TCP/IP based digital io controller for operating DigitalsOuts and reading DigitalIns.
Dependencies: EthernetInterface NetworkAPI mbed-rtos mbed
Fork of NetRelais by
Revision 10:22d49341340c, committed 2012-07-19
- Comitter:
- NegativeBlack
- Date:
- Thu Jul 19 11:13:50 2012 +0000
- Parent:
- 9:a4c85bea2d77
- Child:
- 11:e5375ae5c8c3
- Commit message:
- Implemented TCP/IP based digital io interface.
Changed in this revision
--- a/NetworkAPI.lib Wed Jul 18 18:45:57 2012 +0000 +++ b/NetworkAPI.lib Thu Jul 19 11:13:50 2012 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/NegativeBlack/code/NetworkAPI/#847a0b218e22 +http://mbed.org/users/NegativeBlack/code/NetworkAPI/#9796742904fa
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/controller.cpp Thu Jul 19 11:13:50 2012 +0000
@@ -0,0 +1,310 @@
+/**
+ * Copyright (c) 2012, Roy van Dam <roy@vandam-innovations.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "controller.hpp"
+
+Controller::~Controller()
+{
+ this->stop();
+}
+
+int
+Controller::start(int port, int max_pending)
+{
+ if (this->_network.server.open() < 0) {
+ printf("Could not open server socket.\n\r");
+ return -1;
+ }
+
+ if (this->_network.server.bind(port) < 0) {
+ printf("Could not bind server socket to port '%d'.\n\r", port);
+ return -1;
+ }
+
+ if (this->_network.server.listen(max_pending) < 0) {
+ printf("Could not put server socket into listening mode.\n\r");
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+Controller::stop()
+{
+ this->_network.server.close();
+ this->_network.client.close();
+}
+
+int
+Controller::dispatch()
+{
+ int result = 0;
+ network::Buffer buffer(256);
+
+ while (this->_network.server.getStatus() == network::Socket::Listening) {
+ if (this->_network.server.accept(this->_network.client) < 0) {
+ printf("Warning: failed to accept connection.\n\r");
+ continue;
+ }
+
+ printf("Client connected '%s:%d'.\n\r",
+ this->_network.client.getRemoteEndpoint().getAddress().toString().c_str(),
+ this->_network.client.getRemoteEndpoint().getPort());
+
+ while (this->_network.client.getStatus() == network::Socket::Connected) {
+ buffer.flush();
+
+ switch (this->_network.client.read(buffer)) {
+ case -1:
+ printf("Warning: failed to read data from client.\n\r");
+ break;
+
+ case 0:
+ printf("Connection closed.\n\r");
+ break;
+
+ default:
+ printf("Received %d bytes.\n\r%s\r", buffer.length(), (char *)buffer.pointer());
+
+ // Parse command
+ result = this->_parseCommand(buffer);
+
+ // Format reply code
+ buffer.flush();
+ buffer.setLength(std::snprintf(
+ (char *)buffer.pointer(), buffer.size(),
+ "e;%i;", result));
+
+ if (buffer.length() < 4) {
+ printf("Warning: failed to format error reply.\n\r");
+ continue;
+ }
+
+ if (this->_network.client.write(buffer) < 0) {
+ printf("Warning: failed to reply.\n\r");
+ }
+ continue;
+ }
+
+ this->_network.client.shutdown();
+ this->_network.client.close();
+ }
+ }
+}
+
+int
+Controller::_parseCommand(network::Buffer &buffer)
+{
+ int index = 0;
+ network::Buffer response(32);
+ char *cursor = (char *)buffer.pointer();
+
+ enum Controller::ParseState state = Controller::S_Init;
+ enum Controller::Command command = Controller::C_None;
+
+ while (cursor != NULL) {
+ switch (state) {
+ case Controller::S_Init: {
+ if (((*cursor) == 'r') && ((*(cursor + 1)) == ';')) {
+ command = Controller::C_Read;
+ state = Controller::S_Index;
+ cursor += 2;
+ continue;
+ }
+
+ if (((*cursor) == 'w') && ((*(cursor + 1)) == ';')) {
+ command = Controller::C_Write;
+ state = Controller::S_Index;
+ cursor += 2;
+ continue;
+ }
+
+ return Controller::E_InvalidCommand;
+ }
+
+ case Controller::S_Index: {
+ if (std::sscanf(cursor, "%d;", &index) != 1) {
+ return Controller::E_InvalidFormat;
+ }
+
+ char *offset = std::strchr(cursor, ';');
+ if (offset == NULL) {
+ return Controller::E_InvalidFormat;
+ }
+
+ cursor = offset + 1;
+ state = Controller::S_Execute;
+ continue;
+ }
+
+ case Controller::S_Execute: {
+ switch (command) {
+ case Controller::C_Read: {
+ DigitalIn *input = this->getInput(index);
+ if (input == NULL) {
+ return Controller::E_UnknownIndex;
+ }
+
+ response.setLength(std::snprintf(
+ (char *)response.pointer(), response.size(),
+ "r;%d;%d;", index, input->read()));
+
+ if (response.length() < 6) {
+ printf("Warning: failed to format reply.\n\r");
+ return Controller::E_Internal;
+ }
+
+ if (this->_network.client.write(response) < 0) {
+ printf("Warning: failed to reply on read request.\n\r");
+ return Controller::E_Internal;
+ }
+
+ if ((*cursor) == 0 || (*(cursor + 1)) == 0) {
+ return 0;
+ }
+
+ command = Controller::C_None;
+ state = Controller::S_Init;
+ continue;
+ }
+
+ case Controller::C_Write: {
+ DigitalOut *output = this->getOutput(index);
+ if (output == NULL) {
+ return Controller::E_UnknownIndex;
+ }
+
+ int value = 0;
+ if (std::sscanf(cursor, "%d;", &value) != 1) {
+ return Controller::E_InvalidFormat;
+ }
+
+ char *offset = std::strchr(cursor, ';');
+ if (offset == NULL) {
+ return Controller::E_InvalidFormat;
+ }
+ cursor = offset + 1;
+
+ if (value != 0 && value != 1) {
+ return Controller::E_InvalidValue;
+ }
+
+ output->write(value);
+
+ if ((*cursor) == 0 || (*(cursor + 1)) == 0) {
+ return 0;
+ }
+
+ command = Controller::C_None;
+ state = Controller::S_Init;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int
+Controller::addOutput(DigitalOut *output)
+{
+ if (this->_outputExists(output)) {
+ return false;
+ }
+
+ this->_io.output.push_back(output);
+ return true;
+}
+
+int
+Controller::addInput(DigitalIn *input)
+{
+ if (this->_inputExists(input)) {
+ return false;
+ }
+
+ this->_io.input.push_back(input);
+ return true;
+}
+
+DigitalOut *
+Controller::getOutput(size_t index)
+{
+ Controller::DigitalOutputList::iterator output;
+ output = this->_io.output.begin();
+ output = output + (int)index;
+
+ if (output >= this->_io.output.end()) {
+ return NULL;
+ }
+
+ return (*output);
+}
+
+DigitalIn *
+Controller::getInput(size_t index)
+{
+ DigitalInputList::iterator input;
+ input = this->_io.input.begin();
+ input = input + (int)index;
+
+ if (input >= this->_io.input.end()) {
+ return NULL;
+ }
+
+ return (*input);
+}
+
+bool
+Controller::_outputExists(DigitalOut *output)
+{
+ DigitalOutputList::iterator entry;
+ entry = this->_io.output.begin();
+ for (;entry != this->_io.output.end(); entry++) {
+ if (output == (*entry)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Controller::_inputExists(DigitalIn *input)
+{
+ Controller::DigitalInputList::iterator entry;
+ entry = this->_io.input.begin();
+ for (;entry != this->_io.input.end(); entry++) {
+ if (input == (*entry)) {
+ return true;
+ }
+ }
+
+ return false;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/controller.hpp Thu Jul 19 11:13:50 2012 +0000
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2012, Roy van Dam <roy@vandam-innovations.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CONTROLLER_HPP_
+#define _CONTROLLER_HPP_
+
+#include <cstdio>
+#include <cstring>
+#include <vector>
+
+#include "mbed.h"
+
+#include "NetworkAPI/select.hpp"
+#include "NetworkAPI/tcp/socket.hpp"
+
+class Controller
+{
+ protected:
+ typedef std::vector<DigitalOut *> DigitalOutputList;
+ typedef std::vector<DigitalIn *> DigitalInputList;
+
+ struct {
+ DigitalOutputList output;
+ DigitalInputList input;
+ } _io;
+
+ struct {
+ network::tcp::Socket server;
+ network::tcp::Socket client;
+ } _network;
+
+ enum Command {
+ C_None,
+ C_Read,
+ C_Write
+ };
+
+ enum ParseState {
+ S_Init,
+ S_Index,
+ S_Execute,
+ };
+
+ enum ParseError {
+ E_None = 0,
+ E_Internal = -1,
+ E_InvalidCommand = -2,
+ E_InvalidFormat = -3,
+ E_UnknownIndex = -4,
+ E_InvalidValue = -5
+ };
+
+ public:
+ ~Controller();
+
+ int start(int port = 61850, int max_pending = 1);
+ int stop();
+
+ int dispatch();
+
+ int addOutput(DigitalOut *output);
+ DigitalOut *getOutput(size_t index);
+
+ int addInput(DigitalIn *input);
+ DigitalIn *getInput(size_t index);
+
+ protected:
+ bool _outputExists(DigitalOut *output);
+ bool _inputExists(DigitalIn *input);
+
+ int _parseCommand(network::Buffer &buffer);
+};
+
+#endif // _CONTROLLER_HPP_
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/io.hpp Thu Jul 19 11:13:50 2012 +0000
@@ -0,0 +1,36 @@
+#ifndef _IO_HPP_
+#define _IO_HPP_
+
+#include "mbed.h"
+
+namespace io
+{
+ static DigitalOut led[4] = {
+ DigitalOut(LED1),
+ DigitalOut(LED2),
+ DigitalOut(LED3),
+ DigitalOut(LED4)
+ };
+
+ static DigitalOut ethernet[2] = {
+ DigitalOut(p30),
+ DigitalOut(p29)
+ };
+
+ static DigitalOut output[5] = {
+ DigitalOut(p9),
+ DigitalOut(p8),
+ DigitalOut(p7),
+ DigitalOut(p6),
+ DigitalOut(p5)
+ };
+
+ static DigitalIn input[4] = {
+ DigitalIn(p20),
+ DigitalIn(p19),
+ DigitalIn(p18),
+ DigitalIn(p17)
+ };
+}
+
+#endif // _IO_HPP_
\ No newline at end of file
--- a/main.cpp Wed Jul 18 18:45:57 2012 +0000
+++ b/main.cpp Thu Jul 19 11:13:50 2012 +0000
@@ -1,13 +1,33 @@
+/**
+ * Copyright (c) 2012, Roy van Dam <roy@vandam-innovations.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#include "mbed.h"
#include "EthernetInterface.h"
-#include "NetworkAPI/buffer.hpp"
-#include "NetworkAPI/select.hpp"
-#include "NetworkAPI/ip/address.hpp"
-#include "NetworkAPI/tcp/socket.hpp"
-using namespace network;
-
-#define MAX_CLIENTS 5
+#include "io.hpp"
+#include "controller.hpp"
int
main()
@@ -15,94 +35,22 @@
EthernetInterface interface;
interface.init();
interface.connect();
- printf("IP Address is %s\n\r", interface.getIPAddress());
- Select select;
- tcp::Socket server;
- tcp::Socket client[MAX_CLIENTS];
- tcp::Socket *socket = NULL;
-
- int result = 0;
- int index = 0;
+ printf("IP Address: %s\n\r", interface.getIPAddress());
- network::Buffer buffer(256);
- std::string message("Hello world!");
-
- // Configure the server socket (assume everty thing works)
- server.open();
- server.bind(1234);
- server.listen(MAX_CLIENTS);
-
- // Add sockets to the select api
- select.set(&server, Select::Read);
- for (index = 0; index < MAX_CLIENTS; index++) {
- select.set(&client[index], Select::Read);
+ Controller controller;
+ for (int i = 0; i < 5; i++) {
+ controller.addOutput(&io::output[i]);
}
- do {
- // Wait for activity
- result = select.wait();
- if (result < -1) {
- printf("Failed to select\n\r");
- break;
- }
-
- // Get the first socket
- socket = (tcp::Socket *)select.getReadable();
-
- for (; socket != NULL; socket = (tcp::Socket *)select.getReadable()) {
- // Check if there was a connection request.
- if (socket->getHandle() == server.getHandle()) {
- // Find an unused client
- for (index = 0; index < MAX_CLIENTS; index++) {
- if (client[index].getStatus() == Socket::Closed) {
- break;
- }
- }
-
- // Maximum connections reached
- if (index == MAX_CLIENTS) {
- printf("Maximum connections reached\n\r");
- continue;
- }
-
- // Accept the client
- socket->accept(client[index]);
- printf("Client connected %s:%d\n\r",
- client[index].getRemoteEndpoint().getAddress().toString().c_str(),
- client[index].getRemoteEndpoint().getPort());
-
- // Send a nice message to the client
- client[index].write((void *)message.data(), message.size());
- continue;
- }
-
- // It was not the server socket, so it must be a client talking to us.
- switch (socket->read(buffer)) {
- case 0:
- // Remote end disconnected
- printf("Client disconnected %s:%d\n\r",
- socket->getRemoteEndpoint().getAddress().toString().c_str(),
- socket->getRemoteEndpoint().getPort());
-
- // Close socket
- socket->close();
- break;
-
- case -1:
- printf("Error while reading data from socket\n\r");
- socket->close();
- break;
-
- default:
- printf("Message from %s:%d\n\r",
- socket->getRemoteEndpoint().getAddress().toString().c_str(),
- socket->getRemoteEndpoint().getPort());
-
- printf("%s\n\r", (char *)buffer.pointer());
- break;
- }
- }
-
- } while (server.getStatus() == Socket::Listening);
+ for (int i = 0; i < 4; i++) {
+ controller.addOutput(&io::led[i]);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ controller.addInput(&io::input[i]);
+ }
+
+ controller.start();
+ controller.dispatch();
}
\ No newline at end of file
Roy van Dam
