Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/isotp"]
path = lib/isotp
url = https://github.com/LibreSolar/isotp-c
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,23 @@ The data can be accessed in the same way as described above.
- Data access via HTTP JSON API
- Publishing of monitoring data via WiFi to
- Open Energy Monitor [Emoncms](https://emoncms.org/)
- MQTT sever (ToDo)
- MQTT server (ToDo)
- Data logging on SD card (ToDo)

## Usage

### Build the Webapp
### Getting the firmware

This firmware repository contains git submodules, so you need to clone (download) it by calling:

```
git clone --recursive https://github.com/LibreSolar/esp32-edge-firmware
```

Unfortunately, the green GitHub "Clone or download" button does not include submodules. If you cloned the repository already and want to pull the submodules, run `git submodule update --init --recursive`.


### Building the webapp

To be able to build the esp32-edge-firmware you need to build the webapp first.
To do so, go into the webapp folder
Expand All @@ -59,7 +70,8 @@ and run
npm install
npm run build
```
you are now able to build the firmware itself.

You are now able to build the firmware itself.

### ESP-IDF toolchain

Expand All @@ -72,7 +84,7 @@ After installation run the following commands:

### PlatformIO

You can use PlatformIO for easy bulding and flashing. Currently, ESP-IDF 4.0 support is still in beta phase, so it might not work out of the box. However, the setup in `platformio.ini` was adjusted to support the new ESP-IDF already.
You can use PlatformIO for easy building and flashing. However, the PlatformIO packages for ESP-IDF are not updated as frequently as the official repositories.

### Configuration

Expand Down
1 change: 1 addition & 0 deletions lib/isotp
Submodule isotp added at 9755e0
2 changes: 2 additions & 0 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ set(app_sources
"web_fs.c"
"web_server.c"
"provisioning.c"
"../lib/isotp/isotp.c"
"isotp_user.c"
)

idf_component_register(SRCS ${app_sources} INCLUDE_DIRS ".")
Expand Down
134 changes: 94 additions & 40 deletions main/can.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,28 @@
#include "driver/can.h"
#include "driver/gpio.h"

#include "../lib/isotp/isotp.h"
#include "../lib/isotp/isotp_defines.h"

static const char *TAG = "can";

bool update_bms_received = false;
bool update_mppt_received = false;

#if CONFIG_THINGSET_CAN

#define ISOTP_BUFSIZE 512

/* Alloc IsoTpLink statically in RAM */
static IsoTpLink isotp_link;

/* Alloc send and receive buffer statically in RAM */
static uint8_t isotp_recv_buf[ISOTP_BUFSIZE];
static uint8_t isotp_send_buf[ISOTP_BUFSIZE];

uint32_t can_addr_client = 0xF1; // this device
uint32_t can_addr_server = 0x14; // select MPPT or BMS

// buffer for JSON string generated from received data objects via CAN
static char json_buf[500];

Expand Down Expand Up @@ -101,7 +118,8 @@ static int generate_json_string(char *buf, size_t len, DataObject *objs, size_t
switch (objs[i].raw_data[0]) {
case CAN_TS_T_TRUE:
case CAN_TS_T_FALSE:
pos += snprintf(&buf[pos], len - pos, "%d", (objs[i].raw_data[0] == CAN_TS_T_TRUE) ? 1 : 0);
pos += snprintf(&buf[pos], len - pos, "%d",
(objs[i].raw_data[0] == CAN_TS_T_TRUE) ? 1 : 0);
break;
case CAN_TS_T_POS_INT32:
value_abs =
Expand Down Expand Up @@ -160,7 +178,6 @@ static int generate_json_string(char *buf, size_t len, DataObject *objs, size_t
return pos;
}


char *get_mppt_json_data()
{
generate_json_string(json_buf, sizeof(json_buf),
Expand All @@ -184,69 +201,106 @@ void can_setup()
gpio_set_level(CONFIG_GPIO_CAN_STB, 0);
#endif

if (can_driver_install(&g_config, &t_config, &f_config) == ESP_OK) {
printf("CAN driver installed\n");
}
else {
printf("Failed to install CAN driver\n");
if (can_driver_install(&g_config, &t_config, &f_config) != ESP_OK) {
ESP_LOGE(TAG, "Failed to install CAN driver");
return;
}

if (can_start() == ESP_OK) {
printf("CAN driver started\n");
}
else {
printf("Failed to start CAN driver\n");
if (can_start() != ESP_OK) {
ESP_LOGE(TAG, "Failed to start CAN driver");
return;
}

/* Initialize link with the CAN ID we send with */
isotp_init_link(&isotp_link, can_addr_server << 8 | can_addr_client | 0x1ada << 16,
isotp_send_buf, sizeof(isotp_send_buf), isotp_recv_buf, sizeof(isotp_recv_buf));
}

void can_receive_task(void *arg)
{
can_message_t message;
//unsigned int msg_priority; // currently not used
unsigned int node_id;
unsigned int data_object_id;
unsigned int device_addr;
unsigned int data_node_id;

uint8_t payload[500];

while (1) {
if (can_receive(&message, pdMS_TO_TICKS(10000)) == ESP_OK) {

// ThingSet publication message format: https://thingset.github.io/spec/can
//msg_priority = message.identifier >> 26;
node_id = message.identifier & 0x000000FF;
data_object_id = (message.identifier >> 8) & 0x000000FF;

if (node_id == 0) {
for (int i = 0; i < sizeof(data_obj_bms)/sizeof(DataObject); i++) {
if (data_obj_bms[i].id == data_object_id) {
memcpy(data_obj_bms[i].raw_data, message.data, message.data_length_code);
data_obj_bms[i].len = message.data_length_code;
}
/* checking for CAN ID used to receive ISO-TP frames */
if (message.identifier == (can_addr_client << 8 | can_addr_server | 0x1ada << 16)) {
ESP_LOGI(TAG, "ISO TP msg part received");
isotp_on_can_message(&isotp_link, message.data, message.data_length_code);

/* process multiple frame transmissions and timeouts */
isotp_poll(&isotp_link);

/* extract received data */
uint16_t out_size;
int ret = isotp_receive(&isotp_link, payload, sizeof(payload) - 1, &out_size);
if (ret == ISOTP_RET_OK) {
payload[out_size] = '\0';
ESP_LOGI(TAG, "Received %d bytes via ISO-TP: %s", out_size, payload);
/* ToDo: handle received message */
}
update_bms_received = true;
}
else if (node_id == 10) {
for (int i = 0; i < sizeof(data_obj_mppt)/sizeof(DataObject); i++) {
if (data_obj_mppt[i].id == data_object_id) {
memcpy(data_obj_mppt[i].raw_data, message.data, message.data_length_code);
data_obj_mppt[i].len = message.data_length_code;
else {
// ThingSet publication message format: https://thingset.github.io/spec/can
device_addr = message.identifier & 0x000000FF;
data_node_id = (message.identifier >> 8) & 0x0000FFFF;

if (device_addr == 0) {
for (int i = 0; i < sizeof(data_obj_bms) / sizeof(DataObject); i++) {
if (data_obj_bms[i].id == data_node_id) {
memcpy(data_obj_bms[i].raw_data, message.data,
message.data_length_code);
data_obj_bms[i].len = message.data_length_code;
}
}
update_bms_received = true;
}
else if (device_addr == 10) {
for (int i = 0; i < sizeof(data_obj_mppt) / sizeof(DataObject); i++) {
if (data_obj_mppt[i].id == data_node_id) {
memcpy(data_obj_mppt[i].raw_data, message.data,
message.data_length_code);
data_obj_mppt[i].len = message.data_length_code;
}
}
update_mppt_received = true;
}
update_mppt_received = true;
}

printf("CAN msg node %u, data object 0x%.2x = 0x",
node_id, data_object_id);
if (!(message.flags & CAN_MSG_FLAG_RTR)) {
for (int i = 0; i < message.data_length_code; i++) {
printf("%.2x", message.data[i]);
printf("CAN device addr %u, data node 0x%.2x = 0x", device_addr, data_node_id);
if (!(message.flags & CAN_MSG_FLAG_RTR)) {
for (int i = 0; i < message.data_length_code; i++) {
printf("%.2x", message.data[i]);
}
}
printf("\n");
}
printf("\n");
}
}
}

/* dummy task to send regular requests for testing */
void isotp_task(void *arg)
{
//uint8_t ts_request[] = { 0x01, 0x18, 0x70, 0xA0 };
uint8_t ts_request[] = "?output";

while (1) {

int ret = isotp_send(&isotp_link, ts_request, strlen((char *)ts_request));
if (ISOTP_RET_OK == ret) {
printf("ISOTP Send OK\n");
} else {
printf("ISOTP Send ERROR\n");
}

vTaskDelay(3000 / portTICK_PERIOD_MS);
}
}

#else /* not CONFIG_THINGSET_CAN */

char *get_mppt_json_data()
Expand Down
5 changes: 5 additions & 0 deletions main/can.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ void can_setup();
*/
void can_receive_task(void *arg);

/**
* Thread performing regular requests to other devices using ISO-TP
*/
void isotp_task(void *arg);

/**
* Get data from MPPT connected via CAN bus and convert it to JSON
*
Expand Down
53 changes: 53 additions & 0 deletions main/isotp_user.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2020 Martin Jäger / Libre Solar
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "esp_system.h"
#include "esp_err.h"
#include "esp_log.h"

#include "driver/can.h"
#include "driver/gpio.h"

#include "../lib/isotp/isotp.h"

/*
* required, this must send a single CAN message with the given arbitration
* ID (i.e. the CAN message ID) and data. The size will never be more than 8
* bytes.
*/
int isotp_user_send_can(const uint32_t arbitration_id,
const uint8_t* data, const uint8_t size)
{
can_message_t msg;
memcpy(msg.data, data, size);
msg.data_length_code = size;
msg.identifier = arbitration_id;
msg.flags = CAN_MSG_FLAG_EXTD;
return can_transmit(&msg, 0);
}

/*
* required, return system tick, unit is millisecond
*/
uint32_t isotp_user_get_ms(void)
{
return esp_timer_get_time() / 1000;
}

/*
* optional, provide to receive debugging log messages
*/
void isotp_user_debug(const char* message, ...)
{
va_list argp;
va_start(argp, message);
printf(message, argp);
va_end(argp);
}
2 changes: 2 additions & 0 deletions main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ void app_main(void)
can_setup();
xTaskCreatePinnedToCore(can_receive_task, "CAN_rx", 4096,
NULL, RX_TASK_PRIO, NULL, 1);
xTaskCreatePinnedToCore(isotp_task, "CAN_isotp", 1024,
NULL, RX_TASK_PRIO, NULL, 1);
#endif

#if CONFIG_THINGSET_SERIAL
Expand Down