@@ -63,6 +63,22 @@ enum class ModbusType
63
63
HoldingRegister, // /< Holding register
64
64
};
65
65
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
+
66
82
struct ModbusClientContext {
67
83
uint16_t writeBuffer[ku8MaxBufferSize]; // /< buffer containing data to transmit to Modbus server; set via SetTransmitBuffer()
68
84
uint16_t writeAddress; // /< server register to which to write
@@ -560,6 +576,21 @@ class ModbusClient
560
576
*/
561
577
uint8_t readWriteMultipleRegisters (uint8_t id, uint16_t u16ReadAddress, uint16_t u16ReadQty, uint16_t u16WriteAddress, uint16_t u16WriteQty, ModbusClientContext& context);
562
578
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
+ }
563
594
564
595
/* *
565
596
* @brief Pack two bytes into a 16-bit word.
@@ -626,22 +657,42 @@ class ModbusClient
626
657
*
627
658
* @param word0 First ordered word from register
628
659
* @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
630
661
* @return float Combined 32-bit floating point number.
631
662
*/
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 )
633
664
{
634
665
float val {};
635
666
uint16_t * pval = (uint16_t *)&val;
636
- if (littleEndian )
667
+ switch (endian )
637
668
{
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 ;
645
696
}
646
697
return val;
647
698
}
@@ -652,20 +703,40 @@ class ModbusClient
652
703
* @param value Combined 32-bit floating point number
653
704
* @param word0 First ordered word to register
654
705
* @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
656
707
*/
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 )
658
709
{
659
710
uint16_t * pval = (uint16_t *)&value;
660
- if (littleEndian)
661
- {
662
- word0 = pval[0 ];
663
- word1 = pval[1 ];
664
- }
665
- else
711
+ switch (endian)
666
712
{
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 ;
669
740
}
670
741
}
671
742
0 commit comments