Kan læse og sende til API
Egentligt initial commit. Kan læse tag og sende til API
This commit is contained in:
39
.gitignore
vendored
39
.gitignore
vendored
@@ -1,34 +1,5 @@
|
|||||||
# ---> C++
|
.pio
|
||||||
# Prerequisites
|
.vscode/.browse.c_cpp.db*
|
||||||
*.d
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
# Compiled Object files
|
.vscode/ipch
|
||||||
*.slo
|
|
||||||
*.lo
|
|
||||||
*.o
|
|
||||||
*.obj
|
|
||||||
|
|
||||||
# Precompiled Headers
|
|
||||||
*.gch
|
|
||||||
*.pch
|
|
||||||
|
|
||||||
# Compiled Dynamic libraries
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
*.dll
|
|
||||||
|
|
||||||
# Fortran module files
|
|
||||||
*.mod
|
|
||||||
*.smod
|
|
||||||
|
|
||||||
# Compiled Static libraries
|
|
||||||
*.lai
|
|
||||||
*.la
|
|
||||||
*.a
|
|
||||||
*.lib
|
|
||||||
|
|
||||||
# Executables
|
|
||||||
*.exe
|
|
||||||
*.out
|
|
||||||
*.app
|
|
||||||
|
|
||||||
|
|||||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
|
]
|
||||||
|
}
|
||||||
31
data/root_ca.pem
Normal file
31
data/root_ca.pem
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||||
|
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||||
|
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||||
|
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||||
|
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||||
|
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||||
|
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||||
|
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||||
|
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||||
|
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||||
|
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||||
|
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||||
|
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||||
|
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||||
|
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||||
|
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||||
|
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||||
|
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||||
|
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||||
|
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||||
|
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||||
|
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||||
|
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||||
|
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||||
|
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||||
|
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||||
|
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||||
|
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
15
include/DeviceIdentity.h
Normal file
15
include/DeviceIdentity.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "FilamentGuardDeviceInfo.h"
|
||||||
|
|
||||||
|
class DeviceIdentity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FilamentGuardDeviceInfo create(
|
||||||
|
const String& hostname) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String createDeviceId() const;
|
||||||
|
};
|
||||||
18
include/DeviceSettings.h
Normal file
18
include/DeviceSettings.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class DeviceSettings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
String getHostname();
|
||||||
|
String getOrCreateHostname();
|
||||||
|
|
||||||
|
bool setHostname(const String& hostname);
|
||||||
|
bool clearHostname();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static String createDefaultHostname();
|
||||||
|
static String normalizeHostname(const String& hostname);
|
||||||
|
static bool isValidHostname(const String& hostname);
|
||||||
|
};
|
||||||
20
include/FilamentGuardApiClient.h
Normal file
20
include/FilamentGuardApiClient.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class FilamentGuardApiClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool sendTagRead(
|
||||||
|
const String& json) const;
|
||||||
|
|
||||||
|
bool sendTagEvent(
|
||||||
|
const String& json) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr const char* TagReadEndpointUrl =
|
||||||
|
"http://filamentguard.maximuss.dk/";
|
||||||
|
|
||||||
|
static constexpr const char* TagEventEndpointUrl =
|
||||||
|
"http://filamentguard.maximuss.dk/";
|
||||||
|
};
|
||||||
9
include/FilamentGuardDeviceInfo.h
Normal file
9
include/FilamentGuardDeviceInfo.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
struct FilamentGuardDeviceInfo
|
||||||
|
{
|
||||||
|
String deviceId;
|
||||||
|
String hostname;
|
||||||
|
};
|
||||||
24
include/FilamentGuardTagEvent.h
Normal file
24
include/FilamentGuardTagEvent.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
enum class FilamentGuardTagEventType
|
||||||
|
{
|
||||||
|
TagPlaced,
|
||||||
|
TagRemoved
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FilamentGuardTagEvent
|
||||||
|
{
|
||||||
|
String deviceId;
|
||||||
|
String hostname;
|
||||||
|
|
||||||
|
FilamentGuardTagEventType eventType;
|
||||||
|
|
||||||
|
String tagUid;
|
||||||
|
String instanceUuid;
|
||||||
|
|
||||||
|
String tagJson;
|
||||||
|
|
||||||
|
unsigned long uptimeMilliseconds = 0;
|
||||||
|
};
|
||||||
16
include/FilamentGuardTagEventSerializer.h
Normal file
16
include/FilamentGuardTagEventSerializer.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "FilamentGuardTagEvent.h"
|
||||||
|
|
||||||
|
class FilamentGuardTagEventSerializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static String serialize(
|
||||||
|
const FilamentGuardTagEvent& tagEvent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const char* eventTypeToString(
|
||||||
|
FilamentGuardTagEventType eventType);
|
||||||
|
};
|
||||||
12
include/FilamentGuardTagRead.h
Normal file
12
include/FilamentGuardTagRead.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "FilamentGuardDeviceInfo.h"
|
||||||
|
#include "OpenPrintTagData.h"
|
||||||
|
|
||||||
|
struct FilamentGuardTagRead
|
||||||
|
{
|
||||||
|
FilamentGuardDeviceInfo device;
|
||||||
|
OpenPrintTagData tag;
|
||||||
|
};
|
||||||
34
include/FilamentGuardTagReadSerializer.h
Normal file
34
include/FilamentGuardTagReadSerializer.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "FilamentGuardTagRead.h"
|
||||||
|
|
||||||
|
class FilamentGuardTagReadSerializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
String serialize(
|
||||||
|
const FilamentGuardTagRead& tagRead) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String escapeJson(
|
||||||
|
const String& value) const;
|
||||||
|
|
||||||
|
void appendStringProperty(
|
||||||
|
String& json,
|
||||||
|
const char* propertyName,
|
||||||
|
const String& value,
|
||||||
|
bool addComma = true) const;
|
||||||
|
|
||||||
|
void appendUnsignedProperty(
|
||||||
|
String& json,
|
||||||
|
const char* propertyName,
|
||||||
|
uint16_t value,
|
||||||
|
bool addComma = true) const;
|
||||||
|
|
||||||
|
void appendBooleanProperty(
|
||||||
|
String& json,
|
||||||
|
const char* propertyName,
|
||||||
|
bool value,
|
||||||
|
bool addComma = true) const;
|
||||||
|
};
|
||||||
8
include/FirmwareUpdater.h
Normal file
8
include/FirmwareUpdater.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class FirmwareUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool begin();
|
||||||
|
void checkForUpdate();
|
||||||
|
};
|
||||||
25
include/OpenPrintTagData.h
Normal file
25
include/OpenPrintTagData.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
struct OpenPrintTagData
|
||||||
|
{
|
||||||
|
String tagUid;
|
||||||
|
String instanceUuid;
|
||||||
|
|
||||||
|
String materialName;
|
||||||
|
String materialType;
|
||||||
|
String brand;
|
||||||
|
String colorHex;
|
||||||
|
|
||||||
|
uint16_t actualWeightGrams = 0;
|
||||||
|
uint16_t nominalWeightGrams = 0;
|
||||||
|
uint16_t spoolWeightGrams = 0;
|
||||||
|
uint16_t consumedWeightGrams = 0;
|
||||||
|
uint16_t remainingWeightGrams = 0;
|
||||||
|
|
||||||
|
bool hasActualWeight = false;
|
||||||
|
bool hasNominalWeight = false;
|
||||||
|
bool hasSpoolWeight = false;
|
||||||
|
bool hasConsumedWeight = false;
|
||||||
|
};
|
||||||
73
include/OpenPrintTagParser.h
Normal file
73
include/OpenPrintTagParser.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <cbor.h>
|
||||||
|
|
||||||
|
#include "OpenPrintTagData.h"
|
||||||
|
|
||||||
|
class OpenPrintTagParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool parse(
|
||||||
|
const uint8_t* memory,
|
||||||
|
size_t memoryLength,
|
||||||
|
OpenPrintTagData& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr uint8_t NdefTlvType = 0x03;
|
||||||
|
static constexpr uint8_t TerminatorTlvType = 0xFE;
|
||||||
|
|
||||||
|
static constexpr const char* ExpectedMimeType =
|
||||||
|
"application/vnd.openprinttag";
|
||||||
|
|
||||||
|
bool findNdefMessage(
|
||||||
|
const uint8_t* memory,
|
||||||
|
size_t memoryLength,
|
||||||
|
const uint8_t*& ndefMessage,
|
||||||
|
size_t& ndefMessageLength) const;
|
||||||
|
|
||||||
|
bool parseNdefRecord(
|
||||||
|
const uint8_t* ndefMessage,
|
||||||
|
size_t ndefMessageLength,
|
||||||
|
const uint8_t*& payload,
|
||||||
|
size_t& payloadLength) const;
|
||||||
|
|
||||||
|
bool mimeTypeMatches(
|
||||||
|
const uint8_t* mimeType,
|
||||||
|
size_t mimeTypeLength) const;
|
||||||
|
|
||||||
|
bool parseCborPayload(
|
||||||
|
const uint8_t* payload,
|
||||||
|
size_t payloadLength,
|
||||||
|
OpenPrintTagData& data) const;
|
||||||
|
|
||||||
|
bool parseMainData(
|
||||||
|
const uint8_t* mainData,
|
||||||
|
size_t mainDataLength,
|
||||||
|
OpenPrintTagData& data) const;
|
||||||
|
|
||||||
|
bool readUnsignedInteger(
|
||||||
|
CborValue* value,
|
||||||
|
uint64_t& result) const;
|
||||||
|
|
||||||
|
bool readTextString(
|
||||||
|
CborValue* value,
|
||||||
|
String& result) const;
|
||||||
|
|
||||||
|
bool readByteString(
|
||||||
|
CborValue* value,
|
||||||
|
uint8_t* buffer,
|
||||||
|
size_t bufferCapacity,
|
||||||
|
size_t& bytesRead) const;
|
||||||
|
|
||||||
|
bool skipValue(
|
||||||
|
CborValue* value) const;
|
||||||
|
|
||||||
|
String formatUuid(
|
||||||
|
const uint8_t* bytes,
|
||||||
|
size_t length) const;
|
||||||
|
|
||||||
|
String formatColor(
|
||||||
|
const uint8_t* bytes,
|
||||||
|
size_t length) const;
|
||||||
|
};
|
||||||
99
include/Pn5180Reader.h
Normal file
99
include/Pn5180Reader.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <PN5180ISO14443.h>
|
||||||
|
#include <PN5180ISO15693.h>
|
||||||
|
|
||||||
|
#include "FilamentGuardApiClient.h"
|
||||||
|
#include "FilamentGuardDeviceInfo.h"
|
||||||
|
#include "FilamentGuardTagRead.h"
|
||||||
|
#include "FilamentGuardTagReadSerializer.h"
|
||||||
|
#include "OpenPrintTagData.h"
|
||||||
|
#include "OpenPrintTagParser.h"
|
||||||
|
|
||||||
|
class Pn5180Reader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pn5180Reader();
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
void setDeviceInfo(
|
||||||
|
const FilamentGuardDeviceInfo& deviceInfo);
|
||||||
|
|
||||||
|
void printVersionInformation();
|
||||||
|
void checkForTag();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class TagProtocol : uint8_t
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Iso14443A,
|
||||||
|
Iso15693
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t NssPin = 5;
|
||||||
|
static constexpr uint8_t BusyPin = 16;
|
||||||
|
static constexpr uint8_t ResetPin = 17;
|
||||||
|
|
||||||
|
static constexpr uint8_t RequiredMissingScans = 3;
|
||||||
|
|
||||||
|
PN5180ISO15693 iso15693Reader;
|
||||||
|
PN5180ISO14443 iso14443Reader;
|
||||||
|
|
||||||
|
OpenPrintTagParser openPrintTagParser;
|
||||||
|
FilamentGuardTagReadSerializer tagReadSerializer;
|
||||||
|
FilamentGuardApiClient apiClient;
|
||||||
|
|
||||||
|
FilamentGuardDeviceInfo deviceInfo;
|
||||||
|
|
||||||
|
uint8_t lastUid[10] = {};
|
||||||
|
uint8_t lastUidLength = 0;
|
||||||
|
|
||||||
|
TagProtocol activeProtocol = TagProtocol::None;
|
||||||
|
uint8_t missingScanCount = 0;
|
||||||
|
|
||||||
|
bool checkIso14443Tag();
|
||||||
|
bool checkIso15693Tag();
|
||||||
|
|
||||||
|
void handleTagMissing();
|
||||||
|
|
||||||
|
bool isSameUid(
|
||||||
|
const uint8_t* uid,
|
||||||
|
uint8_t uidLength) const;
|
||||||
|
|
||||||
|
void rememberUid(
|
||||||
|
const uint8_t* uid,
|
||||||
|
uint8_t uidLength,
|
||||||
|
TagProtocol protocol);
|
||||||
|
|
||||||
|
void clearLastUid();
|
||||||
|
|
||||||
|
void printUid(
|
||||||
|
const uint8_t* uid,
|
||||||
|
uint8_t uidLength,
|
||||||
|
bool reverseOrder) const;
|
||||||
|
|
||||||
|
String formatUid(
|
||||||
|
const uint8_t* uid,
|
||||||
|
uint8_t uidLength,
|
||||||
|
bool reverseOrder) const;
|
||||||
|
|
||||||
|
void dumpIso15693Tag(
|
||||||
|
uint8_t* uid,
|
||||||
|
uint8_t uidLength);
|
||||||
|
|
||||||
|
void printTagRead(
|
||||||
|
const FilamentGuardTagRead& tagRead) const;
|
||||||
|
|
||||||
|
void printTagReadJson(
|
||||||
|
const FilamentGuardTagRead& tagRead) const;
|
||||||
|
|
||||||
|
void sendTagRead(
|
||||||
|
const FilamentGuardTagRead& tagRead) const;
|
||||||
|
|
||||||
|
void printBlock(
|
||||||
|
uint8_t blockNumber,
|
||||||
|
const uint8_t* data,
|
||||||
|
uint8_t blockSize) const;
|
||||||
|
};
|
||||||
37
include/README
Normal file
37
include/README
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the convention is to give header files names that end with `.h'.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||||
8
include/WifiSetup.h
Normal file
8
include/WifiSetup.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class WifiSetup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool connect(const char* hostname);
|
||||||
|
void resetSettings();
|
||||||
|
};
|
||||||
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into the executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a separate directory
|
||||||
|
("lib/your_library_name/[Code]").
|
||||||
|
|
||||||
|
For example, see the structure of the following example libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
Example contents of `src/main.c` using Foo and Bar:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries by scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||||
23
platformio.ini
Normal file
23
platformio.ini
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
; 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
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:esp32dev]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp32dev
|
||||||
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
||||||
|
upload_port = COM3
|
||||||
|
monitor_port = COM3
|
||||||
|
board_build.filesystem = spiffs
|
||||||
|
lib_deps =
|
||||||
|
tzapu/WiFiManager
|
||||||
|
chrisjoyce911/esp32FOTA
|
||||||
|
https://github.com/ATrappmann/PN5180-Library.git
|
||||||
|
soburi/TinyCBOR@^0.5.3-arduino2
|
||||||
36
src/DeviceIdentity.cpp
Normal file
36
src/DeviceIdentity.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include "DeviceIdentity.h"
|
||||||
|
|
||||||
|
#include <Esp.h>
|
||||||
|
|
||||||
|
FilamentGuardDeviceInfo DeviceIdentity::create(
|
||||||
|
const String& hostname) const
|
||||||
|
{
|
||||||
|
FilamentGuardDeviceInfo deviceInfo;
|
||||||
|
|
||||||
|
deviceInfo.deviceId =
|
||||||
|
createDeviceId();
|
||||||
|
|
||||||
|
deviceInfo.hostname =
|
||||||
|
hostname;
|
||||||
|
|
||||||
|
return deviceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
String DeviceIdentity::createDeviceId() const
|
||||||
|
{
|
||||||
|
const uint64_t chipId =
|
||||||
|
ESP.getEfuseMac();
|
||||||
|
|
||||||
|
char buffer[19] = {};
|
||||||
|
|
||||||
|
std::snprintf(
|
||||||
|
buffer,
|
||||||
|
sizeof(buffer),
|
||||||
|
"ESP32-%04X%08X",
|
||||||
|
static_cast<uint16_t>(
|
||||||
|
chipId >> 32),
|
||||||
|
static_cast<uint32_t>(
|
||||||
|
chipId));
|
||||||
|
|
||||||
|
return String(buffer);
|
||||||
|
}
|
||||||
159
src/DeviceSettings.cpp
Normal file
159
src/DeviceSettings.cpp
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
#include "DeviceSettings.h"
|
||||||
|
|
||||||
|
#include <Preferences.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr const char* PreferencesNamespace = "device";
|
||||||
|
constexpr const char* HostnameKey = "hostname";
|
||||||
|
}
|
||||||
|
|
||||||
|
String DeviceSettings::getHostname()
|
||||||
|
{
|
||||||
|
Preferences preferences;
|
||||||
|
|
||||||
|
if (!preferences.begin(PreferencesNamespace, true))
|
||||||
|
{
|
||||||
|
Serial.println("Kunne ikke åbne indstillinger.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const String hostname =
|
||||||
|
preferences.getString(HostnameKey, "");
|
||||||
|
|
||||||
|
preferences.end();
|
||||||
|
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
String DeviceSettings::getOrCreateHostname()
|
||||||
|
{
|
||||||
|
String hostname = getHostname();
|
||||||
|
|
||||||
|
if (isValidHostname(hostname))
|
||||||
|
{
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname = createDefaultHostname();
|
||||||
|
|
||||||
|
if (!setHostname(hostname))
|
||||||
|
{
|
||||||
|
Serial.println("Kunne ikke gemme standard-hostname.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceSettings::setHostname(const String& hostname)
|
||||||
|
{
|
||||||
|
const String normalizedHostname =
|
||||||
|
normalizeHostname(hostname);
|
||||||
|
|
||||||
|
if (!isValidHostname(normalizedHostname))
|
||||||
|
{
|
||||||
|
Serial.println("Ugyldigt hostname.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences preferences;
|
||||||
|
|
||||||
|
if (!preferences.begin(PreferencesNamespace, false))
|
||||||
|
{
|
||||||
|
Serial.println("Kunne ikke åbne indstillinger.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t written =
|
||||||
|
preferences.putString(
|
||||||
|
HostnameKey,
|
||||||
|
normalizedHostname
|
||||||
|
);
|
||||||
|
|
||||||
|
preferences.end();
|
||||||
|
|
||||||
|
return written > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceSettings::clearHostname()
|
||||||
|
{
|
||||||
|
Preferences preferences;
|
||||||
|
|
||||||
|
if (!preferences.begin(PreferencesNamespace, false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool removed =
|
||||||
|
preferences.remove(HostnameKey);
|
||||||
|
|
||||||
|
preferences.end();
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
String DeviceSettings::createDefaultHostname()
|
||||||
|
{
|
||||||
|
const uint64_t chipId =
|
||||||
|
ESP.getEfuseMac();
|
||||||
|
|
||||||
|
char hostname[32];
|
||||||
|
|
||||||
|
snprintf(
|
||||||
|
hostname,
|
||||||
|
sizeof(hostname),
|
||||||
|
"filamentguard-%06llx",
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
chipId & 0xFFFFFF
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return String(hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
String DeviceSettings::normalizeHostname(
|
||||||
|
const String& hostname
|
||||||
|
)
|
||||||
|
{
|
||||||
|
String normalized = hostname;
|
||||||
|
|
||||||
|
normalized.trim();
|
||||||
|
normalized.toLowerCase();
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceSettings::isValidHostname(
|
||||||
|
const String& hostname
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const size_t length = hostname.length();
|
||||||
|
|
||||||
|
if (length == 0 || length > 32)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostname[0] == '-' ||
|
||||||
|
hostname[length - 1] == '-')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t index = 0; index < length; index++)
|
||||||
|
{
|
||||||
|
const char character = hostname[index];
|
||||||
|
|
||||||
|
const bool valid =
|
||||||
|
(character >= 'a' && character <= 'z') ||
|
||||||
|
(character >= '0' && character <= '9') ||
|
||||||
|
character == '-';
|
||||||
|
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
121
src/FilamentGuardApiClient.cpp
Normal file
121
src/FilamentGuardApiClient.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#include "FilamentGuardApiClient.h"
|
||||||
|
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool postJson(
|
||||||
|
const char* endpointUrl,
|
||||||
|
const String& json,
|
||||||
|
const char* description)
|
||||||
|
{
|
||||||
|
if (WiFi.status() != WL_CONNECTED)
|
||||||
|
{
|
||||||
|
Serial.print(description);
|
||||||
|
Serial.println(
|
||||||
|
" blev ikke sendt: Wi-Fi er ikke forbundet.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.isEmpty())
|
||||||
|
{
|
||||||
|
Serial.print(description);
|
||||||
|
Serial.println(
|
||||||
|
" blev ikke sendt: JSON-data er tomme.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPClient httpClient;
|
||||||
|
|
||||||
|
if (!httpClient.begin(endpointUrl))
|
||||||
|
{
|
||||||
|
Serial.print(description);
|
||||||
|
Serial.println(
|
||||||
|
" kunne ikke initialiseres.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient.addHeader(
|
||||||
|
"Content-Type",
|
||||||
|
"application/json");
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.print("Sender ");
|
||||||
|
Serial.print(description);
|
||||||
|
Serial.print(" til: ");
|
||||||
|
Serial.println(endpointUrl);
|
||||||
|
|
||||||
|
const int responseCode =
|
||||||
|
httpClient.POST(json);
|
||||||
|
|
||||||
|
if (responseCode <= 0)
|
||||||
|
{
|
||||||
|
Serial.print(description);
|
||||||
|
Serial.print(
|
||||||
|
" fejlede. HTTPClient-fejl: ");
|
||||||
|
|
||||||
|
Serial.println(
|
||||||
|
httpClient.errorToString(
|
||||||
|
responseCode));
|
||||||
|
|
||||||
|
httpClient.end();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String responseBody =
|
||||||
|
httpClient.getString();
|
||||||
|
|
||||||
|
Serial.print("HTTP-status: ");
|
||||||
|
Serial.println(responseCode);
|
||||||
|
|
||||||
|
if (!responseBody.isEmpty())
|
||||||
|
{
|
||||||
|
Serial.println("Svar fra API:");
|
||||||
|
Serial.println(responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool successful =
|
||||||
|
responseCode >= 200 &&
|
||||||
|
responseCode < 300;
|
||||||
|
|
||||||
|
if (successful)
|
||||||
|
{
|
||||||
|
Serial.print(description);
|
||||||
|
Serial.println(
|
||||||
|
" blev sendt korrekt.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print(description);
|
||||||
|
Serial.println(
|
||||||
|
" blev afvist af API'et.");
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient.end();
|
||||||
|
|
||||||
|
return successful;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FilamentGuardApiClient::sendTagRead(
|
||||||
|
const String& json) const
|
||||||
|
{
|
||||||
|
return postJson(
|
||||||
|
TagReadEndpointUrl,
|
||||||
|
json,
|
||||||
|
"tagregistrering");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FilamentGuardApiClient::sendTagEvent(
|
||||||
|
const String& json) const
|
||||||
|
{
|
||||||
|
return postJson(
|
||||||
|
TagEventEndpointUrl,
|
||||||
|
json,
|
||||||
|
"taghændelse");
|
||||||
|
}
|
||||||
55
src/FilamentGuardTagEventSerializer.cpp
Normal file
55
src/FilamentGuardTagEventSerializer.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#include "FilamentGuardTagEventSerializer.h"
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
String FilamentGuardTagEventSerializer::serialize(
|
||||||
|
const FilamentGuardTagEvent& tagEvent)
|
||||||
|
{
|
||||||
|
JsonDocument document;
|
||||||
|
|
||||||
|
document["deviceId"] =
|
||||||
|
tagEvent.deviceId;
|
||||||
|
|
||||||
|
document["hostname"] =
|
||||||
|
tagEvent.hostname;
|
||||||
|
|
||||||
|
document["eventType"] =
|
||||||
|
eventTypeToString(
|
||||||
|
tagEvent.eventType);
|
||||||
|
|
||||||
|
document["tagUid"] =
|
||||||
|
tagEvent.tagUid;
|
||||||
|
|
||||||
|
document["instanceUuid"] =
|
||||||
|
tagEvent.instanceUuid;
|
||||||
|
|
||||||
|
document["tagJson"] =
|
||||||
|
tagEvent.tagJson;
|
||||||
|
|
||||||
|
document["uptimeMilliseconds"] =
|
||||||
|
tagEvent.uptimeMilliseconds;
|
||||||
|
|
||||||
|
String json;
|
||||||
|
|
||||||
|
serializeJson(
|
||||||
|
document,
|
||||||
|
json);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* FilamentGuardTagEventSerializer::eventTypeToString(
|
||||||
|
const FilamentGuardTagEventType eventType)
|
||||||
|
{
|
||||||
|
switch (eventType)
|
||||||
|
{
|
||||||
|
case FilamentGuardTagEventType::TagPlaced:
|
||||||
|
return "TagPlaced";
|
||||||
|
|
||||||
|
case FilamentGuardTagEventType::TagRemoved:
|
||||||
|
return "TagRemoved";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
235
src/FilamentGuardTagReadSerializer.cpp
Normal file
235
src/FilamentGuardTagReadSerializer.cpp
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
#include "FilamentGuardTagReadSerializer.h"
|
||||||
|
|
||||||
|
String FilamentGuardTagReadSerializer::serialize(
|
||||||
|
const FilamentGuardTagRead& tagRead) const
|
||||||
|
{
|
||||||
|
String json;
|
||||||
|
|
||||||
|
json.reserve(768);
|
||||||
|
|
||||||
|
json += '{';
|
||||||
|
|
||||||
|
json += "\"device\":{";
|
||||||
|
|
||||||
|
appendStringProperty(
|
||||||
|
json,
|
||||||
|
"deviceId",
|
||||||
|
tagRead.device.deviceId);
|
||||||
|
|
||||||
|
appendStringProperty(
|
||||||
|
json,
|
||||||
|
"hostname",
|
||||||
|
tagRead.device.hostname,
|
||||||
|
false);
|
||||||
|
|
||||||
|
json += "},";
|
||||||
|
|
||||||
|
json += "\"tag\":{";
|
||||||
|
|
||||||
|
appendStringProperty(
|
||||||
|
json,
|
||||||
|
"tagUid",
|
||||||
|
tagRead.tag.tagUid);
|
||||||
|
|
||||||
|
appendStringProperty(
|
||||||
|
json,
|
||||||
|
"instanceUuid",
|
||||||
|
tagRead.tag.instanceUuid);
|
||||||
|
|
||||||
|
appendStringProperty(
|
||||||
|
json,
|
||||||
|
"materialName",
|
||||||
|
tagRead.tag.materialName);
|
||||||
|
|
||||||
|
appendStringProperty(
|
||||||
|
json,
|
||||||
|
"materialType",
|
||||||
|
tagRead.tag.materialType);
|
||||||
|
|
||||||
|
appendStringProperty(
|
||||||
|
json,
|
||||||
|
"brand",
|
||||||
|
tagRead.tag.brand);
|
||||||
|
|
||||||
|
appendStringProperty(
|
||||||
|
json,
|
||||||
|
"colorHex",
|
||||||
|
tagRead.tag.colorHex);
|
||||||
|
|
||||||
|
appendUnsignedProperty(
|
||||||
|
json,
|
||||||
|
"actualWeightGrams",
|
||||||
|
tagRead.tag.actualWeightGrams);
|
||||||
|
|
||||||
|
appendUnsignedProperty(
|
||||||
|
json,
|
||||||
|
"nominalWeightGrams",
|
||||||
|
tagRead.tag.nominalWeightGrams);
|
||||||
|
|
||||||
|
appendUnsignedProperty(
|
||||||
|
json,
|
||||||
|
"spoolWeightGrams",
|
||||||
|
tagRead.tag.spoolWeightGrams);
|
||||||
|
|
||||||
|
appendUnsignedProperty(
|
||||||
|
json,
|
||||||
|
"consumedWeightGrams",
|
||||||
|
tagRead.tag.consumedWeightGrams);
|
||||||
|
|
||||||
|
appendUnsignedProperty(
|
||||||
|
json,
|
||||||
|
"remainingWeightGrams",
|
||||||
|
tagRead.tag.remainingWeightGrams);
|
||||||
|
|
||||||
|
appendBooleanProperty(
|
||||||
|
json,
|
||||||
|
"hasActualWeight",
|
||||||
|
tagRead.tag.hasActualWeight);
|
||||||
|
|
||||||
|
appendBooleanProperty(
|
||||||
|
json,
|
||||||
|
"hasNominalWeight",
|
||||||
|
tagRead.tag.hasNominalWeight);
|
||||||
|
|
||||||
|
appendBooleanProperty(
|
||||||
|
json,
|
||||||
|
"hasSpoolWeight",
|
||||||
|
tagRead.tag.hasSpoolWeight);
|
||||||
|
|
||||||
|
appendBooleanProperty(
|
||||||
|
json,
|
||||||
|
"hasConsumedWeight",
|
||||||
|
tagRead.tag.hasConsumedWeight,
|
||||||
|
false);
|
||||||
|
|
||||||
|
json += '}';
|
||||||
|
|
||||||
|
json += '}';
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
String FilamentGuardTagReadSerializer::escapeJson(
|
||||||
|
const String& value) const
|
||||||
|
{
|
||||||
|
String escaped;
|
||||||
|
|
||||||
|
escaped.reserve(
|
||||||
|
value.length() + 16);
|
||||||
|
|
||||||
|
for (size_t index = 0;
|
||||||
|
index < value.length();
|
||||||
|
++index)
|
||||||
|
{
|
||||||
|
const char character =
|
||||||
|
value[index];
|
||||||
|
|
||||||
|
switch (character)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
escaped += "\\\"";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
escaped += "\\\\";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\b':
|
||||||
|
escaped += "\\b";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\f':
|
||||||
|
escaped += "\\f";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
escaped += "\\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\r':
|
||||||
|
escaped += "\\r";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\t':
|
||||||
|
escaped += "\\t";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (static_cast<uint8_t>(character) < 0x20)
|
||||||
|
{
|
||||||
|
char buffer[7] = {};
|
||||||
|
|
||||||
|
std::snprintf(
|
||||||
|
buffer,
|
||||||
|
sizeof(buffer),
|
||||||
|
"\\u%04X",
|
||||||
|
static_cast<uint8_t>(
|
||||||
|
character));
|
||||||
|
|
||||||
|
escaped += buffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
escaped += character;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilamentGuardTagReadSerializer::appendStringProperty(
|
||||||
|
String& json,
|
||||||
|
const char* propertyName,
|
||||||
|
const String& value,
|
||||||
|
bool addComma) const
|
||||||
|
{
|
||||||
|
json += '"';
|
||||||
|
json += propertyName;
|
||||||
|
json += "\":\"";
|
||||||
|
json += escapeJson(value);
|
||||||
|
json += '"';
|
||||||
|
|
||||||
|
if (addComma)
|
||||||
|
{
|
||||||
|
json += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilamentGuardTagReadSerializer::appendUnsignedProperty(
|
||||||
|
String& json,
|
||||||
|
const char* propertyName,
|
||||||
|
uint16_t value,
|
||||||
|
bool addComma) const
|
||||||
|
{
|
||||||
|
json += '"';
|
||||||
|
json += propertyName;
|
||||||
|
json += "\":";
|
||||||
|
json += String(value);
|
||||||
|
|
||||||
|
if (addComma)
|
||||||
|
{
|
||||||
|
json += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilamentGuardTagReadSerializer::appendBooleanProperty(
|
||||||
|
String& json,
|
||||||
|
const char* propertyName,
|
||||||
|
bool value,
|
||||||
|
bool addComma) const
|
||||||
|
{
|
||||||
|
json += '"';
|
||||||
|
json += propertyName;
|
||||||
|
json += "\":";
|
||||||
|
json += value
|
||||||
|
? "true"
|
||||||
|
: "false";
|
||||||
|
|
||||||
|
if (addComma)
|
||||||
|
{
|
||||||
|
json += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/FirmwareUpdater.cpp
Normal file
111
src/FirmwareUpdater.cpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#include "FirmwareUpdater.h"
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <SPIFFS.h>
|
||||||
|
#include <esp32FOTA.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr const char* FirmwareType = "filamentguard";
|
||||||
|
constexpr const char* FirmwareVersion = "1.0.1";
|
||||||
|
|
||||||
|
constexpr const char* ManifestUrl =
|
||||||
|
"https://ota-filamentguard.maximuss.dk/firmware/manifest.json";
|
||||||
|
|
||||||
|
esp32FOTA firmwareUpdate(
|
||||||
|
FirmwareType,
|
||||||
|
FirmwareVersion
|
||||||
|
);
|
||||||
|
|
||||||
|
CryptoFileAsset rootCertificate(
|
||||||
|
"/root_ca.pem",
|
||||||
|
&SPIFFS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FirmwareUpdater::begin()
|
||||||
|
{
|
||||||
|
Serial.println("Starter firmware-opdatering...");
|
||||||
|
|
||||||
|
if (!SPIFFS.begin(false))
|
||||||
|
{
|
||||||
|
Serial.println("Kunne ikke starte SPIFFS.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SPIFFS.exists("/root_ca.pem"))
|
||||||
|
{
|
||||||
|
Serial.println("Certifikatet /root_ca.pem findes ikke.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
firmwareUpdate.setManifestURL(ManifestUrl);
|
||||||
|
firmwareUpdate.setRootCA(&rootCertificate);
|
||||||
|
|
||||||
|
firmwareUpdate.setProgressCb(
|
||||||
|
[](size_t progress, size_t total)
|
||||||
|
{
|
||||||
|
if (total == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int percentage =
|
||||||
|
static_cast<unsigned int>(
|
||||||
|
(progress * 100) / total
|
||||||
|
);
|
||||||
|
|
||||||
|
Serial.printf(
|
||||||
|
"\rFirmware download: %u%%",
|
||||||
|
percentage
|
||||||
|
);
|
||||||
|
|
||||||
|
if (progress == total)
|
||||||
|
{
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Serial.print("Firmwaretype: ");
|
||||||
|
Serial.println(FirmwareType);
|
||||||
|
|
||||||
|
Serial.print("Firmwareversion: ");
|
||||||
|
Serial.println(FirmwareVersion);
|
||||||
|
|
||||||
|
Serial.print("Manifest: ");
|
||||||
|
Serial.println(ManifestUrl);
|
||||||
|
|
||||||
|
Serial.println("Firmware-opdatering er klar.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FirmwareUpdater::checkForUpdate()
|
||||||
|
{
|
||||||
|
if (WiFi.status() != WL_CONNECTED)
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"Firmwarekontrol sprunget over: Wi-Fi er ikke forbundet."
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Kontrollerer efter ny firmware...");
|
||||||
|
|
||||||
|
const bool updateAvailable =
|
||||||
|
firmwareUpdate.execHTTPcheck();
|
||||||
|
|
||||||
|
if (!updateAvailable)
|
||||||
|
{
|
||||||
|
Serial.println("Ingen ny firmware fundet.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Ny firmware fundet.");
|
||||||
|
Serial.println("Downloader og installerer firmware...");
|
||||||
|
|
||||||
|
firmwareUpdate.execOTA();
|
||||||
|
}
|
||||||
951
src/OpenPrintTagParser.cpp
Normal file
951
src/OpenPrintTagParser.cpp
Normal file
@@ -0,0 +1,951 @@
|
|||||||
|
#include "OpenPrintTagParser.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr uint64_t FieldInstanceUuid = 0;
|
||||||
|
constexpr uint64_t FieldMaterialName = 10;
|
||||||
|
constexpr uint64_t FieldBrand = 11;
|
||||||
|
constexpr uint64_t FieldNominalWeight = 16;
|
||||||
|
constexpr uint64_t FieldActualWeight = 17;
|
||||||
|
constexpr uint64_t FieldSpoolWeight = 18;
|
||||||
|
constexpr uint64_t FieldPrimaryColor = 19;
|
||||||
|
constexpr uint64_t FieldMaterialType = 52;
|
||||||
|
|
||||||
|
constexpr uint64_t MetadataAuxOffset = 2;
|
||||||
|
|
||||||
|
bool readAuxOffset(
|
||||||
|
const uint8_t* payload,
|
||||||
|
size_t payloadLength,
|
||||||
|
size_t& auxOffset)
|
||||||
|
{
|
||||||
|
CborParser parser;
|
||||||
|
CborValue root;
|
||||||
|
CborValue iterator;
|
||||||
|
|
||||||
|
const CborError parserResult =
|
||||||
|
cbor_parser_init(
|
||||||
|
payload,
|
||||||
|
payloadLength,
|
||||||
|
0,
|
||||||
|
&parser,
|
||||||
|
&root);
|
||||||
|
|
||||||
|
if (parserResult != CborNoError ||
|
||||||
|
!cbor_value_is_map(&root))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbor_value_enter_container(
|
||||||
|
&root,
|
||||||
|
&iterator) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!cbor_value_at_end(&iterator))
|
||||||
|
{
|
||||||
|
if (!cbor_value_is_unsigned_integer(
|
||||||
|
&iterator))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t key = 0;
|
||||||
|
|
||||||
|
if (cbor_value_get_uint64(
|
||||||
|
&iterator,
|
||||||
|
&key) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbor_value_advance(
|
||||||
|
&iterator) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbor_value_at_end(&iterator))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == MetadataAuxOffset)
|
||||||
|
{
|
||||||
|
if (!cbor_value_is_unsigned_integer(
|
||||||
|
&iterator))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
if (cbor_value_get_uint64(
|
||||||
|
&iterator,
|
||||||
|
&value) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= payloadLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auxOffset =
|
||||||
|
static_cast<size_t>(value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbor_value_advance(
|
||||||
|
&iterator) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::parse(
|
||||||
|
const uint8_t* memory,
|
||||||
|
size_t memoryLength,
|
||||||
|
OpenPrintTagData& data)
|
||||||
|
{
|
||||||
|
data = OpenPrintTagData{};
|
||||||
|
|
||||||
|
if (memory == nullptr ||
|
||||||
|
memoryLength == 0)
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"OpenPrintTag-parser: Hukommelsesbufferen er tom.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* ndefMessage = nullptr;
|
||||||
|
size_t ndefMessageLength = 0;
|
||||||
|
|
||||||
|
if (!findNdefMessage(
|
||||||
|
memory,
|
||||||
|
memoryLength,
|
||||||
|
ndefMessage,
|
||||||
|
ndefMessageLength))
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"OpenPrintTag-parser: Ingen gyldig NDEF-meddelelse fundet.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* payload = nullptr;
|
||||||
|
size_t payloadLength = 0;
|
||||||
|
|
||||||
|
if (!parseNdefRecord(
|
||||||
|
ndefMessage,
|
||||||
|
ndefMessageLength,
|
||||||
|
payload,
|
||||||
|
payloadLength))
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"OpenPrintTag-parser: NDEF-recorden kunne ikke fortolkes.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parseCborPayload(
|
||||||
|
payload,
|
||||||
|
payloadLength,
|
||||||
|
data))
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"OpenPrintTag-parser: CBOR-data kunne ikke fortolkes.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t startingWeight =
|
||||||
|
data.hasActualWeight
|
||||||
|
? data.actualWeightGrams
|
||||||
|
: data.nominalWeightGrams;
|
||||||
|
|
||||||
|
if (startingWeight == 0)
|
||||||
|
{
|
||||||
|
data.remainingWeightGrams = 0;
|
||||||
|
}
|
||||||
|
else if (data.consumedWeightGrams >= startingWeight)
|
||||||
|
{
|
||||||
|
data.remainingWeightGrams = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data.remainingWeightGrams =
|
||||||
|
startingWeight -
|
||||||
|
data.consumedWeightGrams;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("OpenPrintTag-data:");
|
||||||
|
|
||||||
|
Serial.print("Instance UUID: ");
|
||||||
|
Serial.println(data.instanceUuid);
|
||||||
|
|
||||||
|
Serial.print("Producent: ");
|
||||||
|
Serial.println(data.brand);
|
||||||
|
|
||||||
|
Serial.print("Materiale: ");
|
||||||
|
Serial.println(data.materialName);
|
||||||
|
|
||||||
|
Serial.print("Materialetype: ");
|
||||||
|
Serial.println(data.materialType);
|
||||||
|
|
||||||
|
Serial.print("Farve: ");
|
||||||
|
Serial.println(data.colorHex);
|
||||||
|
|
||||||
|
if (data.hasNominalWeight)
|
||||||
|
{
|
||||||
|
Serial.print("Nominel vaegt: ");
|
||||||
|
Serial.print(data.nominalWeightGrams);
|
||||||
|
Serial.println(" g");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.hasActualWeight)
|
||||||
|
{
|
||||||
|
Serial.print("Faktisk startvaegt: ");
|
||||||
|
Serial.print(data.actualWeightGrams);
|
||||||
|
Serial.println(" g");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.hasSpoolWeight)
|
||||||
|
{
|
||||||
|
Serial.print("Spolevaegt: ");
|
||||||
|
Serial.print(data.spoolWeightGrams);
|
||||||
|
Serial.println(" g");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Forbrugt vaegt: ");
|
||||||
|
Serial.print(data.consumedWeightGrams);
|
||||||
|
Serial.println(" g");
|
||||||
|
|
||||||
|
Serial.print("Resterende vaegt: ");
|
||||||
|
Serial.print(data.remainingWeightGrams);
|
||||||
|
Serial.println(" g");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::findNdefMessage(
|
||||||
|
const uint8_t* memory,
|
||||||
|
size_t memoryLength,
|
||||||
|
const uint8_t*& ndefMessage,
|
||||||
|
size_t& ndefMessageLength) const
|
||||||
|
{
|
||||||
|
size_t offset = 4;
|
||||||
|
|
||||||
|
while (offset < memoryLength)
|
||||||
|
{
|
||||||
|
const uint8_t tlvType =
|
||||||
|
memory[offset++];
|
||||||
|
|
||||||
|
if (tlvType == 0x00)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tlvType == TerminatorTlvType)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= memoryLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t tlvLength =
|
||||||
|
memory[offset++];
|
||||||
|
|
||||||
|
if (tlvLength == 0xFF)
|
||||||
|
{
|
||||||
|
if (offset + 2 > memoryLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tlvLength =
|
||||||
|
(static_cast<size_t>(
|
||||||
|
memory[offset]) << 8) |
|
||||||
|
static_cast<size_t>(
|
||||||
|
memory[offset + 1]);
|
||||||
|
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset + tlvLength >
|
||||||
|
memoryLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tlvType == NdefTlvType)
|
||||||
|
{
|
||||||
|
ndefMessage =
|
||||||
|
memory + offset;
|
||||||
|
|
||||||
|
ndefMessageLength =
|
||||||
|
tlvLength;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += tlvLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::parseNdefRecord(
|
||||||
|
const uint8_t* ndefMessage,
|
||||||
|
size_t ndefMessageLength,
|
||||||
|
const uint8_t*& payload,
|
||||||
|
size_t& payloadLength) const
|
||||||
|
{
|
||||||
|
if (ndefMessage == nullptr ||
|
||||||
|
ndefMessageLength < 3)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
const uint8_t header =
|
||||||
|
ndefMessage[offset++];
|
||||||
|
|
||||||
|
const bool messageBegin =
|
||||||
|
(header & 0x80) != 0;
|
||||||
|
|
||||||
|
const bool messageEnd =
|
||||||
|
(header & 0x40) != 0;
|
||||||
|
|
||||||
|
const bool chunked =
|
||||||
|
(header & 0x20) != 0;
|
||||||
|
|
||||||
|
const bool shortRecord =
|
||||||
|
(header & 0x10) != 0;
|
||||||
|
|
||||||
|
const bool idLengthPresent =
|
||||||
|
(header & 0x08) != 0;
|
||||||
|
|
||||||
|
const uint8_t typeNameFormat =
|
||||||
|
header & 0x07;
|
||||||
|
|
||||||
|
if (!messageBegin ||
|
||||||
|
!messageEnd ||
|
||||||
|
chunked ||
|
||||||
|
typeNameFormat != 0x02)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= ndefMessageLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t mimeTypeLength =
|
||||||
|
ndefMessage[offset++];
|
||||||
|
|
||||||
|
if (shortRecord)
|
||||||
|
{
|
||||||
|
if (offset >= ndefMessageLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadLength =
|
||||||
|
ndefMessage[offset++];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (offset + 4 >
|
||||||
|
ndefMessageLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadLength =
|
||||||
|
(static_cast<size_t>(
|
||||||
|
ndefMessage[offset]) << 24) |
|
||||||
|
(static_cast<size_t>(
|
||||||
|
ndefMessage[offset + 1]) << 16) |
|
||||||
|
(static_cast<size_t>(
|
||||||
|
ndefMessage[offset + 2]) << 8) |
|
||||||
|
static_cast<size_t>(
|
||||||
|
ndefMessage[offset + 3]);
|
||||||
|
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t idLength = 0;
|
||||||
|
|
||||||
|
if (idLengthPresent)
|
||||||
|
{
|
||||||
|
if (offset >= ndefMessageLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
idLength =
|
||||||
|
ndefMessage[offset++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset + mimeTypeLength >
|
||||||
|
ndefMessageLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* mimeType =
|
||||||
|
ndefMessage + offset;
|
||||||
|
|
||||||
|
if (!mimeTypeMatches(
|
||||||
|
mimeType,
|
||||||
|
mimeTypeLength))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += mimeTypeLength;
|
||||||
|
|
||||||
|
if (offset + idLength >
|
||||||
|
ndefMessageLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += idLength;
|
||||||
|
|
||||||
|
if (offset + payloadLength >
|
||||||
|
ndefMessageLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload =
|
||||||
|
ndefMessage + offset;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::mimeTypeMatches(
|
||||||
|
const uint8_t* mimeType,
|
||||||
|
size_t mimeTypeLength) const
|
||||||
|
{
|
||||||
|
const size_t expectedLength =
|
||||||
|
std::strlen(ExpectedMimeType);
|
||||||
|
|
||||||
|
return mimeTypeLength == expectedLength &&
|
||||||
|
std::memcmp(
|
||||||
|
mimeType,
|
||||||
|
ExpectedMimeType,
|
||||||
|
expectedLength) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::parseCborPayload(
|
||||||
|
const uint8_t* payload,
|
||||||
|
size_t payloadLength,
|
||||||
|
OpenPrintTagData& data) const
|
||||||
|
{
|
||||||
|
size_t auxOffset = 0;
|
||||||
|
|
||||||
|
if (!readAuxOffset(
|
||||||
|
payload,
|
||||||
|
payloadLength,
|
||||||
|
auxOffset))
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"CBOR-parser: Aux-offset kunne ikke laeses.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CborParser metadataParser;
|
||||||
|
CborValue metadataValue;
|
||||||
|
|
||||||
|
if (cbor_parser_init(
|
||||||
|
payload,
|
||||||
|
payloadLength,
|
||||||
|
0,
|
||||||
|
&metadataParser,
|
||||||
|
&metadataValue) != CborNoError)
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"CBOR-parser: Metadata kunne ikke startes.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Metadata er selv en CBOR-map. Hele mappen skal
|
||||||
|
* springes over, før adressen til main-data kan findes.
|
||||||
|
*/
|
||||||
|
if (!skipValue(&metadataValue))
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"CBOR-parser: Metadata kunne ikke springes over.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* mainData =
|
||||||
|
cbor_value_get_next_byte(
|
||||||
|
&metadataValue);
|
||||||
|
|
||||||
|
if (mainData == nullptr ||
|
||||||
|
mainData <= payload ||
|
||||||
|
mainData >= payload + auxOffset)
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"CBOR-parser: Main-data har et ugyldigt offset.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t mainOffset =
|
||||||
|
static_cast<size_t>(
|
||||||
|
mainData - payload);
|
||||||
|
|
||||||
|
const size_t mainDataLength =
|
||||||
|
auxOffset - mainOffset;
|
||||||
|
|
||||||
|
return parseMainData(
|
||||||
|
mainData,
|
||||||
|
mainDataLength,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::parseMainData(
|
||||||
|
const uint8_t* mainData,
|
||||||
|
size_t mainDataLength,
|
||||||
|
OpenPrintTagData& data) const
|
||||||
|
{
|
||||||
|
CborParser parser;
|
||||||
|
CborValue root;
|
||||||
|
CborValue iterator;
|
||||||
|
|
||||||
|
if (cbor_parser_init(
|
||||||
|
mainData,
|
||||||
|
mainDataLength,
|
||||||
|
0,
|
||||||
|
&parser,
|
||||||
|
&root) != CborNoError ||
|
||||||
|
!cbor_value_is_map(&root))
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"CBOR-parser: Main-data er ikke en map.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbor_value_enter_container(
|
||||||
|
&root,
|
||||||
|
&iterator) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!cbor_value_at_end(&iterator))
|
||||||
|
{
|
||||||
|
uint64_t fieldId = 0;
|
||||||
|
|
||||||
|
if (!readUnsignedInteger(
|
||||||
|
&iterator,
|
||||||
|
fieldId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (fieldId)
|
||||||
|
{
|
||||||
|
case FieldInstanceUuid:
|
||||||
|
{
|
||||||
|
uint8_t bytes[16] = {};
|
||||||
|
size_t bytesRead = 0;
|
||||||
|
|
||||||
|
if (!readByteString(
|
||||||
|
&iterator,
|
||||||
|
bytes,
|
||||||
|
sizeof(bytes),
|
||||||
|
bytesRead))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.instanceUuid =
|
||||||
|
formatUuid(
|
||||||
|
bytes,
|
||||||
|
bytesRead);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case FieldMaterialName:
|
||||||
|
if (!readTextString(
|
||||||
|
&iterator,
|
||||||
|
data.materialName))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FieldBrand:
|
||||||
|
if (!readTextString(
|
||||||
|
&iterator,
|
||||||
|
data.brand))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FieldMaterialType:
|
||||||
|
if (!readTextString(
|
||||||
|
&iterator,
|
||||||
|
data.materialType))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FieldPrimaryColor:
|
||||||
|
{
|
||||||
|
uint8_t bytes[4] = {};
|
||||||
|
size_t bytesRead = 0;
|
||||||
|
|
||||||
|
if (!readByteString(
|
||||||
|
&iterator,
|
||||||
|
bytes,
|
||||||
|
sizeof(bytes),
|
||||||
|
bytesRead))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.colorHex =
|
||||||
|
formatColor(
|
||||||
|
bytes,
|
||||||
|
bytesRead);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case FieldNominalWeight:
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
if (!readUnsignedInteger(
|
||||||
|
&iterator,
|
||||||
|
value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.nominalWeightGrams =
|
||||||
|
static_cast<uint16_t>(value);
|
||||||
|
|
||||||
|
data.hasNominalWeight = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case FieldActualWeight:
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
if (!readUnsignedInteger(
|
||||||
|
&iterator,
|
||||||
|
value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.actualWeightGrams =
|
||||||
|
static_cast<uint16_t>(value);
|
||||||
|
|
||||||
|
data.hasActualWeight = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case FieldSpoolWeight:
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
if (!readUnsignedInteger(
|
||||||
|
&iterator,
|
||||||
|
value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.spoolWeightGrams =
|
||||||
|
static_cast<uint16_t>(value);
|
||||||
|
|
||||||
|
data.hasSpoolWeight = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!skipValue(&iterator))
|
||||||
|
{
|
||||||
|
Serial.print(
|
||||||
|
"CBOR-parser: Felt kunne ikke springes over: ");
|
||||||
|
|
||||||
|
Serial.println(
|
||||||
|
static_cast<unsigned long>(
|
||||||
|
fieldId));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::readUnsignedInteger(
|
||||||
|
CborValue* value,
|
||||||
|
uint64_t& result) const
|
||||||
|
{
|
||||||
|
if (value == nullptr ||
|
||||||
|
!cbor_value_is_unsigned_integer(value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbor_value_get_uint64(
|
||||||
|
value,
|
||||||
|
&result) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cbor_value_advance(value) ==
|
||||||
|
CborNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::readTextString(
|
||||||
|
CborValue* value,
|
||||||
|
String& result) const
|
||||||
|
{
|
||||||
|
if (value == nullptr ||
|
||||||
|
!cbor_value_is_text_string(value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t textLength = 0;
|
||||||
|
|
||||||
|
if (cbor_value_calculate_string_length(
|
||||||
|
value,
|
||||||
|
&textLength) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* buffer =
|
||||||
|
new (std::nothrow)
|
||||||
|
char[textLength + 1];
|
||||||
|
|
||||||
|
if (buffer == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bufferLength =
|
||||||
|
textLength + 1;
|
||||||
|
|
||||||
|
CborValue nextValue;
|
||||||
|
|
||||||
|
const CborError copyResult =
|
||||||
|
cbor_value_copy_text_string(
|
||||||
|
value,
|
||||||
|
buffer,
|
||||||
|
&bufferLength,
|
||||||
|
&nextValue);
|
||||||
|
|
||||||
|
if (copyResult != CborNoError)
|
||||||
|
{
|
||||||
|
delete[] buffer;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[textLength] = '\0';
|
||||||
|
result = buffer;
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
*value = nextValue;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::readByteString(
|
||||||
|
CborValue* value,
|
||||||
|
uint8_t* buffer,
|
||||||
|
size_t bufferCapacity,
|
||||||
|
size_t& bytesRead) const
|
||||||
|
{
|
||||||
|
if (value == nullptr ||
|
||||||
|
!cbor_value_is_byte_string(value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t byteLength = 0;
|
||||||
|
|
||||||
|
if (cbor_value_calculate_string_length(
|
||||||
|
value,
|
||||||
|
&byteLength) != CborNoError ||
|
||||||
|
byteLength > bufferCapacity)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bufferLength =
|
||||||
|
byteLength;
|
||||||
|
|
||||||
|
CborValue nextValue;
|
||||||
|
|
||||||
|
const CborError copyResult =
|
||||||
|
cbor_value_copy_byte_string(
|
||||||
|
value,
|
||||||
|
buffer,
|
||||||
|
&bufferLength,
|
||||||
|
&nextValue);
|
||||||
|
|
||||||
|
if (copyResult != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesRead = bufferLength;
|
||||||
|
*value = nextValue;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenPrintTagParser::skipValue(
|
||||||
|
CborValue* value) const
|
||||||
|
{
|
||||||
|
if (value == nullptr ||
|
||||||
|
!cbor_value_is_valid(value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbor_value_is_array(value) ||
|
||||||
|
cbor_value_is_map(value))
|
||||||
|
{
|
||||||
|
CborValue child;
|
||||||
|
|
||||||
|
if (cbor_value_enter_container(
|
||||||
|
value,
|
||||||
|
&child) != CborNoError)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!cbor_value_at_end(&child))
|
||||||
|
{
|
||||||
|
if (!skipValue(&child))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cbor_value_leave_container(
|
||||||
|
value,
|
||||||
|
&child) ==
|
||||||
|
CborNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cbor_value_advance(value) ==
|
||||||
|
CborNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
String OpenPrintTagParser::formatUuid(
|
||||||
|
const uint8_t* bytes,
|
||||||
|
size_t length) const
|
||||||
|
{
|
||||||
|
if (bytes == nullptr ||
|
||||||
|
length != 16)
|
||||||
|
{
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
String result;
|
||||||
|
result.reserve(36);
|
||||||
|
|
||||||
|
for (size_t index = 0;
|
||||||
|
index < length;
|
||||||
|
++index)
|
||||||
|
{
|
||||||
|
if (bytes[index] < 0x10)
|
||||||
|
{
|
||||||
|
result += '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
result += String(
|
||||||
|
bytes[index],
|
||||||
|
HEX);
|
||||||
|
|
||||||
|
if (index == 3 ||
|
||||||
|
index == 5 ||
|
||||||
|
index == 7 ||
|
||||||
|
index == 9)
|
||||||
|
{
|
||||||
|
result += '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.toLowerCase();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String OpenPrintTagParser::formatColor(
|
||||||
|
const uint8_t* bytes,
|
||||||
|
size_t length) const
|
||||||
|
{
|
||||||
|
if (bytes == nullptr ||
|
||||||
|
length != 4)
|
||||||
|
{
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = "#";
|
||||||
|
result.reserve(9);
|
||||||
|
|
||||||
|
for (size_t index = 0;
|
||||||
|
index < length;
|
||||||
|
++index)
|
||||||
|
{
|
||||||
|
if (bytes[index] < 0x10)
|
||||||
|
{
|
||||||
|
result += '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
result += String(
|
||||||
|
bytes[index],
|
||||||
|
HEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.toUpperCase();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
634
src/Pn5180Reader.cpp
Normal file
634
src/Pn5180Reader.cpp
Normal file
@@ -0,0 +1,634 @@
|
|||||||
|
#include "Pn5180Reader.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
Pn5180Reader::Pn5180Reader()
|
||||||
|
: iso15693Reader(NssPin, BusyPin, ResetPin),
|
||||||
|
iso14443Reader(NssPin, BusyPin, ResetPin)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::begin()
|
||||||
|
{
|
||||||
|
iso15693Reader.begin();
|
||||||
|
iso14443Reader.begin();
|
||||||
|
|
||||||
|
iso15693Reader.reset();
|
||||||
|
iso15693Reader.setupRF();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::setDeviceInfo(
|
||||||
|
const FilamentGuardDeviceInfo& newDeviceInfo)
|
||||||
|
{
|
||||||
|
deviceInfo = newDeviceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::printVersionInformation()
|
||||||
|
{
|
||||||
|
uint8_t productVersion[2] = {};
|
||||||
|
uint8_t firmwareVersion[2] = {};
|
||||||
|
uint8_t eepromVersion[2] = {};
|
||||||
|
|
||||||
|
const bool productRead =
|
||||||
|
iso15693Reader.readEEprom(
|
||||||
|
PRODUCT_VERSION,
|
||||||
|
productVersion,
|
||||||
|
sizeof(productVersion));
|
||||||
|
|
||||||
|
const bool firmwareRead =
|
||||||
|
iso15693Reader.readEEprom(
|
||||||
|
FIRMWARE_VERSION,
|
||||||
|
firmwareVersion,
|
||||||
|
sizeof(firmwareVersion));
|
||||||
|
|
||||||
|
const bool eepromRead =
|
||||||
|
iso15693Reader.readEEprom(
|
||||||
|
EEPROM_VERSION,
|
||||||
|
eepromVersion,
|
||||||
|
sizeof(eepromVersion));
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("PN5180 versionsoplysninger:");
|
||||||
|
|
||||||
|
if (!productRead ||
|
||||||
|
!firmwareRead ||
|
||||||
|
!eepromRead)
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"Kunne ikke laese versionsoplysninger fra PN5180.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf(
|
||||||
|
"Produktversion: %u.%u\n",
|
||||||
|
productVersion[1],
|
||||||
|
productVersion[0]);
|
||||||
|
|
||||||
|
Serial.printf(
|
||||||
|
"Firmwareversion: %u.%u\n",
|
||||||
|
firmwareVersion[1],
|
||||||
|
firmwareVersion[0]);
|
||||||
|
|
||||||
|
Serial.printf(
|
||||||
|
"EEPROM-version: %u.%u\n",
|
||||||
|
eepromVersion[1],
|
||||||
|
eepromVersion[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::checkForTag()
|
||||||
|
{
|
||||||
|
bool tagFound = false;
|
||||||
|
|
||||||
|
if (activeProtocol == TagProtocol::Iso14443A)
|
||||||
|
{
|
||||||
|
tagFound = checkIso14443Tag();
|
||||||
|
}
|
||||||
|
else if (activeProtocol == TagProtocol::Iso15693)
|
||||||
|
{
|
||||||
|
tagFound = checkIso15693Tag();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tagFound = checkIso14443Tag();
|
||||||
|
|
||||||
|
if (!tagFound)
|
||||||
|
{
|
||||||
|
tagFound = checkIso15693Tag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagFound)
|
||||||
|
{
|
||||||
|
missingScanCount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTagMissing();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pn5180Reader::checkIso14443Tag()
|
||||||
|
{
|
||||||
|
uint8_t uid[10] = {};
|
||||||
|
|
||||||
|
iso14443Reader.reset();
|
||||||
|
iso14443Reader.setupRF();
|
||||||
|
|
||||||
|
if (!iso14443Reader.isCardPresent())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t uidLength =
|
||||||
|
iso14443Reader.readCardSerial(uid);
|
||||||
|
|
||||||
|
if (uidLength == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeProtocol == TagProtocol::Iso14443A)
|
||||||
|
{
|
||||||
|
return isSameUid(
|
||||||
|
uid,
|
||||||
|
uidLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeProtocol != TagProtocol::None)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rememberUid(
|
||||||
|
uid,
|
||||||
|
uidLength,
|
||||||
|
TagProtocol::Iso14443A);
|
||||||
|
|
||||||
|
Serial.print(
|
||||||
|
"ISO14443A-tag fundet | UID: ");
|
||||||
|
|
||||||
|
printUid(
|
||||||
|
uid,
|
||||||
|
uidLength,
|
||||||
|
false);
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pn5180Reader::checkIso15693Tag()
|
||||||
|
{
|
||||||
|
uint8_t uid[8] = {};
|
||||||
|
|
||||||
|
iso15693Reader.reset();
|
||||||
|
iso15693Reader.setupRF();
|
||||||
|
|
||||||
|
const ISO15693ErrorCode result =
|
||||||
|
iso15693Reader.getInventory(uid);
|
||||||
|
|
||||||
|
if (result != ISO15693_EC_OK)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint8_t uidLength = 8;
|
||||||
|
|
||||||
|
if (activeProtocol == TagProtocol::Iso15693)
|
||||||
|
{
|
||||||
|
return isSameUid(
|
||||||
|
uid,
|
||||||
|
uidLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeProtocol != TagProtocol::None)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t confirmedUid[uidLength] = {};
|
||||||
|
|
||||||
|
delay(20);
|
||||||
|
|
||||||
|
iso15693Reader.reset();
|
||||||
|
iso15693Reader.setupRF();
|
||||||
|
|
||||||
|
const ISO15693ErrorCode confirmationResult =
|
||||||
|
iso15693Reader.getInventory(
|
||||||
|
confirmedUid);
|
||||||
|
|
||||||
|
if (confirmationResult != ISO15693_EC_OK)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::memcmp(
|
||||||
|
uid,
|
||||||
|
confirmedUid,
|
||||||
|
uidLength) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rememberUid(
|
||||||
|
uid,
|
||||||
|
uidLength,
|
||||||
|
TagProtocol::Iso15693);
|
||||||
|
|
||||||
|
Serial.print(
|
||||||
|
"ISO15693-tag fundet | UID: ");
|
||||||
|
|
||||||
|
printUid(
|
||||||
|
uid,
|
||||||
|
uidLength,
|
||||||
|
true);
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
dumpIso15693Tag(
|
||||||
|
uid,
|
||||||
|
uidLength);
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
iso15693Reader.reset();
|
||||||
|
iso15693Reader.setupRF();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::handleTagMissing()
|
||||||
|
{
|
||||||
|
if (activeProtocol == TagProtocol::None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
++missingScanCount;
|
||||||
|
|
||||||
|
if (missingScanCount <
|
||||||
|
RequiredMissingScans)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("NFC-tag fjernet.");
|
||||||
|
|
||||||
|
clearLastUid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pn5180Reader::isSameUid(
|
||||||
|
const uint8_t* uid,
|
||||||
|
uint8_t uidLength) const
|
||||||
|
{
|
||||||
|
if (uid == nullptr ||
|
||||||
|
uidLength != lastUidLength)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::memcmp(
|
||||||
|
lastUid,
|
||||||
|
uid,
|
||||||
|
uidLength) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::rememberUid(
|
||||||
|
const uint8_t* uid,
|
||||||
|
uint8_t uidLength,
|
||||||
|
TagProtocol protocol)
|
||||||
|
{
|
||||||
|
std::memset(
|
||||||
|
lastUid,
|
||||||
|
0,
|
||||||
|
sizeof(lastUid));
|
||||||
|
|
||||||
|
std::memcpy(
|
||||||
|
lastUid,
|
||||||
|
uid,
|
||||||
|
uidLength);
|
||||||
|
|
||||||
|
lastUidLength = uidLength;
|
||||||
|
activeProtocol = protocol;
|
||||||
|
missingScanCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::clearLastUid()
|
||||||
|
{
|
||||||
|
std::memset(
|
||||||
|
lastUid,
|
||||||
|
0,
|
||||||
|
sizeof(lastUid));
|
||||||
|
|
||||||
|
lastUidLength = 0;
|
||||||
|
activeProtocol = TagProtocol::None;
|
||||||
|
missingScanCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::printUid(
|
||||||
|
const uint8_t* uid,
|
||||||
|
uint8_t uidLength,
|
||||||
|
bool reverseOrder) const
|
||||||
|
{
|
||||||
|
Serial.print(
|
||||||
|
formatUid(
|
||||||
|
uid,
|
||||||
|
uidLength,
|
||||||
|
reverseOrder));
|
||||||
|
}
|
||||||
|
|
||||||
|
String Pn5180Reader::formatUid(
|
||||||
|
const uint8_t* uid,
|
||||||
|
uint8_t uidLength,
|
||||||
|
bool reverseOrder) const
|
||||||
|
{
|
||||||
|
if (uid == nullptr ||
|
||||||
|
uidLength == 0)
|
||||||
|
{
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
String result;
|
||||||
|
|
||||||
|
result.reserve(
|
||||||
|
static_cast<size_t>(uidLength) * 3 - 1);
|
||||||
|
|
||||||
|
for (uint8_t index = 0;
|
||||||
|
index < uidLength;
|
||||||
|
++index)
|
||||||
|
{
|
||||||
|
const uint8_t uidIndex =
|
||||||
|
reverseOrder
|
||||||
|
? uidLength - 1 - index
|
||||||
|
: index;
|
||||||
|
|
||||||
|
if (uid[uidIndex] < 0x10)
|
||||||
|
{
|
||||||
|
result += '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
result += String(
|
||||||
|
uid[uidIndex],
|
||||||
|
HEX);
|
||||||
|
|
||||||
|
if (index < uidLength - 1)
|
||||||
|
{
|
||||||
|
result += ':';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.toUpperCase();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::dumpIso15693Tag(
|
||||||
|
uint8_t* uid,
|
||||||
|
uint8_t uidLength)
|
||||||
|
{
|
||||||
|
constexpr uint8_t blockSize = 4;
|
||||||
|
constexpr uint8_t numberOfBlocks = 80;
|
||||||
|
|
||||||
|
constexpr size_t memorySize =
|
||||||
|
static_cast<size_t>(blockSize) *
|
||||||
|
numberOfBlocks;
|
||||||
|
|
||||||
|
uint8_t memory[memorySize] = {};
|
||||||
|
bool allBlocksRead = true;
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println(
|
||||||
|
"ISO15693 hukommelsesoplysninger:");
|
||||||
|
|
||||||
|
Serial.print("Blokstoerrelse: ");
|
||||||
|
Serial.print(blockSize);
|
||||||
|
Serial.println(" bytes");
|
||||||
|
|
||||||
|
Serial.print("Antal blokke: ");
|
||||||
|
Serial.println(numberOfBlocks);
|
||||||
|
|
||||||
|
Serial.print("Samlet hukommelse: ");
|
||||||
|
Serial.print(memorySize);
|
||||||
|
Serial.println(" bytes");
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
for (uint8_t blockNumber = 0;
|
||||||
|
blockNumber < numberOfBlocks;
|
||||||
|
++blockNumber)
|
||||||
|
{
|
||||||
|
uint8_t* blockData =
|
||||||
|
memory +
|
||||||
|
static_cast<size_t>(blockNumber) *
|
||||||
|
blockSize;
|
||||||
|
|
||||||
|
const ISO15693ErrorCode readResult =
|
||||||
|
iso15693Reader.readSingleBlock(
|
||||||
|
uid,
|
||||||
|
blockNumber,
|
||||||
|
blockData,
|
||||||
|
blockSize);
|
||||||
|
|
||||||
|
if (readResult != ISO15693_EC_OK)
|
||||||
|
{
|
||||||
|
allBlocksRead = false;
|
||||||
|
|
||||||
|
Serial.print("Blok ");
|
||||||
|
Serial.print(blockNumber);
|
||||||
|
|
||||||
|
Serial.print(
|
||||||
|
" kunne ikke laeses. Fejlkode: ");
|
||||||
|
|
||||||
|
Serial.println(
|
||||||
|
static_cast<int>(readResult));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
if (!allBlocksRead)
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"OpenPrintTag-parseren blev ikke startet, "
|
||||||
|
"fordi en eller flere blokke ikke kunne laeses.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(
|
||||||
|
"Fortolker OpenPrintTag-data...");
|
||||||
|
|
||||||
|
OpenPrintTagData openPrintTagData;
|
||||||
|
|
||||||
|
if (!openPrintTagParser.parse(
|
||||||
|
memory,
|
||||||
|
memorySize,
|
||||||
|
openPrintTagData))
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"OpenPrintTag-data kunne ikke fortolkes.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openPrintTagData.tagUid =
|
||||||
|
formatUid(
|
||||||
|
uid,
|
||||||
|
uidLength,
|
||||||
|
true);
|
||||||
|
|
||||||
|
FilamentGuardTagRead tagRead;
|
||||||
|
|
||||||
|
tagRead.device =
|
||||||
|
deviceInfo;
|
||||||
|
|
||||||
|
tagRead.tag =
|
||||||
|
openPrintTagData;
|
||||||
|
|
||||||
|
printTagRead(tagRead);
|
||||||
|
printTagReadJson(tagRead);
|
||||||
|
sendTagRead(tagRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::printTagRead(
|
||||||
|
const FilamentGuardTagRead& tagRead) const
|
||||||
|
{
|
||||||
|
Serial.println();
|
||||||
|
Serial.println(
|
||||||
|
"FilamentGuard tagregistrering:");
|
||||||
|
|
||||||
|
Serial.print("Device ID: ");
|
||||||
|
Serial.println(
|
||||||
|
tagRead.device.deviceId);
|
||||||
|
|
||||||
|
Serial.print("Hostname: ");
|
||||||
|
Serial.println(
|
||||||
|
tagRead.device.hostname);
|
||||||
|
|
||||||
|
Serial.print("Tag UID: ");
|
||||||
|
Serial.println(
|
||||||
|
tagRead.tag.tagUid);
|
||||||
|
|
||||||
|
Serial.print("Instance UUID: ");
|
||||||
|
Serial.println(
|
||||||
|
tagRead.tag.instanceUuid);
|
||||||
|
|
||||||
|
Serial.print("Producent: ");
|
||||||
|
Serial.println(
|
||||||
|
tagRead.tag.brand);
|
||||||
|
|
||||||
|
Serial.print("Materiale: ");
|
||||||
|
Serial.println(
|
||||||
|
tagRead.tag.materialName);
|
||||||
|
|
||||||
|
Serial.print("Materialetype: ");
|
||||||
|
Serial.println(
|
||||||
|
tagRead.tag.materialType);
|
||||||
|
|
||||||
|
Serial.print("Farve: ");
|
||||||
|
Serial.println(
|
||||||
|
tagRead.tag.colorHex);
|
||||||
|
|
||||||
|
if (tagRead.tag.hasNominalWeight)
|
||||||
|
{
|
||||||
|
Serial.print("Nominel vaegt: ");
|
||||||
|
Serial.print(
|
||||||
|
tagRead.tag.nominalWeightGrams);
|
||||||
|
|
||||||
|
Serial.println(" g");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagRead.tag.hasActualWeight)
|
||||||
|
{
|
||||||
|
Serial.print("Faktisk startvaegt: ");
|
||||||
|
Serial.print(
|
||||||
|
tagRead.tag.actualWeightGrams);
|
||||||
|
|
||||||
|
Serial.println(" g");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagRead.tag.hasSpoolWeight)
|
||||||
|
{
|
||||||
|
Serial.print("Spolevaegt: ");
|
||||||
|
Serial.print(
|
||||||
|
tagRead.tag.spoolWeightGrams);
|
||||||
|
|
||||||
|
Serial.println(" g");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Forbrugt vaegt: ");
|
||||||
|
Serial.print(
|
||||||
|
tagRead.tag.consumedWeightGrams);
|
||||||
|
|
||||||
|
Serial.println(" g");
|
||||||
|
|
||||||
|
Serial.print("Resterende vaegt: ");
|
||||||
|
Serial.print(
|
||||||
|
tagRead.tag.remainingWeightGrams);
|
||||||
|
|
||||||
|
Serial.println(" g");
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::printTagReadJson(
|
||||||
|
const FilamentGuardTagRead& tagRead) const
|
||||||
|
{
|
||||||
|
const String json =
|
||||||
|
tagReadSerializer.serialize(
|
||||||
|
tagRead);
|
||||||
|
|
||||||
|
Serial.println(
|
||||||
|
"FilamentGuard JSON:");
|
||||||
|
|
||||||
|
Serial.println(json);
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::sendTagRead(
|
||||||
|
const FilamentGuardTagRead& tagRead) const
|
||||||
|
{
|
||||||
|
const String json =
|
||||||
|
tagReadSerializer.serialize(
|
||||||
|
tagRead);
|
||||||
|
|
||||||
|
apiClient.sendTagRead(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pn5180Reader::printBlock(
|
||||||
|
uint8_t blockNumber,
|
||||||
|
const uint8_t* data,
|
||||||
|
uint8_t blockSize) const
|
||||||
|
{
|
||||||
|
Serial.print("Blok ");
|
||||||
|
|
||||||
|
if (blockNumber < 10)
|
||||||
|
{
|
||||||
|
Serial.print('0');
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(blockNumber);
|
||||||
|
Serial.print(": ");
|
||||||
|
|
||||||
|
for (uint8_t index = 0;
|
||||||
|
index < blockSize;
|
||||||
|
++index)
|
||||||
|
{
|
||||||
|
if (data[index] < 0x10)
|
||||||
|
{
|
||||||
|
Serial.print('0');
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(
|
||||||
|
data[index],
|
||||||
|
HEX);
|
||||||
|
|
||||||
|
if (index < blockSize - 1)
|
||||||
|
{
|
||||||
|
Serial.print(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(" | ");
|
||||||
|
|
||||||
|
for (uint8_t index = 0;
|
||||||
|
index < blockSize;
|
||||||
|
++index)
|
||||||
|
{
|
||||||
|
const char character =
|
||||||
|
static_cast<char>(
|
||||||
|
data[index]);
|
||||||
|
|
||||||
|
if (character >= 32 &&
|
||||||
|
character <= 126)
|
||||||
|
{
|
||||||
|
Serial.print(character);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print('.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
61
src/WifiSetup.cpp
Normal file
61
src/WifiSetup.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include "WifiSetup.h"
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
|
||||||
|
bool WifiSetup::connect(const char* hostname)
|
||||||
|
{
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
WiFi.setHostname(hostname);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
|
WiFiManager wifiManager;
|
||||||
|
|
||||||
|
wifiManager.setConfigPortalTimeout(180);
|
||||||
|
|
||||||
|
Serial.print("Hostname: ");
|
||||||
|
Serial.println(hostname);
|
||||||
|
|
||||||
|
Serial.println("Forbinder til Wi-Fi...");
|
||||||
|
|
||||||
|
const bool connected =
|
||||||
|
wifiManager.autoConnect(
|
||||||
|
"FilamentGuard-Setup"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!connected)
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"Wi-Fi-opsætning fejlede eller fik timeout."
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Wi-Fi forbundet.");
|
||||||
|
|
||||||
|
Serial.print("Hostname: ");
|
||||||
|
Serial.println(WiFi.getHostname());
|
||||||
|
|
||||||
|
Serial.print("SSID: ");
|
||||||
|
Serial.println(WiFi.SSID());
|
||||||
|
|
||||||
|
Serial.print("IP-adresse: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WifiSetup::resetSettings()
|
||||||
|
{
|
||||||
|
WiFiManager wifiManager;
|
||||||
|
|
||||||
|
wifiManager.resetSettings();
|
||||||
|
|
||||||
|
Serial.println(
|
||||||
|
"Gemte Wi-Fi-oplysninger er slettet."
|
||||||
|
);
|
||||||
|
}
|
||||||
104
src/main.cpp
Normal file
104
src/main.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
|
||||||
|
#include "DeviceIdentity.h"
|
||||||
|
#include "DeviceSettings.h"
|
||||||
|
#include "FilamentGuardDeviceInfo.h"
|
||||||
|
#include "FirmwareUpdater.h"
|
||||||
|
#include "Pn5180Reader.h"
|
||||||
|
#include "WifiSetup.h"
|
||||||
|
|
||||||
|
DeviceSettings deviceSettings;
|
||||||
|
DeviceIdentity deviceIdentity;
|
||||||
|
WifiSetup wifiSetup;
|
||||||
|
FirmwareUpdater firmwareUpdater;
|
||||||
|
Pn5180Reader pn5180Reader;
|
||||||
|
|
||||||
|
FilamentGuardDeviceInfo deviceInfo;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("FilamentGuard starter...");
|
||||||
|
Serial.println("Firmwareversion: 1.0.1");
|
||||||
|
Serial.println("OTA-testen lykkedes.");
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("Initialiserer PN5180...");
|
||||||
|
|
||||||
|
pn5180Reader.begin();
|
||||||
|
pn5180Reader.printVersionInformation();
|
||||||
|
|
||||||
|
const String hostname =
|
||||||
|
deviceSettings.getOrCreateHostname();
|
||||||
|
|
||||||
|
deviceInfo =
|
||||||
|
deviceIdentity.create(hostname);
|
||||||
|
|
||||||
|
pn5180Reader.setDeviceInfo(
|
||||||
|
deviceInfo);
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("Enhedsidentitet:");
|
||||||
|
|
||||||
|
Serial.print("Device ID: ");
|
||||||
|
Serial.println(
|
||||||
|
deviceInfo.deviceId);
|
||||||
|
|
||||||
|
Serial.print("Hostname: ");
|
||||||
|
Serial.println(
|
||||||
|
deviceInfo.hostname);
|
||||||
|
|
||||||
|
if (!wifiSetup.connect(
|
||||||
|
deviceInfo.hostname.c_str()))
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"Kunne ikke forbinde til Wi-Fi.");
|
||||||
|
|
||||||
|
Serial.println(
|
||||||
|
"Genstarter om 5 sekunder...");
|
||||||
|
|
||||||
|
delay(5000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(
|
||||||
|
"Wi-Fi forbundet | Hostname: ");
|
||||||
|
|
||||||
|
Serial.print(
|
||||||
|
WiFi.getHostname());
|
||||||
|
|
||||||
|
Serial.print(" | SSID: ");
|
||||||
|
Serial.print(WiFi.SSID());
|
||||||
|
|
||||||
|
Serial.print(" | IP: ");
|
||||||
|
Serial.print(WiFi.localIP());
|
||||||
|
|
||||||
|
Serial.print(" | Signal: ");
|
||||||
|
Serial.print(WiFi.RSSI());
|
||||||
|
|
||||||
|
Serial.println(" dBm");
|
||||||
|
|
||||||
|
if (!firmwareUpdater.begin())
|
||||||
|
{
|
||||||
|
Serial.println(
|
||||||
|
"Firmware-opdatering kunne ikke startes.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firmwareUpdater.checkForUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println(
|
||||||
|
"Klar til at laese ISO15693-tags.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
pn5180Reader.checkForTag();
|
||||||
|
delay(200);
|
||||||
|
}
|
||||||
11
test/README
Normal file
11
test/README
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||||
Reference in New Issue
Block a user