diff --git a/.github/workflows/github_actions_arduino_lint.yml b/.github/workflows/github_actions_arduino_lint.yml new file mode 100644 index 0000000..e63ae96 --- /dev/null +++ b/.github/workflows/github_actions_arduino_lint.yml @@ -0,0 +1,7 @@ +on: [push, pull_request] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: arduino/arduino-lint-action@v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c0542d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +documentation.sh +Doxyfile +api.md +./rust/target/* +./javascript/ diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 0000000..d1a923a --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,7 @@ +Thank you for your interest in improving this library. + +If you have any questions or have found a bug, please feel free to create an +Issue on this project's GitHub page. + +Any suggestions for improvements are welcome as well. Please bring them up as +an Issue or a Pull Request. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8a9253b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Microfire LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..832d360 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +Mod-Temp +======= + +> [Datasheet](https://docs.google.com/document/d/1xpLEcBNHOzufufisoClGkodkMlKaTpSObjkInAdEXmk/export?format=pdf&%3Bref=microfire-llc&ref=microfire-llc) 📜 + +Add the ability to measure any 10k NTC temperature to your hardware application with a fully digital interface. + +#### Summary ℹ️ +* Use any 10K NTC +* Resolution 0.125 °C +* I²C, UART, and USB interfaces +* 25x15 mm for castellated or DIP mounting +* Single point calibration + +* * * + +### Use + +* [I²C Arduino Library Documentation](https://docs.google.com/document/d/1qkSqg-a04shQ1ymTpcTnkoENiLpSfFyuMYOSYcGP5eU/export?format=pdf&ref=microfire-llc) +* [I²C Raspberry Pi Python Documentation](https://docs.google.com/document/d/1RHCg3jZDgGcX70pDZXZN49C9UcAZaMesGkK2WYte2zM/export?format=pdf&ref=microfire-llc) + +* * * + +### Design ✒️ + +Adding a module to your own design is straightforward. You'll need: + +1. bus connection to your controlling device +2. clean, preferably isolated, power supply +3. probe connection (U.FL, BNC, terminal block) + +* * * + +### Ask a question 🤙 + +* [Discord](https://discord.gg/rAnZPdW) +* [questions@microfire.co](mailto:questions@microfire.co) + +* * * + +### Website +[microfire.co](https://microfire.co) \ No newline at end of file diff --git a/examples/Basic/Basic.ino b/examples/Basic/Basic.ino new file mode 100644 index 0000000..531866c --- /dev/null +++ b/examples/Basic/Basic.ino @@ -0,0 +1,38 @@ +/*! + microfire.co for links to documentation, examples, and libraries + github.com/u-fire for feature requests, bug reports, and questions + questions@microfire.co to get in touch with someone + + Mod-NTC hardware version 2, firmware 1 +*/ + +#include +Microfire::Mod_NTC::i2c ntc; + +void setup() +{ + Serial.begin(9600); + Wire.begin(); + + // start the sensor after Wire.begin() + ntc.begin(); +} + +void loop() +{ + // take a measurement + ntc.measureTemp(); + + // display the results + Serial.print(ntc.tempC, 2); + Serial.println(" °C"); + Serial.print(ntc.tempF, 2); + Serial.println(" °F"); + Serial.print(ntc.tempK, 2); + Serial.println(" °K"); + Serial.print(ntc.resistance, 0); + Serial.println(" Ohms"); + Serial.println(); + + delay(1000); +} \ No newline at end of file diff --git a/examples/ErrorHandling/ErrorHandling.ino b/examples/ErrorHandling/ErrorHandling.ino new file mode 100644 index 0000000..ac80eae --- /dev/null +++ b/examples/ErrorHandling/ErrorHandling.ino @@ -0,0 +1,46 @@ +/*! + microfire.co for links to documentation, examples, and libraries + github.com/u-fire for feature requests, bug reports, and questions + questions@microfire.co to get in touch with someone + + Mod-NTC hardware version 2, firmware 1 +*/ + +#include +Microfire::Mod_NTC::i2c ntc; + +void setup() +{ + Serial.begin(9600); + Wire.begin(); + + // start the sensor after Wire.begin() + ntc.begin(); +} + +void loop() +{ + // take a measurement + ntc.measureTemp(); + + // display the results + ntc.measureTemp(); + switch (ntc.status) + { + case ntc.STATUS_NO_PROBE: + Serial.println("Error: No probe connected"); + break; + case ntc.STATUS_NO_ERROR: + Serial.print(ntc.tempC, 2); + Serial.println(" °C"); + Serial.print(ntc.tempF, 2); + Serial.println(" °F"); + Serial.print(ntc.tempK, 2); + Serial.println(" °K"); + Serial.print(ntc.resistance, 0); + Serial.println(" Ohms"); + Serial.println(); + break; + } + delay(1000); +} \ No newline at end of file diff --git a/examples/Shell/Shell.ino b/examples/Shell/Shell.ino new file mode 100644 index 0000000..b69125f --- /dev/null +++ b/examples/Shell/Shell.ino @@ -0,0 +1,171 @@ +/* + This allows you to run various functions in a command-line like interface. + Enter: + `config` to see the configuration of the device. + Measure Temperature: + temp + Change beta of probe + beta 3950 + Change the I2C address: + i2c 0x0F +*/ + +#include +#include +#include + +Microfire::Mod_NTC::i2c ntc; + +String buffer, cmd, p1, p2; +const int fw_compatible = 1; +const int hw_compatible = 2; + +void config() +{ + ntc.update(); + Serial.println((String) "Microfire Mod-NTC Sensor: " + (ntc.connected() ? "connected" : "*disconnected*")); + if (!ntc.connected()) + return; + if ((ntc.fwVersion != fw_compatible) || (ntc.hwVersion != hw_compatible)) + { + Serial.println("*This version of shell was designed for a different hardware revision or firmware version*"); + } + Serial.print("β: "); Serial.println(ntc.beta); + Serial.print("hardware:firmware version: "); + Serial.print(ntc.hwVersion); + Serial.print(":"); + Serial.println(ntc.fwVersion); +} + +void beta() +{ + ntc.setBeta(p1.toFloat()); + Serial.print("β: "); Serial.println(ntc.beta); +} + +void temperature() +{ + while (Serial.available() == 0) + { + ntc.measureTemp(); + switch (ntc.status) + { + case ntc.STATUS_NO_PROBE: + Serial.println("Error: No probe connected"); + break; + case ntc.STATUS_NO_ERROR: + Serial.print(ntc.tempC, 2); + Serial.println(" °C"); + Serial.print(ntc.tempF, 2); + Serial.println(" °F"); + Serial.print(ntc.tempK, 2); + Serial.println(" °K"); + Serial.print(ntc.resistance, 0); + Serial.println(" Ohms"); + Serial.println(); + break; + } + delay(1000); + } +} + +void i2c() +{ + uint8_t i2c_address; + char *p; + + if (p1.length()) + { + i2c_address = strtoul(p1.c_str(), &p, 16); + Serial.println(i2c_address); + if (i2c_address == 0) + { + Serial.println("Error: malformed I2C address"); + } + else if ((i2c_address <= 0x07) || (i2c_address > 0x7f)) + { + Serial.println("Error: I2C address not in valid range"); + } + else + { + ntc.setI2CAddress(i2c_address); + } + } +} + +void help() +{ + Serial.println(F("List of available commands")); + Serial.println(F("config -or- c : no parameters : Displays all configuration and system information.")); + Serial.println(F("i2c : I2C_address : Changes the module's I2C address.")); + Serial.println(F("temp -or- t : no parameters : Starts a temperature measurement")); + Serial.println(F("beta -or- b : no parameters : Saves a new probe beta")); +} + +void cmd_run() +{ + if ((cmd == "conf") || (cmd == "config") || (cmd == "c")) + config(); + if ((cmd == "temp") || (cmd == "t")) + temperature(); + if (cmd == "i2c") + i2c(); + if ((cmd == "beta") || (cmd == "b")) + beta(); + if ((cmd == "help") || (cmd == "h")) + help(); +} + +void cli_process() +{ + while (Serial.available()) + { + char c = Serial.read(); + + switch (c) + { + case '\n': + Serial.println(); + cmd = buffer.substring(0, buffer.indexOf(" ", 0)); + cmd.trim(); + buffer.remove(0, buffer.indexOf(" ", 0)); + buffer.trim(); + p1 = buffer.substring(0, buffer.indexOf(" ", 0)); + p1.trim(); + buffer.remove(0, buffer.indexOf(" ", 0)); + buffer.trim(); + p2 = buffer.substring(0, buffer.indexOf(" ", 0)); + p2.trim(); + cmd_run(); + + Serial.print("> "); + buffer = ""; + break; + + case '\b': // backspace + buffer.remove(buffer.length() - 1); + Serial.print("\b \b"); + break; + + default: // everything else + buffer += c; + Serial.print(c); + } + } +} + +void setup() +{ + Wire.begin(); + ntc.begin(); + Serial.begin(9600); + Serial.println("Type `help` for a list of commands"); + Serial.println("Type `t` to take a temperature measurement"); + config(); + Serial.print("> "); +} + +void loop() +{ + cli_process(); +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..1e325ab --- /dev/null +++ b/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring Map +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +connected KEYWORD2 +measureTemp KEYWORD2 +setI2CAddress KEYWORD2 +update KEYWORD2 + + +####################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### +Class ================================== +Mod_Temp KEYWORD1 diff --git a/library.json b/library.json new file mode 100644 index 0000000..5ad803f --- /dev/null +++ b/library.json @@ -0,0 +1,20 @@ +{ + "name": "Microfire Mod-NTC", + "keywords": "temperature, digital, ntc", + "description": "Add the ability to measure any 10k NTC temperature to your hardware application with a fully digital interface.", + "repository": { + "type": "git", + "url": "https://github.com/u-fire/Mod-NTC.git" + }, + "authors": [ + { + "name": "Microfire LLC", + "email": "contact@microfire.co", + "url": "https://microfire.co", + "maintainer": true + } + ], + "version": "2.0.0", + "frameworks": "arduino", + "platforms": "*" +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..c49ba69 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=Microfire Mod-NTC +version=2.0.0 +author=Microfire LLC +maintainer=contact@microfire.co +sentence=Add the ability to measure any 10k NTC temperature to your hardware application with a fully digital interface. +paragraph=I2C, UART, and USB interfaces, connect nearly any flow meter. +category=Sensors +url=https://microfire.co +architectures=* diff --git a/src/Microfire_Mod-NTC.cpp b/src/Microfire_Mod-NTC.cpp new file mode 100644 index 0000000..53bb455 --- /dev/null +++ b/src/Microfire_Mod-NTC.cpp @@ -0,0 +1,158 @@ +#include "Microfire_Mod-NTC.h" + +int Microfire::Mod_NTC::i2c::_hwVersion = -1; +int Microfire::Mod_NTC::i2c::_fwVersion = -1; +int Microfire::Mod_NTC::i2c::_status = -1; + +float Microfire::Mod_NTC::i2c::_tempC = -1; +float Microfire::Mod_NTC::i2c::_tempF = -1; +float Microfire::Mod_NTC::i2c::_tempK = -1; +float Microfire::Mod_NTC::i2c::_resistance = -1; +float Microfire::Mod_NTC::i2c::_beta = -1; + +namespace Microfire +{ + namespace Mod_NTC + { + // Initializes sensor + bool i2c::begin(TwoWire &wirePort, uint8_t address) + { + _address = address; + _i2cPort = &wirePort; + + return connected(); + } + + // Returns true or false if the sensor is connected. + bool i2c::connected() + { + _i2cPort->beginTransmission(_address); + uint8_t retval = _i2cPort->endTransmission(); + + if (retval) + { + return false; + } + else + { + return true; + } + } + + // Takes a temperature measurement + float i2c::measureTemp(bool blocking) + { + _send_command(MEASURE_TEMP_TASK); + if (blocking) + { + delay(150); + } + + _updateRegisters(); + + return tempC; + } + + // Change the I2C address. + void i2c::reset() + { + setBeta(3977.0); + } + + // Change the I2C address. + void i2c::setI2CAddress(uint8_t i2cAddress) + { + _write_4_bytes(BUFFER_REGISTER, i2cAddress); + _send_command(I2C_TASK); + _address = i2cAddress; + } + + // Change the beta. + float i2c::setBeta(float beta) + { + _write_4_bytes(BETA_REGISTER, beta); + _send_command(BETA_TASK); + _updateRegisters(); + return _beta; + } + + void i2c::update() + { + _updateRegisters(); + } + + void i2c::_updateRegisters() + { + _status = _read_byte(STATUS_REGISTER); + _hwVersion = _read_byte(HW_VERSION_REGISTER); + _fwVersion = _read_byte(FW_VERSION_REGISTER); + _resistance = _read_4_bytes(RESISTANCE_REGISTER); + _beta = _read_4_bytes(BETA_REGISTER); + _tempC = _read_4_bytes(TEMP_C_REGISTER); + _tempK = _read_4_bytes(TEMP_K_REGISTER); + _tempF = _read_4_bytes(TEMP_F_REGISTER); + } + + void i2c::_send_command(uint8_t command) + { + _i2cPort->beginTransmission(_address); + _i2cPort->write(TASK_REGISTER); + _i2cPort->write(command); + _i2cPort->endTransmission(); + } + + void i2c::_write_4_bytes(uint8_t reg, float f) + { + uint8_t b[5]; + float f_val = f; + + b[0] = reg; + b[1] = *((uint8_t *)&f_val); + b[2] = *((uint8_t *)&f_val + 1); + b[3] = *((uint8_t *)&f_val + 2); + b[4] = *((uint8_t *)&f_val + 3); + _i2cPort->beginTransmission(_address); + _i2cPort->write(b, 5); + _i2cPort->endTransmission(); + } + + float i2c::_read_4_bytes(uint8_t reg) + { + float retval; + + _i2cPort->beginTransmission(_address); + _i2cPort->write(reg); + _i2cPort->endTransmission(); + _i2cPort->requestFrom(_address, (uint8_t)4); + *((uint8_t *)&retval) = _i2cPort->read(); + *((uint8_t *)&retval + 1) = _i2cPort->read(); + *((uint8_t *)&retval + 2) = _i2cPort->read(); + *((uint8_t *)&retval + 3) = _i2cPort->read(); + return retval; + } + + void i2c::_write_byte(uint8_t reg, uint8_t val) + { + uint8_t b[5]; + + b[0] = reg; + b[1] = val; + _i2cPort->beginTransmission(_address); + _i2cPort->write(b, 2); + _i2cPort->endTransmission(); + } + + uint8_t i2c::_read_byte(uint8_t reg) + { + uint8_t retval; + + _i2cPort->beginTransmission(_address); + _i2cPort->write(reg); + _i2cPort->endTransmission(); + _i2cPort->requestFrom(_address, (uint8_t)1); + retval = _i2cPort->read(); + + return retval; + } + } // namespace Mod_NTC +} // namespace Microfire \ No newline at end of file diff --git a/src/Microfire_Mod-NTC.h b/src/Microfire_Mod-NTC.h new file mode 100644 index 0000000..34af7e5 --- /dev/null +++ b/src/Microfire_Mod-NTC.h @@ -0,0 +1,80 @@ +#ifndef MOD_NTC_H +#define MOD_NTC_H + +#include +#include + +#define UFIRE_MOD_NTC 0x0c + +#define MEASURE_TEMP_TASK 40 /*!< Command to measure temperature */ +#define BETA_TASK 20 /*!< Command to calibrate the probe */ +#define I2C_TASK 2 /*!< Command to change the i2c address */ + +#define HW_VERSION_REGISTER 0 /*!< hardware version register */ +#define FW_VERSION_REGISTER 1 /*!< firmware version register */ +#define TASK_REGISTER 2 /*!< task register */ +#define STATUS_REGISTER 3 /*!< status register */ +#define BETA_REGISTER 4 /*!< Beta register */ +#define TEMP_C_REGISTER 8 /*!< Temperature in C register */ +#define TEMP_K_REGISTER 12 /*!< Temperature in K register */ +#define TEMP_F_REGISTER 16 /*!< Temperature in F register */ +#define RESISTANCE_REGISTER 20 /*!< resistance in Ohms register */ +#define BUFFER_REGISTER 24 /*!< buffer register */ + +namespace Microfire +{ + namespace Mod_NTC + { + class i2c + { + public: + const float &tempC = _tempC; + const float &tempF = _tempF; + const float &tempK = _tempK; + const float &resistance = _resistance; + const float &beta = _beta; + + const int &hwVersion = _hwVersion; + const int &fwVersion = _fwVersion; + const int &status = _status; + + bool begin(TwoWire &wirePort = Wire, uint8_t address = UFIRE_MOD_NTC); + bool connected(); + float measureTemp(bool blocking = true); + void reset(); + void setI2CAddress(uint8_t i2cAddress); + float setBeta(float beta); + void update(); + + typedef enum Temp_Error_e + { + STATUS_NO_ERROR, + STATUS_NO_PROBE, + STATUS_SYSTEM_ERROR + } Temp_Error_t; + + private: + uint8_t _address; + TwoWire *_i2cPort; + static int _hwVersion; + static int _fwVersion; + static int _status; + + static float _tempC; + static float _tempF; + static float _tempK; + static float _resistance; + static float _beta; + + void _beta_calculation(); + void _SH_calculation(); + void _updateRegisters(); + void _send_command(uint8_t command); + void _write_4_bytes(uint8_t reg, float f); + void _write_byte(uint8_t reg, uint8_t val); + float _read_4_bytes(uint8_t reg); + uint8_t _read_byte(uint8_t reg); + }; + } // namespace Mod_NTC +} // namespace Microfire +#endif // ifndef Mod_NTC_H \ No newline at end of file