mirror of
https://github.com/esphome/esphome.git
synced 2025-10-09 13:23:47 +01:00
feat: add FRAM support to DynamicLampComponent for enhanced functionality
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
from esphome.components.fram import FRAM
|
||||
from esphome.components.time import RealTimeClock
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
@@ -12,11 +13,13 @@ DynamicLampComponent = dynamic_lamp_ns.class_('DynamicLampComponent', cg.Compone
|
||||
CONF_DYNAMIC_LAMP_ID = "dynamic_lamp_id"
|
||||
|
||||
CONF_RTC = 'rtc'
|
||||
CONF_FRAM = 'fram'
|
||||
CONF_SAVE_MODE = 'save_mode'
|
||||
CONF_AVAILABLE_OUTPUTS = 'available_outputs'
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(DynamicLampComponent),
|
||||
cv.Required(CONF_RTC): cv.use_id(RealTimeClock),
|
||||
cv.Required(CONF_FRAM): cv.use_id(FRAM),
|
||||
cv.Required(CONF_AVAILABLE_OUTPUTS): [cv.use_id(output.FloatOutput)],
|
||||
cv.Optional(CONF_SAVE_MODE, default=0): cv.int_range(0, 1),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
@@ -26,6 +29,7 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
await cg.get_variable(config[CONF_RTC]),
|
||||
await cg.get_variable(config[CONF_FRAM]),
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
for outputPointer in config.get(CONF_AVAILABLE_OUTPUTS, []):
|
||||
|
@@ -109,6 +109,7 @@ class DynamicLampComponent : public Component {
|
||||
protected:
|
||||
friend class DynamicLamp;
|
||||
time::RealTimeClock *rtc_;
|
||||
FRAM *fram_;
|
||||
void restore_lamp_values_(uint8_t lamp_number);
|
||||
void set_lamp_values_(uint8_t lamp_number, bool active, uint16_t selected_outputs, uint8_t mode, uint8_t mode_value);
|
||||
bool write_state_(uint8_t lamp_number, float state);
|
||||
@@ -117,7 +118,6 @@ class DynamicLampComponent : public Component {
|
||||
LinkedOutput available_outputs_[16];
|
||||
uint8_t save_mode_;
|
||||
uint8_t lamp_count_ = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
629
esphome/components/fram/FRAM.cpp
Normal file
629
esphome/components/fram/FRAM.cpp
Normal file
@@ -0,0 +1,629 @@
|
||||
//
|
||||
// FILE: FRAM.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.5.3
|
||||
// DATE: 2018-01-24
|
||||
// PURPOSE: Arduino library for I2C FRAM
|
||||
// URL: https://github.com/RobTillaart/FRAM_I2C
|
||||
//
|
||||
// ESPHome port: https://github.com/sharkydog/esphome-fram
|
||||
|
||||
#include "FRAM.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace fram {
|
||||
|
||||
// used for metadata and sleep
|
||||
const uint8_t FRAM_SLAVE_ID_ = 0x7C; // == 0xF8
|
||||
const uint8_t FRAM_SLEEP_CMD = 0x86; //
|
||||
static const char * const TAG = "fram";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM PUBLIC
|
||||
//
|
||||
|
||||
void FRAM::setup()
|
||||
{
|
||||
if (!this->isConnected())
|
||||
{
|
||||
ESP_LOGE(TAG, "Device on address 0x%x not found!", this->address_);
|
||||
this->mark_failed();
|
||||
}
|
||||
else if (!this->getSize())
|
||||
{
|
||||
ESP_LOGW(TAG, "Device on address 0x%x returned 0 size, set size in config!", this->address_);
|
||||
}
|
||||
}
|
||||
|
||||
void FRAM::dump_config()
|
||||
{
|
||||
ESP_LOGCONFIG(TAG, "FRAM:");
|
||||
ESP_LOGCONFIG(TAG, " Address: 0x%x", this->address_);
|
||||
|
||||
bool ok = this->isConnected();
|
||||
|
||||
if (!ok) {
|
||||
ESP_LOGE(TAG, " Device not found!");
|
||||
}
|
||||
|
||||
if (this->_sizeBytes) {
|
||||
ESP_LOGCONFIG(TAG, " Size: %uKiB", this->_sizeBytes / 1024UL);
|
||||
} else if(ok) {
|
||||
ESP_LOGW(TAG, " Size: 0KiB, set size in config!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool FRAM::isConnected()
|
||||
{
|
||||
i2c::ErrorCode err = this->bus_->write(this->address_, nullptr, 0, true);
|
||||
return (err == i2c::ERROR_OK);
|
||||
}
|
||||
|
||||
|
||||
void FRAM::write8(uint16_t memaddr, uint8_t value)
|
||||
{
|
||||
uint8_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::write16(uint16_t memaddr, uint16_t value)
|
||||
{
|
||||
uint16_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::write32(uint16_t memaddr, uint32_t value)
|
||||
{
|
||||
uint32_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::writeFloat(uint16_t memaddr, float value)
|
||||
{
|
||||
float val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(float));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::writeDouble(uint16_t memaddr, double value)
|
||||
{
|
||||
double val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(double));
|
||||
}
|
||||
|
||||
|
||||
void FRAM::write(uint16_t memaddr, uint8_t * obj, uint16_t size)
|
||||
{
|
||||
const int blocksize = 24;
|
||||
uint8_t * p = obj;
|
||||
while (size >= blocksize)
|
||||
{
|
||||
this->_writeBlock(memaddr, p, blocksize);
|
||||
memaddr += blocksize;
|
||||
p += blocksize;
|
||||
size -= blocksize;
|
||||
}
|
||||
// remaining
|
||||
if (size > 0)
|
||||
{
|
||||
this->_writeBlock(memaddr, p, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t FRAM::read8(uint16_t memaddr)
|
||||
{
|
||||
uint8_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint8_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
uint16_t FRAM::read16(uint16_t memaddr)
|
||||
{
|
||||
uint16_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint16_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
uint32_t FRAM::read32(uint16_t memaddr)
|
||||
{
|
||||
uint32_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint32_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
float FRAM::readFloat(uint16_t memaddr)
|
||||
{
|
||||
float val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(float));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
double FRAM::readDouble(uint16_t memaddr)
|
||||
{
|
||||
double val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(double));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void FRAM::read(uint16_t memaddr, uint8_t * obj, uint16_t size)
|
||||
{
|
||||
const uint8_t blocksize = 24;
|
||||
uint8_t * p = obj;
|
||||
while (size >= blocksize)
|
||||
{
|
||||
this->_readBlock(memaddr, p, blocksize);
|
||||
memaddr += blocksize;
|
||||
p += blocksize;
|
||||
size -= blocksize;
|
||||
}
|
||||
// remainder
|
||||
if (size > 0)
|
||||
{
|
||||
this->_readBlock(memaddr, p, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int32_t FRAM::readUntil(uint16_t memaddr, char * buf, uint16_t buflen, char separator)
|
||||
{
|
||||
// read and fill the buffer at once.
|
||||
this->read(memaddr, (uint8_t *)buf, buflen);
|
||||
for (uint16_t length = 0; length < buflen; length++)
|
||||
{
|
||||
if (buf[length] == separator)
|
||||
{
|
||||
buf[length] = 0; // replace separator => \0 EndChar
|
||||
return length;
|
||||
}
|
||||
}
|
||||
// entry does not fit in given buffer.
|
||||
return (int32_t)-1;
|
||||
}
|
||||
|
||||
|
||||
int32_t FRAM::readLine(uint16_t memaddr, char * buf, uint16_t buflen)
|
||||
{
|
||||
// read and fill the buffer at once.
|
||||
this->read(memaddr, (uint8_t *)buf, buflen);
|
||||
for (uint16_t length = 0; length < buflen-1; length++)
|
||||
{
|
||||
if (buf[length] == '\n')
|
||||
{
|
||||
buf[length + 1] = 0; // add \0 EndChar after '\n'
|
||||
return length + 1;
|
||||
}
|
||||
}
|
||||
// entry does not fit in given buffer.
|
||||
return (int32_t)-1;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
uint16_t FRAM::getManufacturerID()
|
||||
{
|
||||
return this->_getMetaData(0);
|
||||
}
|
||||
|
||||
|
||||
uint16_t FRAM::getProductID()
|
||||
{
|
||||
return this->_getMetaData(1);
|
||||
}
|
||||
|
||||
|
||||
// NOTE: returns the size in kiloBYTE
|
||||
uint16_t FRAM::getSize()
|
||||
{
|
||||
uint16_t density = this->_getMetaData(2);
|
||||
uint16_t size = 0;
|
||||
if (density > 0)
|
||||
{
|
||||
size = (1UL << density);
|
||||
this->_sizeBytes = size * 1024UL;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
uint32_t FRAM::getSizeBytes()
|
||||
{
|
||||
return this->_sizeBytes;
|
||||
}
|
||||
|
||||
|
||||
// override to be used when getSize() fails == 0
|
||||
void FRAM::setSizeBytes(uint32_t value)
|
||||
{
|
||||
this->_sizeBytes = value;
|
||||
}
|
||||
|
||||
|
||||
uint32_t FRAM::clear(uint8_t value)
|
||||
{
|
||||
uint8_t buffer[16];
|
||||
for (uint8_t i = 0; i < 16; i++) buffer[i] = value;
|
||||
uint32_t start = 0;
|
||||
uint32_t end = this->_sizeBytes;
|
||||
for (uint32_t address = start; address < end; address += 16)
|
||||
{
|
||||
this->_writeBlock(address, buffer, 16);
|
||||
}
|
||||
return end - start;
|
||||
}
|
||||
|
||||
|
||||
// EXPERIMENTAL - to be confirmed
|
||||
// page 12 datasheet
|
||||
// command = S 0xF8 A address A S 86 A P (A = Ack from slave )
|
||||
void FRAM::sleep()
|
||||
{
|
||||
uint8_t addr = this->address_ << 1;
|
||||
this->bus_->write(FRAM_SLAVE_ID_, &addr, 1, false);
|
||||
this->bus_->write(FRAM_SLEEP_CMD >> 1, nullptr, 0, true);
|
||||
}
|
||||
|
||||
|
||||
// page 12 datasheet trec <= 400us
|
||||
bool FRAM::wakeup(uint32_t trec)
|
||||
{
|
||||
bool b = this->isConnected(); // wakeup
|
||||
if (trec == 0) return b;
|
||||
// wait recovery time
|
||||
delayMicroseconds(trec);
|
||||
return this->isConnected(); // check recovery OK
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM PROTECTED
|
||||
//
|
||||
|
||||
// metadata is packed as [....MMMM][MMMMDDDD][PPPPPPPP]
|
||||
// M = manufacturerID
|
||||
// D = density => memory size = 2^D KB
|
||||
// P = product ID (together with D)
|
||||
// P part might be proprietary
|
||||
uint16_t FRAM::_getMetaData(uint8_t field)
|
||||
{
|
||||
if (field > 2) return 0;
|
||||
|
||||
uint8_t addr = this->address_ << 1;
|
||||
this->bus_->write(FRAM_SLAVE_ID_, &addr, 1, false);
|
||||
|
||||
uint8_t data[3] = {0,0,0};
|
||||
i2c::ErrorCode err = this->bus_->read(FRAM_SLAVE_ID_, data, 3);
|
||||
if (err != i2c::ERROR_OK) return 0;
|
||||
|
||||
// MANUFACTURER
|
||||
if (field == 0) return (data[0] << 4) + (data[1] >> 4);
|
||||
// PRODUCT ID
|
||||
if (field == 1) return ((data[1] & 0x0F) << 8) + data[2];
|
||||
// DENSITY
|
||||
// Fujitsu data sheet
|
||||
// 3 => MB85RC64 = 64 Kbit.
|
||||
// 5 => MB85RC256
|
||||
// 6 => MB85RC512
|
||||
// 7 => MB85RC1M
|
||||
if (field == 2) return data[1] & 0x0F;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void FRAM::_writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
i2c::WriteBuffer buff[2];
|
||||
uint8_t maddr[] = { (uint8_t)(memaddr >> 8), (uint8_t)(memaddr & 0xFF) };
|
||||
|
||||
buff[0].data = maddr;
|
||||
buff[0].len = 2;
|
||||
buff[1].data = obj;
|
||||
buff[1].len = size;
|
||||
|
||||
this->bus_->writev(this->address_, buff, 2, true);
|
||||
}
|
||||
|
||||
|
||||
void FRAM::_readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
uint8_t maddr[] = { (uint8_t)(memaddr >> 8), (uint8_t)(memaddr & 0xFF) };
|
||||
this->bus_->write(this->address_, maddr, 2, false);
|
||||
this->bus_->read(this->address_, obj, size);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM32 PUBLIC
|
||||
//
|
||||
|
||||
void FRAM32::write8(uint32_t memaddr, uint8_t value)
|
||||
{
|
||||
uint8_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::write16(uint32_t memaddr, uint16_t value)
|
||||
{
|
||||
uint16_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::write32(uint32_t memaddr, uint32_t value)
|
||||
{
|
||||
uint32_t val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::writeFloat(uint32_t memaddr, float value)
|
||||
{
|
||||
float val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(float));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::writeDouble(uint32_t memaddr, double value)
|
||||
{
|
||||
double val = value;
|
||||
this->_writeBlock(memaddr, (uint8_t *)&val, sizeof(double));
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::write(uint32_t memaddr, uint8_t * obj, uint16_t size)
|
||||
{
|
||||
const int blocksize = 24;
|
||||
uint8_t * p = obj;
|
||||
while (size >= blocksize)
|
||||
{
|
||||
this->_writeBlock(memaddr, p, blocksize);
|
||||
memaddr += blocksize;
|
||||
p += blocksize;
|
||||
size -= blocksize;
|
||||
}
|
||||
// remaining
|
||||
if (size > 0)
|
||||
{
|
||||
this->_writeBlock(memaddr, p, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t FRAM32::read8(uint32_t memaddr)
|
||||
{
|
||||
uint8_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint8_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
uint16_t FRAM32::read16(uint32_t memaddr)
|
||||
{
|
||||
uint16_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint16_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
uint32_t FRAM32::read32(uint32_t memaddr)
|
||||
{
|
||||
uint32_t val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(uint32_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
float FRAM32::readFloat(uint32_t memaddr)
|
||||
{
|
||||
float val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(float));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
double FRAM32::readDouble(uint32_t memaddr)
|
||||
{
|
||||
double val;
|
||||
this->_readBlock(memaddr, (uint8_t *)&val, sizeof(double));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::read(uint32_t memaddr, uint8_t * obj, uint16_t size)
|
||||
{
|
||||
const uint8_t blocksize = 24;
|
||||
uint8_t * p = obj;
|
||||
while (size >= blocksize)
|
||||
{
|
||||
this->_readBlock(memaddr, p, blocksize);
|
||||
memaddr += blocksize;
|
||||
p += blocksize;
|
||||
size -= blocksize;
|
||||
}
|
||||
// remainder
|
||||
if (size > 0)
|
||||
{
|
||||
this->_readBlock(memaddr, p, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t FRAM32::readUntil(uint32_t memaddr, char * buf, uint16_t buflen, char separator)
|
||||
{
|
||||
// read and fill the buffer at once.
|
||||
this->read(memaddr, (uint8_t *)buf, buflen);
|
||||
for (uint16_t length = 0; length < buflen; length++)
|
||||
{
|
||||
if (buf[length] == separator)
|
||||
{
|
||||
buf[length] = 0; // replace separator => \0 EndChar
|
||||
return length;
|
||||
}
|
||||
}
|
||||
// entry does not fit in given buffer.
|
||||
return (int32_t)-1;
|
||||
}
|
||||
|
||||
|
||||
int32_t FRAM32::readLine(uint32_t memaddr, char * buf, uint16_t buflen)
|
||||
{
|
||||
// read and fill the buffer at once.
|
||||
this->read(memaddr, (uint8_t *)buf, buflen);
|
||||
for (uint16_t length = 0; length < buflen-1; length++)
|
||||
{
|
||||
if (buf[length] == '\n')
|
||||
{
|
||||
buf[length + 1] = 0; // add \0 EndChar after '\n'
|
||||
return length + 1;
|
||||
}
|
||||
}
|
||||
// entry does not fit in given buffer.
|
||||
return (int32_t)-1;
|
||||
}
|
||||
|
||||
|
||||
template <class T> uint32_t FRAM32::writeObject(uint32_t memaddr, T &obj)
|
||||
{
|
||||
this->write(memaddr, (uint8_t *) &obj, sizeof(obj));
|
||||
return memaddr + sizeof(obj);
|
||||
};
|
||||
|
||||
|
||||
template <class T> uint32_t FRAM32::readObject(uint32_t memaddr, T &obj)
|
||||
{
|
||||
this->read(memaddr, (uint8_t *) &obj, sizeof(obj));
|
||||
return memaddr + sizeof(obj);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM32 PROTECTED
|
||||
//
|
||||
|
||||
void FRAM32::_writeBlock(uint32_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
uint8_t _addr = this->address_;
|
||||
if (memaddr & 0x00010000) _addr += 0x01;
|
||||
|
||||
i2c::WriteBuffer buff[2];
|
||||
uint8_t maddr[] = { (uint8_t)(memaddr >> 8), (uint8_t)(memaddr & 0xFF) };
|
||||
|
||||
buff[0].data = maddr;
|
||||
buff[0].len = 2;
|
||||
buff[1].data = obj;
|
||||
buff[1].len = size;
|
||||
|
||||
this->bus_->writev(_addr, buff, 2, true);
|
||||
}
|
||||
|
||||
|
||||
void FRAM32::_readBlock(uint32_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
uint8_t _addr = this->address_;
|
||||
if (memaddr & 0x00010000) _addr += 0x01;
|
||||
|
||||
uint8_t maddr[] = { (uint8_t)(memaddr >> 8), (uint8_t)(memaddr & 0xFF) };
|
||||
this->bus_->write(this->address_, maddr, 2, false);
|
||||
this->bus_->read(_addr, obj, size);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM11
|
||||
//
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM11 PROTECTED
|
||||
//
|
||||
|
||||
void FRAM11::_writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
// Device uses Address Pages
|
||||
uint8_t DeviceAddrWithPageBits = this->address_ | ((memaddr & 0x0700) >> 8);
|
||||
|
||||
i2c::WriteBuffer buff[2];
|
||||
uint8_t maddr = memaddr & 0xFF;
|
||||
|
||||
buff[0].data = &maddr;
|
||||
buff[0].len = 1;
|
||||
buff[1].data = obj;
|
||||
buff[1].len = size;
|
||||
|
||||
this->bus_->writev(DeviceAddrWithPageBits, buff, 2, true);
|
||||
}
|
||||
|
||||
|
||||
void FRAM11::_readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
// Device uses Address Pages
|
||||
uint8_t DeviceAddrWithPageBits = this->address_ | ((memaddr & 0x0700) >> 8);
|
||||
uint8_t maddr = memaddr & 0xFF;
|
||||
|
||||
this->bus_->write(DeviceAddrWithPageBits, &maddr, 1, false);
|
||||
this->bus_->read(DeviceAddrWithPageBits, obj, size);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM9
|
||||
//
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM9 PROTECTED
|
||||
//
|
||||
|
||||
void FRAM9::_writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
// Device uses Address Pages
|
||||
uint8_t DeviceAddrWithPageBits = this->address_ | ((memaddr & 0x0100) >> 8);
|
||||
|
||||
i2c::WriteBuffer buff[2];
|
||||
uint8_t maddr = memaddr & 0xFF;
|
||||
|
||||
buff[0].data = &maddr;
|
||||
buff[0].len = 1;
|
||||
buff[1].data = obj;
|
||||
buff[1].len = size;
|
||||
|
||||
this->bus_->writev(DeviceAddrWithPageBits, buff, 2, true);
|
||||
}
|
||||
|
||||
|
||||
void FRAM9::_readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
|
||||
{
|
||||
// Device uses Address Pages
|
||||
uint8_t DeviceAddrWithPageBits = this->address_ | ((memaddr & 0x0100) >> 8);
|
||||
uint8_t maddr = memaddr & 0xFF;
|
||||
|
||||
this->bus_->write(DeviceAddrWithPageBits, &maddr, 1, false);
|
||||
this->bus_->read(DeviceAddrWithPageBits, obj, size);
|
||||
}
|
||||
|
||||
|
||||
} // namespace fram
|
||||
} // namespace esphome
|
||||
|
||||
// -- END OF FILE --
|
164
esphome/components/fram/FRAM.h
Normal file
164
esphome/components/fram/FRAM.h
Normal file
@@ -0,0 +1,164 @@
|
||||
#pragma once
|
||||
//
|
||||
// FILE: FRAM.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.5.3
|
||||
// DATE: 2018-01-24
|
||||
// PURPOSE: Arduino library for I2C FRAM
|
||||
// URL: https://github.com/RobTillaart/FRAM_I2C
|
||||
//
|
||||
// ESPHome port: https://github.com/sharkydog/esphome-fram
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace fram {
|
||||
|
||||
class FRAM : public Component, public i2c::I2CDevice
|
||||
{
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
bool isConnected();
|
||||
|
||||
void write8(uint16_t memaddr, uint8_t value);
|
||||
void write16(uint16_t memaddr, uint16_t value);
|
||||
void write32(uint16_t memaddr, uint32_t value);
|
||||
void writeFloat(uint16_t memaddr, float value);
|
||||
void writeDouble(uint16_t memaddr, double value);
|
||||
void write(uint16_t memaddr, uint8_t * obj, uint16_t size);
|
||||
|
||||
uint8_t read8(uint16_t memaddr);
|
||||
uint16_t read16(uint16_t memaddr);
|
||||
uint32_t read32(uint16_t memaddr);
|
||||
float readFloat(uint16_t memaddr);
|
||||
double readDouble(uint16_t memaddr);
|
||||
void read(uint16_t memaddr, uint8_t * obj, uint16_t size);
|
||||
|
||||
// Experimental 0.5.1
|
||||
// readUntil returns length 0.. n of the buffer.
|
||||
// readUntil does NOT include the separator character.
|
||||
// readUntil returns -1 if data does not fit into buffer,
|
||||
// => separator not encountered.
|
||||
int32_t readUntil(uint16_t memaddr, char * buf, uint16_t buflen, char separator);
|
||||
// readLine returns length 0.. n of the buffer.
|
||||
// readLine does include '\n' as end character.
|
||||
// readLine returns -1 if data does not fit into buffer.
|
||||
// buffer needs one place for end char '\0'.
|
||||
int32_t readLine(uint16_t memaddr, char * buf, uint16_t buflen);
|
||||
|
||||
template <class T> uint16_t writeObject(uint16_t memaddr, T &obj)
|
||||
{
|
||||
this->write(memaddr, (uint8_t *) &obj, sizeof(obj));
|
||||
return memaddr + sizeof(obj);
|
||||
};
|
||||
template <class T> uint16_t readObject(uint16_t memaddr, T &obj)
|
||||
{
|
||||
this->read(memaddr, (uint8_t *) &obj, sizeof(obj));
|
||||
return memaddr + sizeof(obj);
|
||||
}
|
||||
|
||||
// meta info
|
||||
// Fujitsu = 0x000A, Ramtron = 0x004
|
||||
uint16_t getManufacturerID();
|
||||
// Proprietary
|
||||
uint16_t getProductID();
|
||||
// Returns size in KILO-BYTE (or 0)
|
||||
// Verify for all manufacturers.
|
||||
uint16_t getSize();
|
||||
// Returns size in BYTE
|
||||
uint32_t getSizeBytes();
|
||||
// override when getSize() fails == 0 (see readme.md)
|
||||
void setSizeBytes(uint32_t value);
|
||||
|
||||
// fills FRAM with value, default 0.
|
||||
uint32_t clear(uint8_t value = 0);
|
||||
|
||||
// 0.3.6
|
||||
void sleep();
|
||||
// trec <= 400us P12
|
||||
bool wakeup(uint32_t trec = 400);
|
||||
|
||||
|
||||
protected:
|
||||
uint32_t _sizeBytes{0};
|
||||
|
||||
uint16_t _getMetaData(uint8_t id);
|
||||
|
||||
// virtual so derived classes FRAM9/11 use their implementation.
|
||||
virtual void _writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
virtual void _readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM32
|
||||
//
|
||||
|
||||
class FRAM32 : public FRAM
|
||||
{
|
||||
public:
|
||||
void write8(uint32_t memaddr, uint8_t value);
|
||||
void write16(uint32_t memaddr, uint16_t value);
|
||||
void write32(uint32_t memaddr, uint32_t value);
|
||||
void writeFloat(uint32_t memaddr, float value);
|
||||
void writeDouble(uint32_t memaddr, double value);
|
||||
void write(uint32_t memaddr, uint8_t * obj, uint16_t size);
|
||||
|
||||
uint8_t read8(uint32_t memaddr);
|
||||
uint16_t read16(uint32_t memaddr);
|
||||
uint32_t read32(uint32_t memaddr);
|
||||
float readFloat(uint32_t memaddr);
|
||||
double readDouble(uint32_t memaddr);
|
||||
void read(uint32_t memaddr, uint8_t * obj, uint16_t size);
|
||||
|
||||
// readUntil returns length 0.. n of the buffer.
|
||||
// readUntil returns -1 if data does not fit into buffer,
|
||||
// => separator not encountered.
|
||||
int32_t readUntil(uint32_t memaddr, char * buf, uint16_t buflen, char separator);
|
||||
// buffer needs one place for end char '\0'.
|
||||
int32_t readLine(uint32_t memaddr, char * buf, uint16_t buflen);
|
||||
|
||||
template <class T> uint32_t writeObject(uint32_t memaddr, T &obj);
|
||||
template <class T> uint32_t readObject(uint32_t memaddr, T &obj);
|
||||
|
||||
protected:
|
||||
void _writeBlock(uint32_t memaddr, uint8_t * obj, uint8_t size);
|
||||
void _readBlock(uint32_t memaddr, uint8_t * obj, uint8_t size);
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM11 for FRAM that use 11 bits addresses - e.g. MB85RC16
|
||||
//
|
||||
|
||||
class FRAM11 : public FRAM
|
||||
{
|
||||
protected:
|
||||
void _writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
void _readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FRAM9 for FRAM that use 9 bits addresses - e.g. MB85RC04
|
||||
//
|
||||
class FRAM9 : public FRAM
|
||||
{
|
||||
protected:
|
||||
void _writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
void _readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
|
||||
};
|
||||
|
||||
} // namespace fram
|
||||
} // namespace esphome
|
||||
|
||||
// -- END OF FILE --
|
64
esphome/components/fram/__init__.py
Normal file
64
esphome/components/fram/__init__.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
import re
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ID, CONF_TYPE, CONF_SIZE
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
fram_ns = cg.esphome_ns.namespace("fram")
|
||||
FRAMComponent = fram_ns.class_("FRAM", cg.Component, i2c.I2CDevice)
|
||||
FRAM9Component = fram_ns.class_("FRAM9", FRAMComponent)
|
||||
FRAM11Component = fram_ns.class_("FRAM11", FRAMComponent)
|
||||
FRAM32Component = fram_ns.class_("FRAM32", FRAMComponent)
|
||||
|
||||
def validate_bytes_1024(value):
|
||||
value = cv.string(value).lower()
|
||||
match = re.match(r"^([0-9]+)\s*(\w*)$", value)
|
||||
|
||||
if match is None:
|
||||
raise cv.Invalid(f"Expected number of bytes with unit, got {value}")
|
||||
|
||||
SUFF_LIST = ["B","KB","KiB","MB","MiB"]
|
||||
SUFF = {
|
||||
"": 1,
|
||||
"b": 1,
|
||||
"kb": 1000,
|
||||
"kib": 1024,
|
||||
"mb": pow(1000,2),
|
||||
"mib": pow(1024,2)
|
||||
}
|
||||
|
||||
if match.group(2) not in SUFF:
|
||||
raise cv.Invalid(f"Invalid metric suffix {match.group(2)}, valid: {SUFF_LIST}")
|
||||
|
||||
return int(int(match.group(1)) * SUFF[match.group(2)])
|
||||
|
||||
|
||||
FRAM_SCHEMA = cv.Schema({
|
||||
cv.Optional(CONF_SIZE): validate_bytes_1024
|
||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x50))
|
||||
|
||||
CONFIG_SCHEMA = cv.typed_schema({
|
||||
"FRAM": FRAM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(FRAMComponent)
|
||||
}),
|
||||
"FRAM9": FRAM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(FRAM9Component)
|
||||
}),
|
||||
"FRAM11": FRAM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(FRAM11Component)
|
||||
}),
|
||||
"FRAM32": FRAM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(FRAM32Component)
|
||||
})
|
||||
}, key=CONF_TYPE, default_type="FRAM", upper=True)
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_SIZE in config:
|
||||
cg.add(var.setSizeBytes(config[CONF_SIZE]))
|
Reference in New Issue
Block a user