├── .clang-format ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples └── simple │ └── simple.ino ├── keywords.txt ├── library.json ├── library.properties ├── makefile ├── platformio.ini └── src ├── Config.h ├── Definitions.h ├── Opc.h ├── OpcServer.cpp ├── OpcServer.h └── Platforms.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: true 6 | AlignConsecutiveAssignments: false 7 | AlignEscapedNewlinesLeft: false 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: true 12 | AllowShortCaseLabelsOnASingleLine: true 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLoopsOnASingleLine: true 16 | #AlwaysBreakAfterDefinitionReturnType: None #deprecated 17 | AlwaysBreakBeforeMultilineStrings: true 18 | AlwaysBreakTemplateDeclarations: true 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | BreakBeforeBinaryOperators: None 22 | BreakBeforeBraces: Attach 23 | BreakBeforeTernaryOperators: true 24 | BreakConstructorInitializersBeforeComma: false 25 | ColumnLimit: 0 26 | CommentPragmas: '^ IWYU pragma:' 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 28 | ConstructorInitializerIndentWidth: 4 29 | ContinuationIndentWidth: 4 30 | Cpp11BracedListStyle: true 31 | DerivePointerAlignment: false 32 | DisableFormat: false 33 | ExperimentalAutoDetectBinPacking: false 34 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 35 | IndentCaseLabels: true 36 | IndentWidth: 2 37 | IndentWrappedFunctionNames: false 38 | KeepEmptyLinesAtTheStartOfBlocks: false 39 | MacroBlockBegin: '' 40 | MacroBlockEnd: '' 41 | MaxEmptyLinesToKeep: 1 42 | NamespaceIndentation: None 43 | ObjCBlockIndentWidth: 2 44 | ObjCSpaceAfterProperty: true 45 | ObjCSpaceBeforeProtocolList: true 46 | PenaltyBreakBeforeFirstCallParameter: 19 47 | PenaltyBreakComment: 300 48 | PenaltyBreakFirstLessLess: 120 49 | PenaltyBreakString: 1000 50 | PenaltyExcessCharacter: 1000000 51 | PenaltyReturnTypeOnItsOwnLine: 60 52 | PointerAlignment: Left 53 | SpaceAfterCStyleCast: false 54 | SpaceBeforeAssignmentOperators: true 55 | SpaceBeforeParens: ControlStatements 56 | SpaceInEmptyParentheses: false 57 | SpacesBeforeTrailingComments: 2 58 | SpacesInAngles: false 59 | SpacesInContainerLiterals: true 60 | SpacesInCStyleCastParentheses: false 61 | SpacesInParentheses: false 62 | SpacesInSquareBrackets: false 63 | Standard: Cpp11 64 | TabWidth: 8 65 | UseTab: Never 66 | ... 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .clang_complete 3 | .gcc-flags.json 4 | 5 | build/ 6 | 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | *.smod 25 | 26 | # Compiled Static libraries 27 | *.lai 28 | *.la 29 | *.a 30 | *.lib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | 5 | # Cache PlatformIO packages using Travis CI container-based infrastructure 6 | sudo: false 7 | cache: 8 | directories: 9 | - "~/.platformio" 10 | 11 | # Generated using: find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sed 's/^/ - PLATFORMIO_CI_SRC=/' > tmp.yml 12 | env: 13 | - PLATFORMIO_CI_SRC=examples/simple/simple.ino 14 | 15 | install: 16 | - pip install -U platformio 17 | 18 | # 19 | # Libraries from PlatformIO Library Registry: 20 | # 21 | # http://platformio.org/lib/show/17/Adafruit_CC3000 22 | # http://platformio.org/lib/show/299/WiFi101 23 | - platformio lib install 17 299 24 | 25 | script: 26 | - platformio ci --lib="." --project-conf="platformio.ini" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Donald Patrick Seal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpcServer 2 | 3 | [![Build Status](https://travis-ci.org/plasticrake/OpcServer.svg?branch=master)](https://travis-ci.org/plasticrake/OpcServer) 4 | 5 | Open Pixel Control (OPC) server for Arduino. 6 | 7 | Compatible with boards that utilize Arduino style [WiFiServer](https://www.arduino.cc/en/Reference/WiFiServer) / [WiFiClient](https://www.arduino.cc/en/Reference/WiFiClient) APIs. 8 | 9 | - **ESP8266** 10 | - NodeMCU 11 | - [Adafruit HUZZAH](https://www.adafruit.com/products/2471) 12 | - ... 13 | - **ATSAMD21** 14 | - [Arduino Zero](https://www.arduino.cc/en/Main/ArduinoBoardZero) with [WiFi Shield 101](https://www.arduino.cc/en/Main/ArduinoWiFiShield101) **(compiles, not tested)** 15 | - [Adafruit Feather M0 WiFi - ATSAMD21 + ATWINC1500](https://www.adafruit.com/product/3010) **(compiles, not tested)** 16 | - **Particle** (Formerly Spark) 17 | - [Photon](https://docs.particle.io/datasheets/photon-datasheet/) 18 | - [Core](https://docs.particle.io/datasheets/core-datasheet/) **(compiles, not tested)** 19 | 20 | If you've run this successfully on other boards let me know! 21 | 22 | # Installation 23 | 24 | ## Arduino IDE 25 | 1. In the Arduino IDE, Choose **Sketch** > **Include Library** > **Manage Libraries** 26 | 2. Search for **OpcServer** 27 | 3. Click **Install** 28 | 29 | ## [PlatformIO](http://platformio.org/lib/show/350/OpcServer) 30 | 1. platformio lib install 350 31 | 32 | ## Particle 33 | 1. Copy files from src/* into your project 34 | 35 | # Usage 36 | See [examples](https://github.com/plasticrake/OpcServer/tree/master/examples). 37 | ### Includes 38 | ```c++ 39 | #include "OpcServer.h" 40 | ``` 41 | Depending on your platform you may need to add other headers. For example _ESP8266WiFi.h_ for ESP8266. 42 | 43 | ### Initialize 44 | Initialize `OpcServer` with `WiFiServer` (or `TCPServer` for Particle), `OpcClient` sized for the number of clients you'd like to support, and a `buffer` large enough for the OPC messages you'd like to receive. 45 | ```c++ 46 | WiFiServer server = WiFiServer(OPC_PORT); 47 | OpcClient opcClients[OPC_MAX_CLIENTS]; 48 | uint8_t buffer[OPC_BUFFER_SIZE * OPC_MAX_CLIENTS]; 49 | 50 | OpcServer opcServer = OpcServer(server, OPC_CHANNEL, 51 | opcClients, OPC_MAX_CLIENTS, 52 | buffer, OPC_BUFFER_SIZE); 53 | ``` 54 | 55 | ### setup() 56 | 57 | Optionally add any callback functions you'd like `OpcServer` to run when a message is received or a client connects/disconnects. 58 | ```c++ 59 | opcServer.setMsgReceivedCallback(cbOpcMessage); 60 | opcServer.setClientConnectedCallback(cbOpcClientConnected); 61 | opcServer.setClientDisconnectedCallback(cbOpcClientDisconnected); 62 | ``` 63 | Then call `.begin()`. 64 | ```c++ 65 | opcServer.begin(); 66 | ``` 67 | 68 | ### loop() 69 | Call `.process()` in your loop to process any incoming OPC data. 70 | ```c++ 71 | opcServer.process(); 72 | ``` 73 | -------------------------------------------------------------------------------- /examples/simple/simple.ino: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // Simple Example 3 | // * Replace <> and <> with your WiFi credientials 4 | // * At startup the device will output it's IP and OPC Port to Serial. 5 | // * Connect your OPC Client and send data. Received headers will be output to Serial. 6 | //============================================================================== 7 | #include "OpcServer.h" 8 | 9 | #if defined(ESP8266) 10 | #include 11 | #elif defined(PARTICLE) 12 | #include "application.h" 13 | #define WiFiServer TCPServer 14 | #define WiFiClient TCPClient 15 | #else 16 | #include 17 | #include 18 | #endif 19 | 20 | //============================================================================== 21 | // Configuration 22 | //============================================================================== 23 | const char* WIFI_SSID = "<>"; 24 | const char* WIFI_PASS = "<>"; 25 | 26 | const int OPC_PORT = 7890; 27 | const int OPC_CHANNEL = 1; // Channel to respond to in addition to 0 (broadcast channel) 28 | const int OPC_MAX_CLIENTS = 2; // Maxiumum Number of Simultaneous OPC Clients 29 | 30 | // Size of OPC Read Buffer 31 | // * Should probably size to number of pixels * 3 plus 4 byte header 32 | // * If an OPC Message is received that is longer, the remaining pixels are discarded 33 | // * If you are receiving OPC Messages that are longer than the number of pixels you may see increased 34 | // performance by increasing the buffer size to hold the entire message 35 | const int OPC_MAX_PIXELS = 64; 36 | const int OPC_BUFFER_SIZE = OPC_MAX_PIXELS * 3 + OPC_HEADER_BYTES; 37 | //------------------------------------------------------------------------------ 38 | 39 | // Callback when a full OPC Message has been received 40 | void cbOpcMessage(uint8_t channel, uint8_t command, uint16_t length, uint8_t* data) { 41 | Serial.print("chn:"); 42 | Serial.print(channel); 43 | Serial.print("cmd:"); 44 | Serial.print(command); 45 | Serial.print("len:"); 46 | Serial.println(length); 47 | } 48 | 49 | // Callback when a client is connected 50 | void cbOpcClientConnected(WiFiClient& client) { 51 | Serial.print("New OPC Client: "); 52 | #if defined(ESP8266) || defined(PARTICLE) 53 | Serial.println(client.remoteIP()); 54 | #else 55 | Serial.println("(IP Unknown)"); 56 | #endif 57 | } 58 | 59 | // Callback when a client is disconnected 60 | void cbOpcClientDisconnected(OpcClient& opcClient) { 61 | Serial.print("Client Disconnected: "); 62 | Serial.println(opcClient.ipAddress); 63 | } 64 | 65 | //============================================================================== 66 | // OpcServer Init 67 | //============================================================================== 68 | WiFiServer server = WiFiServer(OPC_PORT); 69 | OpcClient opcClients[OPC_MAX_CLIENTS]; 70 | uint8_t buffer[OPC_BUFFER_SIZE * OPC_MAX_CLIENTS]; 71 | OpcServer opcServer = OpcServer(server, OPC_CHANNEL, opcClients, OPC_MAX_CLIENTS, buffer, OPC_BUFFER_SIZE); 72 | //------------------------------------------------------------------------------ 73 | 74 | void setup() { 75 | Serial.begin(115200); 76 | 77 | #if !defined(PARTICLE) 78 | if (WiFi.SSID() != WIFI_SSID) { 79 | WiFi.begin(WIFI_SSID, WIFI_PASS); 80 | } 81 | while (WiFi.status() != WL_CONNECTED) { 82 | delay(500); 83 | Serial.print("."); 84 | } 85 | #endif 86 | 87 | opcServer.setMsgReceivedCallback(cbOpcMessage); 88 | opcServer.setClientConnectedCallback(cbOpcClientConnected); 89 | opcServer.setClientDisconnectedCallback(cbOpcClientDisconnected); 90 | 91 | opcServer.begin(); 92 | 93 | Serial.print("\nOPC Server: "); 94 | Serial.print(WiFi.localIP()); 95 | Serial.print(":"); 96 | Serial.println(OPC_PORT); 97 | } 98 | 99 | void loop() { opcServer.process(); } 100 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For OpcServer 3 | ####################################### 4 | # Class 5 | ####################################### 6 | 7 | OpcServer KEYWORD1 8 | OpcClient KEYWORD1 9 | OpcMessage KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions 13 | ####################################### 14 | 15 | begin KEYWORD2 16 | process KEYWORD2 17 | 18 | getBufferSize KEYWORD2 19 | getBufferSizeInPixels KEYWORD2 20 | getBytesAvailable KEYWORD2 21 | getClientCount KEYWORD2 22 | getClientSize KEYWORD2 23 | 24 | setClientConnectedCallback KEYWORD2 25 | setClientDisconnectedCallback KEYWORD2 26 | setMsgReceivedCallback KEYWORD2 27 | 28 | ####################################### 29 | # Constants 30 | ####################################### 31 | OPC_SET_PIXEL_COLORS LITERAL1 32 | OPC_SYSTEM_EXCLUSIVE LITERAL1 33 | 34 | OPC_GLOBAL_COLOR_CORRECTION LITERAL1 35 | OPC_FIRMWARE_CONFIGURATION LITERAL1 36 | 37 | OPC_HEADER_BYTES LITERAL1 38 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OpcServer", 3 | "description": "Open Pixel Control (OPC) Server Library for Arduino Platform", 4 | "keywords": "led,pixel,pixels,opc,openpixelcontrol,fadecandy,esp8266,wifi,communication,protocol,server", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/plasticrake/OpcServer.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": "*" 12 | } 13 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=OpcServer 2 | version=1.1.0 3 | author=Patrick Seal 4 | maintainer=Patrick Seal 5 | sentence=Open Pixel Control (OPC) Server Library for Arduino 6 | paragraph=Open Pixel Control (OPC) Server Library for Arduino 7 | category=Communication 8 | url=https://github.com/plasticrake/OpcServer 9 | architectures=* 10 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | all: photon core 2 | 3 | photon: 4 | rm -rf build/particle/* 5 | mkdir -p build/particle 6 | cp src/*.* build/particle/ 7 | cp examples/simple/* build/particle/ 8 | cd build/ && particle compile photon particle 9 | 10 | core: 11 | rm -rf build/particle/* 12 | mkdir -p build/particle 13 | cp src/*.* build/particle/ 14 | cp examples/simple/* build/particle/ 15 | cd build && particle compile core particle 16 | 17 | clean: 18 | rm -rf build/* 19 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | [platformio] 2 | 3 | [env:nodemcuv2] 4 | platform = espressif 5 | framework = arduino 6 | board = nodemcuv2 7 | lib_ignore = Adafruit_CC3000_ID17, WiFi101_ID299 8 | 9 | [env:nodemcuv2_160] 10 | platform = espressif 11 | framework = arduino 12 | board = nodemcuv2 13 | board_f_cpu = 160000000L 14 | lib_ignore = Adafruit_CC3000_ID17, WiFi101_ID299 15 | 16 | [env:nodemcu] 17 | platform = espressif 18 | framework = arduino 19 | board = nodemcu 20 | lib_ignore = Adafruit_CC3000_ID17, WiFi101_ID299 21 | 22 | [env:huzzah] 23 | platform = espressif 24 | framework = arduino 25 | board = huzzah 26 | lib_ignore = Adafruit_CC3000_ID17, WiFi101_ID299 27 | 28 | [env:zero] 29 | platform = atmelsam 30 | framework = arduino 31 | board = zero 32 | lib_install = 17,299 33 | -------------------------------------------------------------------------------- /src/Config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 1 = Write Warning Messages to Serial 4 | #ifndef OPC_SERV_WARN 5 | #define OPC_SERV_WARN 1 6 | #endif 7 | 8 | // 1 = Write Info Messages to Serial 9 | #ifndef OPC_SERV_INFO 10 | #define OPC_SERV_INFO 0 11 | #endif 12 | 13 | // 1 = Write Performance Messages to Serial 14 | #ifndef OPC_SERV_PERF 15 | #define OPC_SERV_PERF 0 16 | #endif 17 | 18 | // 1 = Write Debug Messages to Serial 19 | #ifndef OPC_SERV_DEBUG 20 | #define OPC_SERV_DEBUG 0 21 | #endif 22 | -------------------------------------------------------------------------------- /src/Definitions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef min 4 | #undef min 5 | #endif 6 | #define min(a, b) \ 7 | ({ __typeof__ (a) _a = (a); \ 8 | __typeof__ (b) _b = (b); \ 9 | _a < _b ? _a : _b; }) 10 | 11 | #ifdef max 12 | #undef max 13 | #endif 14 | #define max(a, b) \ 15 | ({ __typeof__ (a) _a = (a); \ 16 | __typeof__ (b) _b = (b); \ 17 | _a > _b ? _a : _b; }) 18 | 19 | // Turn macro into a string 20 | #define STR_DETAIL(x) #x 21 | #define STR(x) STR_DETAIL(x) 22 | 23 | // sprint 24 | // Get Serial.printf like ability on all Arduino platforms 25 | // Takes 1-8 paramters 26 | #define sprint1(s1) \ 27 | { Serial.print(s1); } 28 | 29 | #define sprint2(s1, s2) \ 30 | { \ 31 | sprint1(s1); \ 32 | Serial.print(s2); \ 33 | } 34 | 35 | #define sprint3(s1, s2, s3) \ 36 | { \ 37 | sprint2(s1, s2); \ 38 | Serial.print(s3); \ 39 | } 40 | 41 | #define sprint4(s1, s2, s3, s4) \ 42 | { \ 43 | sprint3(s1, s2, s3); \ 44 | Serial.print(s4); \ 45 | } 46 | 47 | #define sprint5(s1, s2, s3, s4, s5) \ 48 | { \ 49 | sprint4(s1, s2, s3, s4); \ 50 | Serial.print(s5); \ 51 | } 52 | 53 | #define sprint6(s1, s2, s3, s4, s5, s6) \ 54 | { \ 55 | sprint5(s1, s2, s3, s4, s5); \ 56 | Serial.print(s6); \ 57 | } 58 | 59 | #define sprint7(s1, s2, s3, s4, s5, s6, s7) \ 60 | { \ 61 | sprint6(s1, s2, s3, s4, s5, s6); \ 62 | Serial.print(s7); \ 63 | } 64 | 65 | #define sprint8(s1, s2, s3, s4, s5, s6, s7, s8) \ 66 | { \ 67 | sprint7(s1, s2, s3, s4, s5, s6, s7); \ 68 | Serial.print(s8); \ 69 | } 70 | 71 | #define GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, NAME, ...) NAME 72 | #define sprint(...) \ 73 | GET_MACRO(__VA_ARGS__, sprint8, sprint7, sprint6, sprint5, sprint4, sprint3, sprint2, sprint1) \ 74 | (__VA_ARGS__) 75 | 76 | #define warn_sprint(...) \ 77 | do { \ 78 | if (OPC_SERV_WARN) sprint(__VA_ARGS__); \ 79 | } while (0) 80 | 81 | #define info_sprint(...) \ 82 | do { \ 83 | if (OPC_SERV_INFO) sprint(__VA_ARGS__); \ 84 | } while (0) 85 | 86 | #define perf_sprint(...) \ 87 | do { \ 88 | if (OPC_SERV_PERF) sprint(__VA_ARGS__); \ 89 | } while (0) 90 | 91 | #define debug_sprint(...) \ 92 | do { \ 93 | if (OPC_SERV_DEBUG) { \ 94 | sprint((strrchr(__FILE__, '/') + 1), ":" STR(__LINE__) ":", __func__, "()"); \ 95 | sprint(__VA_ARGS__); \ 96 | } \ 97 | } while (0) 98 | -------------------------------------------------------------------------------- /src/Opc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Config.h" 4 | #include "Platforms.h" 5 | 6 | enum OpcCommand { 7 | OPC_SET_PIXEL_COLORS = 0x00, 8 | OPC_SYSTEM_EXCLUSIVE = 0xFF, 9 | }; 10 | 11 | enum OpcSysEx { 12 | OPC_GLOBAL_COLOR_CORRECTION = 0x00010001, 13 | OPC_FIRMWARE_CONFIGURATION = 0x00010002 14 | }; 15 | 16 | static const uint8_t OPC_HEADER_BYTES = 4; 17 | 18 | template 19 | struct OpcMessage { 20 | OpcMessage() : channel(0), command(0), lenHigh(0), lenLow(0) {} 21 | 22 | uint16_t getLength() const { 23 | return lenLow | (unsigned(lenHigh) << 8); 24 | } 25 | 26 | void setLength(unsigned l) { 27 | lenLow = (uint8_t)l; 28 | lenHigh = (uint8_t)(l >> 8); 29 | } 30 | 31 | uint8_t channel; 32 | uint8_t command; 33 | uint8_t lenHigh; 34 | uint8_t lenLow; 35 | T data[_size]; 36 | }; 37 | 38 | struct OpcClient { 39 | enum ClientState { 40 | CLIENT_STATE_DISCONNECTED = 0, 41 | CLIENT_STATE_CONNECTED 42 | }; 43 | 44 | OpcClient() : ipAddress((uint32_t)0), state(CLIENT_STATE_DISCONNECTED), bytesAvailable(0), bufferBytesToDiscard(0), bufferLength(0), bufferSize(0), buffer(0) {} 45 | 46 | WiFiClient tcpClient; 47 | IPAddress ipAddress; 48 | ClientState state; 49 | uint32_t bytesAvailable; 50 | uint32_t bufferBytesToDiscard; 51 | uint32_t bufferLength; 52 | uint32_t bufferSize; 53 | uint8_t* buffer; 54 | }; 55 | 56 | typedef void (*OpcMsgReceivedCallback)(uint8_t channel, uint8_t command, uint16_t length, uint8_t* data); 57 | typedef void (*OpcClientConnectedCallback)(WiFiClient&); 58 | typedef void (*OpcClientDisconnectedCallback)(OpcClient&); 59 | -------------------------------------------------------------------------------- /src/OpcServer.cpp: -------------------------------------------------------------------------------- 1 | #include "OpcServer.h" 2 | #include "Definitions.h" 3 | 4 | OpcServer::OpcServer(WiFiServer& server, 5 | uint8_t opcChannel, 6 | OpcClient opcClients[], 7 | uint8_t clientSize, 8 | uint8_t buffer[], 9 | uint32_t bufferSize, 10 | OpcMsgReceivedCallback OpcMsgReceivedCallback, 11 | OpcClientConnectedCallback opcClientConnectedCallback, 12 | OpcClientDisconnectedCallback opcClientDisconnectedCallback) 13 | : opcClients_(opcClients), 14 | server_(server), 15 | opcMsgReceivedCallback_(OpcMsgReceivedCallback), 16 | opcClientConnectedCallback_(opcClientConnectedCallback), 17 | opcClientDisconnectedCallback_(opcClientDisconnectedCallback), 18 | bufferSize_(bufferSize), 19 | opcChannel_(opcChannel), 20 | clientSize_(clientSize), 21 | clientCount_(0) { 22 | for (size_t i = 0; i < clientSize_; i++) { 23 | opcClients_[i].bufferSize = bufferSize_; 24 | opcClients_[i].buffer = buffer + (bufferSize_ * i); 25 | } 26 | } 27 | 28 | uint8_t OpcServer::getClientCount() const { 29 | return clientCount_; 30 | } 31 | 32 | uint8_t OpcServer::getClientSize() const { 33 | return clientSize_; 34 | } 35 | 36 | uint32_t OpcServer::getBufferSize() const { 37 | return bufferSize_; 38 | } 39 | 40 | uint16_t OpcServer::getBufferSizeInPixels() const { 41 | return ((bufferSize_ - OPC_HEADER_BYTES) / 3); 42 | } 43 | 44 | uint32_t OpcServer::getBytesAvailable() const { 45 | uint32_t b = 0; 46 | for (size_t i = 0; i < clientSize_; i++) { 47 | b += opcClients_[i].bytesAvailable; 48 | } 49 | return b; 50 | } 51 | 52 | void OpcServer::setMsgReceivedCallback(OpcMsgReceivedCallback opcMsgReceivedCallback) { 53 | opcMsgReceivedCallback_ = opcMsgReceivedCallback; 54 | } 55 | 56 | void OpcServer::setClientConnectedCallback(OpcClientConnectedCallback opcClientConnectedCallback) { 57 | opcClientConnectedCallback_ = opcClientConnectedCallback; 58 | } 59 | 60 | void OpcServer::setClientDisconnectedCallback(OpcClientDisconnectedCallback opcClientDisconnectedCallback) { 61 | opcClientDisconnectedCallback_ = opcClientDisconnectedCallback; 62 | } 63 | 64 | bool OpcServer::begin() { 65 | #if SERVER_BEGIN_BOOL 66 | if (!server_.begin()) { 67 | return false; 68 | } 69 | #else 70 | server_.begin(); 71 | #endif 72 | 73 | return true; 74 | } 75 | 76 | void OpcServer::process() { 77 | // process existing clients 78 | clientCount_ = 0; 79 | for (size_t i = 0; i < clientSize_; i++) { 80 | if (processClient(opcClients_[i])) { 81 | clientCount_++; 82 | } else if (opcClients_[i].state == OpcClient::CLIENT_STATE_CONNECTED) { 83 | opcClients_[i].state = OpcClient::CLIENT_STATE_DISCONNECTED; 84 | opcClients_[i].tcpClient.stop(); 85 | opcClients_[i].bytesAvailable = 0; 86 | opcClientDisconnectedCallback_(opcClients_[i]); 87 | } 88 | } 89 | 90 | // Any new clients? 91 | WiFiClient tcpClient = server_.available(); 92 | if (tcpClient) { 93 | opcClientConnectedCallback_(tcpClient); 94 | 95 | // Check if we have room for a new client 96 | if (clientCount_ >= clientSize_) { 97 | info_sprint("Too many clients, Connection Refused\n"); 98 | tcpClient.stop(); 99 | } else { 100 | for (size_t i = 0; i < clientSize_; i++) { 101 | if (!opcClients_[i].tcpClient.connected()) { 102 | if (opcClients_[i].state != OpcClient::CLIENT_STATE_DISCONNECTED) { 103 | opcClientDisconnectedCallback_(opcClients_[i]); 104 | } 105 | opcClients_[i].tcpClient.stop(); 106 | opcClients_[i].bytesAvailable = 0; 107 | opcClients_[i].tcpClient = tcpClient; 108 | #if HAS_REMOTE_IP 109 | opcClients_[i].ipAddress = tcpClient.remoteIP(); 110 | #endif 111 | opcClients_[i].state = OpcClient::CLIENT_STATE_CONNECTED; 112 | break; 113 | } 114 | } 115 | } 116 | } 117 | } 118 | 119 | bool OpcServer::processClient(OpcClient& opcClient) { 120 | if (opcClient.tcpClient.connected()) { 121 | opcClient.state = OpcClient::CLIENT_STATE_CONNECTED; 122 | 123 | if ((opcClient.bytesAvailable = opcClient.tcpClient.available()) > 0) { 124 | opcRead(opcClient); 125 | perf_sprint("*"); 126 | } else { 127 | perf_sprint("."); 128 | } 129 | return true; 130 | } 131 | return false; 132 | } 133 | 134 | void OpcServer::opcRead(OpcClient& opcClient) { 135 | size_t readLen; 136 | 137 | WiFiClient client = opcClient.tcpClient; 138 | uint8_t* buf = opcClient.buffer; 139 | 140 | if (opcClient.bufferBytesToDiscard > 0) { 141 | warn_sprint(F("*** DISCARDING BYTES *** bytesToDiscard: "), opcClient.bufferBytesToDiscard, F(" bytesAvailable: "), opcClient.bytesAvailable); 142 | warn_sprint(F(" buffersize: "), bufferSize_, '\n'); 143 | while (opcClient.bufferBytesToDiscard > 0 && opcClient.bytesAvailable > 0) { 144 | readLen = client.read((uint8_t*)buf, min(opcClient.bytesAvailable, min(opcClient.bufferBytesToDiscard, bufferSize_))); 145 | if (readLen == 0) { break; } 146 | opcClient.bytesAvailable -= readLen; 147 | opcClient.bufferBytesToDiscard -= readLen; 148 | } 149 | if (opcClient.bufferBytesToDiscard > 0 || opcClient.bytesAvailable == 0) { 150 | // waiting for more bytes to discard OR no more bytes to read 151 | debug_sprint(F("Waiting for bytes to discard\n")); 152 | return; 153 | } else { 154 | warn_sprint(F("=== DISCARD DONE ===\n")); 155 | } 156 | } 157 | 158 | if (opcClient.bufferLength < OPC_HEADER_BYTES) { 159 | // Read Header 160 | readLen = client.read((uint8_t*)buf + opcClient.bufferLength, min(opcClient.bytesAvailable, OPC_HEADER_BYTES)); 161 | opcClient.bufferLength += readLen; 162 | 163 | if (opcClient.bufferLength < OPC_HEADER_BYTES) { 164 | // Still waiting for a header 165 | debug_sprint(F("Waiting for Header\n")); 166 | } 167 | } 168 | 169 | // We have a header! Read Msg 170 | uint8_t channel = buf[0]; 171 | uint8_t command = buf[1]; 172 | uint8_t lenHigh = buf[2]; 173 | uint8_t lenLow = buf[3]; 174 | uint16_t dataLength = lenLow | (unsigned(lenHigh) << 8); 175 | 176 | uint32_t msgLength = OPC_HEADER_BYTES + dataLength; 177 | uint32_t adjMsgLength = min(msgLength, bufferSize_); 178 | 179 | readLen = client.read((uint8_t*)buf + opcClient.bufferLength, min(opcClient.bytesAvailable, adjMsgLength - opcClient.bufferLength)); 180 | opcClient.bufferLength += readLen; 181 | 182 | if (opcClient.bufferLength < adjMsgLength) { 183 | // Waiting for more data 184 | debug_sprint("Waiting for more data\n"); 185 | } 186 | 187 | // Full OPC Message Read 188 | opcMsgReceivedCallback_(channel, command, dataLength, buf + OPC_HEADER_BYTES); 189 | 190 | // Set to start buffer over on next call 191 | opcClient.bufferLength = 0; 192 | 193 | // Set to discard remaining bytes on next call 194 | opcClient.bufferBytesToDiscard = msgLength - adjMsgLength; 195 | } 196 | -------------------------------------------------------------------------------- /src/OpcServer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Platforms.h" 4 | 5 | #include "Config.h" 6 | #include "Opc.h" 7 | 8 | class OpcServer { 9 | public: 10 | OpcServer(WiFiServer& server, 11 | uint8_t opcChannel, 12 | OpcClient opcClients[], 13 | uint8_t clientSize, 14 | uint8_t buffer[], 15 | uint32_t bufferSize, 16 | OpcMsgReceivedCallback opcMsgReceivedCallback = [](uint8_t channel, uint8_t command, uint16_t length, uint8_t* data) -> void {}, 17 | OpcClientConnectedCallback opcClientConnectedCallback = [](WiFiClient&) -> void {}, 18 | OpcClientDisconnectedCallback opcClientDisconnectedCallback = [](OpcClient&) -> void {}); 19 | 20 | bool begin(); 21 | void process(); 22 | 23 | uint32_t getBufferSize() const; 24 | uint16_t getBufferSizeInPixels() const; 25 | uint32_t getBytesAvailable() const; 26 | uint8_t getClientCount() const; 27 | uint8_t getClientSize() const; 28 | 29 | void setClientConnectedCallback(OpcClientConnectedCallback opcClientConnectedCallback); 30 | void setClientDisconnectedCallback(OpcClientDisconnectedCallback opcClientDisconnectedCallback); 31 | void setMsgReceivedCallback(OpcMsgReceivedCallback opcMsgReceivedCallback); 32 | 33 | private: 34 | bool processClient(OpcClient& opcClient); 35 | void opcRead(OpcClient& opcClient); 36 | 37 | OpcClient* opcClients_; 38 | 39 | WiFiServer& server_; 40 | 41 | OpcMsgReceivedCallback opcMsgReceivedCallback_; 42 | OpcClientConnectedCallback opcClientConnectedCallback_; 43 | OpcClientDisconnectedCallback opcClientDisconnectedCallback_; 44 | 45 | uint32_t bufferSize_; 46 | 47 | uint8_t opcChannel_; 48 | uint8_t clientSize_; 49 | uint8_t clientCount_; 50 | }; 51 | -------------------------------------------------------------------------------- /src/Platforms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(ESP8266) 4 | //============================================================================== 5 | // ESP8266 6 | //============================================================================== 7 | #include "Arduino.h" 8 | #include 9 | #define HAS_REMOTE_IP 1 10 | #define SERVER_BEGIN_BOOL 0 11 | //------------------------------------------------------------------------------ 12 | 13 | #elif defined(PARTICLE) 14 | //============================================================================== 15 | // Particle 16 | //============================================================================== 17 | #define WiFiClient TCPClient 18 | #define WiFiServer TCPServer 19 | 20 | #include "application.h" 21 | #define HAS_REMOTE_IP 1 22 | #define SERVER_BEGIN_BOOL 1 23 | //------------------------------------------------------------------------------ 24 | 25 | #elif defined(ARDUINO) 26 | //============================================================================== 27 | // Zero 28 | //============================================================================== 29 | #ifndef ZERO 30 | #define ZERO 31 | #endif 32 | 33 | #include "Arduino.h" 34 | #include 35 | #define HAS_REMOTE_IP 0 36 | #define SERVER_BEGIN_BOOL 0 37 | //------------------------------------------------------------------------------ 38 | 39 | #endif 40 | --------------------------------------------------------------------------------