diff --git a/inc/drivers/OpenLog/openlog.h b/inc/drivers/OpenLog/openlog.h new file mode 100644 index 000000000..6a873cab0 --- /dev/null +++ b/inc/drivers/OpenLog/openlog.h @@ -0,0 +1,78 @@ +#pragma once + +#include "STM32Pin.h" +#include "STM32Serial.h" + +namespace codal { + +/** + * @brief the class representing the OpenLog micro-sd card reader + */ +class OpenLog { + public: + /** + * @brief Constructor of the OpenLog class + * + * @param tx the pin used to transmit data via serial to the OpenLog + * @param rx the pin used to receive data via serial to the OpenLog + * @param reset the pin used to reset the OpenLog (via OpenLog's GRN pin) + * + * @note As serial is used to communicate with the OpenLog, serial-compatible pins must be provided + */ + OpenLog(STM32Pin& tx, STM32Pin& rx, STM32Pin& reset); + ~OpenLog(); + + /** + * @brief Writes data to the micro-sd card, in a file which name's defined in the .cpp. + * + * @param data the text to write to the micro-sd card + * @param dataSize the size of the text to write + * + * @return Wether the write operation was successful or not + * + * @note As the OpenLog is implemented in order to allow offline sensor data logging, + * being able to set a file name has not been implemented (as not needed at the moment) + */ + bool write(const char* data, unsigned dataSize); + + private: + STM32Serial* serial; + STM32Pin& resetPin; + uint8_t* buffer; + + /** + * @brief an utility function to send the data via serial in fixed sized buffers + * + * @param data the data to send + * @param dataSize the size of the data to send + * + * @return an int representing the number of transmited bytes (should be equal to dataSize), or an error code + * (DEVICE_INVALID_PARAMETER or DEVICE_SERIAL_IN_USE) + */ + int sendData(const uint8_t* data, unsigned dataSize); + + /** + * @brief Sets the command mode on the OpenLog, enabling file operations on the micro-sd card + * @note Can also be used to end some of the openlog commands (e.g the append command) + */ + void goToCommandMode(); + + /** + * @brief hard resets the OpenLog via `resetPin` + * + * @note this function is used to restart the OpenLog after this class' serial is initialized, in order to ensure + * later that the OpenLog is responding well to the send commands / text + */ + void reset(); + + /** + * @brief listens to the OpenLog's serial output until the character `waitchar` + * + * @param waitChar the char to wait for (must be `>` or `<`) + * @note this function is used to wait until the Openlog is ready to accept commands / text + * @see + * https://github.com/sparkfun/OpenLog/blob/master/firmware/Arduino_Examples/Example3_ReadFile/Example3_ReadFile.ino + */ + void waitUntilReady(const char waitChar); +}; +} // namespace codal \ No newline at end of file diff --git a/source/drivers/OpenLog/openlog.cpp b/source/drivers/OpenLog/openlog.cpp new file mode 100644 index 000000000..c9c018d42 --- /dev/null +++ b/source/drivers/OpenLog/openlog.cpp @@ -0,0 +1,89 @@ +#include "openlog.h" + +#include + +using namespace codal; + +constexpr const unsigned BUFFER_SIZE = 100; +constexpr const unsigned OPENLOG_BAUDRATE = 9600; +constexpr const char* APPEND = "append"; +constexpr const char* FILE_NAME = "out.csv"; +constexpr const uint8_t ESCAPE_SEQUENCE[] = {26, 26, 26}; +constexpr const char READY_TO_RECEIVE_ANY = '<'; +constexpr const char READY_TO_RECEIVE_COMMANDS = '>'; + +OpenLog::OpenLog(STM32Pin& tx, STM32Pin& rx, STM32Pin& reset) : resetPin(reset) +{ + this->buffer = (uint8_t*)malloc(BUFFER_SIZE); + this->serial = new STM32Serial(tx, rx); + this->serial->init(OPENLOG_BAUDRATE); + this->reset(); + this->waitUntilReady(READY_TO_RECEIVE_ANY); + this->goToCommandMode(); +} + +OpenLog::~OpenLog() +{ + free(this->buffer); +} + +bool OpenLog::write(const char* data, unsigned dataSize) +{ + int sendResult; + const unsigned commandLength = strlen(APPEND) + 1 + strlen(FILE_NAME) + 1; + strcat(strcat(strcat(strcpy((char*)this->buffer, APPEND), " "), FILE_NAME), "\r"); + + sendResult = this->sendData(this->buffer, commandLength); + if (sendResult == DEVICE_INVALID_PARAMETER || sendResult == DEVICE_SERIAL_IN_USE) return false; + + this->waitUntilReady(READY_TO_RECEIVE_ANY); + sendResult = this->sendData((uint8_t*)data, dataSize - 1); // -1 because we're not sending the trailing \0 + if (sendResult == DEVICE_INVALID_PARAMETER || sendResult == DEVICE_SERIAL_IN_USE) return false; + + this->goToCommandMode(); + + return true; +} + +int OpenLog::sendData(const uint8_t* data, unsigned dataSize) +{ + const unsigned maxSendSize = CODAL_SERIAL_DEFAULT_BUFFER_SIZE; + const unsigned sendNumbers = dataSize / maxSendSize + !(dataSize % maxSendSize == 0); + unsigned transmittedBytes = 0; + int sendResult; + + for (unsigned i = 0; i < sendNumbers; ++i) { + unsigned currentSendSize = (i < dataSize / maxSendSize) ? maxSendSize : dataSize % maxSendSize; + buffer = (uint8_t*)memcpy(this->buffer, data + (i * maxSendSize), currentSendSize); + sendResult = this->serial->send(this->buffer, currentSendSize); + + if (sendResult != DEVICE_INVALID_PARAMETER && sendResult != DEVICE_SERIAL_IN_USE) + transmittedBytes += sendResult; + else { + return sendResult; + } + } + return transmittedBytes; +} + +void OpenLog::goToCommandMode() +{ + this->sendData(ESCAPE_SEQUENCE, 3); + this->waitUntilReady(READY_TO_RECEIVE_COMMANDS); +} + +void OpenLog::reset() +{ + this->resetPin.setDigitalValue(0); + fiber_sleep(100); + this->resetPin.setDigitalValue(1); +} + +void OpenLog::waitUntilReady(const char waitChar) +{ + if (!(waitChar == '>' || waitChar == '<')) return; + do { + fiber_sleep(10); + this->serial->read(this->buffer, BUFFER_SIZE, ASYNC); + } while (strchr((char*)this->buffer, waitChar) == NULL); +} \ No newline at end of file