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

Software Serial #549

Closed
gfwilliams opened this issue Jun 30, 2015 · 48 comments
Closed

Software Serial #549

gfwilliams opened this issue Jun 30, 2015 · 48 comments

Comments

@gfwilliams
Copy link
Member

Shouldn't be too difficult, especially for lower baud rates. Software SPI is already there, and we can execute a user-defined function from inside a watch IRQ.

DrAzzy on the forums definitely wants it anyway.

@gfwilliams
Copy link
Member Author

Also: this could be a way to get Serial that works from within deep sleep.

@gfwilliams
Copy link
Member Author

Software I2C looks trivial, and could be implemented in the same way as software SPI.

@HyGy
Copy link

HyGy commented Jan 13, 2016

This UART will be good to implement to support http://wiki.iteadstudio.com/Nextion_HMI_Solution can somebody implement please?

@tve maybe? :)

@gfwilliams
Copy link
Member Author

I think we ought to have a bounty system. How much do you want it... enough to pay for the time we spend on it? :)

@HyGy
Copy link

HyGy commented Jan 13, 2016

Do it need to implement in javascript? If yes, then can u please help me to start it? (I promiss nothing, but maybe a try... I don't know how good now in low level things, this is why I like espruino couse its js :) and higher level language )

@profra
Copy link

profra commented Jan 13, 2016

... bounty system... It is not bad idea... I am ready to contribute... You can try to begin the discussion in the News... but I have no idea about the total price for such task...
I have asked for SW UART already in the times of Kolban...
...I have been just playing with Lua and WiFiMCU (EMW3165) and there is possible to emulate UART on whatever two pins (TX, RX)...but it is clear, it is another HW platform where you have sufficient quantity of timers for easy implementation and other sources...I am not able to review the possibility to implement it on ESP8266... maybe @tve
.some inspiration for SW UART you can find in the source of WiFiMCU... here..
..https://github.com/loboris/MICO/blob/master/Projects/WiFiMCU/LUA/lua/exlibs/uart.c...
...IMO it has to be written in C

@gfwilliams
Copy link
Member Author

Well, to do it properly you'll need to do it in C. However if you just want to send a few characters you can use JavaScript and digitalPulse, at least for stuff at slow-ish speeds like 9600 baud.

All you need to do is keep the output at 1 usually, then you can use digitalPulse(pin, 0, array) to create an RS232 waveform for each byte.

@HyGy
Copy link

HyGy commented Jan 13, 2016

I think C is hard for me now.

Here is some js implementation but it does not work yet. Thanks for yerpj user from gitter.

Now I just send 'a' ascii char what i think is 01100001.

what i get (the first array) (the comparable array is the second array):

RX done. you got each bit in the RXByte array
[ 0, 0, 0, 1, 1, 1, 1, 1 ]
[ 0, 1, 1, 0, 0, 0, 0, 1 ]
require("ESP8266").logDebug(false);


RX=new Pin(0); // ~D3 port;
//var bitTime=3.333; // for 300 BAUD
//var bitTime=1.666; // for 600 BAUD
var bitTime=0.833;//for 1200 BAUD
//var bitTime=0.104;//for 9600 BAUD
var bitCounter=0;
var RXByte=[];
var asciia=[0,1,1,0,0,0,0,1]; // for comparing with the sent a


var bitSampler = function(){
  setTimeout(function(){
    var currVal=digitalRead(RX);
    RXByte[bitCounter]=currVal;

    bitCounter++;
    if(bitCounter==8)
    {
      bitCounter=0;
      waitForFirstByte();
      console.log("RX done. you got each bit in the RXByte array");
      console.log(RXByte);
      console.log(asciia);

    }
    else
      bitSampler();
  },bitTime);
};//a full bitTime to wait. See comment below

var waitForFirstByte = function()
{

  setWatch(function(){
//    console.log('falling down edge detected');
       setTimeout(function()
       {
          var firstDataByte=digitalRead(RX);
          RXByte[bitCounter]=firstDataByte;
          bitCounter++;
          bitSampler();
       },bitTime/2);
    //bittime/2 first, as you want to sample it at mid-time

  },RX,{repeat:false,edge:'falling'});

};

waitForFirstByte();

what is wrong? :) Maybe @gfwilliams? :)

@gfwilliams
Copy link
Member Author

Espruino isn't fast enough to use the timeout to poll. Try using setWatch.

This works on an Espruino Pico, although it's not good at handling a whole series of bits - it's just too slow to process it.

This is why you'd ideally handle it in C.

Serial1.setup(1200);
function w(a) { Serial1.write(a); }
var RX = A8; // connecting B6 at A8

var BAUD = 1200;
var data = "";
var timeout;

function hasChar(c) {
  console.log("Got "+c+" -> "+String.fromCharCode(c));
}

function charTimeout() {
  timeout = undefined;
  data = data+"11111111";
  hasChar(E.reverseByte(parseInt(data.substr(1,8),2)));
  data = "";
}

setWatch(function(e){
  if (timeout) clearTimeout(timeout);
  timeout = undefined;
  var d = 0|(0.5+((e.time - e.lastTime)*BAUD));
  if (d>10) { d=0; }
  var b = e.state?"0":"1";
  while (d--) data+=b;
  if (data.length>=10) {
    hasChar(E.reverseByte(parseInt(data.substr(1,8),2)));
    data = "";
  }
  if (data.length) timeout = setTimeout(charTimeout, 10000/BAUD);
},RX,{repeat:true,edge:'both'});

w("Hello World");

@profra
Copy link

profra commented Jan 27, 2016

Full ready SW serial in C language for 8266 is here ...
... https://github.com/plerup/espsoftwareserial/blob/master/SoftwareSerial.cpp
... it would be nice to have it but implementation depends on @tve

@gfwilliams
Copy link
Member Author

IMO it'd be much better if someone would actually just write this for Espruino. It'd then work for all Espruino devices, not just ESP8266.

Same with I2C - there's a SW I2C implementation currently for ESP8266, but it's only for ESP8266 so it's useless for any other board that runs Espruino :(

@profra
Copy link

profra commented Jan 27, 2016

I absolutely agree... it would be really nice to have it (SW - I2C, SPI, serial) compatible on both platforms... but I am not able to write it 👎

@yerpj
Copy link
Contributor

yerpj commented Feb 2, 2016

I tested a RX method to recover each incoming byte. Current limitation: need about 6ms to compute the byte after EACH incoming byte.
This method was successfully tested at 9600 & 115200 Baud.

//SW UART
//Jean-Philippe Rey, 02.02.2016
//This code only implements the RX side of UART
//currently, every recorded byte is thrown to the console, as well a the computing time

var Baudrate=9600;
var bitTime=1/Baudrate;
var halfBitTime=bitTime/2;
var ByteDuration=9*bitTime;//8 bits of Data + 1 START bit

var computingTime=0;
var RX=C11;
var edges=[];
var lastEdge=0;
var edgeTime;
var RXStarted=false;
var RXStartTime=0;
var Byte=0;
var lastBit=false;
var timeOffset=0;

function RXhandler(edges){
  computingTime=getTime();
  edges[0]+=bitTime;//remove the START bit
  for(var i=0;i<8;i++)
  {
    if( (i*bitTime+halfBitTime)>(edges[1]-edges[0]+timeOffset) )
    {
      if(lastBit==false)
        Byte+=0x01<<(i);
      timeOffset+=edges[1]-edges[0];
      edges.shift();
      lastBit=!lastBit;
    }
    else
    {
      if(lastBit==true)
        Byte+=0x01<<(i);
    }
  }
  RXStarted=false;
  timeOffset=0;
  console.log(Byte.toString(16));//<<HERE you have your data byte
  Byte=0;
  computingTime=getTime()-computingTime;
  console.log("computing time: "+computingTime+"[sec]");
}

setWatch(function(e){
  edgeTime=e.time;
  if(!RXStarted)
  {
    RXStarted=true;
    edges=[];
    setTimeout(function(){RXhandler(edges);},ByteDuration);
  }
  edges.push(edgeTime);
},RX,{repeat:true,edge:'both'});

Maybe this could help people willing to tinker with SW UART.

@yerpj
Copy link
Contributor

yerpj commented Feb 2, 2016

If anyone wants to write a dedicated module for a SW UART, I would be pleased to contribute. However, as I am having holiday until february 27th (it means I would be AFK), I would help as soon as I come back.

@gfwilliams
Copy link
Member Author

So was there some problem with the receive code that I posted up above? From the look of it, it probably needs less time per byte for calculations?

@yerpj
Copy link
Contributor

yerpj commented Feb 2, 2016

Actually I did not see your implementation :-/ Otherwize I would not have taken time to write it by myself :-D @HyGy said that there weren't any implementation able to decode up to 9600 Baud, this is the reason why I tried to implement something. I was able to decode up to 115200 Baud (did not try faster Baudrate, as they are very unusual). That said, It seems that your implementation is efficient enough to support 9600 Baud. Did you already try?

@HyGy
Copy link

HyGy commented Feb 2, 2016

Sorry I missed this to, I'll test both now.

But I think the best to implement it in c. But I cannot. :(

@gfwilliams
Copy link
Member Author

Ahh, that's a shame. I was a bit surprised there was no response from @HyGy after I'd written it :)

Yes, it'll do 9600, although IIRC maybe only a few characters one after the other. It needs some time to process things too. I guess the 'Compiled Code' could help with that.

@HyGy
Copy link

HyGy commented Feb 2, 2016

I tested @gfwilliams code first, from a separate USB ttl device.

In one turn @gfwilliams code can receive 21 characters each other at 9600bps. In higher speeds the success caracters is less.

@HyGy
Copy link

HyGy commented Feb 2, 2016

@gfwilliams I connected the nextion display, it sends less bytes, so it seems it can work with this code. Now I need just the send code, and then I try to create a lib for this nextion display.

@gfwilliams
Copy link
Member Author

You should be able to use digitalPulse(pin, 0, array_of_times) to output the waveform. Hopefully it won't be that hard.

I'd said elsewhere (it seems not on here) but there is now a branch for software serial in Espruino itself. It's not entirely working yet, but is a good start. Not sure when I'll get time to finish it though.

@HyGy
Copy link

HyGy commented Feb 2, 2016

@gfwilliams where can I watch this new implementation?

@gfwilliams
Copy link
Member Author

Using the link above? That is the branch so any changes will appear on there. You can see what commits were made to see the changes I made.

@HyGy
Copy link

HyGy commented Feb 2, 2016

So I need to checkout the c source code, and compile it?

@gfwilliams
Copy link
Member Author

Yes, or the latest binaries for the Espruino boards are here: http://www.espruino.com/binaries/git/commits/software_serial/

@MaBecker
Copy link
Contributor

Got that branch, increase number of uart in the board file

how to bind that uart to soft serial on esp ?

@gfwilliams
Copy link
Member Author

It just jams the data into Serial6 at the moment. I think:

var s = new Serial();
s.setup(9600, { rx : .. , tx : .. });
s.write("Whatever"); // Timing not quite right
Serial6.on('data', function(d) { ... });

@MaBecker
Copy link
Contributor

hmm, using this code on esp causes reboot as expected

can get Serial2 by increasing the number of uarts in the board config file

but no idea how to bind the serial code in jswrap_serial.c to that Serial2 on ESP

@davidmoshal
Copy link

davidmoshal commented May 7, 2016

Any update on this?
Am trying to get a Wii Nunchuck to work:
http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/

I have 4.7k pullups on SDA and SCL, and have been trying numerous variations along this theme:

I2C1.setup({scl:4,sda:5});
var wii = require("wii_nunchuck").connect(I2C1);

setInterval(function(){
  console.log(JSON.stringify(wii.read()));
}, 2000);

Dave
ps: the device does work on a Pico

@davidmoshal
Copy link

my wii_nunchuck.js file is this, is it current?

exports.connect = function(/*=I2C*/_i2c) {
  var i2c = _i2c;
  // initialise
  i2c.writeTo(0x52, [0xF0,0x55]);
  i2c.writeTo(0x52, [0xFB,0x00]);
  // actual object
  return { read : function () {
    var d = i2c.readFrom(0x52, 6);
    i2c.writeTo(0x52, 0);
    // TODO: we could get another 2 bits of accelerometer data from d[5]
    return { joy : {x:(d[0]-127)/128,y:(d[1]-127)/128},
            acc : {x:(d[2]-127)/54,y:(d[3]-127)/54,z:(d[4]-127)/54},
            btn : {z:!(d[5]&1),c:!(d[5]&2) }
           };
  } };
};

@davidmoshal
Copy link

Based on the Espruino Design notes , I have pullups:

I2C Implementation

The I2C interface is implemented in software because the esp8266 does not have hardware support for i2c (contrary to what the datasheet seems to imply). The software implementation has the following limitations: it operates at approx 100Khz, it is master-only, and it does not support clock stretching (a method by which slaves can slow down the master). The I2C interface can be bound to almost any I/O pin pair, but you should avoid gpio15 because it needs to be pulled-down at boot time and the I2C bus needs pull-up resistors. The pins chosen for I2C are configured to be open-drain outputs and an external pull-up resistor is required on each of the two pins. Remember that esp8266 pins are not 5v compatible...

Based on Thorsten's advice from gitter, I've tried GPIO12/13:

I2C1.setup({scl:12,sda:13});
var wii = require("wii_nunchuck").connect(I2C1);
setInterval(function(){
  console.log(JSON.stringify(wii.read()));
}, 500);

Based on this wii nunchuck blog I'm using 3.3V

Not sure what else to try.
Should I be using an arduino as an I2C to serial adapter?
Seems a pity if this functionality is available on the ESP8266 espruino builds, no?

@tve
Copy link
Contributor

tve commented May 7, 2016

I2C definitely works. It has some quirks, it may be too fast, etc. But I'm using it successfully with multiple devices. You're not providing any info other than "it doesn't work". Do you have a $10 logic state analyzer that you can hook up to record the i2c transactions? Or how about instrumenting the module to record which i2c transactions work and what responses they get?

@davidmoshal
Copy link

@tve sorry, I've been trying to debug in Gitter with MaBecker.
I don't have logic state analyzer - I'm approaching Espruino from the Javascript side, so that's not something I have any experience with ... yet.
What logic analyzer do you recommend, and what's the most efficient route for a JS developer to get proficient with the C side to be able to contribute meaninfully to your project. I'm imaging there is a canonical 'C for embedded devices' manual, or equivalent?

@davidmoshal
Copy link

davidmoshal commented May 8, 2016

@tve after giving this issue of serial device connectivity more thought I'm thinking of going a different route.

If we take a step back and look at this Serial connectivity issue more broadly, we have:
a) A plethora of sensors and actuators that already have a robust C / Arduino library ecosystem.
b) A user base that comes from a completely different ecosystem (Javascript, browsers, etc)
c) Very limited developer resources (and no active bounty system, no corporate sponsorship, or other compensation mechanism) to implement dozens of serial device interfaces, and
d) a target platform (in the case of ESP8266) that can only support at best 1 x ADC pin and 1 x I2C serial device, even if all these libraries were created (at least with the current iteration of hardware).

So, at least for me, instead of learning how to debug using a logic analyzer etc., it makes more sense to accept that there is going to have to be some sort of Arduino device between the ESP and these serial devices. (basically an external version of the ESP-14).

This would have several advantages:

  • JS developers like me would have a reason to get up to speed on the Arduino/C ecosystem.
  • It could be much quicker to get the I2C devices working, assuming the Arduino ecosystem has more libraries.
  • We'd have a short-term path to supporting multiple simultaneous serial devices, of many different protocols.
  • We'd have a route to the sensors/actuators for other JS platforms like Johnny5, Cyclonjs, etc.

So, this leaves the question of how best to network these devices.
Probably JS developers would be comfortable with something like MQTT.
Thoughts? Is this something I should pursue for the community?

David

update: looks like someone has already done a lot of work on the Arduino side:
http://tuanpm.net/rock-solid-esp8266-wifi-mqtt-restful-client-for-arduino/

@davidmoshal
Copy link

davidmoshal commented May 9, 2016

From the Espruino ESP docs it seems like SPI is the best available hardware-implemented serial protocol for used by Espruino on ESP-12 based boards.

  • I2C is implemented in software, is limited to 100kHz (and has issues).
  • the second UART according to your docs doesn't support RX, so UART is out unless you reconfigure the console via loopback (whatever that means).

So, that leaves SPI as the 'native' hardware-implemented serial protocol, no?

@gfwilliams
Copy link
Member Author

A lot of people have had I2C working on ESP8266 - are you sure you have the right pins, as you seem to be using NodeMCU but just specifying the pins as numbers? You could use NodeMCU.D0/etc.

This bug really isn't the place to discuss reimplementing some firmata-style peripheral interface (or I2C connection problems for ESP8266) - maybe you could post on the forum?

But if there is a problem with I2C, it's software so it should be fixable - trying to work around it by connecting another microcontroller seems like massive overkill when fixing the underlying problem is probably quite easy once we know what the problem is.

@davidmoshal
Copy link

are you sure you have the right pins, as you seem to be using NodeMCU but just specifying the pins as numbers? You could use NodeMCU.D0/etc.

I've tried many different combinations, e.g: D1/D2, which do you suggest?

This bug really isn't the place to discuss reimplementing some firmata-style peripheral interface (or I2C connection problems for ESP8266) - maybe you could post on the forum?

Sorry about posting in the wrong place, the earlier comments on this issue reference several serial protocols (SPI, UARTs, pulsing digital IO, etc), so I got the impression that this issue concerned connecting ESP-8266 to various serial peripherals in general. My bad.

trying to work around it by connecting another microcontroller seems like massive overkill when fixing the underlying problem is probably quite easy once we know what the problem is.

Sure, though one does wonder if the ESP-14 approach of adding an STM8 is indeed the best way to solve the problem. At least for me that would be an easier approach than learning how to use a logic analyzer!

@gfwilliams
Copy link
Member Author

As long as you figured out that D1 != NodeMCU.D1 :) It's software, so should work on any pins as long as they're not used for anything else (like serial).

Problem is, instead of just using the Web IDE, then users have to install and learn the Arduino IDE, how to wire the 2 chips, and then also how to make an API to communicate nicely between the two. Suddenly instead of being nice and easy (if everything works) it turns into something very difficult.

This bug was initially about adding software-based peripherals to Espruino itself (so shared between all boards) - although yeah, it has been driven by ESP8266 users who want extra communication ports.

@davidmoshal
Copy link

davidmoshal commented May 9, 2016

As long as you figured out that D1 != NodeMCU.D1 :) It's software, so should work on any pins as long as they're not used for anything else (like serial).

That part is still a bit confusing to me (i.e: the ESP8266 documentation, such as it is, is confusing), I tried all permutations - @MaBecker helped me significantly (much appreciated), and I'm sure that we are hitting the wii nunchuck peripheral.

Suddenly instead of being nice and easy (if everything works) it turns into something very difficult.

True, but if it doesn't work, and you have 20+ quantity each of a couple dozen different peripherals already ordered, and a class of kids eager to use them all ....

I ordered a Bus Pirate and also a logic analyzer to try and debug this. My gut still tells me that I'm going to end up ditching the software I2C, and bridging the hardware SPI to I2C at some point.
I'm new to C, Arduino, embedded devices, as I'm approaching this from the JS side - that's probably true for many Espruino users too I'd imagine, so hopefully all of this will be of help to the community.

This bug was initially about adding software-based peripherals to Espruino itself (so shared between all boards) - although yeah, it has been driven by ESP8266 users who want extra communication ports.

Got it, that explains my confusion, I thought this was ESP8266 specific.
Espruino hardware has been brilliant, no problems connecting the Pico to any peripherals that i have tried, including the wii nunchuck, worked flawless first time (which is how I know that this peripheral actually works).

@iharris
Copy link

iharris commented May 9, 2016

An i2c / logic analyser is pretty cheap and probably the best money you
will ever spend doing embedded development. Trying this sort of stuff
without one is a bit like trying to fly a plane blind folded. It just might
work, but you have to be pretty lucky if you can't see what's happening.

Ian.

On 9 May 2016 at 14:26, davidmoshal notifications@github.com wrote:

As long as you figured out that D1 != NodeMCU.D1 :) It's software, so
should work on any pins as long as they're not used for anything else (like
serial).

That part is still a bit confusing to me (i.e: the documentation such as
it is is confusing), I tried all permutations - @MaBecker
https://github.com/MaBecker helped me significantly (much appreciated),
and I'm sure that we are hitting the wii nunchuck peripheral.

Suddenly instead of being nice and easy (if everything works) it turns
into something very difficult.

True, but if it doesn't work, and you have 20+ quantity each of a couple
dozen different peripherals already ordered, and a class of kids eager to
use them all ....

I ordered a Bus Pirate and also a logic analyzer to try and debug this. My
gut still tells me that I'm going to end up ditching the software I2C, and
bridging the hardware SPI to I2C at some point.
I'm new to C, Arduino, embedded devices, as I'm approaching this from the
JS side - that's probably true for many Espruino users too I'd imagine, so
hopefully all of this will be of help to the community.


You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub
#549 (comment)

@davidmoshal
Copy link

@iharris Thanks Ian, a link to a good starting tutorial would be appreciated.

Dave

@davidmoshal
Copy link

or not, just saying...

@iharris
Copy link

iharris commented May 11, 2016

Hi David,

Funnily enough, there's not much do it. You connect the SDA/SCL pins to
the analyser, and get it to capture while you do things - then look at the
results.

Hackaday shows the Salae analyser working here:
http://hackaday.com/2009/03/06/tools-saleae-logic-logic-analyzer/

Once you can see, for example, what address you're sending, and how the
device responds, it becomes clearer what is going on. You can capture a
working transaction and then compare it to your non-working transaction,
which is what I'd do in your case.

We had one our Embedded Adventures devices working on Arduino, but not on
Raspberry Pi. So we captured both and found that even though the address we
were sending was the same, the i2c libraries on both platforms were
treating it differently (one shifting it left one bit, the other leaving as
is). Much easier to fix by seeing what's happening on the wire.

kind regards
Ian.

On 11 May 2016 at 01:50, davidmoshal notifications@github.com wrote:

or not, just saying...


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#549 (comment)

@davidmoshal
Copy link

Thanks Ian, much appreciated!
will try it out when the device arrives.
David

gfwilliams added a commit that referenced this issue Sep 23, 2016
            Make OneWire use opendrain_pullup (no resistor needed for short runs now)
            Add Software I2C (with opendrain_pullup) (ref #549, fix #29)
@gfwilliams
Copy link
Member Author

Software I2C libraries are in there now. It's just serial to worry about

@gfwilliams gfwilliams changed the title Software Serial (also I2C?) Software Serial Nov 24, 2017
@MaBecker
Copy link
Contributor

MaBecker commented Dec 5, 2018

Software serial is in too. Time to close?

@gfwilliams
Copy link
Member Author

Thanks for the prod :)

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

8 participants