Skip to content

Switching between output port and serial port not working. #4198

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

Closed
stukodotnet opened this issue Jul 24, 2020 · 4 comments
Closed

Switching between output port and serial port not working. #4198

stukodotnet opened this issue Jul 24, 2020 · 4 comments

Comments

@stukodotnet
Copy link

stukodotnet commented Jul 24, 2020

Hardware:

Board: espressif dev board v4 - ESP32-WROVER-IE 8MB FLASH
Core Installation version: 1.0.3
IDE name: Arduino IDE
Flash Frequency: 40Mhz
PSRAM enabled: no
Upload Speed: 115200
Computer OS: Windows 10

Description:

For my application I need to switch a port pin between being an output port and being a serial port. This is for the automotive k-line protocol - you need to pull the k-line low for 25ms then start sending serial comms 25ms later.

This is the KWP2000 fast-init protocol. And it may need to be re-done if the link times out etc. And I may need to try a different init type if fast-init repeatedly fails, such as KWP2000 slow-init, or ISO9141 5 baud init. I also need to run 2 separate k-lines, which run independently. So as you can see, simply resetting the machine and always stating up with the correct sequence of events won't work. It really needs to be switchable on the fly to properly support the SAE protocol.

The code works fine on an Arduino Mega2650, but on ESP32 it doesn't work.

I made a simple sketch using the blink demo as a base.

  1. I start up by toggling the port every 25ms. This works fine using a scope on the pin.
  2. After 10s I change the port to a serial port, then every 100ms I send a byte. This works fine.
  3. After a further 10s I change back to an output port and toggle at 25ms. This doesn't work, the port remains HIGH.
  4. After a further 10s I change back to serial. This works fine.
  5. goto 3

I also tried using Serial2 directly without the hardwareSerial instantiation, with the same results. I set up 2 ports in the sketch but specifically I need the Tx port to work "ledPin2".

I really need this to work to be able to adhere to the KWP2000 k-line serial protocol.

Sketch:

/*
  Blink without Delay

  Turns on and off a light emitting diode (LED) connected to a digital pin,
  without using the delay() function. This means that other code can run at the
  same time without being interrupted by the LED code.

  The circuit:
  - Use the onboard LED.
  - Note: Most Arduinos have an on-board LED you can control. On the UNO, MEGA
    and ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN
    is set to the correct LED pin independent of which board is used.
    If you want to know what pin the on-board LED is connected to on your
    Arduino model, check the Technical Specs of your board at:
    https://www.arduino.cc/en/Main/Products

  created 2005
  by David A. Mellis
  modified 8 Feb 2010
  by Paul Stoffregen
  modified 11 Nov 2013
  by Scott Fitzgerald
  modified 9 Jan 2017
  by Arturo Guadalupi

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
*/
#include <HardwareSerial.h>


// constants won't change. Used here to set a pin number:
const int ledPin =  19;// the number of the LED pin
const int ledPin2 =  2;
// Variables will change:
int ledState = LOW;             // ledState used to set the LED
bool serialActive = false;      // true when set to serial port

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated
unsigned long previousPortSwitchMillis = millis();

// constants won't change:
const long interval = 25;           // interval at which to blink (milliseconds)
const long portSwitchInterval = 10000; // interval at which to switch to serial port
HardwareSerial testPort(2);

#define TEST_PORT testPort // set to: Serial2 or testPort to use HardwareSerial library

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  Serial.begin(115200);
  Serial.println("Started in OUTPUT mode");
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if(serialActive)
  { 
    static unsigned long lastSend;
    if((currentMillis - lastSend) > 50) {
      //send abyte   
      TEST_PORT.write(".");
      Serial.print(".");
      lastSend = currentMillis;
    }
  }
  else if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
    digitalWrite(ledPin2, ledState);
  }
  
  if (currentMillis - previousPortSwitchMillis >= portSwitchInterval) {
    // save the last time you switched port
    previousPortSwitchMillis = currentMillis;
    if(serialActive){
      Serial.println("");
      Serial.println("Serial.end");
      Serial.println("OUTPUT mode begin");
      TEST_PORT.end();
      //change back to outputs
      pinMode(ledPin, OUTPUT);
      pinMode(ledPin2, OUTPUT);
      serialActive = false;
    }
    else{
      //change to serial
      //change to inputs to disable internal pullups
      pinMode(ledPin, INPUT);
      pinMode(ledPin2, INPUT);
      Serial.println("Serial.begin");
      TEST_PORT.begin(9600, SERIAL_8N1, ledPin, ledPin2);
      
      serialActive = true;
    }
  }
  

  
} 
@stukodotnet
Copy link
Author

Adding:
gpio_matrix_out(ledPin2, 0x100, false, false);
after Serial2.end() fixes this issue for now.

I randomly found the fix after looking through pull requests
https://github.com/espressif/arduino-esp32/issues/3205

@stukodotnet
Copy link
Author

stukodotnet commented Jul 24, 2020

Next problem is that its taking 40ms to do Serial2.begin(...)
An Arduino takes microseconds to complete this call and I have to add a delay(25) before serial bytes appear on the port. I need a delay of exactly 25ms between the port returning high, and the first serial byte, for the KWP2000 spec. But the ESP32 version of Serial2.begin is taking a full 40ms before the first byte. Should this be logged as a new issue? I haven't measured it exactly but a real Arduino takes in the order of 500us to do the begin, so the ESP32 Arduino is around 80 times slower.

@lbernstone
Copy link
Contributor

Serial.end is fixed in #3894

@stukodotnet
Copy link
Author

Serial.end is fixed in #3894

Is the 40ms it takes for Serial.begin() related to this problem too?

I have also noticed that the first digitalWrite(LOW) after switching from serial to digital takes a full 10ms. Then when switching back to serial, the begin() method takes 40ms.

These timings take microseconds on a real Arduino.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants