cnlohr / esp8266oddclock

What happens when you rip the ESP8266's stable clock source out from under it's feet?
81 stars 13 forks source link

Arduino Addition #3

Open ranlyticsBrad opened 5 years ago

ranlyticsBrad commented 5 years ago

Adding your code to an Arduino sketch like so. Easier to play about for us Arduino fan boys. And using wifi examples one can quickly test its functionality.

void pico_i2c_writereg_asm(uint32_t a, uint32_t b)
{
   asm volatile (".global pico_i2c_writereg_asm\n.align 4\npico_i2c_writereg_asm:\n_s32i.n  a3, a2, 0\n_memw\n_l32i.n a3, a2, 0\nbbci  a3, 25, .term_pico_writereg\n.reloop_pico_writereg:\n_memw\n_l32i.n a3, a2, 0\nbbsi  a3, 25, .reloop_pico_writereg\n.term_pico_writereg:\n_ret.n");

}
#define pico_i2c_writereg( reg, hostid, par, val ) pico_i2c_writereg_asm( (hostid<<2) + 0x60000a00 + 0x300, (reg | (par<<8) | (val<<16) | 0x01000000 ) )

void setup() {
  pico_i2c_writereg(103,4,1,0x48);  
  pico_i2c_writereg(103,4,2,0xf1);
}
cnlohr commented 5 years ago

Has this been fully tested? If so, I wouldn't be opposed to posting this on the readme.

ranlyticsBrad commented 5 years ago

Yeah, works on all wifi examples in the Arduino esp8266 section.

Serial I set to 57600*2.5 so the standard 57600 can be used on the terminal. I've tested this in all the examples and they all work.

The mesh one is a good tester.

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMesh.h>

String exampleMeshName("MeshNode_");

unsigned int requestNumber = 0;
unsigned int responseNumber = 0;

String manageRequest(const String &request, ESP8266WiFiMesh &meshInstance);
transmission_status_t manageResponse(const String &response, ESP8266WiFiMesh &meshInstance);
void networkFilter(int numberOfNetworks, ESP8266WiFiMesh &meshInstance);

/* Create the mesh node object */
ESP8266WiFiMesh meshNode = ESP8266WiFiMesh(manageRequest, manageResponse, networkFilter, "ChangeThisWiFiPassword_TODO", exampleMeshName, "", true);

void pico_i2c_writereg_asm(uint32_t a, uint32_t b)
{
   asm volatile (".global pico_i2c_writereg_asm\n.align 4\npico_i2c_writereg_asm:\n_s32i.n  a3, a2, 0\n_memw\n_l32i.n a3, a2, 0\nbbci  a3, 25, .term_pico_writereg\n.reloop_pico_writereg:\n_memw\n_l32i.n a3, a2, 0\nbbsi  a3, 25, .reloop_pico_writereg\n.term_pico_writereg:\n_ret.n");

}
#define pico_i2c_writereg( reg, hostid, par, val ) pico_i2c_writereg_asm( (hostid<<2) + 0x60000a00 + 0x300, (reg | (par<<8) | (val<<16) | 0x01000000 ) )

/**
   Callback for when other nodes send you a request

   @param request The request string received from another node in the mesh
   @param meshInstance The ESP8266WiFiMesh instance that called the function.
   @returns The string to send back to the other node
*/
String manageRequest(const String &request, ESP8266WiFiMesh &meshInstance) {
  /* Print out received message */
  Serial.print("Request received: ");
  Serial.println(request);

  /* return a string to send back */
  return ("Hello world response #" + String(responseNumber++) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + ".");
}

/**
   Callback used to decide which networks to connect to once a WiFi scan has been completed.

   @param numberOfNetworks The number of networks found in the WiFi scan.
   @param meshInstance The ESP8266WiFiMesh instance that called the function.
*/
void networkFilter(int numberOfNetworks, ESP8266WiFiMesh &meshInstance) {
  for (int i = 0; i < numberOfNetworks; ++i) {
    String currentSSID = WiFi.SSID(i);
    int meshNameIndex = currentSSID.indexOf(meshInstance.getMeshName());

    /* Connect to any _suitable_ APs which contain meshInstance.getMeshName() */
    if (meshNameIndex >= 0) {
      uint64_t targetNodeID = ESP8266WiFiMesh::stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length()));

      if (targetNodeID < ESP8266WiFiMesh::stringToUint64(meshInstance.getNodeID())) {
        ESP8266WiFiMesh::connectionQueue.push_back(NetworkInfo(i));
      }
    }
  }
}

/**
   Callback for when you get a response from other nodes

   @param response The response string received from another node in the mesh
   @param meshInstance The ESP8266WiFiMesh instance that called the function.
   @returns The status code resulting from the response, as an int
*/
transmission_status_t manageResponse(const String &response, ESP8266WiFiMesh &meshInstance) {
  transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE;

  /* Print out received message */
  Serial.print("Request sent: ");
  Serial.println(meshInstance.getMessage());
  Serial.print("Response received: ");
  Serial.println(response);

  // Our last request got a response, so time to create a new request.
  meshInstance.setMessage("Hello world request #" + String(++requestNumber) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + ".");

  // (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else.
  return statusCode;
}

void setup() {
  pico_i2c_writereg(103,4,1,0x48);  
  pico_i2c_writereg(103,4,2,0xf1);

  // Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 .
  // This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to.
  WiFi.persistent(false);

  Serial.begin(57600*2.5);
  delay(50); // Wait for Serial.

  //yield(); // Use this if you don't want to wait for Serial.

  Serial.println();
  Serial.println();

  Serial.println("Note that this library can use static IP:s for the nodes to speed up connection times.\n"
                 "Use the setStaticIP method as shown in this example to enable this.\n"
                 "Ensure that nodes connecting to the same AP have distinct static IP:s.\n"
                 "Also, remember to change the default mesh network password!\n\n");

  Serial.println("Setting up mesh node...");

  /* Initialise the mesh node */
  meshNode.begin();
  meshNode.activateAP(); // Each AP requires a separate server port.
  meshNode.setStaticIP(IPAddress(192, 168, 4, 22)); // Activate static IP mode to speed up connection times.
}

int32_t timeOfLastScan = -10000;
void loop() {
  if (millis() - timeOfLastScan > 3000 // Give other nodes some time to connect between data transfers.
      || (WiFi.status() != WL_CONNECTED && millis() - timeOfLastScan > 2000)) { // Scan for networks with two second intervals when not already connected.
    String request = "Hello world request #" + String(requestNumber) + " from " + meshNode.getMeshName() + meshNode.getNodeID() + ".";
    meshNode.attemptTransmission(request, false);
    timeOfLastScan = millis();

    if (ESP8266WiFiMesh::latestTransmissionOutcomes.empty()) {
      Serial.println("No mesh AP found.");
    } else {
      for (TransmissionResult &transmissionResult : ESP8266WiFiMesh::latestTransmissionOutcomes) {
        if (transmissionResult.transmissionStatus == TS_TRANSMISSION_FAILED) {
          Serial.println("Transmission failed to mesh AP " + transmissionResult.SSID);
        } else if (transmissionResult.transmissionStatus == TS_CONNECTION_FAILED) {
          Serial.println("Connection failed to mesh AP " + transmissionResult.SSID);
        } else if (transmissionResult.transmissionStatus == TS_TRANSMISSION_COMPLETE) {
          // No need to do anything, transmission was successful.
        } else {
          Serial.println("Invalid transmission status for " + transmissionResult.SSID + "!");
        }
      }
    }
    Serial.println();
  } else {
    /* Accept any incoming connections */
    meshNode.acceptRequest();
  }
}
tdnet12434 commented 5 years ago

Do it work properly with WiFi connection using compattible accesspoint like from mikrotik router? So i will very happy test range using my UAVs.:)

Updated: i just tried to make esp(underclock) as client and tried connect to my mikrotik ap (which configed to 10mhz bw) but no luck, the ap find nothing but used some data in 2447Mhz freq. I think maybe the bandwidth is mismatched (10.8Mhz vs 10Mhz). Still trying to adjust BLPLL to match with ap. Any suggestion would be appriciate!

cnlohr commented 5 years ago

That is a really interesting idea. I wonder if using a different crystal would work out. @ranlyticsBrad Do you want to add this somewhere in arduino land? I don't know anything about arduino dev.

ranlyticsBrad commented 5 years ago

I suppose I could add it to a very small lib for arduino. But it's really just adding the few lines of code at the top of the Arduino project. Probably too small for a downloadable lib. I might add a easy to use function for arduino though, just to make it easily accessible to the masses.

Add this to your wifi Arduino project to get it to use cnlohr's oddclock code:

{
   asm volatile (".global pico_i2c_writereg_asm\n.align 4\npico_i2c_writereg_asm:\n_s32i.n  a3, a2, 0\n_memw\n_l32i.n a3, a2, 0\nbbci  a3, 25, .term_pico_writereg\n.reloop_pico_writereg:\n_memw\n_l32i.n a3, a2, 0\nbbsi  a3, 25, .reloop_pico_writereg\n.term_pico_writereg:\n_ret.n");

}
#define pico_i2c_writereg( reg, hostid, par, val ) pico_i2c_writereg_asm( (hostid<<2) + 0x60000a00 + 0x300, (reg | (par<<8) | (val<<16) | 0x01000000 ) )

void setup() {
  pico_i2c_writereg(103,4,1,0x48);  
  pico_i2c_writereg(103,4,2,0xf1);
}

On a side note: I noticed espressif has done something similar on the esp32 for their new "long range" feature. I'm yet to look into that code to see what they did there.

cnlohr commented 5 years ago

Thank you :)

Also, if you do find out, I am curious, myself!

ramapcsx2 commented 4 years ago

Just hijacking this to say that this reg seems to be the PLL fine adjust: pico_i2c_writereg(103,4,4,0x53); where 0x53 might be the default, and it seems to linearly get faster by increasing the value :)

cnlohr commented 4 years ago

That's probably worth publishing more broadly.

ranlyticsBrad commented 4 years ago

I that's interesting @ramapcsx2 , I'm going to run some tests on this with the spectrum analyser. Makes it worthwhile in writing an arduino lib with a few user friendly clock options to abstract the hackiness.

ramapcsx2 commented 4 years ago

I'm not sure how useful this'll be, but if you want to make it into a library, you'll have to experiment a little with what exactly the register does.
From what I remember, reg 1 and 2 are indeed coarse PLL dividers, and reg 4 is fine adjust.
Fine adjust looked to be linear, since I had to slowly increase/decrease my receiver baud rate to decode serial data correctly.

What I actually wanted though was some kind of "spread spectrum" for the WiFi radio.
I'd like to try this to combat the (somewhat) known poor performance of the ESP8266 when it has to work in a high EMI environment (where "high" means pretty much any noise it happens to not like). Turns out that we can't do this yet though. Too bad :/

cnlohr commented 4 years ago

https://hackaday.com/2019/01/04/underclocking-the-esp8266-leads-to-wifi-weirdness/

ramapcsx2 commented 4 years ago

That article lead me here in the first place :)

The problem I'm trying to work around is the failure to transmit when the ESP is connected to regular WiFi equipment and there is EMI at a certain frequency. I know that the offending EMI frequency band is very narrow, and that moving the band slightly fixes the issue. (My EMI source is the PLL on a big video processor chip. The PLL lock frequency can easily be moved in software, but this isn't workable for a WiFi fix.)

So instead, I was hoping to modify the ESP radio frequeny ever so slightly, so that it can stay connected (or quickly reconnect). The hope is that in-between the EMI noise on the particular WiFi channel, there may be some spots where the ESP can push through :p

fxprime commented 4 years ago

That article lead me here in the first place :)

The problem I'm trying to work around is the failure to transmit when the ESP is connected to regular WiFi equipment and there is EMI at a certain frequency. I know that the offending EMI frequency band is very narrow, and that moving the band slightly fixes the issue. (My EMI source is the PLL on a big video processor chip. The PLL lock frequency can easily be moved in software, but this isn't workable for a WiFi fix.)

So instead, I was hoping to modify the ESP radio frequeny ever so slightly, so that it can stay connected (or quickly reconnect). The hope is that in-between the EMI noise on the particular WiFi channel, there may be some spots where the ESP can push through :p

Agreed. I still looking for the right value that can work with 10Mhz bandwidth with my Mikrotik :D