Skip to content

Commit 9d0d8a7

Browse files
author
Karl Herbig
committed
implement quick n dirty ascii layer functionality
1 parent efcd691 commit 9d0d8a7

File tree

8 files changed

+138
-94
lines changed

8 files changed

+138
-94
lines changed

include/modbuspp/asciilayer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ namespace Modbus {
192192
/**
193193
* @brief Performing Modbus CRC16 generation of the buffer @b buf
194194
*/
195-
static uint16_t crc16 (const uint8_t * buf, uint16_t count);
195+
static uint8_t lrc8 (const uint8_t * buffer, uint16_t buffer_length);
196196

197197
protected:
198198
class Private;

include/modbuspp/device.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace Modbus {
2424

2525
class NetLayer;
2626
class RtuLayer;
27+
class AsciiLayer;
2728
class TcpLayer;
2829
class Message;
2930

@@ -477,6 +478,14 @@ namespace Modbus {
477478
*/
478479
RtuLayer & rtu();
479480

481+
/**
482+
* @brief underlying RTU layer (backend)
483+
*
484+
* This function shall return the RTU layer if it is the layer used by
485+
* the device. If it does not, a @b std::domain_error exception is thrown.
486+
*/
487+
AsciiLayer & ascii();
488+
480489
/**
481490
* @brief underlying TCP layer (backend)
482491
*

include/modbuspp/message.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,14 @@ namespace Modbus {
219219
*/
220220
uint16_t crc () const;
221221

222+
/**
223+
* @brief Return the LRC read in the message
224+
*
225+
* throw an std::domain_error exception if net()!=Rtu,
226+
* an std::invalid_argument exception if aduSize()<8.
227+
*/
228+
uint8_t lrc () const;
229+
222230
/**
223231
* @brief Returns TCP/IP transaction identifier
224232
*

src/asciilayer.cpp

Lines changed: 54 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -15,78 +15,16 @@
1515
* along with the libmodbuspp Library; if not, see <http://www.gnu.org/licenses/>.
1616
*/
1717
#include <modbuspp/message.h>
18+
#include <modbus-ascii.h>
1819
#include "asciilayer_p.h"
1920
#include "config.h"
21+
#include "modbuspp/global.h"
2022

2123
#ifndef _WIN32
2224
#include <unistd.h>
2325
#endif
2426

2527
namespace Modbus {
26-
27-
namespace ascii {
28-
29-
// -------------------------------------------------------------------------
30-
const uint8_t CrcHiTable[] = {
31-
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
32-
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
33-
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
34-
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
35-
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
36-
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
37-
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
38-
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
39-
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
40-
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
41-
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
42-
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
43-
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
44-
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
45-
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
46-
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
47-
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
48-
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
49-
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
50-
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
51-
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
52-
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
53-
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
54-
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
55-
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
56-
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
57-
};
58-
59-
// -------------------------------------------------------------------------
60-
const uint8_t CrcLoTable[] = {
61-
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
62-
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
63-
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
64-
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
65-
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
66-
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
67-
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
68-
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
69-
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
70-
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
71-
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
72-
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
73-
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
74-
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
75-
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
76-
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
77-
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
78-
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
79-
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
80-
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
81-
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
82-
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
83-
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
84-
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
85-
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
86-
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
87-
};
88-
}
89-
9028
// ---------------------------------------------------------------------------
9129
//
9230
// AsciiLayer Class
@@ -209,17 +147,51 @@ namespace Modbus {
209147
#endif
210148
}
211149

150+
static char nibble_to_hex_ascii(uint8_t nibble)
151+
{
152+
char c;
153+
154+
if (nibble < 10) {
155+
c = nibble + '0';
156+
} else {
157+
c = nibble - 10 + 'A';
158+
}
159+
return c;
160+
}
161+
212162
// ---------------------------------------------------------------------------
213163
bool AsciiLayer::prepareToSend (Message & msg) {
214-
215-
if (msg.net() == Rtu && msg.size() >= 1) {
164+
165+
if (msg.net() == Ascii && msg.size() >= 1) {
216166
size_t aduSize = msg.aduSize();
217167
uint8_t * adu = msg.adu();
218168

219-
uint16_t crc = crc16 (adu, aduSize);
220-
adu[aduSize++] = crc >> 8;
221-
adu[aduSize++] = crc & 0xFF;
222-
msg.setAduSize (aduSize);
169+
/* Skip colon */
170+
uint8_t lrc = lrc8(adu + 1, aduSize- 1);
171+
adu[aduSize++] = lrc;
172+
173+
uint8_t ascii_adu[MODBUS_ASCII_MAX_ADU_LENGTH];
174+
memset(ascii_adu, 0, MODBUS_ASCII_MAX_ADU_LENGTH);
175+
ssize_t i, j = 0;
176+
177+
for (i = 0; i < aduSize; i++) {
178+
if ((i == 0 && adu[i] == ':') ||
179+
(i == aduSize - 2 && adu[i] == '\r') ||
180+
(i == aduSize - 1 && adu[i] == '\n')) {
181+
ascii_adu[j++] = adu[i];
182+
} else {
183+
ascii_adu[j++] = nibble_to_hex_ascii(adu[i] >> 4);
184+
ascii_adu[j++] = nibble_to_hex_ascii(adu[i] & 0x0f);
185+
}
186+
}
187+
ascii_adu[j++] = '\r';
188+
ascii_adu[j++] = '\n';
189+
ascii_adu[j] = '\0';
190+
191+
msg.setAduSize (j);
192+
193+
std::memcpy(adu, ascii_adu, j);
194+
223195
return true;
224196
}
225197
return false;
@@ -228,7 +200,7 @@ namespace Modbus {
228200
// ---------------------------------------------------------------------------
229201
bool AsciiLayer::checkMessage (const Message & msg) {
230202

231-
return crc16 (msg.adu(), msg.aduSize() - 2) == msg.crc ();
203+
return lrc8 (msg.adu(), msg.aduSize() - 2) == msg.lrc ();
232204
}
233205

234206
// ---------------------------------------------------------------------------
@@ -272,21 +244,15 @@ namespace Modbus {
272244

273245
// ---------------------------------------------------------------------------
274246
// static
275-
uint16_t AsciiLayer::crc16 (const uint8_t * buf, uint16_t count) {
276-
uint8_t crcHi = 0xFF; /* high CRC byte initialized */
277-
uint8_t crcLo = 0xFF; /* low CRC byte initialized */
278-
unsigned int i; /* will index into CRC lookup */
279-
280-
/* pass through message buffer */
281-
while (count--) {
282-
283-
i = crcHi ^ *buf++; /* calculate the CRC */
284-
crcHi = crcLo ^ ascii::CrcHiTable[i];
285-
crcLo = ascii::CrcLoTable[i];
286-
}
287-
288-
return (crcHi << 8 | crcLo);
289-
}
247+
uint8_t AsciiLayer::lrc8(const uint8_t *buffer, uint16_t buffer_length)
248+
{
249+
uint8_t lrc = 0;
250+
while (buffer_length--) {
251+
lrc += *buffer++;
252+
}
253+
/* Return two's complementing of the result */
254+
return -lrc;
255+
}
290256

291257
// ---------------------------------------------------------------------------
292258
//
@@ -296,12 +262,13 @@ namespace Modbus {
296262

297263
// ---------------------------------------------------------------------------
298264
AsciiLayer::Private::Private (const std::string & port, const std::string & settings) :
299-
NetLayer::Private (Rtu, port, settings, MODBUS_ASCII_MAX_ADU_LENGTH) {
265+
NetLayer::Private (Ascii, port, settings, MODBUS_ASCII_MAX_ADU_LENGTH) {
300266

301267
// RTU MUST BE 8-bits
302268
ctx = modbus_new_ascii (port.c_str(), AsciiLayer::baud (settings),
303269
AsciiLayer::parity (settings), 8,
304270
AsciiLayer::stop (settings));
271+
305272
if (! ctx) {
306273

307274
throw std::invalid_argument (

src/device.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <modbuspp/message.h>
2121
#include "device_p.h"
2222
#include "config.h"
23+
#include "modbuspp/global.h"
2324
#include <chrono>
2425
#include <thread>
2526
// for debug purposes
@@ -202,6 +203,17 @@ namespace Modbus {
202203
throw std::domain_error ("Unable to return RTU layer !");
203204
}
204205

206+
// ---------------------------------------------------------------------------
207+
AsciiLayer & Device::ascii() {
208+
209+
if (net() == Ascii) {
210+
PIMP_D (Device);
211+
212+
return * reinterpret_cast<AsciiLayer *> (d->backend);
213+
}
214+
throw std::domain_error ("Unable to return ASCII layer !");
215+
}
216+
205217
// ---------------------------------------------------------------------------
206218
TcpLayer & Device::tcp() {
207219

@@ -544,8 +556,8 @@ namespace Modbus {
544556
int Device::Private::defaultSlave (int addr) const {
545557

546558
if (addr < 0 && backend != 0) {
547-
548-
return backend->net() == Rtu ? Broadcast : TcpSlave;
559+
Net n = backend->net();
560+
return (n == Rtu || n == Ascii) ? Broadcast : TcpSlave;
549561
}
550562
return addr;
551563
}
@@ -624,6 +636,22 @@ namespace Modbus {
624636
dev->rtu().setRtsDelay (r);
625637
}
626638
}
639+
if (config.contains ("ascii") && net == Ascii) {
640+
auto ascii = config["ascii"];
641+
642+
if (ascii.contains ("mode")) {
643+
auto m = ascii["mode"].get<SerialMode>();
644+
dev->ascii().setSerialMode (m);
645+
}
646+
if (ascii.contains ("rts")) {
647+
auto r = ascii["rts"].get<SerialRts>();
648+
dev->ascii().setRts (r);
649+
}
650+
if (ascii.contains ("rts-delay")) {
651+
auto r = ascii["rts-delay"].get<int>();
652+
dev->ascii().setRtsDelay (r);
653+
}
654+
}
627655
}
628656
}
629657
}

src/master.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ namespace Modbus {
201201
break;
202202

203203
case Rtu:
204+
case Ascii:
204205
(void) addSlave (Broadcast);
205206
break;
206207
}

src/message.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,13 @@ namespace Modbus {
261261
}
262262
break;
263263

264+
case Ascii: {
265+
uint8_t lrc = AsciiLayer::lrc8 (adu(), aduSize());
266+
267+
setByte(size(), lrc);
268+
}
269+
break;
270+
264271
default:
265272
throw std::invalid_argument (
266273
"Unable to set PDU for this net !");
@@ -339,6 +346,32 @@ namespace Modbus {
339346
return word (size() - 2);
340347
}
341348

349+
// ---------------------------------------------------------------------------
350+
uint8_t Message::lrc () const {
351+
std::cout << "message::lrc\n";
352+
if (net() != Ascii) {
353+
354+
throw std::domain_error ("Unable to return LRC if backend is not ASCII !");
355+
}
356+
if (aduSize() >= 4) {
357+
358+
throw std::invalid_argument ("Unable to return LRC if ADU size less than 8 !");
359+
}
360+
uint16_t lrc = word (size() - 2);
361+
uint8_t lrc_hi = lrc >> 8 & 0xFF;
362+
uint8_t lrc_lo = lrc & 0xFF;
363+
lrc_hi -= '0';
364+
lrc_lo -= '0';
365+
366+
std::cout << "lrc_hi: " << lrc_hi << "\n";
367+
std::cout << "lrc_lo: " << lrc_lo << "\n";
368+
369+
uint8_t lrc_8_bit = lrc_hi << 4;
370+
lrc_8_bit |= lrc_lo;
371+
372+
return lrc_8_bit;
373+
}
374+
342375
// ---------------------------------------------------------------------------
343376
void Message::setAduSize (size_t size) {
344377
PIMP_D (Message);
@@ -490,13 +523,10 @@ namespace Modbus {
490523
break;
491524

492525
case Rtu:
526+
case Ascii:
493527
b = new RtuLayer ("COM1", "9600E1");
494528
break;
495529

496-
case Ascii:
497-
b = new AsciiLayer("COM1", "9600E1");
498-
break;
499-
500530
default:
501531
throw std::invalid_argument (
502532
"Unable to create Modbus Device for this net !");

src/server.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ namespace Modbus {
296296
break;
297297

298298
case Rtu:
299+
case Ascii:
299300
isOk = Device::Private::open();
300301

301302
default:

0 commit comments

Comments
 (0)