From 03c08dea127b840df95acf25ba34263feb37bb03 Mon Sep 17 00:00:00 2001 From: Jared Dunbar Date: Wed, 17 Nov 2021 19:11:36 -0500 Subject: [PATCH] Initial commit with testing from load cell --- .gitignore | 4 + CMakeLists.txt | 33 ++++ lib/README | 46 ++++++ lib/ltc2499/src/LTC2499.cpp | 296 ++++++++++++++++++++++++++++++++++++ lib/ltc2499/src/LTC2499.h | 169 ++++++++++++++++++++ platformio.ini | 14 ++ src/main.cpp | 51 +++++++ 7 files changed, 613 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 lib/README create mode 100644 lib/ltc2499/src/LTC2499.cpp create mode 100644 lib/ltc2499/src/LTC2499.h create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c68ab64 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.pio +CMakeListsPrivate.txt +cmake-build-*/ +.idea/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4c554ed --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +# !!! WARNING !!! AUTO-GENERATED FILE, PLEASE DO NOT MODIFY IT AND USE +# https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags +# +# If you need to override existing CMake configuration or add extra, +# please create `CMakeListsUser.txt` in the root of project. +# The `CMakeListsUser.txt` will not be overwritten by PlatformIO. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_C_COMPILER_WORKS 1) +set(CMAKE_CXX_COMPILER_WORKS 1) + +project("Adc24Bits" C CXX) + +include(CMakeListsPrivate.txt) + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeListsUser.txt) +include(CMakeListsUser.txt) +endif() + +add_custom_target( + Production ALL + COMMAND platformio -c clion run "$<$>:-e${CMAKE_BUILD_TYPE}>" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_custom_target( + Debug ALL + COMMAND platformio -c clion debug "$<$>:-e${CMAKE_BUILD_TYPE}>" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_executable(Z_DUMMY_TARGET ${SRC_LIST} lib/ltc2499/src/LTC2499.cpp lib/ltc2499/src/LTC2499.h) diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/lib/ltc2499/src/LTC2499.cpp b/lib/ltc2499/src/LTC2499.cpp new file mode 100644 index 0000000..6663a6c --- /dev/null +++ b/lib/ltc2499/src/LTC2499.cpp @@ -0,0 +1,296 @@ +/************************************************************************* +Title: LTC2493 / LTC2499 library +Authors: Nathan D. Holmes +File: $Id: $ +License: GNU General Public License v3 + +LICENSE: + Copyright (C) 2013 Nathan D. Holmes & Michael D. Petersen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +************************************************************************* + +This library can be used for: +LTC2493: 2/4 channel 24bit ADC +LTC2499: 8/16 channel 24bit ADC + +This library contains the ADC part of the combined library Ard2499 which contains the libraries for the LTC2499 24bit ADC and the 24AA025E48 EEPROM +See the 16-Channel 24-Bit ADC Data Acquisition Shield for Arduino from Iowa Scaled Engineering: +* https://www.iascaled.com/store/ARD-LTC2499 +* GitHub https://github.com/IowaScaledEngineering/ard-ltc2499 + +Changelog +Version 1.0.0 +*/ + +#include +#include + +#include "LTC2499.h" + +byte Ltc2499::changeChannel(byte channel) +{ + return(changeChannel(channel, true)); +} + +byte Ltc2499::changeChannel(byte channel, bool addStop) +{ + uint8_t config1=0, config2=0, blockingCountdown; + + if (CHAN_TEMPERATURE == channel) + { + // The temperature channel is fake - it really has to change the config byte in the message + currentChannel = CHAN_TEMPERATURE; + config1 = 0x80 | _BV(CONFIG1_ENABLE); + config2 = _BV(CONFIG2_ENABLE2) | _BV(CONFIG2_IM) | (CONFIG2_CONFBITS & currentConfig); + } + else + { + currentChannel = CONFIG1_CHANBITS & channel; + config1 = 0x80 | _BV(CONFIG1_ENABLE) | (0x1F & currentChannel); + config2 = _BV(CONFIG2_ENABLE2) | (CONFIG2_CONFBITS & currentConfig); + } + + // If there's no address, error out + if(0 == i2cAddr_) + return(ERR); + + // Block in 5ms increments for up to 200ms. Given the conversion rate + // should be greater than 7.5sps, 200ms is more than we should ever have to + // wait. + + blockingCountdown = 40; + while(blockingCountdown--) + { + this->wireInterface->beginTransmission(i2cAddr_); + this->wireInterface->write(config1); + this->wireInterface->write(config2); + if (0 != this->wireInterface->endTransmission(addStop)) + { + if (0 == blockingCountdown) + return(ERR); + delay(5); + } + else + break; + } + + return(SUCCESS); +} + +byte Ltc2499::changeConfiguration(byte config) +{ + currentConfig = CONFIG2_CONFBITS & config; + return(changeChannel(currentChannel)); +} + +long Ltc2499::read() +{ + unsigned long rawValue = readRaw(); + + if (RAW_READ_ERROR == rawValue) + return(READ_ERROR); + + uint8_t upperByte = 0xFF & (rawValue>>24); + switch(upperByte) + { + case 0xC0: + return(OVERRANGE_POSITIVE); + case 0x3F: + return(OVERRANGE_NEGATIVE); + } + rawValue = (rawValue & 0x7FFFFFFF)>>6; // Get rid of the sub-LSBs + + if (0x01000000 & rawValue) + rawValue |= 0xFF000000; + return((long)rawValue); + +} + +float Ltc2499::readVoltage() +{ + long value = read(); + + if (OVERRANGE_POSITIVE == value) + return(INFINITY); + if (OVERRANGE_NEGATIVE == value) + return(-1*INFINITY); + + return((value / 16777216.0) * (referenceMillivolts / 2000.0)); +} + +float Ltc2499::readVoltageAndChangeChannel(byte nextChannel) +{ + long value = readAndChangeChannel(nextChannel); + + if (OVERRANGE_POSITIVE == value) + return(INFINITY); + if (OVERRANGE_NEGATIVE == value) + return(-1*INFINITY); + + return((value / 16777216.0) * (referenceMillivolts / 2000.0)); +} + +long Ltc2499::readAndChangeChannel(byte nextChannel) +{ + if (SUCCESS != changeChannel(nextChannel, false)) + { + // Run another transmission through just to send out a stop bit + this->wireInterface->beginTransmission(i2cAddr_); + this->wireInterface->endTransmission((uint8_t)true); + return(READ_ERROR); + } + // Otherwise, we've succeeded and are sitting on a restart condition + return(read()); +} + +unsigned long Ltc2499::readRaw() +{ + unsigned long retval=0; + uint8_t blockingCountdown=40; + + if (0 == i2cAddr_) + return(0); + + // Block in 5ms increments for up to 200ms. Given the conversion rate + // should be greater than 7.5sps, 200ms is more than we should ever have to + // wait. + while(blockingCountdown--) + { + this->wireInterface->requestFrom((uint8_t)i2cAddr_, (uint8_t)4, (uint8_t)true); + // Error occurred, we don't have as many bytes as expected + if (this->wireInterface->available() < 4) + { + if (0 == blockingCountdown) + return(RAW_READ_ERROR); + delay(5); + } + else + break; + } + + retval |= this->wireInterface->read(); + retval <<= 8; + retval |= this->wireInterface->read(); + retval <<= 8; + retval |= this->wireInterface->read(); + retval <<= 8; + retval |= this->wireInterface->read(); + + return(retval); +} + +unsigned long Ltc2499::readRawAndChangeChannel(byte nextChannel) +{ + if (SUCCESS != changeChannel(nextChannel, false)) + { + // Run another transmission through just to send out a stop bit + this->wireInterface->beginTransmission(i2cAddr_); + this->wireInterface->endTransmission((uint8_t)true); + return(RAW_READ_ERROR); + } + // Otherwise, we've succeeded and are sitting on a restart condition + return(readRaw()); +} + +float Ltc2499::readTemperature(byte temperatureUnits) +{ + unsigned int tempDK = readTemperatureDeciK(); + float tempK = (float)tempDK/10.0; + switch(temperatureUnits) + { + case TEMP_K: + return(tempK); + case TEMP_C: + return(tempK - 273.15); + case TEMP_F: + return(((tempK - 273.15) * 9.0) / 5.0 + 32.0); + } + return(0); +} + +unsigned int Ltc2499::readTemperatureDeciK() +{ + unsigned long readVal = 0; + unsigned long tempDK = 0; + + // If we're currently not set for the temperature channel, switch us over + // If we are currently set for the temp channel, then that write will have + // triggered a conversion, and readRaw will block until it's done + if(CHAN_TEMPERATURE != currentChannel) + changeChannel(CHAN_TEMPERATURE, true); + + readVal = readRaw(); + + if (RAW_READ_ERROR == readVal) + return(0); + + // Throw away the sub-LSBs + readVal >>= 6; + + tempDK = (0x00FFFFFF & readVal); + // Divide by 2 for FS = 1/2 VREF + // Divide by 1570 (eqn from 2499 datasheet) + // Divide by 100 to convert millivolts to decivolts + tempDK = (tempDK * (unsigned long)referenceMillivolts) / 314000; + + // tempDK is now the temperature in deci-kelvin + return(tempDK); +} + +Ltc2499::Ltc2499() +{ + init_status = ERR; + // init_status = ERR | EEPROM_ERR; + i2cAddr_ = 0; + //i2cAddr_eeprom = 0; + currentConfig = 0; + currentChannel = 0; + +#ifdef ARDUINO_SAM_DUE + wireInterface = &Wire1; +#else + wireInterface = &Wire; +#endif +} + +Ltc2499::Ltc2499(TwoWire& wire) +{ + Ltc2499(); + this->wireInterface = &wire; +} + +byte Ltc2499::begin(byte Address, uint16_t referenceMillivolts) +{ + byte retval = 0; + byte i; + + init_status = SUCCESS; + + i2cAddr_ = Address; + currentChannel = CHAN_DIFF_0P_1N; + currentConfig = CONFIG2_60_50HZ_REJ; + + this->wireInterface->beginTransmission(i2cAddr_); + this->wireInterface->write(0x80 | _BV(CONFIG1_ENABLE) | (0x1F & currentChannel)); + this->wireInterface->write(_BV(CONFIG2_ENABLE2) | (0x7F & currentConfig)); + retval = this->wireInterface->endTransmission(true); + // Anything but zero means we couldn't initialize the LTC2499 + if (0 != retval) + { + i2cAddr_ = 0; + init_status |= ERR; + } + this->referenceMillivolts = referenceMillivolts; + + return(init_status); +} diff --git a/lib/ltc2499/src/LTC2499.h b/lib/ltc2499/src/LTC2499.h new file mode 100644 index 0000000..925b867 --- /dev/null +++ b/lib/ltc2499/src/LTC2499.h @@ -0,0 +1,169 @@ +/************************************************************************* +Title: LTC2493 / LTC2499 library +Authors: Nathan D. Holmes +File: $Id: $ +License: GNU General Public License v3 + +LICENSE: + Copyright (C) 2013 Nathan D. Holmes & Michael D. Petersen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*************************************************************************/ + +#ifndef LTC2499_H +#define LTC2499_H + +#include +#include +#include "Arduino.h" +#include "Wire.h" + +#ifndef _BV +#define _BV(a) (1<<(a)) +#define _UNDEFINE_BV +#endif + +#define FAKE_CONFIG1_TEMPERATURE 6 +#define CONFIG1_ENABLE 5 +#define CONFIG1_SINGLE_END 4 +#define CONFIG1_ODD 3 +#define CONFIG1_A2 2 +#define CONFIG1_A1 1 +#define CONFIG1_A0 0 + +#define CHAN_DIFF_0P_1N (0) +#define CHAN_DIFF_2P_3N (_BV(CONFIG1_A0)) +#define CHAN_DIFF_4P_5N (_BV(CONFIG1_A1)) +#define CHAN_DIFF_6P_7N (_BV(CONFIG1_A1) | _BV(CONFIG1_A0)) +#define CHAN_DIFF_8P_9N (_BV(CONFIG1_A2)) +#define CHAN_DIFF_10P_11N (_BV(CONFIG1_A2) | _BV(CONFIG1_A0)) +#define CHAN_DIFF_12P_13N (_BV(CONFIG1_A2) | _BV(CONFIG1_A1)) +#define CHAN_DIFF_14P_15N (_BV(CONFIG1_A2) | _BV(CONFIG1_A1) | _BV(CONFIG1_A0)) + +#define CHAN_DIFF_1P_0N (_BV(CONFIG1_ODD)) +#define CHAN_DIFF_3P_2N (_BV(CONFIG1_ODD) | _BV(CONFIG1_A0)) +#define CHAN_DIFF_5P_4N (_BV(CONFIG1_ODD) | _BV(CONFIG1_A1)) +#define CHAN_DIFF_7P_6N (_BV(CONFIG1_ODD) | _BV(CONFIG1_A1) | _BV(CONFIG1_A0)) +#define CHAN_DIFF_9P_8N (_BV(CONFIG1_ODD) | _BV(CONFIG1_A2)) +#define CHAN_DIFF_11P_10N (_BV(CONFIG1_ODD) | _BV(CONFIG1_A2) | _BV(CONFIG1_A0)) +#define CHAN_DIFF_13P_12N (_BV(CONFIG1_ODD) | _BV(CONFIG1_A2) | _BV(CONFIG1_A1)) +#define CHAN_DIFF_15P_14N (_BV(CONFIG1_ODD) | _BV(CONFIG1_A2) | _BV(CONFIG1_A1) | _BV(CONFIG1_A0)) + +#define CHAN_SINGLE_0P (_BV(CONFIG1_SINGLE_END)) +#define CHAN_SINGLE_1P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD)) +#define CHAN_SINGLE_2P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_A0)) +#define CHAN_SINGLE_3P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD) | _BV(CONFIG1_A0)) +#define CHAN_SINGLE_4P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_A1)) +#define CHAN_SINGLE_5P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD) | _BV(CONFIG1_A1)) +#define CHAN_SINGLE_6P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_A1) | _BV(CONFIG1_A0)) +#define CHAN_SINGLE_7P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD) | _BV(CONFIG1_A1) | _BV(CONFIG1_A0)) +#define CHAN_SINGLE_8P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_A2)) +#define CHAN_SINGLE_9P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD) | _BV(CONFIG1_A2)) +#define CHAN_SINGLE_10P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_A2) | _BV(CONFIG1_A0)) +#define CHAN_SINGLE_11P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD) | _BV(CONFIG1_A2) | _BV(CONFIG1_A0)) +#define CHAN_SINGLE_12P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_A2) | _BV(CONFIG1_A1)) +#define CHAN_SINGLE_13P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD) | _BV(CONFIG1_A2) | _BV(CONFIG1_A1)) +#define CHAN_SINGLE_14P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_A2) | _BV(CONFIG1_A1) | _BV(CONFIG1_A0)) +#define CHAN_SINGLE_15P (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD) | _BV(CONFIG1_A2) | _BV(CONFIG1_A1) | _BV(CONFIG1_A0)) + +// The temperature channel is actually fake - we manipulate things internally +#define CHAN_TEMPERATURE (_BV(FAKE_CONFIG1_TEMPERATURE)) + +#define CONFIG2_ENABLE2 7 +#define CONFIG2_IM 6 +#define CONFIG2_FA 5 +#define CONFIG2_FB 4 +#define CONFIG2_SPD 3 + +#define CONFIG2_50HZ_REJ (_BV(CONFIG2_FB)) +#define CONFIG2_60HZ_REJ (_BV(CONFIG2_FA)) +#define CONFIG2_60_50HZ_REJ (0) +#define CONFIG2_SPEED_2X (_BV(CONFIG2_SPD)) + +#define CONFIG1_CHANBITS (_BV(CONFIG1_SINGLE_END) | _BV(CONFIG1_ODD) | _BV(CONFIG1_A2) | _BV(CONFIG1_A1) | _BV(CONFIG1_A0)) +#define CONFIG2_CONFBITS (_BV(CONFIG2_FA) | _BV(CONFIG2_FB) | _BV(CONFIG2_SPD)) + +#define SUCCESS 0 +#define ERR 1 + +#define ADDR_000 0x14 +#define ADDR_00Z 0x15 +#define ADDR_001 0x16 +#define ADDR_0Z0 0x17 +#define ADDR_0ZZ 0x24 +#define ADDR_0Z1 0x25 +#define ADDR_010 0x26 +#define ADDR_01Z 0x27 +#define ADDR_011 0x28 +#define ADDR_Z00 0x35 +#define ADDR_Z01 0x37 +#define ADDR_Z0Z 0x36 +#define ADDR_ZZ0 0x44 +#define ADDR_ZZZ 0x45 +#define ADDR_ZZ1 0x46 +#define ADDR_Z10 0x47 +#define ADDR_Z1Z 0x54 +#define ADDR_Z11 0x55 +#define ADDR_100 0x56 +#define ADDR_10Z 0x57 +#define ADDR_101 0x64 +#define ADDR_1Z0 0x65 +#define ADDR_1ZZ 0x66 +#define ADDR_1Z1 0x67 +#define ADDR_110 0x74 +#define ADDR_111 0x76 +#define ADDR_11Z 0x75 + +#define TEMP_DEG_F 0x01 + +#define TEMP_K 0x00 +#define TEMP_F 0x01 +#define TEMP_C 0x02 + +#define RAW_READ_ERROR 0xFFFFFFFF + +#define READ_ERROR 0x01000001 +#define OVERRANGE_POSITIVE 0x01000000 +#define OVERRANGE_NEGATIVE 0x11000000 + +class Ltc2499 +{ +public: + Ltc2499(); + Ltc2499(TwoWire& wire); + + byte begin(byte Address, uint16_t referenceMillivolts = 4096); + + long read(); // 24-bit plus sign + long readAndChangeChannel(byte nextChannel); // 24-bit plus sign + float readVoltage(); + float readVoltageAndChangeChannel(byte nextChannel); + + unsigned long readRaw(); + unsigned long readRawAndChangeChannel(byte nextChannel); + + byte changeChannel(byte channel); + byte changeConfiguration(byte config); + unsigned int readTemperatureDeciK(); + float readTemperature(byte temperatureUnits); + +private: + byte changeChannel(byte channel, bool addStop); + uint8_t init_status; + uint8_t i2cAddr_; + uint8_t currentConfig; + uint8_t currentChannel; + uint16_t referenceMillivolts; + TwoWire* wireInterface; +}; + +#endif diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..398fec3 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:leonardo] +platform = atmelavr +board = leonardo +framework = arduino diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..187bb79 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,51 @@ +#include "Arduino.h" +#include "../lib/ltc2499/src/LTC2499.h" + +Ltc2499 ltc2499; + +void setup() { + Serial.begin(115200); + Wire.begin(); + byte status = ltc2499.begin(ADDR_111); + if (status) { + Serial.print("Error LTC2493"); Serial.println(status); + while(true); + } + ltc2499.changeConfiguration(CONFIG2_60_50HZ_REJ); + ltc2499.changeChannel(CHAN_DIFF_0P_1N); + delay(80); +} + +void loop() { + /* + * Connectivity: + * + * I've got the LTC2499 configured with a 5.0v reference on the testing board we have. + * The e+ wire (red) on the load cell is connected to the 3.332v regulator on the Arduino Leonardo + * The e- wire (black) on the load cell is connected to ground on the ADC + * The s+/- wires are connected to channel 0/1 on the ADC + * + * Some preliminary testing showed that a 42g Lego RCX motor (the heavy style) came in at 1442 on the ADC. + * It seemed accurate to the gram but did drift a bit over time or with load applied then removed. + * The more weight applied, the more it drifted, typically. + */ + + long adc1 = ltc2499.read(); + long adc2 = ltc2499.read(); + long adc3 = ltc2499.read(); + long adc4 = ltc2499.read(); + long adc5 = ltc2499.read(); + long adc = long(double(adc1 + adc2 + adc3 + adc4 + adc5)/5.0); + + Serial.print(adc); Serial.print(", "); + + // This is an approximate bits of ADC precision per single gram. + double bitsPerGram = 34.35; + + // This is a very rough estimate of the correct value to "zero" the scale to 0.0g + // I found it to be accurate to around a gram if you measured quickly, but it did drift based on time and + // the weight applied. + long zero = 596140; + // I've got a negative here but that might be because I connected the wires backwards + Serial.print(-double(adc - zero)/bitsPerGram); Serial.println(""); +} \ No newline at end of file