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

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

MaBecker opened this issue Sep 16, 2018 · 32 comments

Comments

@MaBecker
Copy link
Contributor

@MaBecker MaBecker commented Sep 16, 2018

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');
@wilberforce
Copy link
Member

@wilberforce wilberforce commented Sep 17, 2018

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

@MaBecker MaBecker commented Sep 17, 2018

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

@gfwilliams gfwilliams commented Sep 18, 2018

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 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 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

@MaBecker MaBecker commented Sep 19, 2018

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

@MaBecker MaBecker commented Oct 13, 2018

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

@MaBecker MaBecker commented Oct 16, 2018

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

@MaBecker
Copy link
Contributor Author

@MaBecker MaBecker commented Oct 16, 2018

Got the jsiConsolePrintf active.

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

@gfwilliams
Copy link
Member

@gfwilliams gfwilliams commented Oct 16, 2018

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

@MaBecker MaBecker commented Oct 17, 2018

Cool, starting to work on this.

I'm curious how far I will get.

@MaBecker
Copy link
Contributor Author

@MaBecker MaBecker commented Oct 18, 2018

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

@gfwilliams gfwilliams commented Oct 18, 2018

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

@MaBecker MaBecker commented Oct 19, 2018

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

@gfwilliams gfwilliams commented Oct 19, 2018

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 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

@gfwilliams gfwilliams commented Oct 19, 2018

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

@MaBecker MaBecker commented Oct 19, 2018

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

@MaBecker MaBecker commented Oct 19, 2018

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

@MaBecker MaBecker commented Oct 19, 2018

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

@MaBecker MaBecker commented Oct 19, 2018

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

bildschirmfoto 2018-10-20 um 00 54 02

@MaBecker
Copy link
Contributor Author

@MaBecker MaBecker commented Oct 20, 2018

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

bildschirmfoto 2018-10-20 um 11 47 12

@MaBecker
Copy link
Contributor Author

@MaBecker MaBecker commented Oct 20, 2018

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

@MaBecker MaBecker commented Oct 21, 2018

I think that‘s it.

@gfwilliams
Copy link
Member

@gfwilliams gfwilliams commented Oct 22, 2018

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

@MaBecker MaBecker commented Oct 22, 2018

Yep, lets clean up the code :)

@MaBecker
Copy link
Contributor Author

@MaBecker 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

@MaBecker MaBecker commented Oct 22, 2018

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

@gfwilliams gfwilliams commented Oct 23, 2018

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

@opichals opichals commented Oct 23, 2018

@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

@MaBecker MaBecker commented Oct 23, 2018

@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
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.