[
  {
    "path": ".gitignore",
    "content": "\nAPI/Serverless/.env\nArduino/HardwareTest/NeoPixelRainbow/NeoPixelRainbow.ino.esp32.bin\n*.jpg\n*.bmp\n*.gif\n*.bmp\n*.bmp\n*.png\nAPI/magic.gif\n*.bin\nAPI/sb.py\n*.bmp\nAPI/sb.txt\n*.mov\n*.mp4\n"
  },
  {
    "path": "API/NewConvert/NewConvert.py",
    "content": "from dearpygui.core import *\nfrom dearpygui.simple import *\nimport numpy as np\nimport os\nimport math\nimport time\nimport cv2\nfrom dearpygui.core import *\nfrom dearpygui.simple import *\n\n#配置\nNUMPIXELS = 80 #单边LED数量\nDiv = 320 #1圈分割数\nBright = 60 #輝度\nLed0Bright = 15 #中心LEDの輝度 [%]\nlast_time = time.time()\n\n#Global\ngammaCorrection = False\nidentity = np.arange(256, dtype = np.dtype('uint8'))\ngammatable = np.array([0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n    1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   2,   2,   2,   2,   2,   2,\n    3,   3,   3,   3,   3,   3,   4,   4,   4,   4,   5,   5,   5,   5,   6,   6,\n    6,   7,   7,   7,   7,   8,   8,   8,   9,   9,   9,   10,  10,  11,  11,  11,\n    12,  12,  13,  13,  14,  14,  14,  15,  15,  16,  16,  17,  17,  18,  18,  19,\n    19,  20,  20,  21,  22,  22,  23,  23,  24,  25,  25,  26,  26,  27,  28,  28,\n    29,  30,  30,  31,  32,  33,  33,  34,  35,  35,  36,  37,  38,  39,  39,  40,\n    41,  42,  43,  43,  44,  45,  46,  47,  48,  49,  50,  50,  51,  52,  53,  54,\n    55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  71,\n    72,  73,  74,  75,  76,  77,  78,  80,  81,  82,  83,  84,  86,  87,  88,  89,\n    91,  92,  93,  94,  96,  97,  98,  100, 101, 102, 104, 105, 106, 108, 109, 110,\n    112, 113, 115, 116, 118, 119, 121, 122, 123, 125, 126, 128, 130, 131, 133, 134,\n    136, 137, 139, 140, 142, 144, 145, 147, 149, 150, 152, 154, 155, 157, 159, 160,\n    162, 164, 166, 167, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 187, 189,\n    191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221,\n    223, 225, 227, 229, 231, 233, 235, 238, 240, 242, 244, 246, 248, 251, 253, 255])\nlut = np.dstack((identity, identity, gammatable))\nmedia_path = \"\"\nsave_path = \"\"\n\ndef polarConv(imgOrgin,outputName):\n    global gammaCorrection\n    h = imgOrgin.shape[0] #帧尺寸\n    w = imgOrgin.shape[1]\n    #画像縮小 \n    imgRedu = cv2.resize(imgOrgin,(math.floor((NUMPIXELS * 2 -1)/h *w), NUMPIXELS * 2 -1))\n    #顺时针旋转90度 因为下一步的极坐标转换的0度是正东方向，而我们的POV正北方向为0度\n    imgRedu = cv2.rotate(imgRedu,cv2.ROTATE_90_CLOCKWISE)  \n    polar_image = cv2.warpPolar(imgRedu,(NUMPIXELS , Div ), (imgRedu.shape[1]/2,imgRedu.shape[0]/2) ,min(imgRedu.shape[0], imgRedu.shape[1]) / 2, 0)\n    for i in range(NUMPIXELS):  #亮度处理\n         polar_image[:,i,0] = polar_image[:,i,0] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100\n         polar_image[:,i,1] = polar_image[:,i,1] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100 \n         polar_image[:,i,2] = polar_image[:,i,2] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100 \n        #polar_image[:,i,2] = hsv[:,i,2] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100\n    if(gammaCorrection):\n        polar_image = cv2.LUT(polar_image, lut)\n    cv2.imwrite(outputName+'.jpg',polar_image,[int(cv2.IMWRITE_JPEG_QUALITY), 100]+[int(cv2.IMWRITE_JPEG_OPTIMIZE), True])\nframeNum = 0\ndef start_convert():\n    global frameNum\n    startTime = time.time()\n    if (media_path.endswith(\".jpg\") or media_path.endswith(\".png\") or media_path.endswith(\".bmp\")):\n        frame = cv2.imread(media_path)\n        polarConv(frame,save_path+'\\\\'+str(0))\n    else:\n        media = cv2.VideoCapture(media_path)\n        while True:\n            ret, frame = media.read()\n            if ret == False:\n                frameNum = 0\n                break  \n            polarConv(frame,save_path+'\\\\'+str(frameNum))\n            frameNum=frameNum+1\n    print(time.time() - startTime)        \n           \n\ndef file_picker(sender, data):\n    open_file_dialog(callback=apply_selected_file, extensions=\".*,.gif,.jpg,.png,.mp4,.avi,.mov\")\n\ndef directory_picker(sender, data):\n    select_directory_dialog(callback=apply_selected_directory)\ndef apply_selected_directory(sender, data):\n    global save_path\n    log_debug(data)  # so we can see what is inside of data\n    directory = data[0]\n    folder = data[1]\n    set_value(\"directory\", directory)\n    save_path = f\"{directory}\"\n    print(save_path)\n    \ndef apply_selected_file(sender, data):\n    global media_path\n    log_debug(data)  # so we can see what is inside of data\n    directory = data[0]\n    file = data[1]\n    set_value(\"file_path\", f\"{directory}\\\\{file}\")\n    media_path = f\"{directory}\\\\{file}\"\n    print(media_path)\n\ndef gamma_switch(sender,data):\n    global gammaCorrection\n    gammaCorrection = get_value(\"Gamma Correction\")\n    print(gammaCorrection)\n\nwith window(\"Main\"):\n    add_button(\"Select Image or Video\", callback=file_picker)\n    add_text(\"Input Path: \", color=[128, 255, 0])\n    add_same_line()\n    add_label_text(\"##filepath\", source=\"file_path\", color=[0, 255, 0])\n    add_button(\"Select Output Folder\", callback=directory_picker)\n    add_text(\"Output Folder: \" , color=[0, 255, 255])\n    add_same_line()\n    add_label_text(\"##dir\", source=\"directory\", color=[128, 255, 255])\n    add_checkbox(\"Gamma Correction\",default_value=gammaCorrection,callback=gamma_switch)\n    add_button(\"Go!\",callback=start_convert,width=100,height=50)\n    add_text(\"Ctrl + Click to diretly edit:\" , color=[200,150,255])\n    add_slider_int(\"single-sided LEDs\",default_value=NUMPIXELS,max_value=200) \n    add_slider_int(\"Divide\",default_value=Div,max_value=1000)\n    add_slider_int(\"Center Brightness\",default_value=Led0Bright,max_value=100) \n    add_slider_int(\"Edge Brightness\",default_value=Bright,max_value=100)    \n    add_text(\"Author: Corebb\" , color=[255,255,0])\n    add_drawing(\"Drawing_1\", width=256, height=256)\n    add_same_line()\n    add_drawing(\"Drawing_2\", width=256, height=256)  \n     \ndef resource_path(relative_path):\n    \"\"\" Get absolute path to resource, works for dev and for PyInstaller \"\"\"\n    try:\n        # PyInstaller creates a temp folder and stores path in _MEIPASS\n        base_path = sys._MEIPASS\n    except Exception:\n        base_path = os.path.abspath(\".\")\n\n    return os.path.join(base_path, relative_path)\n    \nset_main_window_size(800, 800)  \nset_main_window_title(\"bbPOV-P Converter\") \ndraw_image(\"Drawing_1\", resource_path('data/Corebb.jpg'), [0, 0], pmax=[256,256], uv_min=[0, 0], uv_max=[1, 1], tag=\"image\")\ndraw_image(\"Drawing_2\", resource_path('data/logo.jpg'), [0, 0], pmax=[256,256], uv_min=[0, 0], uv_max=[1, 1], tag=\"image2\")\nset_theme(\"Cherry\")\nstart_dearpygui(primary_window=\"Main\")\n"
  },
  {
    "path": "API/Serverless/app.py",
    "content": "#Based on homemadegarbage.com\n#Thanks for the knowledge\n#Developed by Corebb\n\nimport numpy as np\nimport os\nimport math\nfrom PIL import Image\nimport base64\nfrom io import BytesIO\nimport flask\nfrom flask import request,json,make_response\nimport gzip\napp = flask.Flask(__name__)\ndata = []\n\n@app.route(\"/\")\ndef index():\n    return \"Hello Flask\"\n\n#画像変換関数\ndef polarConv(imgOrgin, frame,NUMPIXELS,Div):\n    \n    h = imgOrgin.height #帧尺寸\n    w = imgOrgin.width\n \n    #画像縮小\n    imgRedu = imgOrgin.resize((math.floor((NUMPIXELS * 2 -1)/h *w), NUMPIXELS * 2 -1))\n    #imgRedu.save(str(frame)+'.png')   #输出缩小后的原图像\n    imgArray=np.array(imgRedu)\n    #縮小画像中心座標\n    h2 = imgRedu.height\n    w2 = imgRedu.width\n    wC = math.floor(w2 / 2)\n    hC = math.floor(h2 / 2)\n \n    #極座標変換画像準備\n    imgPolar = Image.new('RGB', (NUMPIXELS, Div))\n    \n \n    #極座標変換\n    for j in range(0, Div):\n        for i in range(0, hC+1):\n            #座標色取得\n            rP = imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 0]\n                     \n            gP = imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 1]\n                     \n            bP = imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 2]\n            imgPolar.putpixel((i,j), (rP, gP, bP))\n    buffered = BytesIO()\n    imgPolar.save(buffered,format=\"BMP\") #输出极坐标变换后的图像\n    data.append(base64.b64encode(buffered.getvalue()).decode('utf-8'))        \n\n@app.route('/', methods=['POST'])\ndef convert():\n    data.clear()\n    img = request.form['img']\n    NUMPIXELS = int(request.form['NUMPIXELS']) #半径灯条数\n    Div = int(request.form['Div']) #1圈分割数\n    im = Image.open(BytesIO(base64.b64decode(img)))\n    print(im.is_animated)\n    print(im.n_frames)\n    Frame=im.n_frames\n    for i in range(Frame):\n        #输出每一帧\n        frame = im.convert('RGBA') #如果是RGB的话，有的透明背景GIF不兼容\n        polarConv(frame, i, NUMPIXELS, Div)\n        if i != Frame-1:\n            im.seek(im.tell()+1)\n        response = make_response(json.dumps(data))\n        response.headers['Content-Type'] = 'application/json'\n    return response\n    "
  },
  {
    "path": "API/Serverless/requirements.txt",
    "content": "Flask==1.1.2\nnumpy==1.17.2\nPillow==6.2.1"
  },
  {
    "path": "API/Serverless/serverless.yml",
    "content": "# serverless.yml\n\ncomponent: flask\nname: flashDemo\norg: orgDemo\napp: appDemo\nstage: dev\n\ninputs:\n  src:\n    hook: 'pip -V'\n    dist: ./\n    exclude:\n      - .env\n  region: ap-guangzhou\n  runtime: Python3.6\n  apigatewayConf:\n    protocols:\n      - http\n      - https\n    environment: release"
  },
  {
    "path": "API/convert.py",
    "content": "#Based on homemadegarbage.com\n#Thanks for the knowledge\n#Developed by Corebb\n\nimport numpy as np\nimport os\nimport math\nfrom PIL import Image\n \n#配置\n#Frame = 5 #指定帧数量\nNUMPIXELS = 80 #单边LED数量\nDiv = 320#1圈分割数\nBright = 60 #輝度\nLed0Bright = 15 #中心LEDの輝度 [%]\n\ngif_file_name = \"jellyfish.gif\"\nim = Image.open(gif_file_name)\nprint(im.is_animated)\nprint(im.n_frames)\nFrame=im.n_frames\n\n\n \n\n#画像変換関数\ndef polarConv(imgOrgin, frame):\n    \n    h = imgOrgin.height #帧尺寸\n    w = imgOrgin.width\n \n    #画像縮小\n    imgRedu = imgOrgin.resize((math.floor((NUMPIXELS * 2 -1)/h *w), NUMPIXELS * 2 -1)).rotate(180)\n    #imgRedu.save(str(frame)+'.png')   #输出缩小后的原图像\n    imgArray=np.array(imgRedu)\n    #縮小画像中心座標\n    h2 = imgRedu.height\n    w2 = imgRedu.width\n    wC = math.floor(w2 / 2)\n    hC = math.floor(h2 / 2)\n \n    #極座標変換画像準備\n    imgPolar = Image.new('RGB', (NUMPIXELS, Div))\n    \n \n    #極座標変換\n    for j in range(0, Div):\n        for i in range(0, hC+1):\n            #座標色取得\n            rP = int(imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 0] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100)\n            gP = int(imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 1] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100)\n            bP = int(imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 2] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100)\n            imgPolar.putpixel((i,j), (rP, gP, bP))\n\n    imgPolar.save(str(frame)+'.jpg',quality=95, optimize=True) #输出极坐标变换后的图像\n            \n \n\n    \n    \nfor i in range(Frame):\n    #输出每一帧\n    frame = im.convert('RGBA') #如果是RGB的话，有的透明背景GIF不兼容\n    polarConv(frame, i)\n    if i != Frame-1:\n        im.seek(im.tell()+1)\n\n    "
  },
  {
    "path": "API/convertAPI.py",
    "content": "#Based on homemadegarbage.com\n#Thanks for the knowledge\n#Developed by Corebb\n\nimport numpy as np\nimport os\nimport math\nfrom PIL import Image\nimport base64\nfrom io import BytesIO\nimport flask\nfrom flask import request,json,make_response\nimport gzip\napp = flask.Flask(__name__)\ndata = []\n\n#画像変換関数\ndef polarConv(imgOrgin, frame,NUMPIXELS,Div):\n    \n    h = imgOrgin.height #帧尺寸\n    w = imgOrgin.width\n \n    #画像縮小\n    imgRedu = imgOrgin.resize((math.floor((NUMPIXELS * 2 -1)/h *w), NUMPIXELS * 2 -1))\n    #imgRedu.save(str(frame)+'.png')   #输出缩小后的原图像\n    imgArray=np.array(imgRedu)\n    #縮小画像中心座標\n    h2 = imgRedu.height\n    w2 = imgRedu.width\n    wC = math.floor(w2 / 2)\n    hC = math.floor(h2 / 2)\n \n    #極座標変換画像準備\n    imgPolar = Image.new('RGB', (NUMPIXELS, Div))\n    \n \n    #極座標変換\n    for j in range(0, Div):\n        for i in range(0, hC+1):\n            #座標色取得\n            rP = imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 0]\n                     \n            gP = imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 1]\n                     \n            bP = imgArray[hC + math.ceil(i * math.cos(2*math.pi/Div*j)),\n                         wC - math.ceil(i * math.sin(2*math.pi/Div*j)), 2]\n            imgPolar.putpixel((i,j), (rP, gP, bP))\n    buffered = BytesIO()\n    imgPolar.save(buffered,format=\"BMP\") #输出极坐标变换后的图像\n    data.append(base64.b64encode(buffered.getvalue()).decode('utf-8'))        \n\n@app.route('/', methods=['POST'])\ndef convert():\n    data.clear()\n    gif_file_name = request.files['img']\n    NUMPIXELS = int(request.form['NUMPIXELS']) #半径灯条数\n    Div = int(request.form['Div']) #1圈分割数\n    im = Image.open(gif_file_name)\n    print(im.is_animated)\n    print(im.n_frames)\n    Frame=im.n_frames\n    for i in range(Frame):\n        #输出每一帧\n        frame = im.convert('RGBA') #如果是RGB的话，有的透明背景GIF不兼容\n        polarConv(frame, i, NUMPIXELS, Div)\n        if i != Frame-1:\n            im.seek(im.tell()+1)\n    content = gzip.compress(json.dumps(data).encode('utf8'),5)    \n    response = make_response(content)\n    response.headers['Content-Type'] = 'application/json'\n    response.headers['Content-length'] = len(content)\n    response.headers['Content-Encoding'] = 'gzip'\n    return response\napp.run()\n\n\n\n\n\n\n \n\n\n \n\n    \n\n    "
  },
  {
    "path": "API/screen.py",
    "content": "#Based on homemadegarbage.com\n#Thanks for the knowledge\n#Developed by Corebb\n\nimport numpy as np\nimport os\nimport math\nfrom PIL import Image\nfrom PIL import ImageGrab\nfrom datetime import datetime\nimport cv2\n\n#配置\n#Frame = 5 #指定帧数量\nNUMPIXELS = 80 #单边LED数量\nDiv = 360 #1圈分割数\nBright = 100 #輝度\nLed0Bright = 3 #中心LEDの輝度 [%]\ni = 0\n\n\n \n\n#画像変換関数\ndef polarConv(imgOrgin, frame):\n    h = imgOrgin.height #帧尺寸\n    w = imgOrgin.width\n    #画像縮小\n   # imgRedu = imgOrgin.resize((math.floor((NUMPIXELS * 2 -1)/h *w), NUMPIXELS * 2 -1))\n   # imgRedu = cv2.cvtColor(np.array(imgRedu), cv2.COLOR_BGR2GRAY)\n    #polar_image = cv2.warpPolar(imgRedu,(NUMPIXELS , Div ), (w/2,h/2) ,min(h, w) / 2, 0)\n    #cv2.imwrite('sb.jpg',polar_image)\n    dateTimeObj = datetime.now()\n    print(dateTimeObj)        \n \n\n    \n    \nwhile 1:\n    #捕捉屏幕\n    frame = ImageGrab.grab(bbox=(100,100,160,160)) \n    polarConv(frame, i)\n    #frame.save('1.bmp')\n    \n\n    "
  },
  {
    "path": "API/sender.py",
    "content": "import tkinter as tk\nfrom tkinter import ttk\nfrom tkinter import *\nimport numpy as np\nimport os\nimport math\nfrom mss import mss\nimport time\nfrom threading import Thread\nimport cv2\nimport time\nimport tkinter.font as font\nimport socket\nimport requests\n\n#配置\nNUMPIXELS = 80 #单边LED数量\nDiv = 320 #1圈分割数\nBright = 50 #輝度\nLed0Bright = 15 #中心LEDの輝度 [%]\nlast_time = time.time()\nMAX_FPS=38\nMIN_FRAME_TIME=1/MAX_FPS\n\n#Global\nrunning = False\nposX = 0\nposY = 0\nposX2 = 160\nposY2 = 160\nTCP_IP = '10.0.0.212'\nTCP_PORT = 22333\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n\ndef resource_path(relative_path):\n    \"\"\" Get absolute path to resource, works for dev and for PyInstaller \"\"\"\n    try:\n        # PyInstaller creates a temp folder and stores path in _MEIPASS\n        base_path = sys._MEIPASS\n    except Exception:\n        base_path = os.path.abspath(\".\")\n\n    return os.path.join(base_path, relative_path)\n\ndef polarConv(imgOrgin):\n    h = imgOrgin.height #帧尺寸\n    w = imgOrgin.width\n    #画像縮小\n    imgRedu = cv2.resize(np.array(imgOrgin),(math.floor((NUMPIXELS * 2 -1)/h *w), NUMPIXELS * 2 -1))\n    #顺时针旋转90度 因为下一步的极坐标转换的0度是正东方向，而我们的POV正北方向为0度\n    imgRedu = cv2.rotate(imgRedu,cv2.ROTATE_90_CLOCKWISE)  \n    polar_image = cv2.warpPolar(imgRedu,(NUMPIXELS , Div ), (imgRedu.shape[1]/2,imgRedu.shape[0]/2) ,min(imgRedu.shape[0], imgRedu.shape[1]) / 2, 0)\n    for i in range(NUMPIXELS):  #亮度处理\n         polar_image[:,i,0] = polar_image[:,i,0] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100\n         polar_image[:,i,1] = polar_image[:,i,1] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100 \n         polar_image[:,i,2] = polar_image[:,i,2] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100 \n        #polar_image[:,i,2] = hsv[:,i,2] * ((100 - Led0Bright) / NUMPIXELS * i + Led0Bright) / 100 * Bright /100\n    #cv2.imwrite(outputName+'.jpg',polar_image,[int(cv2.IMWRITE_JPEG_OPTIMIZE), True])\n    ret,img_encode = cv2.imencode('.jpg',polar_image,[int(cv2.IMWRITE_JPEG_QUALITY), 50]+[int(cv2.IMWRITE_JPEG_OPTIMIZE), True])\n    data = np.array(img_encode)\n    stringData = data.tobytes()\n    print(str(len(stringData)))\n    s.send(str.encode(str(len(stringData)).ljust(5)+'\\r'));  \n    s.sendall(stringData)\ndef capture():\n    global posX\n    global posY\n    global posX2\n    global posY2\n    global last_time\n    while running:\n            #if(s.recv(1).decode(\"utf-8\") == \"N\"):\n                #捕捉屏幕\n            start = time.time()\n            frame = mss().grab({'top': posY, 'left': posX, 'width': posX2-posX, 'height': posY2-posY})\n            polarConv(frame)          \n            print('FPS:'+str(int(1 / (time.time()-last_time))))\n            last_time=time.time()\n            time.sleep(max(0, MIN_FRAME_TIME - (time.time() - start)))\ndef startCapture():\n    global running\n    alpha = 0\n    running=True\n    s.connect((TCP_IP, TCP_PORT))\n    t = Thread (target = capture)\n    t.start()\ndef stopCapture():\n    global running\n    running=False\nclass Main(tk.Tk):\n    def __init__(self):\n        tk.Tk.__init__(self)\n        icon = \"\"\"    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\n        \"\"\"\n        iconImg=PhotoImage(data=icon) \n        self.wm_iconphoto(True, iconImg)\n        self.title('bbPOV-P Sender')\n        self.configure(background='#222222')\n        self.resizable(0,0)\n        w = 200\n        h = 100\n        ws = self.winfo_screenwidth()\n        hs = self.winfo_screenheight()\n        x = (ws/2) - (w/2)\n        y = (hs/2) - (h/2)\n        self.geometry('%dx%d+%d+%d' % (w, h, x, y))\n        self.floater = CaptureArea(self)\n        ip = tk.Entry(self,justify='center',bg=\"#222222\",highlightbackground=\"white\",fg=\"white\")\n        ip.insert(0,'IP地址')\n        buttonFont = font.Font(family=\"微软雅黑\",size=16)\n        start = tk.Button(self, text=\"开始\", command=startCapture , bg=\"green\",fg=\"white\")\n        stop = tk.Button(self, text=\"停止\", command=stopCapture , bg=\"red\",fg=\"white\")\n        start['font'] = buttonFont\n        stop['font'] = buttonFont\n        ip.pack(pady=10,anchor=\"center\")\n        start.pack(padx=10,pady=5,ipadx=10,ipady=5,side=\"left\")\n        stop.pack(padx=10,pady=5,ipadx=10,ipady=5,side=\"left\")\nclass ResizingCanvas(tk.Canvas):\n    def __init__(self, parent, **kwargs):\n        tk.Canvas.__init__(self, parent, **kwargs)\n        self.bind(\"<Configure>\", self.on_resize)\n        self.height = self.winfo_reqheight()\n        self.width = self.winfo_reqwidth()\n\n    def on_resize(self,event):\n        # determine the ratio of old width/height to new width/height\n        wscale = event.width/self.width\n        hscale = event.height/self.height\n        self.width = event.width\n        self.height = event.height\n        # rescale all the objects\n        self.scale(\"all\", 0, 0, wscale, hscale)\n        \nclass CaptureArea(tk.Toplevel):\n    def __init__(self, *args, **kwargs):\n        tk.Toplevel.__init__(self, *args, **kwargs)\n        self.overrideredirect(1) \n        self.attributes(\"-alpha\", 0.3)\n        self.attributes(\"-transparentcolor\", 'purple')\n        self.attributes(\"-topmost\", True)\n        myCanvas = ResizingCanvas(self, bg=\"purple\", height=300, width=300,highlightthickness=8, highlightbackground=\"red\")\n        myCanvas.pack(fill=tk.BOTH, expand=tk.YES)\n        self.move = tk.Label(self, bitmap=\"gray25\")\n        self.move.place(x=0,y=0,height=8,width=8)\n        self.move.bind(\"<ButtonPress-1>\", self.start_move)\n        self.move.bind(\"<ButtonRelease-1>\", self.stop_move)\n        self.move.bind(\"<B1-Motion>\", self.do_move)\n        \n        self.grip = ttk.Sizegrip(self)\n        self.grip.place(height=8,width=8,relx=1.0, rely=1.0, anchor=\"se\")\n        self.grip.bind(\"<B1-Motion>\", self.OnMotion)\n\n        \n    def OnMotion(self, event):\n        global posX2\n        global posY2\n        x1 = self.winfo_pointerx()\n        y1 = self.winfo_pointery() \n        x0 = self.winfo_rootx()\n        y0 = self.winfo_rooty()\n        posX2 = x0+self.winfo_width()-8\n        posY2 = y0+self.winfo_height()-8\n        self.geometry(\"%sx%s\" % ((x1-x0),(y1-y0)))\n        return\n    def start_move(self, event):\n        self.x = event.x\n        self.y = event.y\n\n    def stop_move(self, event):\n        self.x = None\n        self.y = None\n\n    def do_move(self, event):\n        global posX\n        global posY\n        global posX2\n        global posY2\n        deltax = event.x - self.x\n        deltay = event.y - self.y\n        x = self.winfo_x() + deltax\n        y = self.winfo_y() + deltay\n        posX = self.winfo_rootx()+8\n        posY = self.winfo_rooty()+8\n        posX2 = x+self.winfo_width()-8\n        posY2 = y+self.winfo_height()-8\n        self.geometry(f\"+{x}+{y}\")\napp=Main()\napp.mainloop()"
  },
  {
    "path": "Arduino/HardwareTest/DivSpeedtest/DivSpeedtest.ino",
    "content": "#include <WiFi.h>\n#include <AsyncTCP.h>\n#include <ESPAsyncWebServer.h>\n#include <AsyncElegantOTA.h>\n#include <NeoPixelBus.h>\n#include <SPI.h>\n#include <ESPmDNS.h>\n#include \"SD_MMC.h\"\n#include \"JPEGDEC.h\"\n#include <Ticker.h>\n#include \"soc/timer_group_struct.h\"\n#include \"soc/timer_group_reg.h\"\n//显示相关\n#define PixelCount 80  //单边LED数量\n#define LedStripCount 2  //LED条数\nuint32_t Frame = 0;\nbyte Hall = 0;  //到达顶端的霍尔传感器代号\nuint32_t Div = 360;\n\n\n//一些机制需要用到的全局变量\nTicker hallHit;\nJPEGDEC jpeg;\nFile myfile;\n\nAsyncWebServer server(80);\nint numRot= 0;\nint numDiv = 0;\nint stateDiv = 0;\nint spinstae = 1;\nvolatile unsigned long rotTime, timeOld, timeNow, opeTime, spinTime;\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod2> strip2(PixelCount);\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod> strip(PixelCount);\n//Adafruit_DotStar_VSPI stripA(PixelCount,DOTSTAR_BRG);\n//CRGB leds[PixelCount];\n\nAsyncWebSocket ws(\"/ws\");\nRgbColor black(0);\n\n\nvoid RotCountCommon(){\n  \n  static unsigned long last_interrupt_time = 0;\n  unsigned long interrupt_time = millis();            //deBounce,as the signal not stable sometimes 去抖动\n    if (interrupt_time - last_interrupt_time > 20)\n    {    \n      numDiv=0;\n      timeNow = micros();\n      rotTime = timeNow - timeOld;\n      timeOld = timeNow;\n      //Serial.println(\"RotCount\");\n    }\n   last_interrupt_time = interrupt_time;\n  } \nvoid setup()\n{\n     pinMode(34,INPUT);\n     pinMode(35,INPUT); \n     Serial.begin(115200);\n     if(!SD_MMC.begin(\"/sdcard\")){\n        Serial.println(\"Card Mount Failed\");\n    }   \n     WiFi.mode(WIFI_AP);\n     WiFi.softAP(\"bbPOV-P\");\n      MDNS.begin(\"bbPOV\");\n      MDNS.addService(\"bbPOV\", \"tcp\", 80);\n    \n    strip.Begin();\n    strip.Show();\n    strip2.Begin(32,25,33,26);\n    strip2.Show();\n    //stripA.begin(); // Initialize pins for output\n    //stripA.show();  // Turn all LEDs off ASAP\n//    FastLED.addLeds<APA102,23,18,BGR,DATA_RATE_MHZ(30)>(leds, PixelCount);\n  //  FastLED.show();\n    hallHit.attach(0.033,RotCountCommon);\n    \n    Serial.println(\"Setup Done\");   \n\n    xTaskCreatePinnedToCore(\n    webloop\n    ,  \"webloop\"\n    ,  1000  // Stack size\n    ,  NULL\n    ,  2 // Priority\n    ,  NULL \n    ,  0); \n    \n    xTaskCreatePinnedToCore(\n    ledloop\n    ,  \"ledloop\"\n    ,  1000  // Stack size\n    ,  NULL\n    ,  5 // Priority\n    ,  NULL \n    ,  1);\n\n    \n        \n}\nvoid loop() { \n   //AsyncElegantOTA.loop(); \n}\nvoid ledloop(void *pvParameters)\n{\n  for (;;)\n  {\n    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n    TIMERG0.wdt_feed=1;\n    TIMERG0.wdt_wprotect=0;\n        if(stateDiv == 1 && micros() - timeOld > (rotTime / (Div/LedStripCount)) * (numDiv)){\n        stateDiv = 0;\n      }\n      if(stateDiv == 0 && micros() - timeOld < (rotTime / (Div/LedStripCount)) * (numDiv + 1 )){\n     //   long stripshowtime=micros();\n        stateDiv = 1;\n      //  long donetime=micros();\n       // switch(Hall){\n        //    case 0:\n              strip.ClearTo(black);\n              strip2.ClearTo(black);\n             // long nowtime=micros();\n             RgbColor Divcolor (0,0,0);\n              if(numDiv%3==0)\n                   // CHOU = CRGB::Red;\n                   Divcolor.R=16;\n              if(numDiv%3==1)\n                    //CHOU = CRGB::Green;\n                   Divcolor.G=16;\n              if(numDiv%3==2)\n                   // CHOU = CRGB::Blue;\n                   Divcolor.B=16;\n              for(int i = 0; i < PixelCount; i++){\n                strip.SetPixelColor(i, Divcolor);\n                strip2.SetPixelColor(i, Divcolor);\n                }\n             // Serial.printf(\"FUcking setpixel tiime:%d\",int(micros()-nowtime));\n              //  FastLED.show(); \n              \n              strip.Show();\n              strip2.Show();  \n              \n              //  stripA.show();\n             // break;\n              /*\n            case 1:\n              strip.ClearTo(black);\n              strip2.ClearTo(black);\n              for(int i = 0; i < PixelCount; i++){\n               strip2.SetPixelColor(i, RgbColor(uint8_t((imgBuffer[numDiv][i] & 0xF800)>>8),uint8_t((imgBuffer[numDiv][i] & 0x07C0)>>3),uint8_t((imgBuffer[numDiv][i] & 0x001F)<<3)));\n               strip.SetPixelColor(i, RgbColor(uint8_t((imgBuffer[numDiv+Div/LedStripCount][i] & 0xF800)>>8),uint8_t((imgBuffer[numDiv+Div/LedStripCount][i] & 0x07C0)>>3),uint8_t((imgBuffer[numDiv+Div/LedStripCount][i] & 0x001F)<<3)));\n              }\n              strip2.Show();  \n              strip.Show();  \n              break;\n          }  \n              */\n        numDiv++;\n        if(numDiv >= (Div / LedStripCount)) numDiv = 0;\n       // donetime=micros()-donetime;\n        //Serial.printf(\"FUcking done tiime:%d\",int(donetime));\n      //  stripshowtime=micros()-stripshowtime;\n        //      Serial.printf(\"FUcking stripshow tiime:%d\",int(stripshowtime));  \n        }\n        if(stateDiv == 0 ){\n          \n          //strip.ClearTo(black);\n         /// strip.Show();\n         // strip2.ClearTo(black);\n         // strip2.Show();\n          }\n  }\n}\n\nvoid webloop(void *pvParameters)\n{\n  server.on(\"/\", HTTP_GET, [](AsyncWebServerRequest *request) {\n      request->send(200, \"text/plain\", \"Hi! I am bbPOV-P.\");\n    });\n    AsyncElegantOTA.begin(&server);    // Start ElegantOTA\n    \n    server.serveStatic(\"/\", SD_MMC, \"/\");\n    server.begin(); \n    Serial.println(\"HTTP server started\");\n    vTaskDelete(NULL);\n}\n  \n"
  },
  {
    "path": "Arduino/HardwareTest/FreeRTOS/FreeRTOS.ino",
    "content": "// the setup function runs once when you press reset or power the board\nTaskHandle_t loop1Handle,loop2Handle;\n#include \"soc/timer_group_struct.h\"\n#include \"soc/timer_group_reg.h\"\nvoid setup() {\n  \n  // initialize serial communication at 115200 bits per second:\n  Serial.begin(115200);\n  \n  // Now set up two tasks to run independently.\n  xTaskCreatePinnedToCore(\n    loop1\n    ,  \"loop1\"   // A name just for humans\n    ,  1024  // This stack size can be checked & adjusted by reading the Stack Highwater\n    ,  NULL\n    ,  20  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.\n    ,  &loop1Handle \n    ,  0);\n\n  xTaskCreatePinnedToCore(\n    loop2\n    ,  \"loop2\"\n    ,  1024  // Stack size\n    ,  NULL\n    ,  20  // Priority\n    ,  &loop2Handle \n    ,  0);\n\n  // Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.\n}\n\nvoid loop()\n{\n  // Empty. Things are done in Tasks.\n}\n\nvoid loop1(void *pvParameters)  // This is a task.\n{\n\n  for (;;)\n  {\n    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n  TIMERG0.wdt_feed=1;\n  TIMERG0.wdt_wprotect=0;\n    Serial.println(\"loop1\");\n    Serial.println(\"loop1Done\");\n  }\n}\nvoid loop2(void *pvParameters)  // This is a task.\n{\n\n  for (;;)\n  {\n    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n  TIMERG0.wdt_feed=1;\n  TIMERG0.wdt_wprotect=0;\n    Serial.println(\"loop2\");\n    Serial.println(\"loop2Done\");\n  }\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/ImgDivSpeedtest/ImgDivSpeedtest.ino",
    "content": "#include <Ticker.h>\nTicker hallHit;\n#include <WiFi.h>\n#include <AsyncTCP.h>\n#include <WebServer.h>\n#include <ElegantOTA.h>\n#include <NeoPixelBus.h>\n#include <SPI.h>\n#include <ESPmDNS.h>\n#include \"SD_MMC.h\"\n#include \"JPEGDEC.h\"\n#include \"soc/timer_group_struct.h\"\n#include \"soc/timer_group_reg.h\"\n#include <ArduinoJson.h>\n#include \"webpage.h\"\n//显示相关\n#define PixelCount 80  //单边LED数量\n#define LedStripCount 2  //LED条数\n#define BufferNum 2\n#define Div 320\n#define MaxStreamBuffer 10*1024\n#define OFFSET_34 0\n#define OFFSET_35 0\n\nuint16_t (*imgBuffer)[320][PixelCount];\nuint8_t streamBuffer[MaxStreamBuffer];\n\n//一些机制需要用到的全局变量\nJPEGDEC jpeg;\nFile root;\nFile dir;\nFile myfile;\nTaskHandle_t nextFileHandle; \nint bufferRot=-1;\nWebServer server(80);\nint numRot= 0;\nint numDiv = 0;\nint stateDiv = 0;\nint spinstae = 1;\nvolatile unsigned long rotTime, timeOld, timeNow, opeTime, spinTime;\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod2> strip2(PixelCount);\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod> strip(PixelCount);\nDynamicJsonDocument doc(4096);\nJsonArray avaliableMedia = doc.to<JsonArray>();\nint displayMode = 0;\nint curMedia = 0;\nWiFiServer tcpStream; //声明服务器对象\nWiFiClient client;\nbool autoNext = true;\nRgbColor black(0);\n\n\nvoid IRAM_ATTR RotCount(){\nstatic unsigned long last_interrupt_time = 0;\n    unsigned long interrupt_time = millis();            //deBounce,as the signal not stable sometimes 去抖动\n    if (interrupt_time - last_interrupt_time > 20)\n    {  \n      numDiv = 0;\n      bufferRot++;\n      if(bufferRot>=BufferNum-1) bufferRot=0;\n      timeNow = micros();\n      rotTime = timeNow - timeOld;\n      timeOld = timeNow;\n      xTaskNotifyGive( nextFileHandle );\n    }\n  last_interrupt_time = interrupt_time;\n}\n  \n\n\nvoid setup()\n{\n     pinMode(34,INPUT);\n     pinMode(35,INPUT); \n     Serial.begin(115200);\n     if(imgBuffer = (uint16_t(*)[320][PixelCount]) calloc(PixelCount*Div*BufferNum,sizeof(uint16_t)))\n        Serial.println(\"Alloc IMG Memory OK\");\n     if(!SD_MMC.begin(\"/sdcard\",true)){\n        Serial.println(\"Card Mount Failed\");\n    }   \n    /*\n     WiFi.mode(WIFI_AP);\n     WiFi.softAP(\"bbPOV-P\");\n      MDNS.begin(\"bbPOV\");\n      MDNS.addService(\"bbPOV\", \"tcp\", 80);*/\n      WiFi.begin(\"Hollyshit_A\", \"00197633\");\n\n    while (WiFi.status() != WL_CONNECTED) {\n        delay(500);\n        Serial.print(\".\");\n    }\n\n    Serial.println(\"\");\n    Serial.println(\"WiFi connected\");\n    Serial.println(\"IP address: \");\n    Serial.println(WiFi.localIP());\n      server.on(\"/\", []() {\n      server.send_P(200, \"text/html\", index_html);\n    });\n  server.on(\"/avaliableMedia\", []() {\n      String json;\n      serializeJson(doc, json);\n      server.send(200, \"text/plain\", json);\n    });\n  server.on(\"/changeMedia\",[]() {\n      int mediaID = server.arg(0).toInt();\n      Serial.println(mediaID);\n      dir=SD_MMC.open(\"/bbPOV-P/\"+avaliableMedia[mediaID].as<String>());\n      server.send(200, \"text/plain\", \"OK\");\n    });\n  server.on(\"/changeAutoNext\",[]() {\n      autoNext = !autoNext;\n      if(autoNext) server.send(200, \"text/plain\", \"True\"); \n      else server.send(200, \"text/plain\", \"False\"); \n  });  \n    ElegantOTA.begin(&server);      \n    server.begin();   \n    Serial.println(\"HTTP server started\");\n    tcpStream.begin(22333); //服务器启动监听端口号22333\n    strip.Begin();\n    strip.Show();\n    strip2.Begin(32,25,33,26);\n    strip2.Show();\n    long lTime;\n\n    root=SD_MMC.open(\"/bbPOV-P\"); \n    while(true){\n      File entry = root.openNextFile();\n      if(!entry) break;\n      if(entry.isDirectory()){\n          avaliableMedia.add(String(entry.name()).substring(9));\n        }\n      }\n    dir=SD_MMC.open(\"/bbPOV-P/\"+avaliableMedia[0].as<String>());\n  if (jpeg.open(\"\", myOpen, myClose, myRead, mySeek, JPEGDraw))\n  {\n    lTime = micros();\n    if (jpeg.decode(0,0,0))\n    {\n      lTime = micros() - lTime;\n      Serial.printf(\"Successfully decoded image in %d us\\n\", (int)lTime);\n    }\n    jpeg.close();\n  }\n  bufferRot=0;\n  hallHit.attach(0.04,RotCount);  \n  attachInterrupt(35, RotCount, FALLING );\n\n      xTaskCreatePinnedToCore(\n    nextFile\n    ,  \"nextFile\"\n    ,  5000  // Stack size\n    ,  NULL\n    ,  4  // Priority\n    ,  &nextFileHandle \n    ,  0); \n        xTaskCreatePinnedToCore(\n    webloop\n    ,  \"webloop\"\n    ,  5000  // Stack size\n    ,  NULL\n    ,  2 // Priority\n    ,  NULL \n    ,  0); \n    Serial.println(\"Setup Done\");\n    vTaskPrioritySet(NULL, 5);              \n}\nvoid loop() { \n  if(stateDiv == 1 && micros() - timeOld > (rotTime / Div) * (numDiv)){\n        stateDiv = 0;\n      }\n      if(stateDiv == 0 && micros() - timeOld < (rotTime / Div) * (numDiv + 1 )){\n        stateDiv = 1;\n      //  long donetime=micros();\n        int showNumDiv = numDiv;\n        if(showNumDiv<Div/LedStripCount){\n              for(int i = 0; i < PixelCount; i++){\n                uint16_t color = imgBuffer[bufferRot][showNumDiv][i];\n                uint16_t color2 = imgBuffer[bufferRot][showNumDiv+Div/LedStripCount][i];\n                strip.SetPixelColor(i, RgbColor(uint8_t((color & 0xF800)>>8),uint8_t((color & 0x07C0)>>3),uint8_t((color & 0x001F)<<3)));\n                strip2.SetPixelColor(i, RgbColor(uint8_t((color2 & 0xF800)>>8),uint8_t((color2 & 0x07C0)>>3),uint8_t((color2 & 0x001F)<<3)));\n              }\n        }\n        else{\n          for(int i = 0; i < PixelCount; i++){\n                uint16_t color = imgBuffer[bufferRot][showNumDiv][i];\n                uint16_t color2 = imgBuffer[bufferRot][showNumDiv-Div/LedStripCount][i];\n                strip.SetPixelColor(i, RgbColor(uint8_t((color & 0xF800)>>8),uint8_t((color & 0x07C0)>>3),uint8_t((color & 0x001F)<<3)));\n                strip2.SetPixelColor(i, RgbColor(uint8_t((color2 & 0xF800)>>8),uint8_t((color2 & 0x07C0)>>3),uint8_t((color2 & 0x001F)<<3)));\n              }\n          }\n              strip.Show();  \n              strip2.Show();  \n              \n        numDiv++;\n        if(numDiv == (Div / LedStripCount)){\n          bufferRot++;\n          if(bufferRot>=BufferNum-1) bufferRot=0;\n          xTaskNotifyGive( nextFileHandle );\n        }\n        if(numDiv >= Div) numDiv=0;    \n        \n        /*\n        if(stateDiv == 0 ){\n          \n          strip.ClearTo(black);\n          strip.Show();\n          strip2.ClearTo(black);\n          strip2.Show();\n          }*/\n  }\n} \n\nvoid webloop(void *pvParameters)\n{\n  for (;;)\n  {\n    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n    TIMERG0.wdt_feed=1;\n    TIMERG0.wdt_wprotect=0;\n    server.handleClient();\n  }\n}\n\n \nvoid * myOpen(const char *filename, int32_t *size) {\n  Serial.print(\"Open:\");\n  myfile = dir.openNextFile();\n  if(!myfile){\n        if(autoNext){\n        curMedia++;\n        if(curMedia>=avaliableMedia.size()) curMedia=0;\n        dir=SD_MMC.open(\"/bbPOV-P/\"+avaliableMedia[curMedia].as<String>());\n        }\n        else dir.rewindDirectory();\n        myfile = dir.openNextFile();\n      }\n Serial.println(myfile.name());\n  *size = myfile.size();\n  return &myfile;\n}\nvoid myClose(void *handle) {\n  if (myfile) myfile.close();\n}\nint32_t myRead(JPEGFILE *handle, uint8_t *buffer, int32_t length) {\n  if (!myfile) return 0;\n  return myfile.read(buffer, length);\n}\nint32_t mySeek(JPEGFILE *handle, int32_t position) {\n  if (!myfile) return 0;\n  return myfile.seek(position);\n}\n\nint JPEGDraw(JPEGDRAW *pDraw) {\n\n // Serial.printf(\"jpeg draw: x,y=%d,%d, cx,cy = %d,%d\\n\",pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);\n  //Serial.printf(\"Before Pixel 80 = 0x%04x\\n\", pDraw->pPixels[80]);\n  int sdbufferRot = bufferRot+1;\n  if(sdbufferRot >= BufferNum-1) sdbufferRot=0;\n  int pixels=pDraw->iWidth*pDraw->iHeight;\n    memcpy(&imgBuffer[sdbufferRot][pDraw->y][pDraw->x],pDraw->pPixels,sizeof(uint16_t)*pixels);\n   // Serial.println(ESP.getFreeHeap());\n  return 1;\n}  \n\n\nvoid nextFile(void *pvParameters){\n  for (;;)\n  {\n  TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n  TIMERG0.wdt_feed=1;\n  TIMERG0.wdt_wprotect=0;\n  \n  client = tcpStream.available(); //尝试建立客户对象\n // client.setNoDelay(true);\n    if (client) //如果当前客户可用\n    {\n        Serial.println(\"[Client connected]\");\n        while (client.connected()) //如果客户端处于连接状态\n        {\n          TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n          TIMERG0.wdt_feed=1;\n          TIMERG0.wdt_wprotect=0;\n          ulTaskNotifyTake( pdTRUE, portMAX_DELAY );\n            if (client.available()) //如果有可读数据\n            {\n                String buff = client.readStringUntil('\\r');\n                int len = buff.toInt();\n                client.readBytes(streamBuffer,len);                              \n                  if (jpeg.openRAM(streamBuffer, len, JPEGDraw)) {\n                 //  Serial.printf(\"Image size: %d x %d, orientation: %d, bpp: %d\\n\", jpeg.getWidth(),\n                   // jpeg.getHeight(), jpeg.getOrientation(), jpeg.getBpp());\n                      if (jpeg.decode(0,0,0)) { // full sized decode\n                      }\n                      jpeg.close();\n                    }\n            }\n        }\n      //  client.stop(); //结束当前连接:\n      //  Serial.println(\"[Client disconnected]\");\n    }\n    else{\n      ulTaskNotifyTake( pdTRUE, portMAX_DELAY );\n //   Serial.println(\"File\");\n    //long lTime=micros();\n      if (jpeg.open(\"\", myOpen, myClose, myRead, mySeek, JPEGDraw))\n    {\n  \n      if (jpeg.decode(0,0,0))\n      {\n       // lTime = micros() - lTime;\n        Serial.println(\"Successfully decoded \");\n      }\n      jpeg.close();\n    }\n  }\n}\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/ImgDivSpeedtest/webpage.h",
    "content": "const char index_html[] PROGMEM = R\"rawliteral(<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<head>\n <style>\n  big{\n    font-size:68px;\n  }\n  ul{\n  }\n  li{\n    text-align:center;\n    line-height:50px;\n    font-size:20px;\n    margin:2%;\n    float:left;\n    width:15%;\n    height:50px;\n    color:white;\n    padding:8px 20px 8px 20px;\n    background-color:#666666;\n    border-radius:8px;\n    }\n  table{\n    table-layout:fixed;\n    \n  }\n  </style>\n  <script>\n  var xhttp = new XMLHttpRequest();\n  xhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n    var avaliableMedia = JSON.parse(this.responseText);\n    \n    for (const [key, value] of Object.entries(avaliableMedia)) {\n      console.log(`${key}: ${value}`);\n      var newitem=document.createElement(\"li\");\n      newitem.setAttribute(\"id\", \"media\"+`${key}`);\n      newitem.appendChild(document.createTextNode(value));\n      newitem.onclick = function() {changeMedia(key)};\n      document.getElementById(\"avaliableMedia\").appendChild(newitem);\n    }\n      \n    }\n  };\n    xhttp.open(\"GET\", \"/avaliableMedia\", true);\n    xhttp.send();\n  \n  function changeMedia(id){\n  xhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n    var allButton = document.getElementsByTagName(\"li\");\n    for (var i = 0; i<allButton.length; i++) {\n      allButton[i].style.backgroundColor = \"#666666\";\n    }\n    document.getElementById('media'+id).style.backgroundColor = \"#68e884\";\n    }\n  };\n    xhttp.open(\"GET\", \"/changeMedia?id=\"+id, true);\n    xhttp.send();   \n  }\n  function changeAutoNext(){\n  xhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n    if(this.response == \"True\") document.getElementById(\"autoNextStatus\").innerHTML = \"⏸️\"\n    else if(this.response == \"False\") document.getElementById(\"autoNextStatus\").innerHTML = \"▶️\"\n    }\n  };\n    xhttp.open(\"GET\", \"/changeAutoNext\", true);\n    xhttp.send();\n  }\n</script>\n</head>\n<body>\n  <div style=\"width:80%;margin:0 auto;text-align:center\">\n  <big>bbPOV-P</big>\n  <div style=\"font-size:40px\" id=\"autoNextStatus\" onclick=\"changeAutoNext()\">⏸️</div>\n  <p>可用图像：</p>\n      <ul id=\"avaliableMedia\">\n      </ul>\n  </div>\n</body>\n</html>)rawliteral\";\n"
  },
  {
    "path": "Arduino/HardwareTest/JPEGDEC/JPEGDEC.ino",
    "content": "#include \"SD_MMC.h\"\n#include \"JPEGDEC.h\"\nJPEGDEC jpeg;\nFile myfile;\nvoid * myOpen(const char *filename, int32_t *size) {\n  Serial.println(\"Open\");\n  myfile = SD_MMC.open(filename);\n  *size = myfile.size();\n  return &myfile;\n}\nvoid myClose(void *handle) {\n  if (myfile) myfile.close();\n}\nint32_t myRead(JPEGFILE *handle, uint8_t *buffer, int32_t length) {\n  if (!myfile) return 0;\n  return myfile.read(buffer, length);\n}\nint32_t mySeek(JPEGFILE *handle, int32_t position) {\n  if (!myfile) return 0;\n  return myfile.seek(position);\n}\nuint16_t (*imgBuffer)[80];\nuint16_t (*imgBuffer2)[80];\n// Function to draw pixels to the display\nint JPEGDraw(JPEGDRAW *pDraw) {\n\n  Serial.printf(\"jpeg draw: x,y=%d,%d, cx,cy = %d,%d\\n\",pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);\n  //Serial.printf(\"Before Pixel 80 = 0x%04x\\n\", pDraw->pPixels[80]);\n  int pixels=pDraw->iWidth*pDraw->iHeight;\n  if(pDraw->x+pDraw->y*80+pixels>=360*80){\n   pixels=pDraw->x+pDraw->y*80+pixels-360*80;\n  }\n  else{\n    memcpy(&imgBuffer[pDraw->y][pDraw->x],pDraw->pPixels,sizeof(uint16_t)*pixels);\n   // Serial.println(ESP.getFreeHeap());\n  }\n  return 1;\n}\n\nvoid setup()\n{\n  pinMode(2, INPUT_PULLUP);\n  Serial.begin(115200);\n  if(imgBuffer = (uint16_t(*)[80]) calloc(80*360,sizeof(uint16_t)))\n    Serial.println(\"Alloc memory1 OK\");\n  else\n    Serial.println(\"FUC KXJP\");\n  if(imgBuffer2 = (uint16_t(*)[80]) calloc(80*360,sizeof(uint16_t)))\n    Serial.println(\"Alloc memory2 OK\");\n  else\n    Serial.println(\"FUC KPLY\");\nif(!SD_MMC.begin(\"/sdcard\",true)){\n        Serial.println(\"Card Mount Failed\");\n    }\n    Serial.println(\"setup done\");\n} /* setup() */\nvoid loop() {\n  \nlong lTime;\n\n  if (jpeg.open(\"/sb.jpg\", myOpen, myClose, myRead, mySeek, JPEGDraw))\n  {\n    Serial.println(\"Successfully opened JPEG image\");\n    Serial.printf(\"Image size: %d x %d, orientation: %d, bpp: %d\\n\", jpeg.getWidth(),\n      jpeg.getHeight(), jpeg.getOrientation(), jpeg.getBpp());\n    if (jpeg.hasThumb())\n       Serial.printf(\"Thumbnail present: %d x %d\\n\", jpeg.getThumbWidth(), jpeg.getThumbHeight());\n    lTime = micros();\n    if (jpeg.decode(0,0,0))\n    {\n      lTime = micros() - lTime;\n      Serial.printf(\"Successfully decoded image in %d us\\n\", (int)lTime);\n    }\n    jpeg.close();\n  }\n  Serial.printf(\"After Pixel 80 = 0x%04x\\n\", imgBuffer[160][50]);\n  Serial.printf(\"\\n\\navailable heap in main %i\\n\", ESP.getFreeHeap());\n  Serial.printf(\"biggest free block: %i\\n\\”\", heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));\n  \n  delay(10000);\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/MultiThreadDivSpeedtest/MultiThreadDivSpeedtest.ino",
    "content": "#include <WiFi.h>\n#include <AsyncTCP.h>\n#include <ESPAsyncWebServer.h>\n#include <AsyncElegantOTA.h>\n#include <NeoPixelBus.h>\n#include <SPI.h>\n#include <ESPmDNS.h>\n#include \"SD_MMC.h\"\n#include \"JPEGDEC.h\"\n#include <Ticker.h>\n#include \"soc/timer_group_struct.h\"\n#include \"soc/timer_group_reg.h\"\n//显示相关\n#define PixelCount 80  //单边LED数量\n#define LedStripCount 2  //LED条数\nuint32_t Frame = 0;\nbyte Hall = 0;  //到达顶端的霍尔传感器代号\nuint32_t Div = 320;\n\n\n//一些机制需要用到的全局变量\nTicker hallHit;\nJPEGDEC jpeg;\nFile myfile;\n\nAsyncWebServer server(80);\nint numRot= 0;\nint numDiv = 0;\nint stateDiv = 0;\nint spinstae = 1;\nvolatile unsigned long rotTime, timeOld, timeNow, opeTime, spinTime;\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod2> strip2(PixelCount);\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod> strip(PixelCount);\n//Adafruit_DotStar_VSPI stripA(PixelCount,DOTSTAR_BRG);\n//CRGB leds[PixelCount];\n\nAsyncWebSocket ws(\"/ws\");\nRgbColor black(0);\n\n\nvoid RotCountCommon(){\n  \n  static unsigned long last_interrupt_time = 0;\n  unsigned long interrupt_time = millis();            //deBounce,as the signal not stable sometimes 去抖动\n    if (interrupt_time - last_interrupt_time > 20)\n    {    \n      numDiv=0;\n      timeNow = micros();\n      rotTime = timeNow - timeOld;\n      timeOld = timeNow;\n      //Serial.println(\"RotCount\");\n    }\n   last_interrupt_time = interrupt_time;\n  }\nTaskHandle_t loop1Handle,loop2Handle,loopSetledHandle;  \nvoid setup()\n{\n     pinMode(34,INPUT);\n     pinMode(35,INPUT);\n     Serial.begin(115200);\n     WiFi.mode(WIFI_AP);\n     WiFi.softAP(\"bbPOV-P\");\n      MDNS.begin(\"bbPOV\");\n      MDNS.addService(\"bbPOV\", \"tcp\", 80);\n     server.on(\"/\", HTTP_GET, [](AsyncWebServerRequest *request) {\n      request->send(200, \"text/plain\", \"Hi! I am bbPOV-P.\");\n    });\n    AsyncElegantOTA.begin(&server);    // Start ElegantOTA\n    if(!SD_MMC.begin(\"/sdcard\")){\n        Serial.println(\"Card Mount Failed\");\n    }\n    server.serveStatic(\"/\", SD_MMC, \"/\");\n    server.begin();\n    Serial.println(\"HTTP server started\");\n    strip.Begin();\n    strip.Show();\n    strip2.Begin(32,25,33,26);\n    strip2.Show();\n\n\n    RgbColor Divcolor (0,20,0);\n              for(int i = 0; i < PixelCount; i++){\n                strip.SetPixelColor(i, Divcolor);\n                strip2.SetPixelColor(i, Divcolor);\n              }\n    //stripA.begin(); // Initialize pins for output\n    //stripA.show();  // Turn all LEDs off ASAP\n//    FastLED.addLeds<APA102,23,18,BGR,DATA_RATE_MHZ(30)>(leds, PixelCount);\n  //  FastLED.show();\n    hallHit.attach(0.033,RotCountCommon);\n\n\n    Serial.println(\"Setup Done\");\n    \n    \n    \n  xTaskCreatePinnedToCore(\n    loop1\n    ,  \"loop1\"   // A name just for humans\n    ,  5000  // This stack size can be checked & adjusted by reading the Stack Highwater\n    ,  NULL\n    ,  5  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.\n    ,  &loop1Handle \n    ,  1);\n\n  xTaskCreatePinnedToCore(\n    loop2\n    ,  \"loop2\"\n    ,  5000  // Stack size\n    ,  NULL\n    ,  5  // Priority\n    ,  &loop2Handle \n    ,  1);\n\n  xTaskCreatePinnedToCore(\n    loopSetled\n    ,  \"loopSetled\"\n    ,  10000  // Stack size\n    ,  NULL\n    ,  5  // Priority\n    ,  &loopSetledHandle\n    ,  1);\n         \n}\n\nvoid loop() {\n    //AsyncElegantOTA.loop(); \n   //Serial.println(\"mainloop\");        \n}\n\nvoid loopSetled(void *pvParameters){\n  for (;;)\n  {\n    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n  TIMERG0.wdt_feed=1;\n  TIMERG0.wdt_wprotect=0;\n      if(stateDiv == 1 && micros() - timeOld > (rotTime / (Div/LedStripCount)) * (numDiv)){\n        stateDiv = 0;\n      }\n      if(stateDiv == 0 && micros() - timeOld < (rotTime / (Div/LedStripCount)) * (numDiv + 1 )){\n        stateDiv = 1;\n      //  long donetime=micros();\n       // switch(Hall){\n        //    case 0:\n              strip.ClearTo(black);\n              strip2.ClearTo(black);\n              RgbColor Divcolor (0,0,0);\n              if(numDiv%3==0)\n                   // CHOU = CRGB::Red;\n                   Divcolor.R=16;\n              if(numDiv%3==1)\n                    //CHOU = CRGB::Green;\n                   Divcolor.G=16;\n              if(numDiv%3==2)\n                   // CHOU = CRGB::Blue;\n                   Divcolor.B=16;\n             // long nowtime=micros();\n              for(int i = 0; i < PixelCount; i++){\n                strip.SetPixelColor(i, Divcolor);\n                strip2.SetPixelColor(i, Divcolor);\n              }\n             // Serial.printf(\"FUcking setpixel tiime:%d\",int(micros()-nowtime));\n              //  FastLED.show(); \n             //stripshowtime=micros();\n              //strip.Show();       \n              //strip2.Show();  \n              xTaskNotifyGive( loop1Handle );\n              xTaskNotifyGive( loop2Handle );\n\n              //ulTaskNotifyTake( pdTRUE, portMAX_DELAY );    \n            //  ulTaskNotifyTake( pdTRUE, portMAX_DELAY );  \n            //  stripshowtime=micros()-stripshowtime;\n          //  Serial.printf(\"FUcking stripshow tiime:%d\",int(stripshowtime));  \n              //  stripA.show();\n             // break;\n              /*\n            case 1:\n              strip.ClearTo(black);\n              strip2.ClearTo(black);\n              for(int i = 0; i < PixelCount; i++){\n               strip2.SetPixelColor(i, RgbColor(uint8_t((imgBuffer[numDiv][i] & 0xF800)>>8),uint8_t((imgBuffer[numDiv][i] & 0x07C0)>>3),uint8_t((imgBuffer[numDiv][i] & 0x001F)<<3)));\n               strip.SetPixelColor(i, RgbColor(uint8_t((imgBuffer[numDiv+Div/LedStripCount][i] & 0xF800)>>8),uint8_t((imgBuffer[numDiv+Div/LedStripCount][i] & 0x07C0)>>3),uint8_t((imgBuffer[numDiv+Div/LedStripCount][i] & 0x001F)<<3)));\n              }\n              strip2.Show();  \n              strip.Show();  \n              break;\n          }  \n              */\n              \n        numDiv++;\n        if(numDiv >= (Div / LedStripCount)) numDiv = 0;\n       // donetime=micros()-donetime;\n        //Serial.printf(\"FUcking done tiime:%d\",int(donetime));\n        }\n        if(stateDiv == 0 ){\n          \n          strip.ClearTo(black);\n          strip2.ClearTo(black);\n          }\n    }\n  }\n\nvoid loop1(void *pvParameters)  // This is a task.\n{\n\n  for (;;)\n  {\n  TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n  TIMERG0.wdt_feed=1;\n  TIMERG0.wdt_wprotect=0;\n\n  ulTaskNotifyTake( pdTRUE, portMAX_DELAY );\n \n  //long stripshowtime=micros();\n  //Serial.println(\"Strip1 Show\");\n    strip.Show();\n    // Serial.printf(\"[1]:%d\\n\",int(micros()-stripshowtime));\n   //Serial.println(\"[Strip1 Done]\");\n   //stripshowtime=micros()-stripshowtime;\n  // Serial.printf(\"[strip1]:%d\",int(stripshowtime));  \n  \n  //ESP_LOGD(\"[1]:%ld\\n\",micros()-stripshowtime);\n  }\n}\nvoid loop2(void *pvParameters)  // This is a task.\n{\n\n  for (;;)\n  {\n    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n  TIMERG0.wdt_feed=1;\n  TIMERG0.wdt_wprotect=0;\n  \n  ulTaskNotifyTake( pdTRUE, portMAX_DELAY );\n  \n// long stripshowtime=micros();\n    //Serial.println(\"Strip2 Show\");\n     strip2.Show();\n    // Serial.printf(\"[2]:%d\\n\",int(micros()-stripshowtime));\n    // xTaskNotifyGive( loopSetledHandle );\n   // Serial.println(\"[Strip2 Done]\");\n  // stripshowtime=micros()-stripshowtime;\n  // Serial.printf(\"[strip2]:%d\",int(stripshowtime));\n  //Serial.printf(\"[2]:%d\\n\",int(micros()-stripshowtime)); \n   // ESP_LOGD(\"[2]:%ld\\n\",micros()-stripshowtime);\n  }\n}\n  \n"
  },
  {
    "path": "Arduino/HardwareTest/NeoPixelBitmap/NeoPixelBitmap.ino",
    "content": "// NeoPixelBuffer\n// This example will animate pixels using a bitmap stored on a SD card\n// \n//\n// This will demonstrate the use of the NeoBitmapFile object \n// NOTE:  The images provided in the example directory should be copied to\n// the root of the SD card so the below code will find it.\n// NOTE:  This sample and the included images were built for a 144 pixel strip so\n// running this with a smaller string may not look as interesting.  Try providing\n// your own 24 bit bitmap for better results.\n\n#include <NeoPixelBrightnessBus.h>\n#include <NeoPixelAnimator.h>\n#include <SPI.h> \n#include <FS.h>\n#include \"SD_MMC.h\"\n\nconst uint16_t PixelCount = 32;\n#include \"SD_MMC.h\"\nNeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpiMethod2> strip2(PixelCount);\nNeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpiMethod> strip(PixelCount);\n\nNeoBitmapFile<DotStarBgrFeature, File> image;\nFile bitmapFile;\n\nvoid setup() {\n    pinMode(2, INPUT_PULLUP);\n    delay(3000);\n    Serial.begin(115200);\n    if(!SD_MMC.begin(\"/sdcard\",true)){\n        Serial.println(\"Card Mount Failed\");\n        return;\n    }\n    strip.Begin();\n    strip.SetBrightness(10);\n    strip.Show();\n    strip2.Begin(32,25,33,26);\n    strip2.SetBrightness(10);\n    strip2.Show();\n    // open the file\n    bitmapFile = SD_MMC.open(\"/1.bmp\", \"r\"); \n    image.Begin(bitmapFile);\n    image.Blt(strip, 0, 1, 1, 32);\n    strip.SetBrightness(1);\n    strip.Show();\n    image.Blt(strip2, 0, 1, 280, 32);\n    strip2.SetBrightness(10);\n    strip2.Show();\n    Serial.println(millis());\n\n\n}\n\nvoid loop() {\n    /*\n    bitmapFile = SD_MMC.open(\"/1.bmp\", \"r\"); \n    image.Begin(bitmapFile);\n    image.Blt(strip, 0, 1, 1, 32);\n    strip.Show();\n    image.Blt(strip2, 0, 1, 280, 32);\n    strip2.Show();\n    Serial.println(millis());\n    bitmapFile = SD_MMC.open(\"/2.bmp\", \"r\"); \n    image.Begin(bitmapFile);\n    image.Blt(strip, 0, 0, 1, 32);\n    strip.Show();\n    image.Blt(strip2, 0, 1, 280, 32);\n    strip2.Show();\n    Serial.println(millis());\n    */\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/NeoPixelFunLoop/NeoPixelFunLoop.ino",
    "content": "// NeoPixelFunLoop\n// This example will move a trail of light around a series of pixels.  \n// A ring formation of pixels looks best.  \n// The trail will have a slowly fading tail.\n// \n// This will demonstrate the use of the NeoPixelAnimator.\n// It shows the advanced use an animation to control the modification and \n// starting of other animations.\n// It also shows the normal use of animating colors.\n// It also demonstrates the ability to share an animation channel rather than\n// hard code them to pixels.\n//\n#include <WiFi.h>\n#include <AsyncTCP.h>\n#include <ESPAsyncWebServer.h>\n#include <AsyncElegantOTA.h>\n#include \"SD_MMC.h\"\n\nAsyncWebServer server(80);\n\n#include <NeoPixelBus.h>\n#include <NeoPixelAnimator.h>\n\n\nconst uint16_t PixelCount = 32; // make sure to set this to the number of pixels in your strip\nconst uint16_t PixelPin = 2;  // make sure to set this to the correct pin, ignored for Esp8266\nconst uint16_t AnimCount = PixelCount / 4 * 2 + 1; // we only need enough animations for the tail and one extra\n\nconst uint16_t PixelFadeDuration = 200; // third of a second\n// one second divide by the number of pixels = loop once a second\nconst uint16_t NextPixelMoveDuration = 500 / PixelCount; // how fast we move through the pixels\n\nNeoGamma<NeoGammaTableMethod> colorGamma;\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod> strip2(PixelCount);\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod> strip(PixelCount);\n// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.  \n// There are other Esp8266 alternative methods that provide more pin options, but also have\n// other side effects.\n// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods \n\n// what is stored for state is specific to the need, in this case, the colors and\n// the pixel to animate;\n// basically what ever you need inside the animation update function\nstruct MyAnimationState\n{\n    RgbColor StartingColor;\n    RgbColor EndingColor;\n    uint16_t IndexPixel; // which pixel this animation is effecting\n};\n\nNeoPixelAnimator animations(AnimCount); // NeoPixel animation management object\nMyAnimationState animationState[AnimCount];\nuint16_t frontPixel = 0;  // the front of the loop\nRgbColor frontColor;  // the color at the front of the loop\n\nvoid SetRandomSeed()\n{\n    uint32_t seed;\n\n    // random works best with a seed that can use 31 bits\n    // analogRead on a unconnected pin tends toward less than four bits\n    seed = analogRead(0);\n    delay(1);\n\n    for (int shifts = 3; shifts < 31; shifts += 3)\n    {\n        seed ^= analogRead(0) << shifts;\n        delay(1);\n    }\n\n    // Serial.println(seed);\n    randomSeed(seed);\n}\n\nvoid FadeOutAnimUpdate(const AnimationParam& param)\n{\n    // this gets called for each animation on every time step\n    // progress will start at 0.0 and end at 1.0\n    // we use the blend function on the RgbColor to mix\n    // color based on the progress given to us in the animation\n    RgbColor updatedColor = RgbColor::LinearBlend(\n        animationState[param.index].StartingColor,\n        animationState[param.index].EndingColor,\n        param.progress);\n    // apply the color to the strip\n    strip.SetPixelColor(animationState[param.index].IndexPixel, \n        colorGamma.Correct(updatedColor));\n        strip2.SetPixelColor(animationState[param.index].IndexPixel, \n        colorGamma.Correct(updatedColor));\n}\n\nvoid LoopAnimUpdate(const AnimationParam& param)\n{\n    // wait for this animation to complete,\n    // we are using it as a timer of sorts\n    if (param.state == AnimationState_Completed)\n    {\n        // done, time to restart this position tracking animation/timer\n        animations.RestartAnimation(param.index);\n\n        // pick the next pixel inline to start animating\n        // \n        frontPixel = (frontPixel + 1) % PixelCount; // increment and wrap\n        if (frontPixel == 0)\n        {\n            // we looped, lets pick a new front color\n            frontColor = HslColor(random(360) / 360.0f, 1.0f, 0.25f);\n        }\n\n        uint16_t indexAnim;\n        // do we have an animation available to use to animate the next front pixel?\n        // if you see skipping, then either you are going to fast or need to increase\n        // the number of animation channels\n        if (animations.NextAvailableAnimation(&indexAnim, 1))\n        {\n            animationState[indexAnim].StartingColor = frontColor;\n            animationState[indexAnim].EndingColor = RgbColor(0, 0, 0);\n            animationState[indexAnim].IndexPixel = frontPixel;\n\n            animations.StartAnimation(indexAnim, PixelFadeDuration, FadeOutAnimUpdate);\n        }\n    }\n}\n\nvoid setup()\n{\n    Serial.begin(115200);\n     WiFi.mode(WIFI_AP);\n     WiFi.softAP(\"bbPOV-P\");\n     server.on(\"/\", HTTP_GET, [](AsyncWebServerRequest *request) {\n    request->send(200, \"text/plain\", \"Hi! I am ESP32.\");\n  });\n\n  AsyncElegantOTA.begin(&server);    // Start ElegantOTA\n  if(!SD_MMC.begin(\"/sdcard\")){\n        Serial.println(\"Card Mount Failed\");\n       // return;\n    }\n  server.serveStatic(\"/\", SD_MMC, \"/\");\n  server.begin();\n  Serial.println(\"HTTP server started\");\n    strip2.Begin(32,25,33,26);\n    strip.Begin();\n\n    \n    SetRandomSeed();\n\n    // we use the index 0 animation to time how often we move to the next\n    // pixel in the strip\n    animations.StartAnimation(0, NextPixelMoveDuration, LoopAnimUpdate);\n}\n\n\nvoid loop()\n{\n    // this is all that is needed to keep it running\n    // and avoiding using delay() is always a good thing for\n    // any timing related routines\n    animations.UpdateAnimations();\n    strip.Show();\n    strip2.Show();\n    AsyncElegantOTA.loop();\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/NeoPixelRainbow/NeoPixelRainbow.ino",
    "content": "#include <WiFi.h>\n#include <AsyncTCP.h>\n#include <ESPAsyncWebServer.h>\n#include <AsyncElegantOTA.h>\n#include <NeoPixelBrightnessBus.h>\n#include <ESPmDNS.h>\nAsyncWebServer server(80);\nconst uint16_t PixelCount = 80;\n#include \"SD_MMC.h\"\nNeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpiMethod2> strip2(PixelCount);\nNeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpiMethod> strip(PixelCount);\nRgbColor color;\nuint8_t pos;\nAsyncWebSocket ws(\"/ws\");\n\nvoid IRAM_ATTR RotCount1() {\n  static unsigned long last_interrupt_time = 0;\n  unsigned long interrupt_time = millis();            //deBounce,as the signal not stable sometimes 去抖动\n  if (interrupt_time - last_interrupt_time > 20)\n  {\n    Serial.println(\"RotCount1\");\n  }\n  last_interrupt_time = interrupt_time;\n}\nvoid IRAM_ATTR RotCount2() {\n  static unsigned long last_interrupt_time = 0;\n  unsigned long interrupt_time = millis();\n  if (interrupt_time - last_interrupt_time > 20)\n  {\n    Serial.println(\"RotCount2\");\n  }\n  last_interrupt_time = interrupt_time;\n}\n\nvoid setup()\n{\n  pinMode(34, INPUT);\n  pinMode(35, INPUT);\n  pinMode(2, INPUT_PULLUP);\n  pinMode(4, INPUT_PULLUP);\n  pinMode(15, INPUT_PULLUP);\n  pinMode(13, INPUT_PULLUP);\n  pinMode(12, INPUT_PULLUP);\n  Serial.begin(115200);\n  WiFi.mode(WIFI_AP);\n  WiFi.softAP(\"bbPOV-P\");\n  MDNS.begin(\"bbclock\");\n  MDNS.addService(\"bbclock\", \"tcp\", 80);\n  server.on(\"/\", HTTP_GET, [](AsyncWebServerRequest * request) {\n    request->send(200, \"text/plain\", \"Hi! I am ESP32.\");\n  });\n  AsyncElegantOTA.begin(&server);    // Start ElegantOTA\n  if (!SD_MMC.begin(\"/sdcard\")) {\n    Serial.println(\"Card Mount Failed\");\n    // return;\n      server.on(\"/fuck\", HTTP_GET, [](AsyncWebServerRequest * request) {\n    request->send(200, \"text/plain\", \"Fuck.\");\n  });\n  }\n  server.serveStatic(\"/\", SD_MMC, \"/\");\n  server.begin();\n  Serial.println(\"HTTP server started\");\n  strip.Begin();\n  strip.SetBrightness(16);\n  strip.Show();\n  strip2.Begin(32, 25, 33, 26);\n  strip2.SetBrightness(16);\n  strip2.Show();\n  Serial.println(\"Setup Done\");\n\n  attachInterrupt(34, RotCount1, FALLING );\n  attachInterrupt(35, RotCount2, FALLING );\n}\nint Rainbowperiod = 5;\nunsigned long Rainbowtime_now = 0;\nuint16_t j = 0;\nvoid loop() {\n  AsyncElegantOTA.loop();\n  if (millis() > Rainbowtime_now + Rainbowperiod) {\n    Rainbowtime_now = millis();\n    if (j < 256 * 5) j++;\n    else j = 0;\n\n    for (uint16_t i = 0; i < PixelCount; i++)\n    {\n      // generate a value between 0~255 according to the position of the pixel\n      // along the strip\n      pos = ((i * 256 / PixelCount) + j) & 0xFF;\n      // calculate the color for the ith pixel\n      color = Wheel( pos );\n      // set the color of the ith pixel\n      strip.SetPixelColor(i, color);\n      strip2.SetPixelColor(i, color);\n    }\n\n    strip.Show();\n    strip2.Show();\n  }\n}\n\n// Input a value 0 to 255 to get a color value.\n// The colours are a transition r - g - b - back to r.\nRgbColor Wheel(uint8_t WheelPos)\n{\n  WheelPos = 255 - WheelPos;\n  if (WheelPos < 85)\n  {\n    return RgbColor(255 - WheelPos * 3, 0, WheelPos * 3);\n  } else if (WheelPos < 170)\n  {\n    WheelPos -= 85;\n    return RgbColor(0, WheelPos * 3, 255 - WheelPos * 3);\n  } else\n  {\n    WheelPos -= 170;\n    return RgbColor(WheelPos * 3, 255 - WheelPos * 3, 0);\n  }\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/NeoPixelStatic/NeoPixelStatic.ino",
    "content": "\n#include <WiFi.h>\n#include <AsyncTCP.h>\n#include <ESPAsyncWebServer.h>\n#include <AsyncElegantOTA.h>\n\n\nAsyncWebServer server(80);\n\n#include <NeoPixelBus.h>\nconst uint16_t PixelCount = 4;\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod> strip2(PixelCount);\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod> strip(PixelCount);\n\n\n#define colorSaturation 128\nRgbColor red(colorSaturation, 0, 0);\nRgbColor green(0, colorSaturation, 0);\nRgbColor blue(0, 0, colorSaturation);\nRgbColor white(colorSaturation);\nRgbColor black(0);\n\nvoid setup()\n{\n    Serial.begin(115200);\n     WiFi.mode(WIFI_AP);\n     WiFi.softAP(\"bbPOV-P\");\n     server.on(\"/\", HTTP_GET, [](AsyncWebServerRequest *request) {\n    request->send(200, \"text/plain\", \"Hi! I am ESP32.\");\n  });\n\n  AsyncElegantOTA.begin(&server);    // Start ElegantOTA\n  server.begin();\n  Serial.println(\"HTTP server started\");\n    strip2.Begin(32,25,33,26);\n    strip.Begin();\n    strip.SetPixelColor(0, red);\n    strip.SetPixelColor(1, green);\n    strip.SetPixelColor(2, blue);\n    strip.SetPixelColor(3, white);\n    strip.Show();\n}\n\n\nvoid loop()\n{\n    AsyncElegantOTA.loop();\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/SDMMC_Test/SDMMC_Test.ino",
    "content": "/*\n * Connect the SD card to the following pins:\n *\n * SD Card | ESP32               //Actually all add 1K pull up except the CLK/VSS/VDD\n *    D2       12\n *    D3       13\n *    CMD      15\n *    VSS      GND\n *    VDD      3.3V\n *    CLK      14\n *    VSS      GND\n *    D0       2  (add 1K pull up after flashing)\n *    D1       4\n */\n\n#include \"FS.h\"\n#include \"SD_MMC.h\"\n\nFile root;\nvoid setup(){\n    pinMode(15, INPUT_PULLUP);\n    pinMode(2, INPUT_PULLUP);\n    pinMode(4, INPUT_PULLUP);\n    pinMode(12, INPUT_PULLUP);\n    pinMode(13, INPUT_PULLUP);\n    Serial.begin(115200);\n    if(!SD_MMC.begin(\"/sdcard\",true)){\n        Serial.println(\"Card Mount Failed\");\n        return;\n    }\n    uint8_t cardType = SD_MMC.cardType();\n\n    if(cardType == CARD_NONE){\n        Serial.println(\"No SD_MMC card attached\");\n        return;\n    }\n\n    Serial.print(\"SD_MMC Card Type: \");\n    if(cardType == CARD_MMC){\n        Serial.println(\"MMC\");\n    } else if(cardType == CARD_SD){\n        Serial.println(\"SDSC\");\n    } else if(cardType == CARD_SDHC){\n        Serial.println(\"SDHC\");\n    } else {\n        Serial.println(\"UNKNOWN\");\n    }\n\n    uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);\n    Serial.printf(\"SD_MMC Card Size: %lluMB\\n\", cardSize);\n    Serial.printf(\"Total space: %lluMB\\n\", SD_MMC.totalBytes() / (1024 * 1024));\n    Serial.printf(\"Used space: %lluMB\\n\", SD_MMC.usedBytes() / (1024 * 1024));\n\n    root=SD_MMC.open(\"/\");\n}\n\nvoid loop(){\n    File file = root.openNextFile();\n    Serial.println(file.name());\n    if(!file){\n        root.rewindDirectory();\n      }\n      \n}\n"
  },
  {
    "path": "Arduino/HardwareTest/TCPReceive/TCPReceive.ino",
    "content": "\n#include <WiFi.h>\n\nconst char *ssid = \"Hollyshit_A\";\nconst char *password = \"00197633\";\n\nWiFiServer server; //声明服务器对象\n\n#include <JPEGDEC.h>\nJPEGDEC jpeg;\n\nint JPEGDraw(JPEGDRAW *pDraw)\n{\n  // do nothing\n  return 1; // continue decode\n} \n\nvoid setup()\n{\n    Serial.begin(115200);\n    Serial.println();\n\n    WiFi.mode(WIFI_STA);\n   // WiFi.setSleep(false); //关闭STA模式下wifi休眠，提高响应速度\n    WiFi.begin(ssid, password);\n    while (WiFi.status() != WL_CONNECTED)\n    {\n        delay(500);\n        Serial.print(\".\");\n    }\n    Serial.println(\"Connected\");\n    Serial.print(\"IP Address:\");\n    Serial.println(WiFi.localIP());\n\n    server.begin(22333); //服务器启动监听端口号22333\n}\n\nuint8_t streamBuffer[15*1024];\nvoid loop()\n{\n    WiFiClient client = server.available(); //尝试建立客户对象\n    if (client) //如果当前客户可用\n    {\n        Serial.println(\"[Client connected]\");\n        while (client.connected()) //如果客户端处于连接状态\n        {\n            if (client.available()) //如果有可读数据\n            {\n                String buff = client.readStringUntil('\\r');\n                int len = buff.toInt();\n                client.readBytes(streamBuffer,len);                    \n                if(streamBuffer[len-2]==0xFF && streamBuffer[len-1]==0xD9){   //JPG结尾\n                  if (jpeg.openRAM(streamBuffer, len, JPEGDraw)) {\n                    Serial.printf(\"Image size: %d x %d, orientation: %d, bpp: %d\\n\", jpeg.getWidth(),\n                    jpeg.getHeight(), jpeg.getOrientation(), jpeg.getBpp());\n                      if (jpeg.decode(0,0,0)) { // full sized decode\n                      }\n                      jpeg.close();\n                    }\n                  }\n            }\n        }\n      //  client.stop(); //结束当前连接:\n      //  Serial.println(\"[Client disconnected]\");\n    }\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/UDPReceive/UDPReceive.ino",
    "content": "#include \"WiFi.h\"\n#include \"AsyncUDP.h\"\n\nconst char * ssid = \"Hollyshit_A\";\nconst char * password = \"00197633\";\n\nAsyncUDP udp;\n\nvoid setup()\n{\n    Serial.begin(115200);\n    WiFi.mode(WIFI_STA);\n    WiFi.begin(ssid, password);\n    if (WiFi.waitForConnectResult() != WL_CONNECTED) {\n        Serial.println(\"WiFi Failed\");\n        while(1) {\n            delay(1000);\n        }\n    }\n    if(udp.listen(1234)) {\n        Serial.print(\"UDP Listening on IP: \");\n        Serial.println(WiFi.localIP());\n        udp.onPacket([](AsyncUDPPacket packet) {\n            Serial.print(\"UDP Packet Type: \");\n            Serial.print(packet.isBroadcast()?\"Broadcast\":packet.isMulticast()?\"Multicast\":\"Unicast\");\n            Serial.print(\", From: \");\n            Serial.print(packet.remoteIP());\n            Serial.print(\":\");\n            Serial.print(packet.remotePort());\n            Serial.print(\", To: \");\n            Serial.print(packet.localIP());\n            Serial.print(\":\");\n            Serial.print(packet.localPort());\n            Serial.print(\", Length: \");\n            Serial.print(packet.length());\n            Serial.print(\", Data: \");\n            Serial.write(packet.data(), packet.length());\n            Serial.println();\n            //reply to the client\n            packet.printf(\"Got %u bytes of data\", packet.length());\n        });\n    }\n}\n\nvoid loop()\n{\n    delay(1000);\n    //Send broadcast\n    udp.broadcast(\"Anyone here?\");\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/WebServer/WebServer.ino",
    "content": "#include <ArduinoOTA.h>\n#ifdef ESP32\n#include <FS.h>\n#include \"SD_MMC.h\"\n#include <ESPmDNS.h>\n#include <WiFi.h>\n#include <AsyncTCP.h>\n#elif defined(ESP8266)\n#include <ESP8266WiFi.h>\n#include <ESPAsyncTCP.h>\n#include <ESP8266mDNS.h>\n#endif\n#include <ESPAsyncWebServer.h>\n#include <JPEGDEC.h>\nJPEGDEC jpeg;\n\nint JPEGDraw(JPEGDRAW *pDraw)\n{\n  // do nothing\n  return 1; // continue decode\n} \n\n// SKETCH BEGIN\nAsyncWebServer server(80);\nAsyncWebSocket ws(\"/ws\");\nAsyncEventSource events(\"/events\");\n\nvoid onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){\n  if(type == WS_EVT_CONNECT){\n    Serial.printf(\"ws[%s][%u] connect\\n\", server->url(), client->id());\n    client->printf(\"Hello Client %u :)\", client->id());\n    client->ping();\n  } else if(type == WS_EVT_DISCONNECT){\n    Serial.printf(\"ws[%s][%u] disconnect\\n\", server->url(), client->id());\n  } else if(type == WS_EVT_ERROR){\n    Serial.printf(\"ws[%s][%u] error(%u): %s\\n\", server->url(), client->id(), *((uint16_t*)arg), (char*)data);\n  } else if(type == WS_EVT_PONG){\n    Serial.printf(\"ws[%s][%u] pong[%u]: %s\\n\", server->url(), client->id(), len, (len)?(char*)data:\"\");\n  } else if(type == WS_EVT_DATA){\n    AwsFrameInfo * info = (AwsFrameInfo*)arg;\n    String msg = \"\";\n    if(info->final && info->index == 0 && info->len == len){\n      //the whole message is in a single frame and we got all of it's data\n     // Serial.printf(\"ws[%s][%u] %s-message[%llu]: \", server->url(), client->id(), (info->opcode == WS_TEXT)?\"text\":\"binary\", info->len);\n\n      if(info->opcode == WS_TEXT){\n        for(size_t i=0; i < info->len; i++) {\n          msg += (char) data[i];\n        }\n      } else {\n        char buff[3];\n        for(size_t i=0; i < info->len; i++) {\n          sprintf(buff, \"%02x \", (uint8_t) data[i]);\n          msg += buff ;\n        }\n      }\n     // Serial.printf(\"%s\\n\",msg.c_str());\n\n      if(info->opcode == WS_TEXT)\n        client->text(\"I got your text message\");\n      else\n        client->binary(\"I got your binary message\");\n    } else {\n      //message is comprised of multiple frames or the frame is split into multiple packets\n      if(info->index == 0){\n      //  if(info->num == 0)\n      //    Serial.printf(\"ws[%s][%u] %s-message start\\n\", server->url(), client->id(), (info->message_opcode == WS_TEXT)?\"text\":\"binary\");\n      //  Serial.printf(\"ws[%s][%u] frame[%u] start[%llu]\\n\", server->url(), client->id(), info->num, info->len);\n      }\n\n    //  Serial.printf(\"ws[%s][%u] frame[%u] %s[%llu - %llu]: \", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?\"text\":\"binary\", info->index, info->index + len);\n\n      if(info->opcode == WS_TEXT){\n        for(size_t i=0; i < len; i++) {\n          msg += (char) data[i];\n        }\n      } else {\n        char buff[3];\n        for(size_t i=0; i < len; i++) {\n          sprintf(buff, \"%02x \", (uint8_t) data[i]);\n          msg += buff ;\n        }\n      }\n    //  Serial.printf(\"%s\\n\",msg.c_str());\n\n      if((info->index + len) == info->len){\n      //  Serial.printf(\"ws[%s][%u] frame[%u] end[%llu]\\n\", server->url(), client->id(), info->num, info->len);\n        if(info->final){\n        //  Serial.printf(\"ws[%s][%u] %s-message end\\n\", server->url(), client->id(), (info->message_opcode == WS_TEXT)?\"text\":\"binary\");\n          if(info->message_opcode == WS_TEXT)\n            client->text(\"I got your text message\");\n          else\n            client->binary(\"I got your binary message\");\n        }\n      }\n    }\n  }\n}\n\n\nconst char* ssid = \"Hollyshit_A\";\nconst char* password = \"00197633\";\nconst char * hostName = \"esp-async\";\nconst char* http_username = \"admin\";\nconst char* http_password = \"admin\";\n\nvoid setup(){\n  Serial.begin(115200);\n  Serial.setDebugOutput(true);\n  WiFi.mode(WIFI_AP_STA);\n  WiFi.softAP(hostName);\n  WiFi.begin(ssid, password);\n  if (WiFi.waitForConnectResult() != WL_CONNECTED) {\n    Serial.printf(\"STA: Failed!\\n\");\n    WiFi.disconnect(false);\n    delay(1000);\n    WiFi.begin(ssid, password);\n  }\n\n  //Send OTA events to the browser\n  ArduinoOTA.onStart([]() { events.send(\"Update Start\", \"ota\"); });\n  ArduinoOTA.onEnd([]() { events.send(\"Update End\", \"ota\"); });\n  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {\n    char p[32];\n    sprintf(p, \"Progress: %u%%\\n\", (progress/(total/100)));\n    events.send(p, \"ota\");\n  });\n  ArduinoOTA.onError([](ota_error_t error) {\n    if(error == OTA_AUTH_ERROR) events.send(\"Auth Failed\", \"ota\");\n    else if(error == OTA_BEGIN_ERROR) events.send(\"Begin Failed\", \"ota\");\n    else if(error == OTA_CONNECT_ERROR) events.send(\"Connect Failed\", \"ota\");\n    else if(error == OTA_RECEIVE_ERROR) events.send(\"Recieve Failed\", \"ota\");\n    else if(error == OTA_END_ERROR) events.send(\"End Failed\", \"ota\");\n  });\n  ArduinoOTA.setHostname(hostName);\n  ArduinoOTA.begin();\n\n  MDNS.addService(\"http\",\"tcp\",80);\n\n  if(!SD_MMC.begin(\"/sdcard\",true)){\n        Serial.println(\"Card Mount Failed\");\n    }   \n\n  ws.onEvent(onWsEvent);\n  server.addHandler(&ws);\n\n  events.onConnect([](AsyncEventSourceClient *client){\n    client->send(\"hello!\",NULL,millis(),1000);\n  });\n  server.addHandler(&events);\n  \n  server.on(\"/heap\", HTTP_GET, [](AsyncWebServerRequest *request){\n    request->send(200, \"text/plain\", String(ESP.getFreeHeap()));\n  });\n\n  server.serveStatic(\"/\", SD_MMC, \"/\").setDefaultFile(\"index.htm\");\n\n  server.onNotFound([](AsyncWebServerRequest *request){\n\n  });\n  server.onFileUpload(handleUpload);\n  server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){\n    if(!index)\n      Serial.printf(\"BodyStart: %u\\n\", total);\n    Serial.printf(\"%s\", (const char*)data);\n    if(index + len == total)\n      Serial.printf(\"BodyEnd: %u\\n\", total);\n  });\n  server.begin();\n}\n\nvoid loop(){\n  ArduinoOTA.handle();\n  ws.cleanupClients();\n}\nuint8_t streamBuffer[10*1024];\nlong lTime;\nvoid handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){\n  if(!index){\n    lTime=micros();\n    Serial.println((String)\"UploadStart: \" + filename);\n    // open the file on first call and store the file handle in the request object\n    //request->_tempFile = SD_MMC.open(\"/bbPOV-P/Stream/\"+filename, \"w\");\n  }\n  if(len) {\n    //Serial.print(\"index:\");\n    //Serial.println(index);\n   // Serial.print(\"len:\");\n   // Serial.println(len);\n    memcpy(&streamBuffer[index],data,len);\n    // stream the incoming chunk to the opened file\n  //  request->_tempFile.write(data,len);\n  }\n  if(final){\n    Serial.print(\"All length:\");\n    Serial.println(index+len);\n    if (jpeg.openRAM(streamBuffer, index+len, JPEGDraw)) {\n                      \n                    Serial.printf(\"Image size: %d x %d, orientation: %d, bpp: %d\\n\", jpeg.getWidth(),\n                    jpeg.getHeight(), jpeg.getOrientation(), jpeg.getBpp());\n                      if (jpeg.decode(0,0,0)) { // full sized decode\n                        lTime = micros() - lTime;\n                        Serial.printf(\"Total time %d us\\n\", (int)lTime);\n                      }\n                      jpeg.close();\n     }\n    // close the file handle as the upload is now done\n    //request->_tempFile.close();\n   // request->send(200);\n  }\n}\n"
  },
  {
    "path": "Arduino/HardwareTest/WebServer/data/.exclude.files",
    "content": "/*.js.gz\n/.exclude.files\n"
  },
  {
    "path": "Arduino/HardwareTest/WebServer/data/index.htm",
    "content": "<!--\n  FSWebServer - Example Index Page\n  Copyright (c) 2015 Hristo Gochkov. All rights reserved.\n  This file is part of the ESP8266WebServer library for Arduino environment.\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n-->\n<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n    <title>WebSocketTester</title>\n    <style type=\"text/css\" media=\"screen\">\n    body {\n      margin:0;\n      padding:0;\n      background-color: black;\n    }\n\n    #dbg, #input_div, #input_el {\n      font-family: monaco;\n      font-size: 12px;\n      line-height: 13px;\n      color: #AAA;\n    }\n\n    #dbg, #input_div {\n      margin:0;\n      padding:0;\n      padding-left:4px;\n    }\n\n    #input_el {\n      width:98%;\n      background-color: rgba(0,0,0,0);\n      border: 0px;\n    }\n    #input_el:focus {\n      outline: none;\n    }\n    </style>\n    <script type=\"text/javascript\">\n    var ws = null;\n    function ge(s){ return document.getElementById(s);}\n    function ce(s){ return document.createElement(s);}\n    function stb(){ window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight); }\n    function sendBlob(str){\n      var buf = new Uint8Array(str.length);\n      for (var i = 0; i < str.length; ++i) buf[i] = str.charCodeAt(i);\n      ws.send(buf);\n    }\n    function addMessage(m){\n      var msg = ce(\"div\");\n      msg.innerText = m;\n      ge(\"dbg\").appendChild(msg);\n      stb();\n    }\n    function startSocket(){\n      ws = new WebSocket('ws://'+document.location.host+'/ws',['arduino']);\n      ws.binaryType = \"arraybuffer\";\n      ws.onopen = function(e){\n        addMessage(\"Connected\");\n      };\n      ws.onclose = function(e){\n        addMessage(\"Disconnected\");\n      };\n      ws.onerror = function(e){\n        console.log(\"ws error\", e);\n        addMessage(\"Error\");\n      };\n      ws.onmessage = function(e){\n        var msg = \"\";\n        if(e.data instanceof ArrayBuffer){\n          msg = \"BIN:\";\n          var bytes = new Uint8Array(e.data);\n          for (var i = 0; i < bytes.length; i++) {\n            msg += String.fromCharCode(bytes[i]);\n          }\n        } else {\n          msg = \"TXT:\"+e.data;\n        }\n        addMessage(msg);\n      };\n      ge(\"input_el\").onkeydown = function(e){\n        stb();\n        if(e.keyCode == 13 && ge(\"input_el\").value != \"\"){\n          ws.send(ge(\"input_el\").value);\n          ge(\"input_el\").value = \"\";\n        }\n      }\n    }\n    function startEvents(){\n      var es = new EventSource('/events');\n      es.onopen = function(e) {\n        addMessage(\"Events Opened\");\n      };\n      es.onerror = function(e) {\n        if (e.target.readyState != EventSource.OPEN) {\n          addMessage(\"Events Closed\");\n        }\n      };\n      es.onmessage = function(e) {\n        addMessage(\"Event: \" + e.data);\n      };\n      es.addEventListener('ota', function(e) {\n        addMessage(\"Event[ota]: \" + e.data);\n      }, false);\n    }\n    function onBodyLoad(){\n      startSocket();\n      startEvents();\n    }\n    </script>\n  </head>\n  <body id=\"body\" onload=\"onBodyLoad()\">\n    <pre id=\"dbg\"></pre>\n    <div id=\"input_div\">\n      $<input type=\"text\" value=\"\" id=\"input_el\">\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "Arduino/HardwareTest/strandtest/strandtest.ino",
    "content": "// Simple strand test for Adafruit Dot Star RGB LED strip.\n// This is a basic diagnostic tool, NOT a graphics demo...helps confirm\n// correct wiring and tests each pixel's ability to display red, green\n// and blue and to forward data down the line.  By limiting the number\n// and color of LEDs, it's reasonably safe to power a couple meters off\n// the Arduino's 5V pin.  DON'T try that with other code!\n#include <WiFi.h>\n#include <AsyncTCP.h>\n#include <ESPAsyncWebServer.h>\n#include <AsyncElegantOTA.h>\n#include <Adafruit_DotStar_VSPI.h>\n// Because conditional #includes don't work w/Arduino sketches...\n#include <SPI.h>         // COMMENT OUT THIS LINE FOR GEMMA OR TRINKET\n//#include <avr/power.h> // ENABLE THIS LINE FOR GEMMA OR TRINKET\n\n#define NUMPIXELS 80 // Number of LEDs in strip\n\n// Here's how to control the LEDs from any two pins:\n#define DATAPIN    23\n#define CLOCKPIN   18\n//Adafruit_DotStar strip(NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_BRG);\n// The last parameter is optional -- this is the color data order of the\n// DotStar strip, which has changed over time in different production runs.\n// Your code just uses R,G,B colors, the library then reassigns as needed.\n// Default is DOTSTAR_BRG, so change this if you have an earlier strip.\n\n// Hardware SPI is a little faster, but must be wired to specific pins\n// (Arduino Uno = pin 11 for data, 13 for clock, other boards are different).\nAdafruit_DotStar_VSPI strip(NUMPIXELS, DOTSTAR_BRG);\nAsyncWebServer server(80);\nvoid setup() {\n  WiFi.mode(WIFI_AP);\n  WiFi.softAP(\"bbPOV-P\");\n  server.on(\"/\", HTTP_GET, [](AsyncWebServerRequest *request) {\n      request->send(200, \"text/plain\", \"Hi! I am bbPOV-P.\");\n    });\n  AsyncElegantOTA.begin(&server);\n  server.begin();\n  Serial.println(\"HTTP server started\");\n  strip.begin(); // Initialize pins for output\n  strip.show();  // Turn all LEDs off ASAP\n  \n}\n\n// Runs 10 LEDs at a time along strip, cycling through red, green and blue.\n// This requires about 200 mA for all the 'on' pixels + 1 mA per 'off' pixel.\n\nint      head  = 0, tail = -10; // Index of first 'on' and 'off' pixels\nuint32_t color = 0xFF0000;      // 'On' color (starts red)\n\nvoid loop() {\n  AsyncElegantOTA.loop(); \n  strip.setPixelColor(head, color); // 'On' pixel at head\n  strip.setPixelColor(tail, 0);     // 'Off' pixel at tail\n  strip.show();                     // Refresh strip\n  delay(20);                        // Pause 20 milliseconds (~50 FPS)\n\n  if(++head >= NUMPIXELS) {         // Increment head index.  Off end of strip?\n    head = 0;                       //  Yes, reset head index to start\n    if((color >>= 8) == 0)          //  Next color (R->G->B) ... past blue now?\n      color = 0xFF0000;             //   Yes, reset to red\n  }\n  if(++tail >= NUMPIXELS) tail = 0; // Increment, reset tail index\n}\n"
  },
  {
    "path": "Arduino/bbPOV-P/bbPOV-P.ino",
    "content": "#include <WiFi.h>\n#include <AsyncTCP.h>\n#include <WebServer.h>\n#include <ElegantOTA.h>\n#include <NeoPixelBus.h>\n#include <SPI.h>\n#include <ESPmDNS.h>\n#include \"SD_MMC.h\"\n#include \"JPEGDEC.h\"\n#include \"soc/timer_group_struct.h\"\n#include \"soc/timer_group_reg.h\"\n#include <ArduinoJson.h>\n#include \"webpage.h\"\n//显示相关\n#define PixelCount 80  //单边LED数量\n#define LedStripCount 2  //LED条数\n#define BufferNum 2\n#define Div 320\n#define MaxStreamBuffer 10*1024\n#define OFFSET_34 0\n#define OFFSET_35 0\n\nuint16_t (*imgBuffer)[320][PixelCount];\nuint8_t streamBuffer[MaxStreamBuffer];\n\n//一些机制需要用到的全局变量\nJPEGDEC jpeg;\nFile root;\nFile dir;\nFile myfile;\nTaskHandle_t nextFileHandle; \nint bufferRot=-1;\nWebServer server(80);\nint numRot= 0;\nint numDiv = 0;\nint stateDiv = 0;\nint spinstae = 1;\nvolatile unsigned long rotTime, timeOld, timeNow, opeTime, spinTime;\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod2> strip2(PixelCount);\nNeoPixelBus<DotStarBgrFeature, DotStarSpiMethod> strip(PixelCount);\nDynamicJsonDocument doc(4096);\nJsonArray avaliableMedia = doc.to<JsonArray>();\nint displayMode = 0;\nint curMedia = 0;\nWiFiServer tcpStream; //声明服务器对象\nWiFiClient client;\nbool autoNext = true;\nRgbColor black(0);\n\n\nvoid IRAM_ATTR RotCount(){\nstatic unsigned long last_interrupt_time = 0;\n    unsigned long interrupt_time = millis();            //deBounce,as the signal not stable sometimes 去抖动\n    if (interrupt_time - last_interrupt_time > 20)\n    {  \n      numDiv = 0;\n      bufferRot++;\n      if(bufferRot>=BufferNum-1) bufferRot=0;\n      timeNow = micros();\n      rotTime = timeNow - timeOld;\n      timeOld = timeNow;\n      xTaskNotifyGive( nextFileHandle );\n    }\n  last_interrupt_time = interrupt_time;\n}\n  \n\n\nvoid setup()\n{\n     pinMode(34,INPUT);\n     pinMode(35,INPUT); \n     Serial.begin(115200);\n     if(imgBuffer = (uint16_t(*)[320][PixelCount]) calloc(PixelCount*Div*BufferNum,sizeof(uint16_t)))\n        Serial.println(\"Alloc IMG Memory OK\");\n     if(!SD_MMC.begin(\"/sdcard\")){\n        Serial.println(\"Card Mount Failed\");\n    }   \n    /*\n     WiFi.mode(WIFI_AP);\n     WiFi.softAP(\"bbPOV-P\");\n      MDNS.begin(\"bbPOV\");\n      MDNS.addService(\"bbPOV\", \"tcp\", 80);*/\n      WiFi.begin(\"Hollyshit_A\", \"00197633\");\n\n    while (WiFi.status() != WL_CONNECTED) {\n        delay(500);\n        Serial.print(\".\");\n    }\n\n    Serial.println(\"\");\n    Serial.println(\"WiFi connected\");\n    Serial.println(\"IP address: \");\n    Serial.println(WiFi.localIP());\n      server.on(\"/\", []() {\n      server.send_P(200, \"text/html\", index_html);\n    });\n  server.on(\"/avaliableMedia\", []() {\n      String json;\n      serializeJson(doc, json);\n      server.send(200, \"text/plain\", json);\n    });\n  server.on(\"/changeMedia\",[]() {\n      int mediaID = server.arg(0).toInt();\n      Serial.println(mediaID);\n      dir=SD_MMC.open(\"/bbPOV-P/\"+avaliableMedia[mediaID].as<String>());\n      server.send(200, \"text/plain\", \"OK\");\n    });\n  server.on(\"/changeAutoNext\",[]() {\n      autoNext = !autoNext;\n      if(autoNext) server.send(200, \"text/plain\", \"True\"); \n      else server.send(200, \"text/plain\", \"False\"); \n  });  \n    ElegantOTA.begin(&server);      \n    server.begin();   \n    Serial.println(\"HTTP server started\");\n    tcpStream.begin(22333); //服务器启动监听端口号22333\n    strip.Begin();\n    strip.Show();\n    strip2.Begin(32,25,33,26);\n    strip2.Show();\n    long lTime;\n\n    root=SD_MMC.open(\"/bbPOV-P\"); \n    while(true){\n      File entry = root.openNextFile();\n      if(!entry) break;\n      if(entry.isDirectory()){\n          avaliableMedia.add(String(entry.name()).substring(9));\n        }\n      }\n    dir=SD_MMC.open(\"/bbPOV-P/\"+avaliableMedia[0].as<String>());\n  if (jpeg.open(\"\", myOpen, myClose, myRead, mySeek, JPEGDraw))\n  {\n    lTime = micros();\n    if (jpeg.decode(0,0,0))\n    {\n      lTime = micros() - lTime;\n      Serial.printf(\"Successfully decoded image in %d us\\n\", (int)lTime);\n    }\n    jpeg.close();\n  }\n  bufferRot=0;\n  attachInterrupt(35, RotCount, FALLING );\n\n      xTaskCreatePinnedToCore(\n    nextFile\n    ,  \"nextFile\"\n    ,  5000  // Stack size\n    ,  NULL\n    ,  4  // Priority\n    ,  &nextFileHandle \n    ,  0); \n        xTaskCreatePinnedToCore(\n    webloop\n    ,  \"webloop\"\n    ,  5000  // Stack size\n    ,  NULL\n    ,  2 // Priority\n    ,  NULL \n    ,  0); \n    Serial.println(\"Setup Done\");\n    vTaskPrioritySet(NULL, 5);              \n}\nvoid loop() { \n  if(stateDiv == 1 && micros() - timeOld > (rotTime / Div) * (numDiv)){\n        stateDiv = 0;\n      }\n      if(stateDiv == 0 && micros() - timeOld < (rotTime / Div) * (numDiv + 1 )){\n        stateDiv = 1;\n      //  long donetime=micros();\n        int showNumDiv = numDiv;\n        if(showNumDiv<Div/LedStripCount){\n              for(int i = 0; i < PixelCount; i++){\n                uint16_t color = imgBuffer[bufferRot][showNumDiv][i];\n                uint16_t color2 = imgBuffer[bufferRot][showNumDiv+Div/LedStripCount][i];\n                strip.SetPixelColor(i, RgbColor(uint8_t((color & 0xF800)>>8),uint8_t((color & 0x07C0)>>3),uint8_t((color & 0x001F)<<3)));\n                strip2.SetPixelColor(i, RgbColor(uint8_t((color2 & 0xF800)>>8),uint8_t((color2 & 0x07C0)>>3),uint8_t((color2 & 0x001F)<<3)));\n              }\n        }\n        else{\n          for(int i = 0; i < PixelCount; i++){\n                uint16_t color = imgBuffer[bufferRot][showNumDiv][i];\n                uint16_t color2 = imgBuffer[bufferRot][showNumDiv-Div/LedStripCount][i];\n                strip.SetPixelColor(i, RgbColor(uint8_t((color & 0xF800)>>8),uint8_t((color & 0x07C0)>>3),uint8_t((color & 0x001F)<<3)));\n                strip2.SetPixelColor(i, RgbColor(uint8_t((color2 & 0xF800)>>8),uint8_t((color2 & 0x07C0)>>3),uint8_t((color2 & 0x001F)<<3)));\n              }\n          }\n              strip.Show();  \n              strip2.Show();  \n              \n        numDiv++;\n        if(numDiv == (Div / LedStripCount)){\n          bufferRot++;\n          if(bufferRot>=BufferNum-1) bufferRot=0;\n          xTaskNotifyGive( nextFileHandle );\n        }\n        if(numDiv >= Div) numDiv=0;    \n        \n        /*\n        if(stateDiv == 0 ){\n          \n          strip.ClearTo(black);\n          strip.Show();\n          strip2.ClearTo(black);\n          strip2.Show();\n          }*/\n  }\n} \n\nvoid webloop(void *pvParameters)\n{\n  for (;;)\n  {\n    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n    TIMERG0.wdt_feed=1;\n    TIMERG0.wdt_wprotect=0;\n    server.handleClient();\n  }\n}\n\n \nvoid * myOpen(const char *filename, int32_t *size) {\n // Serial.println(\"Open\");\n  myfile = dir.openNextFile();\n  if(!myfile){\n        if(autoNext){\n        curMedia++;\n        if(curMedia>=avaliableMedia.size()) curMedia=0;\n        dir=SD_MMC.open(\"/bbPOV-P/\"+avaliableMedia[curMedia].as<String>());\n        }\n        else dir.rewindDirectory();\n        myfile = dir.openNextFile();\n      }\n// Serial.println(myfile.name());\n  *size = myfile.size();\n  return &myfile;\n}\nvoid myClose(void *handle) {\n  if (myfile) myfile.close();\n}\nint32_t myRead(JPEGFILE *handle, uint8_t *buffer, int32_t length) {\n  if (!myfile) return 0;\n  return myfile.read(buffer, length);\n}\nint32_t mySeek(JPEGFILE *handle, int32_t position) {\n  if (!myfile) return 0;\n  return myfile.seek(position);\n}\n\nint JPEGDraw(JPEGDRAW *pDraw) {\n\n // Serial.printf(\"jpeg draw: x,y=%d,%d, cx,cy = %d,%d\\n\",pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);\n  //Serial.printf(\"Before Pixel 80 = 0x%04x\\n\", pDraw->pPixels[80]);\n  int sdbufferRot = bufferRot+1;\n  if(sdbufferRot >= BufferNum-1) sdbufferRot=0;\n  int pixels=pDraw->iWidth*pDraw->iHeight;\n    memcpy(&imgBuffer[sdbufferRot][pDraw->y][pDraw->x],pDraw->pPixels,sizeof(uint16_t)*pixels);\n   // Serial.println(ESP.getFreeHeap());\n  return 1;\n}  \n\n\nvoid nextFile(void *pvParameters){\n  for (;;)\n  {\n  TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n  TIMERG0.wdt_feed=1;\n  TIMERG0.wdt_wprotect=0;\n  \n  client = tcpStream.available(); //尝试建立客户对象\n // client.setNoDelay(true);\n    if (client) //如果当前客户可用\n    {\n        Serial.println(\"[Client connected]\");\n        while (client.connected()) //如果客户端处于连接状态\n        {\n          TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;\n          TIMERG0.wdt_feed=1;\n          TIMERG0.wdt_wprotect=0;\n          ulTaskNotifyTake( pdTRUE, portMAX_DELAY );\n            if (client.available()) //如果有可读数据\n            {\n                String buff = client.readStringUntil('\\r');\n                int len = buff.toInt();\n                client.readBytes(streamBuffer,len);                              \n                  if (jpeg.openRAM(streamBuffer, len, JPEGDraw)) {\n                 //  Serial.printf(\"Image size: %d x %d, orientation: %d, bpp: %d\\n\", jpeg.getWidth(),\n                   // jpeg.getHeight(), jpeg.getOrientation(), jpeg.getBpp());\n                      if (jpeg.decode(0,0,0)) { // full sized decode\n                      }\n                      jpeg.close();\n                    }\n            }\n        }\n      //  client.stop(); //结束当前连接:\n      //  Serial.println(\"[Client disconnected]\");\n    }\n    else{\n      ulTaskNotifyTake( pdTRUE, portMAX_DELAY );\n //   Serial.println(\"File\");\n    //long lTime=micros();\n      if (jpeg.open(\"\", myOpen, myClose, myRead, mySeek, JPEGDraw))\n    {\n  \n      if (jpeg.decode(0,0,0))\n      {\n       // lTime = micros() - lTime;\n       // Serial.printf(\"Successfully decoded image in %d us\\n\", (int)lTime);\n      }\n      jpeg.close();\n    }\n  }\n}\n}\n"
  },
  {
    "path": "Arduino/bbPOV-P/webpage.h",
    "content": "const char index_html[] PROGMEM = R\"rawliteral(<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<head>\n <style>\n  big{\n    font-size:68px;\n  }\n  ul{\n  }\n  li{\n    text-align:center;\n    line-height:50px;\n    font-size:20px;\n    margin:2%;\n    float:left;\n    width:15%;\n    height:50px;\n    color:white;\n    padding:8px 20px 8px 20px;\n    background-color:#666666;\n    border-radius:8px;\n    }\n  table{\n    table-layout:fixed;\n    \n  }\n  </style>\n  <script>\n  var xhttp = new XMLHttpRequest();\n  xhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n    var avaliableMedia = JSON.parse(this.responseText);\n    \n    for (const [key, value] of Object.entries(avaliableMedia)) {\n      console.log(`${key}: ${value}`);\n      var newitem=document.createElement(\"li\");\n      newitem.setAttribute(\"id\", \"media\"+`${key}`);\n      newitem.appendChild(document.createTextNode(value));\n      newitem.onclick = function() {changeMedia(key)};\n      document.getElementById(\"avaliableMedia\").appendChild(newitem);\n    }\n      \n    }\n  };\n    xhttp.open(\"GET\", \"/avaliableMedia\", true);\n    xhttp.send();\n  \n  function changeMedia(id){\n  xhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n    var allButton = document.getElementsByTagName(\"li\");\n    for (var i = 0; i<allButton.length; i++) {\n      allButton[i].style.backgroundColor = \"#666666\";\n    }\n    document.getElementById('media'+id).style.backgroundColor = \"#68e884\";\n    }\n  };\n    xhttp.open(\"GET\", \"/changeMedia?id=\"+id, true);\n    xhttp.send();   \n  }\n  function changeAutoNext(){\n  xhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n    if(this.response == \"True\") document.getElementById(\"autoNextStatus\").innerHTML = \"⏸️\"\n    else if(this.response == \"False\") document.getElementById(\"autoNextStatus\").innerHTML = \"▶️\"\n    }\n  };\n    xhttp.open(\"GET\", \"/changeAutoNext\", true);\n    xhttp.send();\n  }\n</script>\n</head>\n<body>\n  <div style=\"width:80%;margin:0 auto;text-align:center\">\n  <big>bbPOV-P</big>\n  <div style=\"font-size:40px\" id=\"autoNextStatus\" onclick=\"changeAutoNext()\">⏸️</div>\n  <p>可用图像：</p>\n      <ul id=\"avaliableMedia\">\n      </ul>\n  </div>\n</body>\n</html>)rawliteral\";\n"
  },
  {
    "path": "Arduino/bbPOV-P/webpage.html",
    "content": "<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<head>\n\t<style>\n\tbig{\n\t\tfont-size:68px;\n\t}\n\tul{\n\t}\n\tli{\n\t\ttext-align:center;\n\t\tline-height:50px;\n\t\tfont-size:20px;\n\t\tmargin:2%;\n\t\tfloat:left;\n\t\twidth:15%;\n\t\theight:50px;\n\t\tcolor:white;\n\t\tpadding:8px 20px 8px 20px;\n\t\tbackground-color:#666666;\n\t\tborder-radius:8px;\n\t\t}\n\ttable{\n\t\ttable-layout:fixed;\n\t\t\n\t}\n\t</style>\n\t<script>\n\tvar xhttp = new XMLHttpRequest();\n\txhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n\t  var avaliableMedia = JSON.parse(this.responseText);\n\t  \n\t\tfor (const [key, value] of Object.entries(avaliableMedia)) {\n\t\t  console.log(`${key}: ${value}`);\n\t\t  var newitem=document.createElement(\"li\");\n\t\t  newitem.setAttribute(\"id\", \"media\"+`${key}`);\n\t\t  newitem.appendChild(document.createTextNode(value));\n\t\t  newitem.onclick = function() {changeMedia(key)};\n\t\t  document.getElementById(\"avaliableMedia\").appendChild(newitem);\n\t\t}\n      \n    }\n  };\n\t  xhttp.open(\"GET\", \"/avaliableMedia\", true);\n\t  xhttp.send();\n\t\n\tfunction changeMedia(id){\n\txhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n\t  var allButton = document.getElementsByTagName(\"li\");\n\t  for (var i = 0; i<allButton.length; i++) {\n\t\t\tallButton[i].style.backgroundColor = \"#666666\";\n\t\t}\n\t  document.getElementById('media'+id).style.backgroundColor = \"#68e884\";\n    }\n  };\n\t  xhttp.open(\"GET\", \"/changeMedia?id=\"+id, true);\n\t  xhttp.send();\t\t\n\t}\n\tfunction changeAutoNext(){\n\txhttp.onreadystatechange = function() {\n    if (this.readyState == 4 && this.status == 200) {\n\t  if(this.response == \"True\") document.getElementById(\"autoNextStatus\").innerHTML = \"⏸️\"\n\t  else document.getElementById(\"autoNextStatus\").innerHTML = \"▶️\"\n    }\n  };\n\t  xhttp.open(\"GET\", \"/changeMedia\", true);\n\t  xhttp.send();\n\t}\n</script>\n</head>\n<body>\n\t<div style=\"width:80%;margin:0 auto;text-align:center\">\n\t<big>bbPOV-P</big>\n\t<div style=\"font-size:40px\" id=\"autoNextStatus\" onclick=\"changeAutoNext()\">⏸️</div>\n\t<p>可用图像：</p>\n\t\t\t<ul id=\"avaliableMedia\">\n\t\t\t</ul>\n\t</div>\n</body>\n</html>"
  },
  {
    "path": "README.md",
    "content": "# bbPOV-P  [English](https://github.com/RealCorebb/bbPOV-P/blob/main/README_EN.md \"English\")\nA new milestone?  \n# ⭐ 100 Stars!!! Thank You!!!\n\n🔗[PCB工程文件地址](https://oshwhub.com/Corebb/bbpov-mcu_copy_copy_copy \"PCB工程文件地址\")  \n😄[3D模型、更详细的教程](https://www.afdian.com/@kuruibb \"3D模型、更详细的教程\")  \n🧵[Threads](https://www.threads.net/@coreoobb \"@coreoobb\") @coreoobb  \n🐧QQ 群（仅供交流，人满请加Discord）：647186542  \n😈Discord 频道：[加入](https://discord.gg/gvbcCtdQrk \"加入\")   \n▶️视频(Video):[Youtube](https://www.youtube.com/watch?v=HpYd48YgSek&t=5s \"Youtube\")[ Bilibili](https://www.bilibili.com/video/BV1Wy4y1a7t6 \" Bilibili\")  \n# 禁止搬运到Gitee  \n![image](https://github.com/RealCorebb/bbPOV-P/blob/main/IMG/logo.jpg?raw=true)\n新的自我突破\n# 目录结构：\n**API** 包含取模软件、串流软件，都在NewConvert目录下，其它为一些早期的版本  \n**Arduino--HardwareTest** 开发过程中用来测试LED是否正常、硬件性能的一些硬件测试的软件，最终的主程序中用不到  \n**Arduino--bbPOV-P** 主程序  \n# 依赖库 \n除了[NeoPixelBus](https://github.com/RealCorebb/NeoPixelBus \"NeoPixelBus\")需要用我修改过的版本以外，其它的一般找对应名字就能找到\n# 备选LED芯片\nAPA102-2020(好像有的版本IC芯片比较小，PWM速度不够)                          价格￥0.67  \nHD107S-2020（查看了Datasheet，应该是无论5050还是2020版本都有着27khz的PWM）   价格￥0.80  \nLC8822-2020（~~有的地方说是26khz的PWM，但Datasheet里没看到，不清楚~~已发现其就是APA107）           价格￥0.68  \nLC8823-2020（~~规格跟HD107S相似，但找不到购买地址~~已发现其就是HD107S又名NS107S）\n![image](https://github.com/RealCorebb/bbPOV-V3/blob/main/IMG/LED_Chips.jpg?raw=true)  \n"
  }
]