低成本作弊神器?使用ESP32将通义千问AI接入学生计算器

前因:

IT之家 9 月 24 日消息,YouTube 频道 ChromaLock 于 9 天前发布视频,介绍了名为 TI-32 的改造电路板,加装在德州仪器 TI-84 Plus 图形计算器上,可以接入 ChatGPT

IT之家查询公开资料,在 PSAT、SAT 和 ACT 大学入学考试、IB 和 AP 考试中,标准化组织已经批准考生使用 TI-84 Plus 图形计算器。

ChromaLock 探索了该计算器的连接端口,设计了名为 TI-32 的改造电路板,其中一个支持 Wi-Fi 的微型微控制器 Seed Studio ESP32-C3(价格约为 5 美元)以及其他元件,用于连接无线系统。

ChromaLock 为微控制器和计算器开发了定制软件,并在 GitHub 上已开源

该系统模拟了另一个 TI-84,允许人们使用计算器内置的“发送”和“获取”命令来传输文件。这样,用户就可以轻松下载一个启动程序,访问各种专为作弊设计的“小程序”。

除 ChatGPT 界面外,该设备还提供其他几种作弊工具。图像浏览器允许用户访问存储在中央服务器上的预先准备好的视觉辅助工具。

通过应用程序浏览器功能,学生不仅可以下载游戏作为考后娱乐,还可以下载伪装成程序源代码的文本小抄。

但是该计算器售价1100人民币左右,抛开ESP32C3不谈,还得改造电路板

所以准备制造一个低成本的AI分析器

制作思路:

(目前只是理论和思路,等我做出来发教程)

准备工作:

  • ESP32-DEVKIT *1
  • 0.96寸TFT彩屏 ST7735S 分辨率160x80 (推荐这个,因为是宽屏小屏,放到计算器不易看出)
  • ov2640摄像头模块 200w像素 支持jpeg输出
  • 通义千问api_key(需要去阿里云百炼申请:跳转过去
  • 由于是devkit板子,所以需要面包板,400孔和830孔都行

当然,也有其它的硬件可以选择ESP32CAM自带OV3660或者OV2640,上手即用

接线:

(摄像头以OV2640为例 屏幕以ST7735S为例)

摄像头模块引脚ESP32 DevKit C引脚
3.3V3.3V
GNDGND
SIOCGPIO27
SIODGPIO26
XCLKGPIO0
VSYNCGPIO25
HREFGPIO23
PCLKGPIO22
D7GPIO36
D6GPIO39
D5GPIO21
D4GPIO19
D3GPIO18
D2GPIO5
D1GPIO4
D0GPIO15
VSYNCGPIO25
HREFGPIO23
PCLKGPIO22
PWDNGPIO32
RESETGPIO-1
  1. 安装必要库

    • ArduinoJson:打开Arduino IDE,进入工具 -> 库管理,搜索ArduinoJson并安装。
    • ESP32-CAM:打开Arduino IDE,进入文件 -> 示例 -> ESP32 -> ESP32CAM,查看示例代码并安装相关库。
  • ESP32 和 ST7735S 的连接方式如下:
    • TFT_MISO -> 无需连接(SPI不需要)
    • TFT_MOSI -> GPIO23
    • TFT_SCLK -> GPIO18
    • TFT_CS -> GPIO5
    • TFT_DC -> GPIO16
    • TFT_RST -> GPIO17
    • 3.3V -> 3.3V
    • GND -> GND
安装必要库
  • Adafruit_ST7735:打开Arduino IDE,进入工具 -> 库管理,搜索Adafruit_ST7735并安装。
  • Adafruit_GFX:打开Arduino IDE,进入工具 -> 库管理,搜索Adafruit_GFX并安装。

 代码:

思路很简单,配置好WiFi 摄像头和通义千问后,无非就是把照片转为BASE64,构造一下请求体,最后解析下响应而已。此代码只是例子,需要根据实际效果调整,使用硬件模块不同所配置也不同

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>

// WiFi设置
const char* ssid = "SSID";
const char* password = "PASSWORD";

// 通义千问API设置
const char* api_url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions";
const char* api_key = "写自己的";

// 摄像头配置
#define PWDN_GPIO_NUM    32
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    0
#define SIOD_GPIO_NUM    26
#define SIOC_GPIO_NUM    27

#define Y9_GPIO_NUM      35
#define Y8_GPIO_NUM      34
#define Y7_GPIO_NUM      39
#define Y6_GPIO_NUM      36
#define Y5_GPIO_NUM      21
#define Y4_GPIO_NUM      19
#define Y3_GPIO_NUM      18
#define Y2_GPIO_NUM      5
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22

// TFT LCD配置
#define TFT_CS 5
#define TFT_DC 16
#define TFT_RST 17
#define TFT_MOSI 23
#define TFT_SCLK 18

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

void setup() {
  // 初始化串口通信
  Serial.begin(115200);

  // 初始化TFT LCD
  tft.initR(INITR_BLACKTAB);  // 初始化屏幕
  tft.setRotation(1);  // 设置屏幕方向
  tft.fillScreen(ST7735_BLACK);

  // 初始化摄像头
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_UXGA;  // 分辨率
  config.jpeg_quality = 12;  // JPEG质量
  config.fb_count = 1;

  // 初始化摄像头
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  // 连接到WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");

  // 设置HTTP客户端
  HTTPClient http;
  http.begin(api_url);
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", "Bearer " + String(api_key));
}

void loop() {
  // 拍摄照片
  camera_fb_t * fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return;
  }

  // 将照片转换为Base64编码
  size_t encodedLen = base64_encoded_length(fb->len);
  char *encodedData = (char *)malloc(encodedLen + 1);
  base64_encode(encodedData, (const uint8_t *)fb->buf, fb->len);

  // 构造JSON请求体
  String payload = "{\"messages\":[{\"role\":\"user\",\"content\":\"![](data:image/jpeg;base64," + String(encodedData) + ")\"}]}";

  // 发送POST请求
  HTTPClient http;
  http.begin(api_url);
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", "Bearer " + String(api_key));

  int httpResponseCode = http.POST(payload);
  if (httpResponseCode > 0) {
    // 读取响应
    String response = http.getString();
    Serial.println(response);

    // 解析JSON响应
    DynamicJsonDocument doc(1024);
    DeserializationError error = deserializeJson(doc, response);
    if (!error) {
      String answer = doc["choices"][0]["message"]["content"].as<String>();  // 根据实际API返回的字段名称进行调整
      Serial.println("Answer: " + answer);

      // 在TFT LCD上显示答案
      tft.fillScreen(ST7735_BLACK);
      tft.setTextColor(ST7735_WHITE);
      tft.setTextSize(1);  // 设置字体大小
      tft.setCursor(0, 0);
      tft.print(answer);
    } else {
      Serial.println("Error parsing JSON");
    }
  } else {
    Serial.println("Error on sending POST: " + String(httpResponseCode));
  }

  // 释放资源
  esp_camera_fb_return(fb);
  free(encodedData);

  // 延迟一段时间再发送下一个请求
  delay(10000);
}

至于怎么安装进计算器内,需要看具体硬件,需要在后面开孔放摄像头,具体怎么隐藏怎么来吧

直接把计算器的主板扔掉,换上ESP32,哈哈哈

对于此项目,我叫它“ExamScanAI

希望有大佬看到此文章可以研发出更完美更完善的

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值