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
|
||||
PUBLIC_DOMAIN=http://localhost
|
||||
PUBLIC_DOMAIN=https://payloads.p20.dev
|
||||
API_BEARER_TOKEN=replace-with-a-long-random-token
|
||||
WIFI_SSID=replace-with-your-ssid
|
||||
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
|
||||
- [`wifi_matrix_status`](sketches/arduino-uno-r4-wifi/wifi_matrix_status) – forbinder
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,13 @@ const secretsPath = path.join(
|
|||
"wifi_matrix_status",
|
||||
"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 readDefine = (name) => {
|
||||
|
|
@ -20,14 +27,30 @@ const readDefine = (name) => {
|
|||
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 = [
|
||||
"HOST_PORT=8152",
|
||||
"PUBLIC_DOMAIN=http://localhost",
|
||||
`API_BEARER_TOKEN=${crypto.randomBytes(32).toString("base64url")}`,
|
||||
`WIFI_SSID=${JSON.stringify(readDefine("SECRET_SSID"))}`,
|
||||
`WIFI_PASSWORD=${JSON.stringify(readDefine("SECRET_PASS"))}`,
|
||||
"PUBLIC_DOMAIN=https://payloads.p20.dev",
|
||||
`API_BEARER_TOKEN=${apiToken}`,
|
||||
`WIFI_SSID=${JSON.stringify(ssid)}`,
|
||||
`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");
|
||||
|
||||
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