Initial commit with testing from load cell

This commit is contained in:
Jared Dunbar 2021-11-17 19:11:36 -05:00
commit 03c08dea12
7 changed files with 613 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.pio
CMakeListsPrivate.txt
cmake-build-*/
.idea/

33
CMakeLists.txt Normal file
View File

@ -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 "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
Debug ALL
COMMAND platformio -c clion debug "$<$<NOT:$<CONFIG:All>>:-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)

46
lib/README Normal file
View File

@ -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 <Foo.h>
#include <Bar.h>
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

296
lib/ltc2499/src/LTC2499.cpp Normal file
View File

@ -0,0 +1,296 @@
/*************************************************************************
Title: LTC2493 / LTC2499 library
Authors: Nathan D. Holmes <maverick@drgw.net>
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 <stdlib.h>
#include <string.h>
#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);
}

169
lib/ltc2499/src/LTC2499.h Normal file
View File

@ -0,0 +1,169 @@
/*************************************************************************
Title: LTC2493 / LTC2499 library
Authors: Nathan D. Holmes <maverick@drgw.net>
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 <stdlib.h>
#include <string.h>
#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

14
platformio.ini Normal file
View File

@ -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

51
src/main.cpp Normal file
View File

@ -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("");
}