modbus support

This commit is contained in:
Dmitry Borisenko
2020-09-18 02:26:34 +03:00
parent 734f3e1b36
commit 3d1f1ec524
35 changed files with 3310 additions and 1 deletions

View File

@@ -43,3 +43,6 @@ extern void bmp280Temp();
extern void bmp280ReadingTemp();
extern void bmp280Press();
extern void bmp280ReadingPress();
extern void modbus();
extern void modbusReading();

View File

@@ -44,7 +44,7 @@ class SensorBme280Class : public SensorConvertingClass {
void SensorBme280ReadPress(String key) {
float value;
value = bme.readPressure();
value = value / 1.333224;
value = value / 1.333224 / 100;
float valueFl = this->correction(key, value);
eventGen(key, "");
jsonWriteStr(configLiveJson, key, String(valueFl));

View File

@@ -0,0 +1,79 @@
#pragma once
#include <Arduino.h>
#include <ModbusMaster.h>
#include <SoftwareSerial.h>
#include "Class/LineParsing.h"
#include "Global.h"
#include "items/SensorConvertingClass.h"
ModbusMaster modbus1;
SoftwareSerial uart(10, 11); // RX, TX
class SensorModbusClass : public SensorConvertingClass {
public:
SensorModbusClass() : SensorConvertingClass(){};
void SensorModbusInit() {
jsonWriteStr(configOptionJson, _key + "_map", _map);
jsonWriteStr(configOptionJson, _key + "_с", _c);
sensorReadingMap += _key + ","; //" " + _addr + " " + _regaddr
}
void SensorModbusRead(String key, uint8_t slaveAddress, uint16_t regAddress) {
float value;
modbus1.begin(slaveAddress, uart);
uint16_t reqisterValue = modbus1.readHoldingRegisters(regAddress, 1);
if (getResultMsg(&modbus1, reqisterValue)) {
reqisterValue = modbus1.getResponseBuffer(0);
} else {
}
float valueFl = this->correction(key, value);
eventGen(key, "");
jsonWriteStr(configLiveJson, key, String(valueFl));
MqttClient::publishStatus(key, String(valueFl));
Serial.println("I sensor '" + key + "' data: " + String(valueFl));
}
bool getResultMsg(ModbusMaster* modbus1, uint16_t result) {
String tmpstr;
switch (result) {
case modbus1->ku8MBSuccess:
return true;
tmpstr += "Ok";
break;
case modbus1->ku8MBIllegalFunction:
tmpstr += "Illegal Function";
break;
case modbus1->ku8MBIllegalDataAddress:
tmpstr += "Illegal Data Address";
break;
case modbus1->ku8MBIllegalDataValue:
tmpstr += "Illegal Data Value";
break;
case modbus1->ku8MBSlaveDeviceFailure:
tmpstr += "Slave Device Failure";
break;
case modbus1->ku8MBInvalidSlaveID:
tmpstr += "Invalid Slave ID";
break;
case modbus1->ku8MBInvalidFunction:
tmpstr += "Invalid Function";
break;
case modbus1->ku8MBResponseTimedOut:
tmpstr += "Response Timed Out";
break;
case modbus1->ku8MBInvalidCRC:
tmpstr += "Invalid CRC";
break;
default:
tmpstr += "Unknown error: " + String(result);
break;
}
SerialPrint("I", "Modbus", tmpstr);
return false;
}
};
extern SensorModbusClass mySensorModbus;

View File

@@ -0,0 +1,66 @@
<!----------------------------------------------------------------------------
Title - ensure the issue title is clear & concise
- QUESTIONS - describe the specific question
- BUG REPORTS - describe an activity
- FEATURE REQUESTS - describe an activity
-->
<!----------------------------------------------------------------------------
Provide the following information for all issues. Replace [brackets] and placeholder text with your responses.
(QUESTIONS, BUG REPORTS, FEATURE REQUESTS)
-->
### ModbusMaster version
[Version of the project where you are encountering the issue]
### Arduino IDE version
[Version of Arduino IDE in your environment]
### Arduino Hardware
[Hardware information, including board and processor]
### Platform Details
[Operating system distribution and release version]
---
<!----------------------------------------------------------------------------
Provide the following for QUESTIONS & BUG REPORTS. Replace [brackets] and placeholder text with your responses.
-->
### Scenario:
[What you are trying to achieve and you can't?]
### Steps to Reproduce:
[If you are filing an issue what are the things we need to do in order to repro your problem? How are you using this project or any resources it includes?]
### Expected Result:
[What are you expecting to happen as the consequence of above reproduction steps?]
### Actual Result:
[What actually happens after the reproduction steps? Include the error output or a link to a gist if possible.]
---
<!----------------------------------------------------------------------------
Provide the following for FEATURE REQUESTS. Replace [brackets] and placeholder text with your responses.
Refer to [What's in a Story?](https://dannorth.net/whats-in-a-story/)
-->
### Feature Request
#### Narrative:
<!-- Replace role, feature, benefit. -->
```` text
As a [role]
I want [feature]
So that [benefit]
````
#### Acceptance Criteria:
<!--
Present as one or more Scenarios, replacing context, event, outcome.
-->
```` text
Scenario 1: Title
Given [context]
And [some more context]...
When [event]
Then [outcome]
And [another outcome]...
````

View File

@@ -0,0 +1,37 @@
<!----------------------------------------------------------------------------
Please make sure you've read and understood our contributing guidelines;
https://github.com/4-20ma/ModbusMaster/blob/master/CONTRIBUTING.md
Provide the following information for all issues. Replace [brackets] and placeholder text with your responses.
(QUESTIONS, BUG REPORTS, FEATURE REQUESTS).
-->
### Description
[Describe what this change achieves]
### Issues Resolved
[List any existing issues this PR resolves; include Fixes #xxx or Closes #xxx (where xxx is issue number)]
### Check List
General
- [ ] Code follows coding style defined in STYLE.md
- [ ] Doxygen comments are included inline with code
- [ ] No unnecessary whitespace; check with `git diff --check` before committing.
The following have been modified to reflect new features, if warranted
- [ ] README.md
- [ ] keywords.txt (use tabs as whitespace separators)
- [ ] library.properties
- [ ] examples/ - update or create new ones, as warranted
The following have **NOT** been modified
- [ ] doc/ - will be updated upon versioned release
- [ ] .ruby-gemset
- [ ] .ruby-version
- [ ] CHANGELOG.md - will be updated upon versioned release (HISTORY.md is deprecated)
- [ ] Gemfile
- [ ] LICENSE
- [ ] VERSION - will be updated upon versioned release

View File

@@ -0,0 +1,8 @@
add_issues_wo_labels=false
add_pr_wo_labels=false
enhancement-labels=Type: Enhancement
bug-labels=Type: Bug
exclude-labels=Type: Question
header=# ModbusMaster CHANGELOG
include-labels=Type: Bug,Type: Enhancement,Type: Feature Request,Type: Maintenance
future-release=Unreleased

56
lib/ModbusMaster/.gitignore vendored Normal file
View File

@@ -0,0 +1,56 @@
#---------------------------------------------------------------- ModbusMaster
doc/html/
doc/latex/
#-------------- https://github.com/github/gitignore/blob/master/Ruby.gitignore
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
# Used by dotenv library to load environment variables.
# .env
## Specific to RubyMotion:
.dat*
.repl_history
build/
*.bridgesupport
build-iPhoneOS/
build-iPhoneSimulator/
## Specific to RubyMotion (use of CocoaPods):
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# vendor/Pods/
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
## Environment normalization:
/.bundle/
/vendor/bundle
/lib/bundler/man/
# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

View File

@@ -0,0 +1 @@
global

View File

@@ -0,0 +1 @@
2.3.1

View File

@@ -0,0 +1,30 @@
language: python
python:
- 2.7
sudo: false
cache:
directories:
- ~/.platformio
# update Makefile if target boards added
env:
- PLATFORMIO_BOARD=uno
- PLATFORMIO_BOARD=due
- PLATFORMIO_BOARD=huzzah
- PLATFORMIO_BOARD=genuino101
- PLATFORMIO_BOARD=teensy31
install:
- pip install -U platformio
before_script:
- env
- echo $HOME
- echo $TRAVIS_BUILD_DIR
- ls -al $PWD
script:
- make build

View File

@@ -0,0 +1,141 @@
# ModbusMaster CHANGELOG
## [v2.0.0](https://github.com/4-20ma/ModbusMaster/tree/v2.0.0) (2016-09-24)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v1.0.0...v2.0.0)
**Implemented enhancements:**
- BREAK: Update library to match IDE 1.5 spec v2.1 [\#81](https://github.com/4-20ma/ModbusMaster/pull/81) ([4-20ma](https://github.com/4-20ma))
- Use platformio to build multiple boards [\#79](https://github.com/4-20ma/ModbusMaster/pull/79) ([4-20ma](https://github.com/4-20ma))
**Closed issues:**
- Use platformio to build against multiple boards [\#78](https://github.com/4-20ma/ModbusMaster/issues/78)
- Add ruby files to .gitignore [\#75](https://github.com/4-20ma/ModbusMaster/issues/75)
- Reorder Installation section of README [\#73](https://github.com/4-20ma/ModbusMaster/issues/73)
- Update README [\#71](https://github.com/4-20ma/ModbusMaster/issues/71)
- Rename HISTORY to CHANGELOG [\#47](https://github.com/4-20ma/ModbusMaster/issues/47)
- Update library to match 1.5 specification [\#14](https://github.com/4-20ma/ModbusMaster/issues/14)
**Merged pull requests:**
- Use relative path to examples [\#80](https://github.com/4-20ma/ModbusMaster/pull/80) ([4-20ma](https://github.com/4-20ma))
- Add files to .gitignore [\#76](https://github.com/4-20ma/ModbusMaster/pull/76) ([4-20ma](https://github.com/4-20ma))
- Reorder installation section of README [\#74](https://github.com/4-20ma/ModbusMaster/pull/74) ([4-20ma](https://github.com/4-20ma))
- Update README [\#72](https://github.com/4-20ma/ModbusMaster/pull/72) ([4-20ma](https://github.com/4-20ma))
- Automate CHANGELOG generation [\#68](https://github.com/4-20ma/ModbusMaster/pull/68) ([4-20ma](https://github.com/4-20ma))
## [v1.0.0](https://github.com/4-20ma/ModbusMaster/tree/v1.0.0) (2016-09-12)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.11.0...v1.0.0)
**Implemented enhancements:**
- Add LICENSE, convert project to Apache 2.0 [\#67](https://github.com/4-20ma/ModbusMaster/pull/67) ([4-20ma](https://github.com/4-20ma))
- Add example sketch for half-duplex RS485 [\#66](https://github.com/4-20ma/ModbusMaster/pull/66) ([kintel](https://github.com/kintel))
- Add continuous integration testing with Travis CI [\#63](https://github.com/4-20ma/ModbusMaster/pull/63) ([4-20ma](https://github.com/4-20ma))
- Disable \_\_MODBUSMASTER\_DEBUG\_\_ mode by default [\#43](https://github.com/4-20ma/ModbusMaster/pull/43) ([kintel](https://github.com/kintel))
**Closed issues:**
- Fix documentation references in ModbusMaster.h, .cpp [\#69](https://github.com/4-20ma/ModbusMaster/issues/69)
- Clean up template wording [\#64](https://github.com/4-20ma/ModbusMaster/issues/64)
- Add Label section to CONTRIBUTING [\#60](https://github.com/4-20ma/ModbusMaster/issues/60)
- Update README contact information [\#58](https://github.com/4-20ma/ModbusMaster/issues/58)
- Add continuous integration testing with travis [\#55](https://github.com/4-20ma/ModbusMaster/issues/55)
- Add Code of Conduct [\#54](https://github.com/4-20ma/ModbusMaster/issues/54)
- Create PULL\_REQUEST\_TEMPLATE [\#49](https://github.com/4-20ma/ModbusMaster/issues/49)
- Create ISSUE\_TEMPLATE [\#48](https://github.com/4-20ma/ModbusMaster/issues/48)
- Change license to Apache 2.0 [\#45](https://github.com/4-20ma/ModbusMaster/issues/45)
- Set \_\_MODBUSMASTER\_DEBUG\_\_ to 0 by default [\#35](https://github.com/4-20ma/ModbusMaster/issues/35)
- Pass Stream object instead of integer reference [\#17](https://github.com/4-20ma/ModbusMaster/issues/17)
**Merged pull requests:**
- Add documentation cross-references [\#70](https://github.com/4-20ma/ModbusMaster/pull/70) ([4-20ma](https://github.com/4-20ma))
- Clean up ISSUE/PULL\_REQUEST templates [\#65](https://github.com/4-20ma/ModbusMaster/pull/65) ([4-20ma](https://github.com/4-20ma))
- Add initial .travis.yml configuration [\#62](https://github.com/4-20ma/ModbusMaster/pull/62) ([4-20ma](https://github.com/4-20ma))
- Add label guidance to CONTRIBUTING [\#61](https://github.com/4-20ma/ModbusMaster/pull/61) ([4-20ma](https://github.com/4-20ma))
- Update README contact information [\#59](https://github.com/4-20ma/ModbusMaster/pull/59) ([4-20ma](https://github.com/4-20ma))
- Add email address to CODE\_OF\_CONDUCT [\#57](https://github.com/4-20ma/ModbusMaster/pull/57) ([4-20ma](https://github.com/4-20ma))
- Add CODE\_OF\_CONDUCT [\#56](https://github.com/4-20ma/ModbusMaster/pull/56) ([4-20ma](https://github.com/4-20ma))
- Add initial PULL\_REQUEST\_TEMPLATE [\#53](https://github.com/4-20ma/ModbusMaster/pull/53) ([4-20ma](https://github.com/4-20ma))
- Clarify instructions in ISSUE\_TEMPLATE [\#52](https://github.com/4-20ma/ModbusMaster/pull/52) ([4-20ma](https://github.com/4-20ma))
- Add ISSUE\_TEMPLATE title reqs, separator lines [\#51](https://github.com/4-20ma/ModbusMaster/pull/51) ([4-20ma](https://github.com/4-20ma))
- Add initial ISSUE\_TEMPLATE [\#50](https://github.com/4-20ma/ModbusMaster/pull/50) ([4-20ma](https://github.com/4-20ma))
- Add preTransmission\(\), postTransmission\(\) for half-duplex [\#44](https://github.com/4-20ma/ModbusMaster/pull/44) ([kintel](https://github.com/kintel))
- Add STYLE coding style guide [\#29](https://github.com/4-20ma/ModbusMaster/pull/29) ([4-20ma](https://github.com/4-20ma))
## [v0.11.0](https://github.com/4-20ma/ModbusMaster/tree/v0.11.0) (2015-05-22)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.10.3...v0.11.0)
**Implemented enhancements:**
- Update architecture switch [\#28](https://github.com/4-20ma/ModbusMaster/pull/28) ([4-20ma](https://github.com/4-20ma))
**Closed issues:**
- Update architecture switch to match Arduino convention [\#27](https://github.com/4-20ma/ModbusMaster/issues/27)
- Request timeout is impatient [\#3](https://github.com/4-20ma/ModbusMaster/issues/3)
## [v0.10.3](https://github.com/4-20ma/ModbusMaster/tree/v0.10.3) (2015-05-22)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.10.2...v0.10.3)
**Closed issues:**
- Inconsistent Doxygen comments [\#25](https://github.com/4-20ma/ModbusMaster/issues/25)
- Replace C macros with inline functions [\#18](https://github.com/4-20ma/ModbusMaster/issues/18)
**Merged pull requests:**
- Adjust doxygen comments to be consistent [\#26](https://github.com/4-20ma/ModbusMaster/pull/26) ([4-20ma](https://github.com/4-20ma))
- Replace C macros w/inline functions [\#24](https://github.com/4-20ma/ModbusMaster/pull/24) ([4-20ma](https://github.com/4-20ma))
## [v0.10.2](https://github.com/4-20ma/ModbusMaster/tree/v0.10.2) (2015-05-22)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.9.1...v0.10.2)
**Implemented enhancements:**
- Implement CRC16 for SAM3X8E microprocessor [\#11](https://github.com/4-20ma/ModbusMaster/pull/11) ([4-20ma](https://github.com/4-20ma))
- Add rx flush, change response timeout to 2000 ms [\#10](https://github.com/4-20ma/ModbusMaster/pull/10) ([agprimatic](https://github.com/agprimatic))
**Fixed bugs:**
- Fix documentation build error [\#23](https://github.com/4-20ma/ModbusMaster/pull/23) ([4-20ma](https://github.com/4-20ma))
- Work around HardwareSerial for SAM3 micro [\#12](https://github.com/4-20ma/ModbusMaster/pull/12) ([4-20ma](https://github.com/4-20ma))
**Merged pull requests:**
- Update pointers to match C++ convention [\#22](https://github.com/4-20ma/ModbusMaster/pull/22) ([4-20ma](https://github.com/4-20ma))
- Rename markdown file extensions [\#21](https://github.com/4-20ma/ModbusMaster/pull/21) ([4-20ma](https://github.com/4-20ma))
## [v0.9.1](https://github.com/4-20ma/ModbusMaster/tree/v0.9.1) (2013-01-02)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.9...v0.9.1)
## [v0.9](https://github.com/4-20ma/ModbusMaster/tree/v0.9) (2011-12-27)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.8...v0.9)
## [v0.8](https://github.com/4-20ma/ModbusMaster/tree/v0.8) (2011-11-09)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.7...v0.8)
## [v0.7](https://github.com/4-20ma/ModbusMaster/tree/v0.7) (2010-02-10)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.6...v0.7)
## [v0.6](https://github.com/4-20ma/ModbusMaster/tree/v0.6) (2010-02-05)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.5...v0.6)
## [v0.5](https://github.com/4-20ma/ModbusMaster/tree/v0.5) (2010-01-30)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.4...v0.5)
## [v0.4](https://github.com/4-20ma/ModbusMaster/tree/v0.4) (2010-01-30)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.3...v0.4)
## [v0.3](https://github.com/4-20ma/ModbusMaster/tree/v0.3) (2010-01-29)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.2...v0.3)
## [v0.2](https://github.com/4-20ma/ModbusMaster/tree/v0.2) (2010-01-26)
[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.1...v0.2)
## [v0.1](https://github.com/4-20ma/ModbusMaster/tree/v0.1) (2010-01-25)
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

@@ -0,0 +1,78 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project owner at 4-20ma@wvfans.net. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Project Maintainers
- Doc Walker <<4-20ma@wvfans.net>>
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -0,0 +1,58 @@
Contributing
============
- Fork, then clone the repo:
````
git clone git@github.com:your_username/ModbusMaster.git
````
- Create a topic branch from where you want to base your work
- This is usually the master branch
- Only target release branches if you are certain your fix must be on that branch
- To quickly create a topic branch based on master; `git checkout -b fix/master/my_contribution master`. Please avoid working directly on the `master` branch.
- Follow the [style guide](https://github.com/4-20ma/ModbusMaster/blob/master/STYLE.md)
- Test your change
```` bash
$ make
````
Project must build successfully using `make` in order for contribution to be considered.
- Make commits of logical units
- Check for unnecessary whitespace with `git diff --check` before committing
- Each commit should represent one atomic change and should stand on its own
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
- Push to your fork and [submit a pull request](https://github.com/4-20ma/ModbusMaster/compare/)
- [Code of conduct](https://github.com/4-20ma/ModbusMaster/blob/master/CODE_OF_CONDUCT.md)
### Labels
Project maintainers assign labels to Issues and Pull Requests (PRs) to categorize, prioritize, and provide status. The following guidelines and conventions are used in this project:
#### Type
- `Bug` - existing code does not behave as described in the project documentation; _requires_ clear test case and be _reproducible_ by project maintainer
- `Enhancement` - improvement to an existing feature (Issue or Pull Request)
- `Feature Requst` - new functionality; _requires_ a well-written, clear user story (Issue)
- `Maintenance` - minor administrative change that does not provide enhancement or introduce new feature
- `Question` - self-explanatory
#### Priority
- `Low` - default priority; new issues generally start here
- `Medium` - issues are escalated, depending on severity of the issue
- `High` - issues are escalated, depending on severity of the issue
- `Critical` - these issues are to be resolved ahead of any other
#### Status
- `Abandoned` - issue/PR closed due to inactivity
- `Blocked` - issue/PR will not be resolved/merged (some projects label these items as `wontfix`; include explanation in issue/PR)
- `In Progress` - issue has been assigned and is actively being addressed; re-label issue `On Hold` with explanation if there will be a significant delay
- `Maintainer Review Needed` - last step prior to merge; PR passes continuous integration tests and is able to be cleanly merged - awaiting review for style, code cleanliness, etc.
- `On Hold` - implementation delayed; provide explanation in issue/PR
- `Pending Contributor Response` - issue/PR closed after 14 days of inactivity (re-label `Abandoned` at closure)

30
lib/ModbusMaster/Gemfile Normal file
View File

@@ -0,0 +1,30 @@
# encoding: utf-8
# Gemfile style guide derived from:
# http://mcdowall.info/posts/gemfile-best-practices-and-discourse/
# Use `bundle install` after changing this file
# `bundle update [gemname]` to force update of gem
# `bundle show [gemname]` to see where a bundled gem is installed
# `bundle open [gemname]` to edit a bundled gem
# `bundle package` to add gem to vendor/cache
source 'https://rubygems.org'
# place gems sourced from github.com in this section _________________________
# place gems sourced from a project path in this section _____________________
# place general project gems in this section (alphabetical order) ____________
gem 'git', '~> 1.3.0' # git management
gem 'github_changelog_generator', '~> 1.13.1'
gem 'rake', '~> 11.2.2'
gem 'version', '~> 1.0.0' # version management gem
# place gems related to test/specs in this section (alphabetical order) ______
# place gems related to development in this section (alphabetical order) _____

View File

@@ -0,0 +1,47 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.4.0)
colorize (0.8.1)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
git (1.3.0)
github_api (0.14.5)
addressable (~> 2.4.0)
descendants_tracker (~> 0.0.4)
faraday (~> 0.8, < 0.10)
hashie (>= 3.4)
oauth2 (~> 1.0)
github_changelog_generator (1.13.1)
colorize (~> 0.7)
github_api (~> 0.12)
rake (>= 10.0)
hashie (3.4.4)
jwt (1.5.4)
multi_json (1.12.1)
multi_xml (0.5.5)
multipart-post (2.0.0)
oauth2 (1.2.0)
faraday (>= 0.8, < 0.10)
jwt (~> 1.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
rack (2.0.1)
rake (11.2.2)
thread_safe (0.3.5)
version (1.0.0)
PLATFORMS
ruby
DEPENDENCIES
git (~> 1.3.0)
github_changelog_generator (~> 1.13.1)
rake (~> 11.2.2)
version (~> 1.0.0)
BUNDLED WITH
1.12.1

201
lib/ModbusMaster/LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

19
lib/ModbusMaster/Makefile Normal file
View File

@@ -0,0 +1,19 @@
#-------------------------------------------------------------------- settings
FIND := find
DIR := examples
CRITERIA := \( -name "*.ino" -o -name "*.pde" \)
EACH_EXAMPLE := $(FIND) $(DIR) $(CRITERIA) -exec
BUILD := platformio ci
LIB := src
#--------------------------------------------------------------------- targets
# update .travis.yml if target boards added
all: uno due huzzah genuino101 teensy31
uno due huzzah genuino101 teensy31:
PLATFORMIO_BOARD=$@ $(MAKE) build
build:
$(EACH_EXAMPLE) $(BUILD) --board=$(PLATFORMIO_BOARD) --lib=$(LIB) {} \;
.PHONY: all uno due huzzah genuino101 teensy31 build

167
lib/ModbusMaster/README.md Normal file
View File

@@ -0,0 +1,167 @@
# ModbusMaster
[![GitHub release](https://img.shields.io/github/release/4-20ma/ModbusMaster.svg?maxAge=3600)][GitHub release]
[![Travis](https://img.shields.io/travis/4-20ma/ModbusMaster.svg?maxAge=3600)][Travis]
[![license](https://img.shields.io/github/license/4-20ma/ModbusMaster.svg?maxAge=3600)][license]
[![code of conduct](https://img.shields.io/badge/%E2%9D%A4-code%20of%20conduct-blue.svg?maxAge=3600)][code of conduct]
[GitHub release]: https://github.com/4-20ma/ModbusMaster
[Travis]: https://travis-ci.org/4-20ma/ModbusMaster
[license]: LICENSE
[code of conduct]: CODE_OF_CONDUCT.md
## Overview
This is an Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol).
## Features
The following Modbus functions are available:
Discrete Coils/Flags
- 0x01 - Read Coils
- 0x02 - Read Discrete Inputs
- 0x05 - Write Single Coil
- 0x0F - Write Multiple Coils
Registers
- 0x03 - Read Holding Registers
- 0x04 - Read Input Registers
- 0x06 - Write Single Register
- 0x10 - Write Multiple Registers
- 0x16 - Mask Write Register
- 0x17 - Read Write Multiple Registers
Both full-duplex and half-duplex RS232/485 transceivers are supported. Callback functions are provided to toggle Data Enable (DE) and Receiver Enable (/RE) pins.
## Installation
#### Library Manager
Install the library into your Arduino IDE using the Library Manager (available from IDE version 1.6.2). Open the IDE and click Sketch > Include Library > Manage Libraries&hellip;
Scroll or search for `ModbusMaster`, then select the version of the library you want to install. Quit/re-launch the IDE to refresh the list; new versions are automatically added to the list, once released on GitHub.
Refer to Arduino Tutorials > Libraries [Using the Library Manager](https://www.arduino.cc/en/Guide/Libraries#toc3).
#### Zip Library
Refer to Arduino Tutorials > Libraries [Importing a .zip Library](https://www.arduino.cc/en/Guide/Libraries#toc4).
#### Manual
Refer to Arduino Tutorials > Libraries [Manual Installation](https://www.arduino.cc/en/Guide/Libraries#toc5).
## Hardware
This library has been tested with an Arduino [Duemilanove](http://www.arduino.cc/en/Main/ArduinoBoardDuemilanove), PHOENIX CONTACT [nanoLine](https://www.phoenixcontact.com/online/portal/us?1dmy&urile=wcm%3apath%3a/usen/web/main/products/subcategory_pages/standard_logic_modules_p-21-03-03/3329dd38-7c6a-46e1-8260-b9208235d6fe/3329dd38-7c6a-46e1-8260-b9208235d6fe) controller, connected via RS485 using a Maxim [MAX488EPA](http://www.maxim-ic.com/quick_view2.cfm/qv_pk/1111) transceiver.
## Caveats
Conforms to Arduino IDE 1.5 Library Specification v2.1 which requires Arduino IDE >= 1.5.
Arduinos prior to the Mega have one serial port which must be connected to USB (FTDI) for uploading sketches and to the RS232/485 device/network for running sketches. You will need to disconnect pin 0 (RX) while uploading sketches. After a successful upload, you can reconnect pin 0.
## Support
Please [submit an issue](https://github.com/4-20ma/ModbusMaster/issues) for all questions, bug reports, and feature requests. Email requests will be politely redirected to the issue tracker so others may contribute to the discussion and requestors get a more timely response.
## Example
The library contains a few sketches that demonstrate use of the `ModbusMaster` library. You can find these in the [examples](https://github.com/4-20ma/ModbusMaster/tree/master/examples) folder.
``` cpp
/*
Basic.pde - example using ModbusMaster library
Library:: ModbusMaster
Author:: Doc Walker <4-20ma@wvfans.net>
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <ModbusMaster.h>
// instantiate ModbusMaster object
ModbusMaster node;
void setup()
{
// use Serial (port 0); initialize Modbus communication baud rate
Serial.begin(19200);
// communicate with Modbus slave ID 2 over Serial (port 0)
node.begin(2, Serial);
}
void loop()
{
static uint32_t i;
uint8_t j, result;
uint16_t data[6];
i++;
// set word 0 of TX buffer to least-significant word of counter (bits 15..0)
node.setTransmitBuffer(0, lowWord(i));
// set word 1 of TX buffer to most-significant word of counter (bits 31..16)
node.setTransmitBuffer(1, highWord(i));
// slave: write TX buffer to (2) 16-bit registers starting at register 0
result = node.writeMultipleRegisters(0, 2);
// slave: read (6) 16-bit registers starting at register 2 to RX buffer
result = node.readHoldingRegisters(2, 6);
// do something with data if read is successful
if (result == node.ku8MBSuccess)
{
for (j = 0; j < 6; j++)
{
data[j] = node.getResponseBuffer(j);
}
}
}
```
_Project inspired by [Arduino Modbus Master](http://sites.google.com/site/jpmzometa/arduino-mbrt/arduino-modbus-master)._
## License & Authors
- Author:: Doc Walker ([4-20ma@wvfans.net](mailto:4-20ma@wvfans.net))
- Author:: Ag Primatic ([agprimatic@gmail.com](mailto:agprimatic@gmail.com))
- Author:: Marius Kintel ([marius@kintel.net](mailto:marius@kintel.net))
```
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

218
lib/ModbusMaster/Rakefile Normal file
View File

@@ -0,0 +1,218 @@
# encoding: utf-8
#
# Copyright:: 2009-2016 Doc Walker
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'git'
require 'github_changelog_generator/task'
require 'rake'
require 'rubygems'
require 'rake/version_task' # gem install version
require 'version'
# requires additional packages on MacOS (including Homebrew):
# $ /usr/bin/ruby -e "$(curl -fsSL \
# https://raw.githubusercontent.com/Homebrew/install/master/install)"
# $ brew install doxygen # generates documentation from source code
# $ brew cask install mactex # MacTeX
Rake::VersionTask.new do |task|
# prevent auto-commit on version bump
task.with_git = false
end
# adjust as appropriate
CWD = File.expand_path(File.dirname(__FILE__))
DOXYFILE = 'Doxyfile'
GITHUB_USERNAME = '4-20ma'
GITHUB_REPO = 'ModbusMaster'
HEADER_FILE = "#{GITHUB_REPO}.h"
CHANGELOG_FILE = 'CHANGELOG.md'
PROPERTIES_FILE = 'library.properties'
VERSION_FILE = Version.version_file('').basename.to_s
task :default => :info
desc 'Display instructions for public release'
task :info do
puts <<-EOF.gsub(/^\s{2}/, '')
Instructions for public release
- Update version, as appropriate:
$ rake version:bump # or
$ rake version:bump:minor # or
$ rake version:bump:major # or
edit 'VERSION' file directly
- Prepare release date, 'CHANGELOG.md' file, documentation:
$ rake prepare
- Review changes to 'CHANGELOG.md' file
This file is assembled using git commit messages; review for completeness.
- Review html documentation files
These files are assembled using source code Doxygen tags; review for
for completeness.
- Add & commit source files, tag, push to origin/master;
add & commit documentation files, push to origin/gh-pages:
$ rake release
EOF
end # task :info
desc "Prepare #{CHANGELOG_FILE} for release"
task :prepare => 'prepare:default'
namespace :prepare do
task :default => [
:release_date,
:library_properties,
:changelog,
:documentation
]
desc 'Prepare documentation'
task :documentation do
version = Version.current.to_s
# update parameters in Doxyfile
file = File.join(CWD, 'doc', DOXYFILE)
contents = IO.read(file)
contents.sub!(/(^PROJECT_NUMBER\s*=)(.*)$/) do |match|
"#{$1} v#{version}"
end # contents.sub!(...)
IO.write(file, contents)
# chdir to doc/ and call doxygen to update documentation
Dir.chdir(to = File.join(CWD, 'doc'))
system('doxygen', DOXYFILE)
# chdir to doc/latex and call doxygen to update documentation
Dir.chdir(from = File.join(CWD, 'doc', 'latex'))
system('make')
# move/rename file to 'extras/GITHUB_REPO reference-x.y.pdf'
to = File.join(CWD, 'extras')
FileUtils.mv(File.join(from, 'refman.pdf'),
File.join(to, "#{GITHUB_REPO} reference-#{version}.pdf"))
end # task :documentation
desc 'Prepare release history'
GitHubChangelogGenerator::RakeTask.new(:changelog) do |config|
config.add_issues_wo_labels = false
config.add_pr_wo_labels = false
config.enhancement_labels = [
'Type: Enhancement'
]
config.bug_labels = ['Type: Bug']
config.exclude_labels = ['Type: Question']
config.header = '# ModbusMaster CHANGELOG'
config.include_labels = [
'Type: Bug',
'Type: Enhancement',
'Type: Feature Request',
'Type: Maintenance'
]
# config.since_tag = '0.1.0'
config.future_release = "v#{Version.current.to_s}"
config.user = GITHUB_USERNAME
config.project = GITHUB_REPO
end # GitHubChangelogGenerator::RakeTask.new
desc 'Update version in library properties file'
task :library_properties do
version = Version.current.to_s
file = File.join(CWD, PROPERTIES_FILE)
contents = IO.read(file)
contents.sub!(/(version=\s*)(.*)$/) do |match|
"#{$1}#{version}"
end # contents.sub!(...)
IO.write(file, contents)
end # task :library_properties
desc 'Update release date in header file'
task :release_date do
file = File.join(CWD, 'src', HEADER_FILE)
contents = IO.read(file)
contents.sub!(/(\\date\s*)(.*)$/) do |match|
"#{$1}#{Time.now.strftime('%-d %b %Y')}"
end # contents.sub!(...)
IO.write(file, contents)
end # task :release_date
end # namespace :prepare
desc 'Release source & documentation'
task :release => 'release:default'
namespace :release do
task :default => [:source, :documentation]
desc 'Commit documentation changes related to version bump'
task :documentation do
version = Version.current.to_s
cwd = File.expand_path(File.join(File.dirname(__FILE__), 'doc', 'html'))
g = Git.open(cwd)
# `git add .`
g.add
# remove each deleted item
g.status.deleted.each do |item|
g.remove(item[0])
end # g.status.deleted.each
# commit changes if items added, changed, or deleted
if g.status.added.size > 0 || g.status.changed.size > 0 ||
g.status.deleted.size > 0 then
message = "Update documentation for v#{version}"
puts g.commit(message)
else
puts "No changes to commit v#{version}"
end # if g.status.added.size > 0 || g.status.changed.size > 0...
g.push('origin', 'gh-pages')
end # task :documentation
desc 'Commit source changes related to version bump'
task :source do
version = Version.current.to_s
`git add \
doc/#{DOXYFILE} \
"extras/#{GITHUB_REPO} reference-#{version}.pdf" \
src/#{HEADER_FILE} \
#{CHANGELOG_FILE} \
#{PROPERTIES_FILE} \
#{VERSION_FILE} \
`
`git commit -m 'Version bump to v#{version}'`
`git tag -a -f -m 'Version v#{version}' v#{version}`
`git push origin master`
`git push --tags`
end # task :source
end # namespace :release

372
lib/ModbusMaster/STYLE.md Normal file
View File

@@ -0,0 +1,372 @@
ModbusMaster Style Guide
========================
The following references provide sound guidance for writing C/C++ code for the Arduino platform.
- [Arduino API Style Guide (AASG)](http://www.arduino.cc/en/Reference/APIStyleGuide)
- [Bjarne Stroustrup's C++ Style Guide](http://www.stroustrup.com/bs_faq2.html)
- [JSF Air Vehicle C++ Coding Standards (JSFAV)](http://www.stroustrup.com/JSF-AV-rules.pdf)
Opinions about style and generally accepted usage patterns may vary widely. I've carefully chosen a few key items to emphasize and enforce for this library in order to promote readability, usability, and safe coding practices. **Pull requests will follow these guidelines in order to be considered**.
General \[AASG\]
-------
Use the established Arduino core libraries and styles.
- Use `read()` to read inputs, and `write()` to write to outputs, e.g. `digitalRead()`, `analogWrite()`, etc.
- Use the `Stream.h` and `Print.h` libraries when dealing with byte streams. If its not appropriate, at least try to use its API as a model. For more on this, see below.
- For network applications, use the Client and Server libraries as the basis.
- Use `begin()` to initialize a library instance, usually with some settings. Use `end()` to stop it.
- Use camelCase function names, not underscore. For example, `analogRead`, not `analog_read`. Or `myNewFunction`, not `my_new_function`. We've adopted this from Processing.org for readability's sake. Refer to AV Rule 45 and AV Rule 51.
- When using serial communication, allow the user to specify any `Stream` object, rather than hard-coding `Serial`. This will make the library compatible with all serial ports on Mega and the Due, and can also use alternate interfaces like `SoftwareSerial`. The `Stream` object can be passed to the library's constructor or to a `begin()` function (as a reference, not a pointer). See Firmata 2.3 or XBee 0.4 for examples of each approach.
Rules \[JSFAV 4.2\]
-----
#### Should, Will, and Shall Rules
There are three types of rules: **should**, **will**, and **shall** rules. Each rule contains either a **"should"**, **"will"** or a **"shall"** in bold letters indicating its type.
- **Should** rules are advisory rules. They strongly suggest the recommended way of doing things.
- **Will** rules are intended to be mandatory requirements. It is expected that they will be followed, but they do not require verification. They are limited to non-safety-critical requirements that cannot be easily verified (e.g., naming conventions).
- **Shall** rules are mandatory requirements. They must be followed and they require verification (either automatic or manual).
Pre-Processing Directives \[JSFAV 4.6\]
-------------------------
Since the pre-processor knows nothing about C++, it should not be used to do what can otherwise be done in C++.
- AV Rule 26
Only the following pre-processor directives shall be used:
1. `#ifndef`
1. `#define`
1. `#endif`
1. `#include`
**Rationale**: Limit the use of the pre-processor to those cases where it is necessary.
#### \#ifndef and \#endif Pre-Processing Directives
- AV Rule 27
`#ifndef`, `#define` and `#endif` **will** be used to prevent multiple inclusions of the same header file. Other techniques to prevent the multiple inclusions of header files **will not** be used.
**Rationale**: Eliminate multiple inclusions of the same header file in a standard way.
- AV Rule 28
The `#ifndef` and `#endif` pre-processor directives **will** only be used as defined in AV Rule 27 to prevent multiple inclusions of the same header file.
**Rationale**: Conditional code compilation should be kept to a minimum as it can significantly obscure testing and maintenance efforts.
#### \#define Pre-Processing Directive
- AV Rule 29
The `#define` pre-processor directive **shall not** be used to create inline macros. Inline functions **shall** be used instead.
**Rationale**: Inline functions do not require text substitutions and behave well when called with arguments (e.g. type checking is performed).
- AV Rule 30
The `#define` pre-processor directive **shall not** be used to define constant values. Instead, the `const` qualifier shall be applied to variable declarations to specify constant values.
**Rationale**: `const` variables follow scope rules, are subject to type checking and do not require text substitutions (which can be confusing or misleading).
- AV Rule 31
The `#define` pre-processor directive **will** only be used as part of the technique to prevent multiple inclusions of the same header file.
**Rationale**: `#define` can be used to specify conditional compilation (AV Rule 27 and AV Rule 28), inline macros (AV Rule 29) and constants (AV Rule 30). This rule specifies that the only allowable use of `#define` is to prevent multiple includes of the same header file (AV Rule 27).
#### \#include Pre-Processing Directive
- AV Rule 32
The `#include` pre-processor directive **will** only be used to include header (\*.h) files.
**Rationale**: Clarity. The only files included in a .cpp file should be the relevant header (\*.h) files.
Header Files \[JSFAV 4.7\]
------------
- AV Rule 33
The `#include` directive **shall** use the `<filename.h>` notation to include header files.
**Rationale**: The include form `"filename.h"` is typically used to include local header files. However, due to the unfortunate divergence in vendor implementations, only the `<filename.h>` form will be used.
- AV Rule 35
A header file **will** contain a mechanism that prevents multiple inclusions of itself.
**Rationale**: Avoid accidental header file recursion. Note AV Rule 27 specifies the mechanism by which multiple inclusions are to be eliminated whereas this rule (AV Rule 35) specifies that each header file must use that mechanism.
- AV Rule 37
Header (include) files **should** include only those header files that are required for them to successfully compile. Files that are only used by the associated .cpp file should be placed in the .cpp file—not the .h file.
**Rationale**: The `#include` statements in a header file define the dependencies of the file. Fewer dependencies imply looser couplings and hence a smaller ripple-effect when the header file is required to change.
Style \[JSFAV 4.9\]
-----
Imposing constraints on the format of syntactic elements makes source code easier to read due to consistency in form and appearance.
- AV Rule 41 (modified)
Source lines **will** be kept to a length of 78 characters or less.
**Rationale**: Readability and style. Very long source lines can be difficult to read and understand.
- AV Rule 42
Each expression-statement **will** be on a separate line.
**Rationale**: Simplicity, readability, and style.
- AV Rule 43 (modified)
Tabs **will** be avoided.
**Rationale**: Tabs are interpreted differently across various editors and printers.
- AV Rule 44
All indentations **will** be at least two spaces and be consistent within the same source file.
**Rationale**: Readability and style.
#### Naming Identifiers
The choice of identifier names should:
- Suggest the usage of the identifier.
- Consist of a descriptive name that is short yet meaningful.
- Be long enough to avoid name conflicts, but not excessive in length.
- Include abbreviations that are generally accepted.
Note: In general, the above guidelines should be followed. However, conventional usage of simple identifiers (i, x, y, p, etc.) in small scopes can lead to cleaner code and will therefore be permitted.
Additionally, the term word in the following naming convention rules may be used to refer to a word, an acronym, an abbreviation, or a number.
- AV Rule 45
All words in an identifier **will** be separated by the '\_' character.
**Exception**: Function names follow the camelCase convention according to the Arduino API Style Guide. Refer to AV Rule 51.
**Rationale**: Readability and Style.
- AV Rule 47 (modified)
Identifiers **should not** begin with the underscore character '\_'.
**Exception**: Currently, private members of the `ModbusMaster` class begin with '\_'.
**Rationale**: '\_' is often used as the first character in the name of library functions (e.g. `_main`, `_exit`, etc.) In order to avoid name collisions, identifiers should not begin with '\_'.
- AV Rule 49
All acronyms in an identifier **will** be composed of uppercase letters.
Note: An acronym will always be in uppercase, even if the acronym is located in a portion of an identifier that is specified to be lowercase by other rules.
**Rationale**: Readability.
- AV Rule 50
The first word of the name of a class, structure, namespace, enumeration, or type created with typedef **will** begin with an uppercase letter. All others letters will be lowercase.
**Rationale**: Style.
**Exception**: The first letter of a typedef name may be in lowercase in order to conform to a standard library interface or when used as a replacement for fundamental types.
- AV Rule 51 (modified)
All letters contained in *function* names **will** be composed of a mix of lowercase and uppercase letters in camelCase format (e.g. first letter of first word is lowercase and first letter of each subsequent word is uppercase). This is to maintain consistency with the Arduion API Style Guide. Refer to AV Rule 45.
All letters contained in *variable* names **will** be composed entirely of lowercase letters.
**Rationale**: Style.
- AV Rule 52
Identifiers for constant and enumerator values **shall** be lowercase.
**Rationale**: Although it is an accepted convention to use uppercase letters for constants and enumerators, it is possible for third party libraries to replace constant/enumerator names as part of the macro substitution process (macros are also typically represented with uppercase letters).
#### Classes
- AV Rule 57
The public, protected, and private sections of a class **will** be declared in that order (the public section is declared before the protected section which is declared before the private section).
**Rationale**: By placing the public section first, everything that is of interest to a user is gathered in the beginning of the class definition. The protected section may be of interest to designers when considering inheriting from the class. The private section contains details that should be of the least general interest.
#### Functions
- AV Rule 58
When declaring and defining functions with more than two parameters, the leading parenthesis and the first argument **will** be written on the same line as the function name. Each additional argument will be written on a separate line (with the closing parenthesis directly after the last argument).
**Rationale**: Readability and style.
#### Blocks
- AV Rule 59
The statements forming the body of an `if`, `else if`, `else`, `while`, `do...while` or `for` statement **shall** always be enclosed in braces, even if the braces form an empty block.
**Rationale**: Readability. It can be difficult to see ';' when it appears by itself.
- AV Rule 60
Braces ('{}') which enclose a block will be placed in the same column, on separate lines directly before and after the block.
**Example**:
```
if (var_name == true)
{
}
else
{
}
```
- AV Rule 61
Braces ('{}') which enclose a block **will** have nothing else on the line except comments (if necessary).
#### Pointers and References
- AV Rule 62
The dereference operator '\*' and the address-of operator '&' **will** be directly connected with the type-specifier.
**Rationale**: The `int32* p;` form emphasizes type over syntax while the `int32 *p;` form emphasizes syntax over type. Although both forms are equally valid C++, the heavy emphasis on types in C++ suggests that `int32* p;` is the preferable form.
**Examples**:
```
int32* p; // correct
int32 *p; // incorrect
int32* p, q; // probable error
```
#### Miscellaneous
- AV Rule 63
Spaces **will not** be used around '.' or '->', nor between unary operators and operands.
**Rationale**: Readability and style.
Classes \[JSFAV 4.10\]
-------
#### const Member Functions
- AV Rule 69
A member function that does not affect the state of an object (its instance variables) **will** be declared `const`.
Member functions should be `const` by default. Only when there is a clear, explicit reason should the `const` modifier on member functions be omitted.
**Rationale**: Declaring a member function `const` is a means of ensuring that objects will not be modified when they should not. Furthermore, C++ allows member functions to be overloaded on their `const`-ness.
Initialization \[JSFAV 4.16\]
--------------
- AV Rule 142
All variables **shall** be initialized before use.
**Rationale**: Prevent the use of variables before they have been properly initialized.
- AV Rule 143
Variables **will not** be introduced until they can be initialized with meaningful values.
**Rationale**: Prevent clients from accessing variables without meaningful values.
Constants \[JSFAV 4.18\]
---------
- AV Rule 149
Octal constants (other than zero) **shall not** be used.
**Rationale**: Any integer constant beginning with a zero ('0') is defined by the C++ standard to be an octal constant. Due to the confusion this causes, octal constants should be avoided.
Note: Hexadecimal numbers and zero (which is also an octal constant) are allowed.
- AV Rule 150
Hexadecimal constants **will** be represented using all uppercase letters.
Variables \[JSFAV 4.19\]
---------
- AV Rule 152
Multiple variable declarations **shall not** be allowed on the same line.
**Rationale**: Increases readability and prevents confusion (see also AV Rule 62).
**Examples**:
```
int32 p; // correct
int32 q; // correct
int32 p, q; // probable error
int32 first_button_on_the_left_box, i; // incorrect; easy to overlook i
```
Flow Control Structures \[JSFAV 4.24\]
-----------------------
- AV Rule 188
Labels **will** not be used, except in switch statements.
**Rationale**: Labels are typically either used in switch statements or are as the targets for goto statements. See exception given in AV Rule 189.
- AV Rule 189
The `goto` statement **shall not** be used.
**Rationale**: Frequent use of the `goto` statement tends to lead to code that is both difficult to read and maintain.
- AV Rule 190
The `continue` statement **shall not** be used.
- AV Rule 191
The `break` statement **shall not** be used (except to terminate the cases of a switch statement).
**Exception**: The `break` statement may be used to "break" out of a single loop provided the alternative would obscure or otherwise significantly complicate the control logic.
- AV Rule 192
All `if`, `else if` constructs **will** contain either a final `else` clause or a comment indicating why a final `else` clause is not necessary.
**Rationale**: Provide a defensive strategy to ensure that all cases are handled by an `else if` series.
Note: This rule only applies when an `if` statement is followed by one or more `else if`s.
- AV Rule 193
Every non-empty `case` clause in a `switch` statement **shall** be terminated with a `break` statement.
**Rationale**: Eliminates potentially confusing behavior since execution will fall through to the code of the next `case` clause if a `break` statement does not terminate the previous `case` clause.

1
lib/ModbusMaster/VERSION Normal file
View File

@@ -0,0 +1 @@
2.0.1

View File

@@ -0,0 +1,69 @@
/*
Basic.pde - example using ModbusMaster library
Library:: ModbusMaster
Author:: Doc Walker <4-20ma@wvfans.net>
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <ModbusMaster.h>
// instantiate ModbusMaster object
ModbusMaster node;
void setup()
{
// use Serial (port 0); initialize Modbus communication baud rate
Serial.begin(19200);
// communicate with Modbus slave ID 2 over Serial (port 0)
node.begin(2, Serial);
}
void loop()
{
static uint32_t i;
uint8_t j, result;
uint16_t data[6];
i++;
// set word 0 of TX buffer to least-significant word of counter (bits 15..0)
node.setTransmitBuffer(0, lowWord(i));
// set word 1 of TX buffer to most-significant word of counter (bits 31..16)
node.setTransmitBuffer(1, highWord(i));
// slave: write TX buffer to (2) 16-bit registers starting at register 0
result = node.writeMultipleRegisters(0, 2);
// slave: read (6) 16-bit registers starting at register 2 to RX buffer
result = node.readHoldingRegisters(2, 6);
// do something with data if read is successful
if (result == node.ku8MBSuccess)
{
for (j = 0; j < 6; j++)
{
data[j] = node.getResponseBuffer(j);
}
}
}

View File

@@ -0,0 +1,143 @@
/*
PhoenixContact_nanoLC.pde - example using ModbusMaster library
to communicate with PHOENIX CONTACT nanoLine controller.
Library:: ModbusMaster
Author:: Doc Walker <4-20ma@wvfans.net>
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <ModbusMaster.h>
// discrete coils
#define NANO_DO(n) (0x0000 + n) ///< returns nanoLC discrete output address
#define NANO_FLAG(n) (0x1000 + n) ///< returns nanoLC flag address
// discrete inputs
#define NANO_DI(n) (0x0000 + n) ///< returns nanoLC discrete input address
// analog holding registers
#define NANO_REG(n) (0x0000 + 2 * n) ///< returns nanoLC holding register address
#define NANO_AO(n) (0x1000 + 2 * n) ///< returns nanoLC analog output address
#define NANO_TCP(n) (0x2000 + 2 * n) ///< returns nanoLC timer/counter preset address
#define NANO_OTP(n) (0x3000 + 2 * n) ///< returns nanoLC discrete output preset address
#define NANO_HSP(n) (0x4000 + 2 * n) ///< returns nanoLC high-speed counter preset address
#define NANO_TCA(n) (0x5000 + 2 * n) ///< returns nanoLC timer/counter accumulator address
#define NANO_OTA(n) (0x6000 + 2 * n) ///< returns nanoLC discrete output accumulator address
#define NANO_HSA(n) (0x7000 + 2 * n) ///< returns nanoLC high-speed counter accumulator address
// analog input registers
#define NANO_AI(n) (0x0000 + 2 * n) ///< returns nanoLC analog input address
// instantiate ModbusMaster object
ModbusMaster nanoLC;
void setup()
{
// use Serial (port 0); initialize Modbus communication baud rate
Serial.begin(19200);
// communicate with Modbus slave ID 1 over Serial (port 0)
nanoLC.begin(1, Serial);
}
void loop()
{
static uint32_t u32ShiftRegister;
static uint32_t i;
uint8_t u8Status;
u32ShiftRegister = ((u32ShiftRegister < 0x01000000) ? (u32ShiftRegister << 4) : 1);
if (u32ShiftRegister == 0) u32ShiftRegister = 1;
i++;
// set word 0 of TX buffer to least-significant word of u32ShiftRegister (bits 15..0)
nanoLC.setTransmitBuffer(0, lowWord(u32ShiftRegister));
// set word 1 of TX buffer to most-significant word of u32ShiftRegister (bits 31..16)
nanoLC.setTransmitBuffer(1, highWord(u32ShiftRegister));
// set word 2 of TX buffer to least-significant word of i (bits 15..0)
nanoLC.setTransmitBuffer(2, lowWord(i));
// set word 3 of TX buffer to most-significant word of i (bits 31..16)
nanoLC.setTransmitBuffer(3, highWord(i));
// write TX buffer to (4) 16-bit registers starting at NANO_REG(1)
// read (4) 16-bit registers starting at NANO_REG(0) to RX buffer
// data is available via nanoLC.getResponseBuffer(0..3)
nanoLC.readWriteMultipleRegisters(NANO_REG(0), 4, NANO_REG(1), 4);
// write lowWord(u32ShiftRegister) to single 16-bit register starting at NANO_REG(3)
nanoLC.writeSingleRegister(NANO_REG(3), lowWord(u32ShiftRegister));
// write highWord(u32ShiftRegister) to single 16-bit register starting at NANO_REG(3) + 1
nanoLC.writeSingleRegister(NANO_REG(3) + 1, highWord(u32ShiftRegister));
// set word 0 of TX buffer to nanoLC.getResponseBuffer(0) (bits 15..0)
nanoLC.setTransmitBuffer(0, nanoLC.getResponseBuffer(0));
// set word 1 of TX buffer to nanoLC.getResponseBuffer(1) (bits 31..16)
nanoLC.setTransmitBuffer(1, nanoLC.getResponseBuffer(1));
// write TX buffer to (2) 16-bit registers starting at NANO_REG(4)
nanoLC.writeMultipleRegisters(NANO_REG(4), 2);
// read 17 coils starting at NANO_FLAG(0) to RX buffer
// bits 15..0 are available via nanoLC.getResponseBuffer(0)
// bit 16 is available via zero-padded nanoLC.getResponseBuffer(1)
nanoLC.readCoils(NANO_FLAG(0), 17);
// read (66) 16-bit registers starting at NANO_REG(0) to RX buffer
// generates Modbus exception ku8MBIllegalDataAddress (0x02)
u8Status = nanoLC.readHoldingRegisters(NANO_REG(0), 66);
if (u8Status == nanoLC.ku8MBIllegalDataAddress)
{
// read (64) 16-bit registers starting at NANO_REG(0) to RX buffer
// data is available via nanoLC.getResponseBuffer(0..63)
u8Status = nanoLC.readHoldingRegisters(NANO_REG(0), 64);
}
// read (8) 16-bit registers starting at NANO_AO(0) to RX buffer
// data is available via nanoLC.getResponseBuffer(0..7)
nanoLC.readHoldingRegisters(NANO_AO(0), 8);
// read (64) 16-bit registers starting at NANO_TCP(0) to RX buffer
// data is available via nanoLC.getResponseBuffer(0..63)
nanoLC.readHoldingRegisters(NANO_TCP(0), 64);
// read (64) 16-bit registers starting at NANO_OTP(0) to RX buffer
// data is available via nanoLC.getResponseBuffer(0..63)
nanoLC.readHoldingRegisters(NANO_OTP(0), 64);
// read (64) 16-bit registers starting at NANO_TCA(0) to RX buffer
// data is available via nanoLC.getResponseBuffer(0..63)
nanoLC.readHoldingRegisters(NANO_TCA(0), 64);
// read (64) 16-bit registers starting at NANO_OTA(0) to RX buffer
// data is available via nanoLC.getResponseBuffer(0..63)
nanoLC.readHoldingRegisters(NANO_OTA(0), 64);
// read (8) 16-bit registers starting at NANO_AI(0) to RX buffer
// data is available via nanoLC.getResponseBuffer(0..7)
nanoLC.readInputRegisters(NANO_AI(0), 8);
}

View File

@@ -0,0 +1,98 @@
/*
RS485_HalfDuplex.pde - example using ModbusMaster library to communicate
with EPSolar LS2024B controller using a half-duplex RS485 transceiver.
This example is tested against an EPSolar LS2024B solar charge controller.
See here for protocol specs:
http://www.solar-elektro.cz/data/dokumenty/1733_modbus_protocol.pdf
Library:: ModbusMaster
Author:: Marius Kintel <marius at kintel dot net>
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <ModbusMaster.h>
/*!
We're using a MAX485-compatible RS485 Transceiver.
Rx/Tx is hooked up to the hardware serial port at 'Serial'.
The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE 3
#define MAX485_RE_NEG 2
// instantiate ModbusMaster object
ModbusMaster node;
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}
void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}
void setup()
{
pinMode(MAX485_RE_NEG, OUTPUT);
pinMode(MAX485_DE, OUTPUT);
// Init in receive mode
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
// Modbus communication runs at 115200 baud
Serial.begin(115200);
// Modbus slave ID 1
node.begin(1, Serial);
// Callbacks allow us to configure the RS485 transceiver correctly
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
bool state = true;
void loop()
{
uint8_t result;
uint16_t data[6];
// Toggle the coil at address 0x0002 (Manual Load Control)
result = node.writeSingleCoil(0x0002, state);
state = !state;
// Read 16 registers starting at 0x3100)
result = node.readInputRegisters(0x3100, 16);
if (result == node.ku8MBSuccess)
{
Serial.print("Vbatt: ");
Serial.println(node.getResponseBuffer(0x04)/100.0f);
Serial.print("Vload: ");
Serial.println(node.getResponseBuffer(0xC0)/100.0f);
Serial.print("Pload: ");
Serial.println((node.getResponseBuffer(0x0D) +
node.getResponseBuffer(0x0E) << 16)/100.0f);
}
delay(1000);
}

View File

@@ -0,0 +1,6 @@
Documentation is available at:
http://4-20ma.github.com/ModbusMaster
Alternatively, you can download the HTML files at:
[tarball] https://github.com/4-20ma/ModbusMaster/tarball/gh-pages
[zipball] https://github.com/4-20ma/ModbusMaster/zipball/gh-pages

View File

@@ -0,0 +1,50 @@
#######################################
# Syntax Coloring Map For ModbusMaster
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
ModbusMaster KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
lowWord KEYWORD2
highWord KEYWORD2
LONG KEYWORD2
begin KEYWORD2
getResponseBuffer KEYWORD2
clearResponseBuffer KEYWORD2
setTransmitBuffer KEYWORD2
clearTransmitBuffer KEYWORD2
readCoils KEYWORD2
readDiscreteInputs KEYWORD2
readHoldingRegisters KEYWORD2
readInputRegisters KEYWORD2
writeSingleCoil KEYWORD2
writeSingleRegister KEYWORD2
writeMultipleCoils KEYWORD2
writeMultipleRegisters KEYWORD2
maskWriteRegister KEYWORD2
readWriteMultipleRegisters KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
ku8MBIllegalFunction LITERAL1
ku8MBIllegalDataAddress LITERAL1
ku8MBIllegalDataValue LITERAL1
ku8MBSlaveDeviceFailure LITERAL1
ku8MBSuccess LITERAL1
ku8MBInvalidSlaveID LITERAL1
ku8MBInvalidFunction LITERAL1
ku8MBResponseTimedOut LITERAL1
ku8MBInvalidCRC LITERAL1

View File

@@ -0,0 +1,10 @@
name=ModbusMaster
version=2.0.1
author=Doc Walker
maintainer=Doc Walker <4-20ma@wvfans.net>
sentence=Enlighten your Arduino to be a Modbus master.
paragraph=Enables communication with Modbus slaves over RS232/485 (via RTU protocol). Requires an RS232/485 transceiver.
category=Communication
url=https://github.com/4-20ma/ModbusMaster
architectures=*
includes=ModbusMaster.h

View File

@@ -0,0 +1,876 @@
/**
@file
Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol).
*/
/*
ModbusMaster.cpp - Arduino library for communicating with Modbus slaves
over RS232/485 (via RTU protocol).
Library:: ModbusMaster
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* _____PROJECT INCLUDES_____________________________________________________ */
#include "ModbusMaster.h"
/* _____GLOBAL VARIABLES_____________________________________________________ */
/* _____PUBLIC FUNCTIONS_____________________________________________________ */
/**
Constructor.
Creates class object; initialize it using ModbusMaster::begin().
@ingroup setup
*/
ModbusMaster::ModbusMaster(void)
{
_idle = 0;
_preTransmission = 0;
_postTransmission = 0;
}
/**
Initialize class object.
Assigns the Modbus slave ID and serial port.
Call once class has been instantiated, typically within setup().
@param slave Modbus slave ID (1..255)
@param &serial reference to serial port object (Serial, Serial1, ... Serial3)
@ingroup setup
*/
void ModbusMaster::begin(uint8_t slave, Stream &serial)
{
// txBuffer = (uint16_t*) calloc(ku8MaxBufferSize, sizeof(uint16_t));
_u8MBSlave = slave;
_serial = &serial;
_u8TransmitBufferIndex = 0;
u16TransmitBufferLength = 0;
#if __MODBUSMASTER_DEBUG__
pinMode(__MODBUSMASTER_DEBUG_PIN_A__, OUTPUT);
pinMode(__MODBUSMASTER_DEBUG_PIN_B__, OUTPUT);
#endif
}
void ModbusMaster::beginTransmission(uint16_t u16Address)
{
_u16WriteAddress = u16Address;
_u8TransmitBufferIndex = 0;
u16TransmitBufferLength = 0;
}
// eliminate this function in favor of using existing MB request functions
uint8_t ModbusMaster::requestFrom(uint16_t address, uint16_t quantity)
{
uint8_t read;
// clamp to buffer length
if (quantity > ku8MaxBufferSize)
{
quantity = ku8MaxBufferSize;
}
// set rx buffer iterator vars
_u8ResponseBufferIndex = 0;
_u8ResponseBufferLength = read;
return read;
}
void ModbusMaster::sendBit(bool data)
{
uint8_t txBitIndex = u16TransmitBufferLength % 16;
if ((u16TransmitBufferLength >> 4) < ku8MaxBufferSize)
{
if (0 == txBitIndex)
{
_u16TransmitBuffer[_u8TransmitBufferIndex] = 0;
}
bitWrite(_u16TransmitBuffer[_u8TransmitBufferIndex], txBitIndex, data);
u16TransmitBufferLength++;
_u8TransmitBufferIndex = u16TransmitBufferLength >> 4;
}
}
void ModbusMaster::send(uint16_t data)
{
if (_u8TransmitBufferIndex < ku8MaxBufferSize)
{
_u16TransmitBuffer[_u8TransmitBufferIndex++] = data;
u16TransmitBufferLength = _u8TransmitBufferIndex << 4;
}
}
void ModbusMaster::send(uint32_t data)
{
send(lowWord(data));
send(highWord(data));
}
void ModbusMaster::send(uint8_t data)
{
send(word(data));
}
uint8_t ModbusMaster::available(void)
{
return _u8ResponseBufferLength - _u8ResponseBufferIndex;
}
uint16_t ModbusMaster::receive(void)
{
if (_u8ResponseBufferIndex < _u8ResponseBufferLength)
{
return _u16ResponseBuffer[_u8ResponseBufferIndex++];
}
else
{
return 0xFFFF;
}
}
/**
Set idle time callback function (cooperative multitasking).
This function gets called in the idle time between transmission of data
and response from slave. Do not call functions that read from the serial
buffer that is used by ModbusMaster. Use of i2c/TWI, 1-Wire, other
serial ports, etc. is permitted within callback function.
@see ModbusMaster::ModbusMasterTransaction()
*/
void ModbusMaster::idle(void (*idle)())
{
_idle = idle;
}
/**
Set pre-transmission callback function.
This function gets called just before a Modbus message is sent over serial.
Typical usage of this callback is to enable an RS485 transceiver's
Driver Enable pin, and optionally disable its Receiver Enable pin.
@see ModbusMaster::ModbusMasterTransaction()
@see ModbusMaster::postTransmission()
*/
void ModbusMaster::preTransmission(void (*preTransmission)())
{
_preTransmission = preTransmission;
}
/**
Set post-transmission callback function.
This function gets called after a Modbus message has finished sending
(i.e. after all data has been physically transmitted onto the serial
bus).
Typical usage of this callback is to enable an RS485 transceiver's
Receiver Enable pin, and disable its Driver Enable pin.
@see ModbusMaster::ModbusMasterTransaction()
@see ModbusMaster::preTransmission()
*/
void ModbusMaster::postTransmission(void (*postTransmission)())
{
_postTransmission = postTransmission;
}
/**
Retrieve data from response buffer.
@see ModbusMaster::clearResponseBuffer()
@param u8Index index of response buffer array (0x00..0x3F)
@return value in position u8Index of response buffer (0x0000..0xFFFF)
@ingroup buffer
*/
uint16_t ModbusMaster::getResponseBuffer(uint8_t u8Index)
{
if (u8Index < ku8MaxBufferSize)
{
return _u16ResponseBuffer[u8Index];
}
else
{
return 0xFFFF;
}
}
/**
Clear Modbus response buffer.
@see ModbusMaster::getResponseBuffer(uint8_t u8Index)
@ingroup buffer
*/
void ModbusMaster::clearResponseBuffer()
{
uint8_t i;
for (i = 0; i < ku8MaxBufferSize; i++)
{
_u16ResponseBuffer[i] = 0;
}
}
/**
Place data in transmit buffer.
@see ModbusMaster::clearTransmitBuffer()
@param u8Index index of transmit buffer array (0x00..0x3F)
@param u16Value value to place in position u8Index of transmit buffer (0x0000..0xFFFF)
@return 0 on success; exception number on failure
@ingroup buffer
*/
uint8_t ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value)
{
if (u8Index < ku8MaxBufferSize)
{
_u16TransmitBuffer[u8Index] = u16Value;
return ku8MBSuccess;
}
else
{
return ku8MBIllegalDataAddress;
}
}
/**
Clear Modbus transmit buffer.
@see ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value)
@ingroup buffer
*/
void ModbusMaster::clearTransmitBuffer()
{
uint8_t i;
for (i = 0; i < ku8MaxBufferSize; i++)
{
_u16TransmitBuffer[i] = 0;
}
}
/**
Modbus function 0x01 Read Coils.
This function code is used to read from 1 to 2000 contiguous status of
coils in a remote device. The request specifies the starting address,
i.e. the address of the first coil specified, and the number of coils.
Coils are addressed starting at zero.
The coils in the response buffer are packed as one coil per bit of the
data field. Status is indicated as 1=ON and 0=OFF. The LSB of the first
data word contains the output addressed in the query. The other coils
follow toward the high order end of this word and from low order to high
order in subsequent words.
If the returned quantity is not a multiple of sixteen, the remaining
bits in the final data word will be padded with zeros (toward the high
order end of the word).
@param u16ReadAddress address of first coil (0x0000..0xFFFF)
@param u16BitQty quantity of coils to read (1..2000, enforced by remote device)
@return 0 on success; exception number on failure
@ingroup discrete
*/
uint8_t ModbusMaster::readCoils(uint16_t u16ReadAddress, uint16_t u16BitQty)
{
_u16ReadAddress = u16ReadAddress;
_u16ReadQty = u16BitQty;
return ModbusMasterTransaction(ku8MBReadCoils);
}
/**
Modbus function 0x02 Read Discrete Inputs.
This function code is used to read from 1 to 2000 contiguous status of
discrete inputs in a remote device. The request specifies the starting
address, i.e. the address of the first input specified, and the number
of inputs. Discrete inputs are addressed starting at zero.
The discrete inputs in the response buffer are packed as one input per
bit of the data field. Status is indicated as 1=ON; 0=OFF. The LSB of
the first data word contains the input addressed in the query. The other
inputs follow toward the high order end of this word, and from low order
to high order in subsequent words.
If the returned quantity is not a multiple of sixteen, the remaining
bits in the final data word will be padded with zeros (toward the high
order end of the word).
@param u16ReadAddress address of first discrete input (0x0000..0xFFFF)
@param u16BitQty quantity of discrete inputs to read (1..2000, enforced by remote device)
@return 0 on success; exception number on failure
@ingroup discrete
*/
uint8_t ModbusMaster::readDiscreteInputs(uint16_t u16ReadAddress,
uint16_t u16BitQty)
{
_u16ReadAddress = u16ReadAddress;
_u16ReadQty = u16BitQty;
return ModbusMasterTransaction(ku8MBReadDiscreteInputs);
}
/**
Modbus function 0x03 Read Holding Registers.
This function code is used to read the contents of a contiguous block of
holding registers in a remote device. The request specifies the starting
register address and the number of registers. Registers are addressed
starting at zero.
The register data in the response buffer is packed as one word per
register.
@param u16ReadAddress address of the first holding register (0x0000..0xFFFF)
@param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device)
@return 0 on success; exception number on failure
@ingroup register
*/
uint8_t ModbusMaster::readHoldingRegisters(uint16_t u16ReadAddress,
uint16_t u16ReadQty)
{
_u16ReadAddress = u16ReadAddress;
_u16ReadQty = u16ReadQty;
return ModbusMasterTransaction(ku8MBReadHoldingRegisters);
}
/**
Modbus function 0x04 Read Input Registers.
This function code is used to read from 1 to 125 contiguous input
registers in a remote device. The request specifies the starting
register address and the number of registers. Registers are addressed
starting at zero.
The register data in the response buffer is packed as one word per
register.
@param u16ReadAddress address of the first input register (0x0000..0xFFFF)
@param u16ReadQty quantity of input registers to read (1..125, enforced by remote device)
@return 0 on success; exception number on failure
@ingroup register
*/
uint8_t ModbusMaster::readInputRegisters(uint16_t u16ReadAddress,
uint8_t u16ReadQty)
{
_u16ReadAddress = u16ReadAddress;
_u16ReadQty = u16ReadQty;
return ModbusMasterTransaction(ku8MBReadInputRegisters);
}
/**
Modbus function 0x05 Write Single Coil.
This function code is used to write a single output to either ON or OFF
in a remote device. The requested ON/OFF state is specified by a
constant in the state field. A non-zero value requests the output to be
ON and a value of 0 requests it to be OFF. The request specifies the
address of the coil to be forced. Coils are addressed starting at zero.
@param u16WriteAddress address of the coil (0x0000..0xFFFF)
@param u8State 0=OFF, non-zero=ON (0x00..0xFF)
@return 0 on success; exception number on failure
@ingroup discrete
*/
uint8_t ModbusMaster::writeSingleCoil(uint16_t u16WriteAddress, uint8_t u8State)
{
_u16WriteAddress = u16WriteAddress;
_u16WriteQty = (u8State ? 0xFF00 : 0x0000);
return ModbusMasterTransaction(ku8MBWriteSingleCoil);
}
/**
Modbus function 0x06 Write Single Register.
This function code is used to write a single holding register in a
remote device. The request specifies the address of the register to be
written. Registers are addressed starting at zero.
@param u16WriteAddress address of the holding register (0x0000..0xFFFF)
@param u16WriteValue value to be written to holding register (0x0000..0xFFFF)
@return 0 on success; exception number on failure
@ingroup register
*/
uint8_t ModbusMaster::writeSingleRegister(uint16_t u16WriteAddress,
uint16_t u16WriteValue)
{
_u16WriteAddress = u16WriteAddress;
_u16WriteQty = 0;
_u16TransmitBuffer[0] = u16WriteValue;
return ModbusMasterTransaction(ku8MBWriteSingleRegister);
}
/**
Modbus function 0x0F Write Multiple Coils.
This function code is used to force each coil in a sequence of coils to
either ON or OFF in a remote device. The request specifies the coil
references to be forced. Coils are addressed starting at zero.
The requested ON/OFF states are specified by contents of the transmit
buffer. A logical '1' in a bit position of the buffer requests the
corresponding output to be ON. A logical '0' requests it to be OFF.
@param u16WriteAddress address of the first coil (0x0000..0xFFFF)
@param u16BitQty quantity of coils to write (1..2000, enforced by remote device)
@return 0 on success; exception number on failure
@ingroup discrete
*/
uint8_t ModbusMaster::writeMultipleCoils(uint16_t u16WriteAddress,
uint16_t u16BitQty)
{
_u16WriteAddress = u16WriteAddress;
_u16WriteQty = u16BitQty;
return ModbusMasterTransaction(ku8MBWriteMultipleCoils);
}
uint8_t ModbusMaster::writeMultipleCoils()
{
_u16WriteQty = u16TransmitBufferLength;
return ModbusMasterTransaction(ku8MBWriteMultipleCoils);
}
/**
Modbus function 0x10 Write Multiple Registers.
This function code is used to write a block of contiguous registers (1
to 123 registers) in a remote device.
The requested written values are specified in the transmit buffer. Data
is packed as one word per register.
@param u16WriteAddress address of the holding register (0x0000..0xFFFF)
@param u16WriteQty quantity of holding registers to write (1..123, enforced by remote device)
@return 0 on success; exception number on failure
@ingroup register
*/
uint8_t ModbusMaster::writeMultipleRegisters(uint16_t u16WriteAddress,
uint16_t u16WriteQty)
{
_u16WriteAddress = u16WriteAddress;
_u16WriteQty = u16WriteQty;
return ModbusMasterTransaction(ku8MBWriteMultipleRegisters);
}
// new version based on Wire.h
uint8_t ModbusMaster::writeMultipleRegisters()
{
_u16WriteQty = _u8TransmitBufferIndex;
return ModbusMasterTransaction(ku8MBWriteMultipleRegisters);
}
/**
Modbus function 0x16 Mask Write Register.
This function code is used to modify the contents of a specified holding
register using a combination of an AND mask, an OR mask, and the
register's current contents. The function can be used to set or clear
individual bits in the register.
The request specifies the holding register to be written, the data to be
used as the AND mask, and the data to be used as the OR mask. Registers
are addressed starting at zero.
The function's algorithm is:
Result = (Current Contents && And_Mask) || (Or_Mask && (~And_Mask))
@param u16WriteAddress address of the holding register (0x0000..0xFFFF)
@param u16AndMask AND mask (0x0000..0xFFFF)
@param u16OrMask OR mask (0x0000..0xFFFF)
@return 0 on success; exception number on failure
@ingroup register
*/
uint8_t ModbusMaster::maskWriteRegister(uint16_t u16WriteAddress,
uint16_t u16AndMask, uint16_t u16OrMask)
{
_u16WriteAddress = u16WriteAddress;
_u16TransmitBuffer[0] = u16AndMask;
_u16TransmitBuffer[1] = u16OrMask;
return ModbusMasterTransaction(ku8MBMaskWriteRegister);
}
/**
Modbus function 0x17 Read Write Multiple Registers.
This function code performs a combination of one read operation and one
write operation in a single MODBUS transaction. The write operation is
performed before the read. Holding registers are addressed starting at
zero.
The request specifies the starting address and number of holding
registers to be read as well as the starting address, and the number of
holding registers. The data to be written is specified in the transmit
buffer.
@param u16ReadAddress address of the first holding register (0x0000..0xFFFF)
@param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device)
@param u16WriteAddress address of the first holding register (0x0000..0xFFFF)
@param u16WriteQty quantity of holding registers to write (1..121, enforced by remote device)
@return 0 on success; exception number on failure
@ingroup register
*/
uint8_t ModbusMaster::readWriteMultipleRegisters(uint16_t u16ReadAddress,
uint16_t u16ReadQty, uint16_t u16WriteAddress, uint16_t u16WriteQty)
{
_u16ReadAddress = u16ReadAddress;
_u16ReadQty = u16ReadQty;
_u16WriteAddress = u16WriteAddress;
_u16WriteQty = u16WriteQty;
return ModbusMasterTransaction(ku8MBReadWriteMultipleRegisters);
}
uint8_t ModbusMaster::readWriteMultipleRegisters(uint16_t u16ReadAddress,
uint16_t u16ReadQty)
{
_u16ReadAddress = u16ReadAddress;
_u16ReadQty = u16ReadQty;
_u16WriteQty = _u8TransmitBufferIndex;
return ModbusMasterTransaction(ku8MBReadWriteMultipleRegisters);
}
/* _____PRIVATE FUNCTIONS____________________________________________________ */
/**
Modbus transaction engine.
Sequence:
- assemble Modbus Request Application Data Unit (ADU),
based on particular function called
- transmit request over selected serial port
- wait for/retrieve response
- evaluate/disassemble response
- return status (success/exception)
@param u8MBFunction Modbus function (0x01..0xFF)
@return 0 on success; exception number on failure
*/
uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
{
uint8_t u8ModbusADU[256];
uint8_t u8ModbusADUSize = 0;
uint8_t i, u8Qty;
uint16_t u16CRC;
uint32_t u32StartTime;
uint8_t u8BytesLeft = 8;
uint8_t u8MBStatus = ku8MBSuccess;
// assemble Modbus Request Application Data Unit
u8ModbusADU[u8ModbusADUSize++] = _u8MBSlave;
u8ModbusADU[u8ModbusADUSize++] = u8MBFunction;
switch(u8MBFunction)
{
case ku8MBReadCoils:
case ku8MBReadDiscreteInputs:
case ku8MBReadInputRegisters:
case ku8MBReadHoldingRegisters:
case ku8MBReadWriteMultipleRegisters:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadAddress);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadAddress);
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadQty);
break;
}
switch(u8MBFunction)
{
case ku8MBWriteSingleCoil:
case ku8MBMaskWriteRegister:
case ku8MBWriteMultipleCoils:
case ku8MBWriteSingleRegister:
case ku8MBWriteMultipleRegisters:
case ku8MBReadWriteMultipleRegisters:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteAddress);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteAddress);
break;
}
switch(u8MBFunction)
{
case ku8MBWriteSingleCoil:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
break;
case ku8MBWriteSingleRegister:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]);
break;
case ku8MBWriteMultipleCoils:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
u8Qty = (_u16WriteQty % 8) ? ((_u16WriteQty >> 3) + 1) : (_u16WriteQty >> 3);
u8ModbusADU[u8ModbusADUSize++] = u8Qty;
for (i = 0; i < u8Qty; i++)
{
switch(i % 2)
{
case 0: // i is even
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i >> 1]);
break;
case 1: // i is odd
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i >> 1]);
break;
}
}
break;
case ku8MBWriteMultipleRegisters:
case ku8MBReadWriteMultipleRegisters:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty << 1);
for (i = 0; i < lowByte(_u16WriteQty); i++)
{
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i]);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i]);
}
break;
case ku8MBMaskWriteRegister:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]);
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[1]);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[1]);
break;
}
// append CRC
u16CRC = 0xFFFF;
for (i = 0; i < u8ModbusADUSize; i++)
{
u16CRC = crc16_update(u16CRC, u8ModbusADU[i]);
}
u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC);
u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC);
u8ModbusADU[u8ModbusADUSize] = 0;
// flush receive buffer before transmitting request
while (_serial->read() != -1);
// transmit request
if (_preTransmission)
{
_preTransmission();
}
for (i = 0; i < u8ModbusADUSize; i++)
{
_serial->write(u8ModbusADU[i]);
}
u8ModbusADUSize = 0;
_serial->flush(); // flush transmit buffer
if (_postTransmission)
{
_postTransmission();
}
// loop until we run out of time or bytes, or an error occurs
u32StartTime = millis();
while (u8BytesLeft && !u8MBStatus)
{
if (_serial->available())
{
#if __MODBUSMASTER_DEBUG__
digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, true);
#endif
u8ModbusADU[u8ModbusADUSize++] = _serial->read();
u8BytesLeft--;
#if __MODBUSMASTER_DEBUG__
digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, false);
#endif
}
else
{
#if __MODBUSMASTER_DEBUG__
digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, true);
#endif
if (_idle)
{
_idle();
}
#if __MODBUSMASTER_DEBUG__
digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, false);
#endif
}
// evaluate slave ID, function code once enough bytes have been read
if (u8ModbusADUSize == 5)
{
// verify response is for correct Modbus slave
if (u8ModbusADU[0] != _u8MBSlave)
{
u8MBStatus = ku8MBInvalidSlaveID;
break;
}
// verify response is for correct Modbus function code (mask exception bit 7)
if ((u8ModbusADU[1] & 0x7F) != u8MBFunction)
{
u8MBStatus = ku8MBInvalidFunction;
break;
}
// check whether Modbus exception occurred; return Modbus Exception Code
if (bitRead(u8ModbusADU[1], 7))
{
u8MBStatus = u8ModbusADU[2];
break;
}
// evaluate returned Modbus function code
switch(u8ModbusADU[1])
{
case ku8MBReadCoils:
case ku8MBReadDiscreteInputs:
case ku8MBReadInputRegisters:
case ku8MBReadHoldingRegisters:
case ku8MBReadWriteMultipleRegisters:
u8BytesLeft = u8ModbusADU[2];
break;
case ku8MBWriteSingleCoil:
case ku8MBWriteMultipleCoils:
case ku8MBWriteSingleRegister:
case ku8MBWriteMultipleRegisters:
u8BytesLeft = 3;
break;
case ku8MBMaskWriteRegister:
u8BytesLeft = 5;
break;
}
}
if ((millis() - u32StartTime) > ku16MBResponseTimeout)
{
u8MBStatus = ku8MBResponseTimedOut;
}
}
// verify response is large enough to inspect further
if (!u8MBStatus && u8ModbusADUSize >= 5)
{
// calculate CRC
u16CRC = 0xFFFF;
for (i = 0; i < (u8ModbusADUSize - 2); i++)
{
u16CRC = crc16_update(u16CRC, u8ModbusADU[i]);
}
// verify CRC
if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2] ||
highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1]))
{
u8MBStatus = ku8MBInvalidCRC;
}
}
// disassemble ADU into words
if (!u8MBStatus)
{
// evaluate returned Modbus function code
switch(u8ModbusADU[1])
{
case ku8MBReadCoils:
case ku8MBReadDiscreteInputs:
// load bytes into word; response bytes are ordered L, H, L, H, ...
for (i = 0; i < (u8ModbusADU[2] >> 1); i++)
{
if (i < ku8MaxBufferSize)
{
_u16ResponseBuffer[i] = word(u8ModbusADU[2 * i + 4], u8ModbusADU[2 * i + 3]);
}
_u8ResponseBufferLength = i;
}
// in the event of an odd number of bytes, load last byte into zero-padded word
if (u8ModbusADU[2] % 2)
{
if (i < ku8MaxBufferSize)
{
_u16ResponseBuffer[i] = word(0, u8ModbusADU[2 * i + 3]);
}
_u8ResponseBufferLength = i + 1;
}
break;
case ku8MBReadInputRegisters:
case ku8MBReadHoldingRegisters:
case ku8MBReadWriteMultipleRegisters:
// load bytes into word; response bytes are ordered H, L, H, L, ...
for (i = 0; i < (u8ModbusADU[2] >> 1); i++)
{
if (i < ku8MaxBufferSize)
{
_u16ResponseBuffer[i] = word(u8ModbusADU[2 * i + 3], u8ModbusADU[2 * i + 4]);
}
_u8ResponseBufferLength = i;
}
break;
}
}
_u8TransmitBufferIndex = 0;
u16TransmitBufferLength = 0;
_u8ResponseBufferIndex = 0;
return u8MBStatus;
}

View File

@@ -0,0 +1,270 @@
/**
@file
Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol).
@defgroup setup ModbusMaster Object Instantiation/Initialization
@defgroup buffer ModbusMaster Buffer Management
@defgroup discrete Modbus Function Codes for Discrete Coils/Inputs
@defgroup register Modbus Function Codes for Holding/Input Registers
@defgroup constant Modbus Function Codes, Exception Codes
*/
/*
ModbusMaster.h - Arduino library for communicating with Modbus slaves
over RS232/485 (via RTU protocol).
Library:: ModbusMaster
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef ModbusMaster_h
#define ModbusMaster_h
/**
@def __MODBUSMASTER_DEBUG__ (0)
Set to 1 to enable debugging features within class:
- PIN A cycles for each byte read in the Modbus response
- PIN B cycles for each millisecond timeout during the Modbus response
*/
#define __MODBUSMASTER_DEBUG__ (0)
#define __MODBUSMASTER_DEBUG_PIN_A__ 4
#define __MODBUSMASTER_DEBUG_PIN_B__ 5
/* _____STANDARD INCLUDES____________________________________________________ */
// include types & constants of Wiring core API
#include "Arduino.h"
/* _____UTILITY MACROS_______________________________________________________ */
/* _____PROJECT INCLUDES_____________________________________________________ */
// functions to calculate Modbus Application Data Unit CRC
#include "util/crc16.h"
// functions to manipulate words
#include "util/word.h"
/* _____CLASS DEFINITIONS____________________________________________________ */
/**
Arduino class library for communicating with Modbus slaves over
RS232/485 (via RTU protocol).
*/
class ModbusMaster
{
public:
ModbusMaster();
void begin(uint8_t, Stream &serial);
void idle(void (*)());
void preTransmission(void (*)());
void postTransmission(void (*)());
// Modbus exception codes
/**
Modbus protocol illegal function exception.
The function code received in the query is not an allowable action for
the server (or slave). This may be because the function code is only
applicable to newer devices, and was not implemented in the unit
selected. It could also indicate that the server (or slave) is in the
wrong state to process a request of this type, for example because it is
unconfigured and is being asked to return register values.
@ingroup constant
*/
static const uint8_t ku8MBIllegalFunction = 0x01;
/**
Modbus protocol illegal data address exception.
The data address received in the query is not an allowable address for
the server (or slave). More specifically, the combination of reference
number and transfer length is invalid. For a controller with 100
registers, the ADU addresses the first register as 0, and the last one
as 99. If a request is submitted with a starting register address of 96
and a quantity of registers of 4, then this request will successfully
operate (address-wise at least) on registers 96, 97, 98, 99. If a
request is submitted with a starting register address of 96 and a
quantity of registers of 5, then this request will fail with Exception
Code 0x02 "Illegal Data Address" since it attempts to operate on
registers 96, 97, 98, 99 and 100, and there is no register with address
100.
@ingroup constant
*/
static const uint8_t ku8MBIllegalDataAddress = 0x02;
/**
Modbus protocol illegal data value exception.
A value contained in the query data field is not an allowable value for
server (or slave). This indicates a fault in the structure of the
remainder of a complex request, such as that the implied length is
incorrect. It specifically does NOT mean that a data item submitted for
storage in a register has a value outside the expectation of the
application program, since the MODBUS protocol is unaware of the
significance of any particular value of any particular register.
@ingroup constant
*/
static const uint8_t ku8MBIllegalDataValue = 0x03;
/**
Modbus protocol slave device failure exception.
An unrecoverable error occurred while the server (or slave) was
attempting to perform the requested action.
@ingroup constant
*/
static const uint8_t ku8MBSlaveDeviceFailure = 0x04;
// Class-defined success/exception codes
/**
ModbusMaster success.
Modbus transaction was successful; the following checks were valid:
- slave ID
- function code
- response code
- data
- CRC
@ingroup constant
*/
static const uint8_t ku8MBSuccess = 0x00;
/**
ModbusMaster invalid response slave ID exception.
The slave ID in the response does not match that of the request.
@ingroup constant
*/
static const uint8_t ku8MBInvalidSlaveID = 0xE0;
/**
ModbusMaster invalid response function exception.
The function code in the response does not match that of the request.
@ingroup constant
*/
static const uint8_t ku8MBInvalidFunction = 0xE1;
/**
ModbusMaster response timed out exception.
The entire response was not received within the timeout period,
ModbusMaster::ku8MBResponseTimeout.
@ingroup constant
*/
static const uint8_t ku8MBResponseTimedOut = 0xE2;
/**
ModbusMaster invalid response CRC exception.
The CRC in the response does not match the one calculated.
@ingroup constant
*/
static const uint8_t ku8MBInvalidCRC = 0xE3;
uint16_t getResponseBuffer(uint8_t);
void clearResponseBuffer();
uint8_t setTransmitBuffer(uint8_t, uint16_t);
void clearTransmitBuffer();
void beginTransmission(uint16_t);
uint8_t requestFrom(uint16_t, uint16_t);
void sendBit(bool);
void send(uint8_t);
void send(uint16_t);
void send(uint32_t);
uint8_t available(void);
uint16_t receive(void);
uint8_t readCoils(uint16_t, uint16_t);
uint8_t readDiscreteInputs(uint16_t, uint16_t);
uint8_t readHoldingRegisters(uint16_t, uint16_t);
uint8_t readInputRegisters(uint16_t, uint8_t);
uint8_t writeSingleCoil(uint16_t, uint8_t);
uint8_t writeSingleRegister(uint16_t, uint16_t);
uint8_t writeMultipleCoils(uint16_t, uint16_t);
uint8_t writeMultipleCoils();
uint8_t writeMultipleRegisters(uint16_t, uint16_t);
uint8_t writeMultipleRegisters();
uint8_t maskWriteRegister(uint16_t, uint16_t, uint16_t);
uint8_t readWriteMultipleRegisters(uint16_t, uint16_t, uint16_t, uint16_t);
uint8_t readWriteMultipleRegisters(uint16_t, uint16_t);
private:
Stream* _serial; ///< reference to serial port object
uint8_t _u8MBSlave; ///< Modbus slave (1..255) initialized in begin()
static const uint8_t ku8MaxBufferSize = 64; ///< size of response/transmit buffers
uint16_t _u16ReadAddress; ///< slave register from which to read
uint16_t _u16ReadQty; ///< quantity of words to read
uint16_t _u16ResponseBuffer[ku8MaxBufferSize]; ///< buffer to store Modbus slave response; read via GetResponseBuffer()
uint16_t _u16WriteAddress; ///< slave register to which to write
uint16_t _u16WriteQty; ///< quantity of words to write
uint16_t _u16TransmitBuffer[ku8MaxBufferSize]; ///< buffer containing data to transmit to Modbus slave; set via SetTransmitBuffer()
uint16_t* txBuffer; // from Wire.h -- need to clean this up Rx
uint8_t _u8TransmitBufferIndex;
uint16_t u16TransmitBufferLength;
uint16_t* rxBuffer; // from Wire.h -- need to clean this up Rx
uint8_t _u8ResponseBufferIndex;
uint8_t _u8ResponseBufferLength;
// Modbus function codes for bit access
static const uint8_t ku8MBReadCoils = 0x01; ///< Modbus function 0x01 Read Coils
static const uint8_t ku8MBReadDiscreteInputs = 0x02; ///< Modbus function 0x02 Read Discrete Inputs
static const uint8_t ku8MBWriteSingleCoil = 0x05; ///< Modbus function 0x05 Write Single Coil
static const uint8_t ku8MBWriteMultipleCoils = 0x0F; ///< Modbus function 0x0F Write Multiple Coils
// Modbus function codes for 16 bit access
static const uint8_t ku8MBReadHoldingRegisters = 0x03; ///< Modbus function 0x03 Read Holding Registers
static const uint8_t ku8MBReadInputRegisters = 0x04; ///< Modbus function 0x04 Read Input Registers
static const uint8_t ku8MBWriteSingleRegister = 0x06; ///< Modbus function 0x06 Write Single Register
static const uint8_t ku8MBWriteMultipleRegisters = 0x10; ///< Modbus function 0x10 Write Multiple Registers
static const uint8_t ku8MBMaskWriteRegister = 0x16; ///< Modbus function 0x16 Mask Write Register
static const uint8_t ku8MBReadWriteMultipleRegisters = 0x17; ///< Modbus function 0x17 Read Write Multiple Registers
// Modbus timeout [milliseconds]
static const uint16_t ku16MBResponseTimeout = 150; ///< Modbus timeout [milliseconds]
// master function that conducts Modbus transactions
uint8_t ModbusMasterTransaction(uint8_t u8MBFunction);
// idle callback function; gets called during idle time between TX and RX
void (*_idle)();
// preTransmission callback function; gets called before writing a Modbus message
void (*_preTransmission)();
// postTransmission callback function; gets called after a Modbus message has been sent
void (*_postTransmission)();
};
#endif
/**
@example examples/Basic/Basic.pde
@example examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde
@example examples/RS485_HalfDuplex/RS485_HalfDuplex.ino
*/

View File

@@ -0,0 +1,88 @@
/**
@file
CRC Computations
@defgroup util_crc16 "util/crc16.h": CRC Computations
@code#include "util/crc16.h"@endcode
This header file provides functions for calculating
cyclic redundancy checks (CRC) using common polynomials.
Modified by Doc Walker to be processor-independent (removed inline
assembler to allow it to compile on SAM3X8E processors).
@par References:
Jack Crenshaw's "Implementing CRCs" article in the January 1992 issue of @e
Embedded @e Systems @e Programming. This may be difficult to find, but it
explains CRC's in very clear and concise terms. Well worth the effort to
obtain a copy.
*/
/* Copyright (c) 2002, 2003, 2004 Marek Michalkiewicz
Copyright (c) 2005, 2007 Joerg Wunsch
Copyright (c) 2013 Dave Hylands
Copyright (c) 2013 Frederic Nadeau
Copyright (c) 2015 Doc Walker
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holders nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
#ifndef _UTIL_CRC16_H_
#define _UTIL_CRC16_H_
/** @ingroup util_crc16
Processor-independent CRC-16 calculation.
Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)<br>
Initial value: 0xFFFF
This CRC is normally used in disk-drive controllers.
@param uint16_t crc (0x0000..0xFFFF)
@param uint8_t a (0x00..0xFF)
@return calculated CRC (0x0000..0xFFFF)
*/
static uint16_t crc16_update(uint16_t crc, uint8_t a)
{
int i;
crc ^= a;
for (i = 0; i < 8; ++i)
{
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc = (crc >> 1);
}
return crc;
}
#endif /* _UTIL_CRC16_H_ */

View File

@@ -0,0 +1,64 @@
/**
@file
Utility Functions for Manipulating Words
@defgroup util_word "util/word.h": Utility Functions for Manipulating Words
@code#include "util/word.h"@endcode
This header file provides utility functions for manipulating words.
*/
/*
word.h - Utility Functions for Manipulating Words
This file is part of ModbusMaster.
ModbusMaster is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ModbusMaster is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ModbusMaster. If not, see <http://www.gnu.org/licenses/>.
Written by Doc Walker (Rx)
Copyright © 2009-2015 Doc Walker <4-20ma at wvfans dot net>
*/
#ifndef _UTIL_WORD_H_
#define _UTIL_WORD_H_
/** @ingroup util_word
Return low word of a 32-bit integer.
@param uint32_t ww (0x00000000..0xFFFFFFFF)
@return low word of input (0x0000..0xFFFF)
*/
static inline uint16_t lowWord(uint32_t ww)
{
return (uint16_t) ((ww) & 0xFFFF);
}
/** @ingroup util_word
Return high word of a 32-bit integer.
@param uint32_t ww (0x00000000..0xFFFFFFFF)
@return high word of input (0x0000..0xFFFF)
*/
static inline uint16_t highWord(uint32_t ww)
{
return (uint16_t) ((ww) >> 16);
}
#endif /* _UTIL_WORD_H_ */

View File

@@ -43,6 +43,8 @@ void cmd_init() {
sCmd.addCommand("bmp280-temp", bmp280Temp);
sCmd.addCommand("bmp280-press", bmp280Press);
sCmd.addCommand("modbus", modbus);
handle_time_init();
}

View File

@@ -0,0 +1,20 @@
#include "ItemsCmd.h"
#include "items/SensorModbusClass.h"
//#ifdef SensorModbusEnabled
//=========================================Модуль ультрозвукового дальномера==================================================================
//modbus;id;anydata;Сенсоры;Температура;order;addr[1];regaddr[0];c[1]
//=========================================================================================================================================
SensorModbusClass mySensorModbus;
void modbus() {
mySensorModbus.update();
String key = mySensorModbus.gkey();
sCmd.addCommand(key.c_str(), modbusReading);
mySensorModbus.SensorModbusInit();
mySensorModbus.clear();
}
void modbusReading() {
String key = sCmd.order();
mySensorModbus.SensorModbusRead(key, 1, 0);
}
//#endif