CanSee.ino 12.9 KB
Newer Older
Notice B.V.'s avatar
Notice B.V. committed
1
/*
2
CanSee
Bob Fisch's avatar
Bob Fisch committed
3
The firmware to the DIY, superfast, ESP32 based comapanion to CANZE dongle
Notice B.V.'s avatar
Notice B.V. committed
4

5 6
Copyright (C) 2019 - The CanZE Team
http://canze.fisch.lu
Notice B.V.'s avatar
Notice B.V. committed
7

8 9 10 11
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 3 of the License, or any
later version.
Notice B.V.'s avatar
Notice B.V. committed
12

13 14 15 16
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.
Notice B.V.'s avatar
Notice B.V. committed
17

18 19
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
Notice B.V.'s avatar
Notice B.V. committed
20
*/
Notice B.V.'s avatar
Notice B.V. committed
21

22
#define VERSION "002"
Notice B.V.'s avatar
Notice B.V. committed
23 24

#define SERIAL_BPS 115200
s12hosting's avatar
s12hosting committed
25 26

/* board is a CP2102 like this
27 28
https://www.ebay.com.au/itm/262903668612 or
https://www.tinytronics.nl/shop/en/communication/network/esp32-wi-fi-and-bluetooth-board-cp2102
s12hosting's avatar
s12hosting committed
29

30
My board: DOIT ESP32 DEVKIT V1
s12hosting's avatar
s12hosting committed
31

32 33
ESP32 GPIO5 - CAN CTX
ESP32 GPIO4 - CAN CRX
s12hosting's avatar
s12hosting committed
34

35 36 37 38 39
ESP32 GPIO26  white  LED with 3.3 kO resistor
ESP32 GPIO27  yellow LED with 120 O  resistor
ESP32 GPIO32  red    LED with 120 O  resistor
ESP32 GPIO33  green  LED with 120 O  resistor
ESP32 GPIO25  blue   LED with 120 O  resistor
s12hosting's avatar
s12hosting committed
40 41
*/

Notice B.V.'s avatar
Notice B.V. committed
42
// Our own includes, see ./include/ ******************************************
43 44
#include "config.h"
#include "canhandler.h"
45
#include "leds.h"
46 47 48
#include "serialhandler.h"
#include "bluetoothhandler.h"
#include "wifihandler.h"
49
#include "freeframehandler.h"
50
#include "isotphandler.h"
51
#include "utils.h"
Notice B.V.'s avatar
Notice B.V. committed
52 53

// Tidy up defs **************************************************************
s12hosting's avatar
s12hosting committed
54 55 56 57
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

Notice B.V.'s avatar
Notice B.V. committed
58
// Config ********************************************************************
Notice B.V.'s avatar
Notice B.V. committed
59
CS_CONFIG_t *cansee_config;
s12hosting's avatar
s12hosting committed
60

Notice B.V.'s avatar
Notice B.V. committed
61
// Command structure that defines a textual incoming command *****************
Notice B.V.'s avatar
Notice B.V. committed
62 63
typedef struct
{
s12hosting's avatar
s12hosting committed
64 65
  char cmd;
  uint32_t id = 0;
66 67
  uint8_t request[32];
  uint16_t requestLength = 0;
68
  uint8_t reply[32];
69
  uint16_t replyLength = 0;
70
  char line[48];
Notice B.V.'s avatar
Notice B.V. committed
71
} COMMAND_t;
s12hosting's avatar
s12hosting committed
72

Notice B.V.'s avatar
Notice B.V. committed
73
// command read buffer *******************************************************
s12hosting's avatar
s12hosting committed
74 75
String readBuffer = "";

Notice B.V.'s avatar
Notice B.V. committed
76 77 78 79 80 81
//* counters *****************************************************************
uint32_t canFrameCounter = 1;
uint32_t lastCanFrameCounter = 0;
uint32_t cnt5000 = 0;
uint32_t cnt100 = 0;

Notice B.V.'s avatar
Notice B.V. committed
82
// ***************************************************************************
Notice B.V.'s avatar
Notice B.V. committed
83 84 85 86 87 88 89 90 91 92 93 94
void setup()
{
  Serial.begin(SERIAL_BPS); // init serial
  Serial.println("");
  Serial.println("");
  Serial.println("CANSee starting...");

  delay(500); // give user chance to press BUT
  pinMode(0, INPUT);
  cansee_config = getConfig(); // invalid config will reset too
  if (!digitalRead(0))
  { // if pressed
95
    Serial.println("Reset config...");
Notice B.V.'s avatar
Notice B.V. committed
96
    setConfigToEeprom(true);
97
  }
98 99
  cansee_config->output_handler = writeOutgoing;
  cansee_config->command_handler = processCommand;
100

Notice B.V.'s avatar
Notice B.V. committed
101 102 103 104 105 106 107 108 109 110 111 112
  if (cansee_config->mode_debug)
  {
    Serial.print("Version:   ");
    Serial.println(VERSION);
    Serial.println("Serial:    " + getHex(cansee_config->mode_serial));
    Serial.println("Bluetooth: " + getHex(cansee_config->mode_bluetooth));
    Serial.println("WiFi:      " + getHex(cansee_config->mode_wifi));
    Serial.println("Leds:      " + getHex(cansee_config->mode_leds));
    Serial.println("Debug:     " + getHex(cansee_config->mode_debug));
    Serial.println("CANbus0:   " + getHex(cansee_config->can0_speed / 25) + getHex(cansee_config->can0_rx) + getHex(cansee_config->can0_tx));
    Serial.println("CANbus1*   " + getHex(cansee_config->can1_speed / 25) + getHex(cansee_config->can1_rx) + getHex(cansee_config->can1_tx));
    Serial.println("Boot count:" + getHex(cansee_config->boot_count));
113
  }
Notice B.V.'s avatar
Notice B.V. committed
114
  cansee_config->boot_count++;
Notice B.V.'s avatar
Notice B.V. committed
115
  setConfigToEeprom(false);
116

Notice B.V.'s avatar
Notice B.V. committed
117
  leds_init();
s12hosting's avatar
s12hosting committed
118

Notice B.V.'s avatar
Notice B.V. committed
119 120 121
  serial_init();
  bluetooth_init();
  wifi_init();
122

Notice B.V.'s avatar
Notice B.V. committed
123 124 125
  can_init();
  freeframe_init();
  isotp_init();
s12hosting's avatar
s12hosting committed
126 127
}

Notice B.V.'s avatar
Notice B.V. committed
128
// ***************************************************************************
Notice B.V.'s avatar
Notice B.V. committed
129 130 131
void loop()
{
  tickerFast();
Notice B.V.'s avatar
Notice B.V. committed
132 133
}

Notice B.V.'s avatar
Notice B.V. committed
134 135 136 137
void tickerFast()
{
  uint32_t nowMicros = micros();
  static uint32_t lastMicros = nowMicros; // static so should only be initalized once
Notice B.V.'s avatar
Notice B.V. committed
138 139

  // do Fast
Notice B.V.'s avatar
Notice B.V. committed
140 141 142 143
  CAN_frame_t rx_frame; // 1. receive next CAN frame from queue
  if (can_receive(&rx_frame))
  {
    storeFrame(rx_frame);
Notice B.V.'s avatar
Notice B.V. committed
144
    canFrameCounter++;
s12hosting's avatar
s12hosting committed
145
  }
Notice B.V.'s avatar
Notice B.V. committed
146 147
  readIncoming(); // 2. proceed with input (serial & BT)
  isotp_ticker();
Notice B.V.'s avatar
Notice B.V. committed
148
  // end do Fast
s12hosting's avatar
s12hosting committed
149

Notice B.V.'s avatar
Notice B.V. committed
150 151 152
  if ((nowMicros - lastMicros) > 100000L)
  { // 110 ms passed?
    ticker100ms();
153 154
    //lastMicros = nowMicros;
    lastMicros += 100000L;
Notice B.V.'s avatar
Notice B.V. committed
155 156 157
  }
}

Notice B.V.'s avatar
Notice B.V. committed
158 159
void ticker100ms()
{
Notice B.V.'s avatar
Notice B.V. committed
160
  static int tick = 0;
s12hosting's avatar
s12hosting committed
161

Notice B.V.'s avatar
Notice B.V. committed
162 163 164 165
  // do every 100ms
  // Things like button pushed should go here
  // end do every 100 ms

Notice B.V.'s avatar
Notice B.V. committed
166 167 168
  if (++tick == 10)
  {
    ticker1000ms();
Notice B.V.'s avatar
Notice B.V. committed
169 170 171 172
    tick = 0;
  }
}

Notice B.V.'s avatar
Notice B.V. committed
173 174
void ticker1000ms()
{
Notice B.V.'s avatar
Notice B.V. committed
175 176 177 178 179 180 181
  static int tick = 0;

  // do every 1000ms
  static bool powerToggle = false;
  led_set(LED_RED, (powerToggle = !powerToggle));
  // end do every 1000 ms

Notice B.V.'s avatar
Notice B.V. committed
182 183 184
  if (++tick == 5)
  {
    ticker5000ms();
Notice B.V.'s avatar
Notice B.V. committed
185
    tick = 0;
Bob Fisch's avatar
Bob Fisch committed
186
  }
Notice B.V.'s avatar
Notice B.V. committed
187
}
s12hosting's avatar
s12hosting committed
188

Notice B.V.'s avatar
Notice B.V. committed
189 190
void ticker5000ms()
{
Notice B.V.'s avatar
Notice B.V. committed
191
  // do every 5000ms
Notice B.V.'s avatar
Notice B.V. committed
192 193
  setActiveBluetooth(canFrameCounter != lastCanFrameCounter);
  ageFreeFrame();
Notice B.V.'s avatar
Notice B.V. committed
194
  // end do every 5000 ms
s12hosting's avatar
s12hosting committed
195 196
}

197
/*****************************************************************************
198 199
* frame handling function
*/
s12hosting's avatar
s12hosting committed
200

Notice B.V.'s avatar
Notice B.V. committed
201 202 203 204 205 206 207 208 209 210
void storeFrame(CAN_frame_t &frame)
{
  led_set(LED_GREEN, true);
  if (frame.MsgID < 0x700)
  { // free data is < 0x700
    storeFreeframe(frame, 0);
  }
  else if (frame.MsgID < 0x800)
  { // iso-tp data is < 0x800
    storeIsotpframe(frame, 0);
s12hosting's avatar
s12hosting committed
211
  }
Notice B.V.'s avatar
Notice B.V. committed
212
  led_set(LED_GREEN, false);
s12hosting's avatar
s12hosting committed
213 214
}

Notice B.V.'s avatar
Notice B.V. committed
215
// I/O functions *************************************************************
s12hosting's avatar
s12hosting committed
216

Notice B.V.'s avatar
Notice B.V. committed
217 218 219 220 221
void writeOutgoing(String o)
{
  writeOutgoingSerial(o);
  writeOutgoingBluetooth(o);
  writeOutgoingWiFi(o);
s12hosting's avatar
s12hosting committed
222 223
}

Notice B.V.'s avatar
Notice B.V. committed
224 225 226 227 228
void readIncoming()
{
  readIncomingSerial(readBuffer);
  readIncomingBluetooth(readBuffer);
  readIncomingWiFi(readBuffer);
s12hosting's avatar
s12hosting committed
229 230
}

Notice B.V.'s avatar
Notice B.V. committed
231
// execute a command *********************************************************
Notice B.V.'s avatar
Notice B.V. committed
232 233
void processCommand()
{
234
  uint8_t bus = 0;
Notice B.V.'s avatar
Notice B.V. committed
235

Notice B.V.'s avatar
Notice B.V. committed
236 237 238
  if (cansee_config->mode_debug & DEBUG_COMMAND)
    Serial.println("< com:" + readBuffer);
  COMMAND_t command = decodeCommand(readBuffer); // watch out, passe dby reference, so eaten
239

240 241
  //bus = (command.id & 0x40000000) ? 1 : 0;
  //command.id &= 0x1fffffff;                        // allow for 29 bits CAN later
242

Notice B.V.'s avatar
Notice B.V. committed
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
  switch (command.cmd)
  {

  // output all buffered frames ********************************************
  case 'a':
  {
    int count = 0;
    FREEFRAME_t *freeframe;
    for (uint32_t id = 0; id < FREEFRAMEARRAYSIZE; id++)
    {
      freeframe = getFreeframe(id, bus); // bus is ignored for free frames
      if (freeframe->age)
      { // print length 0 frames, but do not print timeout frames
        //writeOutgoing (bufferedFrameToString (id, bus)); // includes \n
        requestFreeframe(id, bus);
        count++;
s12hosting's avatar
s12hosting committed
259 260
      }
    }
Notice B.V.'s avatar
Notice B.V. committed
261 262 263 264
    if (cansee_config->mode_debug & DEBUG_COMMAND)
      Serial.println("> fcn:" + String(count));
  }
  break;
s12hosting's avatar
s12hosting committed
265

Notice B.V.'s avatar
Notice B.V. committed
266 267 268 269 270 271 272 273 274 275 276
  // get a frame ***********************************************************
  case 'g':
    if (command.id < FREEFRAMEARRAYSIZE)
    {
      requestFreeframe(command.id, bus);
    }
    else
    {
      if (cansee_config->mode_debug & DEBUG_COMMAND)
        Serial.print("> com:ID out of bounds (0 - 0x6ff)");
      requestFreeframe(0, bus);
s12hosting's avatar
s12hosting committed
277
    }
278
    break;
s12hosting's avatar
s12hosting committed
279

Notice B.V.'s avatar
Notice B.V. committed
280 281 282 283 284 285 286 287 288 289 290
  // request an ISO-TP frame ***********************************************
  case 'i':
    if (command.id >= 0x700 && command.id <= 0x7ff)
    {
      requestIsotp(command.id, command.requestLength, command.request, bus);
    }
    else
    {
      if (cansee_config->mode_debug)
        Serial.println("E:ID out of bounds (0x700 - 0x7ff)");
      requestIsotp(0, 0, 0, 0);
291 292 293
    }
    break;

Notice B.V.'s avatar
Notice B.V. committed
294 295 296 297 298 299 300
  // inject a frame via serial / BT input **********************************
  case 't':
  {
    CAN_frame_t frame;
    frame.MsgID = command.id;
    frame.FIR.B.DLC = command.requestLength;
    for (int i = 0; i < command.requestLength; i++)
s12hosting's avatar
s12hosting committed
301
      frame.data.u8[i] = command.request[i];
Notice B.V.'s avatar
Notice B.V. committed
302 303 304 305 306 307 308
    if (cansee_config->mode_debug & DEBUG_COMMAND)
      Serial.print("> com:Injecting " + canFrameToString(frame));
    storeFrame(frame);
    // storeframe will output if free frame or ISO-TP Single
    // writeOutgoing (getHex (command.id) + "\n");
  }
  break;
s12hosting's avatar
s12hosting committed
309

Notice B.V.'s avatar
Notice B.V. committed
310 311 312 313 314
  // filter (deprecated) ***************************************************
  case 'f':
    if (cansee_config->mode_debug & DEBUG_COMMAND)
      Serial.println("> com:Filter " + getHex(command.id));
    writeOutgoing(getHex(command.id) + "\n");
315 316
    break;

Notice B.V.'s avatar
Notice B.V. committed
317 318 319 320 321 322 323 324 325 326 327 328
  // config (see config.cpp) ***********************************************
  case 'n':
    if (cansee_config->mode_debug & DEBUG_COMMAND)
      Serial.println("> com:config " + getHex(command.id));
    switch (command.id)
    {
    case 0x100: // set mode flags
      cansee_config->mode_serial = command.request[0];
      cansee_config->mode_bluetooth = command.request[1];
      cansee_config->mode_wifi = command.request[2];
      cansee_config->mode_leds = command.request[3];
      cansee_config->mode_debug = command.request[4];
329
      break;
Notice B.V.'s avatar
Notice B.V. committed
330 331
    case 0x101: // get mode flags
      writeOutgoing(getHex(command.id) + "," + getHex(cansee_config->mode_serial) + getHex(cansee_config->mode_bluetooth) + getHex(cansee_config->mode_wifi) + getHex(cansee_config->mode_leds) + getHex(cansee_config->mode_debug) + "\n");
332
      return;
Notice B.V.'s avatar
Notice B.V. committed
333
      break;
Notice B.V.'s avatar
Notice B.V. committed
334 335
    case 0x200:
      strncpy(cansee_config->name_bluetooth, command.line + 5, sizeof(cansee_config->name_bluetooth));
336
      break;
Notice B.V.'s avatar
Notice B.V. committed
337 338
    case 0x201:
      strncpy(cansee_config->pin_bluetooth, command.line + 5, sizeof(cansee_config->pin_bluetooth));
339
      break;
Notice B.V.'s avatar
Notice B.V. committed
340 341
    case 0x300:
      strncpy(cansee_config->ssid_ap, command.line + 5, sizeof(cansee_config->ssid_ap));
342
      break;
Notice B.V.'s avatar
Notice B.V. committed
343 344
    case 0x301:
      strncpy(cansee_config->password_ap, command.line + 5, sizeof(cansee_config->password_ap));
345
      break;
Notice B.V.'s avatar
Notice B.V. committed
346 347 348 349 350
    case 0x400:
      strncpy(cansee_config->ssid_station, command.line + 5, sizeof(cansee_config->ssid_station));
      Serial.println(sizeof(cansee_config->ssid_station));
      Serial.println(command.line);
      Serial.println(cansee_config->ssid_station);
351
      break;
Notice B.V.'s avatar
Notice B.V. committed
352 353
    case 0x401:
      strncpy(cansee_config->password_station, command.line + 5, sizeof(cansee_config->password_station));
354
      break;
Notice B.V.'s avatar
Notice B.V. committed
355 356 357 358 359 360 361 362
    case 0x500: // can0
      cansee_config->can0_speed = command.request[0] * 25;
      cansee_config->can0_rx = command.request[1];
      cansee_config->can0_tx = command.request[2];
    case 0x501: // can1
      cansee_config->can1_speed = command.request[0] * 25;
      cansee_config->can1_rx = command.request[1];
      cansee_config->can1_tx = command.request[2];
363
      break;
364
    }
Notice B.V.'s avatar
Notice B.V. committed
365 366
    setConfigToEeprom(false);
    writeOutgoing(getHex(command.id) + "\n");
367
    break;
s12hosting's avatar
s12hosting committed
368

Notice B.V.'s avatar
Notice B.V. committed
369 370
  // reboot *****************************************************************
  case 'z':
371 372 373
    ESP.restart();
    break;

Notice B.V.'s avatar
Notice B.V. committed
374 375
  // reset config & reboot **************************************************
  case 'r':
376 377 378 379
    setConfigToEeprom(true);
    ESP.restart();
    break;

Notice B.V.'s avatar
Notice B.V. committed
380 381 382 383
  // give up ****************************************************************
  default:
    if (cansee_config->mode_debug)
      Serial.println("> com:Unknown command " + String(command.cmd));
s12hosting's avatar
s12hosting committed
384
    writeOutgoing("fff,\n");
385
    break;
s12hosting's avatar
s12hosting committed
386 387 388
  }
}

Notice B.V.'s avatar
Notice B.V. committed
389
// parse a string into a command *********************************************
Notice B.V.'s avatar
Notice B.V. committed
390 391
COMMAND_t decodeCommand(String &input)
{
Notice B.V.'s avatar
Notice B.V. committed
392
  COMMAND_t result;
Notice B.V.'s avatar
Notice B.V. committed
393
  result.id = 0; // clear out older data
s12hosting's avatar
s12hosting committed
394 395 396
  result.requestLength = 0;
  result.replyLength = 0;

Notice B.V.'s avatar
Notice B.V. committed
397 398
  input.trim(); // trim whitespaces
  strncpy(result.line, input.c_str(), sizeof(result.line));
s12hosting's avatar
s12hosting committed
399

Notice B.V.'s avatar
Notice B.V. committed
400 401
  if (input.length() == 0)
    return result; // stop if input is empty
s12hosting's avatar
s12hosting committed
402

Notice B.V.'s avatar
Notice B.V. committed
403
  result.cmd = input.charAt(0); // the first letter is the command
s12hosting's avatar
s12hosting committed
404 405
  input.remove(0, 1);

Notice B.V.'s avatar
Notice B.V. committed
406 407 408
  if (input.length() != 0)
  {          // if there is something more,
    char ch; // get the ID
s12hosting's avatar
s12hosting committed
409
    String id = "";
Notice B.V.'s avatar
Notice B.V. committed
410 411
    do
    {
s12hosting's avatar
s12hosting committed
412
      ch = input.charAt(0);
Notice B.V.'s avatar
Notice B.V. committed
413 414
      if (ch != ',')
        id += ch;
s12hosting's avatar
s12hosting committed
415
      input.remove(0, 1);
416
    } while (input.length() != 0 && ch != ',');
s12hosting's avatar
s12hosting committed
417 418 419
    result.id = hexToDec(id);
  }

Notice B.V.'s avatar
Notice B.V. committed
420 421 422
  if (input.length() != 0)
  {          // if there is something more,
    char ch; // get the REQUEST
s12hosting's avatar
s12hosting committed
423
    String request = "";
Notice B.V.'s avatar
Notice B.V. committed
424 425
    do
    {
s12hosting's avatar
s12hosting committed
426
      ch = input.charAt(0);
Notice B.V.'s avatar
Notice B.V. committed
427 428
      if (ch != ',')
        request += ch;
s12hosting's avatar
s12hosting committed
429
      input.remove(0, 1);
430
    } while (input.length() != 0 && ch != ',');
Notice B.V.'s avatar
Notice B.V. committed
431 432
    for (int i = 0; i < request.length() && result.requestLength < 32; i += 2)
    { // check for overflow
s12hosting's avatar
s12hosting committed
433 434 435 436 437
      result.request[result.requestLength] = hexToDec(request.substring(i, i + 2));
      result.requestLength++;
    }
  }

Notice B.V.'s avatar
Notice B.V. committed
438 439 440
  if (input.length() != 0)
  {          // if there is something more,
    char ch; // get the REPLY
s12hosting's avatar
s12hosting committed
441
    String reply = "";
Notice B.V.'s avatar
Notice B.V. committed
442 443
    do
    {
s12hosting's avatar
s12hosting committed
444
      ch = input.charAt(0);
Notice B.V.'s avatar
Notice B.V. committed
445 446
      if (ch != ',')
        reply += ch;
s12hosting's avatar
s12hosting committed
447
      input.remove(0, 1);
448
    } while (input.length() != 0 && ch != ',');
Notice B.V.'s avatar
Notice B.V. committed
449 450
    for (int i = 0; i < reply.length() && result.replyLength < 32; i += 2)
    { // check for overflow
s12hosting's avatar
s12hosting committed
451 452 453 454 455 456
      result.reply[result.replyLength] = hexToDec(reply.substring(i, i + 2));
      result.replyLength++;
    }
  }
  return result;
}