addd ssd1306 lib
14
P1_gateway_FW/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"*.tcc": "cpp",
|
||||
"istream": "cpp",
|
||||
"ostream": "cpp",
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"initializer_list": "cpp"
|
||||
}
|
||||
}
|
||||
43
P1_gateway_FW/include/dsmr.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Arduino DSMR parser.
|
||||
*
|
||||
* This software is licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2015 Matthijs Kooijman <matthijs@stdin.nl>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Main included file. If you include this, you'll get everything,
|
||||
* imported into global scope
|
||||
*/
|
||||
|
||||
#ifndef DSMR_INCLUDE_DSMR_H
|
||||
#define DSMR_INCLUDE_DSMR_H
|
||||
|
||||
#include "../lib/dsmr/parser.h"
|
||||
#include "../lib/dsmr/reader.h"
|
||||
#include "../lib/dsmr/fields.h"
|
||||
|
||||
// Allow using everything without the namespace prefixes
|
||||
using namespace dsmr;
|
||||
using namespace dsmr::fields;
|
||||
|
||||
#endif // DSMR_INCLUDE_DSMR_H
|
||||
7
P1_gateway_FW/lib/MQTT/.editorconfig
Normal file
@@ -0,0 +1,7 @@
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[src/*.h,src/*.cpp,examples/**.ino]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
2
P1_gateway_FW/lib/MQTT/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
cmake-build-debug/
|
||||
1
P1_gateway_FW/lib/MQTT/.piopm
Normal file
@@ -0,0 +1 @@
|
||||
{"type": "library", "name": "MQTT", "version": "2.4.8", "spec": {"owner": "256dpi", "id": 617, "name": "MQTT", "requirements": null, "url": null}}
|
||||
46
P1_gateway_FW/lib/MQTT/.travis.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
language: generic
|
||||
env:
|
||||
global:
|
||||
- IDE_VERSION=1.8.7
|
||||
matrix:
|
||||
- EXAMPLE="AdafruitHuzzahESP8266" BOARD="esp8266:esp8266:huzzah:eesz=4M3M,xtal=80"
|
||||
- EXAMPLE="AdafruitHuzzahESP8266Secure" BOARD="esp8266:esp8266:huzzah:eesz=4M3M,xtal=80"
|
||||
- EXAMPLE="ArduinoEthernetShield" BOARD="arduino:avr:uno"
|
||||
- EXAMPLE="ArduinoMKRGSM1400" BOARD="arduino:samd:mkrgsm1400"
|
||||
- EXAMPLE="ArduinoMKRGSM1400Secure" BOARD="arduino:samd:mkrgsm1400"
|
||||
- EXAMPLE="ArduinoWiFi101Secure" BOARD="arduino:avr:uno"
|
||||
- EXAMPLE="ArduinoWiFiShield" BOARD="arduino:avr:uno"
|
||||
- EXAMPLE="ArduinoYun" BOARD="arduino:avr:yun"
|
||||
- EXAMPLE="ArduinoYunSecure" BOARD="arduino:avr:yun"
|
||||
- EXAMPLE="ESP32DevelopmentBoard" BOARD="espressif:esp32:esp32:FlashFreq=80"
|
||||
- EXAMPLE="ESP32DevelopmentBoardSecure" BOARD="espressif:esp32:esp32:FlashFreq=80"
|
||||
before_install:
|
||||
- /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
|
||||
- sleep 3
|
||||
- export DISPLAY=:1.0
|
||||
- wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz
|
||||
- tar xf arduino-$IDE_VERSION-linux64.tar.xz
|
||||
- mv arduino-$IDE_VERSION ~/arduino-ide
|
||||
- export PATH=$PATH:~/arduino-ide
|
||||
- if [[ "$BOARD" =~ "esp8266:esp8266:" ]]; then
|
||||
arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --install-boards esp8266:esp8266;
|
||||
arduino --pref "boardsmanager.additional.urls=" --save-prefs;
|
||||
fi
|
||||
- if [[ "$BOARD" =~ "espressif:esp32:" ]]; then
|
||||
mkdir -p ~/Arduino/hardware/espressif &&
|
||||
cd ~/Arduino/hardware/espressif &&
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 &&
|
||||
cd esp32/tools/ &&
|
||||
python get.py &&
|
||||
cd $TRAVIS_BUILD_DIR;
|
||||
fi
|
||||
- if [[ "$BOARD" =~ "arduino:samd:mkrgsm1400" ]]; then
|
||||
arduino --install-boards arduino:samd;
|
||||
arduino --install-library MKRGSM;
|
||||
fi
|
||||
- arduino --install-library WiFi101
|
||||
install:
|
||||
- mkdir -p ~/Arduino/libraries
|
||||
- ln -s $PWD ~/Arduino/libraries/.
|
||||
script:
|
||||
- arduino --verbose-build --verify --board $BOARD $PWD/examples/$EXAMPLE/$EXAMPLE.ino;
|
||||
39
P1_gateway_FW/lib/MQTT/CMakeLists.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
# Uncompilable CMake File to enable project editing with CLion IDE
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(arduino-mqtt)
|
||||
|
||||
include_directories(
|
||||
/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino
|
||||
/Users/256dpi/Development/Arduino/libraries/Ethernet/src
|
||||
/Users/256dpi/Development/Arduino/libraries/WiFi101/src
|
||||
/Users/256dpi/Development/Arduino/libraries/MKRGSM/src
|
||||
/Applications/Arduino.app/Contents/Java/libraries/Bridge/src
|
||||
/Users/256dpi/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/libraries/ESP8266WiFi/src
|
||||
/Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFi/src
|
||||
/Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFiClientSecure/src
|
||||
src)
|
||||
|
||||
include_directories(src/)
|
||||
|
||||
set(CMAKE_CXX_FLAGS -std=c++11)
|
||||
|
||||
set(SOURCE_FILES
|
||||
examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino
|
||||
examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino
|
||||
examples/ArduinoEthernetShield/ArduinoEthernetShield.ino
|
||||
examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino
|
||||
examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino
|
||||
examples/ArduinoWiFi101/ArduinoWiFi101.ino
|
||||
examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino
|
||||
examples/ArduinoWiFiShield/ArduinoWiFiShield.ino
|
||||
examples/ArduinoYun/ArduinoYun.ino
|
||||
examples/ArduinoYunSecure/ArduinoYunSecure.ino
|
||||
examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino
|
||||
examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino
|
||||
src/lwmqtt
|
||||
src/MQTT.h
|
||||
src/MQTTClient.h
|
||||
src/MQTTClient.cpp)
|
||||
|
||||
add_executable(arduino-mqtt ${SOURCE_FILES})
|
||||
21
P1_gateway_FW/lib/MQTT/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Joël Gähwiler
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
14
P1_gateway_FW/lib/MQTT/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
all: fmt
|
||||
|
||||
fmt:
|
||||
clang-format -i src/*.h src/*.cpp -style="{BasedOnStyle: Google, ColumnLimit: 120}"
|
||||
|
||||
update:
|
||||
rm -rf ./lwmqtt
|
||||
git clone --branch v0.6.4 https://github.com/256dpi/lwmqtt.git ./lwmqtt
|
||||
mkdir -p ./src/lwmqtt
|
||||
cp -r ./lwmqtt/src/*.c ./src/lwmqtt/
|
||||
cp -r ./lwmqtt/src/*.h ./src/lwmqtt/
|
||||
cp -r ./lwmqtt/include/*.h ./src/lwmqtt/
|
||||
rm -rf ./lwmqtt
|
||||
sed -i '' "s/<lwmqtt.h>/\"lwmqtt.h\"/g" ./src/lwmqtt/*
|
||||
260
P1_gateway_FW/lib/MQTT/README.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# arduino-mqtt
|
||||
|
||||
[](https://travis-ci.org/256dpi/arduino-mqtt)
|
||||
[](https://github.com/256dpi/arduino-mqtt/releases)
|
||||
|
||||
This library bundles the [lwmqtt](https://github.com/256dpi/lwmqtt) MQTT 3.1.1 client and adds a thin wrapper to get an Arduino like API.
|
||||
|
||||
Download the latest version from the [release](https://github.com/256dpi/arduino-mqtt/releases) section. Or even better use the builtin Library Manager in the Arduino IDE and search for "MQTT".
|
||||
|
||||
The library is also available on [PlatformIO](https://platformio.org/lib/show/617/MQTT). You can install it by running: `pio lib install "MQTT"`.
|
||||
|
||||
## Compatibility
|
||||
|
||||
The following examples show how you can use the library with various Arduino compatible hardware:
|
||||
|
||||
- [Arduino Yun & Yun-Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYun/ArduinoYun.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYunSecure/ArduinoYunSecure.ino))
|
||||
- [Arduino Ethernet Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino)
|
||||
- [Arduino WiFi Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino)
|
||||
- [Adafruit HUZZAH ESP8266](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino))
|
||||
- [Arduino/Genuino WiFi101 Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101/ArduinoWiFi101.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino))
|
||||
- [Arduino MKR GSM 1400](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino))
|
||||
- [ESP32 Development Board](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino))
|
||||
|
||||
Other shields and boards should also work if they provide a [Client](https://www.arduino.cc/en/Reference/ClientConstructor) based network implementation.
|
||||
|
||||
**Check out the [Wiki](https://github.com/256dpi/arduino-mqtt/wiki) to find more examples.**
|
||||
|
||||
## Notes
|
||||
|
||||
- The maximum size for packets being published and received is set by default to 128 bytes. To change the buffer sizes, you need to use `MQTTClient client(256)` instead of just `MQTTClient client` on the top of your sketch. The passed value denotes the read and write buffer size.
|
||||
|
||||
- On the ESP8266 it has been reported that an additional `delay(10);` after `client.loop();` fixes many stability issues with WiFi connections.
|
||||
|
||||
- To use the library with shiftr.io, you need to provide the token key (username) and token secret (password) as the second and third argument to `client.connect(name, key, secret)`.
|
||||
|
||||
## Example
|
||||
|
||||
The following example uses an Arduino MKR1000 to connect to shiftr.io. You can check on your device after a successful connection here: https://shiftr.io/try.
|
||||
|
||||
```c++
|
||||
#include <SPI.h>
|
||||
#include <WiFi101.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char ssid[] = "ssid";
|
||||
const char pass[] = "pass";
|
||||
|
||||
WiFiClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("checking wifi...");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
client.begin("broker.shiftr.io", net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Initialize the object using the hostname of the broker, the brokers port (default: `1883`) and the underlying Client class for network transport:
|
||||
|
||||
```c++
|
||||
void begin(Client &client);
|
||||
void begin(const char hostname[], Client &client);
|
||||
void begin(const char hostname[], int port, Client &client);
|
||||
void begin(IPAddress address, Client &client);
|
||||
void begin(IPAddress address, int port, Client &client);
|
||||
```
|
||||
|
||||
- Specify port `8883` when using secure clients for encrypted connections.
|
||||
- Local domain names (e.g. `Computer.local` on OSX) are not supported by Arduino. You need to set the IP address directly.
|
||||
|
||||
The hostname and port can also be changed after calling `begin()`:
|
||||
|
||||
```c++
|
||||
void setHost(const char hostname[]);
|
||||
void setHost(const char hostname[], int port);
|
||||
void setHost(IPAddress address);
|
||||
void setHost(IPAddress address, int port);
|
||||
```
|
||||
|
||||
Set a will message (last testament) that gets registered on the broker after connecting. `setWill()` has to be called before calling `connect()`:
|
||||
|
||||
```c++
|
||||
void setWill(const char topic[]);
|
||||
void setWill(const char topic[], const char payload[]);
|
||||
void setWill(const char topic[], const char payload[], bool retained, int qos);
|
||||
void clearWill();
|
||||
```
|
||||
|
||||
Register a callback to receive messages:
|
||||
|
||||
```c++
|
||||
void onMessage(MQTTClientCallbackSimple);
|
||||
// Callback signature: void messageReceived(String &topic, String &payload) {}
|
||||
|
||||
void onMessageAdvanced(MQTTClientCallbackAdvanced);
|
||||
// Callback signature: void messageReceived(MQTTClient *client, char topic[], char payload[], int payload_length) {}
|
||||
```
|
||||
|
||||
- The set callback is mostly called during a call to `loop()` but may also be called during a call to `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` if messages have been received before receiving the required acknowledgement. Therefore, it is strongly recommended to not call `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` directly in the callback.
|
||||
- In case you need a reference to an object that manages the client, use the `void * ref` property on the client to store a pointer, and access it directly from the advanced callback..
|
||||
|
||||
Set more advanced options:
|
||||
|
||||
```c++
|
||||
void setKeepAlive(int keepAlive);
|
||||
void setCleanSession(bool cleanSession);
|
||||
void setTimeout(int timeout);
|
||||
void setOptions(int keepAlive, bool cleanSession, int timeout);
|
||||
```
|
||||
|
||||
- The `keepAlive` option controls the keep alive interval in seconds (default: 10).
|
||||
- The `cleanSession` option controls the session retention on the broker side (default: true).
|
||||
- The `timeout` option controls the default timeout for all commands in milliseconds (default: 1000).
|
||||
|
||||
Set a custom clock source "custom millis" callback to enable deep sleep applications:
|
||||
|
||||
```c++
|
||||
void setClockSource(MQTTClientClockSource);
|
||||
// Callback signature: uint32_t clockSource() {}
|
||||
```
|
||||
|
||||
- The specified callback is used by the internal timers to get a monotonic time in milliseconds. Since the clock source for the built-in `millis` is stopped when the the Arduino goes into deep sleep, you need to provide a custom callback that first syncs with a built-in or external Real Time Clock (RTC). You can pass `NULL` to reset to the default implementation.
|
||||
|
||||
Connect to broker using the supplied client id and an optional username and password:
|
||||
|
||||
```c++
|
||||
bool connect(const char clientID[], bool skip = false);
|
||||
bool connect(const char clientID[], const char username[], bool skip = false);
|
||||
bool connect(const char clientID[], const char username[], const char password[], bool skip = false);
|
||||
```
|
||||
|
||||
- If the `skip` option is set to true, the client will skip the network level connection and jump to the MQTT level connection. This option can be used in order to establish and verify TLS connections manually before giving control to the MQTT client.
|
||||
- The functions return a boolean that indicates if the connection has been established successfully (true).
|
||||
|
||||
Publishes a message to the broker with an optional payload:
|
||||
|
||||
```c++
|
||||
bool publish(const String &topic);
|
||||
bool publish(const char topic[]);
|
||||
bool publish(const String &topic, const String &payload);
|
||||
bool publish(const String &topic, const String &payload, bool retained, int qos);
|
||||
bool publish(const char topic[], const String &payload);
|
||||
bool publish(const char topic[], const String &payload, bool retained, int qos);
|
||||
bool publish(const char topic[], const char payload[]);
|
||||
bool publish(const char topic[], const char payload[], bool retained, int qos);
|
||||
bool publish(const char topic[], const char payload[], int length);
|
||||
bool publish(const char topic[], const char payload[], int length, bool retained, int qos);
|
||||
```
|
||||
|
||||
- The functions return a boolean that indicates if the publish has been successful (true).
|
||||
|
||||
Subscribe to a topic:
|
||||
|
||||
```c++
|
||||
bool subscribe(const String &topic);
|
||||
bool subscribe(const String &topic, int qos);
|
||||
bool subscribe(const char topic[]);
|
||||
bool subscribe(const char topic[], int qos);
|
||||
```
|
||||
|
||||
- The functions return a boolean that indicates if the subscribe has been successful (true).
|
||||
|
||||
Unsubscribe from a topic:
|
||||
|
||||
```c++
|
||||
bool unsubscribe(const String &topic);
|
||||
bool unsubscribe(const char topic[]);
|
||||
```
|
||||
|
||||
- The functions return a boolean that indicates if the unsubscribe has been successful (true).
|
||||
|
||||
Sends and receives packets:
|
||||
|
||||
```c++
|
||||
bool loop();
|
||||
```
|
||||
|
||||
- This function should be called in every `loop`.
|
||||
- The function returns a boolean that indicates if the loop has been successful (true).
|
||||
|
||||
Check if the client is currently connected:
|
||||
|
||||
```c++
|
||||
bool connected();
|
||||
```
|
||||
|
||||
Access low-level information for debugging:
|
||||
|
||||
```c++
|
||||
lwmqtt_err_t lastError();
|
||||
lwmqtt_return_code_t returnCode();
|
||||
```
|
||||
|
||||
- The error codes can be found [here](https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L11).
|
||||
- The return codes can be found [here](https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L243).
|
||||
|
||||
Disconnect from the broker:
|
||||
|
||||
```c++
|
||||
bool disconnect();
|
||||
```
|
||||
|
||||
- The function returns a boolean that indicates if the disconnect has been successful (true).
|
||||
|
||||
## Release Management
|
||||
|
||||
- Update version in `library.properties`.
|
||||
- Create release on GitHub.
|
||||
@@ -0,0 +1,74 @@
|
||||
// This example uses an Adafruit Huzzah ESP8266
|
||||
// to connect to shiftr.io.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Joël Gähwiler
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char ssid[] = "ssid";
|
||||
const char pass[] = "pass";
|
||||
|
||||
WiFiClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("checking wifi...");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
client.begin("broker.shiftr.io", net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
delay(10); // <- fixes some issues with WiFi stability
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// This example uses an Adafruit Huzzah ESP8266
|
||||
// to connect to shiftr.io.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Joël Gähwiler
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char ssid[] = "ssid";
|
||||
const char pass[] = "pass";
|
||||
|
||||
WiFiClientSecure net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("checking wifi...");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
// do not verify tls certificate
|
||||
// check the following example for methods to verify the server:
|
||||
// https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino
|
||||
net.setInsecure();
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
//
|
||||
// MQTT brokers usually use port 8883 for secure connections.
|
||||
client.begin("broker.shiftr.io", 8883, net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
delay(10); // <- fixes some issues with WiFi stability
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// This example uses an Arduino Uno together with
|
||||
// an Ethernet Shield to connect to shiftr.io.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Joël Gähwiler
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
|
||||
byte ip[] = {192, 168, 1, 177}; // <- change to match your network
|
||||
|
||||
EthernetClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("connecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Ethernet.begin(mac, ip);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
client.begin("broker.shiftr.io", net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// This example uses an Arduino MKR GSM 1400 board
|
||||
// to connect to shiftr.io.
|
||||
//
|
||||
// IMPORTANT: This example uses the new MKRGSM library.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Sandeep Mistry
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <MKRGSM.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char pin[] = "";
|
||||
const char apn[] = "apn";
|
||||
const char login[] = "login";
|
||||
const char password[] = "password";
|
||||
|
||||
GSMClient net;
|
||||
GPRS gprs;
|
||||
GSM gsmAccess;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
// connection state
|
||||
bool connected = false;
|
||||
|
||||
Serial.print("connecting to cellular network ...");
|
||||
|
||||
// After starting the modem with gsmAccess.begin()
|
||||
// attach to the GPRS network with the APN, login and password
|
||||
while (!connected) {
|
||||
if ((gsmAccess.begin(pin) == GSM_READY) &&
|
||||
(gprs.attachGPRS(apn, login, password) == GPRS_READY)) {
|
||||
connected = true;
|
||||
} else {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
client.begin("broker.shiftr.io", net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// This example uses an Arduino MKR GSM 1400 board
|
||||
// to securely connect to shiftr.io.
|
||||
//
|
||||
// IMPORTANT: This example uses the new MKRGSM library.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Sandeep Mistry
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <MKRGSM.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char pin[] = "";
|
||||
const char apn[] = "apn";
|
||||
const char login[] = "login";
|
||||
const char password[] = "password";
|
||||
|
||||
GSMSSLClient net;
|
||||
GPRS gprs;
|
||||
GSM gsmAccess;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
// connection state
|
||||
bool connected = false;
|
||||
|
||||
Serial.print("connecting to cellular network ...");
|
||||
|
||||
// After starting the modem with gsmAccess.begin()
|
||||
// attach to the GPRS network with the APN, login and password
|
||||
while (!connected) {
|
||||
if ((gsmAccess.begin(pin) == GSM_READY) &&
|
||||
(gprs.attachGPRS(apn, login, password) == GPRS_READY)) {
|
||||
connected = true;
|
||||
} else {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
//
|
||||
// MQTT brokers usually use port 8883 for secure connections.
|
||||
client.begin("broker.shiftr.io", 8883, net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// This example uses an Arduino/Genuino Zero together with
|
||||
// a WiFi101 Shield or a MKR1000 to connect to shiftr.io.
|
||||
//
|
||||
// IMPORTANT: This example uses the new WiFi101 library.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Gilberto Conti
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <WiFi101.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char ssid[] = "ssid";
|
||||
const char pass[] = "pass";
|
||||
|
||||
WiFiClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("checking wifi...");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
client.begin("broker.shiftr.io", net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// This example uses an Arduino/Genuino Zero together with
|
||||
// a WiFi101 Shield or a MKR1000 to connect to shiftr.io.
|
||||
//
|
||||
// IMPORTANT: This example uses the new WiFi101 library.
|
||||
//
|
||||
// IMPORTANT: You need to install/update the SSL certificates first:
|
||||
// https://github.com/arduino-libraries/WiFi101-FirmwareUpdater#to-update-ssl-certificates
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Gilberto Conti
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <WiFi101.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char ssid[] = "ssid";
|
||||
const char pass[] = "pass";
|
||||
|
||||
WiFiSSLClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("checking wifi...");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
//
|
||||
// MQTT brokers usually use port 8883 for secure connections.
|
||||
client.begin("broker.shiftr.io", 8883, net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// This example uses an Arduino Uno together with
|
||||
// a WiFi Shield to connect to shiftr.io.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Joël Gähwiler
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char ssid[] = "ssid";
|
||||
const char pass[] = "pass";
|
||||
|
||||
WiFiClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("checking wifi...");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
client.begin("broker.shiftr.io", net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
65
P1_gateway_FW/lib/MQTT/examples/ArduinoYun/ArduinoYun.ino
Normal file
@@ -0,0 +1,65 @@
|
||||
// This example uses an Arduino Yun or a Yun-Shield
|
||||
// and the MQTTClient to connect to shiftr.io.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Joël Gähwiler
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <BridgeClient.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
BridgeClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("connecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Bridge.begin();
|
||||
Serial.begin(115200);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
client.begin("broker.shiftr.io", net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// This example uses an Arduino Yun or a Yun-Shield
|
||||
// and the MQTTClient to connect to shiftr.io.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Joël Gähwiler
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <BridgeSSLClient.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
BridgeSSLClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("connecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Bridge.begin();
|
||||
Serial.begin(115200);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
//
|
||||
// MQTT brokers usually use port 8883 for secure connections.
|
||||
client.begin("broker.shiftr.io", 8883, net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// This example uses an ESP32 Development Board
|
||||
// to connect to shiftr.io.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Joël Gähwiler
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char ssid[] = "ssid";
|
||||
const char pass[] = "pass";
|
||||
|
||||
WiFiClient net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("checking wifi...");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
client.begin("broker.shiftr.io", net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
delay(10); // <- fixes some issues with WiFi stability
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// This example uses an ESP32 Development Board
|
||||
// to connect to shiftr.io.
|
||||
//
|
||||
// You can check on your device after a successful
|
||||
// connection here: https://shiftr.io/try.
|
||||
//
|
||||
// by Joël Gähwiler
|
||||
// https://github.com/256dpi/arduino-mqtt
|
||||
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <MQTT.h>
|
||||
|
||||
const char ssid[] = "ssid";
|
||||
const char pass[] = "pass";
|
||||
|
||||
WiFiClientSecure net;
|
||||
MQTTClient client;
|
||||
|
||||
unsigned long lastMillis = 0;
|
||||
|
||||
void connect() {
|
||||
Serial.print("checking wifi...");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("\nconnecting...");
|
||||
while (!client.connect("arduino", "try", "try")) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println("\nconnected!");
|
||||
|
||||
client.subscribe("/hello");
|
||||
// client.unsubscribe("/hello");
|
||||
}
|
||||
|
||||
void messageReceived(String &topic, String &payload) {
|
||||
Serial.println("incoming: " + topic + " - " + payload);
|
||||
|
||||
// Note: Do not use the client in the callback to publish, subscribe or
|
||||
// unsubscribe as it may cause deadlocks when other things arrive while
|
||||
// sending and receiving acknowledgments. Instead, change a global variable,
|
||||
// or push to a queue and handle it in the loop after calling `client.loop()`.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
|
||||
// by Arduino. You need to set the IP address directly.
|
||||
//
|
||||
// MQTT brokers usually use port 8883 for secure connections.
|
||||
client.begin("broker.shiftr.io", 8883, net);
|
||||
client.onMessage(messageReceived);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
delay(10); // <- fixes some issues with WiFi stability
|
||||
|
||||
if (!client.connected()) {
|
||||
connect();
|
||||
}
|
||||
|
||||
// publish a message roughly every second.
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
client.publish("/hello", "world");
|
||||
}
|
||||
}
|
||||
9
P1_gateway_FW/lib/MQTT/library.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
name=MQTT
|
||||
version=2.4.8
|
||||
author=Joel Gaehwiler <joel.gaehwiler@gmail.com>
|
||||
maintainer=Joel Gaehwiler <joel.gaehwiler@gmail.com>
|
||||
sentence=MQTT library for Arduino
|
||||
paragraph=This library bundles the lwmqtt client and adds a thin wrapper to get an Arduino like API.
|
||||
category=Communication
|
||||
url=https://github.com/256dpi/arduino-mqtt
|
||||
architectures=*
|
||||
6
P1_gateway_FW/lib/MQTT/src/MQTT.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef MQTT_H
|
||||
#define MQTT_H
|
||||
|
||||
#include "MQTTClient.h"
|
||||
|
||||
#endif
|
||||
422
P1_gateway_FW/lib/MQTT/src/MQTTClient.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
#include "MQTTClient.h"
|
||||
|
||||
inline void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout) {
|
||||
// cast timer reference
|
||||
auto t = (lwmqtt_arduino_timer_t *)ref;
|
||||
|
||||
// set future end time
|
||||
if (t->millis != nullptr) {
|
||||
t->end = (uint32_t)(t->millis() + timeout);
|
||||
} else {
|
||||
t->end = (uint32_t)(millis() + timeout);
|
||||
}
|
||||
}
|
||||
|
||||
inline int32_t lwmqtt_arduino_timer_get(void *ref) {
|
||||
// cast timer reference
|
||||
auto t = (lwmqtt_arduino_timer_t *)ref;
|
||||
|
||||
// get difference to end time
|
||||
if (t->millis != nullptr) {
|
||||
return (int32_t)(t->end - t->millis());
|
||||
} else {
|
||||
return (int32_t)(t->end - millis());
|
||||
}
|
||||
}
|
||||
|
||||
inline lwmqtt_err_t lwmqtt_arduino_network_read(void *ref, uint8_t *buffer, size_t len, size_t *read,
|
||||
uint32_t timeout) {
|
||||
// cast network reference
|
||||
auto n = (lwmqtt_arduino_network_t *)ref;
|
||||
|
||||
// set timeout
|
||||
uint32_t start = millis();
|
||||
|
||||
// reset counter
|
||||
*read = 0;
|
||||
|
||||
// read until all bytes have been read or timeout has been reached
|
||||
while (len > 0 && (millis() - start < timeout)) {
|
||||
// read from connection
|
||||
int r = n->client->read(buffer, len);
|
||||
|
||||
// handle read data
|
||||
if (r > 0) {
|
||||
buffer += r;
|
||||
*read += r;
|
||||
len -= r;
|
||||
continue;
|
||||
}
|
||||
|
||||
// wait/unblock for some time (RTOS based boards may otherwise fail since
|
||||
// the wifi task cannot provide the data)
|
||||
delay(0);
|
||||
|
||||
// otherwise check status
|
||||
if (!n->client->connected()) {
|
||||
return LWMQTT_NETWORK_FAILED_READ;
|
||||
}
|
||||
}
|
||||
|
||||
// check counter
|
||||
if (*read == 0) {
|
||||
return LWMQTT_NETWORK_TIMEOUT;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
inline lwmqtt_err_t lwmqtt_arduino_network_write(void *ref, uint8_t *buffer, size_t len, size_t *sent,
|
||||
uint32_t /*timeout*/) {
|
||||
// cast network reference
|
||||
auto n = (lwmqtt_arduino_network_t *)ref;
|
||||
|
||||
// write bytes
|
||||
*sent = n->client->write(buffer, len);
|
||||
if (*sent <= 0) {
|
||||
return LWMQTT_NETWORK_FAILED_WRITE;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
static void MQTTClientHandler(lwmqtt_client_t * /*client*/, void *ref, lwmqtt_string_t topic,
|
||||
lwmqtt_message_t message) {
|
||||
// get callback
|
||||
auto cb = (MQTTClientCallback *)ref;
|
||||
|
||||
// null terminate topic
|
||||
char terminated_topic[topic.len + 1];
|
||||
memcpy(terminated_topic, topic.data, topic.len);
|
||||
terminated_topic[topic.len] = '\0';
|
||||
|
||||
// null terminate payload if available
|
||||
if (message.payload != nullptr) {
|
||||
message.payload[message.payload_len] = '\0';
|
||||
}
|
||||
|
||||
// call the advanced callback and return if available
|
||||
if (cb->advanced != nullptr) {
|
||||
cb->advanced(cb->client, terminated_topic, (char *)message.payload, (int)message.payload_len);
|
||||
return;
|
||||
}
|
||||
|
||||
// return if simple callback is not set
|
||||
if (cb->simple == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create topic string
|
||||
String str_topic = String(terminated_topic);
|
||||
|
||||
// create payload string
|
||||
String str_payload;
|
||||
if (message.payload != nullptr) {
|
||||
str_payload = String((const char *)message.payload);
|
||||
}
|
||||
|
||||
// call simple callback
|
||||
cb->simple(str_topic, str_payload);
|
||||
}
|
||||
|
||||
MQTTClient::MQTTClient(int bufSize) {
|
||||
// allocate buffers
|
||||
this->bufSize = (size_t)bufSize;
|
||||
this->readBuf = (uint8_t *)malloc((size_t)bufSize + 1);
|
||||
this->writeBuf = (uint8_t *)malloc((size_t)bufSize);
|
||||
}
|
||||
|
||||
MQTTClient::~MQTTClient() {
|
||||
// free will
|
||||
this->clearWill();
|
||||
|
||||
// free hostname
|
||||
if (this->hostname != nullptr) {
|
||||
free((void *)this->hostname);
|
||||
}
|
||||
|
||||
// free buffers
|
||||
free(this->readBuf);
|
||||
free(this->writeBuf);
|
||||
}
|
||||
|
||||
void MQTTClient::begin(Client &_client) {
|
||||
// set client
|
||||
this->netClient = &_client;
|
||||
|
||||
// initialize client
|
||||
lwmqtt_init(&this->client, this->writeBuf, this->bufSize, this->readBuf, this->bufSize);
|
||||
|
||||
// set timers
|
||||
lwmqtt_set_timers(&this->client, &this->timer1, &this->timer2, lwmqtt_arduino_timer_set, lwmqtt_arduino_timer_get);
|
||||
|
||||
// set network
|
||||
lwmqtt_set_network(&this->client, &this->network, lwmqtt_arduino_network_read, lwmqtt_arduino_network_write);
|
||||
|
||||
// set callback
|
||||
lwmqtt_set_callback(&this->client, (void *)&this->callback, MQTTClientHandler);
|
||||
}
|
||||
|
||||
void MQTTClient::onMessage(MQTTClientCallbackSimple cb) {
|
||||
// set callback
|
||||
this->callback.client = this;
|
||||
this->callback.simple = cb;
|
||||
this->callback.advanced = nullptr;
|
||||
}
|
||||
|
||||
void MQTTClient::onMessageAdvanced(MQTTClientCallbackAdvanced cb) {
|
||||
// set callback
|
||||
this->callback.client = this;
|
||||
this->callback.simple = nullptr;
|
||||
this->callback.advanced = cb;
|
||||
}
|
||||
|
||||
void MQTTClient::setClockSource(MQTTClientClockSource cb) {
|
||||
this->timer1.millis = cb;
|
||||
this->timer2.millis = cb;
|
||||
}
|
||||
|
||||
void MQTTClient::setHost(IPAddress _address, int _port) {
|
||||
// set address and port
|
||||
this->address = _address;
|
||||
this->port = _port;
|
||||
}
|
||||
|
||||
void MQTTClient::setHost(const char _hostname[], int _port) {
|
||||
// free hostname if set
|
||||
if (this->hostname != nullptr) {
|
||||
free((void *)this->hostname);
|
||||
}
|
||||
|
||||
// set hostname and port
|
||||
this->hostname = strdup(_hostname);
|
||||
this->port = _port;
|
||||
}
|
||||
|
||||
void MQTTClient::setWill(const char topic[], const char payload[], bool retained, int qos) {
|
||||
// return if topic is missing
|
||||
if (topic == nullptr || strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clear existing will
|
||||
this->clearWill();
|
||||
|
||||
// allocate will
|
||||
this->will = (lwmqtt_will_t *)malloc(sizeof(lwmqtt_will_t));
|
||||
memset(this->will, 0, sizeof(lwmqtt_will_t));
|
||||
|
||||
// set topic
|
||||
this->will->topic = lwmqtt_string(strdup(topic));
|
||||
|
||||
// set payload if available
|
||||
if (payload != nullptr && strlen(payload) > 0) {
|
||||
this->will->payload = lwmqtt_string(strdup(payload));
|
||||
}
|
||||
|
||||
// set flags
|
||||
this->will->retained = retained;
|
||||
this->will->qos = (lwmqtt_qos_t)qos;
|
||||
}
|
||||
|
||||
void MQTTClient::clearWill() {
|
||||
// return if not set
|
||||
if (this->will == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// free payload if set
|
||||
if (this->will->payload.len > 0) {
|
||||
free(this->will->payload.data);
|
||||
}
|
||||
|
||||
// free topic if set
|
||||
if (this->will->topic.len > 0) {
|
||||
free(this->will->topic.data);
|
||||
}
|
||||
|
||||
// free will
|
||||
free(this->will);
|
||||
this->will = nullptr;
|
||||
}
|
||||
|
||||
void MQTTClient::setKeepAlive(int _keepAlive) { this->keepAlive = _keepAlive; }
|
||||
|
||||
void MQTTClient::setCleanSession(bool _cleanSession) { this->cleanSession = _cleanSession; }
|
||||
|
||||
void MQTTClient::setTimeout(int _timeout) { this->timeout = _timeout; }
|
||||
|
||||
bool MQTTClient::publish(const char topic[], const char payload[], int length, bool retained, int qos) {
|
||||
// return immediately if not connected
|
||||
if (!this->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// prepare message
|
||||
lwmqtt_message_t message = lwmqtt_default_message;
|
||||
message.payload = (uint8_t *)payload;
|
||||
message.payload_len = (size_t)length;
|
||||
message.retained = retained;
|
||||
message.qos = lwmqtt_qos_t(qos);
|
||||
|
||||
// publish message
|
||||
this->_lastError = lwmqtt_publish(&this->client, lwmqtt_string(topic), message, this->timeout);
|
||||
if (this->_lastError != LWMQTT_SUCCESS) {
|
||||
// close connection
|
||||
this->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MQTTClient::connect(const char clientID[], const char username[], const char password[], bool skip) {
|
||||
// close left open connection if still connected
|
||||
if (!skip && this->connected()) {
|
||||
this->close();
|
||||
}
|
||||
|
||||
// save client
|
||||
this->network.client = this->netClient;
|
||||
|
||||
// connect to host
|
||||
if (!skip) {
|
||||
int ret;
|
||||
if (this->hostname != nullptr) {
|
||||
ret = this->netClient->connect(this->hostname, (uint16_t)this->port);
|
||||
} else {
|
||||
ret = this->netClient->connect(this->address, (uint16_t)this->port);
|
||||
}
|
||||
if (ret <= 0) {
|
||||
this->_lastError = LWMQTT_NETWORK_FAILED_CONNECT;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare options
|
||||
lwmqtt_options_t options = lwmqtt_default_options;
|
||||
options.keep_alive = this->keepAlive;
|
||||
options.clean_session = this->cleanSession;
|
||||
options.client_id = lwmqtt_string(clientID);
|
||||
|
||||
// set username and password if available
|
||||
if (username != nullptr) {
|
||||
options.username = lwmqtt_string(username);
|
||||
|
||||
if (password != nullptr) {
|
||||
options.password = lwmqtt_string(password);
|
||||
}
|
||||
}
|
||||
|
||||
// connect to broker
|
||||
this->_lastError = lwmqtt_connect(&this->client, options, this->will, &this->_returnCode, this->timeout);
|
||||
if (this->_lastError != LWMQTT_SUCCESS) {
|
||||
// close connection
|
||||
this->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// set flag
|
||||
this->_connected = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MQTTClient::subscribe(const char topic[], int qos) {
|
||||
// return immediately if not connected
|
||||
if (!this->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// subscribe to topic
|
||||
this->_lastError = lwmqtt_subscribe_one(&this->client, lwmqtt_string(topic), (lwmqtt_qos_t)qos, this->timeout);
|
||||
if (this->_lastError != LWMQTT_SUCCESS) {
|
||||
// close connection
|
||||
this->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MQTTClient::unsubscribe(const char topic[]) {
|
||||
// return immediately if not connected
|
||||
if (!this->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// unsubscribe from topic
|
||||
this->_lastError = lwmqtt_unsubscribe_one(&this->client, lwmqtt_string(topic), this->timeout);
|
||||
if (this->_lastError != LWMQTT_SUCCESS) {
|
||||
// close connection
|
||||
this->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MQTTClient::loop() {
|
||||
// return immediately if not connected
|
||||
if (!this->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get available bytes on the network
|
||||
auto available = (size_t)this->netClient->available();
|
||||
|
||||
// yield if data is available
|
||||
if (available > 0) {
|
||||
this->_lastError = lwmqtt_yield(&this->client, available, this->timeout);
|
||||
if (this->_lastError != LWMQTT_SUCCESS) {
|
||||
// close connection
|
||||
this->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// keep the connection alive
|
||||
this->_lastError = lwmqtt_keep_alive(&this->client, this->timeout);
|
||||
if (this->_lastError != LWMQTT_SUCCESS) {
|
||||
// close connection
|
||||
this->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MQTTClient::connected() {
|
||||
// a client is connected if the network is connected, a client is available and
|
||||
// the connection has been properly initiated
|
||||
return this->netClient != nullptr && this->netClient->connected() == 1 && this->_connected;
|
||||
}
|
||||
|
||||
bool MQTTClient::disconnect() {
|
||||
// return immediately if not connected anymore
|
||||
if (!this->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// cleanly disconnect
|
||||
this->_lastError = lwmqtt_disconnect(&this->client, this->timeout);
|
||||
|
||||
// close
|
||||
this->close();
|
||||
|
||||
return this->_lastError == LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
void MQTTClient::close() {
|
||||
// set flag
|
||||
this->_connected = false;
|
||||
|
||||
// close network
|
||||
this->netClient->stop();
|
||||
}
|
||||
151
P1_gateway_FW/lib/MQTT/src/MQTTClient.h
Normal file
@@ -0,0 +1,151 @@
|
||||
#ifndef MQTT_CLIENT_H
|
||||
#define MQTT_CLIENT_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Client.h>
|
||||
#include <Stream.h>
|
||||
|
||||
extern "C" {
|
||||
#include "lwmqtt/lwmqtt.h"
|
||||
}
|
||||
|
||||
typedef uint32_t (*MQTTClientClockSource)();
|
||||
|
||||
typedef struct {
|
||||
uint32_t end;
|
||||
MQTTClientClockSource millis;
|
||||
} lwmqtt_arduino_timer_t;
|
||||
|
||||
typedef struct {
|
||||
Client *client;
|
||||
} lwmqtt_arduino_network_t;
|
||||
|
||||
class MQTTClient;
|
||||
|
||||
typedef void (*MQTTClientCallbackSimple)(String &topic, String &payload);
|
||||
typedef void (*MQTTClientCallbackAdvanced)(MQTTClient *client, char topic[], char bytes[], int length);
|
||||
|
||||
typedef struct {
|
||||
MQTTClient *client = nullptr;
|
||||
MQTTClientCallbackSimple simple = nullptr;
|
||||
MQTTClientCallbackAdvanced advanced = nullptr;
|
||||
} MQTTClientCallback;
|
||||
|
||||
class MQTTClient {
|
||||
private:
|
||||
size_t bufSize = 0;
|
||||
uint8_t *readBuf = nullptr;
|
||||
uint8_t *writeBuf = nullptr;
|
||||
|
||||
uint16_t keepAlive = 10;
|
||||
bool cleanSession = true;
|
||||
uint32_t timeout = 1000;
|
||||
|
||||
Client *netClient = nullptr;
|
||||
const char *hostname = nullptr;
|
||||
IPAddress address;
|
||||
int port = 0;
|
||||
lwmqtt_will_t *will = nullptr;
|
||||
MQTTClientCallback callback;
|
||||
|
||||
lwmqtt_arduino_network_t network = {nullptr};
|
||||
lwmqtt_arduino_timer_t timer1 = {0, nullptr};
|
||||
lwmqtt_arduino_timer_t timer2 = {0, nullptr};
|
||||
lwmqtt_client_t client = lwmqtt_client_t();
|
||||
|
||||
bool _connected = false;
|
||||
lwmqtt_return_code_t _returnCode = (lwmqtt_return_code_t)0;
|
||||
lwmqtt_err_t _lastError = (lwmqtt_err_t)0;
|
||||
|
||||
public:
|
||||
void *ref = nullptr;
|
||||
|
||||
explicit MQTTClient(int bufSize = 128);
|
||||
|
||||
~MQTTClient();
|
||||
|
||||
void begin(Client &_client);
|
||||
void begin(const char _hostname[], Client &_client) { this->begin(_hostname, 1883, _client); }
|
||||
void begin(const char _hostname[], int _port, Client &_client) {
|
||||
this->begin(_client);
|
||||
this->setHost(_hostname, _port);
|
||||
}
|
||||
void begin(IPAddress _address, Client &_client) { this->begin(_address, 1883, _client); }
|
||||
void begin(IPAddress _address, int _port, Client &_client) {
|
||||
this->begin(_client);
|
||||
this->setHost(_address, _port);
|
||||
}
|
||||
|
||||
void onMessage(MQTTClientCallbackSimple cb);
|
||||
void onMessageAdvanced(MQTTClientCallbackAdvanced cb);
|
||||
|
||||
void setClockSource(MQTTClientClockSource cb);
|
||||
|
||||
void setHost(const char _hostname[]) { this->setHost(_hostname, 1883); }
|
||||
void setHost(const char hostname[], int port);
|
||||
void setHost(IPAddress _address) { this->setHost(_address, 1883); }
|
||||
void setHost(IPAddress _address, int port);
|
||||
|
||||
void setWill(const char topic[]) { this->setWill(topic, ""); }
|
||||
void setWill(const char topic[], const char payload[]) { this->setWill(topic, payload, false, 0); }
|
||||
void setWill(const char topic[], const char payload[], bool retained, int qos);
|
||||
void clearWill();
|
||||
|
||||
void setKeepAlive(int keepAlive);
|
||||
void setCleanSession(bool cleanSession);
|
||||
void setTimeout(int timeout);
|
||||
|
||||
void setOptions(int _keepAlive, bool _cleanSession, int _timeout) {
|
||||
this->setKeepAlive(_keepAlive);
|
||||
this->setCleanSession(_cleanSession);
|
||||
this->setTimeout(_timeout);
|
||||
}
|
||||
|
||||
bool connect(const char clientId[], bool skip = false) { return this->connect(clientId, nullptr, nullptr, skip); }
|
||||
bool connect(const char clientId[], const char username[], bool skip = false) {
|
||||
return this->connect(clientId, username, nullptr, skip);
|
||||
}
|
||||
bool connect(const char clientID[], const char username[], const char password[], bool skip = false);
|
||||
|
||||
bool publish(const String &topic) { return this->publish(topic.c_str(), ""); }
|
||||
bool publish(const char topic[]) { return this->publish(topic, ""); }
|
||||
bool publish(const String &topic, const String &payload) { return this->publish(topic.c_str(), payload.c_str()); }
|
||||
bool publish(const String &topic, const String &payload, bool retained, int qos) {
|
||||
return this->publish(topic.c_str(), payload.c_str(), retained, qos);
|
||||
}
|
||||
bool publish(const char topic[], const String &payload) { return this->publish(topic, payload.c_str()); }
|
||||
bool publish(const char topic[], const String &payload, bool retained, int qos) {
|
||||
return this->publish(topic, payload.c_str(), retained, qos);
|
||||
}
|
||||
bool publish(const char topic[], const char payload[]) {
|
||||
return this->publish(topic, (char *)payload, (int)strlen(payload));
|
||||
}
|
||||
bool publish(const char topic[], const char payload[], bool retained, int qos) {
|
||||
return this->publish(topic, (char *)payload, (int)strlen(payload), retained, qos);
|
||||
}
|
||||
bool publish(const char topic[], const char payload[], int length) {
|
||||
return this->publish(topic, payload, length, false, 0);
|
||||
}
|
||||
bool publish(const char topic[], const char payload[], int length, bool retained, int qos);
|
||||
|
||||
bool subscribe(const String &topic) { return this->subscribe(topic.c_str()); }
|
||||
bool subscribe(const String &topic, int qos) { return this->subscribe(topic.c_str(), qos); }
|
||||
bool subscribe(const char topic[]) { return this->subscribe(topic, 0); }
|
||||
bool subscribe(const char topic[], int qos);
|
||||
|
||||
bool unsubscribe(const String &topic) { return this->unsubscribe(topic.c_str()); }
|
||||
bool unsubscribe(const char topic[]);
|
||||
|
||||
bool loop();
|
||||
bool connected();
|
||||
|
||||
lwmqtt_err_t lastError() { return this->_lastError; }
|
||||
lwmqtt_return_code_t returnCode() { return this->_returnCode; }
|
||||
|
||||
bool disconnect();
|
||||
|
||||
private:
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif
|
||||
618
P1_gateway_FW/lib/MQTT/src/lwmqtt/client.c
Normal file
@@ -0,0 +1,618 @@
|
||||
#include "packet.h"
|
||||
|
||||
void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf,
|
||||
size_t read_buf_size) {
|
||||
client->last_packet_id = 1;
|
||||
client->keep_alive_interval = 0;
|
||||
client->pong_pending = false;
|
||||
|
||||
client->write_buf = write_buf;
|
||||
client->write_buf_size = write_buf_size;
|
||||
client->read_buf = read_buf;
|
||||
client->read_buf_size = read_buf_size;
|
||||
|
||||
client->callback = NULL;
|
||||
client->callback_ref = NULL;
|
||||
|
||||
client->network = NULL;
|
||||
client->network_read = NULL;
|
||||
client->network_write = NULL;
|
||||
|
||||
client->keep_alive_timer = NULL;
|
||||
client->command_timer = NULL;
|
||||
client->timer_set = NULL;
|
||||
client->timer_get = NULL;
|
||||
}
|
||||
|
||||
void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write) {
|
||||
client->network = ref;
|
||||
client->network_read = read;
|
||||
client->network_write = write;
|
||||
}
|
||||
|
||||
void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set,
|
||||
lwmqtt_timer_get_t get) {
|
||||
client->keep_alive_timer = keep_alive_timer;
|
||||
client->command_timer = command_timer;
|
||||
client->timer_set = set;
|
||||
client->timer_get = get;
|
||||
|
||||
client->timer_set(client->keep_alive_timer, 0);
|
||||
client->timer_set(client->command_timer, 0);
|
||||
}
|
||||
|
||||
void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb) {
|
||||
client->callback_ref = ref;
|
||||
client->callback = cb;
|
||||
}
|
||||
|
||||
static uint16_t lwmqtt_get_next_packet_id(lwmqtt_client_t *client) {
|
||||
// check overflow
|
||||
if (client->last_packet_id == 65535) {
|
||||
client->last_packet_id = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// increment packet id
|
||||
client->last_packet_id++;
|
||||
|
||||
return client->last_packet_id;
|
||||
}
|
||||
|
||||
static lwmqtt_err_t lwmqtt_read_from_network(lwmqtt_client_t *client, size_t offset, size_t len) {
|
||||
// check read buffer capacity
|
||||
if (client->read_buf_size < offset + len) {
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// prepare counter
|
||||
size_t read = 0;
|
||||
|
||||
// read while data is missing
|
||||
while (read < len) {
|
||||
// check remaining time
|
||||
int32_t remaining_time = client->timer_get(client->command_timer);
|
||||
if (remaining_time <= 0) {
|
||||
return LWMQTT_NETWORK_TIMEOUT;
|
||||
}
|
||||
|
||||
// read
|
||||
size_t partial_read = 0;
|
||||
lwmqtt_err_t err = client->network_read(client->network, client->read_buf + offset + read, len - read,
|
||||
&partial_read, (uint32_t)remaining_time);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// increment counter
|
||||
read += partial_read;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
static lwmqtt_err_t lwmqtt_write_to_network(lwmqtt_client_t *client, size_t offset, size_t len) {
|
||||
// prepare counter
|
||||
size_t written = 0;
|
||||
|
||||
// write while data is left
|
||||
while (written < len) {
|
||||
// check remaining time
|
||||
int32_t remaining_time = client->timer_get(client->command_timer);
|
||||
if (remaining_time <= 0) {
|
||||
return LWMQTT_NETWORK_TIMEOUT;
|
||||
}
|
||||
|
||||
// write
|
||||
size_t partial_write = 0;
|
||||
lwmqtt_err_t err = client->network_write(client->network, client->write_buf + offset + written, len - written,
|
||||
&partial_write, (uint32_t)remaining_time);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// increment counter
|
||||
written += partial_write;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
static lwmqtt_err_t lwmqtt_read_packet_in_buffer(lwmqtt_client_t *client, size_t *read,
|
||||
lwmqtt_packet_type_t *packet_type) {
|
||||
// preset packet type
|
||||
*packet_type = LWMQTT_NO_PACKET;
|
||||
|
||||
// read or wait for header byte
|
||||
lwmqtt_err_t err = lwmqtt_read_from_network(client, 0, 1);
|
||||
if (err == LWMQTT_NETWORK_TIMEOUT) {
|
||||
// this is ok as no data has been read at all
|
||||
return LWMQTT_SUCCESS;
|
||||
} else if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// detect packet type
|
||||
err = lwmqtt_detect_packet_type(client->read_buf, 1, packet_type);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// prepare variables
|
||||
size_t len = 0;
|
||||
uint32_t rem_len = 0;
|
||||
|
||||
do {
|
||||
// adjust len
|
||||
len++;
|
||||
|
||||
// read next byte
|
||||
err = lwmqtt_read_from_network(client, len, 1);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// attempt to detect remaining length
|
||||
err = lwmqtt_detect_remaining_length(client->read_buf + 1, len, &rem_len);
|
||||
} while (err == LWMQTT_BUFFER_TOO_SHORT);
|
||||
|
||||
// check final error
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// read the rest of the buffer if needed
|
||||
if (rem_len > 0) {
|
||||
err = lwmqtt_read_from_network(client, 1 + len, rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// adjust counter
|
||||
*read += 1 + len + rem_len;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
static lwmqtt_err_t lwmqtt_send_packet_in_buffer(lwmqtt_client_t *client, size_t length) {
|
||||
// write to network
|
||||
lwmqtt_err_t err = lwmqtt_write_to_network(client, 0, length);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// reset keep alive timer
|
||||
client->timer_set(client->keep_alive_timer, client->keep_alive_interval);
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
static lwmqtt_err_t lwmqtt_cycle(lwmqtt_client_t *client, size_t *read, lwmqtt_packet_type_t *packet_type) {
|
||||
// read next packet from the network
|
||||
lwmqtt_err_t err = lwmqtt_read_packet_in_buffer(client, read, packet_type);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
} else if (*packet_type == LWMQTT_NO_PACKET) {
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
switch (*packet_type) {
|
||||
// handle publish packets
|
||||
case LWMQTT_PUBLISH_PACKET: {
|
||||
// decode publish packet
|
||||
bool dup;
|
||||
uint16_t packet_id;
|
||||
lwmqtt_string_t topic;
|
||||
lwmqtt_message_t msg;
|
||||
err = lwmqtt_decode_publish(client->read_buf, client->read_buf_size, &dup, &packet_id, &topic, &msg);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// call callback if set
|
||||
if (client->callback != NULL) {
|
||||
client->callback(client, client->callback_ref, topic, msg);
|
||||
}
|
||||
|
||||
// break early on qos zero
|
||||
if (msg.qos == LWMQTT_QOS0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// define ack packet
|
||||
lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET;
|
||||
if (msg.qos == LWMQTT_QOS1) {
|
||||
ack_type = LWMQTT_PUBACK_PACKET;
|
||||
} else if (msg.qos == LWMQTT_QOS2) {
|
||||
ack_type = LWMQTT_PUBREC_PACKET;
|
||||
}
|
||||
|
||||
// encode ack packet
|
||||
size_t len;
|
||||
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, ack_type, false, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send ack packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// handle pubrec packets
|
||||
case LWMQTT_PUBREC_PACKET: {
|
||||
// decode pubrec packet
|
||||
bool dup;
|
||||
uint16_t packet_id;
|
||||
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREC_PACKET, &dup, &packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// encode pubrel packet
|
||||
size_t len;
|
||||
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBREL_PACKET, 0, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send pubrel packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// handle pubrel packets
|
||||
case LWMQTT_PUBREL_PACKET: {
|
||||
// decode pubrec packet
|
||||
bool dup;
|
||||
uint16_t packet_id;
|
||||
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREL_PACKET, &dup, &packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// encode pubcomp packet
|
||||
size_t len;
|
||||
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBCOMP_PACKET, 0, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send pubcomp packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// handle pingresp packets
|
||||
case LWMQTT_PINGRESP_PACKET: {
|
||||
// set flag
|
||||
client->pong_pending = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// handle all other packets
|
||||
default: { break; }
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
static lwmqtt_err_t lwmqtt_cycle_until(lwmqtt_client_t *client, lwmqtt_packet_type_t *packet_type, size_t available,
|
||||
lwmqtt_packet_type_t needle) {
|
||||
// prepare counter
|
||||
size_t read = 0;
|
||||
|
||||
// loop until timeout has been reached
|
||||
do {
|
||||
// do one cycle
|
||||
lwmqtt_err_t err = lwmqtt_cycle(client, &read, packet_type);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// return when one packet has been successfully read when no availability has been given
|
||||
if (needle == LWMQTT_NO_PACKET && available == 0) {
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
// otherwise check if needle has been found
|
||||
if (*packet_type == needle) {
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
} while (client->timer_get(client->command_timer) > 0 && (available == 0 || read < available));
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout) {
|
||||
// set command timer
|
||||
client->timer_set(client->command_timer, timeout);
|
||||
|
||||
// cycle until timeout has been reached
|
||||
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
|
||||
lwmqtt_err_t err = lwmqtt_cycle_until(client, &packet_type, available, LWMQTT_NO_PACKET);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will,
|
||||
lwmqtt_return_code_t *return_code, uint32_t timeout) {
|
||||
// set command timer
|
||||
client->timer_set(client->command_timer, timeout);
|
||||
|
||||
// save keep alive interval
|
||||
client->keep_alive_interval = (uint32_t)(options.keep_alive) * 1000;
|
||||
|
||||
// set keep alive timer
|
||||
client->timer_set(client->keep_alive_timer, client->keep_alive_interval);
|
||||
|
||||
// reset pong pending flag
|
||||
client->pong_pending = false;
|
||||
|
||||
// initialize return code
|
||||
*return_code = LWMQTT_UNKNOWN_RETURN_CODE;
|
||||
|
||||
// encode connect packet
|
||||
size_t len;
|
||||
lwmqtt_err_t err = lwmqtt_encode_connect(client->write_buf, client->write_buf_size, &len, options, will);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// wait for connack packet
|
||||
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
|
||||
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_CONNACK_PACKET);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
} else if (packet_type != LWMQTT_CONNACK_PACKET) {
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
|
||||
// decode connack packet
|
||||
bool session_present;
|
||||
err = lwmqtt_decode_connack(client->read_buf, client->read_buf_size, &session_present, return_code);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// return error if connection was not accepted
|
||||
if (*return_code != LWMQTT_CONNECTION_ACCEPTED) {
|
||||
return LWMQTT_CONNECTION_DENIED;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos,
|
||||
uint32_t timeout) {
|
||||
// set command timer
|
||||
client->timer_set(client->command_timer, timeout);
|
||||
|
||||
// encode subscribe packet
|
||||
size_t len;
|
||||
lwmqtt_err_t err = lwmqtt_encode_subscribe(client->write_buf, client->write_buf_size, &len,
|
||||
lwmqtt_get_next_packet_id(client), count, topic_filter, qos);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// wait for suback packet
|
||||
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
|
||||
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_SUBACK_PACKET);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
} else if (packet_type != LWMQTT_SUBACK_PACKET) {
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
|
||||
// decode packet
|
||||
int suback_count = 0;
|
||||
lwmqtt_qos_t granted_qos[count];
|
||||
uint16_t packet_id;
|
||||
err = lwmqtt_decode_suback(client->read_buf, client->read_buf_size, &packet_id, count, &suback_count, granted_qos);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check suback codes
|
||||
for (int i = 0; i < suback_count; i++) {
|
||||
if (granted_qos[i] == LWMQTT_QOS_FAILURE) {
|
||||
return LWMQTT_FAILED_SUBSCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos,
|
||||
uint32_t timeout) {
|
||||
return lwmqtt_subscribe(client, 1, &topic_filter, &qos, timeout);
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout) {
|
||||
// set command timer
|
||||
client->timer_set(client->command_timer, timeout);
|
||||
|
||||
// encode unsubscribe packet
|
||||
size_t len;
|
||||
lwmqtt_err_t err = lwmqtt_encode_unsubscribe(client->write_buf, client->write_buf_size, &len,
|
||||
lwmqtt_get_next_packet_id(client), count, topic_filter);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send unsubscribe packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// wait for unsuback packet
|
||||
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
|
||||
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_UNSUBACK_PACKET);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
} else if (packet_type != LWMQTT_UNSUBACK_PACKET) {
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
|
||||
// decode unsuback packet
|
||||
bool dup;
|
||||
uint16_t packet_id;
|
||||
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_UNSUBACK_PACKET, &dup, &packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout) {
|
||||
return lwmqtt_unsubscribe(client, 1, &topic_filter, timeout);
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t message,
|
||||
uint32_t timeout) {
|
||||
// set command timer
|
||||
client->timer_set(client->command_timer, timeout);
|
||||
|
||||
// add packet id if at least qos 1
|
||||
uint16_t packet_id = 0;
|
||||
if (message.qos == LWMQTT_QOS1 || message.qos == LWMQTT_QOS2) {
|
||||
packet_id = lwmqtt_get_next_packet_id(client);
|
||||
}
|
||||
|
||||
// encode publish packet
|
||||
size_t len = 0;
|
||||
lwmqtt_err_t err =
|
||||
lwmqtt_encode_publish(client->write_buf, client->write_buf_size, &len, 0, packet_id, topic, message);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// immediately return on qos zero
|
||||
if (message.qos == LWMQTT_QOS0) {
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
// define ack packet
|
||||
lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET;
|
||||
if (message.qos == LWMQTT_QOS1) {
|
||||
ack_type = LWMQTT_PUBACK_PACKET;
|
||||
} else if (message.qos == LWMQTT_QOS2) {
|
||||
ack_type = LWMQTT_PUBCOMP_PACKET;
|
||||
}
|
||||
|
||||
// wait for ack packet
|
||||
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
|
||||
err = lwmqtt_cycle_until(client, &packet_type, 0, ack_type);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
} else if (packet_type != ack_type) {
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
|
||||
// decode ack packet
|
||||
bool dup;
|
||||
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, ack_type, &dup, &packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout) {
|
||||
// set command timer
|
||||
client->timer_set(client->command_timer, timeout);
|
||||
|
||||
// encode disconnect packet
|
||||
size_t len;
|
||||
lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_DISCONNECT_PACKET);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send disconnected packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout) {
|
||||
// set command timer
|
||||
client->timer_set(client->command_timer, timeout);
|
||||
|
||||
// return immediately if keep alive interval is zero
|
||||
if (client->keep_alive_interval == 0) {
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
// return immediately if no ping is due
|
||||
if (client->timer_get(client->keep_alive_timer) > 0) {
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
// a ping is due
|
||||
|
||||
// fail immediately if a pong is already pending
|
||||
if (client->pong_pending) {
|
||||
return LWMQTT_PONG_TIMEOUT;
|
||||
}
|
||||
|
||||
// encode pingreq packet
|
||||
size_t len;
|
||||
lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_PINGREQ_PACKET);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// send packet
|
||||
err = lwmqtt_send_packet_in_buffer(client, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// set flag
|
||||
client->pong_pending = true;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
249
P1_gateway_FW/lib/MQTT/src/lwmqtt/helpers.c
Normal file
@@ -0,0 +1,249 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num) { return (byte & (uint8_t)((~(0xFF << num)) << pos)) >> pos; }
|
||||
|
||||
void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num) {
|
||||
*byte = (*byte & ~(uint8_t)((~(0xFF << num)) << pos)) | (value << pos);
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len) {
|
||||
// check zero length
|
||||
if (len == 0) {
|
||||
*data = NULL;
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
// check buffer size
|
||||
if ((size_t)(buf_end - (*buf)) < len) {
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// read data
|
||||
*data = *buf;
|
||||
|
||||
// advance pointer
|
||||
*buf += len;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len) {
|
||||
// check zero length
|
||||
if (len == 0) {
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
// check buffer size
|
||||
if ((size_t)(buf_end - (*buf)) < len) {
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// write data
|
||||
memcpy(*buf, data, len);
|
||||
|
||||
// advance pointer
|
||||
*buf += len;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num) {
|
||||
// check buffer size
|
||||
if ((size_t)(buf_end - (*buf)) < 2) {
|
||||
*num = 0;
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// read two byte integer
|
||||
*num = (uint16_t)256 * (*buf)[0] + (*buf)[1];
|
||||
|
||||
// adjust pointer
|
||||
*buf += 2;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num) {
|
||||
// check buffer size
|
||||
if ((size_t)(buf_end - (*buf)) < 2) {
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// write bytes
|
||||
(*buf)[0] = (uint8_t)(num / 256);
|
||||
(*buf)[1] = (uint8_t)(num % 256);
|
||||
|
||||
// adjust pointer
|
||||
*buf += 2;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str) {
|
||||
// read length
|
||||
uint16_t len;
|
||||
lwmqtt_err_t err = lwmqtt_read_num(buf, buf_end, &len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// read data
|
||||
err = lwmqtt_read_data(buf, buf_end, (uint8_t **)&str->data, len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// set length
|
||||
str->len = len;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str) {
|
||||
// write string length
|
||||
lwmqtt_err_t err = lwmqtt_write_num(buf, buf_end, str.len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write data
|
||||
err = lwmqtt_write_data(buf, buf_end, (uint8_t *)str.data, str.len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte) {
|
||||
// check buffer size
|
||||
if ((size_t)(buf_end - (*buf)) < 1) {
|
||||
*byte = 0;
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// read byte
|
||||
*byte = (*buf)[0];
|
||||
|
||||
// adjust pointer
|
||||
*buf += 1;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte) {
|
||||
// check buffer size
|
||||
if ((size_t)(buf_end - (*buf)) < 1) {
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// write byte
|
||||
(*buf)[0] = byte;
|
||||
|
||||
// adjust pointer
|
||||
*buf += 1;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len) {
|
||||
if (varnum < 128) {
|
||||
*len = 1;
|
||||
return LWMQTT_SUCCESS;
|
||||
} else if (varnum < 16384) {
|
||||
*len = 2;
|
||||
return LWMQTT_SUCCESS;
|
||||
} else if (varnum < 2097151) {
|
||||
*len = 3;
|
||||
return LWMQTT_SUCCESS;
|
||||
} else if (varnum < 268435455) {
|
||||
*len = 4;
|
||||
return LWMQTT_SUCCESS;
|
||||
} else {
|
||||
*len = 0;
|
||||
return LWMQTT_VARNUM_OVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum) {
|
||||
// prepare last byte
|
||||
uint8_t byte;
|
||||
|
||||
// prepare multiplier
|
||||
uint32_t multiplier = 1;
|
||||
|
||||
// prepare length
|
||||
size_t len = 0;
|
||||
|
||||
// initialize number
|
||||
*varnum = 0;
|
||||
|
||||
// decode variadic number
|
||||
do {
|
||||
// increment length
|
||||
len++;
|
||||
|
||||
// return error if buffer is to small
|
||||
if ((size_t)(buf_end - (*buf)) < len) {
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// return error if the length has overflowed
|
||||
if (len > 4) {
|
||||
return LWMQTT_VARNUM_OVERFLOW;
|
||||
}
|
||||
|
||||
// read byte
|
||||
byte = (*buf)[len - 1];
|
||||
|
||||
// add byte to number
|
||||
*varnum += (byte & 127) * multiplier;
|
||||
|
||||
// increase multiplier
|
||||
multiplier *= 128;
|
||||
} while ((byte & 128) != 0);
|
||||
|
||||
// adjust pointer
|
||||
*buf += len;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum) {
|
||||
// init len counter
|
||||
size_t len = 0;
|
||||
|
||||
// encode variadic number
|
||||
do {
|
||||
// check overflow
|
||||
if (len == 4) {
|
||||
return LWMQTT_VARNUM_OVERFLOW;
|
||||
}
|
||||
|
||||
// return error if buffer is to small
|
||||
if ((size_t)(buf_end - (*buf)) < len + 1) {
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// calculate current byte
|
||||
uint8_t byte = (uint8_t)(varnum % 128);
|
||||
|
||||
// change remaining length
|
||||
varnum /= 128;
|
||||
|
||||
// set the top bit of this byte if there are more to encode
|
||||
if (varnum > 0) {
|
||||
byte |= 0x80;
|
||||
}
|
||||
|
||||
// write byte
|
||||
(*buf)[len++] = byte;
|
||||
} while (varnum > 0);
|
||||
|
||||
// adjust pointer
|
||||
*buf += len;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
137
P1_gateway_FW/lib/MQTT/src/lwmqtt/helpers.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#ifndef LWMQTT_HELPERS_H
|
||||
#define LWMQTT_HELPERS_H
|
||||
|
||||
#include "lwmqtt.h"
|
||||
|
||||
/**
|
||||
* Reads bits from a byte.
|
||||
*
|
||||
* @param byte - The byte to read from.
|
||||
* @param pos - The position of the first bit.
|
||||
* @param num - The number of bits to read.
|
||||
* @return The read bits as a byte.
|
||||
*/
|
||||
uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num);
|
||||
|
||||
/**
|
||||
* Write bits to a byte.
|
||||
*
|
||||
* @param byte - The byte to write bits to.
|
||||
* @param value - The bits to write as a byte.
|
||||
* @param pos - The position of the first bit.
|
||||
* @param num - The number of bits to write.
|
||||
*/
|
||||
void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num);
|
||||
|
||||
/**
|
||||
* Reads arbitrary data from the specified buffer. The pointer is incremented by bytes read.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param data - Pointer to beginning of data.
|
||||
* @param len - The amount of data to read.
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len);
|
||||
|
||||
/**
|
||||
* Writes arbitrary data to the specified buffer. The pointer is incremented by the bytes written.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param data - Pointer to the to be written data.
|
||||
* @param len - The amount of data to write.
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* Reads two byte number from the specified buffer. The pointer is incremented by two.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param num - The read number.
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num);
|
||||
|
||||
/**
|
||||
* Writes a two byte number to the specified buffer. The pointer is incremented by two.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param num - The number to write.
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num);
|
||||
|
||||
/**
|
||||
* Reads a string from the specified buffer into the passed object. The pointer is incremented by the bytes read.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param str - The object into which the data is to be read.
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str);
|
||||
|
||||
/**
|
||||
* Writes a string to the specified buffer. The pointer is incremented by the bytes written.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param str - The string to write.
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str);
|
||||
|
||||
/**
|
||||
* Reads one byte from the buffer. The pointer is incremented by one.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param byte - The read byte.
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte);
|
||||
|
||||
/**
|
||||
* Writes one byte to the specified buffer. The pointer is incremented by one.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param byte - The byte to write.
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte);
|
||||
|
||||
/**
|
||||
* Returns the amount of bytes required by the variable number.
|
||||
*
|
||||
* @param varnum - The number to check.
|
||||
* @param len - The required length;
|
||||
* @return LWMQTT_SUCCESS or LWMQTT_VARNUM_OVERFLOW.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len);
|
||||
|
||||
/**
|
||||
* Reads a variable number from the specified buffer. The pointer is incremented by the bytes read.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param varnum - The read varnum.
|
||||
* @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum);
|
||||
|
||||
/**
|
||||
* Writes a variable number to the specified buffer. The pointer is incremented by the bytes written.
|
||||
*
|
||||
* @param buf - Pointer to the buffer.
|
||||
* @param buf_end - Pointer to the end of the buffer.
|
||||
* @param varnum - The number to write.
|
||||
* @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum);
|
||||
|
||||
#endif
|
||||
381
P1_gateway_FW/lib/MQTT/src/lwmqtt/lwmqtt.h
Normal file
@@ -0,0 +1,381 @@
|
||||
#ifndef LWMQTT_H
|
||||
#define LWMQTT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* The error type used by all exposed APIs.
|
||||
*
|
||||
* If a function returns an error that operates on a connected client (e.g publish, keep_alive, etc.) the caller should
|
||||
* switch into a disconnected state, close and cleanup the current connection and start over by creating a new
|
||||
* connection.
|
||||
*/
|
||||
typedef enum {
|
||||
LWMQTT_SUCCESS = 0,
|
||||
LWMQTT_BUFFER_TOO_SHORT = -1,
|
||||
LWMQTT_VARNUM_OVERFLOW = -2,
|
||||
LWMQTT_NETWORK_FAILED_CONNECT = -3,
|
||||
LWMQTT_NETWORK_TIMEOUT = -4,
|
||||
LWMQTT_NETWORK_FAILED_READ = -5,
|
||||
LWMQTT_NETWORK_FAILED_WRITE = -6,
|
||||
LWMQTT_REMAINING_LENGTH_OVERFLOW = -7,
|
||||
LWMQTT_REMAINING_LENGTH_MISMATCH = -8,
|
||||
LWMQTT_MISSING_OR_WRONG_PACKET = -9,
|
||||
LWMQTT_CONNECTION_DENIED = -10,
|
||||
LWMQTT_FAILED_SUBSCRIPTION = -11,
|
||||
LWMQTT_SUBACK_ARRAY_OVERFLOW = -12,
|
||||
LWMQTT_PONG_TIMEOUT = -13,
|
||||
} lwmqtt_err_t;
|
||||
|
||||
/**
|
||||
* A common string object.
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t len;
|
||||
char *data;
|
||||
} lwmqtt_string_t;
|
||||
|
||||
/**
|
||||
* The initializer for string objects.
|
||||
*/
|
||||
#define lwmqtt_default_string \
|
||||
{ 0, NULL }
|
||||
|
||||
/**
|
||||
* Returns a string object for the passed C string.
|
||||
*
|
||||
* @param str - The C string.
|
||||
* @return A string object.
|
||||
*/
|
||||
lwmqtt_string_t lwmqtt_string(const char *str);
|
||||
|
||||
/**
|
||||
* Compares a string object to a C string.
|
||||
*
|
||||
* @param a - The string object to compare.
|
||||
* @param b - The C string to compare.
|
||||
* @return Similarity e.g. strcmp().
|
||||
*/
|
||||
int lwmqtt_strcmp(lwmqtt_string_t a, const char *b);
|
||||
|
||||
/**
|
||||
* The available QOS levels.
|
||||
*/
|
||||
typedef enum { LWMQTT_QOS0 = 0, LWMQTT_QOS1 = 1, LWMQTT_QOS2 = 2, LWMQTT_QOS_FAILURE = 128 } lwmqtt_qos_t;
|
||||
|
||||
/**
|
||||
* The message object used to publish and receive messages.
|
||||
*/
|
||||
typedef struct {
|
||||
lwmqtt_qos_t qos;
|
||||
bool retained;
|
||||
uint8_t *payload;
|
||||
size_t payload_len;
|
||||
} lwmqtt_message_t;
|
||||
|
||||
/**
|
||||
* The initializer for message objects.
|
||||
*/
|
||||
#define lwmqtt_default_message \
|
||||
{ LWMQTT_QOS0, false, NULL, 0 }
|
||||
|
||||
/**
|
||||
* Forward declaration of the client object.
|
||||
*/
|
||||
typedef struct lwmqtt_client_t lwmqtt_client_t;
|
||||
|
||||
/**
|
||||
* The callback used to read from a network object.
|
||||
*
|
||||
* The callbacks is expected to read up to the amount of bytes in to the passed buffer. It should block the specified
|
||||
* timeout and wait for more incoming data.
|
||||
*
|
||||
* @param ref - A custom reference.
|
||||
* @param buf - The buffer.
|
||||
* @param len - The length of the buffer.
|
||||
* @param read - Variable that must be set with the amount of read bytes.
|
||||
* @param timeout - The timeout in milliseconds for the operation.
|
||||
*/
|
||||
typedef lwmqtt_err_t (*lwmqtt_network_read_t)(void *ref, uint8_t *buf, size_t len, size_t *read, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* The callback used to write to a network object.
|
||||
*
|
||||
* The callback is expected to write up to the amount of bytes from the passed buffer. It should wait up to the
|
||||
* specified timeout to write the specified data to the network.
|
||||
*
|
||||
* @param ref - A custom reference.
|
||||
* @param buf - The buffer.
|
||||
* @param len - The length of the buffer.
|
||||
* @param sent - Variable that must be set with the amount of written bytes.
|
||||
* @param timeout - The timeout in milliseconds for the operation.
|
||||
*/
|
||||
typedef lwmqtt_err_t (*lwmqtt_network_write_t)(void *ref, uint8_t *buf, size_t len, size_t *sent, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* The callback used to set a timer.
|
||||
*
|
||||
* @param ref - A custom reference.
|
||||
* @param timeout - The amount of milliseconds until the deadline.
|
||||
*/
|
||||
typedef void (*lwmqtt_timer_set_t)(void *ref, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* The callback used to get a timers value.
|
||||
*
|
||||
* @param - A custom reference.
|
||||
* @return The amount of milliseconds until the deadline. May return negative numbers if the deadline has been reached.
|
||||
*/
|
||||
typedef int32_t (*lwmqtt_timer_get_t)(void *ref);
|
||||
|
||||
/**
|
||||
* The callback used to forward incoming messages.
|
||||
*
|
||||
* Note: The callback is mostly executed because of a call to lwmqtt_yield() that processes incoming messages. However,
|
||||
* it is possible that the callback is also executed during a call to lwmqtt_subscribe(), lwmqtt_publish() or
|
||||
* lwmqtt_unsubscribe() if incoming messages are received between the required acknowledgements. It is therefore not
|
||||
* recommended to call any further lwmqtt methods in the callback as this might result in weird call stacks. The
|
||||
* callback should place the received messages in a queue and dispatch them after the caller has returned.
|
||||
*/
|
||||
typedef void (*lwmqtt_callback_t)(lwmqtt_client_t *client, void *ref, lwmqtt_string_t str, lwmqtt_message_t msg);
|
||||
|
||||
/**
|
||||
* The client object.
|
||||
*/
|
||||
struct lwmqtt_client_t {
|
||||
uint16_t last_packet_id;
|
||||
uint32_t keep_alive_interval;
|
||||
bool pong_pending;
|
||||
|
||||
size_t write_buf_size, read_buf_size;
|
||||
uint8_t *write_buf, *read_buf;
|
||||
|
||||
lwmqtt_callback_t callback;
|
||||
void *callback_ref;
|
||||
|
||||
void *network;
|
||||
lwmqtt_network_read_t network_read;
|
||||
lwmqtt_network_write_t network_write;
|
||||
|
||||
void *keep_alive_timer;
|
||||
void *command_timer;
|
||||
lwmqtt_timer_set_t timer_set;
|
||||
lwmqtt_timer_get_t timer_get;
|
||||
};
|
||||
|
||||
/**
|
||||
* Will initialize the specified client object.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param write_buf - The write buffer.
|
||||
* @param write_buf_size - The write buffer size.
|
||||
* @param read_buf - The read buffer.
|
||||
* @param read_buf_size - The read buffer size.
|
||||
*/
|
||||
void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf,
|
||||
size_t read_buf_size);
|
||||
|
||||
/**
|
||||
* Will set the network reference and callbacks for this client object.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param ref - The reference to the network object.
|
||||
* @param read - The read callback.
|
||||
* @param write - The write callback.
|
||||
*/
|
||||
void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write);
|
||||
|
||||
/**
|
||||
* Will set the timer references and callbacks for this client object.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param keep_alive_timer - The reference to the keep alive timer.
|
||||
* @param command_timer - The reference to the command timer.
|
||||
* @param set - The set callback.
|
||||
* @param get - The get callback.
|
||||
*/
|
||||
void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set,
|
||||
lwmqtt_timer_get_t get);
|
||||
|
||||
/**
|
||||
* Will set the callback used to receive incoming messages.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param ref - A custom reference that will passed to the callback.
|
||||
* @param cb - The callback to be called.
|
||||
*/
|
||||
void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb);
|
||||
|
||||
/**
|
||||
* The object defining the last will of a client.
|
||||
*/
|
||||
typedef struct {
|
||||
lwmqtt_string_t topic;
|
||||
lwmqtt_qos_t qos;
|
||||
bool retained;
|
||||
lwmqtt_string_t payload;
|
||||
} lwmqtt_will_t;
|
||||
|
||||
/**
|
||||
* The default initializer for the will object.
|
||||
*/
|
||||
#define lwmqtt_default_will \
|
||||
{ lwmqtt_default_string, LWMQTT_QOS0, false, lwmqtt_default_string }
|
||||
|
||||
/**
|
||||
* The object containing the connection options for a client.
|
||||
*/
|
||||
typedef struct {
|
||||
lwmqtt_string_t client_id;
|
||||
uint16_t keep_alive;
|
||||
bool clean_session;
|
||||
lwmqtt_string_t username;
|
||||
lwmqtt_string_t password;
|
||||
} lwmqtt_options_t;
|
||||
|
||||
/**
|
||||
* The default initializer for the options object.
|
||||
*/
|
||||
#define lwmqtt_default_options \
|
||||
{ lwmqtt_default_string, 60, true, lwmqtt_default_string, lwmqtt_default_string }
|
||||
|
||||
/**
|
||||
* The available return codes transported by the connack packet.
|
||||
*/
|
||||
typedef enum {
|
||||
LWMQTT_CONNECTION_ACCEPTED = 0,
|
||||
LWMQTT_UNACCEPTABLE_PROTOCOL = 1,
|
||||
LWMQTT_IDENTIFIER_REJECTED = 2,
|
||||
LWMQTT_SERVER_UNAVAILABLE = 3,
|
||||
LWMQTT_BAD_USERNAME_OR_PASSWORD = 4,
|
||||
LWMQTT_NOT_AUTHORIZED = 5,
|
||||
LWMQTT_UNKNOWN_RETURN_CODE = 6
|
||||
} lwmqtt_return_code_t;
|
||||
|
||||
/**
|
||||
* Will send a connect packet and wait for a connack response and set the return code.
|
||||
*
|
||||
* The network object must already be connected to the server. An error is returned if the broker rejects the
|
||||
* connection.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param options - The options object.
|
||||
* @param will - The will object.
|
||||
* @param return_code - The variable that will receive the return code.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will,
|
||||
lwmqtt_return_code_t *return_code, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Will send a publish packet and wait for all acks to complete.
|
||||
*
|
||||
* Note: The message callback might be called with incoming messages as part of this call.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param topic - The topic.
|
||||
* @param message - The message.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t msg, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Will send a subscribe packet with multiple topic filters plus QOS levels and wait for the suback to complete.
|
||||
*
|
||||
* Note: The message callback might be called with incoming messages as part of this call.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param count - The number of topic filters and QOS levels.
|
||||
* @param topic_filter - The list of topic filters.
|
||||
* @param qos - The list of QOS levels.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos,
|
||||
uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Will send a subscribe packet with a single topic filter plus QOS level and wait for the suback to complete.
|
||||
*
|
||||
* Note: The message callback might be called with incoming messages as part of this call.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param topic_filter - The topic filter.
|
||||
* @param qos - The QOS level.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos,
|
||||
uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Will send an unsubscribe packet with multiple topic filters and wait for the unsuback to complete.
|
||||
*
|
||||
* Note: The message callback might be called with incoming messages as part of this call.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param count - The number of topic filters.
|
||||
* @param topic_filter - The topic filter.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Will send an unsubscribe packet with a single topic filter and wait for the unsuback to complete.
|
||||
*
|
||||
* Note: The message callback might be called with incoming messages as part of this call.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param topic_filter - The topic filter.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Will send a disconnect packet and finish the client.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Will yield control to the client and receive incoming packets from the network.
|
||||
*
|
||||
* Single-threaded applications may peek on the network and assess if data is available to read before calling yield and
|
||||
* potentially block until the timeout is reached. Multi-threaded applications may select on the socket and block until
|
||||
* data is available and then yield to the client if data is available. All applications may specify the amount of bytes
|
||||
* available to read in order to constrain the yield to only receive packets that are already in-flight.
|
||||
*
|
||||
* If no availability info is given the yield will return after one packet has been successfully read or the deadline
|
||||
* has been reached but no single bytes has been received.
|
||||
*
|
||||
* Note: The message callback might be called with incoming messages as part of this call.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param available - The available bytes to read.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Will yield control to the client to keep the connection alive.
|
||||
*
|
||||
* This functions must be called at a rate slightly lower than 25% of the configured keep alive. If keep alive is zero,
|
||||
* the function must not be called at all.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param timeout - The command timeout.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout);
|
||||
|
||||
#endif // LWMQTT_H
|
||||
742
P1_gateway_FW/lib/MQTT/src/lwmqtt/packet.c
Normal file
@@ -0,0 +1,742 @@
|
||||
#include "packet.h"
|
||||
|
||||
lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type) {
|
||||
// set default packet type
|
||||
*packet_type = LWMQTT_NO_PACKET;
|
||||
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// prepare header
|
||||
uint8_t header;
|
||||
|
||||
// read header
|
||||
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// get packet type
|
||||
*packet_type = (lwmqtt_packet_type_t)lwmqtt_read_bits(header, 4, 4);
|
||||
|
||||
// check if packet type is correct and can be received
|
||||
switch (*packet_type) {
|
||||
case LWMQTT_CONNACK_PACKET:
|
||||
case LWMQTT_PUBLISH_PACKET:
|
||||
case LWMQTT_PUBACK_PACKET:
|
||||
case LWMQTT_PUBREC_PACKET:
|
||||
case LWMQTT_PUBREL_PACKET:
|
||||
case LWMQTT_PUBCOMP_PACKET:
|
||||
case LWMQTT_SUBACK_PACKET:
|
||||
case LWMQTT_UNSUBACK_PACKET:
|
||||
case LWMQTT_PINGRESP_PACKET:
|
||||
return LWMQTT_SUCCESS;
|
||||
default:
|
||||
*packet_type = LWMQTT_NO_PACKET;
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len) {
|
||||
// prepare pointer
|
||||
uint8_t *ptr = buf;
|
||||
|
||||
// attempt to decode remaining length
|
||||
lwmqtt_err_t err = lwmqtt_read_varnum(&ptr, buf + buf_len, rem_len);
|
||||
if (err == LWMQTT_VARNUM_OVERFLOW) {
|
||||
*rem_len = 0;
|
||||
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
|
||||
} else if (err != LWMQTT_SUCCESS) {
|
||||
*rem_len = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options,
|
||||
lwmqtt_will_t *will) {
|
||||
// prepare pointers
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// fixed header is 10
|
||||
uint32_t rem_len = 10;
|
||||
|
||||
// add client id to remaining length
|
||||
rem_len += options.client_id.len + 2;
|
||||
|
||||
// add will if present to remaining length
|
||||
if (will != NULL) {
|
||||
rem_len += will->topic.len + 2 + will->payload.len + 2;
|
||||
}
|
||||
|
||||
// add username if present to remaining length
|
||||
if (options.username.len > 0) {
|
||||
rem_len += options.username.len + 2;
|
||||
|
||||
// add password if present to remaining length
|
||||
if (options.password.len > 0) {
|
||||
rem_len += options.password.len + 2;
|
||||
}
|
||||
}
|
||||
|
||||
// check remaining length length
|
||||
int rem_len_len;
|
||||
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
|
||||
if (err == LWMQTT_VARNUM_OVERFLOW) {
|
||||
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
|
||||
}
|
||||
|
||||
// prepare header
|
||||
uint8_t header = 0;
|
||||
lwmqtt_write_bits(&header, LWMQTT_CONNECT_PACKET, 4, 4);
|
||||
|
||||
// write header
|
||||
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write remaining length
|
||||
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write version string
|
||||
err = lwmqtt_write_string(&buf_ptr, buf_end, lwmqtt_string("MQTT"));
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write version number
|
||||
err = lwmqtt_write_byte(&buf_ptr, buf_end, 4);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// prepare flags
|
||||
uint8_t flags = 0;
|
||||
|
||||
// set clean session
|
||||
lwmqtt_write_bits(&flags, (uint8_t)(options.clean_session), 1, 1);
|
||||
|
||||
// set will flags if present
|
||||
if (will != NULL) {
|
||||
lwmqtt_write_bits(&flags, 1, 2, 1);
|
||||
lwmqtt_write_bits(&flags, will->qos, 3, 2);
|
||||
lwmqtt_write_bits(&flags, (uint8_t)(will->retained), 5, 1);
|
||||
}
|
||||
|
||||
// set username flag if present
|
||||
if (options.username.len > 0) {
|
||||
lwmqtt_write_bits(&flags, 1, 7, 1);
|
||||
|
||||
// set password flag if present
|
||||
if (options.password.len > 0) {
|
||||
lwmqtt_write_bits(&flags, 1, 6, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// write flags
|
||||
err = lwmqtt_write_byte(&buf_ptr, buf_end, flags);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write keep alive
|
||||
err = lwmqtt_write_num(&buf_ptr, buf_end, options.keep_alive);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write client id
|
||||
err = lwmqtt_write_string(&buf_ptr, buf_end, options.client_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write will if present
|
||||
if (will != NULL) {
|
||||
// write topic
|
||||
err = lwmqtt_write_string(&buf_ptr, buf_end, will->topic);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write payload length
|
||||
err = lwmqtt_write_num(&buf_ptr, buf_end, (uint16_t)will->payload.len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write payload
|
||||
err = lwmqtt_write_data(&buf_ptr, buf_end, (uint8_t *)will->payload.data, will->payload.len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// write username if present
|
||||
if (options.username.len > 0) {
|
||||
err = lwmqtt_write_string(&buf_ptr, buf_end, options.username);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// write password if present
|
||||
if (options.username.len > 0 && options.password.len > 0) {
|
||||
err = lwmqtt_write_string(&buf_ptr, buf_end, options.password);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// set written length
|
||||
*len = buf_ptr - buf;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present,
|
||||
lwmqtt_return_code_t *return_code) {
|
||||
// prepare pointers
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// read header
|
||||
uint8_t header;
|
||||
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check packet type
|
||||
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_CONNACK_PACKET) {
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
|
||||
// read remaining length
|
||||
uint32_t rem_len;
|
||||
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check remaining length
|
||||
if (rem_len != 2) {
|
||||
return LWMQTT_REMAINING_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
// read flags
|
||||
uint8_t flags;
|
||||
err = lwmqtt_read_byte(&buf_ptr, buf_end, &flags);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// read return code
|
||||
uint8_t raw_return_code;
|
||||
err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_return_code);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// get session present
|
||||
*session_present = lwmqtt_read_bits(flags, 7, 1) == 1;
|
||||
|
||||
// get return code
|
||||
switch (raw_return_code) {
|
||||
case 0:
|
||||
*return_code = LWMQTT_CONNECTION_ACCEPTED;
|
||||
break;
|
||||
case 1:
|
||||
*return_code = LWMQTT_UNACCEPTABLE_PROTOCOL;
|
||||
break;
|
||||
case 2:
|
||||
*return_code = LWMQTT_IDENTIFIER_REJECTED;
|
||||
break;
|
||||
case 3:
|
||||
*return_code = LWMQTT_SERVER_UNAVAILABLE;
|
||||
break;
|
||||
case 4:
|
||||
*return_code = LWMQTT_BAD_USERNAME_OR_PASSWORD;
|
||||
break;
|
||||
case 5:
|
||||
*return_code = LWMQTT_NOT_AUTHORIZED;
|
||||
break;
|
||||
default:
|
||||
*return_code = LWMQTT_UNKNOWN_RETURN_CODE;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type) {
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// write header
|
||||
uint8_t header = 0;
|
||||
lwmqtt_write_bits(&header, packet_type, 4, 4);
|
||||
lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write remaining length
|
||||
err = lwmqtt_write_varnum(&buf_ptr, buf_end, 0);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// set length
|
||||
*len = buf_ptr - buf;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup,
|
||||
uint16_t *packet_id) {
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// read header
|
||||
uint8_t header = 0;
|
||||
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check packet type
|
||||
if (lwmqtt_read_bits(header, 4, 4) != packet_type) {
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
|
||||
// get dup
|
||||
*dup = lwmqtt_read_bits(header, 3, 1) == 1;
|
||||
|
||||
// read remaining length
|
||||
uint32_t rem_len;
|
||||
err = lwmqtt_read_varnum(&buf_ptr, buf + buf_len, &rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check remaining length
|
||||
if (rem_len != 2) {
|
||||
return LWMQTT_REMAINING_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
// read packet id
|
||||
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup,
|
||||
uint16_t packet_id) {
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// prepare header
|
||||
uint8_t header = 0;
|
||||
|
||||
// set packet type
|
||||
lwmqtt_write_bits(&header, packet_type, 4, 4);
|
||||
|
||||
// set dup
|
||||
lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1);
|
||||
|
||||
// set qos
|
||||
lwmqtt_write_bits(&header, (uint8_t)(packet_type == LWMQTT_PUBREL_PACKET ? LWMQTT_QOS1 : LWMQTT_QOS0), 1, 2);
|
||||
|
||||
// write header
|
||||
lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write remaining length
|
||||
err = lwmqtt_write_varnum(&buf_ptr, buf_end, 2);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write packet id
|
||||
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// set written length
|
||||
*len = buf_ptr - buf;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic,
|
||||
lwmqtt_message_t *msg) {
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// read header
|
||||
uint8_t header;
|
||||
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check packet type
|
||||
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_PUBLISH_PACKET) {
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
|
||||
// get dup
|
||||
*dup = lwmqtt_read_bits(header, 3, 1) == 1;
|
||||
|
||||
// get retained
|
||||
msg->retained = lwmqtt_read_bits(header, 0, 1) == 1;
|
||||
|
||||
// get qos
|
||||
switch (lwmqtt_read_bits(header, 1, 2)) {
|
||||
case 0:
|
||||
msg->qos = LWMQTT_QOS0;
|
||||
break;
|
||||
case 1:
|
||||
msg->qos = LWMQTT_QOS1;
|
||||
break;
|
||||
case 2:
|
||||
msg->qos = LWMQTT_QOS2;
|
||||
break;
|
||||
default:
|
||||
msg->qos = LWMQTT_QOS0;
|
||||
break;
|
||||
}
|
||||
|
||||
// read remaining length
|
||||
uint32_t rem_len;
|
||||
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check remaining length (topic length)
|
||||
if (rem_len < 2) {
|
||||
return LWMQTT_REMAINING_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
// check buffer capacity
|
||||
if ((uint32_t)(buf_end - buf_ptr) < rem_len) {
|
||||
return LWMQTT_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
// reset buf end
|
||||
buf_end = buf_ptr + rem_len;
|
||||
|
||||
// read topic
|
||||
err = lwmqtt_read_string(&buf_ptr, buf_end, topic);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// read packet id if qos is at least 1
|
||||
if (msg->qos > 0) {
|
||||
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
*packet_id = 0;
|
||||
}
|
||||
|
||||
// set payload length
|
||||
msg->payload_len = buf_end - buf_ptr;
|
||||
|
||||
// read payload
|
||||
err = lwmqtt_read_data(&buf_ptr, buf_end, &msg->payload, buf_end - buf_ptr);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id,
|
||||
lwmqtt_string_t topic, lwmqtt_message_t msg) {
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// calculate remaining length
|
||||
uint32_t rem_len = 2 + topic.len + (uint32_t)msg.payload_len;
|
||||
if (msg.qos > 0) {
|
||||
rem_len += 2;
|
||||
}
|
||||
|
||||
// check remaining length length
|
||||
int rem_len_len;
|
||||
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
|
||||
if (err == LWMQTT_VARNUM_OVERFLOW) {
|
||||
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
|
||||
}
|
||||
|
||||
// prepare header
|
||||
uint8_t header = 0;
|
||||
|
||||
// set packet type
|
||||
lwmqtt_write_bits(&header, LWMQTT_PUBLISH_PACKET, 4, 4);
|
||||
|
||||
// set dup
|
||||
lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1);
|
||||
|
||||
// set qos
|
||||
lwmqtt_write_bits(&header, msg.qos, 1, 2);
|
||||
|
||||
// set retained
|
||||
lwmqtt_write_bits(&header, (uint8_t)(msg.retained), 0, 1);
|
||||
|
||||
// write header
|
||||
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write remaining length
|
||||
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write topic
|
||||
err = lwmqtt_write_string(&buf_ptr, buf_end, topic);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write packet id if qos is at least 1
|
||||
if (msg.qos > 0) {
|
||||
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// write payload
|
||||
err = lwmqtt_write_data(&buf_ptr, buf_end, msg.payload, msg.payload_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// set length
|
||||
*len = buf_ptr - buf;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
|
||||
lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels) {
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// calculate remaining length
|
||||
uint32_t rem_len = 2;
|
||||
for (int i = 0; i < count; i++) {
|
||||
rem_len += 2 + topic_filters[i].len + 1;
|
||||
}
|
||||
|
||||
// check remaining length length
|
||||
int rem_len_len;
|
||||
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
|
||||
if (err == LWMQTT_VARNUM_OVERFLOW) {
|
||||
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
|
||||
}
|
||||
|
||||
// prepare header
|
||||
uint8_t header = 0;
|
||||
|
||||
// set packet type
|
||||
lwmqtt_write_bits(&header, LWMQTT_SUBSCRIBE_PACKET, 4, 4);
|
||||
|
||||
// set qos
|
||||
lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2);
|
||||
|
||||
// write header
|
||||
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write remaining length
|
||||
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write packet id
|
||||
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write all subscriptions
|
||||
for (int i = 0; i < count; i++) {
|
||||
// write topic
|
||||
err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write qos level
|
||||
err = lwmqtt_write_byte(&buf_ptr, buf_end, (uint8_t)qos_levels[i]);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// set length
|
||||
*len = buf_ptr - buf;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count,
|
||||
lwmqtt_qos_t *granted_qos_levels) {
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// read header
|
||||
uint8_t header;
|
||||
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check packet type
|
||||
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_SUBACK_PACKET) {
|
||||
return LWMQTT_MISSING_OR_WRONG_PACKET;
|
||||
}
|
||||
|
||||
// read remaining length
|
||||
uint32_t rem_len;
|
||||
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check remaining length (packet id + min. one suback code)
|
||||
if (rem_len < 3) {
|
||||
return LWMQTT_REMAINING_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
// read packet id
|
||||
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// read all suback codes
|
||||
for (*count = 0; *count < (int)rem_len - 2; (*count)++) {
|
||||
// check max count
|
||||
if (*count > max_count) {
|
||||
return LWMQTT_SUBACK_ARRAY_OVERFLOW;
|
||||
}
|
||||
|
||||
// read qos level
|
||||
uint8_t raw_qos_level;
|
||||
err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_qos_level);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// set qos level
|
||||
switch (raw_qos_level) {
|
||||
case 0:
|
||||
granted_qos_levels[*count] = LWMQTT_QOS0;
|
||||
break;
|
||||
case 1:
|
||||
granted_qos_levels[*count] = LWMQTT_QOS1;
|
||||
break;
|
||||
case 2:
|
||||
granted_qos_levels[*count] = LWMQTT_QOS2;
|
||||
break;
|
||||
default:
|
||||
granted_qos_levels[*count] = LWMQTT_QOS_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
|
||||
lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
|
||||
lwmqtt_string_t *topic_filters) {
|
||||
// prepare pointer
|
||||
uint8_t *buf_ptr = buf;
|
||||
uint8_t *buf_end = buf + buf_len;
|
||||
|
||||
// calculate remaining length
|
||||
uint32_t rem_len = 2;
|
||||
for (int i = 0; i < count; i++) {
|
||||
rem_len += 2 + topic_filters[i].len;
|
||||
}
|
||||
|
||||
// check remaining length length
|
||||
int rem_len_len;
|
||||
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
|
||||
if (err == LWMQTT_VARNUM_OVERFLOW) {
|
||||
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
|
||||
}
|
||||
|
||||
// prepare header
|
||||
uint8_t header = 0;
|
||||
|
||||
// set packet type
|
||||
lwmqtt_write_bits(&header, LWMQTT_UNSUBSCRIBE_PACKET, 4, 4);
|
||||
|
||||
// set qos
|
||||
lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2);
|
||||
|
||||
// write header
|
||||
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write remaining length
|
||||
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write packet id
|
||||
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// write topics
|
||||
for (int i = 0; i < count; i++) {
|
||||
err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]);
|
||||
if (err != LWMQTT_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// set length
|
||||
*len = buf_ptr - buf;
|
||||
|
||||
return LWMQTT_SUCCESS;
|
||||
}
|
||||
185
P1_gateway_FW/lib/MQTT/src/lwmqtt/packet.h
Normal file
@@ -0,0 +1,185 @@
|
||||
#ifndef LWMQTT_PACKET_H
|
||||
#define LWMQTT_PACKET_H
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
/**
|
||||
* The available packet types.
|
||||
*/
|
||||
typedef enum {
|
||||
LWMQTT_NO_PACKET = 0,
|
||||
LWMQTT_CONNECT_PACKET = 1,
|
||||
LWMQTT_CONNACK_PACKET,
|
||||
LWMQTT_PUBLISH_PACKET,
|
||||
LWMQTT_PUBACK_PACKET,
|
||||
LWMQTT_PUBREC_PACKET,
|
||||
LWMQTT_PUBREL_PACKET,
|
||||
LWMQTT_PUBCOMP_PACKET,
|
||||
LWMQTT_SUBSCRIBE_PACKET,
|
||||
LWMQTT_SUBACK_PACKET,
|
||||
LWMQTT_UNSUBSCRIBE_PACKET,
|
||||
LWMQTT_UNSUBACK_PACKET,
|
||||
LWMQTT_PINGREQ_PACKET,
|
||||
LWMQTT_PINGRESP_PACKET,
|
||||
LWMQTT_DISCONNECT_PACKET
|
||||
} lwmqtt_packet_type_t;
|
||||
|
||||
/**
|
||||
* Will detect the packet type from the at least one byte long buffer.
|
||||
*
|
||||
* @param buf - The buffer from which the packet type will be detected.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param packet_type - The packet type.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type);
|
||||
|
||||
/**
|
||||
* Will detect the remaining length form the at least on byte long buffer.
|
||||
*
|
||||
* It will return LWMQTT_BUFFER_TOO_SHORT if the buffer is to short and an additional byte should be read from the
|
||||
* network. In case the remaining length is overflowed it will return LWMQTT_REMAINING_LENGTH_OVERFLOW.
|
||||
*
|
||||
* @param buf - The buffer from which the remaining length will be detected.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param rem_len - The detected remaining length.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len);
|
||||
|
||||
/**
|
||||
* Encodes a connect packet into the supplied buffer.
|
||||
*
|
||||
* @param buf - The buffer into which the packet will be encoded.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param len - The encoded length of the packet.
|
||||
* @param options - The options to be used to build the connect packet.
|
||||
* @param will - The last will and testament.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options,
|
||||
lwmqtt_will_t *will);
|
||||
|
||||
/**
|
||||
* Decodes a connack packet from the supplied buffer.
|
||||
*
|
||||
* @param buf - The raw buffer data.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param session_present - The session present flag.
|
||||
* @param return_code - The return code.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present,
|
||||
lwmqtt_return_code_t *return_code);
|
||||
|
||||
/**
|
||||
* Encodes a zero (disconnect, pingreq) packet into the supplied buffer.
|
||||
*
|
||||
* @param buf - The buffer into which the packet will be encoded.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param len - The encoded length of the packet.
|
||||
* @param packet_type - The packets type.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type);
|
||||
|
||||
/**
|
||||
* Decodes an ack (puback, pubrec, pubrel, pubcomp, unsuback) packet from the supplied buffer.
|
||||
*
|
||||
* @param buf - The raw buffer data.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param packet_type - The packet type.
|
||||
* @param dup - The dup flag.
|
||||
* @param packet_id - The packet id.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup,
|
||||
uint16_t *packet_id);
|
||||
|
||||
/**
|
||||
* Encodes an ack (puback, pubrec, pubrel, pubcomp) packet into the supplied buffer.
|
||||
*
|
||||
* @param buf - The buffer into which the packet will be encoded.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param len - The encoded length of the packet.
|
||||
* @param packet_type - The packets type.
|
||||
* @param dup - The dup flag.
|
||||
* @param packet_id - The packet id.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup,
|
||||
uint16_t packet_id);
|
||||
|
||||
/**
|
||||
* Decodes a publish packet from the supplied buffer.
|
||||
*
|
||||
* @param buf - The raw buffer data.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param dup - The dup flag.
|
||||
* @param packet_id - The packet id.
|
||||
* @param topic - The topic.
|
||||
* @parma msg - The message.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic,
|
||||
lwmqtt_message_t *msg);
|
||||
|
||||
/**
|
||||
* Encodes a publish packet into the supplied buffer.
|
||||
*
|
||||
* @param buf - The buffer into which the packet will be encoded.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param len - The encoded length of the packet.
|
||||
* @param dup - The dup flag.
|
||||
* @param packet_id - The packet id.
|
||||
* @param topic - The topic.
|
||||
* @param msg - The message.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id,
|
||||
lwmqtt_string_t topic, lwmqtt_message_t msg);
|
||||
|
||||
/**
|
||||
* Encodes a subscribe packet into the supplied buffer.
|
||||
*
|
||||
* @param buf - The buffer into which the packet will be encoded.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param len - The encoded length of the packet.
|
||||
* @param packet_id - The packet id.
|
||||
* @param count - The number of members in the topic_filters and qos_levels array.
|
||||
* @param topic_filters - The array of topic filter.
|
||||
* @param qos_levels - The array of requested QoS levels.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
|
||||
lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels);
|
||||
|
||||
/**
|
||||
* Decodes a suback packet from the supplied buffer.
|
||||
*
|
||||
* @param buf - The raw buffer data.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param packet_id - The packet id.
|
||||
* @param max_count - The maximum number of members allowed in the granted_qos_levels array.
|
||||
* @param count - The number of members in the granted_qos_levels array.
|
||||
* @param granted_qos_levels - The granted QoS levels.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count,
|
||||
lwmqtt_qos_t *granted_qos_levels);
|
||||
|
||||
/**
|
||||
* Encodes the supplied unsubscribe data into the supplied buffer, ready for sending
|
||||
*
|
||||
* @param buf - The buffer into which the packet will be encoded.
|
||||
* @param buf_len - The length of the specified buffer.
|
||||
* @param len - The encoded length of the packet.
|
||||
* @param packet_id - The packet id.
|
||||
* @param count - The number of members in the topic_filters array.
|
||||
* @param topic_filters - The array of topic filters.
|
||||
* @return An error value.
|
||||
*/
|
||||
lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
|
||||
lwmqtt_string_t *topic_filters);
|
||||
|
||||
#endif // LWMQTT_PACKET_H
|
||||
38
P1_gateway_FW/lib/MQTT/src/lwmqtt/string.c
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "lwmqtt.h"
|
||||
|
||||
lwmqtt_string_t lwmqtt_string(const char *str) {
|
||||
// check for null
|
||||
if (str == NULL) {
|
||||
return (lwmqtt_string_t){0, NULL};
|
||||
}
|
||||
|
||||
// get length
|
||||
uint16_t len = (uint16_t)strlen(str);
|
||||
|
||||
// check zero length
|
||||
if (len == 0) {
|
||||
return (lwmqtt_string_t){0, NULL};
|
||||
}
|
||||
|
||||
return (lwmqtt_string_t){len, (char *)str};
|
||||
}
|
||||
|
||||
int lwmqtt_strcmp(lwmqtt_string_t a, const char *b) {
|
||||
// get string of b
|
||||
lwmqtt_string_t b_str = lwmqtt_string(b);
|
||||
|
||||
// return if both are zero length
|
||||
if (a.len == 0 && b_str.len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return if lengths are different
|
||||
if (a.len != b_str.len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// compare memory of same length
|
||||
return strncmp(a.data, b_str.data, a.len);
|
||||
}
|
||||
5
P1_gateway_FW/lib/PubSubClient/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
tests/bin
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.clang_complete
|
||||
.gcc-flags.json
|
||||
1
P1_gateway_FW/lib/PubSubClient/.piopm
Normal file
@@ -0,0 +1 @@
|
||||
{"type": "library", "name": "PubSubClient", "version": "2.8.0", "spec": {"owner": "knolleary", "id": 89, "name": "PubSubClient", "requirements": null, "url": null}}
|
||||
7
P1_gateway_FW/lib/PubSubClient/.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
sudo: false
|
||||
language: cpp
|
||||
compiler:
|
||||
- g++
|
||||
script: cd tests && make && make test
|
||||
os:
|
||||
- linux
|
||||
85
P1_gateway_FW/lib/PubSubClient/CHANGES.txt
Executable file
@@ -0,0 +1,85 @@
|
||||
2.8
|
||||
* Add setBufferSize() to override MQTT_MAX_PACKET_SIZE
|
||||
* Add setKeepAlive() to override MQTT_KEEPALIVE
|
||||
* Add setSocketTimeout() to overide MQTT_SOCKET_TIMEOUT
|
||||
* Added check to prevent subscribe/unsubscribe to empty topics
|
||||
* Declare wifi mode prior to connect in ESP example
|
||||
* Use `strnlen` to avoid overruns
|
||||
* Support pre-connected Client objects
|
||||
|
||||
2.7
|
||||
* Fix remaining-length handling to prevent buffer overrun
|
||||
* Add large-payload API - beginPublish/write/publish/endPublish
|
||||
* Add yield call to improve reliability on ESP
|
||||
* Add Clean Session flag to connect options
|
||||
* Add ESP32 support for functional callback signature
|
||||
* Various other fixes
|
||||
|
||||
2.4
|
||||
* Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely
|
||||
whilst waiting for inbound data
|
||||
* Fixed return code when publishing >256 bytes
|
||||
|
||||
2.3
|
||||
* Add publish(topic,payload,retained) function
|
||||
|
||||
2.2
|
||||
* Change code layout to match Arduino Library reqs
|
||||
|
||||
2.1
|
||||
* Add MAX_TRANSFER_SIZE def to chunk messages if needed
|
||||
* Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE
|
||||
|
||||
2.0
|
||||
* Add (and default to) MQTT 3.1.1 support
|
||||
* Fix PROGMEM handling for Intel Galileo/ESP8266
|
||||
* Add overloaded constructors for convenience
|
||||
* Add chainable setters for server/callback/client/stream
|
||||
* Add state function to return connack return code
|
||||
|
||||
1.9
|
||||
* Do not split MQTT packets over multiple calls to _client->write()
|
||||
* API change: All constructors now require an instance of Client
|
||||
to be passed in.
|
||||
* Fixed example to match 1.8 api changes - dpslwk
|
||||
* Added username/password support - WilHall
|
||||
* Added publish_P - publishes messages from PROGMEM - jobytaffey
|
||||
|
||||
1.8
|
||||
* KeepAlive interval is configurable in PubSubClient.h
|
||||
* Maximum packet size is configurable in PubSubClient.h
|
||||
* API change: Return boolean rather than int from various functions
|
||||
* API change: Length parameter in message callback changed
|
||||
from int to unsigned int
|
||||
* Various internal tidy-ups around types
|
||||
1.7
|
||||
* Improved keepalive handling
|
||||
* Updated to the Arduino-1.0 API
|
||||
1.6
|
||||
* Added the ability to publish a retained message
|
||||
|
||||
1.5
|
||||
* Added default constructor
|
||||
* Fixed compile error when used with arduino-0021 or later
|
||||
|
||||
1.4
|
||||
* Fixed connection lost handling
|
||||
|
||||
1.3
|
||||
* Fixed packet reading bug in PubSubClient.readPacket
|
||||
|
||||
1.2
|
||||
* Fixed compile error when used with arduino-0016 or later
|
||||
|
||||
|
||||
1.1
|
||||
* Reduced size of library
|
||||
* Added support for Will messages
|
||||
* Clarified licensing - see LICENSE.txt
|
||||
|
||||
|
||||
1.0
|
||||
* Only Quality of Service (QOS) 0 messaging is supported
|
||||
* The maximum message size, including header, is 128 bytes
|
||||
* The keepalive interval is set to 30 seconds
|
||||
* No support for Will messages
|
||||
20
P1_gateway_FW/lib/PubSubClient/LICENSE.txt
Executable file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2008-2020 Nicholas O'Leary
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
50
P1_gateway_FW/lib/PubSubClient/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Arduino Client for MQTT
|
||||
|
||||
This library provides a client for doing simple publish/subscribe messaging with
|
||||
a server that supports MQTT.
|
||||
|
||||
## Examples
|
||||
|
||||
The library comes with a number of example sketches. See File > Examples > PubSubClient
|
||||
within the Arduino application.
|
||||
|
||||
Full API documentation is available here: https://pubsubclient.knolleary.net
|
||||
|
||||
## Limitations
|
||||
|
||||
- It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.
|
||||
- The maximum message size, including header, is **256 bytes** by default. This
|
||||
is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h` or can be changed
|
||||
by calling `PubSubClient::setBufferSize(size)`.
|
||||
- The keepalive interval is set to 15 seconds by default. This is configurable
|
||||
via `MQTT_KEEPALIVE` in `PubSubClient.h` or can be changed by calling
|
||||
`PubSubClient::setKeepAlive(keepAlive)`.
|
||||
- The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by
|
||||
changing value of `MQTT_VERSION` in `PubSubClient.h`.
|
||||
|
||||
|
||||
## Compatible Hardware
|
||||
|
||||
The library uses the Arduino Ethernet Client api for interacting with the
|
||||
underlying network hardware. This means it Just Works with a growing number of
|
||||
boards and shields, including:
|
||||
|
||||
- Arduino Ethernet
|
||||
- Arduino Ethernet Shield
|
||||
- Arduino YUN – use the included `YunClient` in place of `EthernetClient`, and
|
||||
be sure to do a `Bridge.begin()` first
|
||||
- Arduino WiFi Shield - if you want to send packets > 90 bytes with this shield,
|
||||
enable the `MQTT_MAX_TRANSFER_SIZE` define in `PubSubClient.h`.
|
||||
- Sparkfun WiFly Shield – [library](https://github.com/dpslwk/WiFly)
|
||||
- TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library)
|
||||
- Intel Galileo/Edison
|
||||
- ESP8266
|
||||
- ESP32
|
||||
|
||||
The library cannot currently be used with hardware based on the ENC28J60 chip –
|
||||
such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an
|
||||
[alternative library](https://github.com/njh/NanodeMQTT) available.
|
||||
|
||||
## License
|
||||
|
||||
This code is released under the MIT License.
|
||||
43
P1_gateway_FW/lib/PubSubClient/examples/mqtt_auth/mqtt_auth.ino
Executable file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Basic MQTT example with Authentication
|
||||
|
||||
- connects to an MQTT server, providing username
|
||||
and password
|
||||
- publishes "hello world" to the topic "outTopic"
|
||||
- subscribes to the topic "inTopic"
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
// Update these with values suitable for your network.
|
||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
||||
IPAddress ip(172, 16, 0, 100);
|
||||
IPAddress server(172, 16, 0, 2);
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
// handle message arrived
|
||||
}
|
||||
|
||||
EthernetClient ethClient;
|
||||
PubSubClient client(server, 1883, callback, ethClient);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Ethernet.begin(mac, ip);
|
||||
// Note - the default maximum packet size is 128 bytes. If the
|
||||
// combined length of clientId, username and password exceed this use the
|
||||
// following to increase the buffer size:
|
||||
// client.setBufferSize(255);
|
||||
|
||||
if (client.connect("arduinoClient", "testuser", "testpass")) {
|
||||
client.publish("outTopic","hello world");
|
||||
client.subscribe("inTopic");
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
client.loop();
|
||||
}
|
||||
77
P1_gateway_FW/lib/PubSubClient/examples/mqtt_basic/mqtt_basic.ino
Executable file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Basic MQTT example
|
||||
|
||||
This sketch demonstrates the basic capabilities of the library.
|
||||
It connects to an MQTT server then:
|
||||
- publishes "hello world" to the topic "outTopic"
|
||||
- subscribes to the topic "inTopic", printing out any messages
|
||||
it receives. NB - it assumes the received payloads are strings not binary
|
||||
|
||||
It will reconnect to the server if the connection is lost using a blocking
|
||||
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
|
||||
achieve the same result without blocking the main loop.
|
||||
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
// Update these with values suitable for your network.
|
||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
||||
IPAddress ip(172, 16, 0, 100);
|
||||
IPAddress server(172, 16, 0, 2);
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
Serial.print("Message arrived [");
|
||||
Serial.print(topic);
|
||||
Serial.print("] ");
|
||||
for (int i=0;i<length;i++) {
|
||||
Serial.print((char)payload[i]);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
EthernetClient ethClient;
|
||||
PubSubClient client(ethClient);
|
||||
|
||||
void reconnect() {
|
||||
// Loop until we're reconnected
|
||||
while (!client.connected()) {
|
||||
Serial.print("Attempting MQTT connection...");
|
||||
// Attempt to connect
|
||||
if (client.connect("arduinoClient")) {
|
||||
Serial.println("connected");
|
||||
// Once connected, publish an announcement...
|
||||
client.publish("outTopic","hello world");
|
||||
// ... and resubscribe
|
||||
client.subscribe("inTopic");
|
||||
} else {
|
||||
Serial.print("failed, rc=");
|
||||
Serial.print(client.state());
|
||||
Serial.println(" try again in 5 seconds");
|
||||
// Wait 5 seconds before retrying
|
||||
delay(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(57600);
|
||||
|
||||
client.setServer(server, 1883);
|
||||
client.setCallback(callback);
|
||||
|
||||
Ethernet.begin(mac, ip);
|
||||
// Allow the hardware to sort itself out
|
||||
delay(1500);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (!client.connected()) {
|
||||
reconnect();
|
||||
}
|
||||
client.loop();
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Basic ESP8266 MQTT example
|
||||
This sketch demonstrates the capabilities of the pubsub library in combination
|
||||
with the ESP8266 board/library.
|
||||
It connects to an MQTT server then:
|
||||
- publishes "hello world" to the topic "outTopic" every two seconds
|
||||
- subscribes to the topic "inTopic", printing out any messages
|
||||
it receives. NB - it assumes the received payloads are strings not binary
|
||||
- If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
|
||||
else switch it off
|
||||
It will reconnect to the server if the connection is lost using a blocking
|
||||
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
|
||||
achieve the same result without blocking the main loop.
|
||||
To install the ESP8266 board, (using Arduino 1.6.4+):
|
||||
- Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
|
||||
http://arduino.esp8266.com/stable/package_esp8266com_index.json
|
||||
- Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
|
||||
- Select your ESP8266 in "Tools -> Board"
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
// Update these with values suitable for your network.
|
||||
|
||||
const char* ssid = "........";
|
||||
const char* password = "........";
|
||||
const char* mqtt_server = "broker.mqtt-dashboard.com";
|
||||
|
||||
WiFiClient espClient;
|
||||
PubSubClient client(espClient);
|
||||
unsigned long lastMsg = 0;
|
||||
#define MSG_BUFFER_SIZE (50)
|
||||
char msg[MSG_BUFFER_SIZE];
|
||||
int value = 0;
|
||||
|
||||
void setup_wifi() {
|
||||
|
||||
delay(10);
|
||||
// We start by connecting to a WiFi network
|
||||
Serial.println();
|
||||
Serial.print("Connecting to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
randomSeed(micros());
|
||||
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
Serial.print("Message arrived [");
|
||||
Serial.print(topic);
|
||||
Serial.print("] ");
|
||||
for (int i = 0; i < length; i++) {
|
||||
Serial.print((char)payload[i]);
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Switch on the LED if an 1 was received as first character
|
||||
if ((char)payload[0] == '1') {
|
||||
digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
|
||||
// but actually the LED is on; this is because
|
||||
// it is active low on the ESP-01)
|
||||
} else {
|
||||
digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void reconnect() {
|
||||
// Loop until we're reconnected
|
||||
while (!client.connected()) {
|
||||
Serial.print("Attempting MQTT connection...");
|
||||
// Create a random client ID
|
||||
String clientId = "ESP8266Client-";
|
||||
clientId += String(random(0xffff), HEX);
|
||||
// Attempt to connect
|
||||
if (client.connect(clientId.c_str())) {
|
||||
Serial.println("connected");
|
||||
// Once connected, publish an announcement...
|
||||
client.publish("outTopic", "hello world");
|
||||
// ... and resubscribe
|
||||
client.subscribe("inTopic");
|
||||
} else {
|
||||
Serial.print("failed, rc=");
|
||||
Serial.print(client.state());
|
||||
Serial.println(" try again in 5 seconds");
|
||||
// Wait 5 seconds before retrying
|
||||
delay(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
|
||||
Serial.begin(115200);
|
||||
setup_wifi();
|
||||
client.setServer(mqtt_server, 1883);
|
||||
client.setCallback(callback);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
if (!client.connected()) {
|
||||
reconnect();
|
||||
}
|
||||
client.loop();
|
||||
|
||||
unsigned long now = millis();
|
||||
if (now - lastMsg > 2000) {
|
||||
lastMsg = now;
|
||||
++value;
|
||||
snprintf (msg, MSG_BUFFER_SIZE, "hello world #%ld", value);
|
||||
Serial.print("Publish message: ");
|
||||
Serial.println(msg);
|
||||
client.publish("outTopic", msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
Long message ESP8266 MQTT example
|
||||
|
||||
This sketch demonstrates sending arbitrarily large messages in combination
|
||||
with the ESP8266 board/library.
|
||||
|
||||
It connects to an MQTT server then:
|
||||
- publishes "hello world" to the topic "outTopic"
|
||||
- subscribes to the topic "greenBottles/#", printing out any messages
|
||||
it receives. NB - it assumes the received payloads are strings not binary
|
||||
- If the sub-topic is a number, it publishes a "greenBottles/lyrics" message
|
||||
with a payload consisting of the lyrics to "10 green bottles", replacing
|
||||
10 with the number given in the sub-topic.
|
||||
|
||||
It will reconnect to the server if the connection is lost using a blocking
|
||||
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
|
||||
achieve the same result without blocking the main loop.
|
||||
|
||||
To install the ESP8266 board, (using Arduino 1.6.4+):
|
||||
- Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
|
||||
http://arduino.esp8266.com/stable/package_esp8266com_index.json
|
||||
- Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
|
||||
- Select your ESP8266 in "Tools -> Board"
|
||||
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
// Update these with values suitable for your network.
|
||||
|
||||
const char* ssid = "........";
|
||||
const char* password = "........";
|
||||
const char* mqtt_server = "broker.mqtt-dashboard.com";
|
||||
|
||||
WiFiClient espClient;
|
||||
PubSubClient client(espClient);
|
||||
long lastMsg = 0;
|
||||
char msg[50];
|
||||
int value = 0;
|
||||
|
||||
void setup_wifi() {
|
||||
|
||||
delay(10);
|
||||
// We start by connecting to a WiFi network
|
||||
Serial.println();
|
||||
Serial.print("Connecting to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
randomSeed(micros());
|
||||
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
Serial.print("Message arrived [");
|
||||
Serial.print(topic);
|
||||
Serial.print("] ");
|
||||
for (int i = 0; i < length; i++) {
|
||||
Serial.print((char)payload[i]);
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Find out how many bottles we should generate lyrics for
|
||||
String topicStr(topic);
|
||||
int bottleCount = 0; // assume no bottles unless we correctly parse a value from the topic
|
||||
if (topicStr.indexOf('/') >= 0) {
|
||||
// The topic includes a '/', we'll try to read the number of bottles from just after that
|
||||
topicStr.remove(0, topicStr.indexOf('/')+1);
|
||||
// Now see if there's a number of bottles after the '/'
|
||||
bottleCount = topicStr.toInt();
|
||||
}
|
||||
|
||||
if (bottleCount > 0) {
|
||||
// Work out how big our resulting message will be
|
||||
int msgLen = 0;
|
||||
for (int i = bottleCount; i > 0; i--) {
|
||||
String numBottles(i);
|
||||
msgLen += 2*numBottles.length();
|
||||
if (i == 1) {
|
||||
msgLen += 2*String(" green bottle, standing on the wall\n").length();
|
||||
} else {
|
||||
msgLen += 2*String(" green bottles, standing on the wall\n").length();
|
||||
}
|
||||
msgLen += String("And if one green bottle should accidentally fall\nThere'll be ").length();
|
||||
switch (i) {
|
||||
case 1:
|
||||
msgLen += String("no green bottles, standing on the wall\n\n").length();
|
||||
break;
|
||||
case 2:
|
||||
msgLen += String("1 green bottle, standing on the wall\n\n").length();
|
||||
break;
|
||||
default:
|
||||
numBottles = i-1;
|
||||
msgLen += numBottles.length();
|
||||
msgLen += String(" green bottles, standing on the wall\n\n").length();
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// Now we can start to publish the message
|
||||
client.beginPublish("greenBottles/lyrics", msgLen, false);
|
||||
for (int i = bottleCount; i > 0; i--) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
client.print(i);
|
||||
if (i == 1) {
|
||||
client.print(" green bottle, standing on the wall\n");
|
||||
} else {
|
||||
client.print(" green bottles, standing on the wall\n");
|
||||
}
|
||||
}
|
||||
client.print("And if one green bottle should accidentally fall\nThere'll be ");
|
||||
switch (i) {
|
||||
case 1:
|
||||
client.print("no green bottles, standing on the wall\n\n");
|
||||
break;
|
||||
case 2:
|
||||
client.print("1 green bottle, standing on the wall\n\n");
|
||||
break;
|
||||
default:
|
||||
client.print(i-1);
|
||||
client.print(" green bottles, standing on the wall\n\n");
|
||||
break;
|
||||
};
|
||||
}
|
||||
// Now we're done!
|
||||
client.endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
void reconnect() {
|
||||
// Loop until we're reconnected
|
||||
while (!client.connected()) {
|
||||
Serial.print("Attempting MQTT connection...");
|
||||
// Create a random client ID
|
||||
String clientId = "ESP8266Client-";
|
||||
clientId += String(random(0xffff), HEX);
|
||||
// Attempt to connect
|
||||
if (client.connect(clientId.c_str())) {
|
||||
Serial.println("connected");
|
||||
// Once connected, publish an announcement...
|
||||
client.publish("outTopic", "hello world");
|
||||
// ... and resubscribe
|
||||
client.subscribe("greenBottles/#");
|
||||
} else {
|
||||
Serial.print("failed, rc=");
|
||||
Serial.print(client.state());
|
||||
Serial.println(" try again in 5 seconds");
|
||||
// Wait 5 seconds before retrying
|
||||
delay(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
|
||||
Serial.begin(115200);
|
||||
setup_wifi();
|
||||
client.setServer(mqtt_server, 1883);
|
||||
client.setCallback(callback);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
if (!client.connected()) {
|
||||
reconnect();
|
||||
}
|
||||
client.loop();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Publishing in the callback
|
||||
|
||||
- connects to an MQTT server
|
||||
- subscribes to the topic "inTopic"
|
||||
- when a message is received, republishes it to "outTopic"
|
||||
|
||||
This example shows how to publish messages within the
|
||||
callback function. The callback function header needs to
|
||||
be declared before the PubSubClient constructor and the
|
||||
actual callback defined afterwards.
|
||||
This ensures the client reference in the callback function
|
||||
is valid.
|
||||
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
// Update these with values suitable for your network.
|
||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
||||
IPAddress ip(172, 16, 0, 100);
|
||||
IPAddress server(172, 16, 0, 2);
|
||||
|
||||
// Callback function header
|
||||
void callback(char* topic, byte* payload, unsigned int length);
|
||||
|
||||
EthernetClient ethClient;
|
||||
PubSubClient client(server, 1883, callback, ethClient);
|
||||
|
||||
// Callback function
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
// In order to republish this payload, a copy must be made
|
||||
// as the orignal payload buffer will be overwritten whilst
|
||||
// constructing the PUBLISH packet.
|
||||
|
||||
// Allocate the correct amount of memory for the payload copy
|
||||
byte* p = (byte*)malloc(length);
|
||||
// Copy the payload to the new buffer
|
||||
memcpy(p,payload,length);
|
||||
client.publish("outTopic", p, length);
|
||||
// Free the memory
|
||||
free(p);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
|
||||
Ethernet.begin(mac, ip);
|
||||
if (client.connect("arduinoClient")) {
|
||||
client.publish("outTopic","hello world");
|
||||
client.subscribe("inTopic");
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
client.loop();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Reconnecting MQTT example - non-blocking
|
||||
|
||||
This sketch demonstrates how to keep the client connected
|
||||
using a non-blocking reconnect function. If the client loses
|
||||
its connection, it attempts to reconnect every 5 seconds
|
||||
without blocking the main loop.
|
||||
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
// Update these with values suitable for your hardware/network.
|
||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
||||
IPAddress ip(172, 16, 0, 100);
|
||||
IPAddress server(172, 16, 0, 2);
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
// handle message arrived
|
||||
}
|
||||
|
||||
EthernetClient ethClient;
|
||||
PubSubClient client(ethClient);
|
||||
|
||||
long lastReconnectAttempt = 0;
|
||||
|
||||
boolean reconnect() {
|
||||
if (client.connect("arduinoClient")) {
|
||||
// Once connected, publish an announcement...
|
||||
client.publish("outTopic","hello world");
|
||||
// ... and resubscribe
|
||||
client.subscribe("inTopic");
|
||||
}
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
client.setServer(server, 1883);
|
||||
client.setCallback(callback);
|
||||
|
||||
Ethernet.begin(mac, ip);
|
||||
delay(1500);
|
||||
lastReconnectAttempt = 0;
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (!client.connected()) {
|
||||
long now = millis();
|
||||
if (now - lastReconnectAttempt > 5000) {
|
||||
lastReconnectAttempt = now;
|
||||
// Attempt to reconnect
|
||||
if (reconnect()) {
|
||||
lastReconnectAttempt = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Client connected
|
||||
|
||||
client.loop();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Example of using a Stream object to store the message payload
|
||||
|
||||
Uses SRAM library: https://github.com/ennui2342/arduino-sram
|
||||
but could use any Stream based class such as SD
|
||||
|
||||
- connects to an MQTT server
|
||||
- publishes "hello world" to the topic "outTopic"
|
||||
- subscribes to the topic "inTopic"
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <SRAM.h>
|
||||
|
||||
// Update these with values suitable for your network.
|
||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
||||
IPAddress ip(172, 16, 0, 100);
|
||||
IPAddress server(172, 16, 0, 2);
|
||||
|
||||
SRAM sram(4, SRAM_1024);
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
sram.seek(1);
|
||||
|
||||
// do something with the message
|
||||
for(uint8_t i=0; i<length; i++) {
|
||||
Serial.write(sram.read());
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Reset position for the next message to be stored
|
||||
sram.seek(1);
|
||||
}
|
||||
|
||||
EthernetClient ethClient;
|
||||
PubSubClient client(server, 1883, callback, ethClient, sram);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Ethernet.begin(mac, ip);
|
||||
if (client.connect("arduinoClient")) {
|
||||
client.publish("outTopic","hello world");
|
||||
client.subscribe("inTopic");
|
||||
}
|
||||
|
||||
sram.begin();
|
||||
sram.seek(1);
|
||||
|
||||
Serial.begin(9600);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
client.loop();
|
||||
}
|
||||
36
P1_gateway_FW/lib/PubSubClient/keywords.txt
Executable file
@@ -0,0 +1,36 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For PubSubClient
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
PubSubClient KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
connect KEYWORD2
|
||||
disconnect KEYWORD2
|
||||
publish KEYWORD2
|
||||
publish_P KEYWORD2
|
||||
beginPublish KEYWORD2
|
||||
endPublish KEYWORD2
|
||||
write KEYWORD2
|
||||
subscribe KEYWORD2
|
||||
unsubscribe KEYWORD2
|
||||
loop KEYWORD2
|
||||
connected KEYWORD2
|
||||
setServer KEYWORD2
|
||||
setCallback KEYWORD2
|
||||
setClient KEYWORD2
|
||||
setStream KEYWORD2
|
||||
setKeepAlive KEYWORD2
|
||||
setBufferSize KEYWORD2
|
||||
setSocketTimeout KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
18
P1_gateway_FW/lib/PubSubClient/library.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "PubSubClient",
|
||||
"keywords": "ethernet, mqtt, m2m, iot",
|
||||
"description": "A client library for MQTT messaging. 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.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/knolleary/pubsubclient.git"
|
||||
},
|
||||
"version": "2.8",
|
||||
"exclude": "tests",
|
||||
"examples": "examples/*/*.ino",
|
||||
"frameworks": "arduino",
|
||||
"platforms": [
|
||||
"atmelavr",
|
||||
"espressif8266",
|
||||
"espressif32"
|
||||
]
|
||||
}
|
||||
9
P1_gateway_FW/lib/PubSubClient/library.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
name=PubSubClient
|
||||
version=2.8
|
||||
author=Nick O'Leary <nick.oleary@gmail.com>
|
||||
maintainer=Nick O'Leary <nick.oleary@gmail.com>
|
||||
sentence=A client library for MQTT messaging.
|
||||
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.
|
||||
category=Communication
|
||||
url=http://pubsubclient.knolleary.net
|
||||
architectures=*
|
||||
769
P1_gateway_FW/lib/PubSubClient/src/PubSubClient.cpp
Executable file
@@ -0,0 +1,769 @@
|
||||
/*
|
||||
|
||||
PubSubClient.cpp - A simple client for MQTT.
|
||||
Nick O'Leary
|
||||
http://knolleary.net
|
||||
*/
|
||||
|
||||
#include "PubSubClient.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
PubSubClient::PubSubClient() {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
this->_client = NULL;
|
||||
this->stream = NULL;
|
||||
setCallback(NULL);
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr, port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr, port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip, port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip, port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
this->bufferSize = 0;
|
||||
setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||
setKeepAlive(MQTT_KEEPALIVE);
|
||||
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
|
||||
}
|
||||
|
||||
PubSubClient::~PubSubClient() {
|
||||
free(this->buffer);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id) {
|
||||
return connect(id,NULL,NULL,0,0,0,0,1);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
|
||||
return connect(id,user,pass,0,0,0,0,1);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
|
||||
return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
|
||||
return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
|
||||
if (!connected()) {
|
||||
int result = 0;
|
||||
|
||||
|
||||
if(_client->connected()) {
|
||||
result = 1;
|
||||
} else {
|
||||
if (domain != NULL) {
|
||||
result = _client->connect(this->domain, this->port);
|
||||
} else {
|
||||
result = _client->connect(this->ip, this->port);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 1) {
|
||||
nextMsgId = 1;
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
unsigned int j;
|
||||
|
||||
#if MQTT_VERSION == MQTT_VERSION_3_1
|
||||
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
|
||||
#define MQTT_HEADER_VERSION_LENGTH 9
|
||||
#elif MQTT_VERSION == MQTT_VERSION_3_1_1
|
||||
uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
|
||||
#define MQTT_HEADER_VERSION_LENGTH 7
|
||||
#endif
|
||||
for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
|
||||
this->buffer[length++] = d[j];
|
||||
}
|
||||
|
||||
uint8_t v;
|
||||
if (willTopic) {
|
||||
v = 0x04|(willQos<<3)|(willRetain<<5);
|
||||
} else {
|
||||
v = 0x00;
|
||||
}
|
||||
if (cleanSession) {
|
||||
v = v|0x02;
|
||||
}
|
||||
|
||||
if(user != NULL) {
|
||||
v = v|0x80;
|
||||
|
||||
if(pass != NULL) {
|
||||
v = v|(0x80>>1);
|
||||
}
|
||||
}
|
||||
this->buffer[length++] = v;
|
||||
|
||||
this->buffer[length++] = ((this->keepAlive) >> 8);
|
||||
this->buffer[length++] = ((this->keepAlive) & 0xFF);
|
||||
|
||||
CHECK_STRING_LENGTH(length,id)
|
||||
length = writeString(id,this->buffer,length);
|
||||
if (willTopic) {
|
||||
CHECK_STRING_LENGTH(length,willTopic)
|
||||
length = writeString(willTopic,this->buffer,length);
|
||||
CHECK_STRING_LENGTH(length,willMessage)
|
||||
length = writeString(willMessage,this->buffer,length);
|
||||
}
|
||||
|
||||
if(user != NULL) {
|
||||
CHECK_STRING_LENGTH(length,user)
|
||||
length = writeString(user,this->buffer,length);
|
||||
if(pass != NULL) {
|
||||
CHECK_STRING_LENGTH(length,pass)
|
||||
length = writeString(pass,this->buffer,length);
|
||||
}
|
||||
}
|
||||
|
||||
write(MQTTCONNECT,this->buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
|
||||
while (!_client->available()) {
|
||||
unsigned long t = millis();
|
||||
if (t-lastInActivity >= ((int32_t) this->socketTimeout*1000UL)) {
|
||||
_state = MQTT_CONNECTION_TIMEOUT;
|
||||
_client->stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint8_t llen;
|
||||
uint32_t len = readPacket(&llen);
|
||||
|
||||
if (len == 4) {
|
||||
if (buffer[3] == 0) {
|
||||
lastInActivity = millis();
|
||||
pingOutstanding = false;
|
||||
_state = MQTT_CONNECTED;
|
||||
return true;
|
||||
} else {
|
||||
_state = buffer[3];
|
||||
}
|
||||
}
|
||||
_client->stop();
|
||||
} else {
|
||||
_state = MQTT_CONNECT_FAILED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// reads a byte into result
|
||||
boolean PubSubClient::readByte(uint8_t * result) {
|
||||
uint32_t previousMillis = millis();
|
||||
while(!_client->available()) {
|
||||
yield();
|
||||
uint32_t currentMillis = millis();
|
||||
if(currentMillis - previousMillis >= ((int32_t) this->socketTimeout * 1000)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*result = _client->read();
|
||||
return true;
|
||||
}
|
||||
|
||||
// reads a byte into result[*index] and increments index
|
||||
boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
|
||||
uint16_t current_index = *index;
|
||||
uint8_t * write_address = &(result[current_index]);
|
||||
if(readByte(write_address)){
|
||||
*index = current_index + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t PubSubClient::readPacket(uint8_t* lengthLength) {
|
||||
uint16_t len = 0;
|
||||
if(!readByte(this->buffer, &len)) return 0;
|
||||
bool isPublish = (this->buffer[0]&0xF0) == MQTTPUBLISH;
|
||||
uint32_t multiplier = 1;
|
||||
uint32_t length = 0;
|
||||
uint8_t digit = 0;
|
||||
uint16_t skip = 0;
|
||||
uint32_t start = 0;
|
||||
|
||||
do {
|
||||
if (len == 5) {
|
||||
// Invalid remaining length encoding - kill the connection
|
||||
_state = MQTT_DISCONNECTED;
|
||||
_client->stop();
|
||||
return 0;
|
||||
}
|
||||
if(!readByte(&digit)) return 0;
|
||||
this->buffer[len++] = digit;
|
||||
length += (digit & 127) * multiplier;
|
||||
multiplier <<=7; //multiplier *= 128
|
||||
} while ((digit & 128) != 0);
|
||||
*lengthLength = len-1;
|
||||
|
||||
if (isPublish) {
|
||||
// Read in topic length to calculate bytes to skip over for Stream writing
|
||||
if(!readByte(this->buffer, &len)) return 0;
|
||||
if(!readByte(this->buffer, &len)) return 0;
|
||||
skip = (this->buffer[*lengthLength+1]<<8)+this->buffer[*lengthLength+2];
|
||||
start = 2;
|
||||
if (this->buffer[0]&MQTTQOS1) {
|
||||
// skip message id
|
||||
skip += 2;
|
||||
}
|
||||
}
|
||||
uint32_t idx = len;
|
||||
|
||||
for (uint32_t i = start;i<length;i++) {
|
||||
if(!readByte(&digit)) return 0;
|
||||
if (this->stream) {
|
||||
if (isPublish && idx-*lengthLength-2>skip) {
|
||||
this->stream->write(digit);
|
||||
}
|
||||
}
|
||||
|
||||
if (len < this->bufferSize) {
|
||||
this->buffer[len] = digit;
|
||||
len++;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (!this->stream && idx > this->bufferSize) {
|
||||
len = 0; // This will cause the packet to be ignored.
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
boolean PubSubClient::loop() {
|
||||
if (connected()) {
|
||||
unsigned long t = millis();
|
||||
if ((t - lastInActivity > this->keepAlive*1000UL) || (t - lastOutActivity > this->keepAlive*1000UL)) {
|
||||
if (pingOutstanding) {
|
||||
this->_state = MQTT_CONNECTION_TIMEOUT;
|
||||
_client->stop();
|
||||
return false;
|
||||
} else {
|
||||
this->buffer[0] = MQTTPINGREQ;
|
||||
this->buffer[1] = 0;
|
||||
_client->write(this->buffer,2);
|
||||
lastOutActivity = t;
|
||||
lastInActivity = t;
|
||||
pingOutstanding = true;
|
||||
}
|
||||
}
|
||||
if (_client->available()) {
|
||||
uint8_t llen;
|
||||
uint16_t len = readPacket(&llen);
|
||||
uint16_t msgId = 0;
|
||||
uint8_t *payload;
|
||||
if (len > 0) {
|
||||
lastInActivity = t;
|
||||
uint8_t type = this->buffer[0]&0xF0;
|
||||
if (type == MQTTPUBLISH) {
|
||||
if (callback) {
|
||||
uint16_t tl = (this->buffer[llen+1]<<8)+this->buffer[llen+2]; /* topic length in bytes */
|
||||
memmove(this->buffer+llen+2,this->buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
|
||||
this->buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
|
||||
char *topic = (char*) this->buffer+llen+2;
|
||||
// msgId only present for QOS>0
|
||||
if ((this->buffer[0]&0x06) == MQTTQOS1) {
|
||||
msgId = (this->buffer[llen+3+tl]<<8)+this->buffer[llen+3+tl+1];
|
||||
payload = this->buffer+llen+3+tl+2;
|
||||
callback(topic,payload,len-llen-3-tl-2);
|
||||
|
||||
this->buffer[0] = MQTTPUBACK;
|
||||
this->buffer[1] = 2;
|
||||
this->buffer[2] = (msgId >> 8);
|
||||
this->buffer[3] = (msgId & 0xFF);
|
||||
_client->write(this->buffer,4);
|
||||
lastOutActivity = t;
|
||||
|
||||
} else {
|
||||
payload = this->buffer+llen+3+tl;
|
||||
callback(topic,payload,len-llen-3-tl);
|
||||
}
|
||||
}
|
||||
} else if (type == MQTTPINGREQ) {
|
||||
this->buffer[0] = MQTTPINGRESP;
|
||||
this->buffer[1] = 0;
|
||||
_client->write(this->buffer,2);
|
||||
} else if (type == MQTTPINGRESP) {
|
||||
pingOutstanding = false;
|
||||
}
|
||||
} else if (!connected()) {
|
||||
// readPacket has closed the connection
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const char* payload) {
|
||||
return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,false);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
|
||||
return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,retained);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
|
||||
return publish(topic, payload, plength, false);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
|
||||
if (connected()) {
|
||||
if (this->bufferSize < MQTT_MAX_HEADER_SIZE + 2+strnlen(topic, this->bufferSize) + plength) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
length = writeString(topic,this->buffer,length);
|
||||
|
||||
// Add payload
|
||||
uint16_t i;
|
||||
for (i=0;i<plength;i++) {
|
||||
this->buffer[length++] = payload[i];
|
||||
}
|
||||
|
||||
// Write the header
|
||||
uint8_t header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
return write(header,this->buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
|
||||
return publish_P(topic, (const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0, retained);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
|
||||
uint8_t llen = 0;
|
||||
uint8_t digit;
|
||||
unsigned int rc = 0;
|
||||
uint16_t tlen;
|
||||
unsigned int pos = 0;
|
||||
unsigned int i;
|
||||
uint8_t header;
|
||||
unsigned int len;
|
||||
int expectedLength;
|
||||
|
||||
if (!connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tlen = strnlen(topic, this->bufferSize);
|
||||
|
||||
header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
this->buffer[pos++] = header;
|
||||
len = plength + 2 + tlen;
|
||||
do {
|
||||
digit = len & 127; //digit = len %128
|
||||
len >>= 7; //len = len / 128
|
||||
if (len > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
this->buffer[pos++] = digit;
|
||||
llen++;
|
||||
} while(len>0);
|
||||
|
||||
pos = writeString(topic,this->buffer,pos);
|
||||
|
||||
rc += _client->write(this->buffer,pos);
|
||||
|
||||
for (i=0;i<plength;i++) {
|
||||
rc += _client->write((char)pgm_read_byte_near(payload + i));
|
||||
}
|
||||
|
||||
lastOutActivity = millis();
|
||||
|
||||
expectedLength = 1 + llen + 2 + tlen + plength;
|
||||
|
||||
return (rc == expectedLength);
|
||||
}
|
||||
|
||||
boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
|
||||
if (connected()) {
|
||||
// Send the header and variable length field
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
length = writeString(topic,this->buffer,length);
|
||||
uint8_t header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
size_t hlen = buildHeader(header, this->buffer, plength+length-MQTT_MAX_HEADER_SIZE);
|
||||
uint16_t rc = _client->write(this->buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
|
||||
lastOutActivity = millis();
|
||||
return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int PubSubClient::endPublish() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t PubSubClient::write(uint8_t data) {
|
||||
lastOutActivity = millis();
|
||||
return _client->write(data);
|
||||
}
|
||||
|
||||
size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
|
||||
lastOutActivity = millis();
|
||||
return _client->write(buffer,size);
|
||||
}
|
||||
|
||||
size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
|
||||
uint8_t lenBuf[4];
|
||||
uint8_t llen = 0;
|
||||
uint8_t digit;
|
||||
uint8_t pos = 0;
|
||||
uint16_t len = length;
|
||||
do {
|
||||
|
||||
digit = len & 127; //digit = len %128
|
||||
len >>= 7; //len = len / 128
|
||||
if (len > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
lenBuf[pos++] = digit;
|
||||
llen++;
|
||||
} while(len>0);
|
||||
|
||||
buf[4-llen] = header;
|
||||
for (int i=0;i<llen;i++) {
|
||||
buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
|
||||
}
|
||||
return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
|
||||
}
|
||||
|
||||
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
|
||||
uint16_t rc;
|
||||
uint8_t hlen = buildHeader(header, buf, length);
|
||||
|
||||
#ifdef MQTT_MAX_TRANSFER_SIZE
|
||||
uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
|
||||
uint16_t bytesRemaining = length+hlen; //Match the length type
|
||||
uint8_t bytesToWrite;
|
||||
boolean result = true;
|
||||
while((bytesRemaining > 0) && result) {
|
||||
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
|
||||
rc = _client->write(writeBuf,bytesToWrite);
|
||||
result = (rc == bytesToWrite);
|
||||
bytesRemaining -= rc;
|
||||
writeBuf += rc;
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
|
||||
lastOutActivity = millis();
|
||||
return (rc == hlen+length);
|
||||
#endif
|
||||
}
|
||||
|
||||
boolean PubSubClient::subscribe(const char* topic) {
|
||||
return subscribe(topic, 0);
|
||||
}
|
||||
|
||||
boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
|
||||
size_t topicLength = strnlen(topic, this->bufferSize);
|
||||
if (topic == 0) {
|
||||
return false;
|
||||
}
|
||||
if (qos > 1) {
|
||||
return false;
|
||||
}
|
||||
if (this->bufferSize < 9 + topicLength) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
if (connected()) {
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
nextMsgId++;
|
||||
if (nextMsgId == 0) {
|
||||
nextMsgId = 1;
|
||||
}
|
||||
this->buffer[length++] = (nextMsgId >> 8);
|
||||
this->buffer[length++] = (nextMsgId & 0xFF);
|
||||
length = writeString((char*)topic, this->buffer,length);
|
||||
this->buffer[length++] = qos;
|
||||
return write(MQTTSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::unsubscribe(const char* topic) {
|
||||
size_t topicLength = strnlen(topic, this->bufferSize);
|
||||
if (topic == 0) {
|
||||
return false;
|
||||
}
|
||||
if (this->bufferSize < 9 + topicLength) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
if (connected()) {
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
nextMsgId++;
|
||||
if (nextMsgId == 0) {
|
||||
nextMsgId = 1;
|
||||
}
|
||||
this->buffer[length++] = (nextMsgId >> 8);
|
||||
this->buffer[length++] = (nextMsgId & 0xFF);
|
||||
length = writeString(topic, this->buffer,length);
|
||||
return write(MQTTUNSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PubSubClient::disconnect() {
|
||||
this->buffer[0] = MQTTDISCONNECT;
|
||||
this->buffer[1] = 0;
|
||||
_client->write(this->buffer,2);
|
||||
_state = MQTT_DISCONNECTED;
|
||||
_client->flush();
|
||||
_client->stop();
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
}
|
||||
|
||||
uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
|
||||
const char* idp = string;
|
||||
uint16_t i = 0;
|
||||
pos += 2;
|
||||
while (*idp) {
|
||||
buf[pos++] = *idp++;
|
||||
i++;
|
||||
}
|
||||
buf[pos-i-2] = (i >> 8);
|
||||
buf[pos-i-1] = (i & 0xFF);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
boolean PubSubClient::connected() {
|
||||
boolean rc;
|
||||
if (_client == NULL ) {
|
||||
rc = false;
|
||||
} else {
|
||||
rc = (int)_client->connected();
|
||||
if (!rc) {
|
||||
if (this->_state == MQTT_CONNECTED) {
|
||||
this->_state = MQTT_CONNECTION_LOST;
|
||||
_client->flush();
|
||||
_client->stop();
|
||||
}
|
||||
} else {
|
||||
return this->_state == MQTT_CONNECTED;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
|
||||
IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
|
||||
return setServer(addr,port);
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
|
||||
this->ip = ip;
|
||||
this->port = port;
|
||||
this->domain = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
|
||||
this->domain = domain;
|
||||
this->port = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
|
||||
this->callback = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setClient(Client& client){
|
||||
this->_client = &client;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setStream(Stream& stream){
|
||||
this->stream = &stream;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int PubSubClient::state() {
|
||||
return this->_state;
|
||||
}
|
||||
|
||||
boolean PubSubClient::setBufferSize(uint16_t size) {
|
||||
if (size == 0) {
|
||||
// Cannot set it back to 0
|
||||
return false;
|
||||
}
|
||||
if (this->bufferSize == 0) {
|
||||
this->buffer = (uint8_t*)malloc(size);
|
||||
} else {
|
||||
uint8_t* newBuffer = (uint8_t*)realloc(this->buffer, size);
|
||||
if (newBuffer != NULL) {
|
||||
this->buffer = newBuffer;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->bufferSize = size;
|
||||
return (this->buffer != NULL);
|
||||
}
|
||||
|
||||
uint16_t PubSubClient::getBufferSize() {
|
||||
return this->bufferSize;
|
||||
}
|
||||
PubSubClient& PubSubClient::setKeepAlive(uint16_t keepAlive) {
|
||||
this->keepAlive = keepAlive;
|
||||
return *this;
|
||||
}
|
||||
PubSubClient& PubSubClient::setSocketTimeout(uint16_t timeout) {
|
||||
this->socketTimeout = timeout;
|
||||
return *this;
|
||||
}
|
||||
184
P1_gateway_FW/lib/PubSubClient/src/PubSubClient.h
Executable file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
PubSubClient.h - A simple client for MQTT.
|
||||
Nick O'Leary
|
||||
http://knolleary.net
|
||||
*/
|
||||
|
||||
#ifndef PubSubClient_h
|
||||
#define PubSubClient_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "IPAddress.h"
|
||||
#include "Client.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#define MQTT_VERSION_3_1 3
|
||||
#define MQTT_VERSION_3_1_1 4
|
||||
|
||||
// MQTT_VERSION : Pick the version
|
||||
//#define MQTT_VERSION MQTT_VERSION_3_1
|
||||
#ifndef MQTT_VERSION
|
||||
#define MQTT_VERSION MQTT_VERSION_3_1_1
|
||||
#endif
|
||||
|
||||
// MQTT_MAX_PACKET_SIZE : Maximum packet size. Override with setBufferSize().
|
||||
#ifndef MQTT_MAX_PACKET_SIZE
|
||||
#define MQTT_MAX_PACKET_SIZE 256
|
||||
#endif
|
||||
|
||||
// MQTT_KEEPALIVE : keepAlive interval in Seconds. Override with setKeepAlive()
|
||||
#ifndef MQTT_KEEPALIVE
|
||||
#define MQTT_KEEPALIVE 15
|
||||
#endif
|
||||
|
||||
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds. Override with setSocketTimeout()
|
||||
#ifndef MQTT_SOCKET_TIMEOUT
|
||||
#define MQTT_SOCKET_TIMEOUT 15
|
||||
#endif
|
||||
|
||||
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
|
||||
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
|
||||
// pass the entire MQTT packet in each write call.
|
||||
//#define MQTT_MAX_TRANSFER_SIZE 80
|
||||
|
||||
// Possible values for client.state()
|
||||
#define MQTT_CONNECTION_TIMEOUT -4
|
||||
#define MQTT_CONNECTION_LOST -3
|
||||
#define MQTT_CONNECT_FAILED -2
|
||||
#define MQTT_DISCONNECTED -1
|
||||
#define MQTT_CONNECTED 0
|
||||
#define MQTT_CONNECT_BAD_PROTOCOL 1
|
||||
#define MQTT_CONNECT_BAD_CLIENT_ID 2
|
||||
#define MQTT_CONNECT_UNAVAILABLE 3
|
||||
#define MQTT_CONNECT_BAD_CREDENTIALS 4
|
||||
#define MQTT_CONNECT_UNAUTHORIZED 5
|
||||
|
||||
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
|
||||
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
|
||||
#define MQTTPUBLISH 3 << 4 // Publish message
|
||||
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
|
||||
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
|
||||
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
|
||||
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
|
||||
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
|
||||
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
|
||||
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
|
||||
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
|
||||
#define MQTTPINGREQ 12 << 4 // PING Request
|
||||
#define MQTTPINGRESP 13 << 4 // PING Response
|
||||
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
|
||||
#define MQTTReserved 15 << 4 // Reserved
|
||||
|
||||
#define MQTTQOS0 (0 << 1)
|
||||
#define MQTTQOS1 (1 << 1)
|
||||
#define MQTTQOS2 (2 << 1)
|
||||
|
||||
// Maximum size of fixed header and variable length size header
|
||||
#define MQTT_MAX_HEADER_SIZE 5
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
#include <functional>
|
||||
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
||||
#else
|
||||
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
||||
#endif
|
||||
|
||||
#define CHECK_STRING_LENGTH(l,s) if (l+2+strnlen(s, this->bufferSize) > this->bufferSize) {_client->stop();return false;}
|
||||
|
||||
class PubSubClient : public Print {
|
||||
private:
|
||||
Client* _client;
|
||||
uint8_t* buffer;
|
||||
uint16_t bufferSize;
|
||||
uint16_t keepAlive;
|
||||
uint16_t socketTimeout;
|
||||
uint16_t nextMsgId;
|
||||
unsigned long lastOutActivity;
|
||||
unsigned long lastInActivity;
|
||||
bool pingOutstanding;
|
||||
MQTT_CALLBACK_SIGNATURE;
|
||||
uint32_t readPacket(uint8_t*);
|
||||
boolean readByte(uint8_t * result);
|
||||
boolean readByte(uint8_t * result, uint16_t * index);
|
||||
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
|
||||
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
|
||||
// Build up the header ready to send
|
||||
// Returns the size of the header
|
||||
// Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
|
||||
// (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
|
||||
size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
|
||||
IPAddress ip;
|
||||
const char* domain;
|
||||
uint16_t port;
|
||||
Stream* stream;
|
||||
int _state;
|
||||
public:
|
||||
PubSubClient();
|
||||
PubSubClient(Client& client);
|
||||
PubSubClient(IPAddress, uint16_t, Client& client);
|
||||
PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
|
||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
||||
PubSubClient(uint8_t *, uint16_t, Client& client);
|
||||
PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
|
||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
||||
PubSubClient(const char*, uint16_t, Client& client);
|
||||
PubSubClient(const char*, uint16_t, Client& client, Stream&);
|
||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
||||
|
||||
~PubSubClient();
|
||||
|
||||
PubSubClient& setServer(IPAddress ip, uint16_t port);
|
||||
PubSubClient& setServer(uint8_t * ip, uint16_t port);
|
||||
PubSubClient& setServer(const char * domain, uint16_t port);
|
||||
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
|
||||
PubSubClient& setClient(Client& client);
|
||||
PubSubClient& setStream(Stream& stream);
|
||||
PubSubClient& setKeepAlive(uint16_t keepAlive);
|
||||
PubSubClient& setSocketTimeout(uint16_t timeout);
|
||||
|
||||
boolean setBufferSize(uint16_t size);
|
||||
uint16_t getBufferSize();
|
||||
|
||||
boolean connect(const char* id);
|
||||
boolean connect(const char* id, const char* user, const char* pass);
|
||||
boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
|
||||
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
|
||||
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
|
||||
void disconnect();
|
||||
boolean publish(const char* topic, const char* payload);
|
||||
boolean publish(const char* topic, const char* payload, boolean retained);
|
||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
|
||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
|
||||
boolean publish_P(const char* topic, const char* payload, boolean retained);
|
||||
boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
|
||||
// Start to publish a message.
|
||||
// This API:
|
||||
// beginPublish(...)
|
||||
// one or more calls to write(...)
|
||||
// endPublish()
|
||||
// Allows for arbitrarily large payloads to be sent without them having to be copied into
|
||||
// a new buffer and held in memory at one time
|
||||
// Returns 1 if the message was started successfully, 0 if there was an error
|
||||
boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
|
||||
// Finish off this publish message (started with beginPublish)
|
||||
// Returns 1 if the packet was sent successfully, 0 if there was an error
|
||||
int endPublish();
|
||||
// Write a single byte of payload (only to be used with beginPublish/endPublish)
|
||||
virtual size_t write(uint8_t);
|
||||
// Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
|
||||
// Returns the number of bytes written
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
boolean subscribe(const char* topic);
|
||||
boolean subscribe(const char* topic, uint8_t qos);
|
||||
boolean unsubscribe(const char* topic);
|
||||
boolean loop();
|
||||
boolean connected();
|
||||
int state();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
102
P1_gateway_FW/lib/dsmr/crc16.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* CRC compatibility, adapted from the Teensy 3 core at:
|
||||
https://github.com/PaulStoffregen/cores/tree/master/teensy3
|
||||
which was in turn adapted by Paul Stoffregen from the C-only comments here:
|
||||
http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/include/util/crc16.h?revision=933&root=avr-libc&view=markup */
|
||||
|
||||
/* Copyright (c) 2002, 2003, 2004 Marek Michalkiewicz
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#ifndef _UTIL_CRC16_H_
|
||||
#ifdef ARDUINO_ARCH_AVR
|
||||
#include <util/crc16.h>
|
||||
#else
|
||||
#define _UTIL_CRC16_H_
|
||||
#include <stdint.h>
|
||||
|
||||
static inline uint16_t _crc16_update(uint16_t crc, uint8_t data) __attribute__((always_inline, unused));
|
||||
static inline uint16_t _crc16_update(uint16_t crc, uint8_t data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
crc ^= data;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if (crc & 1) {
|
||||
crc = (crc >> 1) ^ 0xA001;
|
||||
} else {
|
||||
crc = (crc >> 1);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static inline uint16_t _crc_xmodem_update(uint16_t crc, uint8_t data) __attribute__((always_inline, unused));
|
||||
static inline uint16_t _crc_xmodem_update(uint16_t crc, uint8_t data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
crc = crc ^ ((uint16_t)data << 8);
|
||||
for (i=0; i<8; i++) {
|
||||
if (crc & 0x8000) {
|
||||
crc = (crc << 1) ^ 0x1021;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static inline uint16_t _crc_ccitt_update (uint16_t crc, uint8_t data) __attribute__((always_inline, unused));
|
||||
static inline uint16_t _crc_ccitt_update (uint16_t crc, uint8_t data)
|
||||
{
|
||||
data ^= (crc & 255);
|
||||
data ^= data << 4;
|
||||
|
||||
return ((((uint16_t)data << 8) | (crc >> 8)) ^ (uint8_t)(data >> 4)
|
||||
^ ((uint16_t)data << 3));
|
||||
}
|
||||
|
||||
static inline uint8_t _crc_ibutton_update(uint8_t crc, uint8_t data) __attribute__((always_inline, unused));
|
||||
static inline uint8_t _crc_ibutton_update(uint8_t crc, uint8_t data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
crc = crc ^ data;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (crc & 0x01) {
|
||||
crc = (crc >> 1) ^ 0x8C;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
262
P1_gateway_FW/lib/dsmr/fields.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* Arduino DSMR parser.
|
||||
*
|
||||
* This software is licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2015 Matthijs Kooijman <matthijs@stdin.nl>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Field parsing functions
|
||||
*/
|
||||
|
||||
#include "fields.h"
|
||||
|
||||
|
||||
using namespace dsmr;
|
||||
using namespace dsmr::fields;
|
||||
|
||||
// Since C++11 it is possible to define the initial values for static
|
||||
// const members in the class declaration, but if their address is
|
||||
// taken, they still need a normal definition somewhere (to allocate
|
||||
// storage).
|
||||
constexpr char units::none[];
|
||||
constexpr char units::kWh[];
|
||||
constexpr char units::Wh[];
|
||||
constexpr char units::kW[];
|
||||
constexpr char units::W[];
|
||||
constexpr char units::V[];
|
||||
constexpr char units::mV[];
|
||||
constexpr char units::A[];
|
||||
constexpr char units::mA[];
|
||||
constexpr char units::m3[];
|
||||
constexpr char units::dm3[];
|
||||
constexpr char units::GJ[];
|
||||
constexpr char units::MJ[];
|
||||
|
||||
constexpr ObisId identification::id;
|
||||
constexpr char identification::name_progmem[];
|
||||
constexpr const __FlashStringHelper *identification::name;
|
||||
|
||||
constexpr ObisId p1_version::id;
|
||||
constexpr char p1_version::name_progmem[];
|
||||
constexpr const __FlashStringHelper *p1_version::name;
|
||||
|
||||
constexpr ObisId timestamp::id;
|
||||
constexpr char timestamp::name_progmem[];
|
||||
constexpr const __FlashStringHelper *timestamp::name;
|
||||
|
||||
constexpr ObisId equipment_id::id;
|
||||
constexpr char equipment_id::name_progmem[];
|
||||
constexpr const __FlashStringHelper *equipment_id::name;
|
||||
|
||||
constexpr ObisId energy_delivered_tariff1::id;
|
||||
constexpr char energy_delivered_tariff1::name_progmem[];
|
||||
constexpr const __FlashStringHelper *energy_delivered_tariff1::name;
|
||||
|
||||
constexpr ObisId energy_delivered_tariff2::id;
|
||||
constexpr char energy_delivered_tariff2::name_progmem[];
|
||||
constexpr const __FlashStringHelper *energy_delivered_tariff2::name;
|
||||
|
||||
constexpr ObisId energy_returned_tariff1::id;
|
||||
constexpr char energy_returned_tariff1::name_progmem[];
|
||||
constexpr const __FlashStringHelper *energy_returned_tariff1::name;
|
||||
|
||||
constexpr ObisId energy_returned_tariff2::id;
|
||||
constexpr char energy_returned_tariff2::name_progmem[];
|
||||
constexpr const __FlashStringHelper *energy_returned_tariff2::name;
|
||||
|
||||
constexpr ObisId electricity_tariff::id;
|
||||
constexpr char electricity_tariff::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_tariff::name;
|
||||
|
||||
constexpr ObisId power_delivered::id;
|
||||
constexpr char power_delivered::name_progmem[];
|
||||
constexpr const __FlashStringHelper *power_delivered::name;
|
||||
|
||||
constexpr ObisId power_returned::id;
|
||||
constexpr char power_returned::name_progmem[];
|
||||
constexpr const __FlashStringHelper *power_returned::name;
|
||||
|
||||
constexpr ObisId electricity_threshold::id;
|
||||
constexpr char electricity_threshold::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_threshold::name;
|
||||
|
||||
constexpr ObisId electricity_switch_position::id;
|
||||
constexpr char electricity_switch_position::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_switch_position::name;
|
||||
|
||||
constexpr ObisId electricity_failures::id;
|
||||
constexpr char electricity_failures::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_failures::name;
|
||||
|
||||
constexpr ObisId electricity_long_failures::id;
|
||||
constexpr char electricity_long_failures::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_long_failures::name;
|
||||
|
||||
constexpr ObisId electricity_failure_log::id;
|
||||
constexpr char electricity_failure_log::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_failure_log::name;
|
||||
|
||||
constexpr ObisId electricity_sags_l1::id;
|
||||
constexpr char electricity_sags_l1::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_sags_l1::name;
|
||||
|
||||
constexpr ObisId electricity_sags_l2::id;
|
||||
constexpr char electricity_sags_l2::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_sags_l2::name;
|
||||
|
||||
constexpr ObisId electricity_sags_l3::id;
|
||||
constexpr char electricity_sags_l3::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_sags_l3::name;
|
||||
|
||||
constexpr ObisId electricity_swells_l1::id;
|
||||
constexpr char electricity_swells_l1::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_swells_l1::name;
|
||||
|
||||
constexpr ObisId electricity_swells_l2::id;
|
||||
constexpr char electricity_swells_l2::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_swells_l2::name;
|
||||
|
||||
constexpr ObisId electricity_swells_l3::id;
|
||||
constexpr char electricity_swells_l3::name_progmem[];
|
||||
constexpr const __FlashStringHelper *electricity_swells_l3::name;
|
||||
|
||||
constexpr ObisId message_short::id;
|
||||
constexpr char message_short::name_progmem[];
|
||||
constexpr const __FlashStringHelper *message_short::name;
|
||||
|
||||
constexpr ObisId message_long::id;
|
||||
constexpr char message_long::name_progmem[];
|
||||
constexpr const __FlashStringHelper *message_long::name;
|
||||
|
||||
constexpr ObisId voltage_l1::id;
|
||||
constexpr char voltage_l1::name_progmem[];
|
||||
constexpr const __FlashStringHelper *voltage_l1::name;
|
||||
|
||||
constexpr ObisId voltage_l2::id;
|
||||
constexpr char voltage_l2::name_progmem[];
|
||||
constexpr const __FlashStringHelper *voltage_l2::name;
|
||||
|
||||
constexpr ObisId voltage_l3::id;
|
||||
constexpr char voltage_l3::name_progmem[];
|
||||
constexpr const __FlashStringHelper *voltage_l3::name;
|
||||
|
||||
constexpr ObisId current_l1::id;
|
||||
constexpr char current_l1::name_progmem[];
|
||||
constexpr const __FlashStringHelper *current_l1::name;
|
||||
|
||||
constexpr ObisId current_l2::id;
|
||||
constexpr char current_l2::name_progmem[];
|
||||
constexpr const __FlashStringHelper *current_l2::name;
|
||||
|
||||
constexpr ObisId current_l3::id;
|
||||
constexpr char current_l3::name_progmem[];
|
||||
constexpr const __FlashStringHelper *current_l3::name;
|
||||
|
||||
constexpr ObisId power_delivered_l1::id;
|
||||
constexpr char power_delivered_l1::name_progmem[];
|
||||
constexpr const __FlashStringHelper *power_delivered_l1::name;
|
||||
|
||||
constexpr ObisId power_delivered_l2::id;
|
||||
constexpr char power_delivered_l2::name_progmem[];
|
||||
constexpr const __FlashStringHelper *power_delivered_l2::name;
|
||||
|
||||
constexpr ObisId power_delivered_l3::id;
|
||||
constexpr char power_delivered_l3::name_progmem[];
|
||||
constexpr const __FlashStringHelper *power_delivered_l3::name;
|
||||
|
||||
constexpr ObisId power_returned_l1::id;
|
||||
constexpr char power_returned_l1::name_progmem[];
|
||||
constexpr const __FlashStringHelper *power_returned_l1::name;
|
||||
|
||||
constexpr ObisId power_returned_l2::id;
|
||||
constexpr char power_returned_l2::name_progmem[];
|
||||
constexpr const __FlashStringHelper *power_returned_l2::name;
|
||||
|
||||
constexpr ObisId power_returned_l3::id;
|
||||
constexpr char power_returned_l3::name_progmem[];
|
||||
constexpr const __FlashStringHelper *power_returned_l3::name;
|
||||
|
||||
constexpr ObisId gas_device_type::id;
|
||||
constexpr char gas_device_type::name_progmem[];
|
||||
constexpr const __FlashStringHelper *gas_device_type::name;
|
||||
|
||||
constexpr ObisId gas_equipment_id::id;
|
||||
constexpr char gas_equipment_id::name_progmem[];
|
||||
constexpr const __FlashStringHelper *gas_equipment_id::name;
|
||||
|
||||
constexpr ObisId gas_valve_position::id;
|
||||
constexpr char gas_valve_position::name_progmem[];
|
||||
constexpr const __FlashStringHelper *gas_valve_position::name;
|
||||
|
||||
constexpr ObisId gas_delivered::id;
|
||||
constexpr char gas_delivered::name_progmem[];
|
||||
constexpr const __FlashStringHelper *gas_delivered::name;
|
||||
|
||||
constexpr ObisId thermal_device_type::id;
|
||||
constexpr char thermal_device_type::name_progmem[];
|
||||
constexpr const __FlashStringHelper *thermal_device_type::name;
|
||||
|
||||
constexpr ObisId thermal_equipment_id::id;
|
||||
constexpr char thermal_equipment_id::name_progmem[];
|
||||
constexpr const __FlashStringHelper *thermal_equipment_id::name;
|
||||
|
||||
constexpr ObisId thermal_valve_position::id;
|
||||
constexpr char thermal_valve_position::name_progmem[];
|
||||
constexpr const __FlashStringHelper *thermal_valve_position::name;
|
||||
|
||||
constexpr ObisId thermal_delivered::id;
|
||||
constexpr char thermal_delivered::name_progmem[];
|
||||
constexpr const __FlashStringHelper *thermal_delivered::name;
|
||||
|
||||
constexpr ObisId water_device_type::id;
|
||||
constexpr char water_device_type::name_progmem[];
|
||||
constexpr const __FlashStringHelper *water_device_type::name;
|
||||
|
||||
constexpr ObisId water_equipment_id::id;
|
||||
constexpr char water_equipment_id::name_progmem[];
|
||||
constexpr const __FlashStringHelper *water_equipment_id::name;
|
||||
|
||||
constexpr ObisId water_valve_position::id;
|
||||
constexpr char water_valve_position::name_progmem[];
|
||||
constexpr const __FlashStringHelper *water_valve_position::name;
|
||||
|
||||
constexpr ObisId water_delivered::id;
|
||||
constexpr char water_delivered::name_progmem[];
|
||||
constexpr const __FlashStringHelper *water_delivered::name;
|
||||
|
||||
constexpr ObisId slave_device_type::id;
|
||||
constexpr char slave_device_type::name_progmem[];
|
||||
constexpr const __FlashStringHelper *slave_device_type::name;
|
||||
|
||||
constexpr ObisId slave_equipment_id::id;
|
||||
constexpr char slave_equipment_id::name_progmem[];
|
||||
constexpr const __FlashStringHelper *slave_equipment_id::name;
|
||||
|
||||
constexpr ObisId slave_valve_position::id;
|
||||
constexpr char slave_valve_position::name_progmem[];
|
||||
constexpr const __FlashStringHelper *slave_valve_position::name;
|
||||
|
||||
constexpr ObisId slave_delivered::id;
|
||||
constexpr char slave_delivered::name_progmem[];
|
||||
constexpr const __FlashStringHelper *slave_delivered::name;
|
||||
|
||||
352
P1_gateway_FW/lib/dsmr/fields.h
Normal file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* Arduino DSMR parser.
|
||||
*
|
||||
* This software is licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2015 Matthijs Kooijman <matthijs@stdin.nl>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Field parsing functions
|
||||
*/
|
||||
|
||||
#ifndef DSMR_INCLUDE_FIELDS_H
|
||||
#define DSMR_INCLUDE_FIELDS_H
|
||||
|
||||
#include "util.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace dsmr {
|
||||
|
||||
/**
|
||||
* Superclass for data items in a P1 message.
|
||||
*/
|
||||
template <typename T>
|
||||
struct ParsedField {
|
||||
template <typename F>
|
||||
void apply(F& f) {
|
||||
f.apply(*static_cast<T*>(this));
|
||||
}
|
||||
// By defaults, fields have no unit
|
||||
static const char *unit() { return ""; }
|
||||
};
|
||||
|
||||
template <typename T, size_t minlen, size_t maxlen>
|
||||
struct StringField : ParsedField<T> {
|
||||
ParseResult<void> parse(const char *str, const char *end) {
|
||||
ParseResult<String> res = StringParser::parse_string(minlen, maxlen, str, end);
|
||||
if (!res.err)
|
||||
static_cast<T*>(this)->val() = res.result;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
// A timestamp is essentially a string using YYMMDDhhmmssX format (where
|
||||
// X is W or S for wintertime or summertime). Parsing this into a proper
|
||||
// (UNIX) timestamp is hard to do generically. Parsing it into a
|
||||
// single integer needs > 4 bytes top fit and isn't very useful (you
|
||||
// cannot really do any calculation with those values). So we just parse
|
||||
// into a string for now.
|
||||
template <typename T>
|
||||
struct TimestampField : StringField<T, 13, 13> { };
|
||||
|
||||
// Value that is parsed as a three-decimal float, but stored as an
|
||||
// integer (by multiplying by 1000). Supports val() (or implicit cast to
|
||||
// float) to get the original value, and int_val() to get the more
|
||||
// efficient integer value. The unit() and int_unit() methods on
|
||||
// FixedField return the corresponding units for these values.
|
||||
struct FixedValue {
|
||||
operator float() { return val();}
|
||||
float val() { return _value / 1000.0;}
|
||||
uint32_t int_val() { return _value; }
|
||||
|
||||
uint32_t _value;
|
||||
};
|
||||
|
||||
// Floating point numbers in the message never have more than 3 decimal
|
||||
// digits. To prevent inefficient floating point operations, we store
|
||||
// them as a fixed-point number: an integer that stores the value in
|
||||
// thousands. For example, a value of 1.234 kWh is stored as 1234. This
|
||||
// effectively means that the integer value is het value in Wh. To allow
|
||||
// automatic printing of these values, both the original unit and the
|
||||
// integer unit is passed as a template argument.
|
||||
template <typename T, const char *_unit, const char *_int_unit>
|
||||
struct FixedField : ParsedField<T> {
|
||||
ParseResult<void> parse(const char *str, const char *end) {
|
||||
ParseResult<uint32_t> res = NumParser::parse(3, _unit, str, end);
|
||||
if (!res.err)
|
||||
static_cast<T*>(this)->val()._value = res.result;
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *unit() { return _unit; }
|
||||
static const char *int_unit() { return _int_unit; }
|
||||
};
|
||||
|
||||
struct TimestampedFixedValue : public FixedValue {
|
||||
String timestamp;
|
||||
};
|
||||
|
||||
// Some numerical values are prefixed with a timestamp. This is simply
|
||||
// both of them concatenated, e.g. 0-1:24.2.1(150117180000W)(00473.789*m3)
|
||||
template <typename T, const char *_unit, const char *_int_unit>
|
||||
struct TimestampedFixedField : public FixedField<T, _unit, _int_unit> {
|
||||
ParseResult<void> parse(const char *str, const char *end) {
|
||||
// First, parse timestamp
|
||||
ParseResult<String> res = StringParser::parse_string(13, 13, str, end);
|
||||
if (res.err)
|
||||
return res;
|
||||
|
||||
static_cast<T*>(this)->val().timestamp = res.result;
|
||||
|
||||
// Which is immediately followed by the numerical value
|
||||
return FixedField<T, _unit, _int_unit>::parse(res.next, end);
|
||||
}
|
||||
};
|
||||
|
||||
// A integer number is just represented as an integer.
|
||||
template <typename T, const char *_unit>
|
||||
struct IntField : ParsedField<T> {
|
||||
ParseResult<void> parse(const char *str, const char *end) {
|
||||
ParseResult<uint32_t> res = NumParser::parse(0, _unit, str, end);
|
||||
if (!res.err)
|
||||
static_cast<T*>(this)->val() = res.result;
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *unit() { return _unit; }
|
||||
};
|
||||
|
||||
// A RawField is not parsed, the entire value (including any
|
||||
// parenthesis around it) is returned as a string.
|
||||
template <typename T>
|
||||
struct RawField : ParsedField<T> {
|
||||
ParseResult<void> parse(const char *str, const char *end) {
|
||||
// Just copy the string verbatim value without any parsing
|
||||
concat_hack(static_cast<T*>(this)->val(), str, end - str);
|
||||
return ParseResult<void>().until(end);
|
||||
}
|
||||
};
|
||||
|
||||
namespace fields {
|
||||
|
||||
struct units {
|
||||
// These variables are inside a struct, since that allows us to make
|
||||
// them constexpr and define their values here, but define the storage
|
||||
// in a cpp file. Global const(expr) variables have implicitly
|
||||
// internal linkage, meaning each cpp file that includes us will have
|
||||
// its own copy of the variable. Since we take the address of these
|
||||
// variables (passing it as a template argument), this would cause a
|
||||
// compiler warning. By putting these in a struct, this is prevented.
|
||||
static constexpr char none[] = "";
|
||||
static constexpr char kWh[] = "kWh";
|
||||
static constexpr char Wh[] = "Wh";
|
||||
static constexpr char kW[] = "kW";
|
||||
static constexpr char W[] = "W";
|
||||
static constexpr char V[] = "V";
|
||||
static constexpr char mV[] = "mV";
|
||||
static constexpr char A[] = "A";
|
||||
static constexpr char mA[] = "mA";
|
||||
static constexpr char m3[] = "m3";
|
||||
static constexpr char dm3[] = "dm3";
|
||||
static constexpr char GJ[] = "GJ";
|
||||
static constexpr char MJ[] = "MJ";
|
||||
};
|
||||
|
||||
const uint8_t GAS_MBUS_ID = 1;
|
||||
const uint8_t WATER_MBUS_ID = 2;
|
||||
const uint8_t THERMAL_MBUS_ID = 3;
|
||||
const uint8_t SLAVE_MBUS_ID = 4;
|
||||
|
||||
#define DEFINE_FIELD(fieldname, value_t, obis, field_t, field_args...) \
|
||||
struct fieldname : field_t<fieldname, ##field_args> { \
|
||||
value_t fieldname; \
|
||||
bool fieldname ## _present = false; \
|
||||
static constexpr ObisId id = obis; \
|
||||
static constexpr char name_progmem[] DSMR_PROGMEM = #fieldname; \
|
||||
static constexpr const __FlashStringHelper *name = reinterpret_cast<const __FlashStringHelper*>(&name_progmem); \
|
||||
value_t& val() { return fieldname; } \
|
||||
bool& present() { return fieldname ## _present; } \
|
||||
}
|
||||
|
||||
/* Meter identification. This is not a normal field, but a
|
||||
* specially-formatted first line of the message */
|
||||
DEFINE_FIELD(identification, String, ObisId(255, 255, 255, 255, 255, 255), RawField);
|
||||
|
||||
/* Version information for P1 output */
|
||||
DEFINE_FIELD(p1_version, String, ObisId(1, 3, 0, 2, 8), StringField, 2, 2);
|
||||
|
||||
/* Date-time stamp of the P1 message */
|
||||
DEFINE_FIELD(timestamp, String, ObisId(0, 0, 1, 0, 0), TimestampField);
|
||||
|
||||
/* Equipment identifier */
|
||||
DEFINE_FIELD(equipment_id, String, ObisId(0, 0, 96, 1, 1), StringField, 0, 96);
|
||||
|
||||
/* Meter Reading electricity delivered to client (Tariff 1) in 0,001 kWh */
|
||||
DEFINE_FIELD(energy_delivered_tariff1, FixedValue, ObisId(1, 0, 1, 8, 1), FixedField, units::kWh, units::Wh);
|
||||
/* Meter Reading electricity delivered to client (Tariff 2) in 0,001 kWh */
|
||||
DEFINE_FIELD(energy_delivered_tariff2, FixedValue, ObisId(1, 0, 1, 8, 2), FixedField, units::kWh, units::Wh);
|
||||
/* Meter Reading electricity delivered by client (Tariff 1) in 0,001 kWh */
|
||||
DEFINE_FIELD(energy_returned_tariff1, FixedValue, ObisId(1, 0, 2, 8, 1), FixedField, units::kWh, units::Wh);
|
||||
/* Meter Reading electricity delivered by client (Tariff 2) in 0,001 kWh */
|
||||
DEFINE_FIELD(energy_returned_tariff2, FixedValue, ObisId(1, 0, 2, 8, 2), FixedField, units::kWh, units::Wh);
|
||||
|
||||
/* Tariff indicator electricity. The tariff indicator can also be used
|
||||
* to switch tariff dependent loads e.g boilers. This is the
|
||||
* responsibility of the P1 user */
|
||||
DEFINE_FIELD(electricity_tariff, String, ObisId(0, 0, 96, 14, 0), StringField, 4, 4);
|
||||
|
||||
/* Actual electricity power delivered (+P) in 1 Watt resolution */
|
||||
DEFINE_FIELD(power_delivered, FixedValue, ObisId(1, 0, 1, 7, 0), FixedField, units::kW, units::W);
|
||||
/* Actual electricity power received (-P) in 1 Watt resolution */
|
||||
DEFINE_FIELD(power_returned, FixedValue, ObisId(1, 0, 2, 7, 0), FixedField, units::kW, units::W);
|
||||
|
||||
/* The actual threshold Electricity in kW. Removed in 4.0.7 / 4.2.2 / 5.0 */
|
||||
DEFINE_FIELD(electricity_threshold, FixedValue, ObisId(0, 0, 17, 0, 0), FixedField, units::kW, units::W);
|
||||
|
||||
/* Switch position Electricity (in/out/enabled). Removed in 4.0.7 / 4.2.2 / 5.0 */
|
||||
DEFINE_FIELD(electricity_switch_position, uint8_t, ObisId(0, 0, 96, 3, 10), IntField, units::none);
|
||||
|
||||
/* Number of power failures in any phase */
|
||||
DEFINE_FIELD(electricity_failures, uint32_t, ObisId(0, 0, 96, 7, 21), IntField, units::none);
|
||||
/* Number of long power failures in any phase */
|
||||
DEFINE_FIELD(electricity_long_failures, uint32_t, ObisId(0, 0, 96, 7, 9), IntField, units::none);
|
||||
|
||||
/* Power Failure Event Log (long power failures) */
|
||||
DEFINE_FIELD(electricity_failure_log, String, ObisId(1, 0, 99, 97, 0), RawField);
|
||||
|
||||
/* Number of voltage sags in phase L1 */
|
||||
DEFINE_FIELD(electricity_sags_l1, uint32_t, ObisId(1, 0, 32, 32, 0), IntField, units::none);
|
||||
/* Number of voltage sags in phase L2 (polyphase meters only) */
|
||||
DEFINE_FIELD(electricity_sags_l2, uint32_t, ObisId(1, 0, 52, 32, 0), IntField, units::none);
|
||||
/* Number of voltage sags in phase L3 (polyphase meters only) */
|
||||
DEFINE_FIELD(electricity_sags_l3, uint32_t, ObisId(1, 0, 72, 32, 0), IntField, units::none);
|
||||
|
||||
/* Number of voltage swells in phase L1 */
|
||||
DEFINE_FIELD(electricity_swells_l1, uint32_t, ObisId(1, 0, 32, 36, 0), IntField, units::none);
|
||||
/* Number of voltage swells in phase L2 (polyphase meters only) */
|
||||
DEFINE_FIELD(electricity_swells_l2, uint32_t, ObisId(1, 0, 52, 36, 0), IntField, units::none);
|
||||
/* Number of voltage swells in phase L3 (polyphase meters only) */
|
||||
DEFINE_FIELD(electricity_swells_l3, uint32_t, ObisId(1, 0, 72, 36, 0), IntField, units::none);
|
||||
|
||||
/* Text message codes: numeric 8 digits (Note: Missing from 5.0 spec)
|
||||
* */
|
||||
DEFINE_FIELD(message_short, String, ObisId(0, 0, 96, 13, 1), StringField, 0, 16);
|
||||
/* Text message max 2048 characters (Note: Spec says 1024 in comment and
|
||||
* 2048 in format spec, so we stick to 2048). */
|
||||
DEFINE_FIELD(message_long, String, ObisId(0, 0, 96, 13, 0), StringField, 0, 2048);
|
||||
|
||||
/* Instantaneous voltage L1 in 0.1V resolution (Note: Spec says V
|
||||
* resolution in comment, but 0.1V resolution in format spec. Added in
|
||||
* 5.0) */
|
||||
DEFINE_FIELD(voltage_l1, FixedValue, ObisId(1, 0, 32, 7, 0), FixedField, units::V, units::mV);
|
||||
/* Instantaneous voltage L2 in 0.1V resolution (Note: Spec says V
|
||||
* resolution in comment, but 0.1V resolution in format spec. Added in
|
||||
* 5.0) */
|
||||
DEFINE_FIELD(voltage_l2, FixedValue, ObisId(1, 0, 52, 7, 0), FixedField, units::V, units::mV);
|
||||
/* Instantaneous voltage L3 in 0.1V resolution (Note: Spec says V
|
||||
* resolution in comment, but 0.1V resolution in format spec. Added in
|
||||
* 5.0) */
|
||||
DEFINE_FIELD(voltage_l3, FixedValue, ObisId(1, 0, 72, 7, 0), FixedField, units::V, units::mV);
|
||||
|
||||
/* Instantaneous current L1 in A resolution */
|
||||
DEFINE_FIELD(current_l1, uint16_t, ObisId(1, 0, 31, 7, 0), IntField, units::A);
|
||||
/* Instantaneous current L2 in A resolution */
|
||||
DEFINE_FIELD(current_l2, uint16_t, ObisId(1, 0, 51, 7, 0), IntField, units::A);
|
||||
/* Instantaneous current L3 in A resolution */
|
||||
DEFINE_FIELD(current_l3, uint16_t, ObisId(1, 0, 71, 7, 0), IntField, units::A);
|
||||
|
||||
/* Instantaneous active power L1 (+P) in W resolution */
|
||||
DEFINE_FIELD(power_delivered_l1, FixedValue, ObisId(1, 0, 21, 7, 0), FixedField, units::kW, units::W);
|
||||
/* Instantaneous active power L2 (+P) in W resolution */
|
||||
DEFINE_FIELD(power_delivered_l2, FixedValue, ObisId(1, 0, 41, 7, 0), FixedField, units::kW, units::W);
|
||||
/* Instantaneous active power L3 (+P) in W resolution */
|
||||
DEFINE_FIELD(power_delivered_l3, FixedValue, ObisId(1, 0, 61, 7, 0), FixedField, units::kW, units::W);
|
||||
|
||||
/* Instantaneous active power L1 (-P) in W resolution */
|
||||
DEFINE_FIELD(power_returned_l1, FixedValue, ObisId(1, 0, 22, 7, 0), FixedField, units::kW, units::W);
|
||||
/* Instantaneous active power L2 (-P) in W resolution */
|
||||
DEFINE_FIELD(power_returned_l2, FixedValue, ObisId(1, 0, 42, 7, 0), FixedField, units::kW, units::W);
|
||||
/* Instantaneous active power L3 (-P) in W resolution */
|
||||
DEFINE_FIELD(power_returned_l3, FixedValue, ObisId(1, 0, 62, 7, 0), FixedField, units::kW, units::W);
|
||||
|
||||
|
||||
/* Device-Type */
|
||||
DEFINE_FIELD(gas_device_type, uint16_t, ObisId(0, GAS_MBUS_ID, 24, 1, 0), IntField, units::none);
|
||||
|
||||
/* Equipment identifier (Gas) */
|
||||
DEFINE_FIELD(gas_equipment_id, String, ObisId(0, GAS_MBUS_ID, 96, 1, 0), StringField, 0, 96);
|
||||
|
||||
/* Valve position Gas (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */
|
||||
DEFINE_FIELD(gas_valve_position, uint8_t, ObisId(0, GAS_MBUS_ID, 24, 4, 0), IntField, units::none);
|
||||
|
||||
/* Last 5-minute value (temperature converted), gas delivered to client
|
||||
* in m3, including decimal values and capture time (Note: 4.x spec has
|
||||
* "hourly value") */
|
||||
DEFINE_FIELD(gas_delivered, TimestampedFixedValue, ObisId(0, GAS_MBUS_ID, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3);
|
||||
|
||||
|
||||
/* Device-Type */
|
||||
DEFINE_FIELD(thermal_device_type, uint16_t, ObisId(0, THERMAL_MBUS_ID, 24, 1, 0), IntField, units::none);
|
||||
|
||||
/* Equipment identifier (Thermal: heat or cold) */
|
||||
DEFINE_FIELD(thermal_equipment_id, String, ObisId(0, THERMAL_MBUS_ID, 96, 1, 0), StringField, 0, 96);
|
||||
|
||||
/* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */
|
||||
DEFINE_FIELD(thermal_valve_position, uint8_t, ObisId(0, THERMAL_MBUS_ID, 24, 4, 0), IntField, units::none);
|
||||
|
||||
/* Last 5-minute Meter reading Heat or Cold in 0,01 GJ and capture time
|
||||
* (Note: 4.x spec has "hourly meter reading") */
|
||||
DEFINE_FIELD(thermal_delivered, TimestampedFixedValue, ObisId(0, THERMAL_MBUS_ID, 24, 2, 1), TimestampedFixedField, units::GJ, units::MJ);
|
||||
|
||||
|
||||
/* Device-Type */
|
||||
DEFINE_FIELD(water_device_type, uint16_t, ObisId(0, WATER_MBUS_ID, 24, 1, 0), IntField, units::none);
|
||||
|
||||
/* Equipment identifier (Thermal: heat or cold) */
|
||||
DEFINE_FIELD(water_equipment_id, String, ObisId(0, WATER_MBUS_ID, 96, 1, 0), StringField, 0, 96);
|
||||
|
||||
/* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */
|
||||
DEFINE_FIELD(water_valve_position, uint8_t, ObisId(0, WATER_MBUS_ID, 24, 4, 0), IntField, units::none);
|
||||
|
||||
/* Last 5-minute Meter reading in 0,001 m3 and capture time
|
||||
* (Note: 4.x spec has "hourly meter reading") */
|
||||
DEFINE_FIELD(water_delivered, TimestampedFixedValue, ObisId(0, WATER_MBUS_ID, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3);
|
||||
|
||||
|
||||
/* Device-Type */
|
||||
DEFINE_FIELD(slave_device_type, uint16_t, ObisId(0, SLAVE_MBUS_ID, 24, 1, 0), IntField, units::none);
|
||||
|
||||
/* Equipment identifier (Thermal: heat or cold) */
|
||||
DEFINE_FIELD(slave_equipment_id, String, ObisId(0, SLAVE_MBUS_ID, 96, 1, 0), StringField, 0, 96);
|
||||
|
||||
/* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */
|
||||
DEFINE_FIELD(slave_valve_position, uint8_t, ObisId(0, SLAVE_MBUS_ID, 24, 4, 0), IntField, units::none);
|
||||
|
||||
/* Last 5-minute Meter reading Heat or Cold and capture time (e.g. slave
|
||||
* E meter) (Note: 4.x spec has "hourly meter reading") */
|
||||
DEFINE_FIELD(slave_delivered, TimestampedFixedValue, ObisId(0, SLAVE_MBUS_ID, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3);
|
||||
|
||||
} // namespace fields
|
||||
|
||||
} // namespace dsmr
|
||||
|
||||
#endif // DSMR_INCLUDE_FIELDS_H
|
||||
425
P1_gateway_FW/lib/dsmr/parser.h
Normal file
@@ -0,0 +1,425 @@
|
||||
/**
|
||||
* Arduino DSMR parser.
|
||||
*
|
||||
* This software is licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2015 Matthijs Kooijman <matthijs@stdin.nl>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Message parsing core
|
||||
*/
|
||||
|
||||
#ifndef DSMR_INCLUDE_PARSER_H
|
||||
#define DSMR_INCLUDE_PARSER_H
|
||||
|
||||
#include "crc16.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace dsmr {
|
||||
|
||||
/**
|
||||
* ParsedData is a template for the result of parsing a Dsmr P1 message.
|
||||
* You pass the fields you want to add to it as template arguments.
|
||||
*
|
||||
* This template will then generate a class that extends all the fields
|
||||
* passed (the fields really are classes themselves). Since each field
|
||||
* class has a single member variable, with the same name as the field
|
||||
* class, all of these fields will be available on the generated class.
|
||||
*
|
||||
* In other words, if I have:
|
||||
*
|
||||
* using MyData = ParsedData<
|
||||
* identification,
|
||||
* equipment_id
|
||||
* >;
|
||||
*
|
||||
* MyData data;
|
||||
*
|
||||
* then I can refer to the fields like data.identification and
|
||||
* data.equipment_id normally.
|
||||
*
|
||||
* Furthermore, this class offers some helper methods that can be used
|
||||
* to loop over all the fields inside it.
|
||||
*/
|
||||
template<typename... Ts>
|
||||
struct ParsedData;
|
||||
|
||||
/**
|
||||
* Base case: No fields present.
|
||||
*/
|
||||
template<>
|
||||
struct ParsedData<> {
|
||||
ParseResult<void> __attribute__((__always_inline__)) parse_line_inlined(const ObisId& /* id */, const char *str, const char * /* end */) {
|
||||
// Parsing succeeded, but found no matching handler (so return
|
||||
// set the next pointer to show nothing was parsed).
|
||||
return ParseResult<void>().until(str);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void __attribute__((__always_inline__)) applyEach_inlined(F&& /* f */) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
bool all_present_inlined() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Do not use F() for multiply-used strings (including strings used from
|
||||
// multiple template instantiations), that would result in multiple
|
||||
// instances of the string in the binary
|
||||
static constexpr char DUPLICATE_FIELD[] DSMR_PROGMEM = "Duplicate field";
|
||||
|
||||
/**
|
||||
* General case: At least one typename is passed.
|
||||
*/
|
||||
template<typename T, typename... Ts>
|
||||
struct ParsedData<T, Ts...> : public T, ParsedData<Ts...> {
|
||||
/**
|
||||
* This method is used by the parser to parse a single line. The
|
||||
* OBIS id of the line is passed, and this method recursively finds a
|
||||
* field with a matching id. If any, it calls it's parse method, which
|
||||
* parses the value and stores it in the field.
|
||||
*/
|
||||
ParseResult<void> parse_line(const ObisId& id, const char *str, const char *end) {
|
||||
return parse_line_inlined(id, str, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* always_inline version of parse_line. This is a separate method, to
|
||||
* allow recursively inlining all calls, but still have a non-inlined
|
||||
* top-level parse_line method.
|
||||
*/
|
||||
ParseResult<void> __attribute__((__always_inline__)) parse_line_inlined(const ObisId& id, const char *str, const char *end) {
|
||||
if (id == T::id) {
|
||||
if (T::present())
|
||||
return ParseResult<void>().fail((const __FlashStringHelper*)DUPLICATE_FIELD, str);
|
||||
T::present() = true;
|
||||
return T::parse(str, end);
|
||||
}
|
||||
return ParsedData<Ts...>::parse_line_inlined(id, str, end);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void applyEach(F&& f) {
|
||||
applyEach_inlined(f);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void __attribute__((__always_inline__)) applyEach_inlined(F&& f) {
|
||||
T::apply(f);
|
||||
return ParsedData<Ts...>::applyEach_inlined(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when all defined fields are present.
|
||||
*/
|
||||
bool all_present() {
|
||||
return all_present_inlined();
|
||||
}
|
||||
|
||||
bool all_present_inlined() {
|
||||
return T::present() && ParsedData<Ts...>::all_present_inlined();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct StringParser {
|
||||
static ParseResult<String> parse_string(size_t min, size_t max, const char *str, const char *end) {
|
||||
ParseResult<String> res;
|
||||
if (str >= end || *str != '(')
|
||||
return res.fail(F("Missing ("), str);
|
||||
|
||||
const char *str_start = str + 1; // Skip (
|
||||
const char *str_end = str_start;
|
||||
|
||||
while(str_end < end && *str_end != ')')
|
||||
++str_end;
|
||||
|
||||
if (str_end == end)
|
||||
return res.fail(F("Missing )"), str_end);
|
||||
|
||||
size_t len = str_end - str_start;
|
||||
if (len < min || len > max)
|
||||
return res.fail(F("Invalid string length"), str_start);
|
||||
|
||||
concat_hack(res.result, str_start, len);
|
||||
|
||||
return res.until(str_end + 1); // Skip )
|
||||
}
|
||||
};
|
||||
|
||||
// Do not use F() for multiply-used strings (including strings used from
|
||||
// multiple template instantiations), that would result in multiple
|
||||
// instances of the string in the binary
|
||||
static constexpr char INVALID_NUMBER[] DSMR_PROGMEM = "Invalid number";
|
||||
static constexpr char INVALID_UNIT[] DSMR_PROGMEM = "Invalid unit";
|
||||
|
||||
struct NumParser {
|
||||
static ParseResult<uint32_t> parse(size_t max_decimals, const char* unit, const char *str, const char *end) {
|
||||
ParseResult<uint32_t> res;
|
||||
if (str >= end || *str != '(')
|
||||
return res.fail(F("Missing ("), str);
|
||||
|
||||
const char *num_start = str + 1; // Skip (
|
||||
const char *num_end = num_start;
|
||||
|
||||
uint32_t value = 0;
|
||||
|
||||
// Parse integer part
|
||||
while(num_end < end && !strchr("*.)", *num_end)) {
|
||||
if (*num_end < '0' || *num_end > '9')
|
||||
return res.fail((const __FlashStringHelper*)INVALID_NUMBER, num_end);
|
||||
value *= 10;
|
||||
value += *num_end - '0';
|
||||
++num_end;
|
||||
}
|
||||
|
||||
// Parse decimal part, if any
|
||||
if (max_decimals && num_end < end && *num_end == '.') {
|
||||
++num_end;
|
||||
|
||||
while(num_end < end && !strchr("*)", *num_end) && max_decimals--) {
|
||||
if (*num_end < '0' || *num_end > '9')
|
||||
return res.fail((const __FlashStringHelper*)INVALID_NUMBER, num_end);
|
||||
value *= 10;
|
||||
value += *num_end - '0';
|
||||
++num_end;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in missing decimals with zeroes
|
||||
while(max_decimals--)
|
||||
value *= 10;
|
||||
|
||||
if (unit && *unit) {
|
||||
if (num_end >= end || *num_end != '*')
|
||||
return res.fail(F("Missing unit"), num_end);
|
||||
const char *unit_start = ++num_end; // skip *
|
||||
while(num_end < end && *num_end != ')' && *unit) {
|
||||
if (*num_end++ != *unit++)
|
||||
return res.fail((const __FlashStringHelper*)INVALID_UNIT, unit_start);
|
||||
}
|
||||
if (*unit)
|
||||
return res.fail((const __FlashStringHelper*)INVALID_UNIT, unit_start);
|
||||
}
|
||||
|
||||
if (num_end >= end || *num_end != ')')
|
||||
return res.fail(F("Extra data"), num_end);
|
||||
|
||||
return res.succeed(value).until(num_end + 1); // Skip )
|
||||
}
|
||||
};
|
||||
|
||||
struct ObisIdParser {
|
||||
static ParseResult<ObisId> parse(const char *str, const char *end) {
|
||||
// Parse a Obis ID of the form 1-2:3.4.5.6
|
||||
// Stops parsing on the first unrecognized character. Any unparsed
|
||||
// parts are set to 255.
|
||||
ParseResult<ObisId> res;
|
||||
ObisId& id = res.result;
|
||||
res.next = str;
|
||||
uint8_t part = 0;
|
||||
while (res.next < end) {
|
||||
char c = *res.next;
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
uint8_t digit = c - '0';
|
||||
if (id.v[part] > 25 || (id.v[part] == 25 && digit > 5))
|
||||
return res.fail(F("Obis ID has number over 255"), res.next);
|
||||
id.v[part] = id.v[part] * 10 + digit;
|
||||
} else if (part == 0 && c == '-') {
|
||||
part++;
|
||||
} else if (part == 1 && c == ':') {
|
||||
part++;
|
||||
} else if (part > 1 && part < 5 && c == '.') {
|
||||
part++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
++res.next;
|
||||
}
|
||||
|
||||
if (res.next == str)
|
||||
return res.fail(F("OBIS id Empty"), str);
|
||||
|
||||
for (++part; part < 6; ++part)
|
||||
id.v[part] = 255;
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
struct CrcParser {
|
||||
static const size_t CRC_LEN = 4;
|
||||
|
||||
// Parse a crc value. str must point to the first of the four hex
|
||||
// bytes in the CRC.
|
||||
static ParseResult<uint16_t> parse(const char *str, const char *end) {
|
||||
ParseResult<uint16_t> res;
|
||||
// This should never happen with the code in this library, but
|
||||
// check anyway
|
||||
if (str + CRC_LEN > end)
|
||||
return res.fail(F("No checksum found"), str);
|
||||
|
||||
// A bit of a messy way to parse the checksum, but all
|
||||
// integer-parse functions assume nul-termination
|
||||
char buf[CRC_LEN + 1];
|
||||
memcpy(buf, str, CRC_LEN);
|
||||
buf[CRC_LEN] = '\0';
|
||||
char *endp;
|
||||
uint16_t check = strtoul(buf, &endp, 16);
|
||||
|
||||
// See if all four bytes formed a valid number
|
||||
if (endp != buf + CRC_LEN)
|
||||
return res.fail(F("Incomplete or malformed checksum"), str);
|
||||
|
||||
res.next = str + CRC_LEN;
|
||||
return res.succeed(check);
|
||||
}
|
||||
};
|
||||
|
||||
struct P1Parser {
|
||||
/**
|
||||
* Parse a complete P1 telegram. The string passed should start
|
||||
* with '/' and run up to and including the ! and the following
|
||||
* four byte checksum. It's ok if the string is longer, the .next
|
||||
* pointer in the result will indicate the next unprocessed byte.
|
||||
*/
|
||||
template <typename... Ts>
|
||||
static ParseResult<void> parse(ParsedData<Ts...> *data, const char *str, size_t n, bool unknown_error = false) {
|
||||
ParseResult<void> res;
|
||||
if (!n || str[0] != '/')
|
||||
return res.fail(F("Data should start with /"), str);
|
||||
|
||||
// Skip /
|
||||
const char *data_start = str + 1;
|
||||
|
||||
// Look for ! that terminates the data
|
||||
const char *data_end = data_start;
|
||||
uint16_t crc = _crc16_update(0, *str); // Include the / in CRC
|
||||
while (data_end < str + n && *data_end != '!') {
|
||||
crc = _crc16_update(crc, *data_end);
|
||||
++data_end;
|
||||
}
|
||||
|
||||
if (data_end >= str + n)
|
||||
return res.fail(F("No checksum found"), data_end);
|
||||
|
||||
crc = _crc16_update(crc, *data_end); // Include the ! in CRC
|
||||
|
||||
ParseResult<uint16_t> check_res = CrcParser::parse(data_end + 1, str + n);
|
||||
if (check_res.err)
|
||||
return check_res;
|
||||
|
||||
// Check CRC
|
||||
if (check_res.result != crc)
|
||||
return res.fail(F("Checksum mismatch"), data_end + 1);
|
||||
|
||||
res = parse_data(data, data_start, data_end, unknown_error);
|
||||
res.next = check_res.next;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the data part of a message. Str should point to the first
|
||||
* character after the leading /, end should point to the ! before the
|
||||
* checksum. Does not verify the checksum.
|
||||
*/
|
||||
template <typename... Ts>
|
||||
static ParseResult<void> parse_data(ParsedData<Ts...> *data, const char *str, const char *end, bool unknown_error = false) {
|
||||
ParseResult<void> res;
|
||||
// Split into lines and parse those
|
||||
const char *line_end = str, *line_start = str;
|
||||
|
||||
// Parse ID line
|
||||
while (line_end < end) {
|
||||
if (*line_end == '\r' || *line_end == '\n') {
|
||||
// The first identification line looks like:
|
||||
// XXX5<id string>
|
||||
// The DSMR spec is vague on details, but in 62056-21, the X's
|
||||
// are a three-leter (registerd) manufacturer ID, the id
|
||||
// string is up to 16 chars of arbitrary characters and the
|
||||
// '5' is a baud rate indication. 5 apparently means 9600,
|
||||
// which DSMR 3.x and below used. It seems that DSMR 2.x
|
||||
// passed '3' here (which is mandatory for "mode D"
|
||||
// communication according to 62956-21), so we also allow
|
||||
// that.
|
||||
if (line_start + 3 >= line_end || (line_start[3] != '5' && line_start[3] != '3'))
|
||||
return res.fail(F("Invalid identification string"), line_start);
|
||||
// Offer it for processing using the all-ones Obis ID, which
|
||||
// is not otherwise valid.
|
||||
ParseResult<void> tmp = data->parse_line(ObisId(255, 255, 255, 255, 255, 255), line_start, line_end);
|
||||
if (tmp.err)
|
||||
return tmp;
|
||||
line_start = ++line_end;
|
||||
break;
|
||||
}
|
||||
++line_end;
|
||||
}
|
||||
|
||||
// Parse data lines
|
||||
while (line_end < end) {
|
||||
if (*line_end == '\r' || *line_end == '\n') {
|
||||
ParseResult<void> tmp = parse_line(data, line_start, line_end, unknown_error);
|
||||
if (tmp.err)
|
||||
return tmp;
|
||||
line_start = line_end + 1;
|
||||
}
|
||||
line_end++;
|
||||
}
|
||||
|
||||
if (line_end != line_start)
|
||||
return res.fail(F("Last dataline not CRLF terminated"), line_end);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Data>
|
||||
static ParseResult<void> parse_line(Data *data, const char *line, const char *end, bool unknown_error) {
|
||||
ParseResult<void> res;
|
||||
if (line == end)
|
||||
return res;
|
||||
|
||||
ParseResult<ObisId> idres = ObisIdParser::parse(line, end);
|
||||
if (idres.err)
|
||||
return idres;
|
||||
|
||||
ParseResult<void> datares = data->parse_line(idres.result, idres.next, end);
|
||||
if (datares.err)
|
||||
return datares;
|
||||
|
||||
// If datares.next didn't move at all, there was no parser for
|
||||
// this field, that's ok. But if it did move, but not all the way
|
||||
// to the end, that's an error.
|
||||
if (datares.next != idres.next && datares.next != end)
|
||||
return res.fail(F("Trailing characters on data line"), datares.next);
|
||||
else if (datares.next == idres.next && unknown_error)
|
||||
return res.fail(F("Unknown field"), line);
|
||||
|
||||
return res.until(end);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsmr
|
||||
|
||||
#endif // DSMR_INCLUDE_PARSER_H
|
||||
242
P1_gateway_FW/lib/dsmr/reader.h
Normal file
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* Arduino DSMR parser.
|
||||
*
|
||||
* This software is licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2015 Matthijs Kooijman <matthijs@stdin.nl>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* P1 reader, that takes care of toggling a request pin, reading data
|
||||
* from a serial port and parsing it.
|
||||
*/
|
||||
|
||||
#ifndef DSMR_INCLUDE_READER_H
|
||||
#define DSMR_INCLUDE_READER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "crc16.h"
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
namespace dsmr {
|
||||
|
||||
/**
|
||||
* Controls the request pin on the P1 port to enable (periodic)
|
||||
* transmission of messages and reads those messages.
|
||||
*
|
||||
* To enable the request pin, call enable(). This lets the Smart Meter
|
||||
* start periodically sending messages. While the request pin is
|
||||
* enabled, loop() should be regularly called to read pending bytes.
|
||||
*
|
||||
* Once a full and correct message is received, loop() (and available())
|
||||
* start returning true, until the message is cleared. You can then
|
||||
* either read the raw message using raw(), or parse it using parse().
|
||||
*
|
||||
* The message is cleared when:
|
||||
* - clear() is called
|
||||
* - parse() is called
|
||||
* - loop() is called and the start of a new message is available
|
||||
*
|
||||
* When disable is called, the request pin is disabled again and any
|
||||
* partial message is discarded. Any bytes received while disabled are
|
||||
* dropped.
|
||||
*/
|
||||
class P1Reader {
|
||||
public:
|
||||
/**
|
||||
* Create a new P1Reader. The stream passed should be the serial
|
||||
* port to which the P1 TX pin is connected. The req_pin is the
|
||||
* pin connected to the request pin. The pin is configured as an
|
||||
* output, the Stream is assumed to be already set up (e.g. baud
|
||||
* rate configured).
|
||||
*/
|
||||
P1Reader(Stream *stream, uint8_t req_pin)
|
||||
: stream(stream), req_pin(req_pin), once(false), state(State::DISABLED_STATE) {
|
||||
pinMode(req_pin, OUTPUT);
|
||||
digitalWrite(req_pin, LOW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the request pin, to request data on the P1 port.
|
||||
* @param once When true, the request pin is automatically
|
||||
* disabled once a complete and correct message was
|
||||
* receivedc. When false, the request pin stays
|
||||
* enabled, so messages will continue to be sent
|
||||
* periodically.
|
||||
*/
|
||||
void enable(bool once) {
|
||||
digitalWrite(this->req_pin, HIGH);
|
||||
this->state = State::WAITING_STATE;
|
||||
this->once = once;
|
||||
}
|
||||
|
||||
/* Disable the request pin again, to stop data from being sent on
|
||||
* the P1 port. This will also clear any incomplete data that was
|
||||
* previously received, but a complete message will be kept until
|
||||
* clear() is called.
|
||||
*/
|
||||
void disable() {
|
||||
digitalWrite(this->req_pin, LOW);
|
||||
this->state = State::DISABLED_STATE;
|
||||
if (!this->_available)
|
||||
this->buffer = "";
|
||||
// Clear any pending bytes
|
||||
while(this->stream->read() >= 0) /* nothing */;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when a complete and correct message was received,
|
||||
* until it is cleared.
|
||||
*/
|
||||
bool available() {
|
||||
return this->_available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for new data to read. Should be called regularly, such as
|
||||
* once every loop. Returns true if a complete message is available
|
||||
* (just like available).
|
||||
*/
|
||||
bool loop() {
|
||||
while(true) {
|
||||
if (state == State::CHECKSUM_STATE) {
|
||||
// Let the Stream buffer the CRC bytes. Convert to size_t to
|
||||
// prevent unsigned vs signed comparison
|
||||
if ((size_t)this->stream->available() < CrcParser::CRC_LEN)
|
||||
return false;
|
||||
|
||||
char buf[CrcParser::CRC_LEN];
|
||||
for (uint8_t i = 0; i < CrcParser::CRC_LEN; ++i)
|
||||
buf[i] = this->stream->read();
|
||||
|
||||
ParseResult<uint16_t> crc = CrcParser::parse(buf, buf + lengthof(buf));
|
||||
|
||||
// Prepare for next message
|
||||
state = State::WAITING_STATE;
|
||||
|
||||
if (!crc.err && crc.result == this->crc) {
|
||||
// Message complete, checksum correct
|
||||
this->_available = true;
|
||||
|
||||
if (once)
|
||||
this->disable();
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// For other states, read bytes one by one
|
||||
int c = this->stream->read();
|
||||
if (c < 0)
|
||||
return false;
|
||||
|
||||
switch (this->state) {
|
||||
case State::DISABLED_STATE:
|
||||
// Where did this byte come from? Just toss it
|
||||
break;
|
||||
case State::WAITING_STATE:
|
||||
if (c == '/') {
|
||||
this->state = State::READING_STATE;
|
||||
// Include the / in the CRC
|
||||
this->crc = _crc16_update(0, c);
|
||||
this->clear();
|
||||
}
|
||||
break;
|
||||
case State::READING_STATE:
|
||||
// Include the ! in the CRC
|
||||
this->crc = _crc16_update(this->crc, c);
|
||||
if (c == '!')
|
||||
this->state = State::CHECKSUM_STATE;
|
||||
else
|
||||
buffer.concat((char)c);
|
||||
|
||||
break;
|
||||
case State::CHECKSUM_STATE:
|
||||
// This cannot happen (given the surrounding if), but the
|
||||
// compiler is not smart enough to see this, so list this
|
||||
// case to prevent a warning.
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data read so far.
|
||||
*/
|
||||
const String &raw() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a complete message has been received, parse it and store the
|
||||
* result into the ParsedData object passed.
|
||||
*
|
||||
* After parsing, the message is cleared.
|
||||
*
|
||||
* If parsing fails, false is returned. If err is passed, the error
|
||||
* message is appended to that string.
|
||||
*/
|
||||
template<typename... Ts>
|
||||
bool parse(ParsedData<Ts...> *data, String *err) {
|
||||
const char *str = buffer.c_str(), *end = buffer.c_str() + buffer.length();
|
||||
ParseResult<void> res = P1Parser::parse_data(data, str, end);
|
||||
|
||||
if (res.err && err)
|
||||
*err = res.fullError(str, end);
|
||||
|
||||
// Clear the message
|
||||
this->clear();
|
||||
|
||||
return res.err == NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any complete message from the buffer.
|
||||
*/
|
||||
void clear() {
|
||||
if (_available) {
|
||||
buffer = "";
|
||||
_available = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
Stream *stream;
|
||||
uint8_t req_pin;
|
||||
enum class State : uint8_t {
|
||||
DISABLED_STATE,
|
||||
WAITING_STATE,
|
||||
READING_STATE,
|
||||
CHECKSUM_STATE,
|
||||
};
|
||||
bool _available;
|
||||
bool once;
|
||||
State state;
|
||||
String buffer;
|
||||
uint16_t crc;
|
||||
};
|
||||
|
||||
} // namespace dsmr
|
||||
|
||||
#endif // DSMR_INCLUDE_READER_H
|
||||
186
P1_gateway_FW/lib/dsmr/util.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Arduino DSMR parser.
|
||||
*
|
||||
* This software is licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2015 Matthijs Kooijman <matthijs@stdin.nl>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Various utility functions
|
||||
*/
|
||||
|
||||
#ifndef DSMR_INCLUDE_UTIL_H
|
||||
#define DSMR_INCLUDE_UTIL_H
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#define DSMR_PROGMEM
|
||||
#else
|
||||
#define DSMR_PROGMEM PROGMEM
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace dsmr {
|
||||
|
||||
/**
|
||||
* Small utility to get the length of an array at compiletime.
|
||||
*/
|
||||
template<typename T, unsigned int sz>
|
||||
inline unsigned int lengthof(const T (&)[sz]) { return sz; }
|
||||
|
||||
// Hack until https://github.com/arduino/Arduino/pull/1936 is merged.
|
||||
// This appends the given number of bytes from the given C string to the
|
||||
// given Arduino string, without requiring a trailing NUL.
|
||||
// Requires that there _is_ room for nul-termination
|
||||
static void concat_hack(String& s, const char *append, size_t n) {
|
||||
// Add null termination. Inefficient, but it works...
|
||||
char buf[n + 1];
|
||||
memcpy(buf, append, n);
|
||||
buf[n] = 0;
|
||||
s.concat(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* The ParseResult<T> class wraps the result of a parse function. The type
|
||||
* of the result is passed as a template parameter and can be void to
|
||||
* not return any result.
|
||||
*
|
||||
* A ParseResult can either:
|
||||
* - Return an error. In this case, err is set to an error message, ctx
|
||||
* is optionally set to where the error occurred. The result (if any)
|
||||
* and the next pointer are meaningless.
|
||||
* - Return succesfully. In this case, err and ctx are NULL, result
|
||||
* contains the result (if any) and next points one past the last
|
||||
* byte processed by the parser.
|
||||
*
|
||||
* The ParseResult class has some convenience functions:
|
||||
* - succeed(result): sets the result to the given value and returns
|
||||
* the ParseResult again.
|
||||
* - fail(err): Set the err member to the error message passed,
|
||||
* optionally sets the ctx and return the ParseResult again.
|
||||
* - until(next): Set the next member and return the ParseResult again.
|
||||
*
|
||||
* Furthermore, ParseResults can be implicitely converted to other
|
||||
* types. In this case, the error message, context and and next pointer are
|
||||
* conserved, the return value is reset to the default value for the
|
||||
* target type.
|
||||
*
|
||||
* Note that ctx points into the string being parsed, so it does not
|
||||
* need to be freed, lives as long as the original string and is
|
||||
* probably way longer that needed.
|
||||
*/
|
||||
|
||||
// Superclass for ParseResult so we can specialize for void without
|
||||
// having to duplicate all content
|
||||
template <typename P, typename T>
|
||||
struct _ParseResult {
|
||||
T result;
|
||||
|
||||
P& succeed(T& result) {
|
||||
this->result = result; return *static_cast<P*>(this);
|
||||
}
|
||||
P& succeed(T&& result) {
|
||||
this->result = result;
|
||||
return *static_cast<P*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
// partial specialization for void result
|
||||
template <typename P>
|
||||
struct _ParseResult<P, void> {
|
||||
};
|
||||
|
||||
// Actual ParseResult class
|
||||
template <typename T>
|
||||
struct ParseResult : public _ParseResult<ParseResult<T>, T> {
|
||||
const char *next = NULL;
|
||||
const __FlashStringHelper *err = NULL;
|
||||
const char *ctx = NULL;
|
||||
|
||||
ParseResult& fail(const __FlashStringHelper *err, const char* ctx = NULL) {
|
||||
this->err = err;
|
||||
this->ctx = ctx;
|
||||
return *this;
|
||||
}
|
||||
ParseResult& until(const char *next) {
|
||||
this->next = next;
|
||||
return *this;
|
||||
}
|
||||
ParseResult() = default;
|
||||
ParseResult(const ParseResult& other) = default;
|
||||
|
||||
template <typename T2>
|
||||
ParseResult(const ParseResult<T2>& other): next(other.next), err(other.err), ctx(other.ctx) { }
|
||||
|
||||
/**
|
||||
* Returns the error, including context in a fancy multi-line format.
|
||||
* The start and end passed are the first and one-past-the-end
|
||||
* characters in the total parsed string. These are needed to properly
|
||||
* limit the context output.
|
||||
*/
|
||||
String fullError(const char* start, const char* end) const {
|
||||
String res;
|
||||
if (this->ctx && start && end) {
|
||||
// Find the entire line surrounding the context
|
||||
const char *line_end = this->ctx;
|
||||
while(line_end < end && line_end[0] != '\r' && line_end[0] != '\n') ++line_end;
|
||||
const char *line_start = this->ctx;
|
||||
while(line_start > start && line_start[-1] != '\r' && line_start[-1] != '\n') --line_start;
|
||||
|
||||
// We can now predict the context string length, so let String allocate
|
||||
// memory in advance
|
||||
res.reserve((line_end - line_start) + 2 + (this->ctx - line_start) + 1 + 2);
|
||||
|
||||
// Write the line
|
||||
concat_hack(res, line_start, line_end - line_start);
|
||||
res += "\r\n";
|
||||
|
||||
// Write a marker to point out ctx
|
||||
while (line_start++ < this->ctx)
|
||||
res += ' ';
|
||||
res += '^';
|
||||
res += "\r\n";
|
||||
}
|
||||
res += this->err;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An OBIS id is 6 bytes, usually noted as a-b:c.d.e.f. Here we put them
|
||||
* in an array for easy parsing.
|
||||
*/
|
||||
struct ObisId {
|
||||
uint8_t v[6];
|
||||
|
||||
constexpr ObisId(uint8_t a, uint8_t b = 255, uint8_t c = 255, uint8_t d = 255, uint8_t e = 255, uint8_t f = 255)
|
||||
: v{a, b, c, d, e, f} { };
|
||||
constexpr ObisId() : v() {} // Zeroes
|
||||
|
||||
bool operator==(const ObisId &other) const {
|
||||
return memcmp(&v, &other.v, sizeof(v)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsmr
|
||||
|
||||
#endif // DSMR_INCLUDE_UTIL_H
|
||||
14
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/.editorconfig
Normal file
@@ -0,0 +1,14 @@
|
||||
# This file is for unifying the coding style for different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
max_line_length = 120
|
||||
curly_bracket_next_line = false
|
||||
17
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/.github/stale.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 180
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 14
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
2
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.vscode
|
||||
.pio
|
||||
29
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/.travis.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
# documentation at https://docs.platformio.org/en/latest/integration/ci/travis.html
|
||||
|
||||
language: python
|
||||
python:
|
||||
- "3.7"
|
||||
|
||||
# Cache PlatformIO packages using Travis CI container-based infrastructure
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
- $HOME/.cache/pip
|
||||
|
||||
env:
|
||||
- PLATFORMIO_CI_SRC=examples/SSD1306UiDemo
|
||||
- PLATFORMIO_CI_SRC=examples/SSD1306SimpleDemo
|
||||
- PLATFORMIO_CI_SRC=examples/SSD1306DrawingDemo
|
||||
- PLATFORMIO_CI_SRC=examples/SSD1306OTADemo
|
||||
- PLATFORMIO_CI_SRC=examples/SSD1306ClockDemo
|
||||
- PLATFORMIO_CI_SRC=examples/SSD1306TwoScreenDemo
|
||||
|
||||
|
||||
install:
|
||||
- pip install -U platformio
|
||||
- pio update
|
||||
- platformio lib -g install "paulstoffregen/Time@^1.6"
|
||||
|
||||
script:
|
||||
- platformio ci --lib="." --board=nodemcuv2
|
||||
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_ADD_INCLUDEDIRS src)
|
||||
set(COMPONENT_PRIV_REQUIRES arduino-esp32)
|
||||
set(COMPONENT_SRCDIRS src)
|
||||
register_component()
|
||||
70
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/CONTRIBUTING.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Contributing to ThingPulse OLED SSD1306
|
||||
|
||||
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||
|
||||
The following is a set of guidelines for contributing to the ThingPulse OLED SSD1306 library on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request.
|
||||
|
||||
It is appreciated if you raise an issue _before_ you start changing the code, discussing the proposed change; emphasizing that you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself.
|
||||
|
||||
### Table Of Contents
|
||||
* [General remarks](#general-remarks)
|
||||
* [Writing Documentation](#writing-documentation)
|
||||
* [Working with Git and GitHub](#working-with-git-and-github)
|
||||
* [General flow](#general-flow)
|
||||
* [Keeping your fork in sync](#keeping-your-fork-in-sync)
|
||||
* [Commit messages](#commit-messages)
|
||||
|
||||
## General remarks
|
||||
We are a friendly and welcoming community and look forward to your contributions. Once your contribution is integrated into this repository we feel responsible for it. Therefore, be prepared for constructive feedback. Before we merge anything we need to ensure that it fits in and is consistent with the rest of code.
|
||||
If you made something really cool but won't spend the time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issue we'll take a look at it anyway.
|
||||
|
||||
## Writing Documentation
|
||||
ThingPulse maintains documentation for its products at [https://github.com/thingpulse/docs/](https://github.com/thingpulse/docs/). If you contribute features for this project that require altering the respective product guide then we ask you to prepare a pull request with the necessary documentation changes as well.
|
||||
|
||||
## Working with Git and GitHub
|
||||
|
||||
Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutorials/merging-vs-rebasing) your feature branch onto `master` to pull updates and verify your local changes against them before placing the pull request.
|
||||
|
||||
### General flow
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo) this repository on GitHub.
|
||||
1. [Create a branch](https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/#creating-a-branch) in your fork on GitHub **based on the `master` branch**.
|
||||
1. Clone the fork on your machine with `git clone https://github.com/<your-account>/<esp8266-oled-ssd1306>.git`
|
||||
1. `cd <weather-station-fork>` then run `git remote add upstream https://github.com/ThingPulse/esp8266-oled-ssd1306`
|
||||
1. `git checkout <branch-name>`
|
||||
1. Make changes to the code base and commit them using e.g. `git commit -a -m 'Look ma, I did it'`
|
||||
1. When you're done bring your fork up-to-date with the upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `master` running `git rebase master`.
|
||||
1. `git push`
|
||||
1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub.
|
||||
|
||||
This is just one way of doing things. If you're proficient in Git matters you're free to choose your own. If you want to read more then the [GitHub chapter in the Git book](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project#The-GitHub-Flow) is a way to start. [GitHub's own documentation](https://help.github.com/categories/collaborating/) contains a wealth of information as well.
|
||||
|
||||
### Keeping your fork in sync
|
||||
You need to sync your fork with the upstream repository from time to time, latest before you rebase (see flow above).
|
||||
|
||||
1. `git fetch upstream`
|
||||
1. `git checkout master`
|
||||
1. `git merge upstream/master`
|
||||
|
||||
### Commit messages
|
||||
|
||||
From: [http://git-scm.com/book/ch5-2.html](http://git-scm.com/book/ch5-2.html)
|
||||
<pre>
|
||||
Short (50 chars or less) summary of changes
|
||||
|
||||
More detailed explanatory text, if necessary. Wrap it to about 72
|
||||
characters or so. In some contexts, the first line is treated as the
|
||||
subject of an email and the rest of the text as the body. The blank
|
||||
line separating the summary from the body is critical (unless you omit
|
||||
the body entirely); tools like rebase can get confused if you run the
|
||||
two together.
|
||||
|
||||
Further paragraphs come after blank lines.
|
||||
|
||||
- Bullet points are okay, too
|
||||
- Typically a hyphen or asterisk is used for the bullet, preceded by a
|
||||
single space, with blank lines in between, but conventions vary here
|
||||
</pre>
|
||||
|
||||
Don't forget to [reference affected issues](https://help.github.com/articles/closing-issues-via-commit-messages/) in the commit message to have them closed automatically on GitHub.
|
||||
|
||||
[Amend](https://help.github.com/articles/changing-a-commit-message/) your commit messages if necessary to make sure what the world sees on GitHub is as expressive and meaningful as possible.
|
||||
429
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/README.md
Normal file
@@ -0,0 +1,429 @@
|
||||
[](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306)
|
||||
|
||||
# ThingPulse OLED SSD1306 (ESP8266/ESP32/Mbed-OS)
|
||||
|
||||
> We just released version 4.0.0. Please have a look at our [upgrade guide](UPGRADE-4.0.md)
|
||||
|
||||
This is a driver for SSD1306 128x64, 128x32, 64x48 and 64x32 OLED displays running on the Arduino/ESP8266 & ESP32 and mbed-os platforms.
|
||||
Can be used with either the I2C or SPI version of the display.
|
||||
|
||||
You can either download this library as a zip file and unpack it to your Arduino/libraries folder or find it in the Arduino library manager under "ESP8266 and ESP32 Oled Driver for SSD1306 display". For mbed-os a copy of the files are available as an mbed-os library.
|
||||
|
||||
It is also available as a platformio library. Just execute the following command:
|
||||
```
|
||||
platformio lib install 562
|
||||
```
|
||||
|
||||
## Service level promise
|
||||
|
||||
<table><tr><td><img src="https://thingpulse.com/assets/ThingPulse-open-source-prime.png" width="150">
|
||||
</td><td>This is a ThingPulse <em>prime</em> project. See our <a href="https://thingpulse.com/about/open-source-commitment/">open-source commitment declaration</a> for what this means.</td></tr></table>
|
||||
|
||||
## Credits
|
||||
|
||||
This library has initially been written by Daniel Eichhorn ([@squix78](https://github.com/squix78)). Many thanks go to Fabrice Weinberg ([@FWeinb](https://github.com/FWeinb)) for optimizing and refactoring many aspects of the library. Also many thanks to the many committers who helped to add new features and who fixed many bugs. Mbed-OS support and other improvements were contributed by Helmut Tschemernjak ([@helmut64](https://github.com/helmut64)).
|
||||
|
||||
The init sequence for the SSD1306 was inspired by Adafruit's library for the same display.
|
||||
|
||||
## mbed-os
|
||||
This library has been adopted to support the ARM mbed-os environment. A copy of this library is available in mbed-os under the name OLED_SSD1306 by Helmut Tschemernjak. An alternate installation option is to copy the following files into your mbed-os project: OLEDDisplay.cpp OLEDDisplay.h OLEDDisplayFonts.h OLEDDisplayUi.cpp OLEDDisplayUi.h SSD1306I2C.h
|
||||
|
||||
## Usage
|
||||
|
||||
Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the [ESP8266 Weather Station](https://github.com/ThingPulse/esp8266-weather-station) library which uses the OLED library to display beautiful weather information.
|
||||
|
||||
## Upgrade
|
||||
|
||||
The API changed a lot with the 3.0 release. If you were using this library with older versions please have a look at the [Upgrade Guide](UPGRADE-3.0.md).
|
||||
|
||||
Going from 3.x version to 4.0 a lot of internals changed and compatibility for more displays was added. Please read the [Upgrade Guide](UPGRADE-4.0.md).
|
||||
|
||||
## Features
|
||||
|
||||
* Draw pixels at given coordinates
|
||||
* Draw lines from given coordinates to given coordinates
|
||||
* Draw or fill a rectangle with given dimensions
|
||||
* Draw Text at given coordinates:
|
||||
* Define Alignment: Left, Right and Center
|
||||
* Set the Fontface you want to use (see section Fonts below)
|
||||
* Limit the width of the text by an amount of pixels. Before this widths will be reached, the renderer will wrap the text to a new line if possible
|
||||
* Display content in automatically side scrolling carousel
|
||||
* Define transition cycles
|
||||
* Define how long one frame will be displayed
|
||||
* Draw the different frames in callback methods
|
||||
* One indicator per frame will be automatically displayed. The active frame will be displayed from inactive once
|
||||
|
||||
## Fonts
|
||||
|
||||
Fonts are defined in a proprietary but open format. You can create new font files by choosing from a given list
|
||||
of open sourced Fonts from this web app: http://oleddisplay.squix.ch
|
||||
Choose the font family, style and size, check the preview image and if you like what you see click the "Create" button. This will create the font array in a text area form where you can copy and paste it into a new or existing header file.
|
||||
|
||||
|
||||

|
||||
|
||||
## Hardware Abstraction
|
||||
|
||||
The library supports different protocols to access the OLED display. Currently there is support for I2C using the built in Wire.h library, I2C by using the much faster [BRZO I2C library](https://github.com/pasko-zh/brzo_i2c) written in assembler and it also supports displays which come with the SPI interface.
|
||||
|
||||
### I2C with Wire.h
|
||||
|
||||
```C++
|
||||
#include <Wire.h>
|
||||
#include "SSD1306Wire.h"
|
||||
|
||||
// for 128x64 displays:
|
||||
SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
|
||||
// for 128x32 displays:
|
||||
// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, GEOMETRY_128_32 (or 128_64)
|
||||
// for using 2nd Hardware I2C (if available)
|
||||
// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_TWO); //default value is I2C_ONE if not mentioned
|
||||
// By default SD1306Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value
|
||||
// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz
|
||||
// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency
|
||||
```
|
||||
|
||||
for a SH1106:
|
||||
```C++
|
||||
#include <Wire.h>
|
||||
#include "SH1106Wire.h"
|
||||
|
||||
SH1106Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
|
||||
// By default SH1106Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value
|
||||
// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz
|
||||
// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency
|
||||
```
|
||||
|
||||
### I2C with brzo_i2c
|
||||
|
||||
```C++
|
||||
#include <brzo_i2c.h>
|
||||
#include "SSD1306Brzo.h"
|
||||
|
||||
SSD1306Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
|
||||
```
|
||||
or for the SH1106:
|
||||
```C++
|
||||
#include <brzo_i2c.h>
|
||||
#include "SH1106Brzo.h"
|
||||
|
||||
SH1106Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
|
||||
```
|
||||
|
||||
### SPI
|
||||
|
||||
```C++
|
||||
#include <SPI.h>
|
||||
#include "SSD1306Spi.h"
|
||||
|
||||
SSD1306Spi display(D0, D2, D8); // RES, DC, CS
|
||||
```
|
||||
or for the SH1106:
|
||||
```C++
|
||||
#include <SPI.h>
|
||||
#include "SH1106Spi.h"
|
||||
|
||||
SH1106Spi display(D0, D2); // RES, DC
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Display Control
|
||||
|
||||
```C++
|
||||
// Initialize the display
|
||||
void init();
|
||||
|
||||
// Free the memory used by the display
|
||||
void end();
|
||||
|
||||
// Cycle through the initialization
|
||||
void resetDisplay(void);
|
||||
|
||||
// Connect again to the display through I2C
|
||||
void reconnect(void);
|
||||
|
||||
// Turn the display on
|
||||
void displayOn(void);
|
||||
|
||||
// Turn the display offs
|
||||
void displayOff(void);
|
||||
|
||||
// Clear the local pixel buffer
|
||||
void clear(void);
|
||||
|
||||
// Write the buffer to the display memory
|
||||
void display(void);
|
||||
|
||||
// Inverted display mode
|
||||
void invertDisplay(void);
|
||||
|
||||
// Normal display mode
|
||||
void normalDisplay(void);
|
||||
|
||||
// Set display contrast
|
||||
// really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0
|
||||
// normal brightness & contrast: contrast = 100
|
||||
void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64);
|
||||
|
||||
// Convenience method to access
|
||||
void setBrightness(uint8_t);
|
||||
|
||||
// Turn the display upside down
|
||||
void flipScreenVertically();
|
||||
|
||||
// Draw the screen mirrored
|
||||
void mirrorScreen();
|
||||
```
|
||||
|
||||
## Pixel drawing
|
||||
|
||||
```C++
|
||||
|
||||
/* Drawing functions */
|
||||
// Sets the color of all pixel operations
|
||||
// color : BLACK, WHITE, INVERSE
|
||||
void setColor(OLEDDISPLAY_COLOR color);
|
||||
|
||||
// Draw a pixel at given position
|
||||
void setPixel(int16_t x, int16_t y);
|
||||
|
||||
// Draw a line from position 0 to position 1
|
||||
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1);
|
||||
|
||||
// Draw the border of a rectangle at the given location
|
||||
void drawRect(int16_t x, int16_t y, int16_t width, int16_t height);
|
||||
|
||||
// Fill the rectangle
|
||||
void fillRect(int16_t x, int16_t y, int16_t width, int16_t height);
|
||||
|
||||
// Draw the border of a circle
|
||||
void drawCircle(int16_t x, int16_t y, int16_t radius);
|
||||
|
||||
// Fill circle
|
||||
void fillCircle(int16_t x, int16_t y, int16_t radius);
|
||||
|
||||
// Draw a line horizontally
|
||||
void drawHorizontalLine(int16_t x, int16_t y, int16_t length);
|
||||
|
||||
// Draw a lin vertically
|
||||
void drawVerticalLine(int16_t x, int16_t y, int16_t length);
|
||||
|
||||
// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is
|
||||
// a unsigned byte value between 0 and 100
|
||||
void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress);
|
||||
|
||||
// Draw a bitmap in the internal image format
|
||||
void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image);
|
||||
|
||||
// Draw a XBM
|
||||
void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char* xbm);
|
||||
```
|
||||
|
||||
## Text operations
|
||||
|
||||
``` C++
|
||||
void drawString(int16_t x, int16_t y, String text);
|
||||
|
||||
// Draws a String with a maximum width at the given location.
|
||||
// If the given String is wider than the specified width
|
||||
// The text will be wrapped to the next line at a space or dash
|
||||
void drawStringMaxWidth(int16_t x, int16_t y, int16_t maxLineWidth, String text);
|
||||
|
||||
// Returns the width of the const char* with the current
|
||||
// font settings
|
||||
uint16_t getStringWidth(const char* text, uint16_t length);
|
||||
|
||||
// Convencience method for the const char version
|
||||
uint16_t getStringWidth(String text);
|
||||
|
||||
// Specifies relative to which anchor point
|
||||
// the text is rendered. Available constants:
|
||||
// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH
|
||||
void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment);
|
||||
|
||||
// Sets the current font. Available default fonts
|
||||
// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
|
||||
// Or create one with the font tool at http://oleddisplay.squix.ch
|
||||
void setFont(const uint8_t* fontData);
|
||||
```
|
||||
|
||||
## Ui Library (OLEDDisplayUi)
|
||||
|
||||
The Ui Library is used to provide a basic set of Ui elements called, `Frames` and `Overlays`. A `Frame` is used to provide
|
||||
information the default behaviour is to display a `Frame` for a defined time and than move to the next. The library also provides an `Indicator` that will be updated accordingly. An `Overlay` on the other hand is a pieces of information (e.g. a clock) that is displayed always at the same position.
|
||||
|
||||
|
||||
```C++
|
||||
/**
|
||||
* Initialise the display
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Configure the internal used target FPS
|
||||
*/
|
||||
void setTargetFPS(uint8_t fps);
|
||||
|
||||
/**
|
||||
* Enable automatic transition to next frame after the some time can be configured with
|
||||
* `setTimePerFrame` and `setTimePerTransition`.
|
||||
*/
|
||||
void enableAutoTransition();
|
||||
|
||||
/**
|
||||
* Disable automatic transition to next frame.
|
||||
*/
|
||||
void disableAutoTransition();
|
||||
|
||||
/**
|
||||
* Set the direction if the automatic transitioning
|
||||
*/
|
||||
void setAutoTransitionForwards();
|
||||
void setAutoTransitionBackwards();
|
||||
|
||||
/**
|
||||
* Set the approx. time a frame is displayed
|
||||
*/
|
||||
void setTimePerFrame(uint16_t time);
|
||||
|
||||
/**
|
||||
* Set the approx. time a transition will take
|
||||
*/
|
||||
void setTimePerTransition(uint16_t time);
|
||||
|
||||
/**
|
||||
* Draw the indicator.
|
||||
* This is the default state for all frames if
|
||||
* the indicator was hidden on the previous frame
|
||||
* it will be slided in.
|
||||
*/
|
||||
void enableIndicator();
|
||||
|
||||
/**
|
||||
* Don't draw the indicator.
|
||||
* This will slide out the indicator
|
||||
* when transitioning to the next frame.
|
||||
*/
|
||||
void disableIndicator();
|
||||
|
||||
/**
|
||||
* Enable drawing of all indicators.
|
||||
*/
|
||||
void enableAllIndicators();
|
||||
|
||||
/**
|
||||
* Disable drawing of all indicators.
|
||||
*/
|
||||
void disableAllIndicators();
|
||||
|
||||
/**
|
||||
* Set the position of the indicator bar.
|
||||
*/
|
||||
void setIndicatorPosition(IndicatorPosition pos);
|
||||
|
||||
/**
|
||||
* Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING
|
||||
*/
|
||||
void setIndicatorDirection(IndicatorDirection dir);
|
||||
|
||||
/**
|
||||
* Set the symbol to indicate an active frame in the indicator bar.
|
||||
*/
|
||||
void setActiveSymbol(const char* symbol);
|
||||
|
||||
/**
|
||||
* Set the symbol to indicate an inactive frame in the indicator bar.
|
||||
*/
|
||||
void setInactiveSymbol(const char* symbol);
|
||||
|
||||
/**
|
||||
* Configure what animation is used to transition from one frame to another
|
||||
*/
|
||||
void setFrameAnimation(AnimationDirection dir);
|
||||
|
||||
/**
|
||||
* Add frame drawing functions
|
||||
*/
|
||||
void setFrames(FrameCallback* frameFunctions, uint8_t frameCount);
|
||||
|
||||
/**
|
||||
* Add overlays drawing functions that are draw independent of the Frames
|
||||
*/
|
||||
void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount);
|
||||
|
||||
/**
|
||||
* Set the function that will draw each step
|
||||
* in the loading animation
|
||||
*/
|
||||
void setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction);
|
||||
|
||||
/**
|
||||
* Run the loading process
|
||||
*/
|
||||
void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount);
|
||||
|
||||
// Manuell Controll
|
||||
void nextFrame();
|
||||
void previousFrame();
|
||||
|
||||
/**
|
||||
* Switch without transition to frame `frame`.
|
||||
*/
|
||||
void switchToFrame(uint8_t frame);
|
||||
|
||||
/**
|
||||
* Transition to frame `frame`, when the `frame` number is bigger than the current
|
||||
* frame the forward animation will be used, otherwise the backwards animation is used.
|
||||
*/
|
||||
void transitionToFrame(uint8_t frame);
|
||||
|
||||
// State Info
|
||||
OLEDDisplayUiState* getUiState();
|
||||
|
||||
// This needs to be called in the main loop
|
||||
// the returned value is the remaining time (in ms)
|
||||
// you have to draw after drawing to keep the frame budget.
|
||||
int8_t update();
|
||||
```
|
||||
|
||||
## Example: SSD1306Demo
|
||||
|
||||
### Frame 1
|
||||

|
||||
|
||||
This frame shows three things:
|
||||
* How to draw an xbm image
|
||||
* How to draw a static text which is not moved by the frame transition
|
||||
* The active/inactive frame indicators
|
||||
|
||||
### Frame 2
|
||||

|
||||
|
||||
Currently there are one fontface with three sizes included in the library: Arial 10, 16 and 24. Once the converter is published you will be able to convert any ttf font into the used format.
|
||||
|
||||
### Frame 3
|
||||
|
||||

|
||||
|
||||
This frame demonstrates the text alignment. The coordinates in the frame show relative to which position the texts have been rendered.
|
||||
|
||||
### Frame 4
|
||||
|
||||

|
||||
|
||||
This shows how to use define a maximum width after which the driver automatically wraps a word to the next line. This comes in very handy if you have longer texts to display.
|
||||
|
||||
### SPI version
|
||||
|
||||

|
||||
|
||||
This shows the code working on the SPI version of the display. See demo code for ESP8266 pins used.
|
||||
|
||||
## Selection of projects using this library
|
||||
|
||||
* [QRCode ESP8266](https://github.com/anunpanya/ESP8266_QRcode) (by @anunpanya)
|
||||
* [Scan I2C](https://github.com/hallard/Scan-I2C-WiFi) (by @hallard)
|
||||
* [ThingPulse Weather Station](https://github.com/ThingPulse/esp8266-weather-station)
|
||||
* [Meshtastic](https://www.meshtastic.org/) - an open source GPS communicator mesh radio
|
||||
* Yours?
|
||||
@@ -0,0 +1,20 @@
|
||||
# GEOMETRY_64_48
|
||||
|
||||
The 64x48 geometry setting are working with the `Wire.h` and `brzo_i2c` libraries.
|
||||
|
||||
I've tested it successfully with a WEMOS D1 mini Lite and a WEMOS OLED shield
|
||||
|
||||
Initialization code:
|
||||
|
||||
- Wire
|
||||
```
|
||||
#include <Wire.h>
|
||||
#include <SSD1306Wire.h>
|
||||
SSD1306Wire display(0x3c, D2, D1, GEOMETRY_64_48 ); // WEMOS OLED shield
|
||||
```
|
||||
|
||||
- BRZO i2c
|
||||
```
|
||||
#include <SSD1306Brzo.h>
|
||||
SSD1306Brzo display(0x3c, D2, D1, GEOMETRY_64_48 ); // WEMOS OLED Shield
|
||||
```
|
||||
125
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/UPGRADE-3.0.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Upgrade from 2.0 to 3.0
|
||||
|
||||
While developing version 3.0 we made some breaking changes to the public
|
||||
API of this library. This document will help you update your code to work with
|
||||
version 3.0
|
||||
|
||||
## Font Definitions
|
||||
|
||||
To get better performance and a smaller font definition format, we change the memory
|
||||
layout of the font definition format. If you are using custom fonts not included in
|
||||
this library we updated the font generator [here](http://oleddisplay.squix.ch/#/home).
|
||||
Please update your fonts to be working with 3.0 by selecting the respective version in the dropdown.
|
||||
|
||||
|
||||
## Architectural Changes
|
||||
|
||||
To become a more versatile library for the SSD1306 chipset we abstracted the
|
||||
hardware connection into subclasses of the base display class now called `OLEDDisplay`.
|
||||
This library is currently shipping with three implementations:
|
||||
|
||||
* `SSD1306Wire` implementing the I2C protocol using the Wire Library.
|
||||
* `SSD1306Brzo` implementing the I2C protocol using the faster [`brzo_i2c`](https://github.com/pasko-zh/brzo_i2c) library.
|
||||
* `SSD1306Spi` implementing the SPI protocol.
|
||||
|
||||
To keep backwards compatiblity with the old API `SSD1306` is an alias of `SSD1306Wire`.
|
||||
If you are not using the UI components you don't have to change anything to keep your code working.
|
||||
|
||||
## Name Changes
|
||||
|
||||
[Naming things is hard](http://martinfowler.com/bliki/TwoHardThings.html), to better reflect our intention with this library
|
||||
we changed the name of the base class to `OLEDDisplay` and the UI library accordingly to `OLEDDisplayUi`.
|
||||
As a consequence the type definitions of all frame and overlay related functions changed.
|
||||
This means that you have to update all your frame drawing callbacks from:
|
||||
|
||||
```c
|
||||
bool frame1(SSD1306 *display, SSD1306UiState* state, int x, int y);
|
||||
```
|
||||
|
||||
too
|
||||
|
||||
```c
|
||||
void frame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
|
||||
```
|
||||
|
||||
And your overlay drawing functions from:
|
||||
|
||||
```c
|
||||
bool overlay1(SSD1306 *display, SSD1306UiState* state);
|
||||
```
|
||||
|
||||
too
|
||||
|
||||
```c
|
||||
void overlay1(OLEDDisplay *display, OLEDDisplayUiState* state);
|
||||
```
|
||||
|
||||
## New Features
|
||||
|
||||
### Loading Animation
|
||||
|
||||
While using this library ourself we noticed a pattern emerging. We want to drawing
|
||||
a loading progress while connecting to WiFi and updating weather data etc.
|
||||
|
||||
The simplest thing was to add the function `drawProgressBar(x, y, width, height, progress)`
|
||||
,where `progress` is between `0` and `100`, right to the `OLEDDisplay` class.
|
||||
|
||||
But we didn't stop there. We added a new feature to the `OLEDDisplayUi` called `LoadingStages`.
|
||||
You can define your loading process like this:
|
||||
|
||||
```c++
|
||||
LoadingStage loadingStages[] = {
|
||||
{
|
||||
.process = "Connect to WiFi",
|
||||
.callback = []() {
|
||||
// Connect to WiFi
|
||||
}
|
||||
},
|
||||
{
|
||||
.process = "Get time from NTP",
|
||||
.callback = []() {
|
||||
// Get current time via NTP
|
||||
}
|
||||
}
|
||||
// more steps
|
||||
};
|
||||
|
||||
int LOADING_STAGES_COUNT = sizeof(loadingStages) / sizeof(LoadingStage);
|
||||
```
|
||||
|
||||
After defining your array of `LoadingStages` you can then run the loading process by using
|
||||
`ui.runLoadingProcess(loadingStages, LOADING_STAGES_COUNT)`. This will give you a
|
||||
nice little loading animation you can see in the beginning of [this](https://vimeo.com/168362918)
|
||||
video.
|
||||
|
||||
To further customize this you are free to define your own `LoadingDrawFunction` like this:
|
||||
|
||||
```c
|
||||
void myLoadingDraw(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) {
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
// stage->process contains the text of the current progress e.q. "Connect to WiFi"
|
||||
display->drawString(64, 18, stage->process);
|
||||
// you could just print the current process without the progress bar
|
||||
display->drawString(64, 28, progress);
|
||||
}
|
||||
```
|
||||
|
||||
After defining a function like that, you can pass it to the Ui library by use
|
||||
`ui.setLoadingDrawFunction(myLoadingDraw)`.
|
||||
|
||||
|
||||
### Text Logging
|
||||
|
||||
It is always useful to display some text on the display without worrying to much
|
||||
where it goes and managing it. In 3.0 we made the `OLEDDisplay` class implement
|
||||
[`Print`](https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/Print.h)
|
||||
so you can use it like you would use `Serial`. We calls this feature `LogBuffer`
|
||||
and the only thing you have to do is to define how many lines you want to display
|
||||
and how many characters there are on average on each. This is done by calling
|
||||
`setLogBuffer(lines, chars);`. If there is not enough memory the function will
|
||||
return false.
|
||||
|
||||
After that you can draw the `LogBuffer` anywhere you want by calling `drawLogBuffer(x, y)`.
|
||||
(Note: You have to call `display()` to update the screen)
|
||||
We made a [video](https://www.youtube.com/watch?v=8Fiss77A3TE) showing this feature in action.
|
||||
27
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/UPGRADE-4.0.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Upgrade from 3.x to 4.0
|
||||
|
||||
There are changes that breaks compatibility with older versions.
|
||||
|
||||
1. You'll have to change data type for all your binary resources such as images and fonts from
|
||||
|
||||
```c
|
||||
const char MySymbol[] PROGMEM = {
|
||||
```
|
||||
|
||||
to
|
||||
|
||||
```c
|
||||
const uint8_t MySymbol[] PROGMEM = {
|
||||
```
|
||||
|
||||
1. Arguments of `setContrast` from `char` to `uint8_t`
|
||||
|
||||
```c++
|
||||
void OLEDDisplay::setContrast(char contrast, char precharge, char comdetect);
|
||||
```
|
||||
|
||||
to
|
||||
|
||||
```c++
|
||||
void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect);
|
||||
```
|
||||
@@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := src
|
||||
COMPONENT_SRCDIRS := src
|
||||
CXXFLAGS += -Wno-ignored-qualifiers
|
||||
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
|
||||
// Include the correct display library
|
||||
// For a connection via I2C using Wire include
|
||||
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
|
||||
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
|
||||
// For a connection via I2C using brzo_i2c (must be installed) include
|
||||
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Brzo.h"
|
||||
// #include "SH1106Brzo.h"
|
||||
// For a connection via SPI include
|
||||
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Spi.h"
|
||||
// #include "SH1106SPi.h"
|
||||
|
||||
// Include the UI lib
|
||||
#include "OLEDDisplayUi.h"
|
||||
|
||||
// Include custom images
|
||||
#include "images.h"
|
||||
|
||||
// Use the corresponding display class:
|
||||
|
||||
// Initialize the OLED display using SPI
|
||||
// D5 -> CLK
|
||||
// D7 -> MOSI (DOUT)
|
||||
// D0 -> RES
|
||||
// D2 -> DC
|
||||
// D8 -> CS
|
||||
// SSD1306Spi display(D0, D2, D8);
|
||||
// or
|
||||
// SH1106Spi display(D0, D2);
|
||||
|
||||
// Initialize the OLED display using brzo_i2c
|
||||
// D3 -> SDA
|
||||
// D5 -> SCL
|
||||
// SSD1306Brzo display(0x3c, D3, D5);
|
||||
// or
|
||||
// SH1106Brzo display(0x3c, D3, D5);
|
||||
|
||||
// Initialize the OLED display using Wire library
|
||||
SSD1306Wire display(0x3c, D3, D5);
|
||||
// SH1106 display(0x3c, D3, D5);
|
||||
|
||||
OLEDDisplayUi ui ( &display );
|
||||
|
||||
int screenW = 128;
|
||||
int screenH = 64;
|
||||
int clockCenterX = screenW/2;
|
||||
int clockCenterY = ((screenH-16)/2)+16; // top yellow part is 16 px height
|
||||
int clockRadius = 23;
|
||||
|
||||
// utility function for digital clock display: prints leading 0
|
||||
String twoDigits(int digits){
|
||||
if(digits < 10) {
|
||||
String i = '0'+String(digits);
|
||||
return i;
|
||||
}
|
||||
else {
|
||||
return String(digits);
|
||||
}
|
||||
}
|
||||
|
||||
void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
|
||||
|
||||
}
|
||||
|
||||
void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
|
||||
// ui.disableIndicator();
|
||||
|
||||
// Draw the clock face
|
||||
// display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius);
|
||||
display->drawCircle(clockCenterX + x, clockCenterY + y, 2);
|
||||
//
|
||||
//hour ticks
|
||||
for( int z=0; z < 360;z= z + 30 ){
|
||||
//Begin at 0° and stop at 360°
|
||||
float angle = z ;
|
||||
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
|
||||
int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) );
|
||||
int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) );
|
||||
int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
|
||||
int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
|
||||
display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y);
|
||||
}
|
||||
|
||||
// display second hand
|
||||
float angle = second() * 6 ;
|
||||
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
|
||||
int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
|
||||
int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
|
||||
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
|
||||
//
|
||||
// display minute hand
|
||||
angle = minute() * 6 ;
|
||||
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
|
||||
x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
|
||||
y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
|
||||
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
|
||||
//
|
||||
// display hour hand
|
||||
angle = hour() * 30 + int( ( minute() / 12 ) * 6 ) ;
|
||||
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
|
||||
x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
|
||||
y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
|
||||
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
|
||||
}
|
||||
|
||||
void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
|
||||
String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(ArialMT_Plain_24);
|
||||
display->drawString(clockCenterX + x , clockCenterY + y, timenow );
|
||||
}
|
||||
|
||||
// This array keeps function pointers to all frames
|
||||
// frames are the single views that slide in
|
||||
FrameCallback frames[] = { analogClockFrame, digitalClockFrame };
|
||||
|
||||
// how many frames are there?
|
||||
int frameCount = 2;
|
||||
|
||||
// Overlays are statically drawn on top of a frame eg. a clock
|
||||
OverlayCallback overlays[] = { clockOverlay };
|
||||
int overlaysCount = 1;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println();
|
||||
|
||||
// The ESP is capable of rendering 60fps in 80Mhz mode
|
||||
// but that won't give you much time for anything else
|
||||
// run it in 160Mhz mode or just set it to 30 fps
|
||||
ui.setTargetFPS(60);
|
||||
|
||||
// Customize the active and inactive symbol
|
||||
ui.setActiveSymbol(activeSymbol);
|
||||
ui.setInactiveSymbol(inactiveSymbol);
|
||||
|
||||
// You can change this to
|
||||
// TOP, LEFT, BOTTOM, RIGHT
|
||||
ui.setIndicatorPosition(TOP);
|
||||
|
||||
// Defines where the first frame is located in the bar.
|
||||
ui.setIndicatorDirection(LEFT_RIGHT);
|
||||
|
||||
// You can change the transition that is used
|
||||
// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
|
||||
ui.setFrameAnimation(SLIDE_LEFT);
|
||||
|
||||
// Add frames
|
||||
ui.setFrames(frames, frameCount);
|
||||
|
||||
// Add overlays
|
||||
ui.setOverlays(overlays, overlaysCount);
|
||||
|
||||
// Initialising the UI will init the display too.
|
||||
ui.init();
|
||||
|
||||
display.flipScreenVertically();
|
||||
|
||||
unsigned long secsSinceStart = millis();
|
||||
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
|
||||
const unsigned long seventyYears = 2208988800UL;
|
||||
// subtract seventy years:
|
||||
unsigned long epoch = secsSinceStart - seventyYears * SECS_PER_HOUR;
|
||||
setTime(epoch);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
int remainingTimeBudget = ui.update();
|
||||
|
||||
if (remainingTimeBudget > 0) {
|
||||
// You can do some work here
|
||||
// Don't do stuff if you are below your
|
||||
// time budget.
|
||||
delay(remainingTimeBudget);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
const unsigned char activeSymbol[] PROGMEM = {
|
||||
B00000000,
|
||||
B00000000,
|
||||
B00011000,
|
||||
B00100100,
|
||||
B01000010,
|
||||
B01000010,
|
||||
B00100100,
|
||||
B00011000
|
||||
};
|
||||
|
||||
const unsigned char inactiveSymbol[] PROGMEM = {
|
||||
B00000000,
|
||||
B00000000,
|
||||
B00000000,
|
||||
B00000000,
|
||||
B00011000,
|
||||
B00011000,
|
||||
B00000000,
|
||||
B00000000
|
||||
};
|
||||
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
// Include the correct display library
|
||||
// For a connection via I2C using Wire include
|
||||
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
|
||||
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
|
||||
// For a connection via I2C using brzo_i2c (must be installed) include
|
||||
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Brzo.h"
|
||||
// #include "SH1106Brzo.h"
|
||||
// For a connection via SPI include
|
||||
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Spi.h"
|
||||
// #include "SH1106SPi.h"
|
||||
|
||||
// Use the corresponding display class:
|
||||
|
||||
// Initialize the OLED display using SPI
|
||||
// D5 -> CLK
|
||||
// D7 -> MOSI (DOUT)
|
||||
// D0 -> RES
|
||||
// D2 -> DC
|
||||
// D8 -> CS
|
||||
// SSD1306Spi display(D0, D2, D8);
|
||||
// or
|
||||
// SH1106Spi display(D0, D2);
|
||||
|
||||
// Initialize the OLED display using brzo_i2c
|
||||
// D3 -> SDA
|
||||
// D5 -> SCL
|
||||
// SSD1306Brzo display(0x3c, D3, D5);
|
||||
// or
|
||||
// SH1106Brzo display(0x3c, D3, D5);
|
||||
|
||||
// Initialize the OLED display using Wire library
|
||||
SSD1306Wire display(0x3c, D3, D5);
|
||||
// SH1106 display(0x3c, D3, D5);
|
||||
|
||||
// Adapted from Adafruit_SSD1306
|
||||
void drawLines() {
|
||||
for (int16_t i=0; i<display.getWidth(); i+=4) {
|
||||
display.drawLine(0, 0, i, display.getHeight()-1);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
for (int16_t i=0; i<display.getHeight(); i+=4) {
|
||||
display.drawLine(0, 0, display.getWidth()-1, i);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
delay(250);
|
||||
|
||||
display.clear();
|
||||
for (int16_t i=0; i<display.getWidth(); i+=4) {
|
||||
display.drawLine(0, display.getHeight()-1, i, 0);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
for (int16_t i=display.getHeight()-1; i>=0; i-=4) {
|
||||
display.drawLine(0, display.getHeight()-1, display.getWidth()-1, i);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
delay(250);
|
||||
|
||||
display.clear();
|
||||
for (int16_t i=display.getWidth()-1; i>=0; i-=4) {
|
||||
display.drawLine(display.getWidth()-1, display.getHeight()-1, i, 0);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
for (int16_t i=display.getHeight()-1; i>=0; i-=4) {
|
||||
display.drawLine(display.getWidth()-1, display.getHeight()-1, 0, i);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
delay(250);
|
||||
display.clear();
|
||||
for (int16_t i=0; i<display.getHeight(); i+=4) {
|
||||
display.drawLine(display.getWidth()-1, 0, 0, i);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
for (int16_t i=0; i<display.getWidth(); i+=4) {
|
||||
display.drawLine(display.getWidth()-1, 0, i, display.getHeight()-1);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
delay(250);
|
||||
}
|
||||
|
||||
// Adapted from Adafruit_SSD1306
|
||||
void drawRect(void) {
|
||||
for (int16_t i=0; i<display.getHeight()/2; i+=2) {
|
||||
display.drawRect(i, i, display.getWidth()-2*i, display.getHeight()-2*i);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
// Adapted from Adafruit_SSD1306
|
||||
void fillRect(void) {
|
||||
uint8_t color = 1;
|
||||
for (int16_t i=0; i<display.getHeight()/2; i+=3) {
|
||||
display.setColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors
|
||||
display.fillRect(i, i, display.getWidth() - i*2, display.getHeight() - i*2);
|
||||
display.display();
|
||||
delay(10);
|
||||
color++;
|
||||
}
|
||||
// Reset back to WHITE
|
||||
display.setColor(WHITE);
|
||||
}
|
||||
|
||||
// Adapted from Adafruit_SSD1306
|
||||
void drawCircle(void) {
|
||||
for (int16_t i=0; i<display.getHeight(); i+=2) {
|
||||
display.drawCircle(display.getWidth()/2, display.getHeight()/2, i);
|
||||
display.display();
|
||||
delay(10);
|
||||
}
|
||||
delay(1000);
|
||||
display.clear();
|
||||
|
||||
// This will draw the part of the circel in quadrant 1
|
||||
// Quadrants are numberd like this:
|
||||
// 0010 | 0001
|
||||
// ------|-----
|
||||
// 0100 | 1000
|
||||
//
|
||||
display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000001);
|
||||
display.display();
|
||||
delay(200);
|
||||
display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000011);
|
||||
display.display();
|
||||
delay(200);
|
||||
display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000111);
|
||||
display.display();
|
||||
delay(200);
|
||||
display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00001111);
|
||||
display.display();
|
||||
}
|
||||
|
||||
void printBuffer(void) {
|
||||
// Initialize the log buffer
|
||||
// allocate memory to store 8 lines of text and 30 chars per line.
|
||||
display.setLogBuffer(5, 30);
|
||||
|
||||
// Some test data
|
||||
const char* test[] = {
|
||||
"Hello",
|
||||
"World" ,
|
||||
"----",
|
||||
"Show off",
|
||||
"how",
|
||||
"the log buffer",
|
||||
"is",
|
||||
"working.",
|
||||
"Even",
|
||||
"scrolling is",
|
||||
"working"
|
||||
};
|
||||
|
||||
for (uint8_t i = 0; i < 11; i++) {
|
||||
display.clear();
|
||||
// Print to the screen
|
||||
display.println(test[i]);
|
||||
// Draw it to the internal screen buffer
|
||||
display.drawLogBuffer(0, 0);
|
||||
// Display it on the screen
|
||||
display.display();
|
||||
delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
display.init();
|
||||
|
||||
// display.flipScreenVertically();
|
||||
|
||||
display.setContrast(255);
|
||||
|
||||
drawLines();
|
||||
delay(1000);
|
||||
display.clear();
|
||||
|
||||
drawRect();
|
||||
delay(1000);
|
||||
display.clear();
|
||||
|
||||
fillRect();
|
||||
delay(1000);
|
||||
display.clear();
|
||||
|
||||
drawCircle();
|
||||
delay(1000);
|
||||
display.clear();
|
||||
|
||||
printBuffer();
|
||||
delay(1000);
|
||||
display.clear();
|
||||
}
|
||||
|
||||
void loop() { }
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
// WiFi includes
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
// OTA Includes
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
const char *ssid = "[Your SSID]";
|
||||
const char *password = "[Your Password]";
|
||||
|
||||
|
||||
// Include the correct display library
|
||||
// For a connection via I2C using Wire include
|
||||
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
|
||||
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
|
||||
// For a connection via I2C using brzo_i2c (must be installed) include
|
||||
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Brzo.h"
|
||||
// #include "SH1106Brzo.h"
|
||||
// For a connection via SPI include
|
||||
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Spi.h"
|
||||
// #include "SH1106SPi.h"
|
||||
|
||||
// Use the corresponding display class:
|
||||
|
||||
// Initialize the OLED display using SPI
|
||||
// D5 -> CLK
|
||||
// D7 -> MOSI (DOUT)
|
||||
// D0 -> RES
|
||||
// D2 -> DC
|
||||
// D8 -> CS
|
||||
// SSD1306Spi display(D0, D2, D8);
|
||||
// or
|
||||
// SH1106Spi display(D0, D2);
|
||||
|
||||
// Initialize the OLED display using brzo_i2c
|
||||
// D3 -> SDA
|
||||
// D5 -> SCL
|
||||
// SSD1306Brzo display(0x3c, D3, D5);
|
||||
// or
|
||||
// SH1106Brzo display(0x3c, D3, D5);
|
||||
|
||||
// Initialize the OLED display using Wire library
|
||||
SSD1306Wire display(0x3c, D3, D5);
|
||||
// SH1106 display(0x3c, D3, D5);
|
||||
|
||||
|
||||
void setup() {
|
||||
WiFi.begin ( ssid, password );
|
||||
|
||||
// Wait for connection
|
||||
while ( WiFi.status() != WL_CONNECTED ) {
|
||||
delay ( 10 );
|
||||
}
|
||||
|
||||
display.init();
|
||||
display.flipScreenVertically();
|
||||
display.setContrast(255);
|
||||
|
||||
ArduinoOTA.begin();
|
||||
ArduinoOTA.onStart([]() {
|
||||
display.clear();
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
|
||||
display.drawString(display.getWidth()/2, display.getHeight()/2 - 10, "OTA Update");
|
||||
display.display();
|
||||
});
|
||||
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
display.drawProgressBar(4, 32, 120, 8, progress / (total / 100) );
|
||||
display.display();
|
||||
});
|
||||
|
||||
ArduinoOTA.onEnd([]() {
|
||||
display.clear();
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
|
||||
display.drawString(display.getWidth()/2, display.getHeight()/2, "Restart");
|
||||
display.display();
|
||||
});
|
||||
|
||||
// Align text vertical/horizontal center
|
||||
display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
display.drawString(display.getWidth()/2, display.getHeight()/2, "Ready for OTA:\n" + WiFi.localIP().toString());
|
||||
display.display();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
// Include the correct display library
|
||||
|
||||
// For a connection via I2C using the Arduino Wire include:
|
||||
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
#include "SSD1306Wire.h" // legacy: #include "SSD1306.h"
|
||||
// OR #include "SH1106Wire.h" // legacy: #include "SH1106.h"
|
||||
|
||||
// For a connection via I2C using brzo_i2c (must be installed) include:
|
||||
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Brzo.h"
|
||||
// OR #include "SH1106Brzo.h"
|
||||
|
||||
// For a connection via SPI include:
|
||||
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Spi.h"
|
||||
// OR #include "SH1106SPi.h"
|
||||
|
||||
|
||||
// Optionally include custom images
|
||||
#include "images.h"
|
||||
|
||||
|
||||
// Initialize the OLED display using Arduino Wire:
|
||||
SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h
|
||||
// SSD1306Wire display(0x3c, D3, D5); // ADDRESS, SDA, SCL - If not, they can be specified manually.
|
||||
// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, OLEDDISPLAY_GEOMETRY - Extra param required for 128x32 displays.
|
||||
// SH1106 display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
|
||||
|
||||
// Initialize the OLED display using brzo_i2c:
|
||||
// SSD1306Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL
|
||||
// or
|
||||
// SH1106Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL
|
||||
|
||||
// Initialize the OLED display using SPI:
|
||||
// D5 -> CLK
|
||||
// D7 -> MOSI (DOUT)
|
||||
// D0 -> RES
|
||||
// D2 -> DC
|
||||
// D8 -> CS
|
||||
// SSD1306Spi display(D0, D2, D8); // RES, DC, CS
|
||||
// or
|
||||
// SH1106Spi display(D0, D2); // RES, DC
|
||||
|
||||
|
||||
#define DEMO_DURATION 3000
|
||||
typedef void (*Demo)(void);
|
||||
|
||||
int demoMode = 0;
|
||||
int counter = 1;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
|
||||
// Initialising the UI will init the display too.
|
||||
display.init();
|
||||
|
||||
display.flipScreenVertically();
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
|
||||
}
|
||||
|
||||
void drawFontFaceDemo() {
|
||||
// Font Demo1
|
||||
// create more fonts at http://oleddisplay.squix.ch/
|
||||
display.setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
display.drawString(0, 0, "Hello world");
|
||||
display.setFont(ArialMT_Plain_16);
|
||||
display.drawString(0, 10, "Hello world");
|
||||
display.setFont(ArialMT_Plain_24);
|
||||
display.drawString(0, 26, "Hello world");
|
||||
}
|
||||
|
||||
void drawTextFlowDemo() {
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
display.setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display.drawStringMaxWidth(0, 0, 128,
|
||||
"Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore." );
|
||||
}
|
||||
|
||||
void drawTextAlignmentDemo() {
|
||||
// Text alignment demo
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
|
||||
// The coordinates define the left starting point of the text
|
||||
display.setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display.drawString(0, 10, "Left aligned (0,10)");
|
||||
|
||||
// The coordinates define the center of the text
|
||||
display.setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display.drawString(64, 22, "Center aligned (64,22)");
|
||||
|
||||
// The coordinates define the right end of the text
|
||||
display.setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||
display.drawString(128, 33, "Right aligned (128,33)");
|
||||
}
|
||||
|
||||
void drawRectDemo() {
|
||||
// Draw a pixel at given position
|
||||
for (int i = 0; i < 10; i++) {
|
||||
display.setPixel(i, i);
|
||||
display.setPixel(10 - i, i);
|
||||
}
|
||||
display.drawRect(12, 12, 20, 20);
|
||||
|
||||
// Fill the rectangle
|
||||
display.fillRect(14, 14, 17, 17);
|
||||
|
||||
// Draw a line horizontally
|
||||
display.drawHorizontalLine(0, 40, 20);
|
||||
|
||||
// Draw a line horizontally
|
||||
display.drawVerticalLine(40, 0, 20);
|
||||
}
|
||||
|
||||
void drawCircleDemo() {
|
||||
for (int i=1; i < 8; i++) {
|
||||
display.setColor(WHITE);
|
||||
display.drawCircle(32, 32, i*3);
|
||||
if (i % 2 == 0) {
|
||||
display.setColor(BLACK);
|
||||
}
|
||||
display.fillCircle(96, 32, 32 - i* 3);
|
||||
}
|
||||
}
|
||||
|
||||
void drawProgressBarDemo() {
|
||||
int progress = (counter / 5) % 100;
|
||||
// draw the progress bar
|
||||
display.drawProgressBar(0, 32, 120, 10, progress);
|
||||
|
||||
// draw the percentage as String
|
||||
display.setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display.drawString(64, 15, String(progress) + "%");
|
||||
}
|
||||
|
||||
void drawImageDemo() {
|
||||
// see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html
|
||||
// on how to create xbm files
|
||||
display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
|
||||
}
|
||||
|
||||
Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo};
|
||||
int demoLength = (sizeof(demos) / sizeof(Demo));
|
||||
long timeSinceLastModeSwitch = 0;
|
||||
|
||||
void loop() {
|
||||
// clear the display
|
||||
display.clear();
|
||||
// draw the current demo method
|
||||
demos[demoMode]();
|
||||
|
||||
display.setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||
display.drawString(10, 128, String(millis()));
|
||||
// write the buffer to the display
|
||||
display.display();
|
||||
|
||||
if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) {
|
||||
demoMode = (demoMode + 1) % demoLength;
|
||||
timeSinceLastModeSwitch = millis();
|
||||
}
|
||||
counter++;
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#define WiFi_Logo_width 60
|
||||
#define WiFi_Logo_height 36
|
||||
const uint8_t WiFi_Logo_bits[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
|
||||
0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
|
||||
0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
|
||||
0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
|
||||
0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
|
||||
0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
|
||||
0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
|
||||
0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
|
||||
0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
|
||||
0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
|
||||
0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
|
||||
0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
|
||||
0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
|
||||
0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
// Include the correct display library
|
||||
// For a connection via I2C using Wire include
|
||||
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
|
||||
#include "images.h"
|
||||
|
||||
// Initialize the OLED display using Wire library
|
||||
SSD1306Wire display(0x3c, D3, D5);
|
||||
SSD1306Wire display2(0x3c, D1, D2);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
|
||||
// Initialising the UI will init the display too.
|
||||
display.init();
|
||||
display2.init();
|
||||
|
||||
// This will make sure that multiple instances of a display driver
|
||||
// running on different ports will work together transparently
|
||||
display.setI2cAutoInit(true);
|
||||
display2.setI2cAutoInit(true);
|
||||
|
||||
display.flipScreenVertically();
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
display.setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
display2.flipScreenVertically();
|
||||
display2.setFont(ArialMT_Plain_10);
|
||||
display2.setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
display.clear();
|
||||
display.drawString(0, 0, "Hello world: " + String(millis()));
|
||||
display.display();
|
||||
|
||||
display2.clear();
|
||||
display2.drawString(0, 0, "Hello world: " + String(millis()));
|
||||
display2.display();
|
||||
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#define WiFi_Logo_width 60
|
||||
#define WiFi_Logo_height 36
|
||||
const uint8_t WiFi_Logo_bits[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
|
||||
0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
|
||||
0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
|
||||
0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
|
||||
0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
|
||||
0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
|
||||
0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
|
||||
0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
|
||||
0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
|
||||
0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
|
||||
0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
|
||||
0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
|
||||
0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
|
||||
0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
// Include the correct display library
|
||||
// For a connection via I2C using Wire include
|
||||
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
|
||||
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
|
||||
// For a connection via I2C using brzo_i2c (must be installed) include
|
||||
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Brzo.h"
|
||||
// #include "SH1106Brzo.h"
|
||||
// For a connection via SPI include
|
||||
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
|
||||
// #include "SSD1306Spi.h"
|
||||
// #include "SH1106SPi.h"
|
||||
|
||||
// Include the UI lib
|
||||
#include "OLEDDisplayUi.h"
|
||||
|
||||
// Include custom images
|
||||
|
||||
#include "images.h"
|
||||
|
||||
// Use the corresponding display class:
|
||||
|
||||
// Initialize the OLED display using SPI
|
||||
// D5 -> CLK
|
||||
// D7 -> MOSI (DOUT)
|
||||
// D0 -> RES
|
||||
// D2 -> DC
|
||||
// D8 -> CS
|
||||
// SSD1306Spi display(D0, D2, D8);
|
||||
// or
|
||||
// SH1106Spi display(D0, D2);
|
||||
|
||||
// Initialize the OLED display using brzo_i2c
|
||||
// D3 -> SDA
|
||||
// D5 -> SCL
|
||||
// SSD1306Brzo display(0x3c, D3, D5);
|
||||
// or
|
||||
// SH1106Brzo display(0x3c, D3, D5);
|
||||
|
||||
// Initialize the OLED display using Wire library
|
||||
SSD1306Wire display(0x3c, D3, D5);
|
||||
// SH1106Wire display(0x3c, D3, D5);
|
||||
|
||||
OLEDDisplayUi ui ( &display );
|
||||
|
||||
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
|
||||
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
display->drawString(128, 0, String(millis()));
|
||||
}
|
||||
|
||||
void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
|
||||
// draw an xbm image.
|
||||
// Please note that everything that should be transitioned
|
||||
// needs to be drawn relative to x and y
|
||||
|
||||
display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
|
||||
}
|
||||
|
||||
void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
|
||||
// Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file
|
||||
// Besides the default fonts there will be a program to convert TrueType fonts into this format
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
display->drawString(0 + x, 10 + y, "Arial 10");
|
||||
|
||||
display->setFont(ArialMT_Plain_16);
|
||||
display->drawString(0 + x, 20 + y, "Arial 16");
|
||||
|
||||
display->setFont(ArialMT_Plain_24);
|
||||
display->drawString(0 + x, 34 + y, "Arial 24");
|
||||
}
|
||||
|
||||
void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
|
||||
// Text alignment demo
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
|
||||
// The coordinates define the left starting point of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->drawString(0 + x, 11 + y, "Left aligned (0,10)");
|
||||
|
||||
// The coordinates define the center of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->drawString(64 + x, 22 + y, "Center aligned (64,22)");
|
||||
|
||||
// The coordinates define the right end of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||
display->drawString(128 + x, 33 + y, "Right aligned (128,33)");
|
||||
}
|
||||
|
||||
void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
|
||||
// Demo for drawStringMaxWidth:
|
||||
// with the third parameter you can define the width after which words will be wrapped.
|
||||
// Currently only spaces and "-" are allowed for wrapping
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore.");
|
||||
}
|
||||
|
||||
void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
|
||||
|
||||
}
|
||||
|
||||
// This array keeps function pointers to all frames
|
||||
// frames are the single views that slide in
|
||||
FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 };
|
||||
|
||||
// how many frames are there?
|
||||
int frameCount = 5;
|
||||
|
||||
// Overlays are statically drawn on top of a frame eg. a clock
|
||||
OverlayCallback overlays[] = { msOverlay };
|
||||
int overlaysCount = 1;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
// The ESP is capable of rendering 60fps in 80Mhz mode
|
||||
// but that won't give you much time for anything else
|
||||
// run it in 160Mhz mode or just set it to 30 fps
|
||||
ui.setTargetFPS(60);
|
||||
|
||||
// Customize the active and inactive symbol
|
||||
ui.setActiveSymbol(activeSymbol);
|
||||
ui.setInactiveSymbol(inactiveSymbol);
|
||||
|
||||
// You can change this to
|
||||
// TOP, LEFT, BOTTOM, RIGHT
|
||||
ui.setIndicatorPosition(BOTTOM);
|
||||
|
||||
// Defines where the first frame is located in the bar.
|
||||
ui.setIndicatorDirection(LEFT_RIGHT);
|
||||
|
||||
// You can change the transition that is used
|
||||
// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
|
||||
ui.setFrameAnimation(SLIDE_LEFT);
|
||||
|
||||
// Add frames
|
||||
ui.setFrames(frames, frameCount);
|
||||
|
||||
// Add overlays
|
||||
ui.setOverlays(overlays, overlaysCount);
|
||||
|
||||
// Initialising the UI will init the display too.
|
||||
ui.init();
|
||||
|
||||
display.flipScreenVertically();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
int remainingTimeBudget = ui.update();
|
||||
|
||||
if (remainingTimeBudget > 0) {
|
||||
// You can do some work here
|
||||
// Don't do stuff if you are below your
|
||||
// time budget.
|
||||
delay(remainingTimeBudget);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#define WiFi_Logo_width 60
|
||||
#define WiFi_Logo_height 36
|
||||
const uint8_t WiFi_Logo_bits[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
|
||||
0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
|
||||
0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
|
||||
0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
|
||||
0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
|
||||
0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
|
||||
0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
|
||||
0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
|
||||
0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
|
||||
0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
|
||||
0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
|
||||
0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
|
||||
0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
|
||||
0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const uint8_t activeSymbol[] PROGMEM = {
|
||||
B00000000,
|
||||
B00000000,
|
||||
B00011000,
|
||||
B00100100,
|
||||
B01000010,
|
||||
B01000010,
|
||||
B00100100,
|
||||
B00011000
|
||||
};
|
||||
|
||||
const uint8_t inactiveSymbol[] PROGMEM = {
|
||||
B00000000,
|
||||
B00000000,
|
||||
B00000000,
|
||||
B00000000,
|
||||
B00011000,
|
||||
B00011000,
|
||||
B00000000,
|
||||
B00000000
|
||||
};
|
||||
100
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/keywords.txt
Normal file
@@ -0,0 +1,100 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map List
|
||||
#######################################
|
||||
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
INVERSE LITERAL1
|
||||
|
||||
TEXT_ALIGN_LEFT LITERAL1
|
||||
TEXT_ALIGN_RIGHT LITERAL1
|
||||
TEXT_ALIGN_CENTER LITERAL1
|
||||
TEXT_ALIGN_CENTER_BOTH LITERAL1
|
||||
|
||||
GEOMETRY_128_64 LITERAL1
|
||||
GEOMETRY_128_32 LITERAL1
|
||||
GEOMETRY_RAWMODE LITERAL1
|
||||
|
||||
ArialMT_Plain_10 LITERAL1
|
||||
ArialMT_Plain_16 LITERAL1
|
||||
ArialMT_Plain_24 LITERAL1
|
||||
|
||||
SLIDE_UP LITERAL1
|
||||
SLIDE_DOWN LITERAL1
|
||||
SLIDE_LEFT LITERAL1
|
||||
SLIDE_RIGHT LITERAL1
|
||||
|
||||
TOP LITERAL1
|
||||
RIGHT LITERAL1
|
||||
BOTTOM LITERAL1
|
||||
LEFT LITERAL1
|
||||
|
||||
LEFT_RIGHT LITERAL1
|
||||
RIGHT_LEFT LITERAL1
|
||||
|
||||
IN_TRANSITION LITERAL1
|
||||
FIXED LITERAL1
|
||||
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
OLEDDisplay KEYWORD1
|
||||
OLEDDisplayUi KEYWORD1
|
||||
|
||||
SH1106Wire KEYWORD1
|
||||
SH1106Brzo KEYWORD1
|
||||
SH1106Spi KEYWORD1
|
||||
|
||||
SSD1306Wire KEYWORD1
|
||||
SSD1306Brzo KEYWORD1
|
||||
SSD1306I2C KEYWORD1
|
||||
SSD1306Spi KEYWORD1
|
||||
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
allocateBuffer KEYWORD2
|
||||
init KEYWORD2
|
||||
resetDisplay KEYWORD2
|
||||
setColor KEYWORD2
|
||||
getColor KEYWORD2
|
||||
setPixel KEYWORD2
|
||||
setPixelColor KEYWORD2
|
||||
clearPixel KEYWORD2
|
||||
drawLine KEYWORD2
|
||||
drawRect KEYWORD2
|
||||
fillRect KEYWORD2
|
||||
drawCircle KEYWORD2
|
||||
drawCircleQuads KEYWORD2
|
||||
fillCircle KEYWORD2
|
||||
fillRing KEYWORD2
|
||||
drawHorizontalLine KEYWORD2
|
||||
drawVerticalLine KEYWORD2
|
||||
drawProgressBar KEYWORD2
|
||||
drawFastImage KEYWORD2
|
||||
drawXbm KEYWORD2
|
||||
drawIco16x16 KEYWORD2
|
||||
drawString KEYWORD2
|
||||
drawStringMaxWidth KEYWORD2
|
||||
getStringWidth KEYWORD2
|
||||
setTextAlignment KEYWORD2
|
||||
setFont KEYWORD2
|
||||
setFontTableLookupFunction KEYWORD2
|
||||
displayOn KEYWORD2
|
||||
displayOff KEYWORD2
|
||||
invertDisplay KEYWORD2
|
||||
normalDisplay KEYWORD2
|
||||
setContrast KEYWORD2
|
||||
setBrightness KEYWORD2
|
||||
resetOrientation KEYWORD2
|
||||
flipScreenVertically KEYWORD2
|
||||
mirrorScreen KEYWORD2
|
||||
display KEYWORD2
|
||||
setLogBuffer KEYWORD2
|
||||
drawLogBuffer KEYWORD2
|
||||
getWidth KEYWORD2
|
||||
getHeight KEYWORD2
|
||||
29
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/library.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "ESP8266 and ESP32 OLED driver for SSD1306 displays",
|
||||
"version": "4.2.0",
|
||||
"keywords": "ssd1306, oled, display, i2c",
|
||||
"description": "I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS",
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/ThingPulse/esp8266-oled-ssd1306"
|
||||
},
|
||||
"authors":
|
||||
[
|
||||
{
|
||||
"name": "Daniel Eichhorn, ThingPulse",
|
||||
"email": "squix78@gmail.com",
|
||||
"url": "https://thingpulse.com"
|
||||
},
|
||||
{
|
||||
"name": "Fabrice Weinberg",
|
||||
"email": "fabrice@weinberg.me"
|
||||
}
|
||||
],
|
||||
"frameworks": "arduino",
|
||||
"platforms": [
|
||||
"espressif8266",
|
||||
"espressif32",
|
||||
"nordicnrf52"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
name=ESP8266 and ESP32 OLED driver for SSD1306 displays
|
||||
version=4.2.0
|
||||
author=ThingPulse, Fabrice Weinberg
|
||||
maintainer=ThingPulse <info@thingpulse.com>
|
||||
sentence=I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS
|
||||
paragraph=The following geometries are currently supported: 128x64, 128x32, 64x48. The init sequence was inspired by Adafruit's library for the same display.
|
||||
category=Display
|
||||
url=https://github.com/ThingPulse/esp8266-oled-ssd1306
|
||||
architectures=esp8266,esp32
|
||||
24
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/license
Normal file
@@ -0,0 +1,24 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 by Daniel Eichhorn
|
||||
Copyright (c) 2016 by Fabrice Weinberg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
See more at http://blog.squix.ch
|
||||
19
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/platformio.ini
Normal file
@@ -0,0 +1,19 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; http://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:d1_mini]
|
||||
platform = espressif8266
|
||||
board = d1_mini
|
||||
framework = arduino
|
||||
upload_speed = 921600
|
||||
board_build.f_cpu = 160000000L
|
||||
upload_port = /dev/cu.SLAB_USBtoUART
|
||||
monitor_port = /dev/cu.SLAB_USBtoUART
|
||||
lib_deps =
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,664 @@
|
||||
<!--The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 by Xavier Grosjean
|
||||
|
||||
Based on work
|
||||
Copyright (c) 2016 by Daniel Eichhorn
|
||||
Copyright (c) 2016 by Fabrice Weinberg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-->
|
||||
<!--
|
||||
Standalone page to draw icons in matrix and generates the map definition with the format required by
|
||||
library for OLED SD1306 screen: https://github.com/squix78/esp8266-oled-ssd1306
|
||||
100% quick and dirty vanilla ECS6, no framework or library was injured for this project.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
#form, #chars table {
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
margin-top: 5px;
|
||||
}
|
||||
#chars table, tr, td, th {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#chars td, th {
|
||||
border: 1px solid #CCC;
|
||||
width: 12px;
|
||||
height: 15px;
|
||||
}
|
||||
#chars th[action] {
|
||||
cursor:pointer;
|
||||
}
|
||||
#chars th[action="up"], #chars th[action="down"], #chars th[action="left"], #chars th[action="right"] {
|
||||
font-size: 10px;
|
||||
}
|
||||
#chars td.on {
|
||||
background-color: #00F;
|
||||
}
|
||||
|
||||
#addChar, #generate {
|
||||
display: none;
|
||||
}
|
||||
body.started #addChar, body.started #generate {
|
||||
display: inline;
|
||||
}
|
||||
body.started #create {
|
||||
display: none;
|
||||
}
|
||||
#page {
|
||||
position:relative;
|
||||
}
|
||||
#page>div {
|
||||
position: relative;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#output {
|
||||
margin-left: 20px;
|
||||
margin-top: 12px;
|
||||
font-size:12px;
|
||||
user-select: all;
|
||||
-moz-user-select: all;
|
||||
max-width:60%;
|
||||
}
|
||||
|
||||
#header {
|
||||
margin-top: 10px;
|
||||
}
|
||||
#inputText {
|
||||
width: 100%;
|
||||
height:120px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="form">
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td>Font array name:</td><td><input placeholder="Font array name" type="text" id="name" value="My_Font"/></td>
|
||||
<td width="75%" rowspan="5">
|
||||
<textarea id="inputText" placeholder="Or paste a char array font definition here">
|
||||
const uint8_t My_Font[] PROGMEM = {
|
||||
0x0A, // Width: 10
|
||||
0x0D, // Height: 13
|
||||
0x01, // First char: 1
|
||||
0x02, // Number of chars: 2
|
||||
// Jump Table:
|
||||
0x00, 0x00, 0x13, 0x0A, // 1
|
||||
0x00, 0x13, 0x14, 0x0A, // 2
|
||||
// Font Data:
|
||||
0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0xC0, // 1
|
||||
0x00, 0x0C, 0x80, 0x0B, 0x70, 0x08, 0x0C, 0x08, 0xF3, 0x0A, 0xF3, 0x0A, 0x0C, 0x08, 0x70, 0x08, 0x80, 0x0B, 0x00, 0x0C, // 2
|
||||
};
|
||||
</textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>First char code:</td><td><input placeholder="First char code" type="number" id="code" value="1" min="1"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Width:</td><td><input placeholder="Font width" type="number" id="width" value="10"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Height:</td><td><input placeholder="Font height" type="number" id="height" value="13" max="64"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"> </td> <!--slightly improves layout-->
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<button id="create">Create</button>
|
||||
<button id="shiftUp">Shift all up</button>
|
||||
<button id="generate">Generate</button>
|
||||
<button id="savetoFile">Save To File</button>
|
||||
</td>
|
||||
<td><input type="file" id="fileinput" /> <button id="parse">Parse</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
</div>
|
||||
<div id="page">
|
||||
<div id="charxContainer" ondragstart="return false;">
|
||||
<div id="chars"></div><br/>
|
||||
<button id="addChar">Add a character</button>
|
||||
</div>
|
||||
<div id="output">
|
||||
<div class="output" id="header"> </div>
|
||||
<div class="output" id="jump"> </div>
|
||||
<div class="output" id="data"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
let font;
|
||||
|
||||
class Font {
|
||||
constructor() {
|
||||
this.height = parseInt(document.getElementById('height').value);
|
||||
this.width = parseInt(document.getElementById('width').value);
|
||||
this.currentCharCode = parseInt(document.getElementById('code').value);
|
||||
this.fontContainer = document.getElementById('chars');
|
||||
this.bytesForHeight = (1 + ((this.height - 1) >> 3)); // number of bytes needed for this font height
|
||||
document.body.className = "started";
|
||||
document.getElementById('height').disabled = true;
|
||||
document.getElementById('width').disabled = true;
|
||||
}
|
||||
|
||||
// Should we have a Char and a Pixel class ? not sure it's worth it.
|
||||
// Let's use the DOM to store everything for now
|
||||
|
||||
static updateCaption(element, code) {
|
||||
element.textContent = `Char #${code}`;
|
||||
}
|
||||
|
||||
// Add a pixel matrix to draw a new character
|
||||
// jumpaData not used for now: some day, use the last byte for optimisation
|
||||
addChar(jumpData, charData) {
|
||||
let charContainer = this.fontContainer.appendChild(document.createElement("table"));
|
||||
charContainer.setAttribute("code", this.currentCharCode);
|
||||
let caption = charContainer.appendChild(document.createElement("caption"));
|
||||
Font.updateCaption(caption, this.currentCharCode);
|
||||
let header = charContainer.appendChild(document.createElement("tr"));
|
||||
header.innerHTML = '<th title="Delete this char" action="delete">✗</th>'
|
||||
+ '<th title="Add a char above" action="add">+</th>'
|
||||
+ '<th title="Shift pixels to the left" action="left">←</th>'
|
||||
+ '<th title="Shift pixels down" action="down">↓</th>'
|
||||
+ '<th title="Shift pixels up" action="up">↑</th>'
|
||||
+ '<th title="Shift pixels to the right" action="right">→</th>'
|
||||
+ '<th title="Copy from another character" action="copy">©</th>'
|
||||
+ '<th title="Reset all pixels" action="clean">∅</th>';
|
||||
// If data is provided, decode it to pixel initialization friendly structure
|
||||
let pixelInit = [];
|
||||
if (charData && charData.length) {
|
||||
// charData byte count needs to be a multiple of bytesForHeight. End bytes with value 0 may have been trimmed
|
||||
let missingBytes = charData.length % this.bytesForHeight;
|
||||
for(let b = 0; b < missingBytes ; b++) {
|
||||
charData.push(0);
|
||||
}
|
||||
while(charData.length) {
|
||||
let row = charData.splice(0, this.bytesForHeight).reverse();
|
||||
let pixelRow = [];
|
||||
for (let b = 0; b < row.length; b++) {
|
||||
let mask = 0x80;
|
||||
let byte = row[b];
|
||||
for (let bit = 0; bit < 8; bit++) {
|
||||
if (byte & mask) {
|
||||
pixelRow.push(1);
|
||||
} else {
|
||||
pixelRow.push(0);
|
||||
}
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
pixelRow.splice(0, pixelRow.length - this.height);
|
||||
//Font.output('data', pixelRow);
|
||||
pixelInit.push(pixelRow.reverse());
|
||||
}
|
||||
}
|
||||
|
||||
for(let r = 0; r < this.height; r++) {
|
||||
let rowContainer = charContainer.appendChild(document.createElement("tr"));
|
||||
for(let c = 0; c < this.width; c++) {
|
||||
let pixContainer = rowContainer.appendChild(document.createElement("td"));
|
||||
pixContainer.setAttribute('action', 'toggle');
|
||||
// If there is some init data, set the pixel accordingly
|
||||
if (pixelInit.length && pixelInit[c]) {
|
||||
if (pixelInit[c][r]) {
|
||||
pixContainer.className = 'on';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.currentCharCode++;
|
||||
return charContainer;
|
||||
}
|
||||
|
||||
static togglePixel(pixel) {
|
||||
pixel.className = pixel.className === 'on' ? '': 'on';
|
||||
}
|
||||
|
||||
// Return anInt as hex string
|
||||
static toHexString(aByte) {
|
||||
let zero = aByte < 16?'0':'';
|
||||
return `0x${zero}${aByte.toString(16).toUpperCase()}`
|
||||
}
|
||||
|
||||
// Return least significant byte as hex string
|
||||
static getLsB(anInt) {
|
||||
return Font.toHexString(anInt & 0xFF);
|
||||
}
|
||||
// Return most significant byte as hex string
|
||||
static getMsB(anInt) {
|
||||
return Font.toHexString(anInt>>>8);
|
||||
}
|
||||
|
||||
static output(targetId, msg) {
|
||||
let output = document.getElementById(targetId);
|
||||
let line = output.appendChild(document.createElement('div'));
|
||||
line.textContent = msg;
|
||||
}
|
||||
static emptyChars() {
|
||||
document.getElementById('chars').textContent = '';
|
||||
}
|
||||
static emptyOutput() {
|
||||
document.getElementById('header').textContent = '';
|
||||
document.getElementById('jump').textContent = '';
|
||||
document.getElementById('data').textContent = '';
|
||||
}
|
||||
|
||||
saveFile() {
|
||||
let filename = document.getElementById('name').value.replace(/[^a-zA-Z0-9_$]/g, '_') + ".h";
|
||||
let data = document.getElementById("output").innerText;
|
||||
if(data.length < 10) return;
|
||||
let blobObject = new Blob([data], {type:'text/plain'});
|
||||
|
||||
if(window.navigator.msSaveBlob) {
|
||||
window.navigator.msSaveBlob(blobObject, filename);
|
||||
} else {
|
||||
let a = document.createElement("a");
|
||||
a.setAttribute("href", URL.createObjectURL(blobObject));
|
||||
a.setAttribute("download", filename);
|
||||
if (document.createEvent) {
|
||||
let event = document.createEvent('MouseEvents');
|
||||
event.initEvent('click', true, true);
|
||||
a.dispatchEvent(event);
|
||||
} else {
|
||||
a.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read from the <td> css class the pixels that need to be on
|
||||
// generates the jump table and font data
|
||||
generate() {
|
||||
// this.width -= 3; // hack to narrow an existing font
|
||||
Font.emptyOutput();
|
||||
let chars = this.fontContainer.getElementsByTagName('table');
|
||||
let firstCharCode = parseInt(document.getElementById('code').value);
|
||||
let name = document.getElementById('name').value.replace(/[^a-zA-Z0-9_$]/g, '_');
|
||||
|
||||
let bits2add = this.bytesForHeight*8 - this.height; // number of missing bits to fill leftmost byte
|
||||
let charCount = chars.length;
|
||||
let charAddr = 0;
|
||||
// Comments are used when parsing back a generated font
|
||||
Font.output('jump', ' // Jump Table:');
|
||||
Font.output('data', ' // Font Data:');
|
||||
// Browse each character
|
||||
for(let ch = 0; ch < charCount; ch++) {
|
||||
// Fix renumbering in case first char code was modified
|
||||
Font.updateCaption(chars[ch].getElementsByTagName('caption')[0], ch + firstCharCode);
|
||||
let charBytes = [];
|
||||
let charCode = ch + firstCharCode;
|
||||
let rows = chars[ch].getElementsByTagName('tr');
|
||||
let charNotZero = false;
|
||||
// Browse each column
|
||||
for(let col = 0; col < this.width ; col++) {
|
||||
let bits = ""; // using string because js uses 32b ints when performing bit operations
|
||||
// Browse each row starting from the bottom one, going up, and accumulate pixels in
|
||||
// a string: this rotates the glyph
|
||||
// Beware, row 0 is table headers.
|
||||
for(let r = rows.length-1; r >=1 ; r--) {
|
||||
let pixelState = rows[r].children[col].className;
|
||||
bits += (pixelState === 'on' ? '1': '0');
|
||||
}
|
||||
// Need to complete missing bits to have a sizeof byte multiple number of bits
|
||||
for(let b = 0; b < bits2add; b++) {
|
||||
bits = '0' + bits;
|
||||
}
|
||||
// Font.output('data', ` // ${bits}`); // Debugging help: rotated bitmap
|
||||
|
||||
// read bytes from the end
|
||||
for(let b = bits.length - 1; b >= 7; b -= 8) {
|
||||
//Font.output('data', ` // ${bits.substr(b-7, 8)}`); // Debugging help: rotated bitmap
|
||||
let byte = parseInt(bits.substr(b-7, 8), 2);
|
||||
if (byte !== 0) {
|
||||
charNotZero = true;
|
||||
}
|
||||
charBytes.push(Font.toHexString(byte));
|
||||
}
|
||||
}
|
||||
// Compute the used width of the character:
|
||||
// rightmost column with pixels plus one
|
||||
// one column is spread over several bytes...
|
||||
let charWidth = 0;
|
||||
for(let i=charBytes.length - 1; i >= 0; i-=this.bytesForHeight) {
|
||||
let sum = 0;
|
||||
for(let j=0; j < this.bytesForHeight; j++) {
|
||||
sum += parseInt(charBytes[i - j], 16);
|
||||
}
|
||||
if(sum !== 0) {
|
||||
charWidth = (i + 1)/this.bytesForHeight + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Memory optim: Remove bytes with value 0 at the end of the array.
|
||||
while(parseInt(charBytes[charBytes.length-1], 16) === 0 && charBytes.length > 1) {
|
||||
charBytes.pop();
|
||||
}
|
||||
if (charNotZero) {
|
||||
Font.output('data', ` ${charBytes.join(', ')}, // ${charCode}`);
|
||||
Font.output('jump', ` ${Font.getMsB(charAddr)}, ${Font.getLsB(charAddr)}, ${Font.toHexString(charBytes.length)}, ${Font.toHexString(charWidth)}, // ${charCode} `);
|
||||
charAddr += charBytes.length;
|
||||
} else {
|
||||
Font.output('jump', ` 0xFF, 0xFF, 0x00, ${Font.toHexString(this.width)}, // ${charCode} `);
|
||||
}
|
||||
}
|
||||
Font.output('data', '};');
|
||||
|
||||
Font.output('header', "// Font generated or edited with the glyphEditor");
|
||||
Font.output('header', `const uint8_t ${name}[] PROGMEM = {`);
|
||||
// Comments are used when parsing back a generated font
|
||||
Font.output('header', ` ${Font.toHexString(this.width)}, // Width: ${this.width}`);
|
||||
Font.output('header', ` ${Font.toHexString(this.height)}, // Height: ${this.height}`);
|
||||
Font.output('header', ` ${Font.toHexString(firstCharCode)}, // First char: ${firstCharCode}`);
|
||||
Font.output('header', ` ${Font.toHexString(charCount)}, // Number of chars: ${charCount}`);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('fileinput').addEventListener('change', function(e) {
|
||||
let f = e.target.files[0];
|
||||
if (f) {
|
||||
let r = new FileReader();
|
||||
r.onload = function(e) {
|
||||
let contents = e.target.result;
|
||||
alert( "Got the file.\n"
|
||||
+"name: " + f.name + "\n"
|
||||
+"type: " + f.type + "\n"
|
||||
+"size: " + f.size + " bytes\n"
|
||||
+"starts with: " + contents.substr(0, contents.indexOf("\n"))
|
||||
);
|
||||
document.getElementById("inputText").value = contents;
|
||||
};
|
||||
r.readAsText(f);
|
||||
} else {
|
||||
alert("Failed to load file");
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('savetoFile').addEventListener('click', function() {
|
||||
font.saveFile();
|
||||
});
|
||||
|
||||
document.getElementById('shiftUp').addEventListener('click', function() {
|
||||
var chars = document.getElementById("chars");
|
||||
var tables = chars.getElementsByTagName("table");
|
||||
for(var i=0; i< tables.length; i++) {
|
||||
shiftUp(tables[i]);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('generate').addEventListener('click', function() {
|
||||
font.generate();
|
||||
});
|
||||
document.getElementById('addChar').addEventListener('click', function() {
|
||||
font.addChar();
|
||||
});
|
||||
document.getElementById('inputText').addEventListener('click', function(e) {
|
||||
let target = e.target;
|
||||
target.select();
|
||||
});
|
||||
document.getElementById('chars').addEventListener('mousedown', function(e) {
|
||||
if (e.button !== 0) return;
|
||||
let target = e.target;
|
||||
let action = target.getAttribute('action') || '';
|
||||
if (action === '') return;
|
||||
let result, code, nextContainer, previousContainer, pixels ;
|
||||
let currentContainer = target.parentNode.parentNode;
|
||||
switch(action) {
|
||||
case 'add':
|
||||
code = currentContainer.getAttribute('code');
|
||||
nextContainer = font.addChar();
|
||||
currentContainer.parentNode.insertBefore(nextContainer, currentContainer);
|
||||
do {
|
||||
nextContainer.setAttribute('code', code);
|
||||
Font.updateCaption(nextContainer.getElementsByTagName('caption')[0], code);
|
||||
code ++;
|
||||
} while (nextContainer = nextContainer.nextSibling);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
result = confirm("Delete this character ?");
|
||||
if (!result) return;
|
||||
code = currentContainer.getAttribute('code');
|
||||
nextContainer = currentContainer;
|
||||
while (nextContainer = nextContainer.nextSibling) {
|
||||
nextContainer.setAttribute('code', code);
|
||||
Font.updateCaption(nextContainer.getElementsByTagName('caption')[0], code);
|
||||
code ++;
|
||||
}
|
||||
currentContainer.parentNode.removeChild(currentContainer);
|
||||
break;
|
||||
|
||||
// Shift pixels to the left
|
||||
case 'left':
|
||||
pixels = currentContainer.getElementsByTagName('td');
|
||||
for(p = 0; p < pixels.length; p++) {
|
||||
if((p + 1) % font.width) {
|
||||
pixels[p].className = pixels[p + 1].className;
|
||||
} else {
|
||||
pixels[p].className = '';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Shift pixels to the right
|
||||
case 'right':
|
||||
pixels = currentContainer.getElementsByTagName('td');
|
||||
for(p = pixels.length-1; p >=0 ; p--) {
|
||||
if(p % font.width) {
|
||||
pixels[p].className = pixels[p - 1].className;
|
||||
} else {
|
||||
pixels[p].className = '';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Shift pixels down
|
||||
case 'down':
|
||||
pixels = currentContainer.getElementsByTagName('td');
|
||||
for(p = pixels.length-1; p >=0 ; p--) {
|
||||
if(p >= font.width) {
|
||||
pixels[p].className = pixels[p - font.width].className;
|
||||
} else {
|
||||
pixels[p].className = '';
|
||||
}
|
||||
} break;
|
||||
|
||||
// Shift pixels up
|
||||
case 'up':
|
||||
shiftUp(currentContainer);
|
||||
break;
|
||||
|
||||
case 'toggle':
|
||||
Font.togglePixel(target);
|
||||
break;
|
||||
|
||||
case 'clean':
|
||||
result = confirm("Delete the pixels ?");
|
||||
if (!result) return;
|
||||
pixels = currentContainer.getElementsByTagName('td');
|
||||
for (let p = 0; p < pixels.length; p++) {
|
||||
pixels[p].className = '';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'copy':
|
||||
let charNumber = parseInt(prompt("Source char #: "));
|
||||
let chars = font.fontContainer.getElementsByTagName('table');
|
||||
let tableOffset = charNumber - parseInt(document.getElementById('code').value);
|
||||
let srcPixels = chars[tableOffset].getElementsByTagName('td');
|
||||
let targetPixels = currentContainer.getElementsByTagName('td');
|
||||
for(let i=0; i < srcPixels.length; i++) {
|
||||
// First tds are in the th row, for editing actions. Skip them
|
||||
if (targetPixels[i].parentNode.localName === 'th') continue; // In case we give them css at some point...
|
||||
targetPixels[i].className = srcPixels[i].className;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// no.
|
||||
}
|
||||
|
||||
});
|
||||
function shiftUp(container) {
|
||||
var pixels = container.getElementsByTagName('td');
|
||||
for(p = 0; p < pixels.length; p++) {
|
||||
if(p < font.width*(font.height -1)) {
|
||||
pixels[p].className = pixels[p + font.width].className;
|
||||
} else {
|
||||
pixels[p].className = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('chars').addEventListener('mouseover', function(e) {
|
||||
let target = e.target;
|
||||
let action = target.getAttribute('action');
|
||||
if (action !== 'toggle' || e.buttons !== 1) return;
|
||||
Font.togglePixel(target);
|
||||
});
|
||||
document.getElementById('chars').addEventListener('dragstart', function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
document.getElementById('create').addEventListener('click', function() {
|
||||
font = new Font();
|
||||
font.addChar();
|
||||
});
|
||||
|
||||
// parse a char array declaration for an existing font.
|
||||
// parsing heavily relies on comments.
|
||||
document.getElementById('parse').addEventListener('click', function() {
|
||||
if (document.getElementById('chars').childElementCount) {
|
||||
let result = confirm("Confirm you want to overwrite the existing grids ?");
|
||||
if (!result) return;
|
||||
}
|
||||
let lines = document.getElementById('inputText').value.split('\n');
|
||||
let name = '';
|
||||
let height = 0;
|
||||
let width = 0;
|
||||
let firstCharCode = 0;
|
||||
let jumpTable = [];
|
||||
let charData = [];
|
||||
let readingJumpTable = false;
|
||||
let readingData = false;
|
||||
|
||||
for(let l = 0 ; l < lines.length; l++) {
|
||||
// TODO ? keep C compilation directives to copy them (not lowercased :)) to newly generated char array
|
||||
let line = lines[l].trim();
|
||||
//alert(line);
|
||||
let fields;
|
||||
|
||||
// Declaration line: grab the name
|
||||
if (line.indexOf('PROGMEM') > 0) {
|
||||
fields = line.split(' ');
|
||||
name = fields[2].replace(/[\[\]]/g, '');
|
||||
continue;
|
||||
}
|
||||
line = line.toLowerCase();
|
||||
// Todo: could rely on line order...
|
||||
// width line: grab the width
|
||||
if (line.indexOf('width') > 0) {
|
||||
fields = line.split(',');
|
||||
width = fields[0];
|
||||
continue;
|
||||
}
|
||||
// height line: grab the height
|
||||
if (line.indexOf('height') > 0) {
|
||||
fields = line.split(',');
|
||||
height = fields[0];
|
||||
continue;
|
||||
}
|
||||
// first char code line: grab the first char code
|
||||
if (line.indexOf('first char') > 0) {
|
||||
fields = line.split(',');
|
||||
firstCharCode = fields[0];
|
||||
continue;
|
||||
}
|
||||
// End of array declaration
|
||||
// TODO warn if more lines: next fonts are ignored
|
||||
if (line.indexOf('};') === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (readingJumpTable || readingData) {
|
||||
if (line.indexOf('#') !== 0 && line.length !== 0 && line.indexOf('//') !== 0) {
|
||||
line = line.replace(/\/\/.*/, ''); // get rid of end of line comments
|
||||
fields = line.split(',');
|
||||
let newEntry = [];
|
||||
for(let f=0; f < fields.length; f++) {
|
||||
let value = parseInt(fields[f]);
|
||||
if (isNaN(value)) continue;
|
||||
if (readingData) {
|
||||
charData.push(value);
|
||||
}
|
||||
if (readingJumpTable) {
|
||||
newEntry.push(value);
|
||||
}
|
||||
}
|
||||
if (readingJumpTable) {
|
||||
jumpTable.push(newEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Begining of data
|
||||
if (line.indexOf('font data') > 0) {
|
||||
readingData = true;
|
||||
readingJumpTable = false;
|
||||
}
|
||||
// Begining of jump table
|
||||
if (line.indexOf('jump table') > 0) {
|
||||
readingJumpTable = true;
|
||||
}
|
||||
}
|
||||
if (!name || !height || !width || !firstCharCode) {
|
||||
alert("Does not look like a parsable font. Try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
Font.emptyChars();
|
||||
Font.emptyOutput();
|
||||
document.getElementById('name').value = name;
|
||||
document.getElementById('height').value = parseInt(height);
|
||||
document.getElementById('width').value = parseInt(width);
|
||||
document.getElementById('code').value = parseInt(firstCharCode);
|
||||
font = new Font();
|
||||
for(let c = 0 ; c < jumpTable.length; c++) {
|
||||
let jumpEntry = jumpTable[c];
|
||||
let charEntry = [];
|
||||
// displayable character
|
||||
if (jumpEntry[0] !== 255 || jumpEntry[1] !== 255) {
|
||||
charEntry = charData.splice(0, jumpEntry[2]);
|
||||
}
|
||||
font.addChar(jumpEntry, charEntry);
|
||||
}
|
||||
document.body.className = "started";
|
||||
});
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 41 KiB |
1045
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/src/OLEDDisplay.cpp
Normal file
384
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/src/OLEDDisplay.h
Normal file
@@ -0,0 +1,384 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
* Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OLEDDISPLAY_h
|
||||
#define OLEDDISPLAY_h
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#elif __MBED__
|
||||
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
|
||||
|
||||
#include <mbed.h>
|
||||
#define delay(x) wait_ms(x)
|
||||
#define yield() void()
|
||||
|
||||
/*
|
||||
* This is a little Arduino String emulation to keep the OLEDDisplay
|
||||
* library code in common between Arduino and mbed-os
|
||||
*/
|
||||
class String {
|
||||
public:
|
||||
String(const char *s) { _str = s; };
|
||||
int length() { return strlen(_str); };
|
||||
const char *c_str() { return _str; };
|
||||
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
|
||||
memcpy(buf, _str + index, std::min(bufsize, strlen(_str)));
|
||||
};
|
||||
private:
|
||||
const char *_str;
|
||||
};
|
||||
|
||||
#else
|
||||
#error "Unkown operating system"
|
||||
#endif
|
||||
|
||||
#include "OLEDDisplayFonts.h"
|
||||
|
||||
//#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ )
|
||||
//#define DEBUG_OLEDDISPLAY(...) dprintf("%s", __VA_ARGS__ )
|
||||
|
||||
#ifndef DEBUG_OLEDDISPLAY
|
||||
#define DEBUG_OLEDDISPLAY(...)
|
||||
#endif
|
||||
|
||||
// Use DOUBLE BUFFERING by default
|
||||
#ifndef OLEDDISPLAY_REDUCE_MEMORY
|
||||
#define OLEDDISPLAY_DOUBLE_BUFFER
|
||||
#endif
|
||||
|
||||
// Header Values
|
||||
#define JUMPTABLE_BYTES 4
|
||||
|
||||
#define JUMPTABLE_LSB 1
|
||||
#define JUMPTABLE_SIZE 2
|
||||
#define JUMPTABLE_WIDTH 3
|
||||
#define JUMPTABLE_START 4
|
||||
|
||||
#define WIDTH_POS 0
|
||||
#define HEIGHT_POS 1
|
||||
#define FIRST_CHAR_POS 2
|
||||
#define CHAR_NUM_POS 3
|
||||
|
||||
|
||||
// Display commands
|
||||
#define CHARGEPUMP 0x8D
|
||||
#define COLUMNADDR 0x21
|
||||
#define COMSCANDEC 0xC8
|
||||
#define COMSCANINC 0xC0
|
||||
#define DISPLAYALLON 0xA5
|
||||
#define DISPLAYALLON_RESUME 0xA4
|
||||
#define DISPLAYOFF 0xAE
|
||||
#define DISPLAYON 0xAF
|
||||
#define EXTERNALVCC 0x1
|
||||
#define INVERTDISPLAY 0xA7
|
||||
#define MEMORYMODE 0x20
|
||||
#define NORMALDISPLAY 0xA6
|
||||
#define PAGEADDR 0x22
|
||||
#define SEGREMAP 0xA0
|
||||
#define SETCOMPINS 0xDA
|
||||
#define SETCONTRAST 0x81
|
||||
#define SETDISPLAYCLOCKDIV 0xD5
|
||||
#define SETDISPLAYOFFSET 0xD3
|
||||
#define SETHIGHCOLUMN 0x10
|
||||
#define SETLOWCOLUMN 0x00
|
||||
#define SETMULTIPLEX 0xA8
|
||||
#define SETPRECHARGE 0xD9
|
||||
#define SETSEGMENTREMAP 0xA1
|
||||
#define SETSTARTLINE 0x40
|
||||
#define SETVCOMDETECT 0xDB
|
||||
#define SWITCHCAPVCC 0x2
|
||||
|
||||
#ifndef _swap_int16_t
|
||||
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
|
||||
#endif
|
||||
|
||||
enum OLEDDISPLAY_COLOR {
|
||||
BLACK = 0,
|
||||
WHITE = 1,
|
||||
INVERSE = 2
|
||||
};
|
||||
|
||||
enum OLEDDISPLAY_TEXT_ALIGNMENT {
|
||||
TEXT_ALIGN_LEFT = 0,
|
||||
TEXT_ALIGN_RIGHT = 1,
|
||||
TEXT_ALIGN_CENTER = 2,
|
||||
TEXT_ALIGN_CENTER_BOTH = 3
|
||||
};
|
||||
|
||||
|
||||
enum OLEDDISPLAY_GEOMETRY {
|
||||
GEOMETRY_128_64 = 0,
|
||||
GEOMETRY_128_32 = 1,
|
||||
GEOMETRY_64_48 = 2,
|
||||
GEOMETRY_64_32 = 3,
|
||||
GEOMETRY_RAWMODE = 4
|
||||
};
|
||||
|
||||
enum HW_I2C {
|
||||
I2C_ONE,
|
||||
I2C_TWO
|
||||
};
|
||||
|
||||
typedef char (*FontTableLookupFunction)(const uint8_t ch);
|
||||
char DefaultFontTableLookup(const uint8_t ch);
|
||||
|
||||
|
||||
#ifdef ARDUINO
|
||||
class OLEDDisplay : public Print {
|
||||
#elif __MBED__
|
||||
class OLEDDisplay : public Stream {
|
||||
#else
|
||||
#error "Unkown operating system"
|
||||
#endif
|
||||
|
||||
public:
|
||||
OLEDDisplay();
|
||||
virtual ~OLEDDisplay();
|
||||
|
||||
uint16_t width(void) const { return displayWidth; };
|
||||
uint16_t height(void) const { return displayHeight; };
|
||||
|
||||
// Use this to resume after a deep sleep without resetting the display (what init() would do).
|
||||
// Returns true if connection to the display was established and the buffer allocated, false otherwise.
|
||||
bool allocateBuffer();
|
||||
|
||||
// Allocates the buffer and initializes the driver & display. Resets the display!
|
||||
// Returns false if buffer allocation failed, true otherwise.
|
||||
bool init();
|
||||
|
||||
// Free the memory used by the display
|
||||
void end();
|
||||
|
||||
// Cycle through the initialization
|
||||
void resetDisplay(void);
|
||||
|
||||
/* Drawing functions */
|
||||
// Sets the color of all pixel operations
|
||||
void setColor(OLEDDISPLAY_COLOR color);
|
||||
|
||||
// Returns the current color.
|
||||
OLEDDISPLAY_COLOR getColor();
|
||||
|
||||
// Draw a pixel at given position
|
||||
void setPixel(int16_t x, int16_t y);
|
||||
|
||||
// Draw a pixel at given position and color
|
||||
void setPixelColor(int16_t x, int16_t y, OLEDDISPLAY_COLOR color);
|
||||
|
||||
// Clear a pixel at given position FIXME: INVERSE is untested with this function
|
||||
void clearPixel(int16_t x, int16_t y);
|
||||
|
||||
// Draw a line from position 0 to position 1
|
||||
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1);
|
||||
|
||||
// Draw the border of a rectangle at the given location
|
||||
void drawRect(int16_t x, int16_t y, int16_t width, int16_t height);
|
||||
|
||||
// Fill the rectangle
|
||||
void fillRect(int16_t x, int16_t y, int16_t width, int16_t height);
|
||||
|
||||
// Draw the border of a circle
|
||||
void drawCircle(int16_t x, int16_t y, int16_t radius);
|
||||
|
||||
// Draw all Quadrants specified in the quads bit mask
|
||||
void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads);
|
||||
|
||||
// Fill circle
|
||||
void fillCircle(int16_t x, int16_t y, int16_t radius);
|
||||
|
||||
// Draw a line horizontally
|
||||
void drawHorizontalLine(int16_t x, int16_t y, int16_t length);
|
||||
|
||||
// Draw a line vertically
|
||||
void drawVerticalLine(int16_t x, int16_t y, int16_t length);
|
||||
|
||||
// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is
|
||||
// a unsigned byte value between 0 and 100
|
||||
void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress);
|
||||
|
||||
// Draw a bitmap in the internal image format
|
||||
void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image);
|
||||
|
||||
// Draw a XBM
|
||||
void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm);
|
||||
|
||||
// Draw icon 16x16 xbm format
|
||||
void drawIco16x16(int16_t x, int16_t y, const char *ico, bool inverse = false);
|
||||
|
||||
/* Text functions */
|
||||
|
||||
// Draws a string at the given location
|
||||
void drawString(int16_t x, int16_t y, String text);
|
||||
|
||||
// Draws a formatted string (like printf) at the given location
|
||||
void drawStringf(int16_t x, int16_t y, char* buffer, String format, ... );
|
||||
|
||||
// Draws a String with a maximum width at the given location.
|
||||
// If the given String is wider than the specified width
|
||||
// The text will be wrapped to the next line at a space or dash
|
||||
void drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, String text);
|
||||
|
||||
// Returns the width of the const char* with the current
|
||||
// font settings
|
||||
uint16_t getStringWidth(const char* text, uint16_t length);
|
||||
|
||||
// Convencience method for the const char version
|
||||
uint16_t getStringWidth(String text);
|
||||
|
||||
// Specifies relative to which anchor point
|
||||
// the text is rendered. Available constants:
|
||||
// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH
|
||||
void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment);
|
||||
|
||||
// Sets the current font. Available default fonts
|
||||
// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
|
||||
void setFont(const uint8_t *fontData);
|
||||
|
||||
// Set the function that will convert utf-8 to font table index
|
||||
void setFontTableLookupFunction(FontTableLookupFunction function);
|
||||
|
||||
/* Display functions */
|
||||
|
||||
// Turn the display on
|
||||
void displayOn(void);
|
||||
|
||||
// Turn the display offs
|
||||
void displayOff(void);
|
||||
|
||||
// Inverted display mode
|
||||
void invertDisplay(void);
|
||||
|
||||
// Normal display mode
|
||||
void normalDisplay(void);
|
||||
|
||||
// Set display contrast
|
||||
// really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0
|
||||
// normal brightness & contrast: contrast = 100
|
||||
void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64);
|
||||
|
||||
// Convenience method to access
|
||||
void setBrightness(uint8_t);
|
||||
|
||||
// Reset display rotation or mirroring
|
||||
void resetOrientation();
|
||||
|
||||
// Turn the display upside down
|
||||
void flipScreenVertically();
|
||||
|
||||
// Mirror the display (to be used in a mirror or as a projector)
|
||||
void mirrorScreen();
|
||||
|
||||
// Write the buffer to the display memory
|
||||
virtual void display(void) = 0;
|
||||
|
||||
// Clear the local pixel buffer
|
||||
void clear(void);
|
||||
|
||||
// Log buffer implementation
|
||||
|
||||
// This will define the lines and characters you can
|
||||
// print to the screen. When you exeed the buffer size (lines * chars)
|
||||
// the output may be truncated due to the size constraint.
|
||||
bool setLogBuffer(uint16_t lines, uint16_t chars);
|
||||
|
||||
// Draw the log buffer at position (x, y)
|
||||
void drawLogBuffer(uint16_t x, uint16_t y);
|
||||
|
||||
// Get screen geometry
|
||||
uint16_t getWidth(void);
|
||||
uint16_t getHeight(void);
|
||||
|
||||
// Implement needed function to be compatible with Print class
|
||||
size_t write(uint8_t c);
|
||||
size_t write(const char* s);
|
||||
|
||||
// Implement needed function to be compatible with Stream class
|
||||
#ifdef __MBED__
|
||||
int _putc(int c);
|
||||
int _getc() { return -1; };
|
||||
#endif
|
||||
|
||||
|
||||
uint8_t *buffer;
|
||||
|
||||
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
|
||||
uint8_t *buffer_back;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
OLEDDISPLAY_GEOMETRY geometry;
|
||||
|
||||
uint16_t displayWidth;
|
||||
uint16_t displayHeight;
|
||||
uint16_t displayBufferSize;
|
||||
|
||||
// Set the correct height, width and buffer for the geometry
|
||||
void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width = 0, uint16_t height = 0);
|
||||
|
||||
OLEDDISPLAY_TEXT_ALIGNMENT textAlignment;
|
||||
OLEDDISPLAY_COLOR color;
|
||||
|
||||
const uint8_t *fontData;
|
||||
|
||||
// State values for logBuffer
|
||||
uint16_t logBufferSize;
|
||||
uint16_t logBufferFilled;
|
||||
uint16_t logBufferLine;
|
||||
uint16_t logBufferMaxLines;
|
||||
char *logBuffer;
|
||||
|
||||
|
||||
// the header size of the buffer used, e.g. for the SPI command header
|
||||
int BufferOffset;
|
||||
virtual int getBufferOffset(void) = 0;
|
||||
|
||||
// Send a command to the display (low level function)
|
||||
virtual void sendCommand(uint8_t com) {(void)com;};
|
||||
|
||||
// Connect to the display
|
||||
virtual bool connect() { return false; };
|
||||
|
||||
// Send all the init commands
|
||||
void sendInitCommands();
|
||||
|
||||
// converts utf8 characters to extended ascii
|
||||
char* utf8ascii(String s);
|
||||
|
||||
void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline));
|
||||
|
||||
void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth);
|
||||
|
||||
FontTableLookupFunction fontTableLookupFunction;
|
||||
};
|
||||
|
||||
#endif
|
||||
1278
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/src/OLEDDisplayFonts.h
Normal file
@@ -0,0 +1,471 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
* Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
#include "OLEDDisplayUi.h"
|
||||
|
||||
void LoadingDrawDefault(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) {
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
display->drawString(64, 18, stage->process);
|
||||
display->drawProgressBar(4, 32, 120, 8, progress);
|
||||
};
|
||||
|
||||
|
||||
OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) {
|
||||
this->display = display;
|
||||
|
||||
indicatorPosition = BOTTOM;
|
||||
indicatorDirection = LEFT_RIGHT;
|
||||
activeSymbol = ANIMATION_activeSymbol;
|
||||
inactiveSymbol = ANIMATION_inactiveSymbol;
|
||||
frameAnimationDirection = SLIDE_RIGHT;
|
||||
lastTransitionDirection = 1;
|
||||
frameCount = 0;
|
||||
nextFrameNumber = -1;
|
||||
overlayCount = 0;
|
||||
indicatorDrawState = 1;
|
||||
loadingDrawFunction = LoadingDrawDefault;
|
||||
updateInterval = 33;
|
||||
state.lastUpdate = 0;
|
||||
state.ticksSinceLastStateSwitch = 0;
|
||||
state.frameState = FIXED;
|
||||
state.currentFrame = 0;
|
||||
state.frameTransitionDirection = 1;
|
||||
state.isIndicatorDrawen = true;
|
||||
state.manuelControll = false;
|
||||
state.userData = NULL;
|
||||
shouldDrawIndicators = true;
|
||||
autoTransition = true;
|
||||
setTimePerFrame(5000);
|
||||
setTimePerTransition(500);
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::init() {
|
||||
this->display->init();
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::setTargetFPS(uint8_t fps){
|
||||
this->updateInterval = ((float) 1.0 / (float) fps) * 1000;
|
||||
|
||||
this->ticksPerFrame = timePerFrame / updateInterval;
|
||||
this->ticksPerTransition = timePerTransition / updateInterval;
|
||||
}
|
||||
|
||||
// -/------ Automatic controll ------\-
|
||||
|
||||
void OLEDDisplayUi::enableAutoTransition(){
|
||||
this->autoTransition = true;
|
||||
}
|
||||
void OLEDDisplayUi::disableAutoTransition(){
|
||||
this->autoTransition = false;
|
||||
}
|
||||
void OLEDDisplayUi::setAutoTransitionForwards(){
|
||||
this->state.frameTransitionDirection = 1;
|
||||
this->lastTransitionDirection = 1;
|
||||
}
|
||||
void OLEDDisplayUi::setAutoTransitionBackwards(){
|
||||
this->state.frameTransitionDirection = -1;
|
||||
this->lastTransitionDirection = -1;
|
||||
}
|
||||
void OLEDDisplayUi::setTimePerFrame(uint16_t time){
|
||||
this->timePerFrame = time;
|
||||
this->ticksPerFrame = timePerFrame / updateInterval;
|
||||
}
|
||||
void OLEDDisplayUi::setTimePerTransition(uint16_t time){
|
||||
this->timePerTransition = time;
|
||||
this->ticksPerTransition = timePerTransition / updateInterval;
|
||||
}
|
||||
|
||||
// -/------ Customize indicator position and style -------\-
|
||||
void OLEDDisplayUi::enableIndicator(){
|
||||
this->state.isIndicatorDrawen = true;
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::disableIndicator(){
|
||||
this->state.isIndicatorDrawen = false;
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::enableAllIndicators(){
|
||||
this->shouldDrawIndicators = true;
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::disableAllIndicators(){
|
||||
this->shouldDrawIndicators = false;
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) {
|
||||
this->indicatorPosition = pos;
|
||||
}
|
||||
void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) {
|
||||
this->indicatorDirection = dir;
|
||||
}
|
||||
void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) {
|
||||
this->activeSymbol = symbol;
|
||||
}
|
||||
void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) {
|
||||
this->inactiveSymbol = symbol;
|
||||
}
|
||||
|
||||
|
||||
// -/----- Frame settings -----\-
|
||||
void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) {
|
||||
this->frameAnimationDirection = dir;
|
||||
}
|
||||
void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) {
|
||||
this->frameFunctions = frameFunctions;
|
||||
this->frameCount = frameCount;
|
||||
this->resetState();
|
||||
}
|
||||
|
||||
// -/----- Overlays ------\-
|
||||
void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){
|
||||
this->overlayFunctions = overlayFunctions;
|
||||
this->overlayCount = overlayCount;
|
||||
}
|
||||
|
||||
// -/----- Loading Process -----\-
|
||||
|
||||
void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) {
|
||||
this->loadingDrawFunction = loadingDrawFunction;
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) {
|
||||
uint8_t progress = 0;
|
||||
uint8_t increment = 100 / stagesCount;
|
||||
|
||||
for (uint8_t i = 0; i < stagesCount; i++) {
|
||||
display->clear();
|
||||
this->loadingDrawFunction(this->display, &stages[i], progress);
|
||||
display->display();
|
||||
|
||||
stages[i].callback();
|
||||
|
||||
progress += increment;
|
||||
yield();
|
||||
}
|
||||
|
||||
display->clear();
|
||||
this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress);
|
||||
display->display();
|
||||
|
||||
delay(150);
|
||||
}
|
||||
|
||||
// -/----- Manuel control -----\-
|
||||
void OLEDDisplayUi::nextFrame() {
|
||||
if (this->state.frameState != IN_TRANSITION) {
|
||||
this->state.manuelControll = true;
|
||||
this->state.frameState = IN_TRANSITION;
|
||||
this->state.ticksSinceLastStateSwitch = 0;
|
||||
this->lastTransitionDirection = this->state.frameTransitionDirection;
|
||||
this->state.frameTransitionDirection = 1;
|
||||
}
|
||||
}
|
||||
void OLEDDisplayUi::previousFrame() {
|
||||
if (this->state.frameState != IN_TRANSITION) {
|
||||
this->state.manuelControll = true;
|
||||
this->state.frameState = IN_TRANSITION;
|
||||
this->state.ticksSinceLastStateSwitch = 0;
|
||||
this->lastTransitionDirection = this->state.frameTransitionDirection;
|
||||
this->state.frameTransitionDirection = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::switchToFrame(uint8_t frame) {
|
||||
if (frame >= this->frameCount) return;
|
||||
this->state.ticksSinceLastStateSwitch = 0;
|
||||
if (frame == this->state.currentFrame) return;
|
||||
this->state.frameState = FIXED;
|
||||
this->state.currentFrame = frame;
|
||||
this->state.isIndicatorDrawen = true;
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::transitionToFrame(uint8_t frame) {
|
||||
if (frame >= this->frameCount) return;
|
||||
this->state.ticksSinceLastStateSwitch = 0;
|
||||
if (frame == this->state.currentFrame) return;
|
||||
this->nextFrameNumber = frame;
|
||||
this->lastTransitionDirection = this->state.frameTransitionDirection;
|
||||
this->state.manuelControll = true;
|
||||
this->state.frameState = IN_TRANSITION;
|
||||
this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
// -/----- State information -----\-
|
||||
OLEDDisplayUiState* OLEDDisplayUi::getUiState(){
|
||||
return &this->state;
|
||||
}
|
||||
|
||||
int16_t OLEDDisplayUi::update(){
|
||||
#ifdef ARDUINO
|
||||
unsigned long frameStart = millis();
|
||||
#elif __MBED__
|
||||
Timer t;
|
||||
t.start();
|
||||
unsigned long frameStart = t.read_ms();
|
||||
#else
|
||||
#error "Unkown operating system"
|
||||
#endif
|
||||
int32_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate);
|
||||
if ( timeBudget <= 0) {
|
||||
// Implement frame skipping to ensure time budget is keept
|
||||
if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil((double)-timeBudget / (double)this->updateInterval);
|
||||
|
||||
this->state.lastUpdate = frameStart;
|
||||
this->tick();
|
||||
}
|
||||
#ifdef ARDUINO
|
||||
return this->updateInterval - (millis() - frameStart);
|
||||
#elif __MBED__
|
||||
return this->updateInterval - (t.read_ms() - frameStart);
|
||||
#else
|
||||
#error "Unkown operating system"
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void OLEDDisplayUi::tick() {
|
||||
this->state.ticksSinceLastStateSwitch++;
|
||||
|
||||
switch (this->state.frameState) {
|
||||
case IN_TRANSITION:
|
||||
if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){
|
||||
this->state.frameState = FIXED;
|
||||
this->state.currentFrame = getNextFrameNumber();
|
||||
this->state.ticksSinceLastStateSwitch = 0;
|
||||
this->nextFrameNumber = -1;
|
||||
}
|
||||
break;
|
||||
case FIXED:
|
||||
// Revert manuelControll
|
||||
if (this->state.manuelControll) {
|
||||
this->state.frameTransitionDirection = this->lastTransitionDirection;
|
||||
this->state.manuelControll = false;
|
||||
}
|
||||
if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){
|
||||
if (this->autoTransition){
|
||||
this->state.frameState = IN_TRANSITION;
|
||||
}
|
||||
this->state.ticksSinceLastStateSwitch = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this->display->clear();
|
||||
this->drawFrame();
|
||||
if (shouldDrawIndicators) {
|
||||
this->drawIndicator();
|
||||
}
|
||||
this->drawOverlays();
|
||||
this->display->display();
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::resetState() {
|
||||
this->state.lastUpdate = 0;
|
||||
this->state.ticksSinceLastStateSwitch = 0;
|
||||
this->state.frameState = FIXED;
|
||||
this->state.currentFrame = 0;
|
||||
this->state.isIndicatorDrawen = true;
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::drawFrame(){
|
||||
switch (this->state.frameState){
|
||||
case IN_TRANSITION: {
|
||||
float progress = 0.f;
|
||||
if (this->ticksPerTransition > 0u) {
|
||||
progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition;
|
||||
}
|
||||
int16_t x = 0, y = 0, x1 = 0, y1 = 0;
|
||||
switch(this->frameAnimationDirection){
|
||||
case SLIDE_LEFT:
|
||||
x = -this->display->width() * progress;
|
||||
y = 0;
|
||||
x1 = x + this->display->width();
|
||||
y1 = 0;
|
||||
break;
|
||||
case SLIDE_RIGHT:
|
||||
x = this->display->width() * progress;
|
||||
y = 0;
|
||||
x1 = x - this->display->width();
|
||||
y1 = 0;
|
||||
break;
|
||||
case SLIDE_UP:
|
||||
x = 0;
|
||||
y = -this->display->height() * progress;
|
||||
x1 = 0;
|
||||
y1 = y + this->display->height();
|
||||
break;
|
||||
case SLIDE_DOWN:
|
||||
default:
|
||||
x = 0;
|
||||
y = this->display->height() * progress;
|
||||
x1 = 0;
|
||||
y1 = y - this->display->height();
|
||||
break;
|
||||
}
|
||||
|
||||
// Invert animation if direction is reversed.
|
||||
int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1;
|
||||
x *= dir; y *= dir; x1 *= dir; y1 *= dir;
|
||||
|
||||
bool drawenCurrentFrame;
|
||||
|
||||
|
||||
// Prope each frameFunction for the indicator Drawen state
|
||||
this->enableIndicator();
|
||||
(this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y);
|
||||
drawenCurrentFrame = this->state.isIndicatorDrawen;
|
||||
|
||||
this->enableIndicator();
|
||||
(this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1);
|
||||
|
||||
// Build up the indicatorDrawState
|
||||
if (drawenCurrentFrame && !this->state.isIndicatorDrawen) {
|
||||
// Drawen now but not next
|
||||
this->indicatorDrawState = 2;
|
||||
} else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) {
|
||||
// Not drawen now but next
|
||||
this->indicatorDrawState = 1;
|
||||
} else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) {
|
||||
// Not drawen in both frames
|
||||
this->indicatorDrawState = 3;
|
||||
}
|
||||
|
||||
// If the indicator isn't draw in the current frame
|
||||
// reflect it in state.isIndicatorDrawen
|
||||
if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case FIXED:
|
||||
// Always assume that the indicator is drawn!
|
||||
// And set indicatorDrawState to "not known yet"
|
||||
this->indicatorDrawState = 0;
|
||||
this->enableIndicator();
|
||||
(this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::drawIndicator() {
|
||||
|
||||
// Only draw if the indicator is invisible
|
||||
// for both frames or
|
||||
// the indiactor is shown and we are IN_TRANSITION
|
||||
if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t posOfHighlightFrame = 0;
|
||||
float indicatorFadeProgress = 0;
|
||||
|
||||
// if the indicator needs to be slided in we want to
|
||||
// highlight the next frame in the transition
|
||||
uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame;
|
||||
|
||||
// Calculate the frame that needs to be highlighted
|
||||
// based on the Direction the indiactor is drawn
|
||||
switch (this->indicatorDirection){
|
||||
case LEFT_RIGHT:
|
||||
posOfHighlightFrame = frameToHighlight;
|
||||
break;
|
||||
case RIGHT_LEFT:
|
||||
default:
|
||||
posOfHighlightFrame = this->frameCount - frameToHighlight;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (this->indicatorDrawState) {
|
||||
case 1: // Indicator was not drawn in this frame but will be in next
|
||||
// Slide IN
|
||||
indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition);
|
||||
break;
|
||||
case 2: // Indicator was drawn in this frame but not in next
|
||||
// Slide OUT
|
||||
indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition);
|
||||
break;
|
||||
}
|
||||
|
||||
//Space between indicators - reduce for small screen sizes
|
||||
uint16_t indicatorSpacing = 12;
|
||||
if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) {
|
||||
indicatorSpacing = 6;
|
||||
}
|
||||
|
||||
uint16_t frameStartPos = (indicatorSpacing * frameCount / 2);
|
||||
const uint8_t *image;
|
||||
|
||||
uint16_t x = 0,y = 0;
|
||||
|
||||
|
||||
for (uint8_t i = 0; i < this->frameCount; i++) {
|
||||
|
||||
switch (this->indicatorPosition){
|
||||
case TOP:
|
||||
y = 0 - (8 * indicatorFadeProgress);
|
||||
x = (this->display->width() / 2) - frameStartPos + 12 * i;
|
||||
break;
|
||||
case BOTTOM:
|
||||
y = (this->display->height() - 8) + (8 * indicatorFadeProgress);
|
||||
x = (this->display->width() / 2) - frameStartPos + 12 * i;
|
||||
break;
|
||||
case RIGHT:
|
||||
x = (this->display->width() - 8) + (8 * indicatorFadeProgress);
|
||||
y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i;
|
||||
break;
|
||||
case LEFT:
|
||||
default:
|
||||
x = 0 - (8 * indicatorFadeProgress);
|
||||
y = (this->display->height() / 2) - frameStartPos + 2 + indicatorSpacing * i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (posOfHighlightFrame == i) {
|
||||
image = this->activeSymbol;
|
||||
} else {
|
||||
image = this->inactiveSymbol;
|
||||
}
|
||||
|
||||
this->display->drawFastImage(x, y, 8, 8, image);
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplayUi::drawOverlays() {
|
||||
for (uint8_t i=0;i<this->overlayCount;i++){
|
||||
(this->overlayFunctions[i])(this->display, &this->state);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t OLEDDisplayUi::getNextFrameNumber(){
|
||||
if (this->nextFrameNumber != -1) return this->nextFrameNumber;
|
||||
return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount;
|
||||
}
|
||||
315
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/src/OLEDDisplayUi.h
Normal file
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
* Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OLEDDISPLAYUI_h
|
||||
#define OLEDDISPLAYUI_h
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#elif __MBED__
|
||||
#include <mbed.h>
|
||||
#else
|
||||
#error "Unkown operating system"
|
||||
#endif
|
||||
|
||||
#include "OLEDDisplay.h"
|
||||
|
||||
//#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ )
|
||||
|
||||
#ifndef DEBUG_OLEDDISPLAYUI
|
||||
#define DEBUG_OLEDDISPLAYUI(...)
|
||||
#endif
|
||||
|
||||
enum AnimationDirection {
|
||||
SLIDE_UP,
|
||||
SLIDE_DOWN,
|
||||
SLIDE_LEFT,
|
||||
SLIDE_RIGHT
|
||||
};
|
||||
|
||||
enum IndicatorPosition {
|
||||
TOP,
|
||||
RIGHT,
|
||||
BOTTOM,
|
||||
LEFT
|
||||
};
|
||||
|
||||
enum IndicatorDirection {
|
||||
LEFT_RIGHT,
|
||||
RIGHT_LEFT
|
||||
};
|
||||
|
||||
enum FrameState {
|
||||
IN_TRANSITION,
|
||||
FIXED
|
||||
};
|
||||
|
||||
|
||||
const uint8_t ANIMATION_activeSymbol[] PROGMEM = {
|
||||
0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00
|
||||
};
|
||||
|
||||
const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = {
|
||||
0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00
|
||||
};
|
||||
|
||||
|
||||
// Structure of the UiState
|
||||
struct OLEDDisplayUiState {
|
||||
uint64_t lastUpdate;
|
||||
uint16_t ticksSinceLastStateSwitch;
|
||||
|
||||
FrameState frameState;
|
||||
uint8_t currentFrame;
|
||||
|
||||
bool isIndicatorDrawen;
|
||||
|
||||
// Normal = 1, Inverse = -1;
|
||||
int8_t frameTransitionDirection;
|
||||
|
||||
bool manuelControll;
|
||||
|
||||
// Custom data that can be used by the user
|
||||
void* userData;
|
||||
};
|
||||
|
||||
struct LoadingStage {
|
||||
const char* process;
|
||||
void (*callback)();
|
||||
};
|
||||
|
||||
typedef void (*FrameCallback)(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
|
||||
typedef void (*OverlayCallback)(OLEDDisplay *display, OLEDDisplayUiState* state);
|
||||
typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress);
|
||||
|
||||
class OLEDDisplayUi {
|
||||
private:
|
||||
OLEDDisplay *display;
|
||||
|
||||
// Symbols for the Indicator
|
||||
IndicatorPosition indicatorPosition;
|
||||
IndicatorDirection indicatorDirection;
|
||||
|
||||
const uint8_t* activeSymbol;
|
||||
const uint8_t* inactiveSymbol;
|
||||
|
||||
bool shouldDrawIndicators;
|
||||
|
||||
// Values for the Frames
|
||||
AnimationDirection frameAnimationDirection;
|
||||
|
||||
int8_t lastTransitionDirection;
|
||||
|
||||
uint16_t ticksPerFrame; // ~ 5000ms at 30 FPS
|
||||
uint16_t ticksPerTransition; // ~ 500ms at 30 FPS
|
||||
|
||||
bool autoTransition;
|
||||
|
||||
FrameCallback* frameFunctions;
|
||||
uint8_t frameCount;
|
||||
|
||||
// Internally used to transition to a specific frame
|
||||
int8_t nextFrameNumber;
|
||||
|
||||
// Values for Overlays
|
||||
OverlayCallback* overlayFunctions;
|
||||
uint8_t overlayCount;
|
||||
|
||||
// Will the Indicator be drawen
|
||||
// 3 Not drawn in both frames
|
||||
// 2 Drawn this frame but not next
|
||||
// 1 Not drown this frame but next
|
||||
// 0 Not known yet
|
||||
uint8_t indicatorDrawState;
|
||||
|
||||
// Loading screen
|
||||
LoadingDrawFunction loadingDrawFunction;
|
||||
|
||||
// UI State
|
||||
OLEDDisplayUiState state;
|
||||
|
||||
// Bookeeping for update
|
||||
uint16_t updateInterval = 33;
|
||||
|
||||
uint16_t timePerFrame;
|
||||
uint16_t timePerTransition;
|
||||
|
||||
uint8_t getNextFrameNumber();
|
||||
void drawIndicator();
|
||||
void drawFrame();
|
||||
void drawOverlays();
|
||||
void tick();
|
||||
void resetState();
|
||||
|
||||
public:
|
||||
|
||||
OLEDDisplayUi(OLEDDisplay *display);
|
||||
|
||||
/**
|
||||
* Initialise the display
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Configure the internal used target FPS
|
||||
*/
|
||||
void setTargetFPS(uint8_t fps);
|
||||
|
||||
// Automatic Controll
|
||||
/**
|
||||
* Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`.
|
||||
*/
|
||||
void enableAutoTransition();
|
||||
|
||||
/**
|
||||
* Disable automatic transition to next frame.
|
||||
*/
|
||||
void disableAutoTransition();
|
||||
|
||||
/**
|
||||
* Set the direction if the automatic transitioning
|
||||
*/
|
||||
void setAutoTransitionForwards();
|
||||
void setAutoTransitionBackwards();
|
||||
|
||||
/**
|
||||
* Set the approx. time a frame is displayed
|
||||
*/
|
||||
void setTimePerFrame(uint16_t time);
|
||||
|
||||
/**
|
||||
* Set the approx. time a transition will take
|
||||
*/
|
||||
void setTimePerTransition(uint16_t time);
|
||||
|
||||
// Customize indicator position and style
|
||||
|
||||
/**
|
||||
* Draw the indicator.
|
||||
* This is the defaut state for all frames if
|
||||
* the indicator was hidden on the previous frame
|
||||
* it will be slided in.
|
||||
*/
|
||||
void enableIndicator();
|
||||
|
||||
/**
|
||||
* Don't draw the indicator.
|
||||
* This will slide out the indicator
|
||||
* when transitioning to the next frame.
|
||||
*/
|
||||
void disableIndicator();
|
||||
|
||||
/**
|
||||
* Enable drawing of indicators
|
||||
*/
|
||||
void enableAllIndicators();
|
||||
|
||||
/**
|
||||
* Disable draw of indicators.
|
||||
*/
|
||||
void disableAllIndicators();
|
||||
|
||||
/**
|
||||
* Set the position of the indicator bar.
|
||||
*/
|
||||
void setIndicatorPosition(IndicatorPosition pos);
|
||||
|
||||
/**
|
||||
* Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING
|
||||
*/
|
||||
void setIndicatorDirection(IndicatorDirection dir);
|
||||
|
||||
/**
|
||||
* Set the symbol to indicate an active frame in the indicator bar.
|
||||
*/
|
||||
void setActiveSymbol(const uint8_t* symbol);
|
||||
|
||||
/**
|
||||
* Set the symbol to indicate an inactive frame in the indicator bar.
|
||||
*/
|
||||
void setInactiveSymbol(const uint8_t* symbol);
|
||||
|
||||
|
||||
// Frame settings
|
||||
|
||||
/**
|
||||
* Configure what animation is used to transition from one frame to another
|
||||
*/
|
||||
void setFrameAnimation(AnimationDirection dir);
|
||||
|
||||
/**
|
||||
* Add frame drawing functions
|
||||
*/
|
||||
void setFrames(FrameCallback* frameFunctions, uint8_t frameCount);
|
||||
|
||||
// Overlay
|
||||
|
||||
/**
|
||||
* Add overlays drawing functions that are draw independent of the Frames
|
||||
*/
|
||||
void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount);
|
||||
|
||||
|
||||
// Loading animation
|
||||
/**
|
||||
* Set the function that will draw each step
|
||||
* in the loading animation
|
||||
*/
|
||||
void setLoadingDrawFunction(LoadingDrawFunction loadingFunction);
|
||||
|
||||
|
||||
/**
|
||||
* Run the loading process
|
||||
*/
|
||||
void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount);
|
||||
|
||||
|
||||
// Manual Control
|
||||
void nextFrame();
|
||||
void previousFrame();
|
||||
|
||||
/**
|
||||
* Switch without transition to frame `frame`.
|
||||
*/
|
||||
void switchToFrame(uint8_t frame);
|
||||
|
||||
/**
|
||||
* Transition to frame `frame`, when the `frame` number is bigger than the current
|
||||
* frame the forward animation will be used, otherwise the backwards animation is used.
|
||||
*/
|
||||
void transitionToFrame(uint8_t frame);
|
||||
|
||||
// State Info
|
||||
OLEDDisplayUiState* getUiState();
|
||||
|
||||
int16_t update();
|
||||
};
|
||||
#endif
|
||||
39
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/src/SH1106.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SH1106_h
|
||||
#define SH1106_h
|
||||
#include "SH1106Wire.h"
|
||||
|
||||
// For make SH1106 an alias for SH1106Wire
|
||||
typedef SH1106Wire SH1106;
|
||||
|
||||
|
||||
#endif
|
||||
141
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/src/SH1106Brzo.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SH1106Brzo_h
|
||||
#define SH1106Brzo_h
|
||||
|
||||
#include "OLEDDisplay.h"
|
||||
#include <brzo_i2c.h>
|
||||
|
||||
#if F_CPU == 160000000L
|
||||
#define BRZO_I2C_SPEED 1000
|
||||
#else
|
||||
#define BRZO_I2C_SPEED 800
|
||||
#endif
|
||||
|
||||
class SH1106Brzo : public OLEDDisplay {
|
||||
private:
|
||||
uint8_t _address;
|
||||
uint8_t _sda;
|
||||
uint8_t _scl;
|
||||
|
||||
public:
|
||||
SH1106Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) {
|
||||
setGeometry(g);
|
||||
|
||||
this->_address = _address;
|
||||
this->_sda = _sda;
|
||||
this->_scl = _scl;
|
||||
}
|
||||
|
||||
bool connect(){
|
||||
brzo_i2c_setup(_sda, _scl, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void display(void) {
|
||||
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
|
||||
uint8_t minBoundY = UINT8_MAX;
|
||||
uint8_t maxBoundY = 0;
|
||||
|
||||
uint8_t minBoundX = UINT8_MAX;
|
||||
uint8_t maxBoundX = 0;
|
||||
uint8_t x, y;
|
||||
|
||||
// Calculate the Y bounding box of changes
|
||||
// and copy buffer[pos] to buffer_back[pos];
|
||||
for (y = 0; y < (displayHeight / 8); y++) {
|
||||
for (x = 0; x < displayWidth; x++) {
|
||||
uint16_t pos = x + y * displayWidth;
|
||||
if (buffer[pos] != buffer_back[pos]) {
|
||||
minBoundY = _min(minBoundY, y);
|
||||
maxBoundY = _max(maxBoundY, y);
|
||||
minBoundX = _min(minBoundX, x);
|
||||
maxBoundX = _max(maxBoundX, x);
|
||||
}
|
||||
buffer_back[pos] = buffer[pos];
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
// If the minBoundY wasn't updated
|
||||
// we can savely assume that buffer_back[pos] == buffer[pos]
|
||||
// holdes true for all values of pos
|
||||
if (minBoundY == UINT8_MAX) return;
|
||||
|
||||
byte k = 0;
|
||||
uint8_t sendBuffer[17];
|
||||
sendBuffer[0] = 0x40;
|
||||
|
||||
// Calculate the colum offset
|
||||
uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
|
||||
uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );
|
||||
|
||||
brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED);
|
||||
|
||||
for (y = minBoundY; y <= maxBoundY; y++) {
|
||||
sendCommand(0xB0 + y);
|
||||
sendCommand(minBoundXp2H);
|
||||
sendCommand(minBoundXp2L);
|
||||
for (x = minBoundX; x <= maxBoundX; x++) {
|
||||
k++;
|
||||
sendBuffer[k] = buffer[x + y * displayWidth];
|
||||
if (k == 16) {
|
||||
brzo_i2c_write(sendBuffer, 17, true);
|
||||
k = 0;
|
||||
}
|
||||
}
|
||||
if (k != 0) {
|
||||
brzo_i2c_write(sendBuffer, k + 1, true);
|
||||
k = 0;
|
||||
}
|
||||
yield();
|
||||
}
|
||||
if (k != 0) {
|
||||
brzo_i2c_write(sendBuffer, k + 1, true);
|
||||
}
|
||||
brzo_i2c_end_transaction();
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
int getBufferOffset(void) {
|
||||
return 0;
|
||||
}
|
||||
inline void sendCommand(uint8_t com) __attribute__((always_inline)){
|
||||
uint8_t command[2] = {0x80 /* command mode */, com};
|
||||
brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED);
|
||||
brzo_i2c_write(command, 2, true);
|
||||
brzo_i2c_end_transaction();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
135
P1_gateway_FW/lib/esp8266-oled-ssd1306-4.2.0/src/SH1106Spi.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ThingPulse invests considerable time and money to develop these open source libraries.
|
||||
* Please support us by buying our products (and not the clones) from
|
||||
* https://thingpulse.com
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SH1106Spi_h
|
||||
#define SH1106Spi_h
|
||||
|
||||
#include "OLEDDisplay.h"
|
||||
#include <SPI.h>
|
||||
|
||||
class SH1106Spi : public OLEDDisplay {
|
||||
private:
|
||||
uint8_t _rst;
|
||||
uint8_t _dc;
|
||||
|
||||
public:
|
||||
SH1106Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) {
|
||||
setGeometry(g);
|
||||
|
||||
this->_rst = _rst;
|
||||
this->_dc = _dc;
|
||||
}
|
||||
|
||||
bool connect(){
|
||||
pinMode(_dc, OUTPUT);
|
||||
pinMode(_rst, OUTPUT);
|
||||
|
||||
SPI.begin ();
|
||||
SPI.setClockDivider (SPI_CLOCK_DIV2);
|
||||
|
||||
// Pulse Reset low for 10ms
|
||||
digitalWrite(_rst, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(_rst, LOW);
|
||||
delay(10);
|
||||
digitalWrite(_rst, HIGH);
|
||||
return true;
|
||||
}
|
||||
|
||||
void display(void) {
|
||||
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
|
||||
uint8_t minBoundY = UINT8_MAX;
|
||||
uint8_t maxBoundY = 0;
|
||||
|
||||
uint8_t minBoundX = UINT8_MAX;
|
||||
uint8_t maxBoundX = 0;
|
||||
|
||||
uint8_t x, y;
|
||||
|
||||
// Calculate the Y bounding box of changes
|
||||
// and copy buffer[pos] to buffer_back[pos];
|
||||
for (y = 0; y < (displayHeight / 8); y++) {
|
||||
for (x = 0; x < displayWidth; x++) {
|
||||
uint16_t pos = x + y * displayWidth;
|
||||
if (buffer[pos] != buffer_back[pos]) {
|
||||
minBoundY = _min(minBoundY, y);
|
||||
maxBoundY = _max(maxBoundY, y);
|
||||
minBoundX = _min(minBoundX, x);
|
||||
maxBoundX = _max(maxBoundX, x);
|
||||
}
|
||||
buffer_back[pos] = buffer[pos];
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
// If the minBoundY wasn't updated
|
||||
// we can savely assume that buffer_back[pos] == buffer[pos]
|
||||
// holdes true for all values of pos
|
||||
if (minBoundY == UINT8_MAX) return;
|
||||
|
||||
// Calculate the colum offset
|
||||
uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
|
||||
uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );
|
||||
|
||||
for (y = minBoundY; y <= maxBoundY; y++) {
|
||||
sendCommand(0xB0 + y);
|
||||
sendCommand(minBoundXp2H);
|
||||
sendCommand(minBoundXp2L);
|
||||
digitalWrite(_dc, HIGH); // data mode
|
||||
for (x = minBoundX; x <= maxBoundX; x++) {
|
||||
SPI.transfer(buffer[x + y * displayWidth]);
|
||||
}
|
||||
yield();
|
||||
}
|
||||
#else
|
||||
for (uint8_t y=0; y<displayHeight/8; y++) {
|
||||
sendCommand(0xB0 + y);
|
||||
sendCommand(0x02);
|
||||
sendCommand(0x10);
|
||||
digitalWrite(_dc, HIGH); // data mode
|
||||
for( uint8_t x=0; x < displayWidth; x++) {
|
||||
SPI.transfer(buffer[x + y * displayWidth]);
|
||||
}
|
||||
yield();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
int getBufferOffset(void) {
|
||||
return 0;
|
||||
}
|
||||
inline void sendCommand(uint8_t com) __attribute__((always_inline)){
|
||||
digitalWrite(_dc, LOW);
|
||||
SPI.transfer(com);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||