commit 67910264cd5dc90417a641bc5e268ad4b71c3718
Author: Markus Hanetzok <markus@hanetzok.net>
Date: Tue, 11 Jun 2024 22:09:25 +0200
initial commit
Diffstat:
5 files changed, 192 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+mts
+sensors.h
diff --git a/Makefile b/Makefile
@@ -0,0 +1,15 @@
+default: build
+
+build: clean
+ cp sensors.def.h sensors.h
+ gcc -Wall -o mts mts.c -l curl
+
+install: build
+ cp -f mts /usr/bin/mts
+ chmod +x /usr/bin/mts
+
+uninstall:
+ rm -f /usr/bin/mts
+
+clean:
+ rm -rf mts
diff --git a/README b/README
@@ -0,0 +1,15 @@
+sds - sensor data server
+========================
+sds is a simple webserver that I use to gather and display the values recorded
+by temperature & humidity sensors made with D1 Minis and DHT20 sensors.
+
+Dependencies
+------------
+libcurl is needed to compile this program
+
+Usage
+-----
+This program can be used with any kind of sensor, that can make its data
+available via HTTP. The response value should be plain text.
+
+Sensors can be added by creating a custom sensors.h.
diff --git a/sds.c b/sds.c
@@ -0,0 +1,153 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include <curl/curl.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+
+#define BUFFER_SIZE 1024
+#define OK_HEADER "HTTP/1.0 200 OK\r\nContent-Type:text/html\r\n\r\n"
+#define NOTFOUND_HEADER "HTTP/1.0 404 Not Found\r\nContent-Type:text/html\r\n\r\n"
+#define NOTALLOWED_HEADER "HTTP/1.0 405 Method Not Allowed\r\nContent-Type:text/html\r\n\r\n"
+#define INTERNALERR_HEADER "HTTP/1.0 500 Internal Server Error\r\nContent-Type:text/html\r\n\r\n"
+
+typedef struct {
+ char *name;
+ char *addr;
+ char *unit;
+} Sensor;
+
+typedef struct {
+ char *memory;
+ size_t size;
+} MemoryStruct;
+
+void die(char *msg) {
+ printf("%s\n", msg);
+ exit(1);
+}
+
+#include "sensors.h"
+
+size_t writemem(void *contents, size_t size, size_t nmemb, void *userp) {
+ size_t realsize = size * nmemb;
+ MemoryStruct *mem = (MemoryStruct *)userp;
+
+ char *ptr = realloc(mem->memory, mem->size + realsize + 1);
+ if (!ptr) {
+ fprintf(stderr, "not enough memory (realloc returned NULL)\n");
+ return 0;
+ }
+ mem->memory = ptr;
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+
+ return realsize;
+}
+
+void buildsite(char response[], char *name, char *val, char *unit) {
+ strcat(response, "<li>");
+ strcat(response, name);
+ strcat(response, ": ");
+ strcat(response, val);
+ strcat(response, unit);
+ strcat(response, "</li>");
+}
+
+int main(int argc, char *argv[]) {
+ if (argc < 3 || strcmp(argv[1], "-p") != 0)
+ die("usage: mts [-p PORT]");
+ char response[BUFFER_SIZE];
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ int port = atoi(argv[2]);
+ char buffer[BUFFER_SIZE];
+
+ struct sockaddr_in host_addr;
+ int host_addrlen = sizeof(host_addr);
+ host_addr.sin_family = AF_INET;
+ host_addr.sin_port = htons(port);
+ host_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ struct sockaddr_in client_addr;
+ int client_addrlen = sizeof(client_addr);
+
+ if (bind(sockfd, (struct sockaddr *)&host_addr, host_addrlen) != 0)
+ die("could not bind socket");
+ if (listen(sockfd, 10) != 0)
+ die("could not listen");
+
+ while(1) {
+ int clientfd = accept(sockfd, (struct sockaddr *)&host_addr, (socklen_t *)&host_addrlen);
+ if (clientfd < 0) {
+ fprintf(stderr, "could not accept connection\n");
+ continue;
+ }
+ getpeername(clientfd, (struct sockaddr *)&client_addr, (socklen_t *)&client_addrlen);
+
+ memset(buffer, 0, BUFFER_SIZE);
+ read(clientfd, buffer, BUFFER_SIZE);
+ char method[BUFFER_SIZE], uri[BUFFER_SIZE], version[BUFFER_SIZE];
+ sscanf(buffer, "%s %s %s", method, uri, version);
+ printf("[%s:%u] %s %s %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), method, version, uri);
+
+ memset(response, 0, BUFFER_SIZE);
+
+ if (strcmp("GET", method) != 0) {
+ strncat(response, NOTALLOWED_HEADER, BUFFER_SIZE - strlen(NOTALLOWED_HEADER) - 1);
+ strncat(response, "<html>method not allowed</html>", BUFFER_SIZE - strlen(response) - 1);
+ }
+ else if (strcmp("/", uri)) {
+ printf("%s\n", uri);
+ strncat(response, NOTFOUND_HEADER, BUFFER_SIZE - strlen(NOTFOUND_HEADER) - 1);
+ strncat(response, "<html>page not found</html>", BUFFER_SIZE - strlen(response) - 1);
+ }
+ else {
+ strncat(response, OK_HEADER, BUFFER_SIZE - strlen(OK_HEADER) - 1);
+ strcat(response, "<html><h1>Sensor data</h1><ul>");
+ int sensorsc = sizeof(sensors)/sizeof(sensors[0]);
+ int i;
+ for (i = 0; i < sensorsc; i++) {
+ char *name = sensors[i].name;
+ char *unit = sensors[i].unit;
+ CURL *curl;
+ CURLcode curlresponse;
+
+ MemoryStruct chunk;
+ chunk.memory = malloc(1);
+ chunk.size = 0;
+
+
+ curl = curl_easy_init();
+ if (curl == NULL) {
+ fprintf(stderr, "request failed\n");
+ continue;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, sensors[i].addr);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writemem);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
+
+ curlresponse = curl_easy_perform(curl);
+ if (curlresponse != CURLE_OK) {
+ fprintf(stderr, "request failed\n");
+ continue;
+ }
+
+ char *val = chunk.memory;
+ buildsite(response, name, val, unit);
+
+ free(chunk.memory);
+ curl_easy_cleanup(curl);
+
+ }
+ strcat(response, "</ul></html>");
+ }
+ write(clientfd, response, strlen(response));
+ close(clientfd);
+ }
+ return 0;
+}
diff --git a/sensors.def.h b/sensors.def.h
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+static const Sensor sensors[] = {
+ // name // address // unit
+ { "Sensor description", "192.168.178.1/temp", "C"},
+ { "Another sensor description", "192.168.178.1/hum", "%"},
+};