Skip to content
Merged
8 changes: 8 additions & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ avr
Calib
cassert
cfg
charset
chr
chrono
ci
Clib
cmake
concat
config
cout
cpp
Expand Down Expand Up @@ -71,6 +73,7 @@ len
LGPL
libgtk
libtc
localhost
localtime
lowpoint
LOWTHRESH
Expand All @@ -79,6 +82,7 @@ makedirs
mday
markdownlint
mega
memcmp
memset
microcontroler
milli
Expand Down Expand Up @@ -128,13 +132,15 @@ solonoids
src
strcpy
strlen
strncmp
strnlen
strncpy
substr
sudo
sys
tankid
testfile
Thu
timeinfo
Tmp
TODO
Expand All @@ -147,6 +153,7 @@ unittest
unixtime
unshallow
url
UTF
vfprintf
vprintf
vscode
Expand All @@ -161,3 +168,4 @@ wxpython
xslx
yaml
yml
yy
179 changes: 162 additions & 17 deletions src/Devices/EthernetServer_TC.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#include "Devices/EthernetServer_TC.h"

#include <avr/wdt.h>

#include "DateTime_TC.h"
#include "SD_TC.h"
#include "Serial_TC.h"
#include "TankControllerLib.h"

// class variables
EthernetServer_TC* EthernetServer_TC::_instance = nullptr;
Expand All @@ -22,24 +27,164 @@ EthernetServer_TC* EthernetServer_TC::instance() {
*/
EthernetServer_TC::EthernetServer_TC(uint16_t port) : EthernetServer(port) {
begin();
serial(F("Ethernet Server is listening on port 80 of ??????"));
serial(F("Ethernet Server is listening on port 80"));
}

/**
* Look for request from external client and handle it.
* This (incomplete) code copied from HandleRequest()
*/
void EthernetServer_TC::handleRequest() {
// listen for incoming clients
EthernetClient rpc_client = available(); // Raspberry Pi Client
if (rpc_client) {
// TODO: NEED TO IMPLEMENT
// HandleRequest(rpc_client);

// give the web browser time to receive the data
// delay(ONE_SECOND_DELAY_IN_MILLIS);
// close the connection:
rpc_client.stop();
serial(F("rpc_client disconnected"));
void EthernetServer_TC::echo() {
serial(F("echo() - \"%s\""), buffer + 19);
int i = 19;
while (buffer[i] != ' ' && buffer[i] != '\0') {
++i;
}
serial(F("echo() found space or null at %d"), i);
if (memcmp_P(buffer + i - 3, F("%22"), 3)) {
serial(F("bad"));
} else {
buffer[i - 3] = '\0';
serial(F("echo \"%s\""), buffer + 19);
sendHeadersWithSize(strlen(buffer + 19));
client.write(buffer + 19);
client.stop();
state = NOT_CONNECTED;
}
}

bool EthernetServer_TC::file() {
// Buffer has something like "GET /path HTTP/1.1"
// and we want to put a null at the end of the path.
int i = 4;
while (buffer[i] != ' ') {
++i;
}
buffer[i] = '\0';
// Look for file in SD card.
if (!SD_TC::instance()->exists(buffer + 4)) {
serial(F("file - \"%s\" not found!"), buffer + 4);
return false;
}
// Open file and send headers with file size.
File file = SD_TC::instance()->open(buffer + 4);
uint32_t size = file.size();
serial(F("file \"%s\" has a size of %lu"), buffer + 4, size);
sendHeadersWithSize(size);
// Send file contents
uint32_t totalBytes = 0;
uint32_t timeInRead = 0;
uint32_t timeInWrite = 0;
uint32_t timeInFlush = 0;
uint32_t startTime = 0;

wdt_disable();
uint32_t flushCount = 0;
while (file.available32()) {
startTime = millis();
int readSize = file.read(buffer, sizeof(buffer));
timeInRead += millis() - startTime;
startTime = millis();
int writeSize = client.write(buffer, readSize);
timeInWrite += millis() - startTime;
if (writeSize != readSize) {
serial(F("totalBytes = %lu; read = %d; write = %d"), totalBytes, readSize, writeSize);
break;
}
totalBytes += writeSize;
if (totalBytes % 8196 == 0) {
startTime = millis();
client.flush();
timeInFlush += millis() - startTime;
++flushCount;
}
}
file.close();
client.stop();
state = NOT_CONNECTED;
serial(F("write = %lu; freeMemory = %i"), totalBytes, TankControllerLib::instance()->freeMemory());
serial(F("timeInRead = %lu; timeInWrite = %lu; timeInFlush = %lu"), timeInRead, timeInWrite, timeInFlush);
wdt_enable(WDTO_8S);
return true;
}

void EthernetServer_TC::get() {
if (memcmp_P(buffer + 4, F("/echo?value=%22"), 15) == 0) {
echo();
} else if (!file()) {
// TODO: send an error response
serial(F("get \"%s\" not recognized!"), buffer + 4);
client.stop();
state = NOT_CONNECTED;
}
}

void EthernetServer_TC::loop() {
if (client || (client = accept())) { // if we have a connection
if (state == NOT_CONNECTED) {
state = READ_REQUEST;
bufferContentsSize = 0;
connectedAt = millis(); // record start time (so we can do timeout)
}
// read request
int next;
while (state == READ_REQUEST && bufferContentsSize < sizeof(buffer) && (next = client.read()) != -1) {
buffer[bufferContentsSize++] = (char)(next & 0xFF);
if (bufferContentsSize > 1 && buffer[bufferContentsSize - 2] == '\r' && buffer[bufferContentsSize - 1] == '\n') {
buffer[bufferContentsSize - 2] = '\0';
state = HAS_REQUEST;
if (memcmp_P(buffer, F("GET "), 4) == 0) {
state = GET_REQUEST;
break;
}
break;
}
}
switch (state) {
case GET_REQUEST:
get();
break;
default:
break;
}
} else if (state != NOT_CONNECTED) { // existing connection has been closed
state = NOT_CONNECTED;
bufferContentsSize = 0;
client.stop();
connectedAt = 0;
} else {
// no client and not recently connected
}
}

void EthernetServer_TC::sendHeadersWithSize(uint32_t size) {
char buffer[128];
static const char response[] PROGMEM =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain;charset=UTF-8\r\n"
"Content-Encoding: identity\r\n"
"Content-Language: en-US\r\n";
strncpy_P(buffer, (PGM_P)response, sizeof(buffer));
client.write(buffer);
snprintf_P(buffer, sizeof(buffer), (PGM_P)F("Content-Length: %lu\r\n"), (unsigned long)size);
client.write(buffer);

// TODO: add "Date: " header
const __FlashStringHelper* weekdays[] = {F("Sun"), F("Mon"), F("Tue"), F("Wed"), F("Thu"), F("Fri"), F("Sat")};
const __FlashStringHelper* months[] = {F("Jan"), F("Feb"), F("Mar"), F("Apr"), F("May"), F("Jun"),
F("Jul"), F("Aug"), F("Sep"), F("Oct"), F("Nov"), F("Dec")};
DateTime_TC now = DateTime_TC::now();
// int weekday = weekday(now.getYear(), now.getMonth(), now.getDay());
// snprintf_P(buffer, sizeof(buffer), F("Date: %s, %02d %s %04d %02d:%02d:%02d GMT\r\n"), );

// blank line indicates end of headers
client.write('\r');
client.write('\n');
}

int EthernetServer_TC::weekday(int year, int month, int day) {
// Calculate day of week in proleptic Gregorian calendar. Sunday == 0.
int adjustment, mm, yy;
if (year < 2000)
year += 2000;
adjustment = (14 - month) / 12;
mm = month + 12 * adjustment - 2;
yy = year - adjustment;
return (day + (13 * mm - 1) / 5 + yy + yy / 4 - yy / 100 + yy / 400) % 7;
}
20 changes: 18 additions & 2 deletions src/Devices/EthernetServer_TC.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <Ethernet.h>
#endif

enum serverState_t { NOT_CONNECTED, READ_REQUEST, GET_REQUEST, HAS_REQUEST };

/**
* EthernetServer_TC provides wrapper for web server for TankController
*
Expand All @@ -20,12 +22,26 @@ class EthernetServer_TC : public EthernetServer {
const char* className() const {
return "EthernetServer_TC";
}
void handleRequest();
void loop();

private:
// class variables
static EthernetServer_TC* _instance;

// instance methods
// instance variables
EthernetClient client;
serverState_t state = NOT_CONNECTED;
char buffer[1024];
int bufferContentsSize = 0;
unsigned long connectedAt = 0;

// instance methods: constructor
EthernetServer_TC(uint16_t port);
// instance methods: utility
void sendHeadersWithSize(uint32_t size);
int weekday(int year, int month, int day);
// instance methods: HTTP
void echo();
bool file();
void get();
};
4 changes: 2 additions & 2 deletions src/Devices/PushingBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ void PushingBox::sendData() {
}
char buffer[200];
if (TankControllerLib::instance()->isInCalibration()) {
char format[] PROGMEM =
static const char format[] PROGMEM =
"GET /pushingbox?devid=%s&tankid=%i&tempData=C&pHdata=C HTTP/1.1\r\n"
"Host: api.pushingbox.com\r\n"
"Connection: close\r\n"
"\r\n";
snprintf_P(buffer, sizeof(buffer), (PGM_P)format, DevID, tankID);
} else {
char format[] PROGMEM =
static const char format[] PROGMEM =
"GET /pushingbox?devid=%s&tankid=%i&tempData=%.2f&pHdata=%.3f HTTP/1.1\r\n"
"Host: api.pushingbox.com\r\n"
"Connection: close\r\n"
Expand Down
2 changes: 1 addition & 1 deletion src/Devices/PushingBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class PushingBox {
EthernetClient client;
const char *DevID = nullptr; // DeviceID assigned by PushingBox and passed-in from TankController.ino
// wait a bit for first reading (https://github.com/Open-Acidification/TankController/issues/179)
unsigned long nextSendTime = 70000;
uint32_t nextSendTime = 70000;
const char *server = "api.pushingbox.com";

// instance method
Expand Down
5 changes: 1 addition & 4 deletions src/Devices/SD_TC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,8 @@ File SD_TC::open(const char* path, oflag_t oflag) {
return sd.open(path, oflag);
}

/**
* print the root directory and all subdirectories
*/
void SD_TC::printRootDirectory() {
// serial(F("SD_TC::printRootDirectory()"));
sd.ls(LS_DATE | LS_SIZE | LS_R);
}

void SD_TC::todaysDataFileName(char* path, int size) {
Expand Down
3 changes: 1 addition & 2 deletions src/Devices/Serial_TC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ void Serial_TC::vprintf(const __FlashStringHelper *format, va_list args) {
// need to avoid recursion since SD_TC could call serial()
if (!printIsActive) {
printIsActive = true;
// this seems to cause problems on the actual hardware
// SD_TC::instance()->appendToLog(buffer);
SD_TC::instance()->appendToLog(buffer);
printIsActive = false;
}
#ifdef MOCK_PINS_COUNT
Expand Down
Loading