我如何通过私有 Github 存储库进行 ESP8266 HTTP 更新?

How I do an ESP8266 HTTPupdate via private Github repository?

我尝试通过 Git 存储库从 ESP8266 更新固件。但我不知道怎么办。回购是私人的,这意味着我需要一个密码,我读到我可以使用 HTTPclient 库进行身份验证。 Git集线器的身份验证如何工作?

此外,我需要一些额外的代码来更新库吗? HTTP客户端支持HTTPS吗?

编辑:这里有一些我的代码示例,但适用于 public 回购:

update.cpp(我在单独的头文件中)

//#define repo "https://github.com/username/reponame/branch/path/to/file?raw=true"
#define repo "https://raw.githubusercontent.com/username/reponame/branch/path/to/file"

t_httpUpdate_return ret = ESPhttpUpdate.update(client, repo);
// Or:
//t_httpUpdate_return ret = ESPhttpUpdate.update(client, "server", 80, "file.bin");

我已经配置了 httpUpdate 错误消息,它显示下一个错误:

CALLBACK:  HTTP update fatal error code -5
HTTP_UPDATE_FAILD Error (-5): HTTP error: connection lost

有一种不同的方法可以从 GitHub 进行更新,首先 GitHub 使用 HTTPS 连接,这意味着您需要在 TLS/SSL 设置之前进行配置,另外,端口 80 用于不安全连接,443 用于安全连接。

Public 存储库(不安全)

这是更简单的方法,只需将 .setInsecure() 添加到 WiFiClientSecure.h 库中的 wifi 客户端,这允许您建立一个连接,忽略来自 http 连接的所有警报。

WiFiClientSecure client;
client.setInsecure();

不安全,仅用于测试,不用于生产。

您必须使用 https://raw.githubusercontent.com,这是为了从 GitHub 的 public 存储库下载原始数据,只是文件。您完整的 link 文件必须是:

#define repo "https://raw.githubusercontent.com/<user>/<repo>/master/<path to the .bin>"
t_httpUpdate_return ret = ESPhttpUpdate.update(client, repo);

<user> 替换为您的用户名,并将 <repo> 替换为您的存储库名称。 <path to the .bin> 类似于“folder/folder/firmware.bin”

Public 存储库(安全):

ESP8266/Arduino 的官方 GitHub 存储库中有一个示例。 https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266httpUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino

您可以按照此示例与 httpUpdate 建立安全连接。您还需要下载证书,这可以通过在项目的同一文件夹中执行下一个脚本来完成: https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py

如果你使用的是windows,对于运行这个脚本,你需要在路径中添加“OpenSSL.exe”,Git自带,你添加Git 的 bin 文件夹到路径。此外,您还需要一个文件“ar.exe”,这是 ESP8266 内核附带的。您也可以将这两个.exe文件放在脚本的同一个文件夹中。

对于 Arduino IDE 是这样的:

%userprofile%\AppData\Local\Arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc.5.0-4-b40a506\xtensa-lx106-elf\bin

对于 PlaformIO 是:

%userprofile%\.platformio\packages\toolchain-xtensa\xtensa-lx106-elf\bin

脚本完成后,将创建一个名为 data 的文件夹,其中包含 certs.ar。使用 LittleFS 将此文件系统映像上传到 ESP8266。

私人仓库:

这与上一个相同,只是需要更改几处,我们将对 ESP8266httpUpdate 库进行更改。我们使用与 httpupdatesecure 相同的示例,您需要在 GitHub 帐户中配置令牌。

按照 GitHub 帮助页面的说明创建令牌: https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line

您只需选择此选项

复制令牌并保存,您只能看到一次。

你不能使用 raw.githubusercontent.com,会给你 error 404,这只适用于 public 回购。您需要:api.github.com。你满 link 看起来像:

https://api.github.com/repos/<user>/<repo>/contents/<path to the .bin>

并且你需要在http请求中添加headers,在ESP8266httpUpdate.cpp中,你必须将它放在开始添加[=90的部分的函数HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate中=]:

http.addHeader(F("Accept"), "application/vnd.github.v3.raw");
http.addHeader(F("authorization"), "Bearer <your token>");

<your token> 替换为您之前创建并保存的那个。

请记住,编辑此库会影响您以后的所有项目,因此,当您完成后,还原或评论您添加到库中的两个 headers。

NOTE: Using a token will allow your application access to all your repos, even if your token is read-only. Currently, is not possible generate a token for a specific repo on GitHub. Be careful if you share your code. Better use a dummy account with only one repo for this.

[编辑] - 现在可以使用了,这是一个 arduino 核心错误,安装了 2.7.4,现在可以使用了(来自 3.0.2-dev)

在这里不起作用“验证 Bin Header 失败”,尝试在 github 上托管 bin,000webhosting,激增,没有看到与 Web 服务器问题相关的任何内容:(

    #include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
//#include <WiFiClientSecure.h>
#include <CertStoreBearSSL.h>
BearSSL::CertStore certStore;
#include <time.h>
#include <FS.h>
#include <LittleFS.h>
 
const String FirmwareVer={"1"}; 
//#define URL_fw_Version "/teste/key/version.txt"
//#define URL_fw_Bin "https://pacauiot.surge.sh/teste/key/firmware.bin"
//const char* host = "pacauiot.surge.sh";

#define URL_fw_Bin "https://fourieristic-prison.000webhostapp.com/meucu.php"
const char* host = "fourieristic-prison.000webhostapp.com";


const int httpsPort = 443;
const char* ssid = "wifi";
const char* password = "wifipass";
#define RTC_UTC_TEST 1510592825



void setClock()
// *******************************************************************************************
{
  // see https://github.com/esp8266/Arduino/issues/4637
  time_t now; 
  now = time(nullptr); // if there's no time, this will have a value of 28800; Thu Jan  1 08:00:00 1970 
  Serial.print("Initial time:"); Serial.println(now);
  Serial.println(ctime(&now));

  int myTimezone = -7;
  int dst = 0;
  int SecondsPerHour = 3600;
  int MAX_TIME_RETRY = 60;
  int i = 0;

  // it is unlikely that the time is already set since we have no battery; 
  // if no time is avalable, then try to set time from the network
  if (now <= 1500000000) {
    // try to set network time via ntp packets
    configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // see https://github.com/esp8266/Arduino/issues/4749#issuecomment-390822737

                // Starting in 2007, most of the United States and Canada observe DST from
    // the second Sunday in March to the first Sunday in November.
    // example setting Pacific Time:
    setenv("TZ", "EST4EDT", 1); // see https://users.pja.edu.pl/~jms/qnx/help/watcom/clibref/global_data.html
    //                     | month 3, second sunday at 2:00AM
    //                                    | Month 11 - firsst Sunday, at 2:00am  
    // Mm.n.d
    //   The dth day(0 <= d <= 6) of week n of month m of the year(1 <= n <= 5, 1 <= m <= 12, where 
    //     week 5 means "the last d day in month m", which may occur in the fourth or fifth week).
    //     Week 1 is the first week in which the dth day occurs.Day zero is Sunday.

    tzset();
    Serial.print("Waiting for time(nullptr).");
    i = 0;
    while (!time(nullptr)) {
      Serial.print(".");
      delay(1000);
      i++;
      if (i > MAX_TIME_RETRY) {
        Serial.println("Gave up waiting for time(nullptr) to have a valid value.");
        break;
      }
    }
  }
  Serial.println("");

  // wait and determine if we have a valid time from the network. 
  now = time(nullptr);
  i = 0;
  Serial.print("Waiting for network time.");
  while (now <= 1500000000) {
    Serial.print(".");
    delay(1000); // allow a few seconds to connect to network time.
    i++;
    now = time(nullptr);
    if (i > MAX_TIME_RETRY) {
      Serial.println("Gave up waiting for network time(nullptr) to have a valid value.");
      break;
    }
  }
  Serial.println("ok");

  // get the time from the system
  char *tzvalue;
  tzvalue = getenv("TZ");
  Serial.print("Network time:");  Serial.println(now);
  Serial.println(ctime(&now));
  Serial.print("tzvalue for timezone = "); Serial.println(tzvalue);

  // TODO - implement a web service that returns current epoch time to use when NTP unavailable (insecure SSL due to cert date validation)

  // some networks may not allow ntp protocol (e.g. guest networks) so we may need to fudge the time
  if (now <= 1500000000) {
    Serial.println("Unable to get network time. Setting to fixed value. \n");
    // set to RTC text value
    // see https://www.systutorials.com/docs/linux/man/2-settimeofday/
    //
    //struct timeval {
    //  time_t      tv_sec;     /* seconds */
    //  suseconds_t tv_usec;    /* microseconds */
    //};
    timeval tv = { RTC_UTC_TEST, 0 };
    //
    //struct timezone {
    //  int tz_minuteswest;     /* minutes west of Greenwich */
    //  int tz_dsttime;         /* type of DST correction */
    //};
    timezone tz = { myTimezone * 60 , 0 };  

    // int settimeofday(const struct timeval *tv, const struct timezone *tz);
    settimeofday(&tv, &tz);
  }

  now = time(nullptr);
  Serial.println("Final time:");  Serial.println(now);
  Serial.println(ctime(&now));
}

  
void FirmwareUpdate()
{  
  //WiFiClientSecure client;
  BearSSL::WiFiClientSecure client;
  bool mfln = client.probeMaxFragmentLength(host, 443, 1024);  // server must be the same as in ESPhttpUpdate.update()
    Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no");
    if (mfln) {
      client.setBufferSizes(1024, 1024);
  }
  client.setCertStore(&certStore);
  //client.setTrustAnchors(&cert);
//  if (!client.connect(host, httpsPort)) {
//    Serial.println("Connection failed");
//    return;
//  }
//  client.print(String("GET ") + URL_fw_Version + " HTTP/1.1\r\n" +
//               "Host: " + host + "\r\n" +
//               "User-Agent: BuildFailureDetectorESP8266\r\n" +
//               "Connection: close\r\n\r\n");
//  while (client.connected()) {
//    String line = client.readStringUntil('\n');
//    if (line == "\r") {
//      //Serial.println("Headers received");
//      break;
//    }
//  }
//  String payload = client.readStringUntil('\n');
//
//  payload.trim();
//  if(payload.equals(FirmwareVer) )
//  {   
//     Serial.println("Device already on latest firmware version"); 
//  }
  if(1==2){
    
  }
  else
  {
    Serial.println("New firmware detected");
    ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); 
    t_httpUpdate_return ret = ESPhttpUpdate.update(client, URL_fw_Bin);
        
    switch (ret) {
      case HTTP_UPDATE_FAILED:
        Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
        break;

      case HTTP_UPDATE_NO_UPDATES:
        Serial.println("HTTP_UPDATE_NO_UPDATES");
        break;

      case HTTP_UPDATE_OK:
        Serial.println("HTTP_UPDATE_OK");
        break;
    } 
  }
}  
void connect_wifi();
unsigned long previousMillis_2 = 0;
unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 10000;
const long mini_interval = 5000;
 void repeatedCall(){
    unsigned long currentMillis = millis();
    if ((currentMillis - previousMillis) >= interval) 
    {
      // save the last time you blinked the LED
      previousMillis = currentMillis;
      setClock();
      FirmwareUpdate();
    }

    if ((currentMillis - previousMillis_2) >= mini_interval) {
      static int idle_counter=0;
      previousMillis_2 = currentMillis;    
      Serial.print(" Active fw version:");
      Serial.println(FirmwareVer);
      Serial.print("Idle Loop(5s)...");
      //Serial.println(idle_counter++);
     if(idle_counter%2==0)
      digitalWrite(LED_BUILTIN, HIGH);
     else 
      digitalWrite(LED_BUILTIN, LOW);
     if(WiFi.status() == !WL_CONNECTED) 
          connect_wifi();
   }
 }

  
void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println();
  Serial.println();
  for (uint8_t t = 4; t > 0; t--) {
    Serial.printf("[SETUP] WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
  }
  Serial.println("Start Xuxu");
  WiFi.mode(WIFI_STA);
  connect_wifi();  
  setClock();
  pinMode(LED_BUILTIN, OUTPUT);
  LittleFS.begin();
  int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
  Serial.print(F("Number of CA certs read: "));
  Serial.println(numCerts);
  if (numCerts == 0) {
    Serial.println(F("No certs found. Did you run certs-from-mozill.py and upload the LittleFS directory before running?"));
    return; // Can't connect to anything w/o certs!
  }
   //repeatedCall();  
 FirmwareUpdate();
}
void loop()
{
   
}

void connect_wifi()
{
  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print("O");
  }                                   
  Serial.println("Connected to WiFi");
}