Skip to content

Commit 1c76a5a

Browse files
committed
Use C++ templates to select Modbus implementation
1 parent d0ff2bf commit 1c76a5a

File tree

4 files changed

+61
-118
lines changed

4 files changed

+61
-118
lines changed

NetSwarm.h

+6-84
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,6 @@
1717
#ifndef DONT_USE_NETSWARM_EEPROM
1818
#define USE_NETSWARM_EEPROM 1
1919
#endif
20-
#ifndef DONT_USE_NETSWARM_MASTER
21-
#define USE_NETSWARM_MASTER 1
22-
#endif
23-
24-
// Which modbus variant to use (default ModbusUDP)
25-
#if defined(USE_NETSWARM_MODBUS_IP)
26-
#include <ModbusIP.h>
27-
#define NETSWARM_MODBUS_CLASS ModbusIP
28-
#else
29-
#ifndef USE_NETSWARM_MODBUS_UDP
30-
#define USE_NETSWARM_MODBUS_UDP 1
31-
#endif
32-
#include <ModbusUDP.h>
33-
#define NETSWARM_MODBUS_CLASS ModbusUDP
34-
#endif
3520

3621
// ip range starts at 192.168.1.177
3722
#ifndef NETSWARM_IP_START_1
@@ -47,13 +32,6 @@
4732
#define NETSWARM_IP_START_4 177
4833
#endif
4934

50-
// port for sending packets as modbus master
51-
#ifdef USE_NETSWARM_MASTER
52-
#ifndef NETSWARM_MASTER_PORT
53-
#define NETSWARM_MASTER_PORT 10502
54-
#endif
55-
#endif /* USE_NETSWARM_MASTER */
56-
5735
enum netswarmModbusRegister {
5836
// ip address (default 192.168.1.177) *
5937
HREG_IP_ADDR_1, // first two bytes
@@ -79,9 +57,9 @@ typedef struct TPRegister {
7957
// command callback prototype
8058
typedef void (*command_callback_t)(enum netswarmModbusRegister);
8159

82-
class NetSwarm {
60+
template<class ModbusT>
61+
class NetSwarm : public ModbusT {
8362
public:
84-
8563
NetSwarm(byte dataVersion = 1, unsigned int eepromOffset = 0);
8664
void config();
8765
void task();
@@ -101,78 +79,19 @@ class NetSwarm {
10179
// return current MAC address (based on IP address)
10280
void getMacAddr(byte mac[6]);
10381

104-
inline void addHreg(word offset, word value = 0) {
105-
mb->addHreg(offset, value);
106-
}
10782
inline void addHregPersist(word offset, word value = 0) {
108-
addHreg(offset, value);
83+
ModbusT::addHreg(offset, value);
10984
#ifdef USE_NETSWARM_EEPROM
11085
setPersist(offset);
11186
#endif
11287
}
113-
inline bool Hreg(word offset, word value) {
114-
return mb->Hreg(offset, value);
115-
}
116-
inline word Hreg(word offset) {
117-
return mb->Hreg(offset);
118-
}
119-
#ifdef USE_NETSWARM_MASTER
120-
void sendHreg(IPAddress ip, word offset, word value);
121-
#endif
122-
123-
#ifndef USE_HOLDING_REGISTERS_ONLY
124-
inline void addCoil(word offset, bool value = false) {
125-
mb->addCoil(offset, value);
126-
}
127-
inline void addIsts(word offset, bool value = false) {
128-
mb->addIsts(offset, value);
129-
}
130-
inline void addIreg(word offset, word value = false) {
131-
mb->addIreg(offset, value);
132-
}
133-
134-
inline bool Coil(word offset, bool value) {
135-
return mb->Coil(offset, value);
136-
}
137-
inline bool Ists(word offset, bool value) {
138-
return mb->Ists(offset, value);
139-
}
140-
inline bool Ireg(word offset, word value) {
141-
return mb->Ireg(offset, value);
142-
}
143-
144-
inline bool Coil(word offset) {
145-
return mb->Coil(offset);
146-
}
147-
inline bool Ists(word offset) {
148-
return mb->Ists(offset);
149-
}
150-
inline word Ireg(word offset) {
151-
return mb->Ireg(offset);
152-
}
153-
#endif /* USE_HOLDING_REGISTERS_ONLY */
15488

15589
private:
156-
157-
// Modbus slave
158-
NETSWARM_MODBUS_CLASS *mb;
15990
// whether setup is done
16091
bool setupDone;
16192
// optional callback to run on COIL_APPLY
16293
command_callback_t commandCallback;
16394

164-
#ifdef USE_NETSWARM_MASTER
165-
// Modbus master socket
166-
#ifdef USE_NETSWARM_MODBUS_IP
167-
EthernetClient _masterTcp;
168-
#endif
169-
#ifdef USE_NETSWARM_MODBUS_UDP
170-
EthernetUDP _masterUdp;
171-
#endif
172-
// Modbus master transaction id
173-
word _masterTransactionId;
174-
#endif /* USE_NETSWARM_MASTER */
175-
17695
#ifdef USE_NETSWARM_EEPROM
17796
// offset in the EEPROM where data is stored (in case you have other things there)
17897
unsigned int eepromOffset;
@@ -197,4 +116,7 @@ class NetSwarm {
197116
#endif
198117
};
199118

119+
// need to include implementation when using C++ templates
120+
#include "NetSwarm.tpp"
121+
200122
#endif /* __NETSWARM_H__ */

NetSwarm.cpp NetSwarm.tpp

+51-32
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
* NetSwarm library
33
*
44
*
5+
* Note that this implementation uses templates, which means it can't be
6+
* compiled as a regular C++ file; it is included in the header instead.
7+
* http://stackoverflow.com/q/495021/2866660
8+
*
59
* Created 05 Aug 2016
610
* by wvengen
7-
* Modified 22 Aug 2016
11+
* Modified 12 Sep 2016
812
* by wvengen
913
*
1014
* https://github.com/wvengen/netswarm-arduino/blob/master/NetSwarm.cpp
@@ -23,16 +27,17 @@
2327
* Public methods
2428
*/
2529

26-
NetSwarm::NetSwarm(byte dataVersion, unsigned int eepromOffset) {
30+
template<class ModbusT>
31+
NetSwarm<ModbusT>::NetSwarm(byte dataVersion, unsigned int eepromOffset) {
2732
#ifdef USE_NETSWARM_EEPROM
2833
this->dataVersion = dataVersion;
2934
this->eepromOffset = eepromOffset;
3035
#endif
3136
this->setupDone = false;
32-
this->mb = new NETSWARM_MODBUS_CLASS();
3337
}
3438

35-
void NetSwarm::config() {
39+
template<class ModbusT>
40+
void NetSwarm<ModbusT>::config() {
3641
#ifdef USE_NETSWARM_EEPROM
3742
setupEeprom();
3843
#endif
@@ -41,33 +46,36 @@ void NetSwarm::config() {
4146
setupDone = true;
4247
}
4348

44-
void NetSwarm::setCommandCallback(command_callback_t callback) {
49+
template<class ModbusT>
50+
void NetSwarm<ModbusT>::setCommandCallback(command_callback_t callback) {
4551
commandCallback = callback;
4652
}
4753

48-
void NetSwarm::task() {
49-
mb->task();
50-
if (Coil(COIL_APPLY)) {
54+
template<class ModbusT>
55+
void NetSwarm<ModbusT>::task() {
56+
ModbusT::task();
57+
if (ModbusT::Coil(COIL_APPLY)) {
5158
setupNetwork();
52-
Coil(COIL_APPLY, 0);
59+
ModbusT::Coil(COIL_APPLY, 0);
5360
if (commandCallback) commandCallback(COIL_APPLY);
5461
}
5562
#ifdef USE_NETSWARM_EEPROM
56-
if (Coil(COIL_SAVE)) {
63+
if (ModbusT::Coil(COIL_SAVE)) {
5764
saveEeprom();
58-
Coil(COIL_SAVE, 0);
65+
ModbusT::Coil(COIL_SAVE, 0);
5966
if (commandCallback) commandCallback(COIL_SAVE);
6067
}
61-
if (Coil(COIL_LOAD)) {
68+
if (ModbusT::Coil(COIL_LOAD)) {
6269
loadEeprom();
63-
Coil(COIL_LOAD, 0);
70+
ModbusT::Coil(COIL_LOAD, 0);
6471
if (commandCallback) commandCallback(COIL_LOAD);
6572
}
6673
#endif
6774
}
6875

6976
#ifdef USE_NETSWARM_EEPROM
70-
bool NetSwarm::loadEeprom() {
77+
template<class ModbusT>
78+
bool NetSwarm<ModbusT>::loadEeprom() {
7179
// make sure we have valid data in the EEPROM
7280
setupEeprom();
7381
if (currentDataVersion == 0 || dataVersion != currentDataVersion) {
@@ -81,13 +89,14 @@ bool NetSwarm::loadEeprom() {
8189
do {
8290
w = (EEPROM.read(eepromOffset + 4 + reg->offset * 2 + 0) << 8) +
8391
(EEPROM.read(eepromOffset + 4 + reg->offset * 2 + 1));
84-
mb->Hreg(reg->offset, w);
92+
ModbusT::Hreg(reg->offset, w);
8593
reg = reg->next;
8694
} while(reg);
8795
return true;
8896
}
8997

90-
void NetSwarm::saveEeprom() {
98+
template<class ModbusT>
99+
void NetSwarm<ModbusT>::saveEeprom() {
91100
// write header
92101
EEPROM.write(eepromOffset + 0, (byte)'N');
93102
EEPROM.write(eepromOffset + 1, (byte)'S');
@@ -99,20 +108,22 @@ void NetSwarm::saveEeprom() {
99108
if(reg == 0) return;
100109
word w;
101110
do {
102-
w = mb->Hreg(reg->offset);
111+
w = ModbusT::Hreg(reg->offset);
103112
EEPROM.write(eepromOffset + 4 + reg->offset * 2 + 0, (byte)(w >> 8));
104113
EEPROM.write(eepromOffset + 4 + reg->offset * 2 + 1, (byte)(w & 0xff));
105114
reg = reg->next;
106115
} while(reg);
107116
}
108117
#endif /* USE_NETSWARM_EEPROM */
109118

110-
byte NetSwarm::getId() {
119+
template<class ModbusT>
120+
byte NetSwarm<ModbusT>::getId() {
111121
word v = HregRead(HREG_IP_ADDR_2, HREG_IP_ADDR_DEFAULT_2) & 0xff;
112122
return v - NETSWARM_IP_START_4;
113123
}
114124

115-
void NetSwarm::getIpAddr(byte ip[4]) {
125+
template<class ModbusT>
126+
void NetSwarm<ModbusT>::getIpAddr(byte ip[4]) {
116127
word value;
117128

118129
value = HregRead(HREG_IP_ADDR_1, HREG_IP_ADDR_DEFAULT_1);
@@ -123,18 +134,21 @@ void NetSwarm::getIpAddr(byte ip[4]) {
123134
ip[3] = value & 0xff;
124135
}
125136

126-
void NetSwarm::getIpBcast(byte ip[4]) {
137+
template<class ModbusT>
138+
void NetSwarm<ModbusT>::getIpBcast(byte ip[4]) {
127139
getIpAddr(ip);
128140
ip[3] = 0xff; // @todo configurable netmask
129141
}
130142

131-
void NetSwarm::getMacAddr(byte mac[6]) {
143+
template<class ModbusT>
144+
void NetSwarm<ModbusT>::getMacAddr(byte mac[6]) {
132145
mac[0] = 2; // locally administered mac range, unicast
133146
getIpAddr(&mac[2]);
134147
}
135148

136149
#ifdef USE_NETSWARM_MASTER
137-
void NetSwarm::sendHreg(IPAddress ip, word offset, word value) {
150+
template<class ModbusT>
151+
void NetSwarm<ModbusT>::sendHreg(IPAddress ip, word offset, word value) {
138152
// use separate buffers to avoid trouble when packet in coming in
139153
byte sendbuffer[7 + 5]; // MBAP + frame
140154
byte *MBAP = &sendbuffer[0];
@@ -181,11 +195,12 @@ void NetSwarm::sendHreg(IPAddress ip, word offset, word value) {
181195
*/
182196

183197
// Read Modbus holding register with fallback to EEPROM and given value
184-
word NetSwarm::HregRead(word offset, word fallback) {
198+
template<class ModbusT>
199+
word NetSwarm<ModbusT>::HregRead(word offset, word fallback) {
185200
// if Modbus was fully setup, use memory registers
186201
// enables e.g. applying network settings without saving in EEPROM
187202
if (setupDone) {
188-
return Hreg(offset);
203+
return ModbusT::Hreg(offset);
189204

190205
#ifdef USE_NETSWARM_EEPROM
191206
// if we have valid data in the EEPROM, use that
@@ -201,14 +216,15 @@ word NetSwarm::HregRead(word offset, word fallback) {
201216
}
202217
}
203218

204-
void NetSwarm::setupNetwork() {
219+
template<class ModbusT>
220+
void NetSwarm<ModbusT>::setupNetwork() {
205221
byte ip[4];
206222
byte mac[6];
207223

208224
getIpAddr(ip);
209225
getMacAddr(mac);
210226

211-
mb->config(mac, ip);
227+
ModbusT::config(mac, ip);
212228
#ifdef USE_NETSWARM_MASTER
213229
#ifdef USE_NETSWARM_MODBUS_IP
214230
// nothing to do
@@ -220,20 +236,22 @@ void NetSwarm::setupNetwork() {
220236
}
221237

222238
// Setup default modbus registers
223-
void NetSwarm::setupRegisters() {
239+
template<class ModbusT>
240+
void NetSwarm<ModbusT>::setupRegisters() {
224241
// the ip-addresses are expected to be persistant and at the start
225242
addHregPersist(HREG_IP_ADDR_1, HregRead(HREG_IP_ADDR_1, HREG_IP_ADDR_DEFAULT_1));
226243
addHregPersist(HREG_IP_ADDR_2, HregRead(HREG_IP_ADDR_2, HREG_IP_ADDR_DEFAULT_2));
227244

228-
addCoil(COIL_APPLY);
245+
ModbusT::addCoil(COIL_APPLY);
229246
#ifdef USE_NETSWARM_EEPROM
230-
addCoil(COIL_SAVE);
231-
addCoil(COIL_LOAD);
247+
ModbusT::addCoil(COIL_SAVE);
248+
ModbusT::addCoil(COIL_LOAD);
232249
#endif
233250
}
234251

235252
#ifdef USE_NETSWARM_EEPROM
236-
void NetSwarm::setupEeprom() {
253+
template<class ModbusT>
254+
void NetSwarm<ModbusT>::setupEeprom() {
237255
// first make sure we have the magic 'NSd' at the beginning
238256
if (EEPROM.read(eepromOffset + 0) == (byte)'N' &&
239257
EEPROM.read(eepromOffset + 1) == (byte)'S' &&
@@ -245,7 +263,8 @@ void NetSwarm::setupEeprom() {
245263
}
246264
}
247265

248-
void NetSwarm::setPersist(word offset) {
266+
template<class ModbusT>
267+
void NetSwarm<ModbusT>::setPersist(word offset) {
249268
// (based on Modbus::addReg implementation)
250269
TPRegister *reg;
251270
reg = (TPRegister*) malloc(sizeof(TPRegister));

examples/Dimmer/Dimmer.pde

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*
55
* Created 22 Aug 2016
66
* by wvengen
7+
* Updated 10 Sep 2016
8+
* by wvengen
79
*
810
* https://github.com/wvengen/netswarm-arduino/blob/master/examples/Dimmer/Dimmer.pde
911
*/
@@ -22,7 +24,7 @@ enum modbusRegister {
2224
HREG_DIMMER = NETSWARM_MODBUS_OFFSET // we start with register number 100
2325
};
2426

25-
NetSwarm ns;
27+
NetSwarm<ModbusUDP> ns;
2628

2729
void setup() {
2830
// initialize NetSwarm

examples/IPAddress/IPAddress.pde

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include <ModbusUDP.h>
1515
#include <NetSwarm.h>
1616

17-
NetSwarm ns;
17+
NetSwarm<ModbusUDP> ns;
1818

1919
// declare functions we're using so we can reference them
2020
void showIpAddr();

0 commit comments

Comments
 (0)