Skip to content

Commit 195f694

Browse files
Eric Bersetheberseth
Eric Berseth
authored andcommittedSep 30, 2023
Add Modbus RTU polling
1 parent 6c7f037 commit 195f694

9 files changed

+1753
-440
lines changed
 

‎config-schema.json

+447
Large diffs are not rendered by default.

‎lib/edge/src/ThresholdComparator.h

+28-18
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ enum class ThresholdState {
5757
BelowThreshold /**< Input value is below threshold */
5858
};
5959

60+
/**
61+
* @brief Convenient states for publishing
62+
*/
63+
enum class ThresholdAlert {
64+
None,
65+
Above,
66+
Below,
67+
Both,
68+
};
69+
6070
/**
6171
* @brief Inclusive limit options for thresholds.
6272
* The the equal is also considered if a threshold (plus hysteresis) is inclusive
@@ -85,7 +95,7 @@ enum class ThresholdInitial {
8595

8696
/**
8797
* @brief Event handler for threshold crossings.
88-
*
98+
*
8999
* @tparam T Type of the input value.
90100
* @param value The input value that triggered the event.
91101
* @param state The current threshold state.
@@ -95,7 +105,7 @@ using ThresholdEvent = std::function<void(T value, ThresholdState state)>;
95105

96106
/**
97107
* @brief Class for comparing an input value to a threshold.
98-
*
108+
*
99109
* @tparam T Type of the input value.
100110
*/
101111
template<typename T>
@@ -126,7 +136,7 @@ class ThresholdComparator {
126136

127137
/**
128138
* @brief Set the threshold value.
129-
*
139+
*
130140
* @param threshold The new threshold value.
131141
*/
132142
void setThreshold(T threshold) {
@@ -137,7 +147,7 @@ class ThresholdComparator {
137147

138148
/**
139149
* @brief Get the current threshold value.
140-
*
150+
*
141151
* @return The current threshold value.
142152
*/
143153
T getThreshold() const {
@@ -147,7 +157,7 @@ class ThresholdComparator {
147157

148158
/**
149159
* @brief Set the hysteresis value. This value must be positive.
150-
*
160+
*
151161
* @param hysteresis The new hysteresis value.
152162
*/
153163
void setHysteresis(T hysteresis) {
@@ -163,27 +173,27 @@ class ThresholdComparator {
163173

164174
/**
165175
* @brief Get the current hysteresis value.
166-
*
176+
*
167177
* @return The current hysteresis value.
168178
*/
169179
T getHysteresis() const {
170180
std::lock_guard<RecursiveMutex> lock(_mutex);
171181
return _hysteresis;
172182
}
173183

174-
/**
175-
* @brief Set the threshold crossing callback function.
176-
*
177-
* @param callback The callback function for threshold crossings.
178-
*/
179-
void setCallback(ThresholdEvent<T> callback) {
180-
std::lock_guard<RecursiveMutex> lock(_mutex);
181-
_thresholdMetCallback = callback;
182-
}
184+
/**
185+
* @brief Set the threshold crossing callback function.
186+
*
187+
* @param callback The callback function for threshold crossings.
188+
*/
189+
void setCallback(ThresholdEvent<T> callback) {
190+
std::lock_guard<RecursiveMutex> lock(_mutex);
191+
_thresholdMetCallback = callback;
192+
}
183193

184194
/**
185195
* @brief Set the inclusive limit options.
186-
*
196+
*
187197
* @param inclusive The new inclusive limit options.
188198
*/
189199
void setInclusive(ThresholdInclusive inclusive) {
@@ -194,7 +204,7 @@ class ThresholdComparator {
194204

195205
/**
196206
* @brief Get the current inclusive limit options.
197-
*
207+
*
198208
* @return The current inclusive limit options.
199209
*/
200210
ThresholdInclusive getInclusive() const {
@@ -204,7 +214,7 @@ class ThresholdComparator {
204214

205215
/**
206216
* @brief Evaluate the input value against the threshold.
207-
*
217+
*
208218
* @param inputValue The input value to compare against the threshold and hysteresis.
209219
* @return The current threshold state.
210220
*/

‎lib/modbus/src/ModbusClient.h

+91-20
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ enum class ModbusType
6363
HoldingRegister, ///< Holding register
6464
};
6565

66+
/**
67+
* @brief Enums to help with 4-byte endian conversion for floating point numbers.
68+
* The Particle platforms operate with little endian byte order when storing numbers.
69+
* Modbus is supposed to be a big endian word order protocol but each server implementation
70+
* may have a different way to read out 32-bit floating point values which probably depends
71+
* on the embedded controller employed in the remote peice of equipment.
72+
*
73+
*/
74+
enum class ModbusFloatEndianess
75+
{
76+
ABCD, ///< All bytes and words are in big endian order
77+
BADC, ///< Bytes are in little endian, words are in big endian order
78+
CDAB, ///< Bytes are in big endian, words are in little endian order
79+
DCBA, ///< All bytes and words are in little endian order
80+
};
81+
6682
struct ModbusClientContext {
6783
uint16_t writeBuffer[ku8MaxBufferSize]; ///< buffer containing data to transmit to Modbus server; set via SetTransmitBuffer()
6884
uint16_t writeAddress; ///< server register to which to write
@@ -560,6 +576,21 @@ class ModbusClient
560576
*/
561577
uint8_t readWriteMultipleRegisters(uint8_t id, uint16_t u16ReadAddress, uint16_t u16ReadQty, uint16_t u16WriteAddress, uint16_t u16WriteQty, ModbusClientContext& context);
562578

579+
/**
580+
* @brief Swap two bytes in a 16-bit word.
581+
*
582+
* @param hi Most significant byte
583+
* @param lo Least significant byte
584+
* @return uint16_t Combined 16-bit word.
585+
*/
586+
static inline uint16_t swapBytes(uint16_t word)
587+
{
588+
uint8_t* bytes = (uint8_t*)&word;
589+
uint8_t swap = bytes[1];
590+
bytes[1] = bytes[0];
591+
bytes[0] = swap;
592+
return word;
593+
}
563594

564595
/**
565596
* @brief Pack two bytes into a 16-bit word.
@@ -626,22 +657,42 @@ class ModbusClient
626657
*
627658
* @param word0 First ordered word from register
628659
* @param word1 Second ordered word from register
629-
* @param littleEndian Use little endian word order to form number; otherwise, use big endian word order
660+
* @param endian Specify byte and word endian orders
630661
* @return float Combined 32-bit floating point number.
631662
*/
632-
static inline float wordsToFloat(uint16_t word0, uint16_t word1, bool littleEndian = false)
663+
static inline float wordsToFloat(uint16_t word0, uint16_t word1, ModbusFloatEndianess endian = ModbusFloatEndianess::CDAB)
633664
{
634665
float val {};
635666
uint16_t* pval = (uint16_t*)&val;
636-
if (littleEndian)
667+
switch (endian)
637668
{
638-
pval[0] = word0;
639-
pval[1] = word1;
640-
}
641-
else
642-
{
643-
pval[0] = word1;
644-
pval[1] = word0;
669+
case ModbusFloatEndianess::ABCD:
670+
// Word[0] has bytes A*256 + B, word[1] has bytes C*256 + D
671+
// Big endian word order and normal byte order
672+
pval[0] = word1;
673+
pval[1] = word0;
674+
break;
675+
676+
case ModbusFloatEndianess::BADC:
677+
// Word[0] has bytes B*256 + A, word[1] has bytes D*256 + C
678+
// Big endian word order and swapped byte order
679+
pval[0] = swapBytes(word1);
680+
pval[1] = swapBytes(word0);
681+
break;
682+
683+
case ModbusFloatEndianess::CDAB:
684+
// Word[0] has bytes C*256 + D, word[1] has bytes A*256 + B
685+
// Little endian word order and normal byte order
686+
pval[0] = word0;
687+
pval[1] = word1;
688+
break;
689+
690+
case ModbusFloatEndianess::DCBA:
691+
// Word[0] has bytes D*256 + C, word[1] has bytes B*256 + A
692+
// Little endian word order and swapped byte order
693+
pval[0] = swapBytes(word0);
694+
pval[1] = swapBytes(word1);
695+
break;
645696
}
646697
return val;
647698
}
@@ -652,20 +703,40 @@ class ModbusClient
652703
* @param value Combined 32-bit floating point number
653704
* @param word0 First ordered word to register
654705
* @param word1 Second ordered word to register
655-
* @param littleEndian Use little endian word order to form number; otherwise, use big endian word order
706+
* @param endian Specify byte and word endian orders
656707
*/
657-
static inline void floatToWords(float value, uint16_t& word0, uint16_t& word1, bool littleEndian = false)
708+
static inline void floatToWords(float value, uint16_t& word0, uint16_t& word1, ModbusFloatEndianess endian = ModbusFloatEndianess::CDAB)
658709
{
659710
uint16_t* pval = (uint16_t*)&value;
660-
if (littleEndian)
661-
{
662-
word0 = pval[0];
663-
word1 = pval[1];
664-
}
665-
else
711+
switch (endian)
666712
{
667-
word1 = pval[0];
668-
word0 = pval[1];
713+
case ModbusFloatEndianess::ABCD:
714+
// Word[0] has bytes A*256 + B, word[1] has bytes C*256 + D
715+
// Big endian word order and normal byte order
716+
word1 = pval[0];
717+
word0 = pval[1];
718+
break;
719+
720+
case ModbusFloatEndianess::BADC:
721+
// Word[0] has bytes B*256 + A, word[1] has bytes D*256 + C
722+
// Big endian word order and swapped byte order
723+
word1 = swapBytes(pval[0]);
724+
word0 = swapBytes(pval[1]);
725+
break;
726+
727+
case ModbusFloatEndianess::CDAB:
728+
// Word[0] has bytes C*256 + D, word[1] has bytes A*256 + B
729+
// Little endian word order and normal byte order
730+
word0 = pval[0];
731+
word1 = pval[1];
732+
break;
733+
734+
case ModbusFloatEndianess::DCBA:
735+
// Word[0] has bytes D*256 + C, word[1] has bytes B*256 + A
736+
// Little endian word order and swapped byte order
737+
word0 = swapBytes(pval[0]);
738+
word1 = swapBytes(pval[1]);
739+
break;
669740
}
670741
}
671742

‎lib/monitor-one/src/monitor_edge_ioexpansion.h

+2
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@
3030
#define MONITOREDGE_IOEX_RS485_RX_PIN (RX)
3131
#define MONITOREDGE_IOEX_RS485_TX_PIN (TX)
3232
#define MONITOREDGE_IOEX_RS485_DE_PIN (D4)
33+
34+
constexpr char MONITOREDGE_IOEX_SKU[] {"EXP1_IO_BASIC_485CAN"};

‎lib/monitor-one/src/monitor_one_config.h

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#pragma once
1818

1919
#include "tracker_config.h"
20+
#include "IEdgePlatformConfiguration.h"
2021
#include "monitor_one_user_led.h"
2122
#include "Adp8866GnssLed.h"
2223

‎src/user_config.h

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2023 Particle Industries, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "tracker_config.h" // For TSOM related configuration
20+
#include "monitor_one_config.h" // For Monitor One related configuration
21+
22+
#define MONITOR_ONE_SUPPORT_PROTO (1)
23+
#define MONITOR_ONE_SUPPORT_IOEXP (1)
24+
25+
using MonitorOneCardFunction = std::function<int(void)>;
26+
27+
#if defined(MONITOR_ONE_SUPPORT_PROTO) && MONITOR_ONE_SUPPORT_PROTO
28+
constexpr char MONITOREDGE_PROTO_SKU[] {"EXP1_PROTO"};
29+
#endif // MONITOR_ONE_SUPPORT_PROTO
30+
31+
32+
#if defined(MONITOR_ONE_SUPPORT_IOEXP) && MONITOR_ONE_SUPPORT_IOEXP
33+
#include "monitor_edge_ioexpansion.h"
34+
int expanderIoInit();
35+
int expanderIoLoop();
36+
#endif // MONITOR_ONE_SUPPORT_IOEXP

‎src/user_io.cpp

+430
Large diffs are not rendered by default.

‎src/user_modbus.cpp

+682
Large diffs are not rendered by default.

‎src/user_setup.cpp

+36-402
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.