Skip to content
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

Soft Serial on ESP8266 is broken because of missing hw_timer functionality #1511

Closed
MaBecker opened this issue Sep 16, 2018 · 32 comments
Closed
Labels
enhancement ESP8266 This is only a problem on ESP8266 devices implemented

Comments

@MaBecker
Copy link
Contributor

Try to test soft serial on ESP8266.

In ESP8266_4MB.py are two serial defines and assigned to pins and can be access is via Serial1 and Serial2.

What is necessary use a soft serial on ESP8266 and run this simple testcode?

Serial?.setup(9600,{tx:D12,rx:D13});
Serial?.write('Hello');
@MaBecker MaBecker added question ESP8266 This is only a problem on ESP8266 devices labels Sep 16, 2018
@wilberforce
Copy link
Member

I'm not sure if this would work very well as the wifi could interrupt the timing of the software and the bit rates could be off.

Are two hardware ports not enough? Given that you can turn off debug and the serial console anspd use wifi.

@MaBecker
Copy link
Contributor Author

Well I just want to know how to use this Software Serial port and If it is not possible to use with ESP8266 than I will mention this on ESP8266 page - Thats all.

@gfwilliams
Copy link
Member

Literally just like you do with software SPI/I2C/etc:

var s = new Serial();
s.setup(9600,{tx:D12,rx:D13});
s.write('Hello');

@MaBecker
Copy link
Contributor Author

MaBecker commented Sep 18, 2018

Thanks, looks simple so tried immediately.

require("ESP8266").logDebug(true);require("ESP8266").setLog(2);
var s = new Serial();
=Serial: {  }
s.setup(9600,{tx:D12,rx:D13});
34014> > ESP8266: jshPinSetState state: GPIO_OUT
34014> > ESP8266: jshPinSetState state: GPIO_OUT
34014> > ESP8266: jshPinSetState state: GPIO_IN_PULLUP
34017> > ESP8266: jshPinSetState state: GPIO_IN
=undefined
s.write('Hello');
ets Jan  8 2013,rst cause:2, boot mode:(3,4)
load 0x40100000, len 2408, room 16
....

Is there something I can check to find out why s.write() causes a reboot?

@gfwilliams
Copy link
Member

gfwilliams commented Sep 19, 2018

You'd have to ask someone who knows ESP8266 butter. My guess would be it's the digitalPulse implementation - either the whole thing takes too long (unlikely) or it's blocking execution because it's not using the util timer, and that means the time periods given to it get out of sync.

... maybe the time given to it ends in the past and there's no bounds checking so the negative number ends up as a big positive number, which makes it pause for too long and trip the watchdog

@MaBecker
Copy link
Contributor Author

Ok, so I will check digitalPulse and come back with my results.

@MaBecker MaBecker changed the title Soft Serial on ESP8266 Soft Serial on ESP8266 is broken Sep 19, 2018
@MaBecker MaBecker removed the question label Sep 19, 2018
@MaBecker
Copy link
Contributor Author

Sending a single char does not reboot, but send more cause reboot no matter what baud rate is chosen.

var s = new Serial();
s.setup(9600,{tx:D12,rx:D13});
s.write('H');
s.write('e');
s.write('l');
s.write('l');
s.write('o');

@MaBecker
Copy link
Contributor Author

Where to find the function that loops over the given data?

@gfwilliams
Copy link
Member

@MaBecker
Copy link
Contributor Author

Got the jsiConsolePrintf active.

one single char s.write works, but more than one char than it reboots

@gfwilliams
Copy link
Member

Ok, it's using the Util Timer. The Util timer is supposed to be IRQ-based and able to preempt execution in the main thread.

In ESP8266 it seems it's not.

So what happens is serial write schedules a bunch of tasks and then waits for the buffer to get empty, thinking the tasks will get executed eventually. They never will.

Sure, we could come up with some awful hack using delays to make software serial TX work, but it doesn't solve the underlying problem.

IMO we'd be best off using the proper hardware timer (according to https://www.espressif.com/sites/default/files/documentation/2C-ESP8266_Non_OS_SDK_API_Reference__EN.pdf there's an example in ESP8266_NONOS_SDK/examples/driver_lib/hw_timer.c) and replacing the code at https://github.com/espruino/Espruino/blob/master/targets/esp8266/jshardware.c#L1201-L1223 with that.

Bonus is:

@MaBecker
Copy link
Contributor Author

Cool, starting to work on this.

I'm curious how far I will get.

@MaBecker
Copy link
Contributor Author

Ok, code is there and compiling is possible.

this is how I understand what happens when softSerial is using write to send data:

  1. src/jsserial.c calls

    jstPinOutputAtTime(time, &inf->pinTX, 1, 1);

  2. src/jstimer.c

    bool jstPinOutputAtTime(....) {
    .....
    return utilTimerInsertTask(&task);
    }

  3. src/jstimer.c

    jshUtilTimerStart(utilTimerTasks[utilTimerTasksTail].time - jshGetSystemTime());

  4. targets/esp8266/jshardware.c

    void jshUtilTimerStart(JsSysTime period) {
    if (period<1) period=1; {
    utilTimerInit()
    hw_timer_arm((uint32_t) period);
    /*
    how to stop this and return?
    */
    }
    }

Question: how to code void jshUtilTimerStart(JsSysTime period) { } so make this work

code is available in branch ESP8266_hw_timer

@gfwilliams
Copy link
Member

What you're doing there looks promising (the period may need some scaling based on the timer speed I guess). Basically you want jshUtilTimerStart to call jstUtilTimerInterruptHandler after period.

IMO you might be better swapping the code for jshPinPulse over and then testing using digitalPulse(pin,1,100) - that's much simpler than the serial TX code and much easier to check.

@MaBecker
Copy link
Contributor Author

Thanks, pushed changes.

  • handle CPU_CLK_FREQ
  • added callback jstUtilTimerInterruptHandler
  • enabled hardware timer for digitalPulse / jshPinPulse

Anything else that should be added?

@gfwilliams
Copy link
Member

Great! Does it work? What about analogWrite(pin,0.5,{freq:100}) - does stuff like that work better now?

@MaBecker
Copy link
Contributor Author

MaBecker commented Oct 19, 2018

It does not reboot when sending more than one char or when running digitalPulse()

Have to check the Data with a logic analyser and scope.

Yes sure why not test pwm too.

@gfwilliams
Copy link
Member

I imagine waveform might work well too now as well!

Thanks for this - it's one of the really big things that ESP8266 has been missing.

@MaBecker
Copy link
Contributor Author

var s = new Serial();
s.setup(9600,{tx:D12,rx:D13});
s.write('Hello');

bildschirmfoto 2018-10-19 um 23 49 41

var s = new Serial();
s.setup(19200,{tx:D12,rx:D13});
s.write('Hello');

bildschirmfoto 2018-10-19 um 23 55 04

@MaBecker
Copy link
Contributor Author

E.setClock(80);
digitalPulse(D12,1,[1,2,3,4,5,6,7,8,9,10]);

bildschirmfoto 2018-10-20 um 00 03 59

E.setClock(160;)
digitalPulse(D12,1,[10,20,30,40,50,60,70,80,90,100]);

bildschirmfoto 2018-10-20 um 00 10 03

@MaBecker
Copy link
Contributor Author

analogWrite(D12,0.5,{freq:100})

bildschirmfoto 2018-10-20 um 00 42 28

analogWrite(D12,0.5,{freq:500})

bildschirmfoto 2018-10-20 um 00 44 00

@MaBecker
Copy link
Contributor Author

analogWrite(D12,0.5,{freq:500})

bildschirmfoto 2018-10-20 um 00 54 02

@MaBecker
Copy link
Contributor Author

analogWrite(D12, 0.5, {freq:10000});

bildschirmfoto 2018-10-20 um 11 47 12

@MaBecker
Copy link
Contributor Author

digitalPulse(D12,1,[0.25,0.25,0.25,0.1,0.1,0.1]);

bildschirmfoto 2018-10-20 um 12 03 16

@MaBecker
Copy link
Contributor Author

I think that‘s it.

@gfwilliams
Copy link
Member

That looks awesome - thanks! You happy to merge in what's there?

Please could you just delete the comments and #if 0 bit in jshPinPulse? I don't think there's any need for that now (and it'll be in git history anyway). Also maybe you could just delete utilTimerInit and call the hw_timer line direct? There doesn't seem to be any need for it now...

@MaBecker
Copy link
Contributor Author

Yep, lets clean up the code :)

@MaBecker
Copy link
Contributor Author

MaBecker commented Oct 22, 2018

Just one more, because it works so nice ;-)

analogWrite(D12, 0.5, {freq:500, soft:true});
analogWrite(D13, 0.5, {freq:1000, soft:true});
analogWrite(D14, 0.5, {freq:2000, soft:true});

bildschirmfoto 2018-10-22 um 20 37 49

@MaBecker
Copy link
Contributor Author

Gordon, I would say it's time to merge.

Cleaned code is pushed, will update CHANGELOG and docs after merge.

Many Thanks to Gordon for his support to make this work even he is not payed for his effort.

ESP8666 folks: If you where waiting for this feature please donate.

@MaBecker MaBecker changed the title Soft Serial on ESP8266 is broken Soft Serial on ESP8266 is broken because of missing hw_timer functionality Oct 22, 2018
@gfwilliams
Copy link
Member

Thanks for your work on this! This should make a really big difference to ESP8266's usefulness - especially with the soft PWM stuff.

@opichals
Copy link
Contributor

@MaBecker This is cool! Thanks!

This enables the ESP8266 as more reliable option with regards to following all kinds of the existing tutorials.

BTW @MaBecker based on your testing... did the @tve comments turn out to be irrelevant when used with the current SDK version?

@MaBecker
Copy link
Contributor Author

@opichals It's all gone, please check the latest version

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement ESP8266 This is only a problem on ESP8266 devices implemented
Projects
None yet
Development

No branches or pull requests

4 participants