Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 3 1 2 3 LastLast
Results 1 to 25 of 58

Thread: I2C, SPI, ESP webSockets controller for teensy

  1. #1
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716

    I2C, SPI, ESP webSockets controller for teensy

    Attached are the files for all 3 libraris/sketches


    SPI controller for multiple teensies: spi_controller.h
    Click image for larger version. 

Name:	teensyspi.jpg 
Views:	263 
Size:	101.9 KB 
ID:	12415

    I2C controller for multiple teensies: i2c_controller.h
    Click image for larger version. 

Name:	teensyi2c.jpg 
Views:	100 
Size:	84.1 KB 
ID:	12416

    ESP websockets controller for multiple teensies: teensywifi_controller.h
    Click image for larger version. 

Name:	teensyespwebsockets.jpg 
Views:	131 
Size:	96.3 KB 
ID:	12417

    For the SPI version, on the host, you may use any SPI0/1/2 of it, the slave must be using SPI0.

    Code:
    here are a few class initializers for the SPI version:
    spi_controller teensyUART = spi_controller("Serial", 43, &SPI2);
    spi_controller teensyUART1 = spi_controller("Serial1", 43, &SPI2);
    spi_controller teensyUART2 = spi_controller("Serial2", 43, &SPI2);
    spi_controller teensyUART3 = spi_controller("Serial3", 43, &SPI2);
    spi_controller teensyGPIO = spi_controller("GPIO", 43, &SPI2);
    spi_controller TWire = spi_controller("Wire", 43, &SPI2);
    spi_controller TWire1 = spi_controller("Wire1", 43, &SPI2);
    spi_controller TWire2 = spi_controller("Wire2", 43, &SPI2);
    spi_controller TSPI1 = spi_controller("SPI1", 43, &SPI2);
    spi_controller TSPI2 = spi_controller("SPI2", 43, &SPI2);
    spi_controller teensyEP = spi_controller("EEPROM", 43, &SPI2);
    spi_controller SERVO = spi_controller("SERVO", 43, &SPI2);
    spi_controller FASTLED = spi_controller("FASTLED", 43, &SPI2);
    first const char dipicts the object to control, 2nd is the CS pin #, the library automatically sets the pinMode and deasserts the CS line from the class constructor, so thats already handled automatically, 3rd reference is the SPI port.
    The commands should be self-explanatory:
    Ex.:
    Code:
    TWire1.beginTransmission();
    TWire1.write(0x00);
    TWire1.endTransmission();
    You have access to the slave's eeprom, digital pins, analog pins, spi busses, wire busses, all uarts, control servos and fastleds
    The same can be done with the I2C version, here's a few initializers:

    Code:
    i2c_controller S = i2c_controller("Serial", 0x04, &Wire);
    i2c_controller S1 = i2c_controller("Serial1", 0x04, &Wire);
    i2c_controller S2 = i2c_controller("Serial2", 0x04, &Wire);
    i2c_controller W2 = i2c_controller("Wire2", 0x04, &Wire);
    i2c_controller EE = i2c_controller("EEPROM", 0x04, &Wire);
    i2c_controller SV = i2c_controller("SERVO", 0x04, &Wire);
    i2c_controller FASTLED = i2c_controller("FASTLED", 0x04, &Wire);
    i2c_controller SPIport2 = i2c_controller("SPI2", 0x04, &Wire);
    const char object is the bus to control, the slave address of the slave (0x04), and the referenced bus it's attached to.

    the slave address is hardcoded on the MCU_SLAVE_CONTROLLER sketch, in the TEENSY_I2C_SETUP tab. --> Wire.begin(0x04);
    Changing it's value allows you to have multiple slaves on the bus.

    The SPI & I2C versions both are the same sketch code, so in the main sketch, choose which you want to use:
    Code:
    const bool spi_slave = 1;
    const bool i2c_slave = 0;
    choose one, or the other, NOT both.
    pin10 and 13 in SPI mode are released for the user to use, since SCK and CS were moved to 14 and 15 respectively, so toggling the led is at your disposal via:
    Code:
    whatever.digitalWrite(13,!whatever.digitalRead(13));
    For the Teensy & ESP8266 websockets version, it uses a single sketch for the ESP's and a single library for both teensies. It is designed as multi-master, and can even connect to each other and control each other's hardware. I also finished off adding user payload handler sending, where when it is modified (or not) at the other end, it will come back with the updated (or not) array in it's own handler. I also brought over some transparency for ESP control of both local and remote ESP's and even ESP standalone mode at the endpoint for control.

    Code:
    teensywifi_controller S1 = teensywifi_controller("Serial1", &Serial1, "192.168.2.168:80");
    teensywifi_controller S2 = teensywifi_controller("Serial2", &Serial1, "192.168.2.168:80");
    teensywifi_controller S6 = teensywifi_controller("Serial6", &Serial1, "192.168.2.168:80");
    teensywifi_controller EEP = teensywifi_controller("EEPROM", &Serial1, "192.168.2.168:80");
    teensywifi_controller W1 = teensywifi_controller("Wire1", &Serial1, "192.168.2.168:80");
    teensywifi_controller SRVO = teensywifi_controller("SERVO", &Serial1, "192.168.2.168:80");
    teensywifi_controller FASTLED = teensywifi_controller("FASTLED", &Serial1, "192.168.2.168:80");
    teensywifi_controller SPI2WIFI = teensywifi_controller("SPI2", &Serial1, "192.168.2.168:80");
    teensywifi_controller TeensyNet = teensywifi_controller("WIFI", &Serial1, "192.168.2.168:80");
    teensywifi_controller localesp = teensywifi_controller("LOCAL_ESP", &Serial1, "192.168.2.168:80");
    teensywifi_controller useher = teensywifi_controller("USERDATA", &Serial1, "192.168.2.168:80");
    teensywifi_controller eSerial1 = teensywifi_controller("ESP_Serial1", &Serial1, "192.168.2.175:80");
    teensywifi_controller ESP = teensywifi_controller("ESP", &Serial1, "192.168.2.168:80");
    teensywifi_controller eWire = teensywifi_controller("ESPWire", &Serial1, "192.168.2.168:80");
    teensywifi_controller eSPI = teensywifi_controller("ESPSPI", &Serial1, "192.168.2.168:80");
    teensywifi_controller eSerial = teensywifi_controller("ESP_Serial", &Serial1, "192.168.2.175:80");
    teensywifi_controller eFL = teensywifi_controller("ESPFASTLED", &Serial1, "192.168.2.168:80");
    
    teensywifi_controller Udp = teensywifi_controller("WiFiUdp", &Serial1);
    teensywifi_controller client = teensywifi_controller("WiFiClient", &Serial1);
    teensywifi_controller WiFiMulti = teensywifi_controller("WiFiMulti", &Serial1);
    teensywifi_controller WiFi = teensywifi_controller("WiFi", &Serial1);
    teensywifi_controller CONTROLLER = teensywifi_controller("ESP", &Serial1);
    the constructors with the IP given are the ones remotely to be controlled. the consts starting with ESP dipict ESP as standalone mode without a teensy attached, which frees up the comms port to use remotely
    the last few constructors without the IP are for controlling the local ESP
    You get the usual commands like:
    WiFi.config(ip, subnet, gateway);
    WiFi.connect("user","pass");
    Udp.beginPacket....
    and the WifiClient class as well
    client.read() etc

    I tried to make them as transparent as possible, with most of the libs.
    I plan to work on the MQTT version as it seems it's a better idea than this was when I originally started, so I'll be busy on that subject
    spi_controller-master.zipMCU_SLAVE_CONTROLLER.zip
    heres the libraries (single folder) and the SLAVE sketch for the SPI/I2C teensy slave

    ESPWIFI.zip
    and here is the ESP sketch for the esp's

    SPI in action: https://www.youtube.com/watch?v=llW59Pand44
    I2C in action: https://www.youtube.com/watch?v=eZ87QCAoBN4
    ESP in action: Fastled streaming: https://www.youtube.com/watch?v=rWOwf272Pl8
    ESP in action: Uart display streaming: https://www.youtube.com/watch?v=lHd-eY5bzCE
    ESP in action: teensy vs teensy, control each other's led toggling via !digitalread, digitalwrite: https://www.youtube.com/watch?v=RuPVjO1DDnY
    ESP in action: teensy running ESP's WiFiScan.ino demo with 3 line modification for library: https://www.youtube.com/watch?v=oK2DiuyKvjA

    wifiscan.ino demo below: ( red == modified/added )
    Code:
    /*
     *  This sketch demonstrates how to scan WiFi networks. 
     *  The API is almost the same as with the WiFi Shield library, 
     *  the most obvious difference being the different file you need to include:
     */
    #include <teensywifi_controller.h>
    teensywifi_controller WiFi = teensywifi_controller("WiFi", &Serial1);
    
    void setup() {
      Serial.begin(115200);
     Serial1.begin(1000000);
    
      // Set WiFi to station mode and disconnect from an AP if it was previously connected
      WiFi.mode(WIFI_STA);
      WiFi.disconnect();
      delay(100);
    
      Serial.println("Setup done");
    }
    
    void loop() {
      Serial.println("scan start");
    
      // WiFi.scanNetworks will return the number of networks found
      int n = WiFi.scanNetworks();
      Serial.println("scan done");
      if (n == 0)
        Serial.println("no networks found");
      else
      {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i)
        {
          // Print SSID and RSSI for each network found
          Serial.print(i + 1);
          Serial.print(": ");
          Serial.print(WiFi.SSID(i));
          Serial.print(" (");
          Serial.print(WiFi.RSSI(i));
          Serial.print(")");
          Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*");
          delay(10);
        }
      }
      Serial.println("");
      // Wait a bit before scanning again
      delay(5000);
    }

    Tony

  2. #2
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    Dependancies: to use fastled or servos, you need the latest fastled version off github, and for the ESP you need the GIT version and not the stock one from the IDE.
    If your using the ESP version, be sure to set your Serial1.begin(1000000) so it can communicate with it.
    for Fastled, you setup your array buffer only, never addleds.

    Code:
    #include <FastLED.h>
    #define NUM_LEDS    144
    CRGB leds[NUM_LEDS];
    after your done manipulating your CRGB array, you use:
    Code:
    FASTLED.show(23, leds, NUM_LEDS);
    where pin23 for example is the teensie's pin to offload your fastled array onto.

    as for the servo controls, you simply do:
    Code:
    SERVO.write(22, 25)
    Where 22 is the pin a servo is attached to, and 25 is the angle (values: 0-180)

    Note, watchdog on the slave runs in both i2c & spi versions, you must kick the dog to keep the slave happy, use at least one class object to kick the dog anywhere in your loop

    Code:
    TWire1.events();
    Last edited by tonton81; 12-28-2017 at 05:06 PM.

  3. #3
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    tonton81 - wondering about using this SPI with Teensy Master to Teensy Slave? I pulled down both SPI_controller zips and not stumbling into sample that would explain it to me.

    Would this provide a way to have a function to transmit 50 bytes from MASTER to SLAVE over SPI - just RAM to RAM? - and at what speed? - and could be incorporated into another sketch easily - not breaking other i2c/usb/Serial# activity?

    I see #ifdef for T 3.6 & 3.5 and transfer speeds of 4Mbit - does it work at 12 Mbit or faster?

    Are there other Threads or libraries that would do that any better - given that I don't need to feed them to a second bus?

  4. #4
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    the majority of the ports are opened on the slave, the master doesnt touch its own ports other than the one you assign to it for communication of the slave, last test over SPI i did between a T3.5/T3.6 had a SPISETTINGS of 75 000 000, not sure how realistic that was but both worked overclocked and stock. 4MBit is the default, running SPISettings overrides it

    i have not messed with memory storage locations? controller was meant for port access of all gpios/analogs/spibusses,i2cbusses,and uarts

    if theres plans to use the extra RAM in teensy I wouldn’t know where to start on adding a feature like that :P (unless we create a huge storage array and functions on the master to control it?)

  5. #5
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    That was quick. If I could clock out 50 bytes at 12 or 25 or 30 Mhz that would be great - better than Serial# The Slave would be a special purpose device just getting the data - binary data of a list of values - formatting to float and other and then sending out USB. Just looking to offload the formatting conversion and the expansion of <50 bytes of 'values' into over 133 bytes of csv text while it is busy accumulating and calculating the next set of number 100 or 1000 times per second to feed out to a PC for display.

  6. #6
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    i guess its possible (if we do an array storage buffer in ram and use custom functions on master end to access);

    i presume you’d format it within the same block unless your looking to store more blocks of storage for different results
    they could always be sent(data) over the usbserial of the slave as well using Serial.x

    if your printing directly to Serial.x USB serial on slave, you should be able to do that as is
    I just checked the constructor, I never added the SPI bus speed, it may be edited in the cpp file or just update SPISettings from your sketch setup()

  7. #7
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    The idea came up like this - perhaps some useful detail: uNav-AHRS.

    The uBlox PVT formatted GPS data string is a compact 100 byte linear group of bytes for values - easily pulled in inorder and then - confirmed accurate with checksum - then munged byte group by group into value by value.

    So a struct on MASTER would move to a same struct on SLAVE. Then the slave could have full CPU use to format printf strings to .print() to Serial/USB.

    The normal operations would update the struct, then call the XMIT function for SPI transfer and return - - with added 'ninja' help the Master could even set up a DMA transfer of a byte area and it would just need to update complete flags ( a counter in first byte? ) for each new data set the slave quickly/safely could parse.

    Currently the MASTER is getting overwhelmed in printf formatting and printing the copious strings on a fast update to feed a plotting viewer.

  8. #8
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    it is possible, but would need an idea for a return signal (unless not cared for)
    current implementation of library doesnt have DMA for slave
    we could add a function on the master that accepts a payload and length and that data would be dumped into the slave’s ram buffer over a single transaction

    once the data in the slave ram is loaded on slave the spi handler could also run your custom function (printf(usb))

  9. #9
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    That would be an interesting development. I think write only is good enough - though perhaps some pin change from slave could indicate needed feedback - though SPI data is one each way so it could send back a status signal - or if DMA a trigger for the next batch so it wasn't overrun with master updates while it was busy with the last update.

    Only issue is some users on that thread have their IMU on SPI IIRC. Time for rest.

  10. #10
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    yeah but retrieving the signal over the same transaction requires polling if the data wasnt processed fast enough,which is blocking, i think you want more of an asynchronous method, a pin change interrupt or polling the slave in cycles (not same transaction type of polling), regarding your center comment, we could also queue events to be processed if you dont really care about feedback, we could use an std::array & std::queue for FIFO bufferring a static array of 100 bytes that would be processed in sequence, (unless you want data being overridden as an alternative, then thats another type of implementation)

  11. #11
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    That code was being tested in TeensyThreads and now back to std loop()- in the middle of changes the output timing went awry and is now cleaned up, so this may happen but no longer urgent. It was stalling and failing at 120 MHz - now running okay at 48 MHz.

    I'll flesh it out with Serial# perhaps first. It would be fine async. - no feedback needed - it should just work and get skipped on failure is okay - no resend.

  12. #12
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    hmm its a good idea for an improvement to the SPI controller code to take advantage of the remote memory since weve got tons of bandwidth on the bus (as seen in past videos especially streaming 144leds data to fastled video while flooding 2 uart lcds at 650K baud), but we might need to think of ideas first and implementations, if we do go this route to add a feature as this, obviously it is going to be custom functions, but, we might need to go a bigger route, perhaps, give the user ability to add multiple dynamic arrays and do as he wishes with them, but more research will have to be done to improve it further than just printing ram to serial. although serialusb is accessible over the spi controller, i never tested printf, but, for the ahrs we could automate that into its own custom funtion for the remote to do EVERYTHING over a single call which will be send 100 bytes and run the functions specified as needed from slave end, so youd prolly only have to do a ahrs.send(buffer,len) or whatever you want to call it and the slave will do everything else on its end keeping your code cleaner. if you have no use for the watchdog you could always disable it as well, ive put it there for precautions but have never had the teensy fail to end up at that point of use, so events() wouldnt need to be called to feed the dog either.

  13. #13
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    then again, if no one really cares about using a memory map of a slave mcu, we could just offload local/global variables from master when needed for processing, which is what you want, although it would be nice to have both

  14. #14
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    Indeed - just an on demand byte transfer to a waiting slave would suit my purposes to start.

    It looks like my idea doesn't have a need just now though - after cleaning up a bit after the TeensyThreads distraction what wouldn't run right at 120 MHz does fine at 48 MHz and 240.

    I do have a second Teensy UART to UART for a spare debug port - that is optional but when there would be easy enough to test without the extra SPI wires. The numbers to print take over 400 uS to convert to float printable strings - doing that 71 Hz takes over 31 ms each second (at 168 MHz just now). So that overhead would go away assuming transferring less than half as much binary data might take the same amount of time. One 64 byte buffer can transfer over 3000 times each second at 2 Mbps.

  15. #15
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    well if we do that on the slave you could OC a T3.6 and run it at 240MHz to process those 100bytes
    but im a bit dumb on how people want things done i need like a step-by-step sometimes, like the exact cpmmands you want to run etc that i could implement in order to do this, be aware my terminology is obviously not of an expert and this is a hobby i wasnt taught to do other than learnt on my own, but the spi speeds will surely destroy the uart speeds while giving you full cpu support which hopefully helps on your project

  16. #16
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    That would be a promising addition and could be fun to actually make it work and learn about SPI - someday.

    Running at 168 MHz instead of 240 because the other guy is using a T_3.5 so trying to get comparable numbers, and that suggested to me his PC USB is running slower than mine.

  17. #17
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    slower? but if u both run same code then output should be same, i mean, does the pc discard data from the buffer as its too fast to cope with? regarding the 168vs 240 i meant the slave, not your mastrr, im pretty sure youd want to compare same specs with a slave helper to verify differences hehehe

  18. #18
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    do you have an exact command list you want to run on the slave if i were to work on it?
    (for slave)


    and the object/method of your choice?

    defrag.burst(array,len); // ?
    (for master)


  19. #19
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    This seems like it would suit the usage as I saw it. Not sure if it is complete enough - or open enough as you saw it?

    Post #7 links to the unpacking the uBlox GPS does as it parse()'s the string from the GPS once it is received.

    step 1: [setup() code]Request the byte array into struct with tonSPI.Slave_read( &NewDataStruct_IN, &DataReady_flag, DataDumpId ) : actually setup for an isr() in response to Master initiated send?
    step 1.1: Repeat for multiple DataDumpId's
    step 2: parse/copy raw data into proper struct based on DataDumpId : this could include responding to multiple DataDumpId's for alternate Struct layouts ?
    step 3: Optional validate data chksum? GPS incorporates a chksum, could just be having DataDumpId on both ends of transmitted struct
    step 4: Set DataReady_flag showing data arrived - or instead issue a callback to DataReady_func() that would just copy the data out of this 'shared' NewDataStruct_IN for processing and it would set a flag?
    step 5: wait and repeat at step 2 as new data arrives
    step 5.1: [loop() code] Perform the DataDumpId task - in this case format to desired output spec ( to feed program TViewer on USB of Slave Teensy )
    Code:
      cout.printf("%10.6f, %10.6f, %10.6f, %8.6f, %d",
               timeStamp*0.001, rtk.utcTime, IMU_RX_time*0.000001f,
               _dt, rtk.fixType);
      cout.printf("%12.8lf,%12.8lf,%10.4f", rtk.lat1, rtk.lon1, rtk.hMSL);
      cout.printf("%10.4f,%10.4f,%10.4f", rtk.velN, rtk.velE, rtk.velD);
      cout.printf("%10.4f,%10.4f,%10.4f", ypr[1], ypr[2], ypr[0]);
      cout.printf("%10.4f,%10.4f,%10.4f\n",aNEDx1, aNEDy1, aNEDz1);
    NOTES:
    > NewDataStruct_IN would be specific to DataDumpId, it would have a field of DataDumpId, and checksum field if used, the rest would be user defined to length based on DataDumpId.
    > This wouldn't be for generic task - but for specific tasks programmed into both ends with shared NewDataStruct_IN and predefined taskbased on DataDumpId
    > Setup on Slave end could include compile time buffer large enough for reading any expected DataDumpId as they may be different sizes and &NewDataStruct_IN pointers
    Last edited by defragster; 02-21-2018 at 05:12 AM.

  20. #20
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    i thought we were just going to send a variable array over and process it :P
    without polling or another way to the slave to talk back, the master has to initiate the call as it controls the clock, so unless you want to poll the state of your call i dont think callbacks will help/work, at that point you mightes just read the return value while polling for answer :/

    is this to be done 1 struct at a time per completion or consecutive? i could queue the dataid's that were successful in a response flag but you may over flow it if your sending more than you'll read back

    ok a bit of coffee helps

    tonSPI.Slave_read( &NewDataStruct_IN, &DataReady_flag, DataDumpId )

    reference to a struct, reference to a ready flag (set on the host i gather), and a dataID from host (like packet in mqtt world)
    you want the ready flag to be updated as well as the same dataID returned as a success indicator correct? im not sure how you can get the response without polling or setting interrupts on a pin to force your master to pull the data back, but, for both ways, an additional function can be used to check for the response, like tonSPI.Slave_check or whatever, and that will return the ready state (0/1) as well as the dataID(xxxx). So you could poll that function in the loop for the answer, or once a pin interrupt fires you could send a read request to the same function. I presume you also want to do this because the response wont be immediate? SPI is an awesome protocol, but I2C and SPI have their limitations of directions, so we have to figure out polling vs isr route, the rest is easy

    but if your not gonna wait for the transaction processing to finish in the same call, I also don't see how the dataready flag will be set on completion which makes that reference invalid use, the moment the function exits, it wont update the references, so we need to call again

    for the dataID, your using it for verification or? to see if the slave(when done) returns the same ID as you sent it?

    if thats the case, why not we do

    tonSPI.Slave_read(&NewDataStruct_IN, DataDumpId) // which pushes the DataReady_flag to 0 on slave since it's a call, while sending the struct and dataID to slave
    tonSPI.Slave_response(&DataReady_flag) // poll loop or ISR() read this function to get the response

    the SPI polling or ISR use will update your &DataReady_flag to 1 in your sketch and return the dataID after the spi transaction everytime you call it, so you know its a success. if it's not done, flag remains 0, and the ID as well, they are also reset to 0 as well when the Slave_read is called again to update new data

  21. #21
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    oh im so sorry guys, its been awhile since i went back to touch my spi_controller, i went to hook it up to my other setup and noticed, the slave's CS line is not 15, it's actually 2. so if you want it to work, follow the spi connection pic above but instead of pin15 being wired, re-route it to pin2, you should be able to run this test demo after over usbserial:

    Code:
    #include <SPI.h>
    #include <spi_controller.h>
    spi_controller teensyUART = spi_controller("Serial", 43, &SPI2);
    
    void setup() {
      SPI2.begin();
      teensyUART.begin(115200);
    }
    
    void loop() {
      static uint32_t _timer = millis();
      if ( millis() - _timer > 400 ) {
        _timer = millis();
        teensyUART.println("teensy");
        teensyUART.events(); // here is good 
      }
      // teensyUART.events(); --> not in loop(), if you dont feed the dog slower, he'll get mad
    }
    also disable the watchdog in the Slave end MCU_SLAVE_CONTROLLER.ino --> line 34 // teensy_watchdog_setup(); // enable watchdog

    host should be printing "teensy" to slave's usbserial monitor every 400ms if wired properly
    Last edited by tonton81; 02-20-2018 at 08:10 PM.

  22. #22
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    if you plan to keep watchdog support, DO NOT and I mean DO NOT poll the SPI with object.events(), the watchdog doesnt like being fed too fast, and it's set delay is 10 seconds as default. you can disable the watchdog in the slave if you dont want it at all, then you dont need to run events(), i chalked up a 400ms hit to the watchdog and its happy enough as an alternate i could drop in a millis timer that it can handle itself, but i have bigger project than the watchdog

  23. #23
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    Quote Originally Posted by tonton81 View Post
    ...

    ok a bit of coffee helps

    tonSPI.Slave_read( &NewDataStruct_IN, &DataReady_flag, DataDumpId )

    reference to a struct, reference to a ready flag (set on the host i gather), and a dataID from host (like packet in mqtt world)
    you want the ready flag to be updated as well as the same dataID returned as a success indicator correct? im not sure how you can get the response without polling or setting interrupts on a pin to force your master to pull the data back, but, for both ways, an additional function can be used to check for the response, like tonSPI.Slave_check or whatever, and that will return the ready state (0/1) as well as the dataID(xxxx). So you could poll that function in the loop for the answer, or once a pin interrupt fires you could send a read request to the same function. I presume you also want to do this because the response wont be immediate? SPI is an awesome protocol, but I2C and SPI have their limitations of directions, so we have to figure out polling vs isr route, the rest is easy

    but if your not gonna wait for the transaction processing to finish in the same call, I also don't see how the dataready flag will be set on completion which makes that reference invalid use, the moment the function exits, it wont update the references, so we need to call again

    for the dataID, your using it for verification or? to see if the slave(when done) returns the same ID as you sent it?

    if thats the case, why not we do

    tonSPI.Slave_read(&NewDataStruct_IN, DataDumpId) // which pushes the DataReady_flag to 0 on slave since it's a call, while sending the struct and dataID to slave
    tonSPI.Slave_response(&DataReady_flag) // poll loop or ISR() read this function to get the response

    the SPI polling or ISR use will update your &DataReady_flag to 1 in your sketch and return the dataID after the spi transaction everytime you call it, so you know its a success. if it's not done, flag remains 0, and the ID as well, they are also reset to 0 as well when the Slave_read is called again to update new data
    Quick first scan - need to be on my way ... Glad you kept going after the coffee

    Post #19 was the process on the SLAVE - all slave internal. With hardware connected and ready the slave code would be ready for those steps ... you did ask

    On the Master end it would take the needed steps to take a packet from user code and queue it to transmit to mate with the slave's expectations for those #'d steps.

    Step #1 is 'Class Init' - or registration {like TeensyThreads initializing threads} of expected events to be handled. If the Master presents a DataDumpId that is not registered it can go in the 'bit bucket' to get the Master back on track. For debug the slave could keep a list of unknown DataDumpID's ( and lengths ?) that were not known - or corrupted and failed checksum/verify.

    After Step #1 registration of 1 or more expected DataDumpID's the Slave goes and loop(){twiddles.thumbs(); } until tonSPI gets an SPI packet.

    There is no 'waiting' - Packets will show up 'randomly' and the slave may have other tasks - or multiple packets to handle differently - one may go out USB another may go out Serial#1 in some fashion to any other device or code that needs to be run.

    When a packet arrives worthy of sharing tonSPI can set a flag that is polled in twiddles.thumbs(), or tonSPI could be proactive and do a callback saying: copy this data before I overwrite it with the next msg, and set your own flag. Or if desired the twiddles.thumbs() code could poll tonSPI.DataReady( DataDumpID ); But if a callback was honored - it could make sure to clear that data from that shared *struct so the next message would not trash it, even if the slave was busy getting the next digit of Pi or something.

    Let me know if that clears anything up - or made it worse - ...

  24. #24
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,716
    ok on post #19 the only issue is the slave_read happening if that code is on slave end, the slave cant call the master and tell it to send data, at least not from the same SPI port. queuing shouldnt be an issue, especially with static arrays, ive learned abit about std::array and std::vectors enough to implement a vector array queue for callback data of variable lengths, so even if you polled (from the master), your data would be dequeued in a FIFO fashion, so making sure to clear data from the shared struct (while queuing it) without next message trashing it is possible (if you wanted to save it and not discard it)

  25. #25
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,788
    I was assuming the Master><Slave connect would just be there. The Slave would just wait. Perhaps the master could send a default heartbeat 'online' DataDumpID.

    The slave in my plan was just passive receiver. Acts when it gets a message. Step 1 was internally setting up the slave tonSPI telling it what to accept from the master, and giving it the shared memory to return those messages and the way to notify loop()/callback it arrived.

    Just edited to: step 1: [setup() code]Request the byte array into struct with tonSPI.Slave_read( &NewDataStruct_IN, &DataReady_flag, DataDumpId ) ...

    Does the slave not have a way to emit anything on the MISO line? Or at least pull it low when it detects CS? The master can run just fine without an active slave, and the slave can wait forever with no master sending messages. The Master and Slave could be connected over Serial easily enough - but that may not be there either.

    FIFO or data storage could be useful, I was assuming the slave would preserve the data - in the callback if provided - and free tonSPI to reuse the same pointer for the next message. That would make the tonSPI simple and robust - and leave data storage to the client code. The setup() call could provide *ptr to an array of structs of known length it could cycle through to handle burst traffic - and if it overwrites ... so be it. The client could zero each element struct first and last DataDumpId bytes to show/know they are processed - but in the end the client managing data keeping the tonSPI stable and reliable would make it easiest to get running.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •