Add DS18B20 payload sketch
This commit is contained in:
parent
43dcd37ea7
commit
3960b4db6e
7 changed files with 267 additions and 7 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
HOST_PORT=8152
|
HOST_PORT=8152
|
||||||
PUBLIC_DOMAIN=http://localhost
|
PUBLIC_DOMAIN=https://payloads.p20.dev
|
||||||
API_BEARER_TOKEN=replace-with-a-long-random-token
|
API_BEARER_TOKEN=replace-with-a-long-random-token
|
||||||
WIFI_SSID=replace-with-your-ssid
|
WIFI_SSID=replace-with-your-ssid
|
||||||
WIFI_PASSWORD=replace-with-your-password
|
WIFI_PASSWORD=replace-with-your-password
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,8 @@ Det samlede komponentindeks findes i [`components/`](components/README.md).
|
||||||
– browserstyret løbetekst via Wi-Fi
|
– browserstyret løbetekst via Wi-Fi
|
||||||
- [`wifi_matrix_status`](sketches/arduino-uno-r4-wifi/wifi_matrix_status) – forbinder
|
- [`wifi_matrix_status`](sketches/arduino-uno-r4-wifi/wifi_matrix_status) – forbinder
|
||||||
til et hotspot og viser forbindelse samt signalstyrke på LED-matrixen
|
til et hotspot og viser forbindelse samt signalstyrke på LED-matrixen
|
||||||
|
- [`ds18b20_payload`](sketches/arduino-uno-r4-wifi/ds18b20_payload) – måler en
|
||||||
|
KS0023 DS18B20 og sender temperaturen til payload-serveren hvert 10. sekund
|
||||||
- [`lcd1602_i2c_demo`](sketches/arduino-uno-r4-wifi/lcd1602_i2c_demo) – effekt-demo
|
- [`lcd1602_i2c_demo`](sketches/arduino-uno-r4-wifi/lcd1602_i2c_demo) – effekt-demo
|
||||||
til et eksternt 16×2 HD44780-display med PCF8574 I²C-backpack
|
til et eksternt 16×2 HD44780-display med PCF8574 I²C-backpack
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
`DAT` forbindes til en digital pin. Modulet indeholder den nødvendige
|
`DAT` forbindes til en digital pin. Modulet indeholder den nødvendige
|
||||||
pull-up-komponent; flere DS18B20-enheder kan dele samme 1-Wire-bus. Typiske
|
pull-up-komponent; flere DS18B20-enheder kan dele samme 1-Wire-bus. Typiske
|
||||||
biblioteker er `OneWire` og `DallasTemperature`. Ingen sketches endnu.
|
biblioteker er `OneWire` og `DallasTemperature`.
|
||||||
|
|
||||||
|
## Sketches
|
||||||
|
|
||||||
|
- Arduino UNO R4 WiFi: [`ds18b20_payload`](../../sketches/arduino-uno-r4-wifi/ds18b20_payload) – måler hvert 10. sekund og sender temperaturen til payload-serveren.
|
||||||
|
|
||||||
## Kilde
|
## Kilde
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@ const secretsPath = path.join(
|
||||||
"wifi_matrix_status",
|
"wifi_matrix_status",
|
||||||
"arduino_secrets.h",
|
"arduino_secrets.h",
|
||||||
);
|
);
|
||||||
|
const payloadSecretsPath = path.join(
|
||||||
|
repositoryRoot,
|
||||||
|
"sketches",
|
||||||
|
"arduino-uno-r4-wifi",
|
||||||
|
"ds18b20_payload",
|
||||||
|
"arduino_secrets.h",
|
||||||
|
);
|
||||||
|
|
||||||
const secrets = await fs.readFile(secretsPath, "utf8");
|
const secrets = await fs.readFile(secretsPath, "utf8");
|
||||||
const readDefine = (name) => {
|
const readDefine = (name) => {
|
||||||
|
|
@ -20,14 +27,30 @@ const readDefine = (name) => {
|
||||||
return JSON.parse(`"${match[1]}"`);
|
return JSON.parse(`"${match[1]}"`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ssid = readDefine("SECRET_SSID");
|
||||||
|
const password = readDefine("SECRET_PASS");
|
||||||
|
const existingEnv = await fs.readFile(path.join(repositoryRoot, ".env"), "utf8").catch(() => "");
|
||||||
|
const existingToken = existingEnv.match(/^API_BEARER_TOKEN=(.+)$/m)?.[1]?.trim();
|
||||||
|
const apiToken = existingToken || crypto.randomBytes(32).toString("base64url");
|
||||||
const env = [
|
const env = [
|
||||||
"HOST_PORT=8152",
|
"HOST_PORT=8152",
|
||||||
"PUBLIC_DOMAIN=http://localhost",
|
"PUBLIC_DOMAIN=https://payloads.p20.dev",
|
||||||
`API_BEARER_TOKEN=${crypto.randomBytes(32).toString("base64url")}`,
|
`API_BEARER_TOKEN=${apiToken}`,
|
||||||
`WIFI_SSID=${JSON.stringify(readDefine("SECRET_SSID"))}`,
|
`WIFI_SSID=${JSON.stringify(ssid)}`,
|
||||||
`WIFI_PASSWORD=${JSON.stringify(readDefine("SECRET_PASS"))}`,
|
`WIFI_PASSWORD=${JSON.stringify(password)}`,
|
||||||
|
"",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const payloadSecrets = [
|
||||||
|
"#pragma once",
|
||||||
|
"",
|
||||||
|
"// Generated by npm --prefix server run configure. Do not commit.",
|
||||||
|
`#define SECRET_SSID ${JSON.stringify(ssid)}`,
|
||||||
|
`#define SECRET_PASS ${JSON.stringify(password)}`,
|
||||||
|
`#define SECRET_API_TOKEN ${JSON.stringify(apiToken)}`,
|
||||||
"",
|
"",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
await fs.writeFile(path.join(repositoryRoot, ".env"), env, { mode: 0o600 });
|
await fs.writeFile(path.join(repositoryRoot, ".env"), env, { mode: 0o600 });
|
||||||
console.log("Created .env with HOST_PORT=8152, PUBLIC_DOMAIN=http://localhost and local secrets.");
|
await fs.writeFile(payloadSecretsPath, payloadSecrets, { mode: 0o600 });
|
||||||
|
console.log("Created .env and the DS18B20 payload sketch's local secret file.");
|
||||||
|
|
|
||||||
49
sketches/arduino-uno-r4-wifi/ds18b20_payload/README.md
Normal file
49
sketches/arduino-uno-r4-wifi/ds18b20_payload/README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# DS18B20 til payload-server
|
||||||
|
|
||||||
|
Sketchen måler temperaturen med et Keyestudio KS0023 DS18B20-modul og sender
|
||||||
|
en JSON-payload til `https://payloads.p20.dev/api/payloads` hvert 10. sekund.
|
||||||
|
Den indbyggede 12×8 LED-matrix viser samtidig:
|
||||||
|
|
||||||
|
- et flueben, når Wi-Fi er forbundet, eller et kryds uden forbindelse;
|
||||||
|
- en stribe på 10 LED'er, som tæller ned med én LED pr. sekund.
|
||||||
|
|
||||||
|
## Tilslutning
|
||||||
|
|
||||||
|
| KS0023 | Arduino UNO R4 WiFi |
|
||||||
|
| --- | --- |
|
||||||
|
| `DAT` | D2 |
|
||||||
|
| `VCC` | 5V |
|
||||||
|
| `GND` | GND |
|
||||||
|
|
||||||
|
KS0023-modulet har pull-up-modstanden til 1-Wire-datalinjen monteret på boardet.
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
Kopiér `arduino_secrets.example.h` til `arduino_secrets.h`. Brug samme
|
||||||
|
`SECRET_SSID` og `SECRET_PASS` som i `wifi_matrix_status`, og indsæt værdien af
|
||||||
|
`API_BEARER_TOKEN` fra repoets lokale `.env` som `SECRET_API_TOKEN`.
|
||||||
|
|
||||||
|
Den lokale secret-fil er allerede ignoreret af Git.
|
||||||
|
|
||||||
|
## Biblioteker og kompilering
|
||||||
|
|
||||||
|
Installér `OneWire` og `DallasTemperature` i Arduino IDE's Library Manager.
|
||||||
|
`WiFiS3` og `Arduino_LED_Matrix` følger med UNO R4-boardpakken.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
arduino-cli lib install OneWire DallasTemperature
|
||||||
|
arduino-cli compile --fqbn arduino:renesas_uno:unor4wifi sketches/arduino-uno-r4-wifi/ds18b20_payload
|
||||||
|
arduino-cli upload -p COM5 --fqbn arduino:renesas_uno:unor4wifi sketches/arduino-uno-r4-wifi/ds18b20_payload
|
||||||
|
```
|
||||||
|
|
||||||
|
API-payloaden har formen:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"topic": "sensors/temperature",
|
||||||
|
"payload": {
|
||||||
|
"device": "uno-r4-wifi-ds18b20",
|
||||||
|
"temperatureC": 22.50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Copy this file to arduino_secrets.h and fill in the local values.
|
||||||
|
// arduino_secrets.h is ignored by Git and must never be committed.
|
||||||
|
#define SECRET_SSID "your-network-name"
|
||||||
|
#define SECRET_PASS "your-network-password"
|
||||||
|
#define SECRET_API_TOKEN "your-payload-api-bearer-token"
|
||||||
175
sketches/arduino-uno-r4-wifi/ds18b20_payload/ds18b20_payload.ino
Normal file
175
sketches/arduino-uno-r4-wifi/ds18b20_payload/ds18b20_payload.ino
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
#include <Arduino_LED_Matrix.h>
|
||||||
|
#include <DallasTemperature.h>
|
||||||
|
#include <OneWire.h>
|
||||||
|
#include <WiFiS3.h>
|
||||||
|
|
||||||
|
#include "arduino_secrets.h"
|
||||||
|
|
||||||
|
constexpr uint8_t ROWS = 8;
|
||||||
|
constexpr uint8_t COLUMNS = 12;
|
||||||
|
constexpr uint8_t ONE_WIRE_PIN = 2;
|
||||||
|
constexpr unsigned long PAYLOAD_INTERVAL_MS = 10000;
|
||||||
|
constexpr unsigned long WIFI_RETRY_MS = 3000;
|
||||||
|
constexpr unsigned long RESPONSE_TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
|
constexpr char API_HOST[] = "payloads.p20.dev";
|
||||||
|
constexpr uint16_t API_PORT = 443;
|
||||||
|
constexpr char API_PATH[] = "/api/payloads";
|
||||||
|
|
||||||
|
ArduinoLEDMatrix matrix;
|
||||||
|
OneWire oneWire(ONE_WIRE_PIN);
|
||||||
|
DallasTemperature temperatureSensors(&oneWire);
|
||||||
|
uint8_t frame[ROWS][COLUMNS] = {};
|
||||||
|
|
||||||
|
unsigned long lastPayloadAt = 0;
|
||||||
|
unsigned long lastWiFiAttemptAt = 0;
|
||||||
|
int lastDisplayedSeconds = -1;
|
||||||
|
bool lastDisplayedConnected = false;
|
||||||
|
|
||||||
|
void clearFrame() {
|
||||||
|
memset(frame, 0, sizeof(frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPixel(int x, int y, bool on = true) {
|
||||||
|
if (x >= 0 && x < COLUMNS && y >= 0 && y < ROWS) {
|
||||||
|
frame[y][x] = on ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showCountdown(uint8_t secondsRemaining, bool connected) {
|
||||||
|
clearFrame();
|
||||||
|
|
||||||
|
// Wi-Fi status: a check mark when connected, an X otherwise.
|
||||||
|
if (connected) {
|
||||||
|
setPixel(0, 2);
|
||||||
|
setPixel(1, 3);
|
||||||
|
setPixel(2, 2);
|
||||||
|
setPixel(3, 1);
|
||||||
|
} else {
|
||||||
|
setPixel(0, 0);
|
||||||
|
setPixel(1, 1);
|
||||||
|
setPixel(2, 2);
|
||||||
|
setPixel(2, 0);
|
||||||
|
setPixel(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ten LEDs count down from 10 to 1 before the next payload.
|
||||||
|
const uint8_t lit = min(secondsRemaining, static_cast<uint8_t>(10));
|
||||||
|
for (uint8_t column = 0; column < lit; ++column) {
|
||||||
|
setPixel(column + 1, 6);
|
||||||
|
}
|
||||||
|
matrix.renderBitmap(frame, ROWS, COLUMNS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connectWiFi() {
|
||||||
|
if (WiFi.status() == WL_CONNECTED) return;
|
||||||
|
if (millis() - lastWiFiAttemptAt < WIFI_RETRY_MS) return;
|
||||||
|
|
||||||
|
lastWiFiAttemptAt = millis();
|
||||||
|
Serial.print(F("Forbinder til Wi-Fi: "));
|
||||||
|
Serial.println(SECRET_SSID);
|
||||||
|
WiFi.begin(SECRET_SSID, SECRET_PASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool postTemperature(float celsius) {
|
||||||
|
WiFiSSLClient client;
|
||||||
|
if (!client.connect(API_HOST, API_PORT)) {
|
||||||
|
Serial.println(F("Kunne ikke oprette HTTPS-forbindelse til payload-serveren."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String body = F("{\"topic\":\"sensors/temperature\",\"payload\":{");
|
||||||
|
body += F("\"device\":\"uno-r4-wifi-ds18b20\",\"temperatureC\":");
|
||||||
|
body += String(celsius, 2);
|
||||||
|
body += F("}}");
|
||||||
|
|
||||||
|
client.print(F("POST "));
|
||||||
|
client.print(API_PATH);
|
||||||
|
client.println(F(" HTTP/1.1"));
|
||||||
|
client.print(F("Host: "));
|
||||||
|
client.println(API_HOST);
|
||||||
|
client.print(F("Authorization: Bearer "));
|
||||||
|
client.println(SECRET_API_TOKEN);
|
||||||
|
client.println(F("Content-Type: application/json"));
|
||||||
|
client.println(F("X-Device-ID: uno-r4-wifi-ds18b20"));
|
||||||
|
client.println(F("X-Client-Name: ds18b20-payload-sketch"));
|
||||||
|
client.print(F("Content-Length: "));
|
||||||
|
client.println(body.length());
|
||||||
|
client.println(F("Connection: close"));
|
||||||
|
client.println();
|
||||||
|
client.print(body);
|
||||||
|
|
||||||
|
const unsigned long startedAt = millis();
|
||||||
|
while (!client.available() && client.connected() &&
|
||||||
|
millis() - startedAt < RESPONSE_TIMEOUT_MS) {
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.available()) {
|
||||||
|
Serial.println(F("Payload-serveren svarede ikke inden timeout."));
|
||||||
|
client.stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String statusLine = client.readStringUntil('\n');
|
||||||
|
const bool accepted = statusLine.indexOf(" 201 ") >= 0;
|
||||||
|
Serial.print(F("Serverens svar: "));
|
||||||
|
Serial.println(statusLine);
|
||||||
|
client.stop();
|
||||||
|
return accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPayload() {
|
||||||
|
temperatureSensors.requestTemperatures();
|
||||||
|
const float celsius = temperatureSensors.getTempCByIndex(0);
|
||||||
|
|
||||||
|
if (celsius == DEVICE_DISCONNECTED_C) {
|
||||||
|
Serial.println(F("DS18B20 blev ikke fundet paa 1-Wire-bussen."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(F("Temperatur: "));
|
||||||
|
Serial.print(celsius, 2);
|
||||||
|
Serial.println(F(" C"));
|
||||||
|
Serial.println(postTemperature(celsius) ? F("Payload sendt.") : F("Payload fejlede."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
matrix.begin();
|
||||||
|
temperatureSensors.begin();
|
||||||
|
temperatureSensors.setResolution(10);
|
||||||
|
|
||||||
|
showCountdown(10, false);
|
||||||
|
lastWiFiAttemptAt = millis() - WIFI_RETRY_MS;
|
||||||
|
connectWiFi();
|
||||||
|
lastPayloadAt = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
connectWiFi();
|
||||||
|
|
||||||
|
const unsigned long elapsed = millis() - lastPayloadAt;
|
||||||
|
const uint8_t secondsRemaining = elapsed >= PAYLOAD_INTERVAL_MS
|
||||||
|
? 0
|
||||||
|
: static_cast<uint8_t>((PAYLOAD_INTERVAL_MS - elapsed + 999) / 1000);
|
||||||
|
|
||||||
|
const bool connected = WiFi.status() == WL_CONNECTED;
|
||||||
|
if (secondsRemaining != lastDisplayedSeconds || connected != lastDisplayedConnected) {
|
||||||
|
lastDisplayedSeconds = secondsRemaining;
|
||||||
|
lastDisplayedConnected = connected;
|
||||||
|
showCountdown(secondsRemaining, connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elapsed >= PAYLOAD_INTERVAL_MS) {
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
sendPayload();
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Payload sprunget over: Wi-Fi er ikke forbundet."));
|
||||||
|
}
|
||||||
|
lastPayloadAt = millis();
|
||||||
|
lastDisplayedSeconds = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(20);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue