二进制图片直接显示在HTML中

参考:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

参考Data_URIs的方式,把二进制文件直接显示在网页中

格式

data:[<mediatype>][;base64],<data>

例如

data:text/plain,文本数据
data:text/html,HTML代码
data:text/html;base64,base64编码的HTML代码
data:text/css,CSS代码
data:text/css;base64,base64编码的CSS代码
data:text/javascript,Javascript代码
data:text/javascript;base64,base64编码的Javascript代码
data:image/gif;base64,base64编码的gif图片数据
data:image/png;base64,base64编码的png图片数据
data:image/jpeg;base64,base64编码的jpeg图片数据
data:image/x-icon;base64,base64编码的icon图片数据
  • base64
    是否通过base64编码

  • ,data
    二进制文件内容

比如要显示一个png图片的二进制文件,可以先读取图片内容,做base64的编码

<img src="data:image/png ;base64, <data>">

L298P 电机驱动 Shield

L298P 电机驱动 shield

这个shield也不知道啥意思,怎么翻译,大概就是扩展板的意思?
买了一块L298P电机驱动的扩展板,没找到资料,搜索一下只有英文的,就翻译一下了

来源

https://electropeak.com/learn/interfacing-l298p-h-bridge-motor-driver-shield-with-arduino/
一个购买连接,不过也没找到更多资料了

  • 先看看图

以下是说明

可以控制两台直流电机:

VMS: Module voltage(外部供电)
GND: Ground
MA1: Positive end for motor A (A电机正极)
MA2: Negative end for motor A (A电机负极)
MB1: Positive end for motor B (B电机正极)
MB2: Negative end for motor B (B电机负极)
PWMA: Speed control signal for motor A – This pin is connected to pin 10 of Arduino

A电机PWM输入 速度控制—也就是arduino的D10

PWMB: Speed control signal for motor B – This pin is connected to pin 11 of Arduino

B电机PWM输入 速度控制—也就是arduino的D11

ENA: Control signal for motor A – If HIGH, motor is in direct mode and if LOW, motor is rotating in reverse. This pin is connected to pin 12 of Arduino

A电机正反转控制—arduino的D12

ENB: Control signal for motor B – If HIGH, motor is in direct mode and if LOW, motor is rotating in reverse. This pin is connected to pin 13 of Arduino

B电机正反转控制—arduino的D13

Buzzer for make sound:
BUZ: Buzzer pin – This pin is connected to pin 4 of Arduino

蜂鸣器 D4

Connection for control servo motor:
SER: PWM pin for control servo motor – This pin is connected to pin 9 of Arduino

伺服电机(舵机) D9-PWM

Bluetooth connections:
BT2: Bluetooth Pins including +(3.3V) pins, (GND), RX (connected to D0) and TX (connected to D1)

连接蓝牙

Ultrasonic sensor connection:
ULT: connection pins to ultrasonic sensor including +(5V), (GND), Return (connected to D9) and Trigger (connected to D8)

超声波传感器

RBG LED connection:
RGB: RGB: For connection to RGB LED including pins B (connected to D6), G (connected to D5) and R (connected to D3)

RGB LED灯 R-D3 G-D5 B-D6 也不知道在哪亮。。

Other connections:
A/D: Analog and digital pins A0 to A5 for sensor and module use
D2: Digital pin 2 for sensor and module use
RS: Reset pin
GND: Ground
VCC: Board power supply – 3V, 5V

板供电

另一个地方找的示例代码,也是购买页。

https://protosupplies.com/product/l298p-motor-driver-shield/

/*
*  L298P Motor Shield
*  Code for exercising the L298P Motor Control portion of the shield
*  The low level motor control logic is kept in the function 'Motor'
*/
// The following pin designations are fixed by the shield
int const BUZZER = 4;
//  Motor A
int const ENA = 10;  
int const INA = 12;
//  Motor B
int const ENB = 11;  
int const INB = 13;

int const MIN_SPEED = 27;   // Set to minimum PWM value that will make motors turn
int const ACCEL_DELAY = 50; // delay between steps when ramping motor speed up or down.
//===============================================================================
//  Initialization
//===============================================================================
void setup()
{
  pinMode(ENA, OUTPUT);   // set all the motor control pins to outputs
  pinMode(ENB, OUTPUT);
  pinMode(INA, OUTPUT);
  pinMode(INB, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  Serial.begin(9600);     // Set comm speed for serial monitor messages
}
//===============================================================================
//  Main
//===============================================================================
void loop()
{
  // Run both motors Forward at 75% power
  Motor('C', 'F', 75);   
  delay(3000);

  // Run both motors in Reverse at 75% power but sound beeper first
  Motor('C', 'F', 0);  // Stop motors
  delay(1000);
  digitalWrite(BUZZER,HIGH);delay(500);digitalWrite(BUZZER,LOW); 
  delay(500);
  digitalWrite(BUZZER,HIGH);delay(500);digitalWrite(BUZZER,LOW); 
  delay(1000);
  Motor('C', 'R', 75);  // Run motors forward at 75%
 delay(3000); 

  // now run motors in opposite directions at same time at 50% speed
  Motor('A', 'F', 50);
  Motor ('B', 'R', 50);
  delay(3000);

  // now turn off both motors
  Motor('C', 'F', 0);  
  delay(3000);

  // Run the motors across the range of possible speeds in both directions
  // Maximum speed is determined by the motor itself and the operating voltage

  // Accelerate from zero to maximum speed
  for (int i = 0; i <= 100; i++)
  {
    Motor('C', 'F', i);
    delay(ACCEL_DELAY);
  }
  delay (2000);

  // Decelerate from maximum speed to zero
  for (int i = 100; i >= 0; --i)
  {
    Motor('C', 'F', i);
    delay(ACCEL_DELAY);
  }
  delay (2000);

  // Set direction to reverse and accelerate from zero to maximum speed
  for (int i = 0; i <= 100; i++)
  {
    Motor('C', 'R', i);
    delay(ACCEL_DELAY);
  }
  delay (2000);

  // Decelerate from maximum speed to zero
  for (int i = 100; i >= 0; --i)
  {
    Motor('C', 'R', i);
    delay(ACCEL_DELAY);
  }
  // Turn off motors
  Motor('C', 'F', 0);
  delay (3000);
}
/*
 * Motor function does all the heavy lifting of controlling the motors
 * mot = motor to control either 'A' or 'B'.  'C' controls both motors.
 * dir = Direction either 'F'orward or 'R'everse
 * speed = Speed.  Takes in 1-100 percent and maps to 0-255 for PWM control.  
 * Mapping ignores speed values that are too low to make the motor turn.
 * In this case, anything below 27, but 0 still means 0 to stop the motors.
 */
void Motor(char mot, char dir, int speed)
{
  // remap the speed from range 0-100 to 0-255
  int newspeed;
  if (speed == 0)
    newspeed = 0;   // Don't remap zero, but remap everything else.
  else
    newspeed = map(speed, 1, 100, MIN_SPEED, 255);

  switch (mot) {
    case 'A':   // Controlling Motor A
      if (dir == 'F') {
        digitalWrite(INA, HIGH);
      }
      else if (dir == 'R') {
        digitalWrite(INB, LOW);
      }
      analogWrite(ENA, newspeed);
      break;

    case 'B':   // Controlling Motor B
      if (dir == 'F') {
        digitalWrite(INB, HIGH);
      }
      else if (dir == 'R') {
        digitalWrite(INB, LOW);
      }
      analogWrite(ENB, newspeed);
      break;

    case 'C':  // Controlling Both Motors
      if (dir == 'F') {
        digitalWrite(INA, HIGH);
        digitalWrite(INB, HIGH);
      }
      else if (dir == 'R') {
        digitalWrite(INA, LOW);
         digitalWrite(INB, LOW);
      }
      analogWrite(ENA, newspeed);
      analogWrite(ENB, newspeed);
      break;
  }
  // Send what we are doing with the motors out to the Serial Monitor.

  Serial.print ("Motor: ");
  if (mot=='C')
      Serial.print ("Both");
    else
      Serial.print (mot);
  Serial.print ("t Direction: ");
  Serial.print (dir);
  Serial.print ("t Speed: ");
  Serial.print (speed);
  Serial.print ("t Mapped Speed: ");
  Serial.println (newspeed);
}

微信小程序登录

问题

1、调用wx.login后code传给后端需要保存session_token,但是这个session_token无法设置缓存时间,有效时间和用户操作有关,只能通过前端wx.checksession判断有效性.
2、用户信息、手机号等授权接口解密都需要使用这个session_token。

解决方式

取消原先系统的登录验证方式,直接用checksession方法检查用户登录状态
前端直接用wx.checksession作为判断用户登录状态的方法,session无效再调用wx.login更新后端数据

流程

wx.checksession
->ok->判断用户已登录

->无效->执行->wx.login->用返回的code传给后端更新session_token

app.js

    wx.checkSession().then(
      ()=>{}, 
      ()=>{
        return utils.authLogin()
      }).finally(()=>{
        this.getUserInfo().then(res=>{
          this.globalData.userInfo = res.data;
          if(res.data.is_registered == 0){
            wx.navigateTo({
              url: '/pages/getUserProfile/getUserProfile'
            })
          } 
        })
      })

utils.js

module.exports = {
  formatTime,
  request: function(params){
    return new Promise((resolve, reject)=>{
      wx.request({
        ...params,
        dataType: 'json',
        header: {
          Authorization: 'Bearer ' + wx.getStorageSync('authToken')
        },
        success:res=>{
          if(res.data.code == 4003){
            wx.navigateTo({
              url: '/pages/index/forbidden'
            })
          }
          if(res.data.code == 403){
            this.authLogin()
          }
          resolve(res.data)
        },
        error:error=>{
          reject(error)
        }
      })
    })
  },
  login: function(){
    return new Promise((resolve, reject)=>{
      wx.login({
        success:res=>{
          resolve(res)
        },
        error:error=>{
          reject(error)
        }
      })
    })
  },
  authLogin: function(){
    this.login().then(res=>{
      this.request({
        url: 'http://www.pp.com/api/user/login',
        data: {
          code: res.code
        }
      }).then(res=>{
        wx.setStorageSync('authToken', res.data.token)
      }).catch(e=>{

      })

    })
  }
}

ESP8266 arduino wifi文档阅读

来源

https://arduino-esp8266.readthedocs.io/en/3.0.2/ideoptions.html
https://www.arduino.cc/en/Reference/WiFi

WIFI库

连接示例

#include <ESP8266WiFi.h>
void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.begin("network-name", "pass-to-network");

  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
}
void loop(){}

包含的class

  • Station 连接wifi

  • SOFT-AP 作为软路由-提供其他设备联网能力

  • Scan 扫描范围内的SSID

  • Client

    WiFiClient()
    connected()
    connect()
    write()
    print()
    println()
    available()
    read()
    flush()
    stop()
  • WiFi Multi 记录多个ap并在断线时选择信号最好的wifi连接

    #include <ESP8266WiFiMulti.h>
    
    ESP8266WiFiMulti wifiMulti;
    // WiFi connect timeout per AP. Increase when connecting takes longer.
    const uint32_t connectTimeoutMs = 5000;
    
    void setup()
    {
      // Set in station mode
      WiFi.mode(WIFI_STA);
    
      // Register multi WiFi networks
      wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");
      wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
      wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
    }
    
    void loop()
    {
      // Maintain WiFi connection
      if (wifiMulti.run(connectTimeoutMs) == WL_CONNECTED) {
          ...
      }
    }
  • Server
    例子

    #include <ESP8266WiFi.h>
    
    const char* ssid = "********";
    const char* password = "********";
    
    WiFiServer server(80);
    
    void setup()
    {
      Serial.begin(115200);
      Serial.println();
    
      Serial.printf("Connecting to %s ", ssid);
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED)
      {
        delay(500);
        Serial.print(".");
      }
      Serial.println(" connected");
    
      server.begin();
      Serial.printf("Web server started, open %s in a web browser\n", WiFi.localIP().toString().c_str());
    }
    
    // prepare a web page to be send to a client (web browser)
    String prepareHtmlPage()
    {
      String htmlPage;
      htmlPage.reserve(1024);               // prevent ram fragmentation
      htmlPage = F("HTTP/1.1 200 OK\r\n"
                   "Content-Type: text/html\r\n"
                   "Connection: close\r\n"  // the connection will be closed after completion of the response
                   "Refresh: 5\r\n"         // refresh the page automatically every 5 sec
                   "\r\n"
                   "<!DOCTYPE HTML>"
                   "<html>"
                   "Analog input:  ");
      htmlPage += analogRead(A0);
      htmlPage += F("</html>"
                    "\r\n");
      return htmlPage;
    }
    
    void loop()
    {
      WiFiClient client = server.available();
      // wait for a client (web browser) to connect
      if (client)
      {
        Serial.println("\n[Client connected]");
        while (client.connected())
        {
          // read line by line what the client (web browser) is requesting
          if (client.available())
          {
            String line = client.readStringUntil('\r');
            Serial.print(line);
            // wait for end of client's request, that is marked with an empty line
            if (line.length() == 1 && line[0] == '\n')
            {
              client.println(prepareHtmlPage());
              break;
            }
          }
        }
    
        while (client.available()) {
          // but first, let client finish its request
          // that's diplomatic compliance to protocols
          // (and otherwise some clients may complain, like curl)
          // (that is an example, prefer using a proper webserver library)
          client.read();
        }
    
        // close the connection:
        client.stop();
        Serial.println("[Client disconnected]");
      }
    }
  • UDP UDP通信

    begin()
    available()
    beginPacket()
    endPacket()
    write()
    parsePacket()
    peek()
    read()
    flush()
    stop()
    remoteIP()
    remotePort()

下载离线网站

wget --mirror --convert-links --adjust-extension --page-requisites 
--no-parent http://example.org
  • –mirror – Makes (among other things) the download recursive.
  • –convert-links – convert all the links (also to stuff like CSS stylesheets) to relative, so it will be suitable for offline viewing.
  • –adjust-extension – Adds suitable extensions to filenames (html or css) depending on their content-type.
  • –page-requisites – Download things like CSS style-sheets and images required to properly display the page offline.
  • –no-parent – When recursing do not ascend to the parent directory. It useful for restricting the download to only a portion of the site.

简写

wget -mkEpnp http://example.org

wget 准确率不高,HTTrack是更好用的工具

https://www.httrack.com/
HTTrack is a free (GPL, libre/free software) and easy-to-use offline browser utility.

PHP常量表的实现

实际项目过程,经常遇到一些键值组成的常量表,今天想了一下怎么用比较好的方式实现这种结构。

  • 一个想法是建一个常量表,通过数据库来管理常量,这个实现比较直接,就不展开了。

  • 另一个想法是把这种结构用类来表示。

abstract class Constants{
    static $map = [];
    public static function getKey($value){
        return !empty($key = array_search($value, static::$map)) ? $key : null;
    }

    public static function getValue($key){
        return isset(static::$map[$key])? static::$map[$key] : null;
    }

    public static function getMap(){
        return static::$map;
    }
}

class Phone extends Constants{
    static $map = [
        10086 => '移动',
        10000 => '电信'
    ];

}

echo Phone::getValue(10000), PHP_EOL;
echo Phone::getKey('电信'), PHP_EOL;

这里本来Constants 这个类本来打算设置一个$map的abstract的属性,但是却报错了,属性不支持定义为abstract。

然后一顿搜索,找到了一个替代方案

abstract class Foo_Abstract {
  abstract public function get_tablename();
}

class Foo extends Foo_Abstract {
  protected $tablename = 'tablename';
  public function get_tablename() {
    return $this->tablename;
  }
}

不过这样代码会多很多,所以感觉用上面的实现也足够了吧。

laravel sanctum 错误自定义返回

laravel sanctum 错误自定义返回

现象

sanctum 认证失败会自动返回401或419 errercode,
前端认证逻辑这里会受到影响,所以希望可以自定义错误返回。

解决方法

根据

https://laravel.com/docs/9.x/errors#introduction

错误处理定义的handler内处理,这个类继承自框架的vendor\laravel\framework\src\Illuminate\Foundation\Exceptions\Handler.php

    public function render($request, Throwable $e)
    {
        if (method_exists($e, 'render') && $response = $e->render($request)) {
            return Router::toResponse($request, $response);
        }

        if ($e instanceof Responsable) {
            return $e->toResponse($request);
        }

        $e = $this->prepareException($this->mapException($e));

        if ($response = $this->renderViaCallbacks($request, $e)) {
            return $response;
        }

        return match (true) {
            $e instanceof HttpResponseException => $e->getResponse(),
            $e instanceof AuthenticationException => $this->unauthenticated($request, $e),
            $e instanceof ValidationException => $this->convertValidationExceptionToResponse($e, $request),
            default => $this->renderExceptionResponse($request, $e),
        };
    }

这里用到php8的match表达式,顺便学习一下

https://www.php.net/manual/en/control-structures.match.php

可以看到会调用到unauthenticated这个方法,方法是protected的,所以可以在项目里继承自这个handler的类里面覆写这个方法

    protected function unauthenticated($request, AuthenticationException $exception) {
        return response()->json(['code' => 403, 'message' => '未授权']);
    }

codesignal 练习题

1

boundedRatio

You are given an array of integers a and two integers l and r. You task is to calculate a boolean array b, where b[i] = true if there exists an integer x, such that a[i] = (i + 1) * x and l ≤ x ≤ r. Otherwise, b[i] should be set to false.

Example

For a = [8, 5, 6, 16, 5], l = 1, and r = 3, the output should be boundedRatio(a, l, r) = [false, false, true, false, true].

For a[0] = 8, we need to find a value of x such that 1 * x = 8, but the only value that would work is x = 8 which doesn't satisfy the boundaries 1 ≤ x ≤ 3, so b[0] = false.
For a[1] = 5, we need to find a value of x such that 2 * x = 5, but there is no integer value that would satisfy this equation, so b[1] = false.
For a[2] = 6, we can choose x = 2 because 3 * 2 = 6 and 1 ≤ 2 ≤ 3, so b[2] = true.
For a[3] = 16, there is no an integer 1 ≤ x ≤ 3, such that 4 * x = 16, so b[3] = false.
For a[4] = 5, we can choose x = 1 because 5 * 1 = 5 and 1 ≤ 1 ≤ 3, so b[4] = true.
Input/Output

[execution time limit] 4 seconds (php)

[input] array.integer a

An array of integers.

Guaranteed constraints:
1 ≤ a.length ≤ 100,
1 ≤ a[i] ≤ 106.

[input] integer l

An integer representing the lower bound for x.

Guaranteed constraints:
1 ≤ l ≤ 104.

[input] integer r

An integer representing the upper bound for x.

Guaranteed constraints:
1 ≤ r ≤ 104,
l ≤ r.

[output] array.boolean

A boolean array.

2

You are given an array of integers a. A new array b is generated by rearranging the elements of a in the following way:

b[0] is equal to a[0];
b[1] is equal to the last element of a;
b[2] is equal to a[1];
b[3] is equal to the second-last element of a;
b[4] is equal to a[2];
b[5] is equal to the third-last element of a;
and so on.
Your task is to determine whether the new array b is sorted in strictly ascending order or not.

Here is how the process of generating the new array b works:

Example

For a = [1, 3, 5, 6, 4, 2], the output should be alternatingSort(a) = true.

The new array b will look like [1, 2, 3, 4, 5, 6], which is in strictly ascending order, so the answer is true.

For a = [1, 4, 5, 6, 3], the output should be alternatingSort(a) = false.

The new array b will look like [1, 3, 4, 6, 5], which is not in strictly ascending order, so the answer is false.

Input/Output

[execution time limit] 4 seconds (php)

[input] array.integer a

The given array of integers.

Guaranteed constraints:
1 ≤ a.length ≤ 105,
-109 ≤ a[i] ≤ 109.

[output] boolean

A boolean representing whether the new array b will be sorted in strictly ascending order or not.

3

You are given an array of arrays a. Your task is to group the arrays a[i] by their mean values, so that arrays with equal mean values are in the same group, and arrays with different mean values are in different groups.

Each group should contain a set of indices (i, j, etc), such that the corresponding arrays (a[i], a[j], etc) all have the same mean. Return the set of groups as an array of arrays, where the indices within each group are sorted in ascending order, and the groups are sorted in ascending order of their minimum element.

Example

For

a = [[3, 3, 4, 2],
     [4, 4],
     [4, 0, 3, 3],
     [2, 3],
     [3, 3, 3]]
the output should be

meanGroups(a) = [[0, 4],
                 [1],
                 [2, 3]]
mean(a[0]) = (3 + 3 + 4 + 2) / 4 = 3;
mean(a[1]) = (4 + 4) / 2 = 4;
mean(a[2]) = (4 + 0 + 3 + 3) / 4 = 2.5;
mean(a[3]) = (2 + 3) / 2 = 2.5;
mean(a[4]) = (3 + 3 + 3) / 3 = 3.
There are three groups of means: those with mean 2.5, 3, and 4. And they form the following groups:

Arrays with indices 0 and 4 form a group with mean 3;
Array with index 1 forms a group with mean 4;
Arrays with indices 2 and 3 form a group with mean 2.5.
Note that neither

meanGroups(a) = [[0, 4],
                 [2, 3],
                 [1]]
nor

meanGroups(a) = [[0, 4],
                 [1],
                 [3, 2]]
will be considered as a correct answer:

In the first case, the minimal element in the array at index 2 is 1, and it is less then the minimal element in the array at index 1, which is 2.
In the second case, the array at index 2 is not sorted in ascending order.
For

a = [[-5, 2, 3],
     [0, 0],
     [0],
     [-100, 100]]
the output should be

meanGroups(a) = [[0, 1, 2, 3]]
The mean values of all of the arrays are 0, so all of them are in the same group.

Input/Output

[execution time limit] 4 seconds (php)

[input] array.array.integer a

An array of arrays of integers.

Guaranteed constraints:
1 ≤ a.length ≤ 100,
1 ≤ a[i].length ≤ 100,
-100 ≤ a[i][j] ≤ 100.

[output] array.array.integer

An array of arrays, representing the groups of indices.

4

You are given an array of integers a. A new array b is generated by rearranging the elements of a in the following way:

b[0] is equal to a[0];
b[1] is equal to the last element of a;
b[2] is equal to a[1];
b[3] is equal to the second-last element of a;
b[4] is equal to a[2];
b[5] is equal to the third-last element of a;
and so on.
Your task is to determine whether the new array b is sorted in strictly ascending order or not.

Here is how the process of generating the new array b works:

Example

For a = [1, 3, 5, 6, 4, 2], the output should be alternatingSort(a) = true.

The new array b will look like [1, 2, 3, 4, 5, 6], which is in strictly ascending order, so the answer is true.

For a = [1, 4, 5, 6, 3], the output should be alternatingSort(a) = false.

The new array b will look like [1, 3, 4, 6, 5], which is not in strictly ascending order, so the answer is false.

Input/Output

[execution time limit] 4 seconds (php)

[input] array.integer a

The given array of integers.

Guaranteed constraints:
1 ≤ a.length ≤ 105,
-109 ≤ a[i] ≤ 109.

[output] boolean

A boolean representing whether the new array b will be sorted in strictly ascending order or not.