-
Notifications
You must be signed in to change notification settings - Fork 1
battery monitor tx message #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 3 commits
98e5101
ff4607c
ade072d
0dfd2d0
239458f
bc81fca
835eb8e
b1b6906
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| 'steer', | ||
| 'sup', | ||
| 'throttle', | ||
| 'batt', | ||
| ] | ||
| ] | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # ruff: noqa: F821 | ||
|
|
||
| Import('env') | ||
| Import('envs') | ||
|
|
||
|
|
||
| firmware, flash = env.SConscript( | ||
| 'src/SConscript.py', | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is the |
||
| exports={'env': envs['esp32s3']}, | ||
| ) | ||
|
|
||
| component, name = env.Component(firmware, env.File('component.toml')) | ||
| env.ComponentSubtarget(name, 'flash', flash) | ||
|
|
||
|
|
||
| Return('component') | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [metadata] | ||
| name = 'batt' | ||
| description = 'battery-monitor' |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,39 @@ | ||||||
| # ruff: noqa: F821 | ||||||
|
|
||||||
| Import('env') | ||||||
|
|
||||||
| node = 'BATT' | ||||||
|
|
||||||
| opencan = env.OpenCan( | ||||||
| network=env['CAN']['NETWORK'], | ||||||
| node=node, | ||||||
| ) | ||||||
|
|
||||||
| source = [ | ||||||
| env.StaticObject( | ||||||
| src, | ||||||
| CPPDEFINES=[ | ||||||
| ('EMBER_NODE_IDENTITY', node), | ||||||
| '$CPPDEFINES', | ||||||
| ], | ||||||
| CPPPATH=[ | ||||||
| env.Dir(opencan[0].dir.name), | ||||||
| '$CPPPATH', | ||||||
| ], | ||||||
| ) | ||||||
| for src in [ | ||||||
| 'batt.c', # Ensure this file exists in your source directory | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| *env['LIBRARIES']['firmware-base'], | ||||||
| ] | ||||||
| ] | ||||||
|
|
||||||
| # Correct the typo in the variable name | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why didn't you just fix it? |
||||||
| source += opencan | ||||||
| source += env['LIBRARIES']['ember'] | ||||||
| source += env['LIBRARIES']['node-entry'] | ||||||
| source += env['LIBRARIES']['selfdrive'] | ||||||
|
|
||||||
| batt = env.StaticLibrary(node.lower(), source)[0] | ||||||
| firmware, flash = env.EspIdf(batt, 'esp32s3') | ||||||
|
|
||||||
| Return('firmware', 'flash') | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| #include "batt.h" | ||
|
|
||
| #include <ember_taskglue.h> | ||
| #include <opencan_rx.h> | ||
| #include <opencan_tx.h> | ||
|
|
||
| #include <math.h> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Include only the headers you need. |
||
|
|
||
| static uint8_t batteryPercent; | ||
|
|
||
| static void batt_1Hz(); | ||
|
|
||
| ember_rate_funcs_S module_rf = { | ||
| .call_1Hz = batt_1Hz, | ||
| }; | ||
|
|
||
| static void batt_1Hz() | ||
| { | ||
| batteryPercent = 0; | ||
| } | ||
|
|
||
| void CANTX_populate_BatteryStatus( | ||
| struct CAN_Message_BATT_BatteryStatus * const m) | ||
| { | ||
| m->BATT_batteryPercent = batteryPercent; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,4 @@ | ||||||
| #ifndef BATT_H | ||||||
| #define BATT_H | ||||||
|
|
||||||
| #endif // BATT.H | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # ruff: noqa: F821 | ||
|
|
||
| Import('env') | ||
|
|
||
|
|
||
| # since this has to be compiled uniquely for each micro we'll leave this | ||
| # as strings so that we can symlink this directory and turn these into | ||
| # File() nodes | ||
| firmware_base = [ | ||
| f'firmware-base/{src}' | ||
| for src in [ | ||
| 'app-description.c', | ||
| 'eeprom.c', | ||
| 'eeprom_ember.c', | ||
| 'state-machine.c', | ||
| ] | ||
| ] | ||
|
|
||
|
|
||
| Return('firmware_base') |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| #include <ember_app_desc.h> | ||
|
|
||
| // stringification macros | ||
| #define XSTR(s) STR(s) | ||
| #define STR(s) #s | ||
|
|
||
| const IN_DESC_SECTION ember_app_desc_v1_t ember_app_description = { | ||
| .ember_magic = EMBER_MAGIC, | ||
| .app_desc_version = EMBER_APP_DESC_VERSION, | ||
|
|
||
| .node_identity = XSTR(EMBER_NODE_IDENTITY), | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,240 @@ | ||
| #include "eeprom.h" | ||
|
|
||
| #include <driver/gpio.h> | ||
| #include <driver/i2c.h> | ||
| #include <esp_err.h> | ||
| #include <node_pins.h> | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| #define EEPROM_PAGESIZ 64 | ||
| #define EEPROM_ADDR_I2C (0xa0 | (0x00 << 1)) | ||
| #define EEPROM_ADDR_MAX 0x7fff | ||
| #define EEPROM_SCL_GPIO NODE_BOARD_PIN_EEPROM_SCL | ||
| #define EEPROM_SDA_GPIO NODE_BOARD_PIN_EEPROM_SDA | ||
| #define EEPROM_WP_GPIO NODE_BOARD_PIN_EEPROM_WP | ||
| #define EEPROM_WP(ENABLE) gpio_set_level(EEPROM_WP_GPIO, ENABLE) | ||
|
|
||
| #define I2C_ESP_INTR_FLAGS 0 | ||
| #define I2C_MASTER_FREQ_HZ 100000 | ||
| #define I2C_MASTER_NUM 0 | ||
| #define I2C_MASTER_RX_BUF 0 | ||
| #define I2C_MASTER_TX_BUF 0 | ||
| #define I2C_TICK_TIMEOUT 0 | ||
| #define I2C_WRITE 0 | ||
| #define I2C_READ 1 | ||
|
|
||
| static esp_err_t sel_read(uint16_t addr); | ||
|
|
||
| static uint16_t internal_addr; | ||
|
|
||
| void eeprom_init() | ||
| { | ||
| gpio_set_direction(EEPROM_WP_GPIO, GPIO_MODE_OUTPUT); | ||
| EEPROM_WP(true); | ||
|
|
||
| i2c_config_t conf = { | ||
| .mode = I2C_MODE_MASTER, | ||
| .sda_io_num = EEPROM_SDA_GPIO, | ||
| .scl_io_num = EEPROM_SCL_GPIO, | ||
| .sda_pullup_en = GPIO_PULLUP_DISABLE, | ||
| .scl_pullup_en = GPIO_PULLUP_DISABLE, | ||
| .master.clk_speed = I2C_MASTER_FREQ_HZ, | ||
| }; | ||
|
|
||
| i2c_param_config(I2C_MASTER_NUM, &conf); | ||
| i2c_driver_install(I2C_MASTER_NUM, | ||
| I2C_MODE_MASTER, | ||
| I2C_MASTER_RX_BUF, | ||
| I2C_MASTER_TX_BUF, | ||
| I2C_ESP_INTR_FLAGS); | ||
|
|
||
| // we want to know where we are in EEPROM | ||
| // in case of an immediate address read | ||
| // the following sets internal_addr to 0 | ||
| uint8_t tmp; | ||
| eeprom_read(EEPROM_ADDR_MAX, &tmp, 1); | ||
| } | ||
|
|
||
| // sel_read attempts a selective read at the given address. | ||
| // can be used for acknowledge polling (to ensure EEPROM is not busy) | ||
| static esp_err_t sel_read(uint16_t addr) | ||
| { | ||
| i2c_cmd_handle_t cmd; | ||
| uint8_t buf[2]; | ||
| uint8_t data[1]; | ||
| esp_err_t val = ESP_OK; | ||
|
|
||
| buf[0] = addr >> 8; | ||
| buf[1] = addr & 0xff; | ||
|
|
||
| cmd = i2c_cmd_link_create(); | ||
| if (!cmd) return ESP_ERR_NO_MEM; | ||
|
|
||
| i2c_master_start(cmd); | ||
| i2c_master_write_byte(cmd, EEPROM_ADDR_I2C | I2C_WRITE, true); | ||
| i2c_master_write(cmd, buf, sizeof(buf), true); | ||
| i2c_master_start(cmd); // Missing end signal is intentional | ||
| i2c_master_write_byte(cmd, EEPROM_ADDR_I2C | I2C_READ, true); | ||
| i2c_master_read(cmd, data, 1, I2C_MASTER_LAST_NACK); | ||
| i2c_master_stop(cmd); | ||
|
|
||
| val = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_TICK_TIMEOUT); | ||
| i2c_cmd_link_delete(cmd); | ||
|
|
||
| return val; | ||
| } | ||
|
|
||
| esp_err_t eeprom_read(uint16_t addr, uint8_t *data, size_t len) | ||
| { | ||
| i2c_cmd_handle_t cmd; | ||
| uint8_t buf[2]; | ||
| esp_err_t val = ESP_OK; | ||
|
|
||
| // mask MSB of the address to ensure | ||
| // we can compare w/ internal_addr | ||
| addr &= EEPROM_ADDR_MAX; | ||
|
|
||
| buf[0] = addr >> 8; | ||
| buf[1] = addr & 0xff; | ||
|
|
||
| // ensure we can read | ||
| while (true) { | ||
| val = sel_read(internal_addr); | ||
| if (val == ESP_OK) break; | ||
|
|
||
| // when the EEPROM is busy writing it will | ||
| // not send an ACK, returning ESP_FAIL | ||
| if (val != ESP_FAIL) goto error; | ||
| } | ||
|
|
||
| cmd = i2c_cmd_link_create(); | ||
| if (!cmd) return ESP_ERR_NO_MEM; | ||
|
|
||
| if (internal_addr != addr) { // selective read at the given address | ||
| i2c_master_start(cmd); | ||
| i2c_master_write_byte(cmd, EEPROM_ADDR_I2C | I2C_WRITE, true); | ||
| i2c_master_write(cmd, buf, sizeof(buf), true); | ||
| // missing end signal is intentional | ||
| } // otherwise, do an immediate access read (address not required) | ||
|
|
||
| i2c_master_start(cmd); | ||
| i2c_master_write_byte(cmd, EEPROM_ADDR_I2C | I2C_READ, true); | ||
| i2c_master_read(cmd, data, len, I2C_MASTER_LAST_NACK); | ||
| i2c_master_stop(cmd); | ||
|
|
||
| val = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_TICK_TIMEOUT); | ||
| i2c_cmd_link_delete(cmd); | ||
|
|
||
| internal_addr = addr + len; | ||
| internal_addr &= EEPROM_ADDR_MAX; | ||
|
|
||
| error: | ||
| return val; | ||
| } | ||
|
|
||
| esp_err_t eeprom_write(uint16_t addr, const uint8_t *data, size_t len) | ||
| { | ||
| i2c_cmd_handle_t cmd; | ||
| uint16_t page; | ||
| uint8_t offset, write_len; | ||
| uint8_t buf[2]; | ||
| esp_err_t val = ESP_OK; | ||
|
|
||
| while (len) { | ||
| // mask MSB of the address to ensure correct calculations | ||
| addr &= EEPROM_ADDR_MAX; | ||
|
|
||
| page = addr / EEPROM_PAGESIZ; | ||
| offset = addr % EEPROM_PAGESIZ; | ||
| buf[0] = page >> 2; | ||
| buf[1] = ((page & 0x03) << 6) | offset; | ||
|
|
||
| // we don't want to write too much | ||
| // since we can only write maximum one page at at time | ||
| write_len = EEPROM_PAGESIZ - offset; | ||
| if (write_len > len) write_len = len; | ||
|
|
||
| // ensure we can write | ||
| while (true) { | ||
| val = sel_read(internal_addr); | ||
| if (val == ESP_OK) break; | ||
|
|
||
| // when the EEPROM is busy writing it will | ||
| // not send an ACK, returning ESP_FAIL | ||
| if (val != ESP_FAIL) goto error; | ||
| } | ||
|
|
||
| cmd = i2c_cmd_link_create(); | ||
| if (!cmd) return ESP_ERR_NO_MEM; | ||
|
|
||
| // WP is always enabled after sel_read() call | ||
| EEPROM_WP(false); | ||
|
|
||
| i2c_master_start(cmd); | ||
| i2c_master_write_byte(cmd, EEPROM_ADDR_I2C | I2C_WRITE, true); | ||
| i2c_master_write(cmd, buf, sizeof(buf), true); | ||
| i2c_master_write(cmd, data, write_len, true); | ||
| i2c_master_stop(cmd); | ||
|
|
||
| val = i2c_master_cmd_begin( | ||
| I2C_MASTER_NUM, cmd, I2C_TICK_TIMEOUT); | ||
| i2c_cmd_link_delete(cmd); | ||
| if (val != ESP_OK) goto error; | ||
|
|
||
| addr += write_len; | ||
| internal_addr = addr; | ||
| internal_addr &= EEPROM_ADDR_MAX; | ||
|
|
||
| data += write_len; | ||
| len -= write_len; | ||
| } | ||
|
|
||
| error: | ||
| EEPROM_WP(true); | ||
|
|
||
| return val; | ||
| } | ||
|
|
||
| esp_err_t eeprom_write_byte(uint16_t addr, const uint8_t data) | ||
| { | ||
| i2c_cmd_handle_t cmd; | ||
| uint8_t buf[3]; | ||
| esp_err_t val = ESP_OK; | ||
|
|
||
| // ensure we can write | ||
| while (true) { | ||
| val = sel_read(internal_addr); | ||
| if (val == ESP_OK) break; | ||
|
|
||
| // when the EEPROM is busy writing it will | ||
| // not send an ACK, returning ESP_FAIL | ||
| if (val != ESP_FAIL) goto error; | ||
| } | ||
|
|
||
| buf[0] = addr >> 8; | ||
| buf[1] = addr & 0xff; | ||
| buf[2] = data; | ||
|
|
||
| cmd = i2c_cmd_link_create(); | ||
| if (!cmd) return ESP_ERR_NO_MEM; | ||
|
|
||
| // WP is always enabled after sel_read() call | ||
| EEPROM_WP(false); | ||
|
|
||
| i2c_master_start(cmd); | ||
| i2c_master_write_byte(cmd, EEPROM_ADDR_I2C | I2C_WRITE, true); | ||
| i2c_master_write(cmd, buf, sizeof(buf), true); | ||
| i2c_master_stop(cmd); | ||
|
|
||
| val = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_TICK_TIMEOUT); | ||
| i2c_cmd_link_delete(cmd); | ||
|
|
||
| EEPROM_WP(true); | ||
|
|
||
| internal_addr = addr + 1; | ||
| internal_addr &= EEPROM_ADDR_MAX; | ||
|
|
||
| error: | ||
| return val; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's try to keep this list in alphabetical order.