├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report2.yaml
│ ├── config.yml
│ ├── feature_request2.yaml
│ ├── help.yaml
│ └── working_device.yaml
└── workflows
│ ├── ISSUE_WORKFLOW.yaml
│ └── update_central_repo.yml
├── .gitignore
├── LICENSE
├── Protocol
├── Deye Modbus储能-组串-微逆宁波德业V118.pdf
├── HS_MS_MSX RS232 Protocol 20140603B (1).pdf
├── PH1800 PV1800 EP1800 PV3500 EP3500 RS485 Modbud RTU communication Protocol 1.4.15 (1).xlsx
├── PI16_VoltronicPowerSUNNYProtocol.md
├── PI18_InfiniSolar-V-protocol-20170926.pdf
├── PI30MAX.Communication.Protocol20210217.pdf
├── PI30REVO.20Protocol.20V03--20201112.pdf
├── PI30_Communication-Protocol-20150924-Customer.pdf
├── PI30_HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf
├── PI30_PIP-GK_MK-Protocol.pdf
├── PI41_LV5048.5KW.protocol-20190222.for.customer.pdf
└── readme.MD
├── README.md
├── images
└── ToDo.md
├── include
└── readme.md
├── lib
└── pubsubclient-2.8.13
│ ├── .gitignore
│ ├── .travis.yml
│ ├── CHANGES.txt
│ ├── LICENSE.txt
│ ├── README.md
│ ├── examples
│ ├── mqtt_auth
│ │ └── mqtt_auth.ino
│ ├── mqtt_basic
│ │ └── mqtt_basic.ino
│ ├── mqtt_esp8266
│ │ └── mqtt_esp8266.ino
│ ├── mqtt_large_message
│ │ └── mqtt_large_message.ino
│ ├── mqtt_publish_in_callback
│ │ └── mqtt_publish_in_callback.ino
│ ├── mqtt_reconnect_nonblocking
│ │ └── mqtt_reconnect_nonblocking.ino
│ └── mqtt_stream
│ │ └── mqtt_stream.ino
│ ├── keywords.txt
│ ├── library.json
│ ├── library.properties
│ ├── src
│ ├── PubSubClient.cpp
│ └── PubSubClient.h
│ └── tests
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── src
│ ├── connect_spec.cpp
│ ├── keepalive_spec.cpp
│ ├── lib
│ │ ├── Arduino.h
│ │ ├── BDDTest.cpp
│ │ ├── BDDTest.h
│ │ ├── Buffer.cpp
│ │ ├── Buffer.h
│ │ ├── Client.h
│ │ ├── IPAddress.cpp
│ │ ├── IPAddress.h
│ │ ├── Print.h
│ │ ├── ShimClient.cpp
│ │ ├── ShimClient.h
│ │ ├── Stream.cpp
│ │ ├── Stream.h
│ │ └── trace.h
│ ├── publish_spec.cpp
│ ├── receive_spec.cpp
│ └── subscribe_spec.cpp
│ ├── testcases
│ ├── __init__.py
│ ├── mqtt_basic.py
│ ├── mqtt_publish_in_callback.py
│ └── settings.py
│ └── testsuite.py
├── platformio.ini
├── src
├── PI_Serial
│ ├── PI_Serial.cpp
│ ├── PI_Serial.h
│ ├── Q1.h
│ ├── Q1_old.h
│ ├── QEX.h
│ ├── QFLAG.h
│ ├── QMN.h
│ ├── QMOD.h
│ ├── QPI.h
│ ├── QPIGS.h
│ ├── QPIGS2.h
│ ├── QPIRI.h
│ └── QPIWS.h
├── Settings.h
├── htmlProzessor.h
├── main.cpp
├── main.h
├── modbus
│ ├── modbus.cpp
│ ├── modbus.h
│ └── modbus_registers.h
├── status-LED.h
└── webpages
│ ├── HTML_CONFIRM_RESET.html
│ ├── HTML_FOOT.html
│ ├── HTML_HEAD.html
│ ├── HTML_MAIN.html
│ ├── HTML_REBOOT.html
│ ├── HTML_SETTINGS.html
│ └── HTML_SETTINGS_EDIT.html
└── tools
├── mini_html.py
├── post_compile.py
└── pre_compile.py
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom: ['https://donate.softwarecrash.de']
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report2.yaml:
--------------------------------------------------------------------------------
1 | name: 🐞 Bug Report
2 | description: If something isn't working as expected.
3 | title: "[Bug]: "
4 | labels: ["bug", "triage"]
5 | body:
6 | - type: checkboxes
7 | attributes:
8 | label: Is there an existing issue for this?
9 | description: Please search to see if an issue already exists for the bug you encountered.
10 | options:
11 | - label: I have searched the existing issues
12 | required: true
13 | - type: dropdown
14 | attributes:
15 | label: Used Hardware?
16 | description: Please let us know what hardware you are using.
17 | multiple: true
18 | options:
19 | - Wemos D1 Mini
20 | - ESP-01 (512KB Flash)
21 | - ESP-01S (1MB Flash)
22 | - Other (please write your hardware in 'What happened?')
23 | validations:
24 | required: true
25 | - type: textarea
26 | id: what-happened
27 | attributes:
28 | label: What happened?
29 | description: Also tell us, what did you expect to happen?
30 | placeholder: Tell us what you see!
31 | validations:
32 | required: true
33 | - type: textarea
34 | id: screenhots
35 | attributes:
36 | label: Screenshots / Fotos
37 | description: Add screenshots and fotos of your wiring to help explain your problem.
38 | placeholder: Drag&Drop screenshots and Fotos here
39 | validations:
40 | required: true
41 | - type: textarea
42 | attributes:
43 | label: Steps To Reproduce
44 | description: If applicable, steps to reproduce the behavior.
45 | placeholder: |
46 | 1. Go to...
47 | 2. Click on...
48 | 3. See error...
49 | validations:
50 | required: false
51 | - type: dropdown
52 | id: version
53 | attributes:
54 | label: Version
55 | description: What version of our software are you running?
56 | multiple: true
57 | options:
58 | - 1.x.x and above
59 | - 0.5.1
60 | - 0.3.3
61 | - 0.3.1
62 | - 0.3.0
63 | - 0.2.x (Depreciated)
64 | validations:
65 | required: true
66 | - type: textarea
67 | id: output
68 | attributes:
69 | label: Relevant livejson output
70 | description: Please copy and paste your livejson-output (http://IP_of_your_ESP/livejson). This will be automatically formatted into code, so no need for backticks.
71 | render: json
72 | validations:
73 | required: true
74 |
75 | - type: dropdown
76 | id: browsers
77 | attributes:
78 | label: What browsers are you seeing the problem on?
79 | multiple: true
80 | options:
81 | - Firefox
82 | - Chrome
83 | - Safari
84 | - Microsoft Edge
85 | - no Issue with the Browser or WebUI
86 | validations:
87 | required: true
88 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request2.yaml:
--------------------------------------------------------------------------------
1 | name: 🚀 Feature Request
2 | description: I have a suggestion (and may want to implement it 🙂)!
3 | title: "[FEATURE]
"
4 | body:
5 | - type: textarea
6 | attributes:
7 | label: Is your feature request related to a problem? Please describe
8 | description: A clear and concise description of what the problem is.
9 | validations:
10 | required: true
11 | - type: textarea
12 | attributes:
13 | label: Describe the solution you'd like
14 | description: A clear and concise description of what you want to happen.
15 | validations:
16 | required: true
17 | - type: textarea
18 | attributes:
19 | label: Describe alternatives you've considered
20 | description: A clear and concise description of any alternative solutions or features you've considered.
21 | validations:
22 | required: true
23 | - type: textarea
24 | attributes:
25 | label: Additional context
26 | description: Add any other context or screenshots about the feature request here.
27 | validations:
28 | required: false
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/help.yaml:
--------------------------------------------------------------------------------
1 | name: 😭 Help!
2 | description: Ask for help
3 | title: "[HELP] "
4 | labels: help wanted
5 | body:
6 | - type: textarea
7 | attributes:
8 | label: A clear and concise description of what the problem is.
9 | description: A clear and concise description of what the problem is.
10 | validations:
11 | required: true
12 | - type: textarea
13 | attributes:
14 | label: Additional context
15 | description: Add any other context or screenshots about the feature request here.
16 | validations:
17 | required: false
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/working_device.yaml:
--------------------------------------------------------------------------------
1 | name: Working device
2 | description: Report a new working device
3 | title: New working device
4 | labels: ["working device"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill out this report!
10 | - type: input
11 | id: device-manufacturer
12 | attributes:
13 | label: Please enter the name of the device manufacturer
14 | description: Tell us the device manufacturer
15 | placeholder: enter device manufacturer!
16 | validations:
17 | required: true
18 | - type: input
19 | id: device-type
20 | attributes:
21 | label: Please enter the device type
22 | description: Also tell us the device type
23 | placeholder: enter device type!
24 | validations:
25 | required: true
26 |
--------------------------------------------------------------------------------
/.github/workflows/ISSUE_WORKFLOW.yaml:
--------------------------------------------------------------------------------
1 | name: Close inactive issues
2 | on:
3 | schedule:
4 | - cron: "30 1 * * *"
5 |
6 | jobs:
7 | close-issues:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | issues: write
11 | pull-requests: write
12 | steps:
13 | - uses: actions/stale@v5
14 | with:
15 | days-before-issue-stale: 30
16 | days-before-issue-close: 14
17 | stale-issue-label: "stale"
18 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
19 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
20 | days-before-pr-stale: -1
21 | days-before-pr-close: -1
22 | repo-token: ${{ secrets.GITHUB_TOKEN }}
23 |
--------------------------------------------------------------------------------
/.github/workflows/update_central_repo.yml:
--------------------------------------------------------------------------------
1 | name: Update Central Firmware Repository
2 |
3 | on:
4 | release:
5 | types: [published, edited]
6 | workflow_dispatch:
7 |
8 | jobs:
9 | update-central-repo:
10 | if: ${{ github.event.release.prerelease == false || github.event_name == 'workflow_dispatch' }}
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout Repository
14 | uses: actions/checkout@v3
15 |
16 | - name: Set up Git
17 | run: |
18 | git config --global user.name 'GitHub Actions'
19 | git config --global user.email 'actions@github.com'
20 |
21 | - name: Clone Central Repository
22 | run: |
23 | git clone https://all-solutions:${{ secrets.CENTRAL_REPO_TOKEN }}@github.com/all-solutions/Flash2MQTT.git Flash2MQTT
24 |
25 | - name: Download Firmware Assets
26 | id: fetch-release
27 | uses: actions/github-script@v6
28 | with:
29 | github-token: ${{ secrets.GITHUB_TOKEN }}
30 | script: |
31 | const fs = require('fs');
32 | const owner = context.repo.owner;
33 | const repo = context.repo.repo;
34 |
35 | let release;
36 | let releaseTag;
37 |
38 | if (context.eventName === 'release') {
39 | releaseTag = context.payload.release.tag_name;
40 | console.log(`Using release tag from event: ${releaseTag}`);
41 | release = await github.rest.repos.getReleaseByTag({ owner, repo, tag: releaseTag });
42 | } else {
43 | release = await github.rest.repos.getLatestRelease({ owner, repo });
44 | releaseTag = release.data.tag_name;
45 | console.log(`Using latest release tag: ${releaseTag}`);
46 | }
47 |
48 | const assets = release.data.assets.filter(asset => asset.name.endsWith('.bin'));
49 |
50 | if (assets.length === 0) {
51 | core.setFailed('No .bin assets found in the release.');
52 | return;
53 | }
54 |
55 | for (const asset of assets) {
56 | const download = await github.rest.repos.getReleaseAsset({
57 | owner,
58 | repo,
59 | asset_id: asset.id,
60 | headers: {
61 | Accept: 'application/octet-stream',
62 | },
63 | });
64 | fs.writeFileSync(asset.name, Buffer.from(download.data));
65 | console.log(`Downloaded ${asset.name}`);
66 | }
67 |
68 | core.setOutput('tag', releaseTag);
69 |
70 | - name: List Downloaded Files
71 | run: ls -la
72 |
73 | - name: Copy Firmware Files
74 | run: |
75 | mkdir -p Flash2MQTT/firmware/${{ github.event.repository.name }}
76 | rm -f Flash2MQTT/firmware/${{ github.event.repository.name }}/*.bin
77 | cp *.bin Flash2MQTT/firmware/${{ github.event.repository.name }}/
78 | echo "Updated at $(date -u)" > Flash2MQTT/firmware/${{ github.event.repository.name }}/last_update.txt
79 |
80 | - name: Install jq
81 | run: sudo apt-get update && sudo apt-get install -y jq
82 |
83 | - name: Update variants.json and firmware_list.json
84 | env:
85 | FIRMWARE_NAME: ${{ github.event.repository.name }}
86 | RELEASE_VERSION: ${{ steps.fetch-release.outputs.tag }}
87 | run: |
88 | cd Flash2MQTT/firmware/${FIRMWARE_NAME}
89 | ls *.bin > bin_files.txt
90 | total=0
91 | count=0
92 | version="${RELEASE_VERSION#v}"
93 | version="${version#V}"
94 | echo "Firmware Name: $FIRMWARE_NAME"
95 | echo "Release Version: $version"
96 |
97 | echo "Determining total number of desired variants..."
98 | while read file; do
99 | if [[ "$file" == *"_${version}.bin" ]]; then
100 | variant_part=$(echo "$file" | sed -E 's/^'"$FIRMWARE_NAME"'_//; s/_'"${version}"'\.bin$//')
101 | variant_name="${variant_part}"
102 | if [[ "$variant_name" == "d1_mini" || "$variant_name" == "esp01_1m" ]]; then
103 | total=$((total + 1))
104 | fi
105 | fi
106 | done < bin_files.txt
107 |
108 | echo '[' > variants.json
109 | while read file; do
110 | if [[ "$file" == *"_${version}.bin" ]]; then
111 | variant_part=$(echo "$file" | sed -E 's/^'"$FIRMWARE_NAME"'_//; s/_'"${version}"'\.bin$//')
112 | variant_name="${variant_part}"
113 | case "$variant_name" in
114 | "d1_mini") display_name="D1 Mini" ;;
115 | "esp01_1m") display_name="ESP-01" ;;
116 | *) continue ;;
117 | esac
118 | count=$((count + 1))
119 | echo ' {' >> variants.json
120 | echo ' "displayName": "'"$display_name"'",' >> variants.json
121 | echo ' "file": "https://all-solutions.github.io/Flash2MQTT/firmware/'"$FIRMWARE_NAME"'/'"$file"'"' >> variants.json
122 | if [ $count -lt $total ]; then
123 | echo ' },' >> variants.json
124 | else
125 | echo ' }' >> variants.json
126 | fi
127 | fi
128 | done < bin_files.txt
129 | echo ']' >> variants.json
130 | rm bin_files.txt
131 |
132 | cd ..
133 | if [ ! -f firmware_list.json ]; then
134 | echo '[]' > firmware_list.json
135 | fi
136 | tmpfile=$(mktemp)
137 | jq --arg name "$FIRMWARE_NAME" --arg version "$version" \
138 | 'if any(.[]; .name == $name) then map(if .name == $name then .version = $version else . end) else . + [{"name": $name, "version": $version}] end' \
139 | firmware_list.json > "$tmpfile" && mv "$tmpfile" firmware_list.json
140 |
141 | - name: Commit and Push Changes
142 | env:
143 | RELEASE_VERSION: ${{ steps.fetch-release.outputs.tag }}
144 | run: |
145 | cd Flash2MQTT
146 | git add firmware/${{ github.event.repository.name }}
147 | git add firmware/firmware_list.json
148 | git commit -m "Update firmware for ${{ github.event.repository.name }} to version $RELEASE_VERSION" || \
149 | git commit --allow-empty -m "Force update for ${{ github.event.repository.name }} to version $RELEASE_VERSION (no file changes)"
150 | git pull --rebase origin main
151 | git push https://all-solutions:${{ secrets.CENTRAL_REPO_TOKEN }}@github.com/all-solutions/Flash2MQTT.git HEAD:main
152 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .pio
2 | .vscode/.browse.c_cpp.db*
3 | .vscode/c_cpp_properties.json
4 | .vscode/launch.json
5 | .vscode/ipch
6 | .vscode
7 | .firmware
8 | src/html.h
--------------------------------------------------------------------------------
/Protocol/Deye Modbus储能-组串-微逆宁波德业V118.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/Deye Modbus储能-组串-微逆宁波德业V118.pdf
--------------------------------------------------------------------------------
/Protocol/HS_MS_MSX RS232 Protocol 20140603B (1).pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/HS_MS_MSX RS232 Protocol 20140603B (1).pdf
--------------------------------------------------------------------------------
/Protocol/PH1800 PV1800 EP1800 PV3500 EP3500 RS485 Modbud RTU communication Protocol 1.4.15 (1).xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/PH1800 PV1800 EP1800 PV3500 EP3500 RS485 Modbud RTU communication Protocol 1.4.15 (1).xlsx
--------------------------------------------------------------------------------
/Protocol/PI18_InfiniSolar-V-protocol-20170926.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/PI18_InfiniSolar-V-protocol-20170926.pdf
--------------------------------------------------------------------------------
/Protocol/PI30MAX.Communication.Protocol20210217.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/PI30MAX.Communication.Protocol20210217.pdf
--------------------------------------------------------------------------------
/Protocol/PI30REVO.20Protocol.20V03--20201112.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/PI30REVO.20Protocol.20V03--20201112.pdf
--------------------------------------------------------------------------------
/Protocol/PI30_Communication-Protocol-20150924-Customer.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/PI30_Communication-Protocol-20150924-Customer.pdf
--------------------------------------------------------------------------------
/Protocol/PI30_HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/PI30_HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf
--------------------------------------------------------------------------------
/Protocol/PI30_PIP-GK_MK-Protocol.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/PI30_PIP-GK_MK-Protocol.pdf
--------------------------------------------------------------------------------
/Protocol/PI41_LV5048.5KW.protocol-20190222.for.customer.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/Protocol/PI41_LV5048.5KW.protocol-20190222.for.customer.pdf
--------------------------------------------------------------------------------
/Protocol/readme.MD:
--------------------------------------------------------------------------------
1 | some protocol source
2 | https://github.com/jblance/mpp-solar/tree/master/docs/protocols
3 | https://github.com/syssi/esphome-pipsolar
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Solar2MQTT [](https://github.com/softwarecrash/Solar2MQTT/releases/latest) [](https://discord.gg/fb2nZWDExz)
2 |
3 | # Features:
4 | - captive portal for wifi and MQTT config
5 | - config in webinterface
6 | - Full Controll with [Custom commands](https://github.com/softwarecrash/Solar2MQTT/wiki/Set-parameters)
7 | - get essential data over webinterface, get [all data](https://github.com/softwarecrash/Solar2MQTT/wiki/Datapoints-and-units) over MQTT
8 | - classic MQTT datapoints or Json string over MQTT
9 | - get Json over web at /livejson?
10 | - firmware update over webinterface
11 | - debug log on Wemos USB or Webserial
12 | - [blink codes](https://github.com/softwarecrash/Solar2MQTT/wiki/Blink-Codes) for the current state of the ESP
13 | - [Reset functions](https://github.com/softwarecrash/Solar2MQTT/wiki/Reset)
14 | - [Support Home Assistant](https://github.com/softwarecrash/Solar2MQTT/wiki/HomeAssistant-integration)
15 | - with Teapod
16 |
17 |
18 |
19 |
20 | **works with**
21 | - Most devices that use the watchpower PC Software
22 | - Most devices that use the Solarpower PC Software
23 | - PIP devices
24 | - i solar
25 | - IGrid
26 | - Many devices from EASUN
27 | - and many many others based on the chinese solar inverter with a rj45 jack and usb port, primary identified by the display
28 | - Take a look at the [device list in the wiki](https://github.com/softwarecrash/Solar2MQTT/wiki/Confirmed-Working-Device-List)
29 |
30 |
31 | **Main screen:**
32 |
33 | 
34 |
35 | **Settings:**
36 |
37 | 
38 |
39 | **Config:**
40 |
41 | 
42 |
43 |
44 |
45 |
46 |
47 | # How to use:
48 | - flash your ESP8266 (recommended Wemos D1 Mini) with our [Flash2MQTT-Tool](https://all-solutions.github.io/Flash2MQTT/?get=Solar2MQTT) or with [Tasmotizer](https://github.com/tasmota/tasmotizer/releases)
49 | - connect the ESP like the [wiring diagram](https://github.com/softwarecrash/Solar2MQTT/wiki/Wiring-Diagram)
50 | - search for the wifi ap "Solar2MQTT-AP" and connect to it
51 | - surf to 192.168.4.1 and set up your wifi and optional MQTT
52 | - that's it :)
53 |
54 | ### How-To video by Jarnsen
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | **POWER:** Using a 3.3V DC Buck Converter that can handle up to 20V or a DC/DC or USB power currently.
63 |
64 | # Parts required to build
65 |
66 | Most of the parts can be bought as modules, it's usually cheaper that way.
67 |
68 | - ESP8266 - Wemos D1 Mini or ESP8266-01
69 | - MAX3232 module Like this https://amzn.eu/d/8t3gk5t or https://bit.ly/3BFPqrw or with orginal cable https://www.amazon.de/dp/B09XWPTDYP
70 | - DC-DC buck module - 12-80v down to 5v
71 |
72 | # Completely assembled and tested PCB's
73 |
74 | You are welcome to get fully stocked and tested PCB's. These are then already loaded with the lastest firmware. The earnings from the PCBs are used for the further development of existing and new projects.
75 |
76 | 
77 |
78 | If interested see [here](https://all-solutions.store)
79 |
80 | #
81 | Questions?
82 | [Join the Discord Channel (German / English)](https://discord.gg/pAArqVsVS4)
83 |
84 | #
85 | [
](https://donate.softwarecrash.de)
86 |
87 | [](https://creativecommons.org/licenses/by-nc-nd/4.0/)
88 |
89 |
--------------------------------------------------------------------------------
/images/ToDo.md:
--------------------------------------------------------------------------------
1 | wiki:
2 | - ~~verbundungsdiagramme einbauen~~
3 | - anleitung um herauszufinden ob der inverter mit der soft funktioniert
4 | - fehlende infos ergänzen
5 |
6 | readme.md
7 | - ~~neue bilder~~
8 | - ~~allgemein mal drüber schauen und neu formatieren~~
9 |
10 | diesen ordner ~~mit bildern löschen~~ wenn fertig
11 |
--------------------------------------------------------------------------------
/include/readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/include/readme.md
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/.gitignore:
--------------------------------------------------------------------------------
1 | tests/bin
2 | .pioenvs
3 | .piolibdeps
4 | .clang_complete
5 | .gcc-flags.json
6 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: cpp
3 | compiler:
4 | - g++
5 | script: cd tests && make && make test
6 | os:
7 | - linux
8 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/CHANGES.txt:
--------------------------------------------------------------------------------
1 | 2.8
2 | * Add setBufferSize() to override MQTT_MAX_PACKET_SIZE
3 | * Add setKeepAlive() to override MQTT_KEEPALIVE
4 | * Add setSocketTimeout() to overide MQTT_SOCKET_TIMEOUT
5 | * Added check to prevent subscribe/unsubscribe to empty topics
6 | * Declare wifi mode prior to connect in ESP example
7 | * Use `strnlen` to avoid overruns
8 | * Support pre-connected Client objects
9 |
10 | 2.7
11 | * Fix remaining-length handling to prevent buffer overrun
12 | * Add large-payload API - beginPublish/write/publish/endPublish
13 | * Add yield call to improve reliability on ESP
14 | * Add Clean Session flag to connect options
15 | * Add ESP32 support for functional callback signature
16 | * Various other fixes
17 |
18 | 2.4
19 | * Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely
20 | whilst waiting for inbound data
21 | * Fixed return code when publishing >256 bytes
22 |
23 | 2.3
24 | * Add publish(topic,payload,retained) function
25 |
26 | 2.2
27 | * Change code layout to match Arduino Library reqs
28 |
29 | 2.1
30 | * Add MAX_TRANSFER_SIZE def to chunk messages if needed
31 | * Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE
32 |
33 | 2.0
34 | * Add (and default to) MQTT 3.1.1 support
35 | * Fix PROGMEM handling for Intel Galileo/ESP8266
36 | * Add overloaded constructors for convenience
37 | * Add chainable setters for server/callback/client/stream
38 | * Add state function to return connack return code
39 |
40 | 1.9
41 | * Do not split MQTT packets over multiple calls to _client->write()
42 | * API change: All constructors now require an instance of Client
43 | to be passed in.
44 | * Fixed example to match 1.8 api changes - dpslwk
45 | * Added username/password support - WilHall
46 | * Added publish_P - publishes messages from PROGMEM - jobytaffey
47 |
48 | 1.8
49 | * KeepAlive interval is configurable in PubSubClient.h
50 | * Maximum packet size is configurable in PubSubClient.h
51 | * API change: Return boolean rather than int from various functions
52 | * API change: Length parameter in message callback changed
53 | from int to unsigned int
54 | * Various internal tidy-ups around types
55 | 1.7
56 | * Improved keepalive handling
57 | * Updated to the Arduino-1.0 API
58 | 1.6
59 | * Added the ability to publish a retained message
60 |
61 | 1.5
62 | * Added default constructor
63 | * Fixed compile error when used with arduino-0021 or later
64 |
65 | 1.4
66 | * Fixed connection lost handling
67 |
68 | 1.3
69 | * Fixed packet reading bug in PubSubClient.readPacket
70 |
71 | 1.2
72 | * Fixed compile error when used with arduino-0016 or later
73 |
74 |
75 | 1.1
76 | * Reduced size of library
77 | * Added support for Will messages
78 | * Clarified licensing - see LICENSE.txt
79 |
80 |
81 | 1.0
82 | * Only Quality of Service (QOS) 0 messaging is supported
83 | * The maximum message size, including header, is 128 bytes
84 | * The keepalive interval is set to 30 seconds
85 | * No support for Will messages
86 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2020 Nicholas O'Leary
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/README.md:
--------------------------------------------------------------------------------
1 | # Arduino Client for MQTT
2 |
3 | This library provides a client for doing simple publish/subscribe messaging with
4 | a server that supports MQTT.
5 |
6 | ## Examples
7 |
8 | The library comes with a number of example sketches. See File > Examples > PubSubClient
9 | within the Arduino application.
10 |
11 | Full API documentation is available here: https://pubsubclient.knolleary.net
12 |
13 | ## Limitations
14 |
15 | - It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.
16 | - The maximum message size, including header, is **256 bytes** by default. This
17 | is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h` or can be changed
18 | by calling `PubSubClient::setBufferSize(size)`.
19 | - The keepalive interval is set to 15 seconds by default. This is configurable
20 | via `MQTT_KEEPALIVE` in `PubSubClient.h` or can be changed by calling
21 | `PubSubClient::setKeepAlive(keepAlive)`.
22 | - The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by
23 | changing value of `MQTT_VERSION` in `PubSubClient.h`.
24 |
25 |
26 | ## Compatible Hardware
27 |
28 | The library uses the Arduino Ethernet Client api for interacting with the
29 | underlying network hardware. This means it Just Works with a growing number of
30 | boards and shields, including:
31 |
32 | - Arduino Ethernet
33 | - Arduino Ethernet Shield
34 | - Arduino YUN – use the included `YunClient` in place of `EthernetClient`, and
35 | be sure to do a `Bridge.begin()` first
36 | - Arduino WiFi Shield - if you want to send packets > 90 bytes with this shield,
37 | enable the `MQTT_MAX_TRANSFER_SIZE` define in `PubSubClient.h`.
38 | - Sparkfun WiFly Shield – [library](https://github.com/dpslwk/WiFly)
39 | - TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library)
40 | - Intel Galileo/Edison
41 | - ESP8266
42 | - ESP32
43 |
44 | The library cannot currently be used with hardware based on the ENC28J60 chip –
45 | such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an
46 | [alternative library](https://github.com/njh/NanodeMQTT) available.
47 |
48 | ## License
49 |
50 | This code is released under the MIT License.
51 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/examples/mqtt_auth/mqtt_auth.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Basic MQTT example with Authentication
3 |
4 | - connects to an MQTT server, providing username
5 | and password
6 | - publishes "hello world" to the topic "outTopic"
7 | - subscribes to the topic "inTopic"
8 | */
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | // Update these with values suitable for your network.
15 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
16 | IPAddress ip(172, 16, 0, 100);
17 | IPAddress server(172, 16, 0, 2);
18 |
19 | void callback(char* topic, byte* payload, unsigned int length) {
20 | // handle message arrived
21 | }
22 |
23 | EthernetClient ethClient;
24 | PubSubClient client(server, 1883, callback, ethClient);
25 |
26 | void setup()
27 | {
28 | Ethernet.begin(mac, ip);
29 | // Note - the default maximum packet size is 128 bytes. If the
30 | // combined length of clientId, username and password exceed this use the
31 | // following to increase the buffer size:
32 | // client.setBufferSize(255);
33 |
34 | if (client.connect("arduinoClient", "testuser", "testpass")) {
35 | client.publish("outTopic","hello world");
36 | client.subscribe("inTopic");
37 | }
38 | }
39 |
40 | void loop()
41 | {
42 | client.loop();
43 | }
44 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/examples/mqtt_basic/mqtt_basic.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Basic MQTT example
3 |
4 | This sketch demonstrates the basic capabilities of the library.
5 | It connects to an MQTT server then:
6 | - publishes "hello world" to the topic "outTopic"
7 | - subscribes to the topic "inTopic", printing out any messages
8 | it receives. NB - it assumes the received payloads are strings not binary
9 |
10 | It will reconnect to the server if the connection is lost using a blocking
11 | reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
12 | achieve the same result without blocking the main loop.
13 |
14 | */
15 |
16 | #include
17 | #include
18 | #include
19 |
20 | // Update these with values suitable for your network.
21 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
22 | IPAddress ip(172, 16, 0, 100);
23 | IPAddress server(172, 16, 0, 2);
24 |
25 | void callback(char* topic, byte* payload, unsigned int length) {
26 | Serial.print("Message arrived [");
27 | Serial.print(topic);
28 | Serial.print("] ");
29 | for (int i=0;i Preferences -> Additional Boards Manager URLs":
16 | http://arduino.esp8266.com/stable/package_esp8266com_index.json
17 | - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
18 | - Select your ESP8266 in "Tools -> Board"
19 | */
20 |
21 | #include
22 | #include
23 |
24 | // Update these with values suitable for your network.
25 |
26 | const char* ssid = "........";
27 | const char* password = "........";
28 | const char* mqtt_server = "broker.mqtt-dashboard.com";
29 |
30 | WiFiClient espClient;
31 | PubSubClient client(espClient);
32 | unsigned long lastMsg = 0;
33 | #define MSG_BUFFER_SIZE (50)
34 | char msg[MSG_BUFFER_SIZE];
35 | int value = 0;
36 |
37 | void setup_wifi() {
38 |
39 | delay(10);
40 | // We start by connecting to a WiFi network
41 | Serial.println();
42 | Serial.print("Connecting to ");
43 | Serial.println(ssid);
44 |
45 | WiFi.mode(WIFI_STA);
46 | WiFi.begin(ssid, password);
47 |
48 | while (WiFi.status() != WL_CONNECTED) {
49 | delay(500);
50 | Serial.print(".");
51 | }
52 |
53 | randomSeed(micros());
54 |
55 | Serial.println("");
56 | Serial.println("WiFi connected");
57 | Serial.println("IP address: ");
58 | Serial.println(WiFi.localIP());
59 | }
60 |
61 | void callback(char* topic, byte* payload, unsigned int length) {
62 | Serial.print("Message arrived [");
63 | Serial.print(topic);
64 | Serial.print("] ");
65 | for (int i = 0; i < length; i++) {
66 | Serial.print((char)payload[i]);
67 | }
68 | Serial.println();
69 |
70 | // Switch on the LED if an 1 was received as first character
71 | if ((char)payload[0] == '1') {
72 | digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
73 | // but actually the LED is on; this is because
74 | // it is active low on the ESP-01)
75 | } else {
76 | digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
77 | }
78 |
79 | }
80 |
81 | void reconnect() {
82 | // Loop until we're reconnected
83 | while (!client.connected()) {
84 | Serial.print("Attempting MQTT connection...");
85 | // Create a random client ID
86 | String clientId = "ESP8266Client-";
87 | clientId += String(random(0xffff), HEX);
88 | // Attempt to connect
89 | if (client.connect(clientId.c_str())) {
90 | Serial.println("connected");
91 | // Once connected, publish an announcement...
92 | client.publish("outTopic", "hello world");
93 | // ... and resubscribe
94 | client.subscribe("inTopic");
95 | } else {
96 | Serial.print("failed, rc=");
97 | Serial.print(client.state());
98 | Serial.println(" try again in 5 seconds");
99 | // Wait 5 seconds before retrying
100 | delay(5000);
101 | }
102 | }
103 | }
104 |
105 | void setup() {
106 | pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
107 | Serial.begin(115200);
108 | setup_wifi();
109 | client.setServer(mqtt_server, 1883);
110 | client.setCallback(callback);
111 | }
112 |
113 | void loop() {
114 |
115 | if (!client.connected()) {
116 | reconnect();
117 | }
118 | client.loop();
119 |
120 | unsigned long now = millis();
121 | if (now - lastMsg > 2000) {
122 | lastMsg = now;
123 | ++value;
124 | snprintf (msg, MSG_BUFFER_SIZE, "hello world #%ld", value);
125 | Serial.print("Publish message: ");
126 | Serial.println(msg);
127 | client.publish("outTopic", msg);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/examples/mqtt_large_message/mqtt_large_message.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Long message ESP8266 MQTT example
3 |
4 | This sketch demonstrates sending arbitrarily large messages in combination
5 | with the ESP8266 board/library.
6 |
7 | It connects to an MQTT server then:
8 | - publishes "hello world" to the topic "outTopic"
9 | - subscribes to the topic "greenBottles/#", printing out any messages
10 | it receives. NB - it assumes the received payloads are strings not binary
11 | - If the sub-topic is a number, it publishes a "greenBottles/lyrics" message
12 | with a payload consisting of the lyrics to "10 green bottles", replacing
13 | 10 with the number given in the sub-topic.
14 |
15 | It will reconnect to the server if the connection is lost using a blocking
16 | reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
17 | achieve the same result without blocking the main loop.
18 |
19 | To install the ESP8266 board, (using Arduino 1.6.4+):
20 | - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
21 | http://arduino.esp8266.com/stable/package_esp8266com_index.json
22 | - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
23 | - Select your ESP8266 in "Tools -> Board"
24 |
25 | */
26 |
27 | #include
28 | #include
29 |
30 | // Update these with values suitable for your network.
31 |
32 | const char* ssid = "........";
33 | const char* password = "........";
34 | const char* mqtt_server = "broker.mqtt-dashboard.com";
35 |
36 | WiFiClient espClient;
37 | PubSubClient client(espClient);
38 | long lastMsg = 0;
39 | char msg[50];
40 | int value = 0;
41 |
42 | void setup_wifi() {
43 |
44 | delay(10);
45 | // We start by connecting to a WiFi network
46 | Serial.println();
47 | Serial.print("Connecting to ");
48 | Serial.println(ssid);
49 |
50 | WiFi.begin(ssid, password);
51 |
52 | while (WiFi.status() != WL_CONNECTED) {
53 | delay(500);
54 | Serial.print(".");
55 | }
56 |
57 | randomSeed(micros());
58 |
59 | Serial.println("");
60 | Serial.println("WiFi connected");
61 | Serial.println("IP address: ");
62 | Serial.println(WiFi.localIP());
63 | }
64 |
65 | void callback(char* topic, byte* payload, unsigned int length) {
66 | Serial.print("Message arrived [");
67 | Serial.print(topic);
68 | Serial.print("] ");
69 | for (int i = 0; i < length; i++) {
70 | Serial.print((char)payload[i]);
71 | }
72 | Serial.println();
73 |
74 | // Find out how many bottles we should generate lyrics for
75 | String topicStr(topic);
76 | int bottleCount = 0; // assume no bottles unless we correctly parse a value from the topic
77 | if (topicStr.indexOf('/') >= 0) {
78 | // The topic includes a '/', we'll try to read the number of bottles from just after that
79 | topicStr.remove(0, topicStr.indexOf('/')+1);
80 | // Now see if there's a number of bottles after the '/'
81 | bottleCount = topicStr.toInt();
82 | }
83 |
84 | if (bottleCount > 0) {
85 | // Work out how big our resulting message will be
86 | int msgLen = 0;
87 | for (int i = bottleCount; i > 0; i--) {
88 | String numBottles(i);
89 | msgLen += 2*numBottles.length();
90 | if (i == 1) {
91 | msgLen += 2*String(" green bottle, standing on the wall\n").length();
92 | } else {
93 | msgLen += 2*String(" green bottles, standing on the wall\n").length();
94 | }
95 | msgLen += String("And if one green bottle should accidentally fall\nThere'll be ").length();
96 | switch (i) {
97 | case 1:
98 | msgLen += String("no green bottles, standing on the wall\n\n").length();
99 | break;
100 | case 2:
101 | msgLen += String("1 green bottle, standing on the wall\n\n").length();
102 | break;
103 | default:
104 | numBottles = i-1;
105 | msgLen += numBottles.length();
106 | msgLen += String(" green bottles, standing on the wall\n\n").length();
107 | break;
108 | };
109 | }
110 |
111 | // Now we can start to publish the message
112 | client.beginPublish("greenBottles/lyrics", msgLen, false);
113 | for (int i = bottleCount; i > 0; i--) {
114 | for (int j = 0; j < 2; j++) {
115 | client.print(i);
116 | if (i == 1) {
117 | client.print(" green bottle, standing on the wall\n");
118 | } else {
119 | client.print(" green bottles, standing on the wall\n");
120 | }
121 | }
122 | client.print("And if one green bottle should accidentally fall\nThere'll be ");
123 | switch (i) {
124 | case 1:
125 | client.print("no green bottles, standing on the wall\n\n");
126 | break;
127 | case 2:
128 | client.print("1 green bottle, standing on the wall\n\n");
129 | break;
130 | default:
131 | client.print(i-1);
132 | client.print(" green bottles, standing on the wall\n\n");
133 | break;
134 | };
135 | }
136 | // Now we're done!
137 | client.endPublish();
138 | }
139 | }
140 |
141 | void reconnect() {
142 | // Loop until we're reconnected
143 | while (!client.connected()) {
144 | Serial.print("Attempting MQTT connection...");
145 | // Create a random client ID
146 | String clientId = "ESP8266Client-";
147 | clientId += String(random(0xffff), HEX);
148 | // Attempt to connect
149 | if (client.connect(clientId.c_str())) {
150 | Serial.println("connected");
151 | // Once connected, publish an announcement...
152 | client.publish("outTopic", "hello world");
153 | // ... and resubscribe
154 | client.subscribe("greenBottles/#");
155 | } else {
156 | Serial.print("failed, rc=");
157 | Serial.print(client.state());
158 | Serial.println(" try again in 5 seconds");
159 | // Wait 5 seconds before retrying
160 | delay(5000);
161 | }
162 | }
163 | }
164 |
165 | void setup() {
166 | pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
167 | Serial.begin(115200);
168 | setup_wifi();
169 | client.setServer(mqtt_server, 1883);
170 | client.setCallback(callback);
171 | }
172 |
173 | void loop() {
174 |
175 | if (!client.connected()) {
176 | reconnect();
177 | }
178 | client.loop();
179 | }
180 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Publishing in the callback
3 |
4 | - connects to an MQTT server
5 | - subscribes to the topic "inTopic"
6 | - when a message is received, republishes it to "outTopic"
7 |
8 | This example shows how to publish messages within the
9 | callback function. The callback function header needs to
10 | be declared before the PubSubClient constructor and the
11 | actual callback defined afterwards.
12 | This ensures the client reference in the callback function
13 | is valid.
14 |
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | // Update these with values suitable for your network.
22 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
23 | IPAddress ip(172, 16, 0, 100);
24 | IPAddress server(172, 16, 0, 2);
25 |
26 | // Callback function header
27 | void callback(char* topic, byte* payload, unsigned int length);
28 |
29 | EthernetClient ethClient;
30 | PubSubClient client(server, 1883, callback, ethClient);
31 |
32 | // Callback function
33 | void callback(char* topic, byte* payload, unsigned int length) {
34 | // In order to republish this payload, a copy must be made
35 | // as the orignal payload buffer will be overwritten whilst
36 | // constructing the PUBLISH packet.
37 |
38 | // Allocate the correct amount of memory for the payload copy
39 | byte* p = (byte*)malloc(length);
40 | // Copy the payload to the new buffer
41 | memcpy(p,payload,length);
42 | client.publish("outTopic", p, length);
43 | // Free the memory
44 | free(p);
45 | }
46 |
47 | void setup()
48 | {
49 |
50 | Ethernet.begin(mac, ip);
51 | if (client.connect("arduinoClient")) {
52 | client.publish("outTopic","hello world");
53 | client.subscribe("inTopic");
54 | }
55 | }
56 |
57 | void loop()
58 | {
59 | client.loop();
60 | }
61 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Reconnecting MQTT example - non-blocking
3 |
4 | This sketch demonstrates how to keep the client connected
5 | using a non-blocking reconnect function. If the client loses
6 | its connection, it attempts to reconnect every 5 seconds
7 | without blocking the main loop.
8 |
9 | */
10 |
11 | #include
12 | #include
13 | #include
14 |
15 | // Update these with values suitable for your hardware/network.
16 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
17 | IPAddress ip(172, 16, 0, 100);
18 | IPAddress server(172, 16, 0, 2);
19 |
20 | void callback(char* topic, byte* payload, unsigned int length) {
21 | // handle message arrived
22 | }
23 |
24 | EthernetClient ethClient;
25 | PubSubClient client(ethClient);
26 |
27 | long lastReconnectAttempt = 0;
28 |
29 | boolean reconnect() {
30 | if (client.connect("arduinoClient")) {
31 | // Once connected, publish an announcement...
32 | client.publish("outTopic","hello world");
33 | // ... and resubscribe
34 | client.subscribe("inTopic");
35 | }
36 | return client.connected();
37 | }
38 |
39 | void setup()
40 | {
41 | client.setServer(server, 1883);
42 | client.setCallback(callback);
43 |
44 | Ethernet.begin(mac, ip);
45 | delay(1500);
46 | lastReconnectAttempt = 0;
47 | }
48 |
49 |
50 | void loop()
51 | {
52 | if (!client.connected()) {
53 | long now = millis();
54 | if (now - lastReconnectAttempt > 5000) {
55 | lastReconnectAttempt = now;
56 | // Attempt to reconnect
57 | if (reconnect()) {
58 | lastReconnectAttempt = 0;
59 | }
60 | }
61 | } else {
62 | // Client connected
63 |
64 | client.loop();
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/examples/mqtt_stream/mqtt_stream.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Example of using a Stream object to store the message payload
3 |
4 | Uses SRAM library: https://github.com/ennui2342/arduino-sram
5 | but could use any Stream based class such as SD
6 |
7 | - connects to an MQTT server
8 | - publishes "hello world" to the topic "outTopic"
9 | - subscribes to the topic "inTopic"
10 | */
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | // Update these with values suitable for your network.
18 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
19 | IPAddress ip(172, 16, 0, 100);
20 | IPAddress server(172, 16, 0, 2);
21 |
22 | SRAM sram(4, SRAM_1024);
23 |
24 | void callback(char* topic, byte* payload, unsigned int length) {
25 | sram.seek(1);
26 |
27 | // do something with the message
28 | for(uint8_t i=0; i
4 | maintainer=Nick O'Leary
5 | sentence=A client library for MQTT messaging.
6 | paragraph=MQTT is a lightweight messaging protocol ideal for small devices. This library allows you to send and receive MQTT messages. It supports the latest MQTT 3.1.1 protocol and can be configured to use the older MQTT 3.1 if needed. It supports all Arduino Ethernet Client compatible hardware, including the Intel Galileo/Edison, ESP8266 and TI CC3000.
7 | category=Communication
8 | url=http://pubsubclient.knolleary.net
9 | architectures=esp8266,esp32
10 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/src/PubSubClient.h:
--------------------------------------------------------------------------------
1 | /*
2 | PubSubClient.h - A simple client for MQTT.
3 | Nick O'Leary
4 | http://knolleary.net
5 | */
6 |
7 | #ifndef PubSubClient_h
8 | #define PubSubClient_h
9 |
10 | #include
11 | #include "IPAddress.h"
12 | #include "Client.h"
13 | #include "Stream.h"
14 |
15 | #define MQTT_VERSION_3_1 3
16 | #define MQTT_VERSION_3_1_1 4
17 |
18 | // MQTT_VERSION : Pick the version
19 | //#define MQTT_VERSION MQTT_VERSION_3_1
20 | #ifndef MQTT_VERSION
21 | #define MQTT_VERSION MQTT_VERSION_3_1_1
22 | #endif
23 |
24 | // MQTT_MAX_PACKET_SIZE : Maximum packet size. Override with setBufferSize().
25 | #ifndef MQTT_MAX_PACKET_SIZE
26 | //#define MQTT_MAX_PACKET_SIZE 256
27 | #define MQTT_MAX_PACKET_SIZE 1200 // Tasmota v8.1.0.8
28 | #endif
29 |
30 | // MQTT_KEEPALIVE : keepAlive interval in Seconds. Override with setKeepAlive()
31 | #ifndef MQTT_KEEPALIVE
32 | #define MQTT_KEEPALIVE 15
33 | #endif
34 |
35 | // MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds. Override with setSocketTimeout()
36 | #ifndef MQTT_SOCKET_TIMEOUT
37 | #define MQTT_SOCKET_TIMEOUT 15
38 | #endif
39 |
40 | // MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
41 | // in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
42 | // pass the entire MQTT packet in each write call.
43 | //#define MQTT_MAX_TRANSFER_SIZE 80
44 |
45 | // Possible values for client.state()
46 | #define MQTT_CONNECTION_TIMEOUT -4
47 | #define MQTT_CONNECTION_LOST -3
48 | #define MQTT_CONNECT_FAILED -2
49 | #define MQTT_DISCONNECTED -1
50 | #define MQTT_CONNECTED 0
51 | #define MQTT_CONNECT_BAD_PROTOCOL 1
52 | #define MQTT_CONNECT_BAD_CLIENT_ID 2
53 | #define MQTT_CONNECT_UNAVAILABLE 3
54 | #define MQTT_CONNECT_BAD_CREDENTIALS 4
55 | #define MQTT_CONNECT_UNAUTHORIZED 5
56 |
57 | #define MQTTCONNECT 1 << 4 // Client request to connect to Server
58 | #define MQTTCONNACK 2 << 4 // Connect Acknowledgment
59 | #define MQTTPUBLISH 3 << 4 // Publish message
60 | #define MQTTPUBACK 4 << 4 // Publish Acknowledgment
61 | #define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
62 | #define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
63 | #define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
64 | #define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
65 | #define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
66 | #define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
67 | #define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
68 | #define MQTTPINGREQ 12 << 4 // PING Request
69 | #define MQTTPINGRESP 13 << 4 // PING Response
70 | #define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
71 | #define MQTTReserved 15 << 4 // Reserved
72 |
73 | #define MQTTQOS0 (0 << 1)
74 | #define MQTTQOS1 (1 << 1)
75 | #define MQTTQOS2 (2 << 1)
76 |
77 | // Maximum size of fixed header and variable length size header
78 | #define MQTT_MAX_HEADER_SIZE 5
79 |
80 | #if defined(ESP8266) || defined(ESP32)
81 | #include
82 | #define MQTT_CALLBACK_SIGNATURE std::function callback
83 | #else
84 | #define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
85 | #endif
86 |
87 | #define CHECK_STRING_LENGTH(l,s) if (l+2+strnlen(s, this->bufferSize) > this->bufferSize) {_client->stop();return false;}
88 |
89 | class PubSubClient : public Print {
90 | private:
91 | Client* _client;
92 | uint8_t* buffer;
93 | uint16_t bufferSize;
94 | uint16_t keepAlive;
95 | uint16_t socketTimeout;
96 | uint16_t nextMsgId;
97 | unsigned long lastOutActivity;
98 | unsigned long lastInActivity;
99 | bool pingOutstanding;
100 | MQTT_CALLBACK_SIGNATURE;
101 | uint32_t readPacket(uint8_t*);
102 | boolean readByte(uint8_t * result);
103 | boolean readByte(uint8_t * result, uint16_t * index);
104 | boolean write(uint8_t header, uint8_t* buf, uint16_t length);
105 | uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
106 | // Build up the header ready to send
107 | // Returns the size of the header
108 | // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
109 | // (MQTT_MAX_HEADER_SIZE - ) bytes into the buffer
110 | size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
111 | IPAddress ip;
112 |
113 | // Start Tasmota patch
114 | // const char* domain;
115 |
116 | String domain;
117 | // End Tasmota patch
118 |
119 | uint16_t port;
120 | Stream* stream;
121 | int _state;
122 | public:
123 | PubSubClient();
124 | PubSubClient(Client& client);
125 | PubSubClient(IPAddress, uint16_t, Client& client);
126 | PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
127 | PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
128 | PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
129 | PubSubClient(uint8_t *, uint16_t, Client& client);
130 | PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
131 | PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
132 | PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
133 | PubSubClient(const char*, uint16_t, Client& client);
134 | PubSubClient(const char*, uint16_t, Client& client, Stream&);
135 | PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
136 | PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
137 |
138 | ~PubSubClient();
139 |
140 | PubSubClient& setServer(IPAddress ip, uint16_t port);
141 | PubSubClient& setServer(uint8_t * ip, uint16_t port);
142 | PubSubClient& setServer(const char * domain, uint16_t port);
143 | PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
144 | PubSubClient& setClient(Client& client);
145 | PubSubClient& setStream(Stream& stream);
146 | PubSubClient& setKeepAlive(uint16_t keepAlive);
147 | PubSubClient& setSocketTimeout(uint16_t timeout);
148 |
149 | boolean setBufferSize(uint16_t size);
150 | uint16_t getBufferSize();
151 |
152 | boolean connect(const char* id);
153 | boolean connect(const char* id, const char* user, const char* pass);
154 | boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
155 | boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
156 | boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
157 |
158 | // Start Tasmota patch
159 | // void disconnect();
160 |
161 | void disconnect(bool disconnect_package = false);
162 | // End Tasmota patch
163 |
164 | boolean publish(const char* topic, const char* payload);
165 | boolean publish(const char* topic, const char* payload, boolean retained);
166 | boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
167 | boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
168 | boolean publish_P(const char* topic, const char* payload, boolean retained);
169 | boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
170 | // Start to publish a message.
171 | // This API:
172 | // beginPublish(...)
173 | // one or more calls to write(...)
174 | // endPublish()
175 | // Allows for arbitrarily large payloads to be sent without them having to be copied into
176 | // a new buffer and held in memory at one time
177 | // Returns 1 if the message was started successfully, 0 if there was an error
178 | boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
179 | // Finish off this publish message (started with beginPublish)
180 | // Returns 1 if the packet was sent successfully, 0 if there was an error
181 | int endPublish();
182 | // Write a single byte of payload (only to be used with beginPublish/endPublish)
183 | virtual size_t write(uint8_t);
184 | // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
185 | // Returns the number of bytes written
186 | virtual size_t write(const uint8_t *buffer, size_t size);
187 | boolean subscribe(const char* topic);
188 | boolean subscribe(const char* topic, uint8_t qos);
189 | boolean unsubscribe(const char* topic);
190 | boolean loop();
191 | boolean connected();
192 | int state();
193 |
194 | };
195 |
196 |
197 | #endif
198 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/.gitignore:
--------------------------------------------------------------------------------
1 | .build
2 | tmpbin
3 | logs
4 | *.pyc
5 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/Makefile:
--------------------------------------------------------------------------------
1 | SRC_PATH=./src
2 | OUT_PATH=./bin
3 | TEST_SRC=$(wildcard ${SRC_PATH}/*_spec.cpp)
4 | TEST_BIN= $(TEST_SRC:${SRC_PATH}/%.cpp=${OUT_PATH}/%)
5 | VPATH=${SRC_PATH}
6 | SHIM_FILES=${SRC_PATH}/lib/*.cpp
7 | PSC_FILE=../src/PubSubClient.cpp
8 | CC=g++
9 | CFLAGS=-I${SRC_PATH}/lib -I../src
10 |
11 | all: $(TEST_BIN)
12 |
13 | ${OUT_PATH}/%: ${SRC_PATH}/%.cpp ${PSC_FILE} ${SHIM_FILES}
14 | mkdir -p ${OUT_PATH}
15 | ${CC} ${CFLAGS} $^ -o $@
16 |
17 | clean:
18 | @rm -rf ${OUT_PATH}
19 |
20 | test:
21 | @bin/connect_spec
22 | @bin/publish_spec
23 | @bin/receive_spec
24 | @bin/subscribe_spec
25 | @bin/keepalive_spec
26 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/README.md:
--------------------------------------------------------------------------------
1 | # Arduino Client for MQTT Test Suite
2 |
3 | This is a regression test suite for the `PubSubClient` library.
4 |
5 | There are two parts:
6 |
7 | - Tests that can be compiled and run on any machine
8 | - Tests that build the example sketches using the Arduino IDE
9 |
10 |
11 | It is a work-in-progress and is subject to complete refactoring as the whim takes
12 | me.
13 |
14 |
15 | ## Local tests
16 |
17 | These are a set of executables that can be run to test specific areas of functionality.
18 | They do not require a real Arduino to be attached, nor the use of the Arduino IDE.
19 |
20 | The tests include a set of mock files to stub out the parts of the Arduino environment the library
21 | depends on.
22 |
23 | ### Dependencies
24 |
25 | - g++
26 |
27 | ### Running
28 |
29 | Build the tests using the provided `Makefile`:
30 |
31 | $ make
32 |
33 | This will create a set of executables in `./bin/`. Run each of these executables to test the corresponding functionality.
34 |
35 | *Note:* the `connect_spec` and `keepalive_spec` tests involve testing keepalive timers so naturally take a few minutes to run through.
36 |
37 | ## Arduino tests
38 |
39 | *Note:* INO Tool doesn't currently play nicely with Arduino 1.5. This has broken this test suite.
40 |
41 | Without a suitable arduino plugged in, the test suite will only check the
42 | example sketches compile cleanly against the library.
43 |
44 | With an arduino plugged in, each sketch that has a corresponding python
45 | test case is built, uploaded and then the tests run.
46 |
47 | ### Dependencies
48 |
49 | - Python 2.7+
50 | - [INO Tool](http://inotool.org/) - this provides command-line build/upload of Arduino sketches
51 |
52 | ### Running
53 |
54 | The test suite _does not_ run an MQTT server - it is assumed to be running already.
55 |
56 | $ python testsuite.py
57 |
58 | A summary of activity is printed to the console. More comprehensive logs are written
59 | to the `logs` directory.
60 |
61 | ### What it does
62 |
63 | For each sketch in the library's `examples` directory, e.g. `mqtt_basic.ino`, the suite looks for a matching test case
64 | `testcases/mqtt_basic.py`.
65 |
66 | The test case must follow these conventions:
67 | - sub-class `unittest.TestCase`
68 | - provide the class methods `setUpClass` and `tearDownClass` (TODO: make this optional)
69 | - all test method names begin with `test_`
70 |
71 | The suite will call the `setUpClass` method _before_ uploading the sketch. This
72 | allows any test setup to be performed before the sketch runs - such as connecting
73 | a client and subscribing to topics.
74 |
75 |
76 | ### Settings
77 |
78 | The file `testcases/settings.py` is used to config the test environment.
79 |
80 | - `server_ip` - the IP address of the broker the client should connect to (the broker port is assumed to be 1883).
81 | - `arduino_ip` - the IP address the arduino should use (when not testing DHCP).
82 |
83 | Before each sketch is compiled, these values are automatically substituted in. To
84 | do this, the suite looks for lines that _start_ with the following:
85 |
86 | byte server[] = {
87 | byte ip[] = {
88 |
89 | and replaces them with the appropriate values.
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/keepalive_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 | #include
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | void callback(char* topic, byte* payload, unsigned int length) {
11 | // handle message arrived
12 | }
13 |
14 |
15 | int test_keepalive_pings_idle() {
16 | IT("keeps an idle connection alive (takes 1 minute)");
17 |
18 | ShimClient shimClient;
19 | shimClient.setAllowConnect(true);
20 |
21 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
22 | shimClient.respond(connack,4);
23 |
24 | PubSubClient client(server, 1883, callback, shimClient);
25 | int rc = client.connect((char*)"client_test1");
26 | IS_TRUE(rc);
27 |
28 | byte pingreq[] = { 0xC0,0x0 };
29 | shimClient.expect(pingreq,2);
30 | byte pingresp[] = { 0xD0,0x0 };
31 | shimClient.respond(pingresp,2);
32 |
33 | for (int i = 0; i < 50; i++) {
34 | sleep(1);
35 | if ( i == 15 || i == 31 || i == 47) {
36 | shimClient.expect(pingreq,2);
37 | shimClient.respond(pingresp,2);
38 | }
39 | rc = client.loop();
40 | IS_TRUE(rc);
41 | }
42 |
43 | IS_FALSE(shimClient.error());
44 |
45 | END_IT
46 | }
47 |
48 | int test_keepalive_pings_with_outbound_qos0() {
49 | IT("keeps a connection alive that only sends qos0 (takes 1 minute)");
50 |
51 | ShimClient shimClient;
52 | shimClient.setAllowConnect(true);
53 |
54 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
55 | shimClient.respond(connack,4);
56 |
57 | PubSubClient client(server, 1883, callback, shimClient);
58 | int rc = client.connect((char*)"client_test1");
59 | IS_TRUE(rc);
60 |
61 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
62 |
63 | for (int i = 0; i < 50; i++) {
64 | TRACE(i<<":");
65 | shimClient.expect(publish,16);
66 | rc = client.publish((char*)"topic",(char*)"payload");
67 | IS_TRUE(rc);
68 | IS_FALSE(shimClient.error());
69 | sleep(1);
70 | if ( i == 15 || i == 31 || i == 47) {
71 | byte pingreq[] = { 0xC0,0x0 };
72 | shimClient.expect(pingreq,2);
73 | byte pingresp[] = { 0xD0,0x0 };
74 | shimClient.respond(pingresp,2);
75 | }
76 | rc = client.loop();
77 | IS_TRUE(rc);
78 | IS_FALSE(shimClient.error());
79 | }
80 |
81 | END_IT
82 | }
83 |
84 | int test_keepalive_pings_with_inbound_qos0() {
85 | IT("keeps a connection alive that only receives qos0 (takes 1 minute)");
86 |
87 | ShimClient shimClient;
88 | shimClient.setAllowConnect(true);
89 |
90 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
91 | shimClient.respond(connack,4);
92 |
93 | PubSubClient client(server, 1883, callback, shimClient);
94 | int rc = client.connect((char*)"client_test1");
95 | IS_TRUE(rc);
96 |
97 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
98 |
99 | for (int i = 0; i < 50; i++) {
100 | TRACE(i<<":");
101 | sleep(1);
102 | if ( i == 15 || i == 31 || i == 47) {
103 | byte pingreq[] = { 0xC0,0x0 };
104 | shimClient.expect(pingreq,2);
105 | byte pingresp[] = { 0xD0,0x0 };
106 | shimClient.respond(pingresp,2);
107 | }
108 | shimClient.respond(publish,16);
109 | rc = client.loop();
110 | IS_TRUE(rc);
111 | IS_FALSE(shimClient.error());
112 | }
113 |
114 | END_IT
115 | }
116 |
117 | int test_keepalive_no_pings_inbound_qos1() {
118 | IT("does not send pings for connections with inbound qos1 (takes 1 minute)");
119 |
120 | ShimClient shimClient;
121 | shimClient.setAllowConnect(true);
122 |
123 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
124 | shimClient.respond(connack,4);
125 |
126 | PubSubClient client(server, 1883, callback, shimClient);
127 | int rc = client.connect((char*)"client_test1");
128 | IS_TRUE(rc);
129 |
130 | byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
131 | byte puback[] = {0x40,0x2,0x12,0x34};
132 |
133 | for (int i = 0; i < 50; i++) {
134 | shimClient.respond(publish,18);
135 | shimClient.expect(puback,4);
136 | sleep(1);
137 | rc = client.loop();
138 | IS_TRUE(rc);
139 | IS_FALSE(shimClient.error());
140 | }
141 |
142 | END_IT
143 | }
144 |
145 | int test_keepalive_disconnects_hung() {
146 | IT("disconnects a hung connection (takes 30 seconds)");
147 |
148 | ShimClient shimClient;
149 | shimClient.setAllowConnect(true);
150 |
151 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
152 | shimClient.respond(connack,4);
153 |
154 | PubSubClient client(server, 1883, callback, shimClient);
155 | int rc = client.connect((char*)"client_test1");
156 | IS_TRUE(rc);
157 |
158 | byte pingreq[] = { 0xC0,0x0 };
159 | shimClient.expect(pingreq,2);
160 |
161 | for (int i = 0; i < 32; i++) {
162 | sleep(1);
163 | rc = client.loop();
164 | }
165 | IS_FALSE(rc);
166 |
167 | int state = client.state();
168 | IS_TRUE(state == MQTT_CONNECTION_TIMEOUT);
169 |
170 | IS_FALSE(shimClient.error());
171 |
172 | END_IT
173 | }
174 |
175 | int main()
176 | {
177 | SUITE("Keep-alive");
178 | test_keepalive_pings_idle();
179 | test_keepalive_pings_with_outbound_qos0();
180 | test_keepalive_pings_with_inbound_qos0();
181 | test_keepalive_no_pings_inbound_qos1();
182 | test_keepalive_disconnects_hung();
183 |
184 | FINISH
185 | }
186 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/Arduino.h:
--------------------------------------------------------------------------------
1 | #ifndef Arduino_h
2 | #define Arduino_h
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "Print.h"
9 |
10 |
11 | extern "C"{
12 | typedef uint8_t byte ;
13 | typedef uint8_t boolean ;
14 |
15 | /* sketch */
16 | extern void setup( void ) ;
17 | extern void loop( void ) ;
18 | uint32_t millis( void );
19 | }
20 |
21 | #define PROGMEM
22 | #define pgm_read_byte_near(x) *(x)
23 |
24 | #define yield(x) {}
25 |
26 | #endif // Arduino_h
27 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/BDDTest.cpp:
--------------------------------------------------------------------------------
1 | #include "BDDTest.h"
2 | #include "trace.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | int testCount = 0;
9 | int testPasses = 0;
10 | const char* testDescription;
11 |
12 | std::list failureList;
13 |
14 | void bddtest_suite(const char* name) {
15 | LOG(name << "\n");
16 | }
17 |
18 | int bddtest_test(const char* file, int line, const char* assertion, int result) {
19 | if (!result) {
20 | LOG("✗\n");
21 | std::ostringstream os;
22 | os << " ! "<::iterator it = failureList.begin(); it != failureList.end(); it++) {
40 | LOG("\n");
41 | LOG(*it);
42 | LOG("\n");
43 | }
44 |
45 | LOG(std::dec << testPasses << "/" << testCount << " tests passed\n\n");
46 | if (testPasses == testCount) {
47 | return 0;
48 | }
49 | return 1;
50 | }
51 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/BDDTest.h:
--------------------------------------------------------------------------------
1 | #ifndef bddtest_h
2 | #define bddtest_h
3 |
4 | void bddtest_suite(const char* name);
5 | int bddtest_test(const char*, int, const char*, int);
6 | void bddtest_start(const char*);
7 | void bddtest_end();
8 | int bddtest_summary();
9 |
10 | #define SUITE(x) { bddtest_suite(x); }
11 | #define TEST(x) { if (!bddtest_test(__FILE__, __LINE__, #x, (x))) return false; }
12 |
13 | #define IT(x) { bddtest_start(x); }
14 | #define END_IT { bddtest_end();return true;}
15 |
16 | #define FINISH { return bddtest_summary(); }
17 |
18 | #define IS_TRUE(x) TEST(x)
19 | #define IS_FALSE(x) TEST(!(x))
20 | #define IS_EQUAL(x,y) TEST(x==y)
21 | #define IS_NOT_EQUAL(x,y) TEST(x!=y)
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/Buffer.cpp:
--------------------------------------------------------------------------------
1 | #include "Buffer.h"
2 | #include "Arduino.h"
3 |
4 | Buffer::Buffer() {
5 | this->pos = 0;
6 | this->length = 0;
7 | }
8 |
9 | Buffer::Buffer(uint8_t* buf, size_t size) {
10 | this->pos = 0;
11 | this->length = 0;
12 | this->add(buf,size);
13 | }
14 | bool Buffer::available() {
15 | return this->pos < this->length;
16 | }
17 |
18 | uint8_t Buffer::next() {
19 | if (this->available()) {
20 | return this->buffer[this->pos++];
21 | }
22 | return 0;
23 | }
24 |
25 | void Buffer::reset() {
26 | this->pos = 0;
27 | }
28 |
29 | void Buffer::add(uint8_t* buf, size_t size) {
30 | uint16_t i = 0;
31 | for (;ibuffer[this->length++] = buf[i];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/Buffer.h:
--------------------------------------------------------------------------------
1 | #ifndef buffer_h
2 | #define buffer_h
3 |
4 | #include "Arduino.h"
5 |
6 | class Buffer {
7 | private:
8 | uint8_t buffer[2048];
9 | uint16_t pos;
10 | uint16_t length;
11 |
12 | public:
13 | Buffer();
14 | Buffer(uint8_t* buf, size_t size);
15 |
16 | virtual bool available();
17 | virtual uint8_t next();
18 | virtual void reset();
19 |
20 | virtual void add(uint8_t* buf, size_t size);
21 | };
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/Client.h:
--------------------------------------------------------------------------------
1 | #ifndef client_h
2 | #define client_h
3 | #include "IPAddress.h"
4 |
5 | class Client {
6 | public:
7 | virtual int connect(IPAddress ip, uint16_t port) =0;
8 | virtual int connect(const char *host, uint16_t port) =0;
9 | virtual size_t write(uint8_t) =0;
10 | virtual size_t write(const uint8_t *buf, size_t size) =0;
11 | virtual int available() = 0;
12 | virtual int read() = 0;
13 | virtual int read(uint8_t *buf, size_t size) = 0;
14 | virtual int peek() = 0;
15 | virtual void flush() = 0;
16 | virtual void stop() = 0;
17 | virtual uint8_t connected() = 0;
18 | virtual operator bool() = 0;
19 | };
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/IPAddress.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 |
5 | IPAddress::IPAddress()
6 | {
7 | memset(_address, 0, sizeof(_address));
8 | }
9 |
10 | IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
11 | {
12 | _address[0] = first_octet;
13 | _address[1] = second_octet;
14 | _address[2] = third_octet;
15 | _address[3] = fourth_octet;
16 | }
17 |
18 | IPAddress::IPAddress(uint32_t address)
19 | {
20 | memcpy(_address, &address, sizeof(_address));
21 | }
22 |
23 | IPAddress::IPAddress(const uint8_t *address)
24 | {
25 | memcpy(_address, address, sizeof(_address));
26 | }
27 |
28 | IPAddress& IPAddress::operator=(const uint8_t *address)
29 | {
30 | memcpy(_address, address, sizeof(_address));
31 | return *this;
32 | }
33 |
34 | IPAddress& IPAddress::operator=(uint32_t address)
35 | {
36 | memcpy(_address, (const uint8_t *)&address, sizeof(_address));
37 | return *this;
38 | }
39 |
40 | bool IPAddress::operator==(const uint8_t* addr)
41 | {
42 | return memcmp(addr, _address, sizeof(_address)) == 0;
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/IPAddress.h:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * MIT License:
4 | * Copyright (c) 2011 Adrian McEwen
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | * THE SOFTWARE.
22 | *
23 | * adrianm@mcqn.com 1/1/2011
24 | */
25 |
26 | #ifndef IPAddress_h
27 | #define IPAddress_h
28 |
29 |
30 | // A class to make it easier to handle and pass around IP addresses
31 |
32 | class IPAddress {
33 | private:
34 | uint8_t _address[4]; // IPv4 address
35 | // Access the raw byte array containing the address. Because this returns a pointer
36 | // to the internal structure rather than a copy of the address this function should only
37 | // be used when you know that the usage of the returned uint8_t* will be transient and not
38 | // stored.
39 | uint8_t* raw_address() { return _address; };
40 |
41 | public:
42 | // Constructors
43 | IPAddress();
44 | IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
45 | IPAddress(uint32_t address);
46 | IPAddress(const uint8_t *address);
47 |
48 | // Overloaded cast operator to allow IPAddress objects to be used where a pointer
49 | // to a four-byte uint8_t array is expected
50 | operator uint32_t() { return *((uint32_t*)_address); };
51 | bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); };
52 | bool operator==(const uint8_t* addr);
53 |
54 | // Overloaded index operator to allow getting and setting individual octets of the address
55 | uint8_t operator[](int index) const { return _address[index]; };
56 | uint8_t& operator[](int index) { return _address[index]; };
57 |
58 | // Overloaded copy operators to allow initialisation of IPAddress objects from other types
59 | IPAddress& operator=(const uint8_t *address);
60 | IPAddress& operator=(uint32_t address);
61 |
62 |
63 | friend class EthernetClass;
64 | friend class UDP;
65 | friend class Client;
66 | friend class Server;
67 | friend class DhcpClass;
68 | friend class DNSClient;
69 | };
70 |
71 |
72 | #endif
73 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/Print.h:
--------------------------------------------------------------------------------
1 | /*
2 | Print.h - Base class that provides print() and println()
3 | Copyright (c) 2008 David A. Mellis. All right reserved.
4 |
5 | This library is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU Lesser General Public
7 | License as published by the Free Software Foundation; either
8 | version 2.1 of the License, or (at your option) any later version.
9 |
10 | This library is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public
16 | License along with this library; if not, write to the Free Software
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 |
20 | #ifndef Print_h
21 | #define Print_h
22 |
23 | class Print {
24 | public:
25 | virtual size_t write(uint8_t) = 0;
26 | };
27 |
28 | #endif
29 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/ShimClient.cpp:
--------------------------------------------------------------------------------
1 | #include "ShimClient.h"
2 | #include "trace.h"
3 | #include
4 | #include
5 | #include
6 |
7 | extern "C" {
8 | uint32_t millis(void) {
9 | return time(0)*1000;
10 | }
11 | }
12 |
13 | ShimClient::ShimClient() {
14 | this->responseBuffer = new Buffer();
15 | this->expectBuffer = new Buffer();
16 | this->_allowConnect = true;
17 | this->_connected = false;
18 | this->_error = false;
19 | this->expectAnything = true;
20 | this->_received = 0;
21 | this->_expectedPort = 0;
22 | }
23 |
24 | int ShimClient::connect(IPAddress ip, uint16_t port) {
25 | if (this->_allowConnect) {
26 | this->_connected = true;
27 | }
28 | if (this->_expectedPort !=0) {
29 | // if (memcmp(ip,this->_expectedIP,4) != 0) {
30 | // TRACE( "ip mismatch\n");
31 | // this->_error = true;
32 | // }
33 | if (port != this->_expectedPort) {
34 | TRACE( "port mismatch\n");
35 | this->_error = true;
36 | }
37 | }
38 | return this->_connected;
39 | }
40 | int ShimClient::connect(const char *host, uint16_t port) {
41 | if (this->_allowConnect) {
42 | this->_connected = true;
43 | }
44 | if (this->_expectedPort !=0) {
45 | if (strcmp(host,this->_expectedHost) != 0) {
46 | TRACE( "host mismatch\n");
47 | this->_error = true;
48 | }
49 | if (port != this->_expectedPort) {
50 | TRACE( "port mismatch\n");
51 | this->_error = true;
52 | }
53 |
54 | }
55 | return this->_connected;
56 | }
57 | size_t ShimClient::write(uint8_t b) {
58 | this->_received += 1;
59 | TRACE(std::hex << (unsigned int)b);
60 | if (!this->expectAnything) {
61 | if (this->expectBuffer->available()) {
62 | uint8_t expected = this->expectBuffer->next();
63 | if (expected != b) {
64 | this->_error = true;
65 | TRACE("!=" << (unsigned int)expected);
66 | }
67 | } else {
68 | this->_error = true;
69 | }
70 | }
71 | TRACE("\n"<< std::dec);
72 | return 1;
73 | }
74 | size_t ShimClient::write(const uint8_t *buf, size_t size) {
75 | this->_received += size;
76 | TRACE( "[" << std::dec << (unsigned int)(size) << "] ");
77 | uint16_t i=0;
78 | for (;i0) {
80 | TRACE(":");
81 | }
82 | TRACE(std::hex << (unsigned int)(buf[i]));
83 |
84 | if (!this->expectAnything) {
85 | if (this->expectBuffer->available()) {
86 | uint8_t expected = this->expectBuffer->next();
87 | if (expected != buf[i]) {
88 | this->_error = true;
89 | TRACE("!=" << (unsigned int)expected);
90 | }
91 | } else {
92 | this->_error = true;
93 | }
94 | }
95 | }
96 | TRACE("\n"<responseBuffer->available();
101 | }
102 | int ShimClient::read() { return this->responseBuffer->next(); }
103 | int ShimClient::read(uint8_t *buf, size_t size) {
104 | uint16_t i = 0;
105 | for (;iread();
107 | }
108 | return size;
109 | }
110 | int ShimClient::peek() { return 0; }
111 | void ShimClient::flush() {}
112 | void ShimClient::stop() {
113 | this->setConnected(false);
114 | }
115 | uint8_t ShimClient::connected() { return this->_connected; }
116 | ShimClient::operator bool() { return true; }
117 |
118 |
119 | ShimClient* ShimClient::respond(uint8_t *buf, size_t size) {
120 | this->responseBuffer->add(buf,size);
121 | return this;
122 | }
123 |
124 | ShimClient* ShimClient::expect(uint8_t *buf, size_t size) {
125 | this->expectAnything = false;
126 | this->expectBuffer->add(buf,size);
127 | return this;
128 | }
129 |
130 | void ShimClient::setConnected(bool b) {
131 | this->_connected = b;
132 | }
133 | void ShimClient::setAllowConnect(bool b) {
134 | this->_allowConnect = b;
135 | }
136 |
137 | bool ShimClient::error() {
138 | return this->_error;
139 | }
140 |
141 | uint16_t ShimClient::received() {
142 | return this->_received;
143 | }
144 |
145 | void ShimClient::expectConnect(IPAddress ip, uint16_t port) {
146 | this->_expectedIP = ip;
147 | this->_expectedPort = port;
148 | }
149 |
150 | void ShimClient::expectConnect(const char *host, uint16_t port) {
151 | this->_expectedHost = host;
152 | this->_expectedPort = port;
153 | }
154 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/ShimClient.h:
--------------------------------------------------------------------------------
1 | #ifndef shimclient_h
2 | #define shimclient_h
3 |
4 | #include "Arduino.h"
5 | #include "Client.h"
6 | #include "IPAddress.h"
7 | #include "Buffer.h"
8 |
9 |
10 | class ShimClient : public Client {
11 | private:
12 | Buffer* responseBuffer;
13 | Buffer* expectBuffer;
14 | bool _allowConnect;
15 | bool _connected;
16 | bool expectAnything;
17 | bool _error;
18 | uint16_t _received;
19 | IPAddress _expectedIP;
20 | uint16_t _expectedPort;
21 | const char* _expectedHost;
22 |
23 | public:
24 | ShimClient();
25 | virtual int connect(IPAddress ip, uint16_t port);
26 | virtual int connect(const char *host, uint16_t port);
27 | virtual size_t write(uint8_t);
28 | virtual size_t write(const uint8_t *buf, size_t size);
29 | virtual int available();
30 | virtual int read();
31 | virtual int read(uint8_t *buf, size_t size);
32 | virtual int peek();
33 | virtual void flush();
34 | virtual void stop();
35 | virtual uint8_t connected();
36 | virtual operator bool();
37 |
38 | virtual ShimClient* respond(uint8_t *buf, size_t size);
39 | virtual ShimClient* expect(uint8_t *buf, size_t size);
40 |
41 | virtual void expectConnect(IPAddress ip, uint16_t port);
42 | virtual void expectConnect(const char *host, uint16_t port);
43 |
44 | virtual uint16_t received();
45 | virtual bool error();
46 |
47 | virtual void setAllowConnect(bool b);
48 | virtual void setConnected(bool b);
49 | };
50 |
51 | #endif
52 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/Stream.cpp:
--------------------------------------------------------------------------------
1 | #include "Stream.h"
2 | #include "trace.h"
3 | #include
4 | #include
5 |
6 | Stream::Stream() {
7 | this->expectBuffer = new Buffer();
8 | this->_error = false;
9 | this->_written = 0;
10 | }
11 |
12 | size_t Stream::write(uint8_t b) {
13 | this->_written++;
14 | TRACE(std::hex << (unsigned int)b);
15 | if (this->expectBuffer->available()) {
16 | uint8_t expected = this->expectBuffer->next();
17 | if (expected != b) {
18 | this->_error = true;
19 | TRACE("!=" << (unsigned int)expected);
20 | }
21 | } else {
22 | this->_error = true;
23 | }
24 | TRACE("\n"<< std::dec);
25 | return 1;
26 | }
27 |
28 |
29 | bool Stream::error() {
30 | return this->_error;
31 | }
32 |
33 | void Stream::expect(uint8_t *buf, size_t size) {
34 | this->expectBuffer->add(buf,size);
35 | }
36 |
37 | uint16_t Stream::length() {
38 | return this->_written;
39 | }
40 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/Stream.h:
--------------------------------------------------------------------------------
1 | #ifndef Stream_h
2 | #define Stream_h
3 |
4 | #include "Arduino.h"
5 | #include "Buffer.h"
6 |
7 | class Stream {
8 | private:
9 | Buffer* expectBuffer;
10 | bool _error;
11 | uint16_t _written;
12 |
13 | public:
14 | Stream();
15 | virtual size_t write(uint8_t);
16 |
17 | virtual bool error();
18 | virtual void expect(uint8_t *buf, size_t size);
19 | virtual uint16_t length();
20 | };
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/lib/trace.h:
--------------------------------------------------------------------------------
1 | #ifndef trace_h
2 | #define trace_h
3 | #include
4 |
5 | #include
6 |
7 | #define LOG(x) {std::cout << x << std::flush; }
8 | #define TRACE(x) {if (getenv("TRACE")) { std::cout << x << std::flush; }}
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/publish_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 |
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | void callback(char* topic, byte* payload, unsigned int length) {
11 | // handle message arrived
12 | }
13 |
14 | int test_publish() {
15 | IT("publishes a null-terminated string");
16 | ShimClient shimClient;
17 | shimClient.setAllowConnect(true);
18 |
19 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
20 | shimClient.respond(connack,4);
21 |
22 | PubSubClient client(server, 1883, callback, shimClient);
23 | int rc = client.connect((char*)"client_test1");
24 | IS_TRUE(rc);
25 |
26 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
27 | shimClient.expect(publish,16);
28 |
29 | rc = client.publish((char*)"topic",(char*)"payload");
30 | IS_TRUE(rc);
31 |
32 | IS_FALSE(shimClient.error());
33 |
34 | END_IT
35 | }
36 |
37 |
38 | int test_publish_bytes() {
39 | IT("publishes a byte array");
40 | ShimClient shimClient;
41 | shimClient.setAllowConnect(true);
42 |
43 | byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
44 | int length = 5;
45 |
46 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
47 | shimClient.respond(connack,4);
48 |
49 | PubSubClient client(server, 1883, callback, shimClient);
50 | int rc = client.connect((char*)"client_test1");
51 | IS_TRUE(rc);
52 |
53 | byte publish[] = {0x30,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
54 | shimClient.expect(publish,14);
55 |
56 | rc = client.publish((char*)"topic",payload,length);
57 | IS_TRUE(rc);
58 |
59 | IS_FALSE(shimClient.error());
60 |
61 | END_IT
62 | }
63 |
64 |
65 | int test_publish_retained() {
66 | IT("publishes retained - 1");
67 | ShimClient shimClient;
68 | shimClient.setAllowConnect(true);
69 |
70 | byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
71 | int length = 5;
72 |
73 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
74 | shimClient.respond(connack,4);
75 |
76 | PubSubClient client(server, 1883, callback, shimClient);
77 | int rc = client.connect((char*)"client_test1");
78 | IS_TRUE(rc);
79 |
80 | byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
81 | shimClient.expect(publish,14);
82 |
83 | rc = client.publish((char*)"topic",payload,length,true);
84 | IS_TRUE(rc);
85 |
86 | IS_FALSE(shimClient.error());
87 |
88 | END_IT
89 | }
90 |
91 | int test_publish_retained_2() {
92 | IT("publishes retained - 2");
93 | ShimClient shimClient;
94 | shimClient.setAllowConnect(true);
95 |
96 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
97 | shimClient.respond(connack,4);
98 |
99 | PubSubClient client(server, 1883, callback, shimClient);
100 | int rc = client.connect((char*)"client_test1");
101 | IS_TRUE(rc);
102 |
103 | byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,'A','B','C','D','E'};
104 | shimClient.expect(publish,14);
105 |
106 | rc = client.publish((char*)"topic",(char*)"ABCDE",true);
107 | IS_TRUE(rc);
108 |
109 | IS_FALSE(shimClient.error());
110 |
111 | END_IT
112 | }
113 |
114 | int test_publish_not_connected() {
115 | IT("publish fails when not connected");
116 | ShimClient shimClient;
117 |
118 | PubSubClient client(server, 1883, callback, shimClient);
119 |
120 | int rc = client.publish((char*)"topic",(char*)"payload");
121 | IS_FALSE(rc);
122 |
123 | IS_FALSE(shimClient.error());
124 |
125 | END_IT
126 | }
127 |
128 | int test_publish_too_long() {
129 | IT("publish fails when topic/payload are too long");
130 | ShimClient shimClient;
131 | shimClient.setAllowConnect(true);
132 |
133 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
134 | shimClient.respond(connack,4);
135 |
136 | PubSubClient client(server, 1883, callback, shimClient);
137 | client.setBufferSize(128);
138 | int rc = client.connect((char*)"client_test1");
139 | IS_TRUE(rc);
140 |
141 | // 0 1 2 3 4 5 6 7 8 9 0 1 2
142 | rc = client.publish((char*)"topic",(char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
143 | IS_FALSE(rc);
144 |
145 | IS_FALSE(shimClient.error());
146 |
147 | END_IT
148 | }
149 |
150 | int test_publish_P() {
151 | IT("publishes using PROGMEM");
152 | ShimClient shimClient;
153 | shimClient.setAllowConnect(true);
154 |
155 | byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
156 | int length = 5;
157 |
158 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
159 | shimClient.respond(connack,4);
160 |
161 | PubSubClient client(server, 1883, callback, shimClient);
162 | int rc = client.connect((char*)"client_test1");
163 | IS_TRUE(rc);
164 |
165 | byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
166 | shimClient.expect(publish,14);
167 |
168 | rc = client.publish_P((char*)"topic",payload,length,true);
169 | IS_TRUE(rc);
170 |
171 | IS_FALSE(shimClient.error());
172 |
173 | END_IT
174 | }
175 |
176 |
177 |
178 |
179 | int main()
180 | {
181 | SUITE("Publish");
182 | test_publish();
183 | test_publish_bytes();
184 | test_publish_retained();
185 | test_publish_retained_2();
186 | test_publish_not_connected();
187 | test_publish_too_long();
188 | test_publish_P();
189 |
190 | FINISH
191 | }
192 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/receive_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 |
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | bool callback_called = false;
11 | char lastTopic[1024];
12 | char lastPayload[1024];
13 | unsigned int lastLength;
14 |
15 | void reset_callback() {
16 | callback_called = false;
17 | lastTopic[0] = '\0';
18 | lastPayload[0] = '\0';
19 | lastLength = 0;
20 | }
21 |
22 | void callback(char* topic, byte* payload, unsigned int length) {
23 | TRACE("Callback received topic=[" << topic << "] length=" << length << "\n")
24 | callback_called = true;
25 | strcpy(lastTopic,topic);
26 | memcpy(lastPayload,payload,length);
27 | lastLength = length;
28 | }
29 |
30 | int test_receive_callback() {
31 | IT("receives a callback message");
32 | reset_callback();
33 |
34 | ShimClient shimClient;
35 | shimClient.setAllowConnect(true);
36 |
37 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
38 | shimClient.respond(connack,4);
39 |
40 | PubSubClient client(server, 1883, callback, shimClient);
41 | int rc = client.connect((char*)"client_test1");
42 | IS_TRUE(rc);
43 |
44 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
45 | shimClient.respond(publish,16);
46 |
47 | rc = client.loop();
48 |
49 | IS_TRUE(rc);
50 |
51 | IS_TRUE(callback_called);
52 | IS_TRUE(strcmp(lastTopic,"topic")==0);
53 | IS_TRUE(memcmp(lastPayload,"payload",7)==0);
54 | IS_TRUE(lastLength == 7);
55 |
56 | IS_FALSE(shimClient.error());
57 |
58 | END_IT
59 | }
60 |
61 | int test_receive_stream() {
62 | IT("receives a streamed callback message");
63 | reset_callback();
64 |
65 | Stream stream;
66 | stream.expect((uint8_t*)"payload",7);
67 |
68 | ShimClient shimClient;
69 | shimClient.setAllowConnect(true);
70 |
71 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
72 | shimClient.respond(connack,4);
73 |
74 | PubSubClient client(server, 1883, callback, shimClient, stream);
75 | int rc = client.connect((char*)"client_test1");
76 | IS_TRUE(rc);
77 |
78 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
79 | shimClient.respond(publish,16);
80 |
81 | rc = client.loop();
82 |
83 | IS_TRUE(rc);
84 |
85 | IS_TRUE(callback_called);
86 | IS_TRUE(strcmp(lastTopic,"topic")==0);
87 | IS_TRUE(lastLength == 7);
88 |
89 | IS_FALSE(stream.error());
90 | IS_FALSE(shimClient.error());
91 |
92 | END_IT
93 | }
94 |
95 | int test_receive_max_sized_message() {
96 | IT("receives an max-sized message");
97 | reset_callback();
98 |
99 | ShimClient shimClient;
100 | shimClient.setAllowConnect(true);
101 |
102 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
103 | shimClient.respond(connack,4);
104 |
105 | PubSubClient client(server, 1883, callback, shimClient);
106 | int length = 80; // If this is changed to > 128 then the publish packet below
107 | // is no longer valid as it assumes the remaining length
108 | // is a single-byte. Don't make that mistake like I just
109 | // did and lose a whole evening tracking down the issue.
110 | client.setBufferSize(length);
111 | int rc = client.connect((char*)"client_test1");
112 | IS_TRUE(rc);
113 |
114 |
115 | byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
116 | byte bigPublish[length];
117 | memset(bigPublish,'A',length);
118 | bigPublish[length] = 'B';
119 | memcpy(bigPublish,publish,16);
120 | shimClient.respond(bigPublish,length);
121 |
122 | rc = client.loop();
123 |
124 | IS_TRUE(rc);
125 |
126 | IS_TRUE(callback_called);
127 | IS_TRUE(strcmp(lastTopic,"topic")==0);
128 | IS_TRUE(lastLength == length-9);
129 | IS_TRUE(memcmp(lastPayload,bigPublish+9,lastLength)==0);
130 |
131 | IS_FALSE(shimClient.error());
132 |
133 | END_IT
134 | }
135 |
136 | int test_receive_oversized_message() {
137 | IT("drops an oversized message");
138 | reset_callback();
139 |
140 | ShimClient shimClient;
141 | shimClient.setAllowConnect(true);
142 |
143 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
144 | shimClient.respond(connack,4);
145 |
146 | int length = 80; // See comment in test_receive_max_sized_message before changing this value
147 |
148 | PubSubClient client(server, 1883, callback, shimClient);
149 | client.setBufferSize(length-1);
150 | int rc = client.connect((char*)"client_test1");
151 | IS_TRUE(rc);
152 |
153 | byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
154 | byte bigPublish[length];
155 | memset(bigPublish,'A',length);
156 | bigPublish[length] = 'B';
157 | memcpy(bigPublish,publish,16);
158 | shimClient.respond(bigPublish,length);
159 |
160 | rc = client.loop();
161 |
162 | IS_TRUE(rc);
163 |
164 | IS_FALSE(callback_called);
165 |
166 | IS_FALSE(shimClient.error());
167 |
168 | END_IT
169 | }
170 |
171 | int test_drop_invalid_remaining_length_message() {
172 | IT("drops invalid remaining length message");
173 | reset_callback();
174 |
175 | ShimClient shimClient;
176 | shimClient.setAllowConnect(true);
177 |
178 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
179 | shimClient.respond(connack,4);
180 |
181 | PubSubClient client(server, 1883, callback, shimClient);
182 | int rc = client.connect((char*)"client_test1");
183 | IS_TRUE(rc);
184 |
185 | byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x01,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
186 | shimClient.respond(publish,20);
187 |
188 | rc = client.loop();
189 |
190 | IS_FALSE(rc);
191 |
192 | IS_FALSE(callback_called);
193 |
194 | IS_FALSE(shimClient.error());
195 |
196 | END_IT
197 | }
198 |
199 | int test_resize_buffer() {
200 | IT("receives a message larger than the default maximum");
201 | reset_callback();
202 |
203 | ShimClient shimClient;
204 | shimClient.setAllowConnect(true);
205 |
206 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
207 | shimClient.respond(connack,4);
208 |
209 | int length = 80; // See comment in test_receive_max_sized_message before changing this value
210 |
211 | PubSubClient client(server, 1883, callback, shimClient);
212 | client.setBufferSize(length-1);
213 | int rc = client.connect((char*)"client_test1");
214 | IS_TRUE(rc);
215 |
216 | byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
217 | byte bigPublish[length];
218 | memset(bigPublish,'A',length);
219 | bigPublish[length] = 'B';
220 | memcpy(bigPublish,publish,16);
221 | // Send it twice
222 | shimClient.respond(bigPublish,length);
223 | shimClient.respond(bigPublish,length);
224 |
225 | rc = client.loop();
226 | IS_TRUE(rc);
227 |
228 | // First message fails as it is too big
229 | IS_FALSE(callback_called);
230 |
231 | // Resize the buffer
232 | client.setBufferSize(length);
233 |
234 | rc = client.loop();
235 | IS_TRUE(rc);
236 |
237 | IS_TRUE(callback_called);
238 |
239 | IS_TRUE(strcmp(lastTopic,"topic")==0);
240 | IS_TRUE(lastLength == length-9);
241 | IS_TRUE(memcmp(lastPayload,bigPublish+9,lastLength)==0);
242 |
243 | IS_FALSE(shimClient.error());
244 |
245 | END_IT
246 | }
247 |
248 |
249 | int test_receive_oversized_stream_message() {
250 | IT("receive an oversized streamed message");
251 | reset_callback();
252 |
253 | Stream stream;
254 |
255 | ShimClient shimClient;
256 | shimClient.setAllowConnect(true);
257 |
258 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
259 | shimClient.respond(connack,4);
260 |
261 | int length = 80; // See comment in test_receive_max_sized_message before changing this value
262 |
263 | PubSubClient client(server, 1883, callback, shimClient, stream);
264 | client.setBufferSize(length-1);
265 | int rc = client.connect((char*)"client_test1");
266 | IS_TRUE(rc);
267 |
268 | byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
269 |
270 | byte bigPublish[length];
271 | memset(bigPublish,'A',length);
272 | bigPublish[length] = 'B';
273 | memcpy(bigPublish,publish,16);
274 |
275 | shimClient.respond(bigPublish,length);
276 | stream.expect(bigPublish+9,length-9);
277 |
278 | rc = client.loop();
279 |
280 | IS_TRUE(rc);
281 |
282 | IS_TRUE(callback_called);
283 | IS_TRUE(strcmp(lastTopic,"topic")==0);
284 |
285 | IS_TRUE(lastLength == length-10);
286 |
287 | IS_FALSE(stream.error());
288 | IS_FALSE(shimClient.error());
289 |
290 | END_IT
291 | }
292 |
293 | int test_receive_qos1() {
294 | IT("receives a qos1 message");
295 | reset_callback();
296 |
297 | ShimClient shimClient;
298 | shimClient.setAllowConnect(true);
299 |
300 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
301 | shimClient.respond(connack,4);
302 |
303 | PubSubClient client(server, 1883, callback, shimClient);
304 | int rc = client.connect((char*)"client_test1");
305 | IS_TRUE(rc);
306 |
307 | byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
308 | shimClient.respond(publish,18);
309 |
310 | byte puback[] = {0x40,0x2,0x12,0x34};
311 | shimClient.expect(puback,4);
312 |
313 | rc = client.loop();
314 |
315 | IS_TRUE(rc);
316 |
317 | IS_TRUE(callback_called);
318 | IS_TRUE(strcmp(lastTopic,"topic")==0);
319 | IS_TRUE(memcmp(lastPayload,"payload",7)==0);
320 | IS_TRUE(lastLength == 7);
321 |
322 | IS_FALSE(shimClient.error());
323 |
324 | END_IT
325 | }
326 |
327 | int main()
328 | {
329 | SUITE("Receive");
330 | test_receive_callback();
331 | test_receive_stream();
332 | test_receive_max_sized_message();
333 | test_drop_invalid_remaining_length_message();
334 | test_receive_oversized_message();
335 | test_resize_buffer();
336 | test_receive_oversized_stream_message();
337 | test_receive_qos1();
338 |
339 | FINISH
340 | }
341 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/src/subscribe_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 |
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | void callback(char* topic, byte* payload, unsigned int length) {
11 | // handle message arrived
12 | }
13 |
14 | int test_subscribe_no_qos() {
15 | IT("subscribe without qos defaults to 0");
16 | ShimClient shimClient;
17 | shimClient.setAllowConnect(true);
18 |
19 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
20 | shimClient.respond(connack,4);
21 |
22 | PubSubClient client(server, 1883, callback, shimClient);
23 | int rc = client.connect((char*)"client_test1");
24 | IS_TRUE(rc);
25 |
26 | byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x0 };
27 | shimClient.expect(subscribe,12);
28 | byte suback[] = { 0x90,0x3,0x0,0x2,0x0 };
29 | shimClient.respond(suback,5);
30 |
31 | rc = client.subscribe((char*)"topic");
32 | IS_TRUE(rc);
33 |
34 | IS_FALSE(shimClient.error());
35 |
36 | END_IT
37 | }
38 |
39 | int test_subscribe_qos_1() {
40 | IT("subscribes qos 1");
41 | ShimClient shimClient;
42 | shimClient.setAllowConnect(true);
43 |
44 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
45 | shimClient.respond(connack,4);
46 |
47 | PubSubClient client(server, 1883, callback, shimClient);
48 | int rc = client.connect((char*)"client_test1");
49 | IS_TRUE(rc);
50 |
51 | byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1 };
52 | shimClient.expect(subscribe,12);
53 | byte suback[] = { 0x90,0x3,0x0,0x2,0x1 };
54 | shimClient.respond(suback,5);
55 |
56 | rc = client.subscribe((char*)"topic",1);
57 | IS_TRUE(rc);
58 |
59 | IS_FALSE(shimClient.error());
60 |
61 | END_IT
62 | }
63 |
64 | int test_subscribe_not_connected() {
65 | IT("subscribe fails when not connected");
66 | ShimClient shimClient;
67 |
68 | PubSubClient client(server, 1883, callback, shimClient);
69 |
70 | int rc = client.subscribe((char*)"topic");
71 | IS_FALSE(rc);
72 |
73 | IS_FALSE(shimClient.error());
74 |
75 | END_IT
76 | }
77 |
78 | int test_subscribe_invalid_qos() {
79 | IT("subscribe fails with invalid qos values");
80 | ShimClient shimClient;
81 | shimClient.setAllowConnect(true);
82 |
83 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
84 | shimClient.respond(connack,4);
85 |
86 | PubSubClient client(server, 1883, callback, shimClient);
87 | int rc = client.connect((char*)"client_test1");
88 | IS_TRUE(rc);
89 |
90 | rc = client.subscribe((char*)"topic",2);
91 | IS_FALSE(rc);
92 | rc = client.subscribe((char*)"topic",254);
93 | IS_FALSE(rc);
94 |
95 | IS_FALSE(shimClient.error());
96 |
97 | END_IT
98 | }
99 |
100 | int test_subscribe_too_long() {
101 | IT("subscribe fails with too long topic");
102 | ShimClient shimClient;
103 | shimClient.setAllowConnect(true);
104 |
105 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
106 | shimClient.respond(connack,4);
107 |
108 | PubSubClient client(server, 1883, callback, shimClient);
109 | client.setBufferSize(128);
110 | int rc = client.connect((char*)"client_test1");
111 | IS_TRUE(rc);
112 |
113 | // max length should be allowed
114 | // 0 1 2 3 4 5 6 7 8 9 0 1 2
115 | rc = client.subscribe((char*)"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789");
116 | IS_TRUE(rc);
117 |
118 | // 0 1 2 3 4 5 6 7 8 9 0 1 2
119 | rc = client.subscribe((char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
120 | IS_FALSE(rc);
121 |
122 | IS_FALSE(shimClient.error());
123 |
124 | END_IT
125 | }
126 |
127 |
128 | int test_unsubscribe() {
129 | IT("unsubscribes");
130 | ShimClient shimClient;
131 | shimClient.setAllowConnect(true);
132 |
133 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
134 | shimClient.respond(connack,4);
135 |
136 | PubSubClient client(server, 1883, callback, shimClient);
137 | int rc = client.connect((char*)"client_test1");
138 | IS_TRUE(rc);
139 |
140 | byte unsubscribe[] = { 0xA2,0x9,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63 };
141 | shimClient.expect(unsubscribe,12);
142 | byte unsuback[] = { 0xB0,0x2,0x0,0x2 };
143 | shimClient.respond(unsuback,4);
144 |
145 | rc = client.unsubscribe((char*)"topic");
146 | IS_TRUE(rc);
147 |
148 | IS_FALSE(shimClient.error());
149 |
150 | END_IT
151 | }
152 |
153 | int test_unsubscribe_not_connected() {
154 | IT("unsubscribe fails when not connected");
155 | ShimClient shimClient;
156 |
157 | PubSubClient client(server, 1883, callback, shimClient);
158 |
159 | int rc = client.unsubscribe((char*)"topic");
160 | IS_FALSE(rc);
161 |
162 | IS_FALSE(shimClient.error());
163 |
164 | END_IT
165 | }
166 |
167 | int main()
168 | {
169 | SUITE("Subscribe");
170 | test_subscribe_no_qos();
171 | test_subscribe_qos_1();
172 | test_subscribe_not_connected();
173 | test_subscribe_invalid_qos();
174 | test_subscribe_too_long();
175 | test_unsubscribe();
176 | test_unsubscribe_not_connected();
177 | FINISH
178 | }
179 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/testcases/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Solar2MQTT/dcb6b39a7d139c9b2e8a240caab1f3bd3bac7da5/lib/pubsubclient-2.8.13/tests/testcases/__init__.py
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/testcases/mqtt_basic.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import settings
3 | import time
4 | import mosquitto
5 |
6 |
7 | def on_message(mosq, obj, msg):
8 | obj.message_queue.append(msg)
9 |
10 |
11 | class mqtt_basic(unittest.TestCase):
12 |
13 | message_queue = []
14 |
15 | @classmethod
16 | def setUpClass(self):
17 | self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self)
18 | self.client.connect(settings.server_ip)
19 | self.client.on_message = on_message
20 | self.client.subscribe("outTopic", 0)
21 |
22 | @classmethod
23 | def tearDownClass(self):
24 | self.client.disconnect()
25 |
26 | def test_one(self):
27 | i = 30
28 | while len(self.message_queue) == 0 and i > 0:
29 | self.client.loop()
30 | time.sleep(0.5)
31 | i -= 1
32 | self.assertTrue(i > 0, "message receive timed-out")
33 | self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
34 | msg = self.message_queue[0]
35 | self.assertEqual(msg.mid, 0, "message id not 0")
36 | self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
37 | self.assertEqual(msg.payload, "hello world")
38 | self.assertEqual(msg.qos, 0, "message qos not 0")
39 | self.assertEqual(msg.retain, False, "message retain flag incorrect")
40 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/testcases/mqtt_publish_in_callback.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import settings
3 | import time
4 | import mosquitto
5 |
6 |
7 | def on_message(mosq, obj, msg):
8 | obj.message_queue.append(msg)
9 |
10 |
11 | class mqtt_publish_in_callback(unittest.TestCase):
12 |
13 | message_queue = []
14 |
15 | @classmethod
16 | def setUpClass(self):
17 | self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self)
18 | self.client.connect(settings.server_ip)
19 | self.client.on_message = on_message
20 | self.client.subscribe("outTopic", 0)
21 |
22 | @classmethod
23 | def tearDownClass(self):
24 | self.client.disconnect()
25 |
26 | def test_connect(self):
27 | i = 30
28 | while len(self.message_queue) == 0 and i > 0:
29 | self.client.loop()
30 | time.sleep(0.5)
31 | i -= 1
32 | self.assertTrue(i > 0, "message receive timed-out")
33 | self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
34 | msg = self.message_queue.pop(0)
35 | self.assertEqual(msg.mid, 0, "message id not 0")
36 | self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
37 | self.assertEqual(msg.payload, "hello world")
38 | self.assertEqual(msg.qos, 0, "message qos not 0")
39 | self.assertEqual(msg.retain, False, "message retain flag incorrect")
40 |
41 | def test_publish(self):
42 | self.assertEqual(len(self.message_queue), 0, "message queue not empty")
43 | payload = "abcdefghij"
44 | self.client.publish("inTopic", payload)
45 |
46 | i = 30
47 | while len(self.message_queue) == 0 and i > 0:
48 | self.client.loop()
49 | time.sleep(0.5)
50 | i -= 1
51 |
52 | self.assertTrue(i > 0, "message receive timed-out")
53 | self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
54 | msg = self.message_queue.pop(0)
55 | self.assertEqual(msg.mid, 0, "message id not 0")
56 | self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
57 | self.assertEqual(msg.payload, payload)
58 | self.assertEqual(msg.qos, 0, "message qos not 0")
59 | self.assertEqual(msg.retain, False, "message retain flag incorrect")
60 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/testcases/settings.py:
--------------------------------------------------------------------------------
1 | server_ip = "172.16.0.2"
2 | arduino_ip = "172.16.0.100"
3 |
--------------------------------------------------------------------------------
/lib/pubsubclient-2.8.13/tests/testsuite.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import os.path
4 | import sys
5 | import shutil
6 | from subprocess import call
7 | import importlib
8 | import unittest
9 | import re
10 |
11 | from testcases import settings
12 |
13 |
14 | class Workspace(object):
15 |
16 | def __init__(self):
17 | self.root_dir = os.getcwd()
18 | self.build_dir = os.path.join(self.root_dir, "tmpbin")
19 | self.log_dir = os.path.join(self.root_dir, "logs")
20 | self.tests_dir = os.path.join(self.root_dir, "testcases")
21 | self.examples_dir = os.path.join(self.root_dir, "../PubSubClient/examples")
22 | self.examples = []
23 | self.tests = []
24 | if not os.path.isdir("../PubSubClient"):
25 | raise Exception("Cannot find PubSubClient library")
26 | try:
27 | return __import__('ino')
28 | except ImportError:
29 | raise Exception("ino tool not installed")
30 |
31 | def init(self):
32 | if os.path.isdir(self.build_dir):
33 | shutil.rmtree(self.build_dir)
34 | os.mkdir(self.build_dir)
35 | if os.path.isdir(self.log_dir):
36 | shutil.rmtree(self.log_dir)
37 | os.mkdir(self.log_dir)
38 |
39 | os.chdir(self.build_dir)
40 | call(["ino", "init"])
41 |
42 | shutil.copytree("../../PubSubClient", "lib/PubSubClient")
43 |
44 | filenames = []
45 | for root, dirs, files in os.walk(self.examples_dir):
46 | filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")]
47 | filenames.sort()
48 | for e in filenames:
49 | self.examples.append(Sketch(self, e))
50 |
51 | filenames = []
52 | for root, dirs, files in os.walk(self.tests_dir):
53 | filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")]
54 | filenames.sort()
55 | for e in filenames:
56 | self.tests.append(Sketch(self, e))
57 |
58 | def clean(self):
59 | shutil.rmtree(self.build_dir)
60 |
61 |
62 | class Sketch(object):
63 | def __init__(self, wksp, fn):
64 | self.w = wksp
65 | self.filename = fn
66 | self.basename = os.path.basename(self.filename)
67 | self.build_log = os.path.join(self.w.log_dir, "%s.log" % (os.path.basename(self.filename),))
68 | self.build_err_log = os.path.join(self.w.log_dir, "%s.err.log" % (os.path.basename(self.filename),))
69 | self.build_upload_log = os.path.join(self.w.log_dir, "%s.upload.log" % (os.path.basename(self.filename),))
70 |
71 | def build(self):
72 | sys.stdout.write(" Build: ")
73 | sys.stdout.flush()
74 |
75 | # Copy sketch over, replacing IP addresses as necessary
76 | fin = open(self.filename, "r")
77 | lines = fin.readlines()
78 | fin.close()
79 | fout = open(os.path.join(self.w.build_dir, "src", "sketch.ino"), "w")
80 | for l in lines:
81 | if re.match(r"^byte server\[\] = {", l):
82 | fout.write("byte server[] = { %s };\n" % (settings.server_ip.replace(".", ", "),))
83 | elif re.match(r"^byte ip\[\] = {", l):
84 | fout.write("byte ip[] = { %s };\n" % (settings.arduino_ip.replace(".", ", "),))
85 | else:
86 | fout.write(l)
87 | fout.flush()
88 | fout.close()
89 |
90 | # Run build
91 | fout = open(self.build_log, "w")
92 | ferr = open(self.build_err_log, "w")
93 | rc = call(["ino", "build"], stdout=fout, stderr=ferr)
94 | fout.close()
95 | ferr.close()
96 | if rc == 0:
97 | sys.stdout.write("pass")
98 | sys.stdout.write("\n")
99 | return True
100 | else:
101 | sys.stdout.write("fail")
102 | sys.stdout.write("\n")
103 | with open(self.build_err_log) as f:
104 | for line in f:
105 | print(" " + line)
106 | return False
107 |
108 | def upload(self):
109 | sys.stdout.write(" Upload: ")
110 | sys.stdout.flush()
111 | fout = open(self.build_upload_log, "w")
112 | rc = call(["ino", "upload"], stdout=fout, stderr=fout)
113 | fout.close()
114 | if rc == 0:
115 | sys.stdout.write("pass")
116 | sys.stdout.write("\n")
117 | return True
118 | else:
119 | sys.stdout.write("fail")
120 | sys.stdout.write("\n")
121 | with open(self.build_upload_log) as f:
122 | for line in f:
123 | print(" " + line)
124 | return False
125 |
126 | def test(self):
127 | # import the matching test case, if it exists
128 | try:
129 | basename = os.path.basename(self.filename)[:-4]
130 | i = importlib.import_module("testcases." + basename)
131 | except:
132 | sys.stdout.write(" Test: no tests found")
133 | sys.stdout.write("\n")
134 | return
135 | c = getattr(i, basename)
136 |
137 | testmethods = [m for m in dir(c) if m.startswith("test_")]
138 | testmethods.sort()
139 | tests = []
140 | for m in testmethods:
141 | tests.append(c(m))
142 |
143 | result = unittest.TestResult()
144 | c.setUpClass()
145 | if self.upload():
146 | sys.stdout.write(" Test: ")
147 | sys.stdout.flush()
148 | for t in tests:
149 | t.run(result)
150 | print(str(result.testsRun - len(result.failures) - len(result.errors)) + "/" + str(result.testsRun))
151 | if not result.wasSuccessful():
152 | if len(result.failures) > 0:
153 | for f in result.failures:
154 | print("-- " + str(f[0]))
155 | print(f[1])
156 | if len(result.errors) > 0:
157 | print(" Errors:")
158 | for f in result.errors:
159 | print("-- " + str(f[0]))
160 | print(f[1])
161 | c.tearDownClass()
162 |
163 |
164 | if __name__ == '__main__':
165 | run_tests = True
166 |
167 | w = Workspace()
168 | w.init()
169 |
170 | for e in w.examples:
171 | print("--------------------------------------")
172 | print("[" + e.basename + "]")
173 | if e.build() and run_tests:
174 | e.test()
175 | for e in w.tests:
176 | print("--------------------------------------")
177 | print("[" + e.basename + "]")
178 | if e.build() and run_tests:
179 | e.test()
180 |
181 | w.clean()
182 |
--------------------------------------------------------------------------------
/platformio.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [env]
12 | platform = espressif8266@4.2.1
13 | framework = arduino
14 | monitor_speed = 115200
15 | custom_prog_version = 1.2.0-Pre4A6
16 | build_flags =
17 | -DVERSION=${this.custom_prog_version}
18 | -DPIO_SRC_NAM="Solar2MQTT"
19 | -DESP8266 -DATOMIC_FS_UPDATE
20 |
21 | extra_scripts = pre:tools/mini_html.py
22 | pre:tools/pre_compile.py
23 | post:tools/post_compile.py
24 | lib_deps =
25 | ;bblanchon/ArduinoJson @ ^6.21.2
26 | bblanchon/ArduinoJson @ ^7.2.0
27 | esphome/ESPAsyncTCP-esphome @ 2.0.0
28 | mathieucarbou/ESPAsyncWebServer @ ^3.3.16
29 | mathieucarbou/WebSerialLite@^6.2.0
30 | alanswx/ESPAsyncWiFiManager @ ^0.31.0
31 | plerup/EspSoftwareSerial @ ^8.2.0
32 | https://github.com/dok-net/ghostl
33 | robtillaart/CRC@^1.0.3
34 | 4-20ma/ModbusMaster@^2.0.1
35 | paulstoffregen/OneWire@^2.3.7
36 | milesburton/DallasTemperature@^3.11.0
37 |
38 | [env:d1_mini]
39 | board = d1_mini
40 | board_build.ldscript = eagle.flash.4m.ld
41 | build_flags = ${env.build_flags}
42 | custom_hardwareserial = false
43 | monitor_filters = esp8266_exception_decoder, default, time, printable, colorize
44 | upload_speed = 921600
45 |
46 | [env:WiFi-Dongle]
47 | board = esp12e
48 | board_build.ldscript = eagle.flash.4m.ld
49 | custom_hardwareserial = true
50 | build_flags = ${env.build_flags}
51 | custom_prog_version = ${env.custom_prog_version}
52 | monitor_filters = esp8266_exception_decoder, default, time, printable, colorize
53 | upload_speed = 921600
54 |
55 | [env:esp01_1m]
56 | board = esp01_1m
57 | board_build.ldscript = eagle.flash.1m.ld
58 | custom_hardwareserial = true
59 | build_flags = ${env.build_flags}
60 | custom_prog_version = ${env.custom_prog_version}
61 | monitor_filters = esp8266_exception_decoder, default, time, printable, colorize
62 |
--------------------------------------------------------------------------------
/src/PI_Serial/PI_Serial.h:
--------------------------------------------------------------------------------
1 | #include "SoftwareSerial.h"
2 | #ifndef PI_SERIAL_H
3 | #define PI_SERIAL_H
4 | #include "vector"
5 | #include
6 | #include
7 | extern JsonObject deviceJson;
8 | extern JsonObject staticData;
9 | extern JsonObject liveData;
10 |
11 | class PI_Serial
12 | {
13 | public:
14 | const char *startChar = "(";
15 | const char *delimiter = " ";
16 | bool requestStaticData = true;
17 | byte protocol = NoD;
18 | bool connection = false;
19 |
20 | struct
21 | {
22 | struct
23 | {
24 | // static
25 | String qpi;
26 | String qall;
27 | String qpiri;
28 | String qmn;
29 | String qflag;
30 | // dynamic
31 | String q1;
32 | String qpigs;
33 | String qpigs2;
34 | String qmod;
35 | String qt;
36 | String qet;
37 | String qey;
38 | String qem;
39 | String qed;
40 | String qlt;
41 | String qly;
42 | String qlm;
43 | String qld;
44 | String commandAnswer;
45 | String qpiws;
46 | } raw;
47 |
48 | } get;
49 |
50 | struct
51 | {
52 |
53 | } alarm;
54 |
55 | /**
56 | * @brief Construct a new PI_Serial object
57 | *
58 | * @param serialIntf UART interface BMS is connected to
59 | */
60 | PI_Serial(int rx, int tx);
61 |
62 | /**
63 | * @brief Initializes this driver
64 | * @details Configures the serial peripheral and pre-loads the transmit buffer with command-independent bytes
65 | */
66 | bool Init();
67 |
68 | /**
69 | * @brief Updating the Data from the BMS
70 | */
71 | bool loop();
72 |
73 | /**
74 | * @brief Send custom command to the device
75 | */
76 | String sendCommand(String command);
77 |
78 | /**
79 | * @brief
80 | *
81 | */
82 | bool sendCustomCommand();
83 |
84 | /**
85 | * @brief callback function
86 | *
87 | */
88 | void callback(std::function func);
89 | std::function requestCallback;
90 |
91 | enum protocolType
92 | {
93 | NoD,
94 | PI18,
95 | PI30,
96 | MODBUS_MUST
97 | };
98 |
99 | private:
100 | unsigned int serialIntfBaud;
101 |
102 | unsigned long previousTime = 0;
103 | unsigned long delayTime = 100;
104 | byte requestCounter = 0;
105 |
106 | long long int connectionCounter = 0;
107 |
108 | byte qexCounter = 0;
109 |
110 | String customCommandBuffer;
111 |
112 | MODBUS *modbus;
113 |
114 | /**
115 | * @brief get the crc from a string
116 | */
117 | uint16_t getCRC(String data);
118 |
119 | /**
120 | * @brief get the crc from a string
121 | */
122 | byte getCHK(String data);
123 |
124 | /**
125 | * @brief function for autodetect the inverter
126 | * @details ask all modes and sort it to a protocol
127 | */
128 | void autoDetect();
129 |
130 | /**
131 | * @brief Sends a complete packet with the specified command
132 | * @details calculates the checksum and sends the command over the specified serial connection
133 | */
134 | String requestData(String command);
135 |
136 | /**
137 | * @brief accept a achar and get back the operation mode as string
138 | */
139 | char *getModeDesc(char mode);
140 |
141 | /**
142 | * @brief Serial interface used for communication
143 | * @details This is set in the constructor
144 | */
145 | SoftwareSerial *my_serialIntf;
146 | // dynamic requests
147 | bool PIXX_Q1();
148 | bool PIXX_QPIGS();
149 | bool PIXX_QPIGS2();
150 | bool PIXX_QMOD();
151 | bool PIXX_QEX();
152 | bool PIXX_QPIWS();
153 | // static reqeuests
154 | bool PIXX_QPIRI();
155 | bool PIXX_QPI();
156 | bool PIXX_QMN();
157 | bool PIXX_QFLAG();
158 |
159 | bool isModbus();
160 |
161 | static bool checkQFLAG(const String& flags, char symbol);
162 | };
163 |
164 | #endif
--------------------------------------------------------------------------------
/src/PI_Serial/QEX.h:
--------------------------------------------------------------------------------
1 | bool PI_Serial::PIXX_QEX()
2 | {
3 | if (protocol == PI30)
4 | {
5 | String commandAnswer = this->requestData("QET");
6 | get.raw.qet = commandAnswer;
7 | if (commandAnswer == "NAK")
8 | {
9 | return true;
10 | }
11 | if (commandAnswer == "ERCRC")
12 | {
13 | return false;
14 | }
15 | liveData["PV_generation_sum"] = commandAnswer.toInt();
16 | commandAnswer = this->requestData("QT");
17 | get.raw.qt = commandAnswer.substring(0, 8);
18 | if (commandAnswer == "ERCRC")
19 | {
20 | return false;
21 | }
22 | if (commandAnswer == "NAK")
23 | {
24 | return true;
25 | }
26 | else
27 | {
28 | commandAnswer = this->requestData("QEY" + get.raw.qt.substring(0, 4));
29 | get.raw.qey = commandAnswer;
30 | if (commandAnswer == "ERCRC")
31 | return false;
32 | if (commandAnswer == "NAK")
33 | return true;
34 | liveData["PV_generation_year"] = commandAnswer.toInt();
35 |
36 | commandAnswer = this->requestData("QEM" + get.raw.qt.substring(0, 6));
37 | get.raw.qem = commandAnswer;
38 | if (commandAnswer == "ERCRC")
39 | return false;
40 | if (commandAnswer == "NAK")
41 | return true;
42 | liveData["PV_generation_month"] = commandAnswer.toInt();
43 |
44 | commandAnswer = this->requestData("QED" + get.raw.qt.substring(0, 8));
45 | get.raw.qed = commandAnswer;
46 | if (commandAnswer == "ERCRC")
47 | return false;
48 | if (commandAnswer == "NAK")
49 | return true;
50 | liveData["PV_generation_day"] = commandAnswer.toInt();
51 |
52 | commandAnswer = this->requestData("QLT");
53 | get.raw.qlt = commandAnswer;
54 | if (commandAnswer == "ERCRC")
55 | return false;
56 | if (commandAnswer == "NAK")
57 | return true;
58 | liveData["AC_in_generation_sum"] = commandAnswer.toInt();
59 |
60 | commandAnswer = this->requestData("QLY" + get.raw.qt.substring(0, 4));
61 | get.raw.qly = commandAnswer;
62 | if (commandAnswer == "ERCRC")
63 | return false;
64 | if (commandAnswer == "NAK")
65 | return true;
66 | liveData["AC_in_generation_year"] = commandAnswer.toInt();
67 |
68 | commandAnswer = this->requestData("QLM" + get.raw.qt.substring(0, 6));
69 | get.raw.qlm = commandAnswer;
70 | if (commandAnswer == "ERCRC")
71 | return false;
72 | if (commandAnswer == "NAK")
73 | return true;
74 | liveData["AC_in_generation_month"] = commandAnswer.toInt();
75 |
76 | commandAnswer = this->requestData("QLD" + get.raw.qt.substring(0, 8));
77 | get.raw.qld = commandAnswer;
78 | if (commandAnswer == "ERCRC")
79 | return false;
80 | if (commandAnswer == "NAK")
81 | return true;
82 | liveData["AC_in_generation_day"] = commandAnswer.toInt();
83 |
84 | return true;
85 | }
86 | }
87 | else if (protocol == PI18)
88 | {
89 | String commandAnswer = "";
90 | switch (qexCounter)
91 | {
92 | case 0:
93 | commandAnswer = this->requestData("^P004T");
94 | if (commandAnswer == "ERCRC" || commandAnswer == "NAK" || commandAnswer == "" || commandAnswer.toInt() < 1)
95 | {
96 | qexCounter = 0;
97 | get.raw.qt = "";
98 | return true;
99 | }
100 | else
101 | {
102 | get.raw.qt = commandAnswer;
103 | qexCounter++;
104 | }
105 | break;
106 | case 1:
107 | commandAnswer = this->requestData("^P013ED" + get.raw.qt.substring(0, 8));
108 | if (commandAnswer == "ERCRC" || commandAnswer == "NAK" || commandAnswer == "" || get.raw.qt == "" /*|| commandAnswer.toInt() < 1*/)
109 | {
110 | qexCounter = 0;
111 | return true;
112 | }
113 | else
114 | {
115 | get.raw.qed = commandAnswer;
116 | liveData["PV_generation_day"] = commandAnswer.toInt();
117 | qexCounter++;
118 | }
119 | break;
120 | case 2:
121 | commandAnswer = this->requestData("^P011EM" + get.raw.qt.substring(0, 6));
122 | if (commandAnswer == "ERCRC" || commandAnswer == "NAK" || commandAnswer == "" || get.raw.qt == "" /*|| commandAnswer.toInt() < 1*/)
123 | {
124 | qexCounter = 0;
125 | return true;
126 | }
127 | else
128 | {
129 | get.raw.qem = commandAnswer;
130 | liveData["PV_generation_month"] = commandAnswer.toInt();
131 | qexCounter++;
132 | }
133 | break;
134 | case 3:
135 | commandAnswer = this->requestData("^P009EY" + get.raw.qt.substring(0, 4));
136 | if (commandAnswer == "ERCRC" || commandAnswer == "NAK" || commandAnswer == "" || get.raw.qt == "" /*|| commandAnswer.toInt() < 1*/)
137 | {
138 | qexCounter = 0;
139 | return true;
140 | }
141 | else
142 | {
143 | get.raw.qey = commandAnswer;
144 | liveData["PV_generation_year"] = commandAnswer.toInt();
145 | qexCounter++;
146 | }
147 | break;
148 | case 4:
149 | commandAnswer = this->requestData("^P005ET");
150 | if (commandAnswer == "ERCRC" || commandAnswer == "NAK" || commandAnswer == "" /*|| commandAnswer.toInt() < 1*/)
151 | {
152 | return true;
153 | }
154 | else
155 | {
156 | get.raw.qet = commandAnswer;
157 | liveData["PV_generation_sum"] = commandAnswer.toInt();
158 | qexCounter = 0;
159 | }
160 | break;
161 |
162 | default:
163 | break;
164 | } // return true;
165 |
166 | return true;
167 | }
168 | else if (protocol == NoD)
169 | {
170 | return false;
171 | }
172 | else
173 | {
174 | return false;
175 | }
176 | }
--------------------------------------------------------------------------------
/src/PI_Serial/QFLAG.h:
--------------------------------------------------------------------------------
1 | bool PI_Serial::PIXX_QFLAG()
2 | {
3 | if (protocol == PI30)
4 | {
5 | String commandAnswer = this->requestData("QFLAG");
6 | get.raw.qflag = commandAnswer;
7 | byte commandAnswerLength = commandAnswer.length();
8 | if (commandAnswer == "NAK")
9 | {
10 | return true;
11 | }
12 | if (commandAnswer == "ERCRC")
13 | {
14 | return false;
15 | }
16 | if (commandAnswerLength == 11)
17 | {
18 | staticData["Buzzer_Enabled"] = checkQFLAG(commandAnswer, 'a');
19 |
20 | staticData["Buzzer_Enabled"] = checkQFLAG(commandAnswer, 'a');
21 | staticData["Overload_bypass_Enabled"] = checkQFLAG(commandAnswer, 'b');
22 | staticData["Power_saving_Enabled"] = checkQFLAG(commandAnswer, 'j');
23 | staticData["LCD_reset_to_default_Enabled"] = checkQFLAG(commandAnswer, 'k');
24 | staticData["Overload_restart_Enabled"] = checkQFLAG(commandAnswer, 'u');
25 | staticData["Over_temperature_restart_Enabled"] = checkQFLAG(commandAnswer, 'v');
26 | staticData["LCD_backlight_Enabled"] = checkQFLAG(commandAnswer, 'x');
27 | staticData["Primary_source_interrupt_alarm_Enabled"] = checkQFLAG(commandAnswer, 'y');
28 | staticData["Record_fault_code_Enabled"] = checkQFLAG(commandAnswer, 'z');
29 | }
30 | return true;
31 | }
32 | else if (protocol == PI18)
33 | {
34 | String commandAnswer = this->requestData("^P007FLAG");
35 | get.raw.qflag = commandAnswer;
36 | byte commandAnswerLength = commandAnswer.length();
37 | if (commandAnswer == "NAK")
38 | {
39 | return true;
40 | }
41 | if (commandAnswer == "ERCRC")
42 | {
43 | return false;
44 | }
45 | //[C: ^P007FLAG][CR: 190F][CC: 190F][L: 17]
46 | //QFLAG 1,1,0,0,0,1,1,1,0
47 | if (commandAnswerLength == 17)
48 | {
49 | staticData["Buzzer_Enabled"] = ((String)commandAnswer.charAt(0) == "1") ? true : false;
50 | staticData["Overload_bypass_Enabled"] = ((String)(commandAnswer.charAt(2)) == "1") ? true : false;
51 | staticData["LCD_reset_to_default_Enabled"] = ((String)(commandAnswer.charAt(4)) == "1") ? true : false;
52 | staticData["Overload_restart_Enabled"] = ((String)(commandAnswer.charAt(6)) == "1") ? true : false;
53 | staticData["Over_temperature_restart_Enabled"] = ((String)(commandAnswer.charAt(8)) == "1") ? true : false;
54 | staticData["LCD_backlight_Enabled"] = ((String)(commandAnswer.charAt(10)) == "1") ? true : false;
55 | staticData["Primary_source_interrupt_alarm_Enabled"] = ((String)(commandAnswer.charAt(12)) == "1") ? true : false;
56 | staticData["Record_fault_code_Enabled"] = ((String)(commandAnswer.charAt(14)) == "1") ? true : false;
57 |
58 | } else {
59 | get.raw.qflag = "Wrong Length(" + (String)get.raw.qflag.length() + "), Contact Dev:" +get.raw.qflag;
60 | }
61 | return true;
62 | }
63 | else if (protocol == NoD)
64 | {
65 | return false;
66 | }
67 | else
68 | {
69 | return false;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/PI_Serial/QMN.h:
--------------------------------------------------------------------------------
1 | bool PI_Serial::PIXX_QMN()
2 | {
3 | if (protocol == PI30)
4 | {
5 | String commandAnswer = this->requestData("QMN");
6 | get.raw.qmn = commandAnswer;
7 | // calculate the length with https://elmar-eigner.de/text-zeichen-laenge.html
8 | if (commandAnswer == "NAK")
9 | {
10 | return true;
11 | }
12 | if (commandAnswer == "ERCRC")
13 | {
14 | return false;
15 | }
16 | if (commandAnswer.length() > 3 && commandAnswer.length() < 50)
17 | {
18 | staticData["Device_Model"] = commandAnswer;
19 | return true;
20 | }
21 | return true;
22 | }
23 | else if (protocol == PI18)
24 | {
25 | return true;
26 | }
27 | else if (protocol == NoD)
28 | {
29 | return false;
30 | }
31 | else
32 | {
33 | return false;
34 | }
35 | }
--------------------------------------------------------------------------------
/src/PI_Serial/QMOD.h:
--------------------------------------------------------------------------------
1 | bool PI_Serial::PIXX_QMOD()
2 | {
3 | if (protocol == PI30)
4 | {
5 | String commandAnswer = this->requestData("QMOD");
6 | get.raw.qmod = commandAnswer;
7 | if (commandAnswer == "NAK")
8 | {
9 | return true;
10 | }
11 | if (commandAnswer == "ERCRC")
12 | {
13 | return false;
14 | }
15 | if (commandAnswer.length() == 1)
16 | {
17 | liveData["Inverter_Operation_Mode"] = getModeDesc((char)commandAnswer.charAt(0));
18 | }
19 | return true;
20 | }
21 | else if (protocol == PI18)
22 | {
23 | String commandAnswer = this->requestData("^P006MOD");
24 | get.raw.qmod = commandAnswer;
25 | if (commandAnswer == "NAK")
26 | {
27 | return true;
28 | }
29 | if (commandAnswer == "ERCRC")
30 | {
31 | return false;
32 | }
33 | if (commandAnswer.length() == 2)
34 | {
35 | switch (commandAnswer.toInt())
36 | {
37 | case 0:
38 | liveData["Inverter_Operation_Mode"] = "Power on";
39 | break;
40 | case 1:
41 | liveData["Inverter_Operation_Mode"] = "Standby";
42 | break;
43 | case 2:
44 | liveData["Inverter_Operation_Mode"] = "Bypass";
45 | break;
46 | case 3:
47 | liveData["Inverter_Operation_Mode"] = "Battery";
48 | break;
49 | case 4:
50 | liveData["Inverter_Operation_Mode"] = "Fault";
51 | break;
52 | case 5:
53 | liveData["Inverter_Operation_Mode"] = "Hybrid";
54 | break;
55 | default:
56 | liveData["Inverter_Operation_Mode"] = "No data";
57 | break;
58 | }
59 |
60 | }
61 | return true;
62 | }
63 | else if (protocol == NoD)
64 | {
65 | return false;
66 | }
67 | else
68 | {
69 | return false;
70 | }
71 | }
--------------------------------------------------------------------------------
/src/PI_Serial/QPI.h:
--------------------------------------------------------------------------------
1 | bool PI_Serial::PIXX_QPI()
2 | {
3 | if (protocol == PI30)
4 | {
5 | String commandAnswer = this->requestData("QPI");
6 | get.raw.qpi = commandAnswer;
7 | if (commandAnswer == "NAK")
8 | {
9 | return true;
10 | }
11 | if (commandAnswer == "ERCRC")
12 | {
13 | return false;
14 | }
15 | staticData["Protocol_ID"] = commandAnswer;
16 | return true;
17 | }
18 | else if (protocol == PI18)
19 | {
20 | String commandAnswer = this->requestData("^P005PI");
21 | get.raw.qpi = commandAnswer;
22 | if (commandAnswer == "NAK")
23 | {
24 | return true;
25 | }
26 | if (commandAnswer == "ERCRC")
27 | {
28 | return false;
29 | }
30 | staticData["Protocol_ID"] = commandAnswer.toInt();
31 | return true;
32 | }
33 | else if (protocol == NoD)
34 | {
35 | return false;
36 | }
37 | else
38 | {
39 | return false;
40 | }
41 | }
--------------------------------------------------------------------------------
/src/PI_Serial/QPIGS.h:
--------------------------------------------------------------------------------
1 | static const char *const qpigsList[][24] = {
2 | // [PI34 / MPPT-3000], [PI30 HS MS MSX], [PI30 Revo], [PI30 PIP], [PI41 / LV5048]
3 | {
4 | "AC_in_Voltage", // BBB.B
5 | "AC_in_Frequenz", // CC.C
6 | "AC_out_Voltage", // DDD.D
7 | "AC_out_Frequenz", // EE.E
8 | "AC_out_VA", // FFFF
9 | "AC_out_Watt", // GGGG
10 | "AC_out_percent", // HHH
11 | "Inverter_Bus_Voltage", // III
12 | "Battery_Voltage", // JJ.JJ
13 | "Battery_Charge_Current", // KKK
14 | "Battery_Percent", // OOO
15 | "Inverter_Bus_Temperature", // TTTT
16 | "PV_Input_Current", // EE.E
17 | "PV_Input_Voltage", // UUU.U
18 | "Battery_SCC_Volt", // WW.WW
19 | "Battery_Discharge_Current", // PPPP
20 | "Status_Flag", // b0-b7
21 | "Battery_voltage_offset_fans_on", // QQ
22 | "EEPROM_Version", // VV
23 | "PV_Charging_Power", // MMMM
24 | "Device_Status", // b8-b10
25 | "Solar_feed_to_Grid_status", // Y
26 | "Country", // ZZ
27 | "Solar_feed_to_grid_power", // AAAA
28 | },
29 | // [PI16]
30 | {
31 | "Grid_voltage", // AAA.A
32 | "Output_power", // BBBBBB
33 | "Grid_frequency", // CC.C
34 | "Output_current", // DDDD.D
35 | "AC_output_voltage", // EEE.E
36 | "AC_output_power", // FFFFF
37 | "AC_output_frequency", // GG.G
38 | "AC_output_current", // HHH.H
39 | "Output_load_percent", // III
40 | "PBUS_voltage", // JJJ.J
41 | "SBUS_voltage", // KKK.K
42 | "Positive_battery_voltage", // LLL.L
43 | "Negative_battery_voltage", // MMM.M
44 | "Battery_capacity", // NNN
45 | "PV1_input_power", // OOOOO
46 | "PV2_input_power", // PPPPP
47 | "PV3_input_power", // QQQQQ
48 | "PV1_input_voltage", // RRR.R
49 | "PV2_input_voltage", // SSS.S
50 | "PV3_input_voltage", // TTT.T
51 | "Max_temperature", // UUU.U
52 | },
53 | };
54 | static const char *const qallList[] = {
55 | // [PI30 Revo]
56 | "AC_in_Voltage", // BBB.B
57 | "AC_in_Frequenz", // CC.C
58 | "AC_out_Voltage", // DDD.D
59 | "AC_out_Frequenz", // EE.E
60 | "AC_out_VA", // FFFF
61 | "AC_out_percent", // GGG
62 | "Battery_Voltage", // HH.H
63 | "Battery_Percent", // III
64 | "Battery_Charge_Current", // JJJ
65 | "Battery_Discharge_Current", // KKK
66 | "PV_Input_Voltage", // LLL
67 | "PV_Input_Current", // MM.M
68 | "PV_Charging_Power", // NNNN
69 | "PV_generation_day", // OOOOOO
70 | "PV_generation_sum", // PPPPPP
71 | "Inverter_Operation_Mode", // Q
72 | "Warning_Code", // KK
73 | "Fault_code", // SS
74 | };
75 | static const char *const P005GS[][28] = {
76 | {"AC_in_Voltage", "10"}, // AAAA
77 | {"AC_in_Frequenz", "10"}, // BBB
78 | {"AC_out_Voltage", "10"}, // CCCC
79 | {"AC_out_Frequenz", "10"}, // DDD
80 | {"AC_out_VA", "0"}, // EEEE
81 | {"AC_out_Watt", "0"}, // FFFF
82 | {"AC_out_percent", "0"}, // GGGG
83 | {"Battery_Voltage", "10"}, // HHHH
84 | {"Battery_SCC_Volt", "10"}, // III
85 | {"Battery_SCC2_Volt", "10"}, // JJJ
86 | {"Battery_Discharge_Current", "0"}, // KKK
87 | {"Battery_Charge_Current", "0"}, // LLL
88 | {"Battery_Percent", "0"}, // MMM
89 | {"Inverter_Bus_Temperature", "0"}, // NNN
90 | {"MPPT1_Charger_Temperature", "0"}, // OOO
91 | {"MPPT2_Charger_Temperature", "0"}, // PPP
92 | {"PV1_Input_Power", "0"}, // QQQQ
93 | {"PV2_Input_Power", "0"}, // RRRR
94 | {"PV1_Input_Voltage", "10"}, // SSSS
95 | {"PV2_Input_Voltage", "10"}, // TTTT
96 | {"Configuration_State", "0"}, // U
97 | {"MPPT1_Charger_Status", "0"}, // V
98 | {"MPPT2_CHarger_Status", "0"}, // W
99 | {"Load_Connection", "0"}, // X
100 | {"Battery_Power_Direction", "0"}, // Y
101 | {"ACDC_Power_Direction", "0"}, // Z
102 | {"Line_Power_Direction", "0"}, // a
103 | {"Local_Parallel_ID", "0"}, // b
104 | };
105 |
106 | bool PI_Serial::PIXX_QPIGS()
107 | {
108 | if (protocol == PI30)
109 | {
110 | byte protocolNum = 0; // for future use
111 | get.raw.qall = "";
112 | String commandAnswerQALL = this->requestData("QALL");
113 | get.raw.qall = commandAnswerQALL;
114 |
115 | //get.raw.qall = "NAK";
116 | // if (commandAnswerQALL == "ERCRC")
117 | //{
118 | // return false;
119 | //}
120 | //
121 | String commandAnswerQPIGS = this->requestData("QPIGS");
122 | get.raw.qpigs = commandAnswerQPIGS;
123 | if (commandAnswerQPIGS == "NAK")
124 | return true;
125 | if (commandAnswerQPIGS == "ERCRC")
126 | return false;
127 | byte commandAnswerLength = commandAnswerQPIGS.length();
128 |
129 | // calculate the length with https://elmar-eigner.de/text-zeichen-laenge.html
130 | if (commandAnswerLength >= 60 && commandAnswerLength <= 140)
131 | {
132 | if (commandAnswerLength <= 116)
133 | {
134 | protocolNum = 0;
135 | }
136 | else if (commandAnswerLength > 131)
137 | {
138 | protocolNum = 1;
139 | }
140 |
141 | // Split the string into substrings
142 | String strs[30]; // buffer for string splitting
143 | int StringCount = 0;
144 | while (commandAnswerQPIGS.length() > 0)
145 | {
146 | int index = commandAnswerQPIGS.indexOf(delimiter);
147 | if (index == -1) // No space found
148 | {
149 | strs[StringCount++] = commandAnswerQPIGS;
150 | break;
151 | }
152 | else
153 | {
154 | strs[StringCount++] = commandAnswerQPIGS.substring(0, index);
155 | commandAnswerQPIGS = commandAnswerQPIGS.substring(index + 1);
156 | }
157 | }
158 |
159 | for (unsigned int i = 0; i < sizeof qpigsList[protocolNum] / sizeof qpigsList[protocolNum][0]; i++)
160 | {
161 | if (!strs[i].isEmpty() && strcmp(qpigsList[protocolNum][i], "") != 0)
162 | liveData[qpigsList[protocolNum][i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0;
163 | }
164 | // make some things pretty
165 | liveData["Battery_Load"] = (liveData["Battery_Charge_Current"].as() - liveData["Battery_Discharge_Current"].as());
166 | liveData["PV_Input_Power"] = (liveData["PV_Input_Voltage"].as() * liveData["PV_Input_Current"].as());
167 | }
168 |
169 | if (get.raw.qall.length() > 10 /*get.raw.qall != "NAK" || get.raw.qall != "ERCRC" || get.raw.qall != ""*/)
170 | {
171 | String strsQALL[30];
172 | // Split the string into substrings
173 | int StringCountQALL = 0;
174 | while (commandAnswerQALL.length() > 0)
175 | {
176 | int index = commandAnswerQALL.indexOf(delimiter);
177 | if (index == -1) // No space found
178 | {
179 | strsQALL[StringCountQALL++] = commandAnswerQALL;
180 | break;
181 | }
182 | else
183 | {
184 | strsQALL[StringCountQALL++] = commandAnswerQALL.substring(0, index);
185 | commandAnswerQALL = commandAnswerQALL.substring(index + 1);
186 | }
187 | }
188 |
189 | for (unsigned int i = 0; i < sizeof qallList / sizeof qallList[0]; i++)
190 | {
191 | if (!strsQALL[i].isEmpty() && strcmp(qallList[i], "") != 0)
192 | liveData[qallList[i]] = (int)(strsQALL[i].toFloat() * 100 + 0.5) / 100.0;
193 | }
194 | liveData["Inverter_Operation_Mode"] = getModeDesc((char)liveData["Inverter_Operation_Mode"].as().charAt(0));
195 | liveData["Battery_Load"] = (liveData["Battery_Charge_Current"].as() - liveData["Battery_Discharge_Current"].as());
196 | }
197 |
198 | return true;
199 | }
200 | else if (protocol == PI18)
201 | {
202 | String commandAnswer = this->requestData("^P005GS");
203 | get.raw.qpigs = commandAnswer;
204 | if (commandAnswer == "NAK")
205 | return true;
206 | if (commandAnswer == "ERCRC")
207 | return false;
208 | byte commandAnswerLength = commandAnswer.length();
209 |
210 | // calculate the length with https://elmar-eigner.de/text-zeichen-laenge.html
211 | if (commandAnswerLength >= 60 && commandAnswerLength <= 140)
212 | {
213 | // Split the string into substrings
214 | String strs[30]; // buffer for string splitting
215 | int StringCount = 0;
216 | while (commandAnswer.length() > 0)
217 | {
218 | int index = commandAnswer.indexOf(delimiter);
219 | if (index == -1) // No space found
220 | {
221 | strs[StringCount++] = commandAnswer;
222 | break;
223 | }
224 | else
225 | {
226 | strs[StringCount++] = commandAnswer.substring(0, index);
227 | commandAnswer = commandAnswer.substring(index + 1);
228 | }
229 | }
230 |
231 | for (unsigned int i = 0; i < sizeof P005GS[0] / sizeof P005GS[0][0]; i++)
232 | {
233 | if (!strs[i].isEmpty() && strcmp(P005GS[i][0], "") != 0)
234 | {
235 | if (atoi(P005GS[i][1]) > 0)
236 | {
237 | liveData[P005GS[i][0]] = (int)((strs[i].toFloat() / atoi(P005GS[i][1])) * 100 + 0.5) / 100.0;
238 | }
239 | else if (atoi(P005GS[i][1]) == 0)
240 | {
241 | liveData[P005GS[i][0]] = strs[i].toInt();
242 | }
243 | else
244 | {
245 | liveData[P005GS[i][0]] = strs[i];
246 | }
247 | }
248 | }
249 | // make some things pretty
250 |
251 | liveData["PV_Input_Voltage"] = (liveData["PV1_Input_Voltage"].as() + liveData["PV2_Input_Voltage"].as());
252 | liveData["PV_Charging_Power"] = (liveData["PV1_Input_Power"].as() + liveData["PV2_Input_Power"].as());
253 | liveData["PV_Input_Current"] = (int)((liveData["PV_Charging_Power"].as() / (liveData["PV_Input_Voltage"].as()+0.5)) * 100) / 100.0;
254 | liveData["Battery_Load"] = (liveData["Battery_Charge_Current"].as() - liveData["Battery_Discharge_Current"].as());
255 | }
256 | return true;
257 | }
258 | else if (protocol == NoD)
259 | {
260 | return false;
261 | }
262 | else
263 | {
264 | return false;
265 | }
266 | }
--------------------------------------------------------------------------------
/src/PI_Serial/QPIGS2.h:
--------------------------------------------------------------------------------
1 | static const char *const qpigs2List[] = {
2 | // [PI34 / MPPT-3000], [PI30 HS MS MSX], [PI30 Revo], [PI30 PIP], [PI41 / LV5048]
3 | "PV2_Input_Current", // BBB.B
4 | "PV2_Input_Voltage", // CCC.C
5 | "PV2_Charging_Power", // DDDD
6 | };
7 |
8 | bool PI_Serial::PIXX_QPIGS2()
9 | {
10 | if (protocol == PI30)
11 | {
12 | String commandAnswer = this->requestData("QPIGS2");
13 | get.raw.qpigs2 = commandAnswer;
14 | byte commandAnswerLength = commandAnswer.length();
15 | String strs[30]; // buffer for string splitting
16 | if (commandAnswer == "NAK")
17 | {
18 | return true;
19 | }
20 | if (commandAnswer == "ERCRC")
21 | {
22 | return false;
23 | }
24 |
25 | // calculate the length with https://elmar-eigner.de/text-zeichen-laenge.html
26 | if (commandAnswerLength >= 10 && commandAnswerLength <= 20)
27 | {
28 |
29 | // Split the string into substrings
30 | int StringCount = 0;
31 | while (commandAnswer.length() > 0)
32 | {
33 | int index = commandAnswer.indexOf(delimiter);
34 | if (index == -1) // No space found
35 | {
36 | strs[StringCount++] = commandAnswer;
37 | break;
38 | }
39 | else
40 | {
41 | strs[StringCount++] = commandAnswer.substring(0, index);
42 | commandAnswer = commandAnswer.substring(index + 1);
43 | }
44 | }
45 |
46 | for (unsigned int i = 0; i < sizeof qpigs2List / sizeof qpigs2List[0]; i++)
47 | {
48 | if (!strs[i].isEmpty() && strcmp(qpigs2List[i], "") != 0)
49 | liveData[qpigs2List[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0;
50 | }
51 | // make some things pretty
52 | liveData["PV2_Input_Power"] = (liveData["PV2_Input_Voltage"].as() * liveData["PV2_Input_Current"].as());
53 | }
54 | return true;
55 | }
56 | else if (protocol == PI18)
57 | {
58 | return true;
59 | }
60 | else if (protocol == NoD)
61 | {
62 | return false;
63 | }
64 | else
65 | {
66 | return false;
67 | }
68 | }
--------------------------------------------------------------------------------
/src/PI_Serial/QPIWS.h:
--------------------------------------------------------------------------------
1 | bool PI_Serial::PIXX_QPIWS()
2 | {
3 | if (protocol == PI30)
4 | {
5 | String commandAnswer = this->requestData("QPIWS");
6 | //String commandAnswer = "10000000001010000000000000000000";
7 | get.raw.qpiws = commandAnswer;
8 | if (commandAnswer == "NAK")
9 | {
10 | return true;
11 | }
12 | if (commandAnswer == "ERCRC")
13 | {
14 | return false;
15 | }
16 | if (commandAnswer.length() == 32)
17 | {
18 | std::vector qpiwsStrings;
19 | if ((char)commandAnswer.charAt(1) == '1') qpiwsStrings.emplace_back("Inverter fault"); // 2
20 | if ((char)commandAnswer.charAt(2) == '1') qpiwsStrings.emplace_back("Bus over fault"); // 3
21 | if ((char)commandAnswer.charAt(3) == '1') qpiwsStrings.emplace_back("Bus under fault"); // 4
22 | if ((char)commandAnswer.charAt(4) == '1') qpiwsStrings.emplace_back("Bus soft fail fault"); // 5
23 | if ((char)commandAnswer.charAt(5) == '1') qpiwsStrings.emplace_back("Line fail warning"); // 6
24 | if ((char)commandAnswer.charAt(6) == '1') qpiwsStrings.emplace_back("OPV short warning"); // 7
25 | if ((char)commandAnswer.charAt(7) == '1') qpiwsStrings.emplace_back("Inverter voltage too low fault"); // 8
26 | if ((char)commandAnswer.charAt(8) == '1') qpiwsStrings.emplace_back("Inverter voltage too high fault"); // 9
27 | if ((char)commandAnswer.charAt(9) == '1') qpiwsStrings.emplace_back("Over temperature fault"); // 10
28 | if ((char)commandAnswer.charAt(10) == '1') qpiwsStrings.emplace_back("Fan locked fault"); // 11
29 | if ((char)commandAnswer.charAt(11) == '1') qpiwsStrings.emplace_back("Battery voltage too high fault"); // 12
30 | if ((char)commandAnswer.charAt(12) == '1') qpiwsStrings.emplace_back("Battery low alarm warning"); // 13
31 | if ((char)commandAnswer.charAt(14) == '1') qpiwsStrings.emplace_back("Battery under shutdown warning"); // 15
32 | if ((char)commandAnswer.charAt(16) == '1') qpiwsStrings.emplace_back("Overload fault"); // 17
33 | if ((char)commandAnswer.charAt(17) == '1') qpiwsStrings.emplace_back("EEPROM fault"); // 18
34 | if ((char)commandAnswer.charAt(18) == '1') qpiwsStrings.emplace_back("Inverter over current fault"); // 19
35 | if ((char)commandAnswer.charAt(19) == '1') qpiwsStrings.emplace_back("Inverter soft fail fault"); // 20
36 | if ((char)commandAnswer.charAt(20) == '1') qpiwsStrings.emplace_back("Self test fail fault"); // 21
37 | if ((char)commandAnswer.charAt(21) == '1') qpiwsStrings.emplace_back("OP DC voltage over fault"); // 22
38 | if ((char)commandAnswer.charAt(22) == '1') qpiwsStrings.emplace_back("Battery open fault"); // 23
39 | if ((char)commandAnswer.charAt(23) == '1') qpiwsStrings.emplace_back("Current sensor fail fault"); // 24
40 | if ((char)commandAnswer.charAt(24) == '1') qpiwsStrings.emplace_back("Battery short fault"); // 25
41 | if ((char)commandAnswer.charAt(25) == '1') qpiwsStrings.emplace_back("Power limit warning"); // 26
42 | if ((char)commandAnswer.charAt(26) == '1') qpiwsStrings.emplace_back("PV voltage high warning"); // 27
43 | if ((char)commandAnswer.charAt(27) == '1') qpiwsStrings.emplace_back("MPPT overload fault"); // 28
44 | if ((char)commandAnswer.charAt(28) == '1') qpiwsStrings.emplace_back("MPPT overload warning"); // 29
45 | if ((char)commandAnswer.charAt(29) == '1') qpiwsStrings.emplace_back("Battery too low to charge warning"); // 30
46 | if (!qpiwsStrings.empty())
47 | {
48 | String qpiwsStr = "";
49 | for (size_t i = 0; i < qpiwsStrings.size(); i++) {
50 | qpiwsStr += qpiwsStrings[i];
51 | if (i < qpiwsStrings.size() - 1) {
52 | qpiwsStr += "; ";
53 | }
54 | }
55 | liveData["Fault_code"] = qpiwsStr;
56 | }
57 | else
58 | {
59 | liveData["Fault_code"] = "Ok";
60 | }
61 | }
62 | return true;
63 | }
64 | else if(protocol == PI18){
65 | String commandAnswer = this->requestData("^P005FWS");
66 | get.raw.qpiws = commandAnswer;
67 | if (commandAnswer == "NAK")
68 | {
69 | return true;
70 | }
71 | if (commandAnswer == "ERCRC")
72 | {
73 | return false;
74 | }
75 | //[C: ^P005FWS][CR: B69E][CC: B69E][L: 36] from valqk
76 | //QPIWS 00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
77 | if (commandAnswer.length() == 36)
78 | {
79 | std::vector qpiwsStrings;
80 | if (commandAnswer.substring(0,2) == "01") qpiwsStrings.emplace_back("Fan is locked"); // 2
81 | if (commandAnswer.substring(0,2) == "02") qpiwsStrings.emplace_back("Over temperature"); // 2
82 | if (commandAnswer.substring(0,2) == "03") qpiwsStrings.emplace_back("Battery voltage is too high"); // 2
83 | if (commandAnswer.substring(0,2) == "04") qpiwsStrings.emplace_back("Battery voltage is too low"); // 2
84 | if (commandAnswer.substring(0,2) == "05") qpiwsStrings.emplace_back("Output short circuited or Over temperature"); // 2
85 | if (commandAnswer.substring(0,2) == "06") qpiwsStrings.emplace_back("Output voltage is too high"); // 2
86 | if (commandAnswer.substring(0,2) == "07") qpiwsStrings.emplace_back("Over load time out"); // 2
87 | if (commandAnswer.substring(0,2) == "08") qpiwsStrings.emplace_back("Bus voltage is too high"); // 2
88 | if (commandAnswer.substring(0,2) == "09") qpiwsStrings.emplace_back("Bus soft start failed"); // 2
89 | if (commandAnswer.substring(0,2) == "11") qpiwsStrings.emplace_back("Main relay failed"); // 2
90 | if (commandAnswer.substring(0,2) == "51") qpiwsStrings.emplace_back("Over current inverter"); // 2
91 | if (commandAnswer.substring(0,2) == "52") qpiwsStrings.emplace_back("Bus soft start failed"); // 2
92 | if (commandAnswer.substring(0,2) == "53") qpiwsStrings.emplace_back("Inverter soft start failed"); // 2
93 | if (commandAnswer.substring(0,2) == "54") qpiwsStrings.emplace_back("Self-test failed"); // 2
94 | if (commandAnswer.substring(0,2) == "55") qpiwsStrings.emplace_back("Over DC voltage on output of inverter"); // 2
95 | if (commandAnswer.substring(0,2) == "56") qpiwsStrings.emplace_back("Battery connection is open"); // 2
96 | if (commandAnswer.substring(0,2) == "57") qpiwsStrings.emplace_back("Current sensor failed"); // 2
97 | if (commandAnswer.substring(0,2) == "58") qpiwsStrings.emplace_back("Output voltage is too low"); // 2
98 | if (commandAnswer.substring(0,2) == "60") qpiwsStrings.emplace_back("Inverter negative power"); // 2
99 | if (commandAnswer.substring(0,2) == "71") qpiwsStrings.emplace_back("Parallel version different"); // 2
100 | if (commandAnswer.substring(0,2) == "72") qpiwsStrings.emplace_back("Output circuit failed"); // 2
101 | if (commandAnswer.substring(0,2) == "80") qpiwsStrings.emplace_back("CAN communication failed"); // 2
102 | if (commandAnswer.substring(0,2) == "81") qpiwsStrings.emplace_back("Parallel host line lost"); // 2
103 | if (commandAnswer.substring(0,2) == "82") qpiwsStrings.emplace_back("Parallel synchronized signal lost"); // 2
104 | if (commandAnswer.substring(0,2) == "83") qpiwsStrings.emplace_back("Parallel battery voltage detect different"); // 2
105 | if (commandAnswer.substring(0,2) == "84") qpiwsStrings.emplace_back("Parallel Line voltage or frequency detect different"); // 2
106 | if (commandAnswer.substring(0,2) == "85") qpiwsStrings.emplace_back("Parallel Line input current unbalanced"); // 2
107 | if (commandAnswer.substring(0,2) == "86") qpiwsStrings.emplace_back("Parallel output setting different"); // 2
108 |
109 | if ((char)commandAnswer.charAt(3) == '1') qpiwsStrings.emplace_back("Line fail"); // 2
110 | if ((char)commandAnswer.charAt(5) == '1') qpiwsStrings.emplace_back("Over temperature"); // 20
111 | if ((char)commandAnswer.charAt(7) == '1') qpiwsStrings.emplace_back("Output circuit short"); // 3
112 | if ((char)commandAnswer.charAt(9) == '1') qpiwsStrings.emplace_back("Inverter over temperature"); // 4
113 | if ((char)commandAnswer.charAt(11) == '1') qpiwsStrings.emplace_back("Fan lock"); // 5
114 | if ((char)commandAnswer.charAt(13) == '1') qpiwsStrings.emplace_back("Battery voltage high"); // 6
115 | if ((char)commandAnswer.charAt(15) == '1') qpiwsStrings.emplace_back("Battery low"); // 7
116 | if ((char)commandAnswer.charAt(17) == '1') qpiwsStrings.emplace_back("Battery under"); // 8
117 | if ((char)commandAnswer.charAt(19) == '1') qpiwsStrings.emplace_back("Over load"); // 9
118 | if ((char)commandAnswer.charAt(21) == '1') qpiwsStrings.emplace_back("Eeprom fail"); // 10
119 | if ((char)commandAnswer.charAt(23) == '1') qpiwsStrings.emplace_back("Power limit"); // 11
120 | if ((char)commandAnswer.charAt(25) == '1') qpiwsStrings.emplace_back("PV1 voltage high"); // 12
121 | if ((char)commandAnswer.charAt(27) == '1') qpiwsStrings.emplace_back("PV2 voltage high"); // 13
122 | if ((char)commandAnswer.charAt(29) == '1') qpiwsStrings.emplace_back("MPPT1 overload warning"); // 15
123 | if ((char)commandAnswer.charAt(31) == '1') qpiwsStrings.emplace_back("MPPT2 overload warning"); // 17
124 | if ((char)commandAnswer.charAt(33) == '1') qpiwsStrings.emplace_back("Battery too low to charge for SCC1"); // 18
125 | if ((char)commandAnswer.charAt(35) == '1') qpiwsStrings.emplace_back("Battery too low to charge for SCC2"); // 19
126 |
127 | if (!qpiwsStrings.empty())
128 | {
129 | String qpiwsStr = "";
130 | for (size_t i = 0; i < qpiwsStrings.size(); i++) {
131 | qpiwsStr += qpiwsStrings[i];
132 | if (i < qpiwsStrings.size() - 1) {
133 | qpiwsStr += "; ";
134 | }
135 | }
136 | liveData["Fault_code"] = qpiwsStr;
137 | }
138 | else
139 | {
140 | liveData["Fault_code"] = "Ok";
141 | }
142 | }
143 | return true;
144 | }
145 | else if (protocol == NoD)
146 | {
147 | return false;
148 | }
149 | else
150 | {
151 | return false;
152 | }
153 | }
--------------------------------------------------------------------------------
/src/Settings.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #define EEPROM_SIZE 1024
5 | // Settings: Stores persistant settings, loads and saves to EEPROM
6 |
7 | class Settings
8 | {
9 | // change eeprom config version ONLY when new parameter is added and need reset the parameter
10 | unsigned int configVersion = 11;
11 |
12 | public:
13 | String deviceNameStr;
14 | struct Data
15 | { // do not re-sort this struct
16 | unsigned int coVers; // config version, if changed, previus config will erased
17 | char deviceName[40]; // device name
18 | char mqttServer[40]; // mqtt Server adress
19 | char mqttUser[40]; // mqtt Username
20 | char mqttPassword[40]; // mqtt Password
21 | char mqttTopic[40]; // mqtt publish topic
22 | char mqttTriggerPath[80]; // MQTT Data Trigger Path
23 | unsigned int mqttPort; // mqtt port
24 | unsigned int mqttRefresh; // mqtt refresh time
25 | unsigned int deviceQuantity; // Quantity of Devices
26 | bool mqttJson; // switch between classic mqtt and json
27 | bool webUIdarkmode; // Flag for color mode in webUI
28 | char httpUser[40]; // http basic auth username
29 | char httpPass[40]; // http basic auth password
30 | bool haDiscovery; // HomeAssistant Discovery switch
31 | } data;
32 |
33 | void load()
34 | {
35 | data = {}; // clear bevor load data
36 | EEPROM.begin(EEPROM_SIZE);
37 | EEPROM.get(0, data);
38 | EEPROM.end();
39 | coVersCheck();
40 | sanitycheck();
41 | deviceNameStr = data.deviceName;
42 | }
43 |
44 | void save()
45 | {
46 | sanitycheck();
47 | EEPROM.begin(EEPROM_SIZE);
48 | EEPROM.put(0, data);
49 | EEPROM.commit();
50 | EEPROM.end();
51 | }
52 |
53 | void reset()
54 | {
55 | data = {};
56 | save();
57 | }
58 |
59 | private:
60 | // check the variables from eeprom
61 |
62 | void sanitycheck()
63 | {
64 | if (strlen(data.deviceName) == 0 || strlen(data.deviceName) >= 40)
65 | {
66 | strcpy(data.deviceName, "Solar2MQTT");
67 | }
68 | if (strlen(data.mqttServer) == 0 || strlen(data.mqttServer) >= 40)
69 | {
70 | strcpy(data.mqttServer, "");
71 | }
72 | if (strlen(data.mqttUser) == 0 || strlen(data.mqttUser) >= 40)
73 | {
74 | strcpy(data.mqttUser, "");
75 | }
76 | if (strlen(data.mqttPassword) == 0 || strlen(data.mqttPassword) >= 40)
77 | {
78 | strcpy(data.mqttPassword, "");
79 | }
80 | if (strlen(data.mqttTopic) == 0 || strlen(data.mqttTopic) >= 40)
81 | {
82 | strcpy(data.mqttTopic, "Solar");
83 | }
84 | if (data.mqttPort <= 0 || data.mqttPort >= 65530)
85 | {
86 | data.mqttPort = 0;
87 | }
88 | if (data.mqttRefresh <= 1 || data.mqttRefresh >= 65530)
89 | {
90 | data.mqttRefresh = 0;
91 | }
92 | if (data.mqttJson && !data.mqttJson)
93 | {
94 | data.mqttJson = false;
95 | }
96 | if (data.deviceQuantity < 1 || data.deviceQuantity >= 10)
97 | {
98 | data.deviceQuantity = 1;
99 | }
100 | if (strlen(data.mqttTriggerPath) == 0 || strlen(data.mqttTriggerPath) >= 80)
101 | {
102 | strcpy(data.mqttTriggerPath, "");
103 | }
104 | if (data.webUIdarkmode && !data.webUIdarkmode)
105 | {
106 | data.webUIdarkmode = false;
107 | }
108 | if (strlen(data.httpUser) == 0 || strlen(data.httpUser) >= 40)
109 | {
110 | strcpy(data.httpUser, "");
111 | }
112 | if (strlen(data.httpPass) == 0 || strlen(data.httpPass) >= 40)
113 | {
114 | strcpy(data.httpPass, "");
115 | }
116 | if (data.haDiscovery && !data.haDiscovery)
117 | {
118 | data.haDiscovery = false;
119 | }
120 | }
121 | void coVersCheck()
122 | {
123 | if (data.coVers != configVersion)
124 | {
125 | data.coVers = configVersion;
126 | strcpy(data.deviceName, "Solar2MQTT");
127 | strcpy(data.mqttServer, "");
128 | strcpy(data.mqttUser, "");
129 | strcpy(data.mqttPassword, "");
130 | strcpy(data.mqttTopic, "Solar");
131 | strcpy(data.mqttTriggerPath, "");
132 | data.deviceQuantity = 1;
133 | data.mqttPort = 0;
134 | data.mqttRefresh = 300;
135 | data.mqttJson = false;
136 | data.webUIdarkmode = false;
137 | strcpy(data.httpUser, "");
138 | strcpy(data.httpPass, "");
139 | data.haDiscovery = false;
140 | save();
141 | load();
142 | }
143 | }
144 | };
145 |
--------------------------------------------------------------------------------
/src/htmlProzessor.h:
--------------------------------------------------------------------------------
1 | String htmlProcessor(const String &var)
2 | {
3 | extern Settings settings;
4 | if (var == F("pre_head_template"))
5 | return (FPSTR(HTML_HEAD));
6 | if (var == F("pre_foot_template"))
7 | return (FPSTR(HTML_FOOT));
8 | if (var == F("pre_software_version"))
9 | return (SOFTWARE_VERSION);
10 | if (var == F("pre_swversion"))
11 | return (SWVERSION);
12 | if (var == F("pre_flash_size"))
13 | return (String(ESP.getFreeSketchSpace()).c_str());
14 | // if (var == F("pre_esp01"))
15 | // return (String(ESP01).c_str());
16 | if (var == F("pre_device_name"))
17 | return (settings.data.deviceName);
18 | if (var == F("pre_mqtt_server"))
19 | return (settings.data.mqttServer);
20 | if (var == F("pre_mqtt_port"))
21 | return (String(settings.data.mqttPort).c_str());
22 | if (var == F("pre_mqtt_user"))
23 | return (settings.data.mqttUser);
24 | if (var == F("pre_mqtt_pass"))
25 | return (settings.data.mqttPassword);
26 | if (var == F("pre_mqtt_topic"))
27 | return (settings.data.mqttTopic);
28 | if (var == F("pre_mqtt_refresh"))
29 | return (String(settings.data.mqttRefresh).c_str());
30 | if (var == F("pre_mqtt_json"))
31 | return (settings.data.mqttJson ? "checked" : "");
32 | if (var == F("pre_mqtt_mqtttrigger"))
33 | return (settings.data.mqttTriggerPath);
34 | if (var == F("pre_darkmode"))
35 | return (settings.data.webUIdarkmode ? "dark" : "light");
36 | if (var == F("pre_webuidarkmode"))
37 | return (settings.data.webUIdarkmode ? "checked" : "");
38 | if (var == F("pre_http_user"))
39 | return (settings.data.httpUser);
40 | if (var == F("pre_http_pass"))
41 | return (settings.data.httpPass);
42 | if (var == F("pre_hadiscovery"))
43 | return (settings.data.haDiscovery ? "checked" : "");
44 | return String();
45 | }
46 |
--------------------------------------------------------------------------------
/src/main.h:
--------------------------------------------------------------------------------
1 | #include
2 | #define ARDUINOJSON_USE_DOUBLE 1
3 | #define ARDUINOJSON_USE_LONG_LONG 1
4 | #define JSON_BUFFER 2048
5 |
6 |
7 | #ifdef isUART_HARDWARE
8 | #define INVERTER_TX 1
9 | #define INVERTER_RX 3
10 | #define LED_COM 5
11 | #define LED_SRV 0
12 | #define LED_NET 4
13 | #else
14 | #define INVERTER_TX 13
15 | #define INVERTER_RX 12
16 | #define TEMPSENS_PIN 4 // DS18B20 Pin; D2 on Wemos D1 Mini
17 | #endif
18 |
19 | #define LED_PIN 02 // D4 with the LED on Wemos D1 Mini
20 |
21 |
22 | #define DBG_BAUD 115200
23 | #define DBG_WEBLN(...) WebSerial.println(__VA_ARGS__)
24 | #define SOFTWARE_VERSION SWVERSION
25 | #define DBG Serial
26 | #define DBG_BEGIN(...) DBG.begin(__VA_ARGS__)
27 | #define DBG_PRINTLN(...) DBG.println(__VA_ARGS__)
28 |
29 |
30 | /**
31 | * @brief callback function for wifimanager save config data
32 | *
33 | */
34 | void saveConfigCallback();
35 |
36 | /**
37 | * @brief callback function for data
38 | *
39 | */
40 | bool prozessData();
41 |
42 | /**
43 | * @brief fires up the websocket and send data to the clients
44 | *
45 | */
46 | void notifyClients();
47 |
48 | /**
49 | * @brief build the topic string and return
50 | *
51 | */
52 | char *topicBuilder(char *buffer, char const *path, char const *numering);
53 |
54 | /**
55 | * @brief mqtt connect function, check if connection etablished and reconnect and subscribe to spezific topics if needed
56 | *
57 | */
58 | bool connectMQTT();
59 |
60 | /**
61 | * @brief send the data to mqtt
62 | *
63 | */
64 | bool sendtoMQTT();
65 |
66 | /**
67 | * @brief get the basic device data
68 | *
69 | */
70 | // void getJsonDevice();
71 |
72 | /**
73 | * @brief read the data from bms and put it in the json
74 | */
75 | void getJsonData();
76 |
77 | /**
78 | * @brief callback function, watch the sunscribed topics and process the data
79 | *
80 | */
81 | void mqttcallback(char *top, unsigned char *payload, unsigned int length);
82 |
83 | bool sendHaDiscovery();
84 |
85 | /**
86 | * @brief this function act like s/n/printf() and give the output to the configured serial and webserial
87 | *
88 | */
89 | void writeLog(const char* format, ...);
90 |
91 | static const char *const haStaticDescriptor[][4]{
92 | // state_topic, icon, unit_ofmeasurement, class
93 | {"AC_in_rating_current", "current-ac", "A", "current"},
94 | {"AC_in_rating_voltage", "flash-triangle-outline", "V", "voltage"},
95 | {"AC_out_rating_active_power", "sine-wave", "W", "power"},
96 | {"AC_out_rating_apparent_power", "sine-wave", "W", "power"},
97 | {"AC_out_rating_current", "current-ac", "A", "current"},
98 | {"AC_out_rating_frequency", "sine-wave", "Hz", "frequency"},
99 | {"AC_out_rating_voltage", "flash-triangle-outline", "V", "voltage"},
100 | {"Battery_bulk_voltage", "car-battery", "V", "voltage"},
101 | {"Battery_float_voltage", "car-battery", "V", "voltage"},
102 | {"Battery_rating_voltage", "car-battery", "V", "voltage"},
103 | {"Battery_re-charge_voltage", "battery-charging-high", "V", "voltage"},
104 | {"Battery_re-discharge_voltage", "battery-charging-outline", "V", "voltage"},
105 | {"Battery_type", "car-battery", "", ""},
106 | {"Battery_under_voltage", "battery-remove-outline", "V", "voltage"},
107 | {"Charger_source_priority", "ev-station", "", ""},
108 | {"Current_max_AC_charging_current", "current-ac", "A", "current"},
109 | {"Current_max_charging_current", "battery-charging", "A", "current"},
110 | {"Device_Model", "battery-charging", "", ""},
111 | {"Input_voltage_range", "flash-triangle-outline", "", ""},
112 | {"Machine_type", "state-machine", "", ""},
113 | {"Max_charging_time_at_CV_stage", "clock-time-eight-outli", "s", "duration"},
114 | {"Max_discharging_current", "battery-outline", "A", "current"},
115 | {"MPPT_string", "string-lights", "", ""},
116 | {"Operation_Logic", "access-point", "", ""},
117 | {"Output_mode", "export", "", ""},
118 | {"Output_source_priority", "export", "", ""},
119 | //{"Parallel_max_num","","",""},
120 | {"Protocol_ID", "protocol", "", ""},
121 | //{"PV_OK_condition_for_parallel","solar-panel","",""},
122 | {"PV_power_balance", "solar-panel", "", ""},
123 | {"Solar_power_priority", "priority-high", "", ""},
124 | {"Topology", "earth", "", ""},
125 | {"Buzzer_Enabled", "tune-variant", "", ""},
126 | {"Overload_bypass_Enabled", "tune-variant", "", ""},
127 | {"Power_saving_Enabled", "tune-variant", "", ""},
128 | {"LCD_reset_to_default_Enabled", "tune-variant", "", ""},
129 | {"Overload_restart_Enabled", "tune-variant", "", ""},
130 | {"Over_temperature_restart_Enabled", "tune-variant", "", ""},
131 | {"LCD_backlight_Enabled", "tune-variant", "", ""},
132 | {"Primary_source_interrupt_alarm_Enabled", "tune-variant", "", ""},
133 | {"Record_fault_code_Enabled", "tune-variant", "", ""}};
134 | static const char *const haLiveDescriptor[][4]{
135 | // state_topic, icon, unit_ofmeasurement, class
136 | {"AC_in_Frequenz", "import", "Hz", "frequency"},
137 | {"AC_in_generation_day", "import", "Wh", "energy"},
138 | {"AC_in_generation_month", "import", "Wh", "energy"},
139 | {"AC_in_generation_sum", "import", "Wh", "energy"},
140 | {"AC_in_generation_year", "import", "Wh", "energy"},
141 | {"AC_in_Voltage", "import", "V", "voltage"},
142 | {"AC_out_Frequenz", "export", "Hz", "frequency"},
143 | {"AC_out_percent", "export", "%", "power_factor"},
144 | {"AC_out_VA", "export", "VA", "apparent_power"},
145 | {"AC_out_Voltage", "export", "V", "voltage"},
146 | {"AC_out_Watt", "export", "W", "power"},
147 | {"AC_output_current", "export", "A", "current"},
148 | {"AC_output_frequency", "export", "Hz", "frequency"},
149 | {"AC_output_power", "export", "W", "power"},
150 | {"AC_output_voltage", "export", "V", "voltage"},
151 | //{"ACDC_Power_Direction","sign-direction","",""},
152 | {"Battery_capacity", "battery-high", "%", "battery"},
153 | //{"Battery_Charge_Current","battery-charging-high","A","current"},
154 | //{"Battery_Discharge_Current","battery-charging-outli","A","current"},
155 | {"Battery_Load", "battery-charging-high", "A", "current"},
156 | {"Battery_Percent", "battery-charging-high", "%", "battery"},
157 | {"Battery_Power_Direction", "battery-charging-high", "", ""},
158 | //{"Battery_SCC_Volt","battery-high","V","voltage"},
159 | //{"Battery_SCC2_Volt","battery-high","V","voltage"},
160 | {"Battery_temperature", "thermometer-lines", "°C", "temperature"},
161 | {"Battery_Voltage", "battery-high", "V", "voltage"},
162 | //{"Battery_voltage_offset_fans_on","fan","",""},
163 | //{"Configuration_State","state-machine","",""},
164 | //{"Country","earth","",""},
165 | //{"Device_Status","state-machine","",""},
166 | //{"EEPROM_Version","chip","",""},
167 | {"Fan_speed","fan","%",""},
168 | {"Fault_code","alert-outline","",""},
169 | {"Grid_frequency", "import", "Hz", "frequency"},
170 | {"Grid_voltage", "import", "V", "voltage"},
171 | {"Inverter_Bus_Temperature", "thermometer-lines", "°C", "temperature"},
172 | {"Inverter_Bus_Voltage", "flash-triangle-outline", "V", "voltage"},
173 | //{"Inverter_charge_state","car-turbocharger","",""},
174 | {"Inverter_Operation_Mode", "car-turbocharger", "", ""},
175 | {"Inverter_temperature", "thermometer-lines", "°C", "temperature"},
176 | //{"Line_Power_Direction","transmission-tower","",""},
177 | //{"Load_Connection","connection","",""},
178 | {"Local_Parallel_ID", "card-account-details-outline", "", ""},
179 | //{"Max_temperature","thermometer-plus","C","temperature"},
180 | //{"MPPT1_Charger_Status","car-turbocharger","",""},
181 | {"MPPT1_Charger_Temperature", "thermometer-lines", "°C", "temperature"},
182 | //{"MPPT2_CHarger_Status","car-turbocharger","",""},
183 | {"MPPT2_Charger_Temperature", "thermometer-lines", "°C", "temperature"},
184 | {"Negative_battery_voltage", "battery-minus-outline", "V", "voltage"},
185 | {"Output_current", "export", "A", "current"},
186 | {"Output_load_percent", "export", "%", "battery"},
187 | {"Output_power", "export", "W", "power"},
188 | //{"PBUS_voltage","","V","voltage"},
189 | {"Positive_battery_voltage", "car-battery", "V", "voltage"},
190 | {"PV_Charging_Power", "solar-power-variant", "W", "power"},
191 | {"PV_generation_day", "solar-power-variant", "Wh", "energy"},
192 | {"PV_generation_month", "solar-power-variant", "Wh", "energy"},
193 | {"PV_generation_sum", "solar-power-variant", "Wh", "energy"},
194 | {"PV_generation_year", "solar-power-variant", "Wh", "energy"},
195 | {"PV_Input_Current", "solar-power-variant", "A", "current"},
196 | {"PV_Input_Power", "solar-power-variant", "W", "power"},
197 | {"PV_Input_Voltage", "solar-power-variant", "V", "voltage"},
198 | {"PV1_input_power", "solar-power-variant", "W", "power"},
199 | {"PV1_input_voltage", "solar-power-variant", "V", "voltage"},
200 | {"PV2_Charging_Power", "solar-power-variant", "W", "power"},
201 | {"PV2_Input_Current", "solar-power-variant", "A", "current"},
202 | {"PV2_input_power", "solar-power-variant", "W", "power"},
203 | {"PV2_input_voltage", "solar-power-variant", "V", "voltage"},
204 | {"PV3_input_power", "solar-power-variant", "W", "power"},
205 | {"PV3_input_voltage", "solar-power-variant", "V", "voltage"},
206 | //{"SBUS_voltage","flash-triangle-outline","V","voltage"},
207 | {"Solar_feed_to_grid_power", "solar-power-variant", "W", "power"},
208 | {"Solar_feed_to_Grid_status", "solar-power-variant", "", ""},
209 | //{"Status_Flag","flag","",""},
210 | //{"Time_until_absorb_charge","solar-power-variant","s","duration"},
211 | //{"Time_until_float_charge","solar-power-variant","s","duration"},
212 | {"Tracker_temperature", "thermometer-lines", "°C", "temperature"},
213 | {"Transformer_temperature", "thermometer-lines", "°C", "temperature"},
214 | {"Warning_Code", "alert-outline", "", ""}};
--------------------------------------------------------------------------------
/src/modbus/modbus.h:
--------------------------------------------------------------------------------
1 | #include "SoftwareSerial.h"
2 | #ifndef MODBUS_H
3 | #define MODBUS_H
4 |
5 | #include
6 | #include
7 | #include "modbus_registers.h"
8 | extern JsonObject deviceJson;
9 | extern JsonObject staticData;
10 | extern JsonObject liveData;
11 |
12 | #define RS485_DIR_PIN 14 // D5
13 | #define RS485_ESP01_DIR_PIN 0
14 |
15 | #define RS485_BAUDRATE 19200
16 |
17 | #define INVERTER_MODBUS_ADDR 4
18 |
19 | #define MODBUS_RETRIES 2
20 |
21 | typedef enum
22 | {
23 | READ_FAIL = 0,
24 | READ_OK = 1,
25 | } response_type_t;
26 |
27 | typedef struct
28 | {
29 | JsonObject *variant;
30 | const modbus_register_t *registers;
31 | uint8_t array_size;
32 | uint8_t curr_register;
33 | } modbus_register_info_t;
34 |
35 | class MODBUS
36 | {
37 | public:
38 | const uint8_t MAX_CONNECTION_ATTEMPTS = 10;
39 | bool requestStaticData = true;
40 | bool connection = false;
41 | modbus_register_info_t live_info;
42 | modbus_register_info_t static_info;
43 | MODBUS(SoftwareSerial *port);
44 |
45 | /**
46 | * @brief Initializes this driver
47 | * @details Configures the serial peripheral and pre-loads the transmit buffer with command-independent bytes
48 | */
49 | bool Init();
50 |
51 | /**
52 | * @brief Updating the Data from the inverter
53 | */
54 | void loop();
55 |
56 | /**
57 | * @brief Send custom command to the device
58 | */
59 | bool sendCommand(String command);
60 |
61 | /**
62 | * @brief callback function
63 | *
64 | */
65 | void callback(std::function func);
66 | std::function requestCallback;
67 | bool readModbusRegisterToJson(const modbus_register_t *reg, JsonObject *variant);
68 | response_type_t parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_reg_on_error = true);
69 | bool isAllRegistersRead(modbus_register_info_t ®ister_info);
70 | bool autoDetect();
71 | /**
72 | * @brief Sends a complete packet with the specified command
73 | * @details sends the command over the specified serial connection
74 | */
75 | String requestData(String command);
76 |
77 | private:
78 | bool device_found = false;
79 | unsigned long previousTime = 0;
80 | unsigned long cmdDelayTime = 100;
81 |
82 | byte requestCounter = 0;
83 |
84 | long long int connectionCounter = 0;
85 |
86 | byte qexCounter = 0;
87 |
88 | static void preTransmission();
89 | static void postTransmission();
90 | String toBinary(uint16_t input);
91 | bool decodeDiematicDecimal(uint16_t int_input, int8_t decimals, float *value_ptr);
92 | bool getModbusResultMsg(uint8_t result);
93 | bool getModbusValue(uint16_t register_id, modbus_entity_t modbus_entity, uint16_t *value_ptr);
94 | /**
95 | * @brief get the crc from a string
96 | */
97 | uint16_t getCRC(String data);
98 |
99 | /**
100 | * @brief get the crc from a string
101 | */
102 | byte getCHK(String data);
103 |
104 | String retrieveModel();
105 |
106 | /**
107 | * @brief Serial interface used for communication
108 | * @details This is set in the constructor
109 | */
110 | SoftwareSerial *my_serialIntf;
111 |
112 | ModbusMaster mb;
113 | };
114 |
115 | #endif
116 |
--------------------------------------------------------------------------------
/src/modbus/modbus_registers.h:
--------------------------------------------------------------------------------
1 | #ifndef SRC_MODBUS_REGISTERS_H_
2 | #define SRC_MODBUS_REGISTERS_H_
3 | #include "Arduino.h"
4 |
5 | typedef enum
6 | {
7 | MODBUS_TYPE_HOLDING = 0x00, /*!< Modbus Holding register. */
8 | // MODBUS_TYPE_INPUT, /*!< Modbus Input register. */
9 | // MODBUS_TYPE_COIL, /*!< Modbus Coils. */
10 | // MODBUS_TYPE_DISCRETE, /*!< Modbus Discrete bits. */
11 | // MODBUS_TYPE_COUNT,
12 | // MODBUS_TYPE_UNKNOWN = 0xFF
13 | } modbus_entity_t;
14 |
15 | typedef enum
16 | {
17 | // REGISTER_TYPE_U8 = 0x00, /*!< Unsigned 8 */
18 | REGISTER_TYPE_U16 = 0x01, /*!< Unsigned 16 */
19 | REGISTER_TYPE_INT16 = 0x02, /*!< Signed 16 */
20 | // REGISTER_TYPE_U32 = 0x02, /*!< Unsigned 32 */
21 | // REGISTER_TYPE_FLOAT = 0x03, /*!< Float type */
22 | REGISTER_TYPE_ASCII = 0x04, /*!< ASCII type */
23 | REGISTER_TYPE_DIEMATIC_ONE_DECIMAL = 0x05,
24 | REGISTER_TYPE_DIEMATIC_TWO_DECIMAL = 0x06,
25 | REGISTER_TYPE_BITFIELD = 0x07,
26 | REGISTER_TYPE_DEBUG = 0x08,
27 | REGISTER_TYPE_CUSTOM_VAL_NAME = 0x09,
28 | } register_type_t;
29 |
30 | typedef union
31 | {
32 | const char *bitfield[16];
33 | } optional_param_t;
34 |
35 | typedef struct
36 | {
37 | uint16_t id;
38 | modbus_entity_t modbus_entity; /*!< Type of modbus parameter */
39 | register_type_t type; /*!< Float, U8, U16, U32, ASCII, etc. */
40 | const char *name;
41 | optional_param_t optional_param;
42 | } modbus_register_t;
43 |
44 | const modbus_register_t registers_live[] = {
45 |
46 | {25201, MODBUS_TYPE_HOLDING, REGISTER_TYPE_CUSTOM_VAL_NAME, "Inverter_Operation_Mode", {.bitfield = {
47 | "Power On",
48 | "Self Test",
49 | "OffGrid",
50 | "GridTie",
51 | "ByPass",
52 | "Stop",
53 | "GridCharging",
54 | }}},
55 |
56 | {25205, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Battery_Voltage"},
57 | {25206, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "AC_out_Voltage"},
58 | {25207, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "AC_in_Voltage"},
59 | {25208, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Inverter_Bus_Voltage"},
60 | {25225, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_TWO_DECIMAL, "AC_out_Frequenz"},
61 | {25226, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_TWO_DECIMAL, "AC_in_Frequenz"},
62 |
63 | {25216, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Output_load_percent"},
64 | {15205, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "PV_Input_Voltage"},
65 | {15208, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "PV_Charging_Power"},
66 | {15207, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "PV_Input_Current"},
67 | {25233, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Inverter_Bus_Temperature"},
68 | {25234, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Transformer_temperature"},
69 | {15209, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "MPPT1_Charger_Temperature"},
70 |
71 | {25215, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "AC_out_Watt"}, //W
72 | {25216, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "AC_out_percent"}, //%
73 |
74 | {25274, MODBUS_TYPE_HOLDING, REGISTER_TYPE_INT16, "Battery_Load"},
75 | };
76 |
77 | const modbus_register_t registers_static[] = {
78 |
79 | {10110, MODBUS_TYPE_HOLDING, REGISTER_TYPE_CUSTOM_VAL_NAME, "Battery_type", {.bitfield = {
80 | "No choose",
81 | "User defined",
82 | "Lithium",
83 | "Sealed Lead",
84 | "AGM",
85 | "GEL",
86 | "Flooded",
87 | }}},
88 | {10103, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Battery_float_voltage"},
89 | };
90 |
91 |
92 | #define DEVICE_MODEL_HIGH "Device_Model_Hight"
93 | #define DEVICE_MODEL_LOW "Device_Model_Low"
94 |
95 | const modbus_register_t registers_device_model[] = {
96 | {20000, MODBUS_TYPE_HOLDING, REGISTER_TYPE_ASCII, "Device_Model_Hight"},
97 | {20001, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Device_Model_Low"}
98 | };
99 |
100 | #endif // SRC_MODBUS_REGISTERS_H_
--------------------------------------------------------------------------------
/src/status-LED.h:
--------------------------------------------------------------------------------
1 | #include
2 | /*
3 | Blinking LED = Relais Off
4 | Waveing LED = Relais On
5 | every 5 seconds:
6 | 1x all ok - Working
7 | 2x no Inverter Connection
8 | 3x no MQTT Connection
9 | 4x no WiFi Connection
10 |
11 |
12 | //#define LED_BUILTIN 5
13 | const int PIN_LED = 5; // COM
14 | const int PIN_LED2 = 0; // SRV
15 | const int PIN_LED4 = 4; // NET
16 |
17 |
18 | */
19 | unsigned int ledPin = 0;
20 | unsigned int ledTimer = 0;
21 | unsigned int repeatTime = 5000;
22 | unsigned int cycleTime = 250;
23 | unsigned int cycleMillis = 0;
24 | byte ledState = 0;
25 |
26 | // bool waveHelper = false;
27 | void notificationLED()
28 | {
29 |
30 | if (millis() >= (ledTimer + repeatTime) && ledState == 0)
31 | {
32 | if (WiFi.status() != WL_CONNECTED)
33 | ledState = 4;
34 | else if (!mqttclient.connected() && strcmp(settings.data.mqttServer, "") != 0)
35 | ledState = 3;
36 | else if (!mppClient.connection)
37 | ledState = 2;
38 | else if (WiFi.status() == WL_CONNECTED && mqttclient.connected() && mppClient.connection)
39 | ledState = 1;
40 |
41 | #ifdef isUART_HARDWARE
42 | digitalWrite(LED_COM, !mppClient.connection); // make it blink blink when communication
43 | digitalWrite(LED_SRV, !mqttclient.connected()); // make it blinky when sending data
44 | digitalWrite(LED_NET, !(WiFi.status() == WL_CONNECTED) ? true : false);
45 | #endif
46 | }
47 |
48 | if (ledState > 0)
49 | {
50 | if (millis() >= (cycleMillis + cycleTime) /*&& relaisOn != true*/)
51 | {
52 | if (ledPin == 0)
53 | {
54 | ledPin = 255;
55 | }
56 | else
57 | {
58 | ledPin = 0;
59 | ledState--;
60 | }
61 | cycleMillis = millis();
62 | if (ledState == 0)
63 | {
64 | ledTimer = millis();
65 | }
66 | }
67 | /* make it later
68 | if (millis() >= (cycleMillis + cycleTime) && relaisOn == true)
69 | {
70 | //ledPin = 127.0 + 128.0 * sin((millis() / (float)(cycleTime * 2)) * 2.0 * PI);
71 | ledPin = (cos((millis() / (float)(cycleTime/4)) - PI)*0.5+0.5)*255;
72 |
73 | if (ledPin == 254 && waveHelper == false)
74 | {
75 | waveHelper = true;
76 | }
77 | if (ledPin == 0 && waveHelper == true)
78 | {
79 | ledState--;
80 | waveHelper = false;
81 | }
82 |
83 |
84 | if (ledState == 0)
85 | {
86 | ledTimer = millis();
87 | }
88 | }
89 | */
90 | }
91 | analogWrite(LED_PIN, 255 - ledPin);
92 | }
--------------------------------------------------------------------------------
/src/webpages/HTML_CONFIRM_RESET.html:
--------------------------------------------------------------------------------
1 | %pre_head_template%
2 |
3 | Erase all Data?
4 |
8 |
9 | %pre_foot_template%
10 |
--------------------------------------------------------------------------------
/src/webpages/HTML_FOOT.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | Solar2MQTT %pre_software_version% By Softwarecrash
11 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
47 |
48 |